1 /* $NetBSD: vs.c,v 1.26 2004/12/13 02:14:14 chs Exp $ */ 2 3 /* 4 * Copyright (c) 2001 Tetsuya Isaki. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 /* 31 * VS - OKI MSM6258 ADPCM voice synthesizer device driver. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: vs.c,v 1.26 2004/12/13 02:14:14 chs Exp $"); 36 37 #include "audio.h" 38 #include "vs.h" 39 #if NAUDIO > 0 && NVS > 0 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/device.h> 44 45 #include <sys/audioio.h> 46 #include <dev/audio_if.h> 47 48 #include <machine/bus.h> 49 #include <machine/cpu.h> 50 51 #include <dev/ic/msm6258var.h> 52 53 #include <arch/x68k/dev/dmacvar.h> 54 #include <arch/x68k/dev/intiovar.h> 55 #include <arch/x68k/dev/opmvar.h> 56 57 #include <arch/x68k/dev/vsvar.h> 58 59 #ifdef VS_DEBUG 60 #define DPRINTF(y,x) if(vs_debug>=(y))printf x 61 static int vs_debug; 62 #ifdef AUDIO_DEBUG 63 extern int audiodebug; 64 #endif 65 #else 66 #define DPRINTF(y,x) 67 #endif 68 69 static int vs_match __P((struct device *, struct cfdata *, void *)); 70 static void vs_attach __P((struct device *, struct device *, void *)); 71 72 static int vs_dmaintr __P((void *)); 73 static int vs_dmaerrintr __P((void *)); 74 75 /* MI audio layer interface */ 76 static int vs_open __P((void *, int)); 77 static void vs_close __P((void *)); 78 static int vs_query_encoding __P((void *, struct audio_encoding *)); 79 static int vs_set_params __P((void *, int, int, struct audio_params *, 80 struct audio_params *)); 81 static int vs_trigger_output __P((void *, void *, void *, int, 82 void (*)(void *), void *, 83 struct audio_params *)); 84 static int vs_trigger_input __P((void *, void *, void *, int, 85 void (*)(void *), void *, 86 struct audio_params *)); 87 static int vs_halt_output __P((void *)); 88 static int vs_halt_input __P((void *)); 89 static int vs_allocmem __P((struct vs_softc *, size_t, size_t, size_t, int, 90 struct vs_dma *)); 91 static void vs_freemem __P((struct vs_dma *)); 92 static int vs_getdev __P((void *, struct audio_device *)); 93 static int vs_set_port __P((void *, mixer_ctrl_t *)); 94 static int vs_get_port __P((void *, mixer_ctrl_t *)); 95 static int vs_query_devinfo __P((void *, mixer_devinfo_t *)); 96 static void *vs_allocm __P((void *, int, size_t, struct malloc_type *, int)); 97 static void vs_freem __P((void *, void *, struct malloc_type *)); 98 static size_t vs_round_buffersize __P((void *, int, size_t)); 99 static int vs_get_props __P((void *)); 100 101 /* lower functions */ 102 static int vs_round_sr(u_long); 103 static void vs_set_sr(struct vs_softc *sc, int); 104 static inline void vs_set_po(struct vs_softc *sc, u_long); 105 106 extern struct cfdriver vs_cd; 107 108 CFATTACH_DECL(vs, sizeof(struct vs_softc), 109 vs_match, vs_attach, NULL, NULL); 110 111 static int vs_attached; 112 113 static const struct audio_hw_if vs_hw_if = { 114 vs_open, 115 vs_close, 116 NULL, /* drain */ 117 118 vs_query_encoding, 119 vs_set_params, 120 NULL, /* round_blocksize */ 121 NULL, /* commit_settings */ 122 123 NULL, /* init_output */ 124 NULL, /* init_input */ 125 NULL, /* start_output */ 126 NULL, /* start_input */ 127 128 vs_halt_output, 129 vs_halt_input, 130 NULL, /* speaker_ctl */ 131 132 vs_getdev, 133 NULL, /* setfd */ 134 135 vs_set_port, 136 vs_get_port, 137 vs_query_devinfo, 138 139 vs_allocm, 140 vs_freem, 141 vs_round_buffersize, 142 NULL, /* mappage */ 143 144 vs_get_props, 145 146 vs_trigger_output, 147 vs_trigger_input, 148 149 NULL, 150 }; 151 152 static struct audio_device vs_device = { 153 "OKI MSM6258", 154 "", 155 "vs" 156 }; 157 158 struct { 159 u_long rate; 160 u_char clk; 161 u_char den; 162 } vs_l2r[] = { 163 { VS_RATE_15K, VS_CLK_8MHZ, VS_SRATE_512 }, 164 { VS_RATE_10K, VS_CLK_8MHZ, VS_SRATE_768 }, 165 { VS_RATE_7K, VS_CLK_8MHZ, VS_SRATE_1024}, 166 { VS_RATE_5K, VS_CLK_4MHZ, VS_SRATE_768 }, 167 { VS_RATE_3K, VS_CLK_4MHZ, VS_SRATE_1024} 168 }; 169 170 #define NUM_RATE (sizeof(vs_l2r)/sizeof(vs_l2r[0])) 171 172 struct { 173 char *name; 174 int encoding; 175 int precision; 176 } vs_encodings[] = { 177 {AudioEadpcm, AUDIO_ENCODING_ADPCM, 4}, 178 {AudioEslinear, AUDIO_ENCODING_SLINEAR, 8}, 179 {AudioEulinear, AUDIO_ENCODING_ULINEAR, 8}, 180 {AudioEmulaw, AUDIO_ENCODING_ULAW, 8}, 181 {AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE, 16}, 182 {AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE, 16}, 183 }; 184 185 static int 186 vs_match(struct device *parent, struct cfdata *cf, void *aux) 187 { 188 struct intio_attach_args *ia = aux; 189 190 if (strcmp(ia->ia_name, "vs") || vs_attached) 191 return 0; 192 193 if (ia->ia_addr == INTIOCF_ADDR_DEFAULT) 194 ia->ia_addr = VS_ADDR; 195 if (ia->ia_dma == INTIOCF_DMA_DEFAULT) 196 ia->ia_dma = VS_DMA; 197 if (ia->ia_dmaintr == INTIOCF_DMAINTR_DEFAULT) 198 ia->ia_dmaintr = VS_DMAINTR; 199 200 /* fixed parameters */ 201 if (ia->ia_addr != VS_ADDR) 202 return 0; 203 if (ia->ia_dma != VS_DMA) 204 return 0; 205 if (ia->ia_dmaintr != VS_DMAINTR) 206 return 0; 207 208 #ifdef VS_DEBUG 209 vs_debug = 1; 210 #ifdef AUDIO_DEBUG 211 audiodebug = 2; 212 #endif 213 #endif 214 215 return 1; 216 } 217 218 static void 219 vs_attach(struct device *parent, struct device *self, void *aux) 220 { 221 struct vs_softc *sc = (struct vs_softc *)self; 222 bus_space_tag_t iot; 223 bus_space_handle_t ioh; 224 struct intio_attach_args *ia = aux; 225 226 vs_attached = 1; 227 228 printf("\n"); 229 230 /* Re-map the I/O space */ 231 iot = ia->ia_bst; 232 bus_space_map(iot, ia->ia_addr, 0x2000, BUS_SPACE_MAP_SHIFTED, &ioh); 233 234 /* Initialize sc */ 235 sc->sc_iot = iot; 236 sc->sc_ioh = ioh; 237 sc->sc_hw_if = &vs_hw_if; 238 sc->sc_addr = (caddr_t) ia->ia_addr; 239 sc->sc_dmas = NULL; 240 241 /* Initialize codec */ 242 sc->sc_codec = msm6258_codec_init(); 243 if (sc->sc_codec == NULL) { 244 printf ("Could not init codec\n"); 245 return; 246 } 247 248 /* XXX */ 249 bus_space_map(iot, PPI_ADDR, PPI_MAPSIZE, BUS_SPACE_MAP_SHIFTED, 250 &sc->sc_ppi); 251 252 /* Initialize DMAC */ 253 sc->sc_dmat = ia->ia_dmat; 254 sc->sc_dma_ch = dmac_alloc_channel(parent, ia->ia_dma, "vs", 255 ia->ia_dmaintr, vs_dmaintr, sc, 256 ia->ia_dmaintr+1, vs_dmaerrintr, sc); 257 258 printf("%s: MSM6258V ADPCM voice synthesizer\n", sc->sc_dev.dv_xname); 259 260 audio_attach_mi(&vs_hw_if, sc, &sc->sc_dev); 261 } 262 263 /* 264 * vs interrupt handler 265 */ 266 static int 267 vs_dmaintr(void *hdl) 268 { 269 struct vs_softc *sc = hdl; 270 271 DPRINTF(2, ("vs_dmaintr\n")); 272 273 if (sc->sc_pintr) { 274 /* start next transfer */ 275 sc->sc_current.dmap += sc->sc_current.blksize; 276 if (sc->sc_current.dmap + sc->sc_current.blksize 277 > sc->sc_current.bufsize) 278 sc->sc_current.dmap -= sc->sc_current.bufsize; 279 dmac_start_xfer_offset(sc->sc_dma_ch->ch_softc, 280 sc->sc_current.xfer, 281 sc->sc_current.dmap, 282 sc->sc_current.blksize); 283 sc->sc_pintr(sc->sc_parg); 284 } else if (sc->sc_rintr) { 285 /* start next transfer */ 286 sc->sc_current.dmap += sc->sc_current.blksize; 287 if (sc->sc_current.dmap + sc->sc_current.blksize 288 > sc->sc_current.bufsize) 289 sc->sc_current.dmap -= sc->sc_current.bufsize; 290 dmac_start_xfer_offset(sc->sc_dma_ch->ch_softc, 291 sc->sc_current.xfer, 292 sc->sc_current.dmap, 293 sc->sc_current.blksize); 294 sc->sc_rintr(sc->sc_rarg); 295 } else { 296 printf("vs_dmaintr: spurious interrupt\n"); 297 } 298 299 return 1; 300 } 301 302 static int 303 vs_dmaerrintr(void *hdl) 304 { 305 struct vs_softc *sc = hdl; 306 307 DPRINTF(1, ("%s: DMA transfer error.\n", sc->sc_dev.dv_xname)); 308 /* XXX */ 309 vs_dmaintr(sc); 310 311 return 1; 312 } 313 314 315 /* 316 * audio MD layer interfaces 317 */ 318 319 static int 320 vs_open(void *hdl, int flags) 321 { 322 struct vs_softc *sc = hdl; 323 324 DPRINTF(1, ("vs_open: flags=%d\n", flags)); 325 326 sc->sc_pintr = NULL; 327 sc->sc_rintr = NULL; 328 329 msm6258_codec_open(sc); 330 331 return 0; 332 } 333 334 static void 335 vs_close(void *hdl) 336 { 337 DPRINTF(1, ("vs_close\n")); 338 } 339 340 static int 341 vs_query_encoding(void *hdl, struct audio_encoding *fp) 342 { 343 DPRINTF(1, ("vs_query_encoding\n")); 344 345 if (fp->index >= sizeof(vs_encodings) / sizeof(vs_encodings[0])) 346 return EINVAL; 347 348 strcpy(fp->name, vs_encodings[fp->index].name); 349 fp->encoding = vs_encodings[fp->index].encoding; 350 fp->precision = vs_encodings[fp->index].precision; 351 if (fp->encoding == AUDIO_ENCODING_ADPCM) 352 fp->flags = 0; 353 else 354 fp->flags = AUDIO_ENCODINGFLAG_EMULATED; 355 return 0; 356 } 357 358 static int 359 vs_round_sr(u_long rate) 360 { 361 int i; 362 int diff = rate; 363 int nearest = 0; 364 365 for (i = 0; i < NUM_RATE; i++) { 366 if (rate >= vs_l2r[i].rate) { 367 if (rate - vs_l2r[i].rate < diff) { 368 diff = rate - vs_l2r[i].rate; 369 nearest = i; 370 } 371 } else { 372 if (vs_l2r[i].rate - rate < diff) { 373 diff = vs_l2r[i].rate - rate; 374 nearest = i; 375 } 376 } 377 } 378 if (diff * 100 / rate > 15) 379 return -1; 380 else 381 return nearest; 382 } 383 384 static int 385 vs_set_params(void *hdl, int setmode, int usemode, 386 struct audio_params *play, struct audio_params *rec) 387 { 388 struct vs_softc *sc = hdl; 389 struct audio_params *p; 390 int mode; 391 int rate; 392 void (*pswcode)(void *, u_char *, int); 393 void (*rswcode)(void *, u_char *, int); 394 395 DPRINTF(1, ("vs_set_params: setmode=%d, usemode=%d\n", 396 setmode, usemode)); 397 398 /* set first record info, then play info */ 399 for (mode = AUMODE_RECORD; mode != -1; 400 mode = (mode == AUMODE_RECORD) ? AUMODE_PLAY : -1) { 401 if ((setmode & mode) == 0) 402 continue; 403 404 p = (mode == AUMODE_PLAY) ? play : rec; 405 406 if (p->channels != 1) 407 return (EINVAL); 408 409 rate = p->sample_rate; 410 pswcode = NULL; 411 rswcode = NULL; 412 p->factor = 1; 413 p->factor_denom = 0; 414 p->hw_precision = 4; 415 p->hw_encoding = AUDIO_ENCODING_ADPCM; 416 DPRINTF(1, ("vs_set_params: encoding=%d, precision=%d\n", 417 p->encoding, p->precision)); 418 switch (p->precision) { 419 case 4: 420 if (p->encoding == AUDIO_ENCODING_ADPCM) 421 p->factor_denom = 1; 422 break; 423 case 8: 424 switch (p->encoding) { 425 case AUDIO_ENCODING_ULAW: 426 p->factor_denom = 2; 427 pswcode = msm6258_mulaw_to_adpcm; 428 rswcode = msm6258_adpcm_to_mulaw; 429 break; 430 case AUDIO_ENCODING_SLINEAR: 431 case AUDIO_ENCODING_SLINEAR_LE: 432 case AUDIO_ENCODING_SLINEAR_BE: 433 p->factor_denom = 2; 434 pswcode = msm6258_slinear8_to_adpcm; 435 rswcode = msm6258_adpcm_to_slinear8; 436 break; 437 case AUDIO_ENCODING_ULINEAR: 438 case AUDIO_ENCODING_ULINEAR_LE: 439 case AUDIO_ENCODING_ULINEAR_BE: 440 p->factor_denom = 2; 441 pswcode = msm6258_ulinear8_to_adpcm; 442 rswcode = msm6258_adpcm_to_ulinear8; 443 break; 444 } 445 break; 446 case 16: 447 switch (p->encoding) { 448 case AUDIO_ENCODING_SLINEAR_LE: 449 p->factor_denom = 4; 450 pswcode = msm6258_slinear16_le_to_adpcm; 451 rswcode = msm6258_adpcm_to_slinear16_le; 452 break; 453 case AUDIO_ENCODING_SLINEAR_BE: 454 p->factor_denom = 4; 455 pswcode = msm6258_slinear16_be_to_adpcm; 456 rswcode = msm6258_adpcm_to_slinear16_be; 457 break; 458 } 459 break; 460 } 461 if (p->factor_denom == 0) { 462 DPRINTF(1, ("vs_set_params: mode=%d, encoding=%d\n", 463 mode, p->encoding)); 464 return EINVAL; 465 } 466 467 DPRINTF(1, ("vs_set_params: rate=%d -> ", rate)); 468 rate = vs_round_sr(rate); 469 DPRINTF(1, ("%d\n", rate)); 470 if (rate < 0) 471 return (EINVAL); 472 if (mode == AUMODE_PLAY) { 473 p->sw_code = pswcode; 474 sc->sc_current.prate = rate; 475 } else { 476 p->sw_code = rswcode; 477 sc->sc_current.rrate = rate; 478 } 479 } 480 481 return 0; 482 } 483 484 static void 485 vs_set_sr(struct vs_softc *sc, int rate) 486 { 487 DPRINTF(1, ("setting sample rate to %d, %d\n", 488 rate, (int)vs_l2r[rate].rate)); 489 bus_space_write_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC, 490 (bus_space_read_1 (sc->sc_iot, sc->sc_ppi, 491 PPI_PORTC) & 0xf0) 492 | vs_l2r[rate].den); 493 adpcm_chgclk(vs_l2r[rate].clk); 494 } 495 496 static inline void 497 vs_set_po(struct vs_softc *sc, u_long po) 498 { 499 bus_space_write_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC, 500 (bus_space_read_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC) 501 & 0xfc) | po); 502 } 503 504 static int 505 vs_trigger_output(void *hdl, void *start, void *end, int bsize, 506 void (*intr)(void *), void *arg, 507 struct audio_params *p) 508 { 509 struct vs_softc *sc = hdl; 510 struct vs_dma *vd; 511 struct dmac_dma_xfer *xf; 512 struct dmac_channel_stat *chan = sc->sc_dma_ch; 513 514 DPRINTF(2, ("vs_trigger_output: start=%p, bsize=%d, intr=%p, arg=%p\n", 515 start, bsize, intr, arg)); 516 517 sc->sc_pintr = intr; 518 sc->sc_parg = arg; 519 sc->sc_current.blksize = bsize; 520 sc->sc_current.bufsize = (char*)end - (char*)start; 521 sc->sc_current.dmap = 0; 522 523 /* Find DMA buffer. */ 524 for (vd = sc->sc_dmas; vd != NULL && KVADDR(vd) != start; 525 vd = vd->vd_next) 526 ; 527 if (vd == NULL) { 528 printf("%s: trigger_output: bad addr %p\n", 529 sc->sc_dev.dv_xname, start); 530 return (EINVAL); 531 } 532 533 vs_set_sr(sc, sc->sc_current.prate); 534 vs_set_po(sc, VS_PANOUT_LR); 535 536 xf = dmac_alloc_xfer(chan, sc->sc_dmat, vd->vd_map); 537 sc->sc_current.xfer = xf; 538 chan->ch_dcr = (DMAC_DCR_XRM_CSWOH | DMAC_DCR_OTYP_EASYNC | 539 DMAC_DCR_OPS_8BIT); 540 chan->ch_ocr = DMAC_OCR_REQG_EXTERNAL; 541 xf->dx_ocr = DMAC_OCR_DIR_MTD; 542 xf->dx_scr = DMAC_SCR_MAC_COUNT_UP | DMAC_SCR_DAC_NO_COUNT; 543 xf->dx_device = sc->sc_addr + MSM6258_DATA*2 + 1; 544 545 dmac_load_xfer(chan->ch_softc, xf); 546 dmac_start_xfer_offset(chan->ch_softc, xf, 0, sc->sc_current.blksize); 547 bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 2); 548 549 return 0; 550 } 551 552 static int 553 vs_trigger_input(void *hdl, void *start, void *end, int bsize, 554 void (*intr)(void *), void *arg, 555 struct audio_params *p) 556 { 557 struct vs_softc *sc = hdl; 558 struct vs_dma *vd; 559 struct dmac_dma_xfer *xf; 560 struct dmac_channel_stat *chan = sc->sc_dma_ch; 561 562 DPRINTF(2, ("vs_trigger_input: start=%p, bsize=%d, intr=%p, arg=%p\n", 563 start, bsize, intr, arg)); 564 565 sc->sc_rintr = intr; 566 sc->sc_rarg = arg; 567 sc->sc_current.blksize = bsize; 568 sc->sc_current.bufsize = (char*)end - (char*)start; 569 sc->sc_current.dmap = 0; 570 571 /* Find DMA buffer. */ 572 for (vd = sc->sc_dmas; vd != NULL && KVADDR(vd) != start; 573 vd = vd->vd_next) 574 ; 575 if (vd == NULL) { 576 printf("%s: trigger_output: bad addr %p\n", 577 sc->sc_dev.dv_xname, start); 578 return (EINVAL); 579 } 580 581 vs_set_sr(sc, sc->sc_current.rrate); 582 xf = dmac_alloc_xfer(chan, sc->sc_dmat, vd->vd_map); 583 sc->sc_current.xfer = xf; 584 chan->ch_dcr = (DMAC_DCR_XRM_CSWOH | DMAC_DCR_OTYP_EASYNC | 585 DMAC_DCR_OPS_8BIT); 586 chan->ch_ocr = DMAC_OCR_REQG_EXTERNAL; 587 xf->dx_ocr = DMAC_OCR_DIR_DTM; 588 xf->dx_scr = DMAC_SCR_MAC_COUNT_UP | DMAC_SCR_DAC_NO_COUNT; 589 xf->dx_device = sc->sc_addr + MSM6258_DATA*2 + 1; 590 591 dmac_load_xfer(chan->ch_softc, xf); 592 dmac_start_xfer_offset(chan->ch_softc, xf, 0, sc->sc_current.blksize); 593 bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 4); 594 595 return 0; 596 } 597 598 static int 599 vs_halt_output(void *hdl) 600 { 601 struct vs_softc *sc = hdl; 602 603 DPRINTF(1, ("vs_halt_output\n")); 604 605 /* stop ADPCM play */ 606 dmac_abort_xfer(sc->sc_dma_ch->ch_softc, sc->sc_current.xfer); 607 bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 1); 608 609 return 0; 610 } 611 612 static int 613 vs_halt_input(void *hdl) 614 { 615 struct vs_softc *sc = hdl; 616 617 DPRINTF(1, ("vs_halt_input\n")); 618 619 /* stop ADPCM recoding */ 620 dmac_abort_xfer(sc->sc_dma_ch->ch_softc, sc->sc_current.xfer); 621 bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 1); 622 623 return 0; 624 } 625 626 static int 627 vs_allocmem(struct vs_softc *sc, size_t size, size_t align, size_t boundary, 628 int flags, struct vs_dma *vd) 629 { 630 int error, wait; 631 632 #ifdef DIAGNOSTIC 633 if (size > DMAC_MAXSEGSZ) 634 panic ("vs_allocmem: maximum size exceeded, %d", (int) size); 635 #endif 636 637 wait = (flags & M_NOWAIT) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK; 638 vd->vd_size = size; 639 640 error = bus_dmamem_alloc(vd->vd_dmat, vd->vd_size, align, boundary, 641 vd->vd_segs, 642 sizeof (vd->vd_segs) / sizeof (vd->vd_segs[0]), 643 &vd->vd_nsegs, wait); 644 if (error) 645 goto out; 646 647 error = bus_dmamem_map(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs, 648 vd->vd_size, &vd->vd_addr, 649 wait | BUS_DMA_COHERENT); 650 if (error) 651 goto free; 652 653 error = bus_dmamap_create(vd->vd_dmat, vd->vd_size, 1, DMAC_MAXSEGSZ, 654 0, wait, &vd->vd_map); 655 if (error) 656 goto unmap; 657 658 error = bus_dmamap_load(vd->vd_dmat, vd->vd_map, vd->vd_addr, 659 vd->vd_size, NULL, wait); 660 if (error) 661 goto destroy; 662 663 return (0); 664 665 destroy: 666 bus_dmamap_destroy(vd->vd_dmat, vd->vd_map); 667 unmap: 668 bus_dmamem_unmap(vd->vd_dmat, vd->vd_addr, vd->vd_size); 669 free: 670 bus_dmamem_free(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs); 671 out: 672 return (error); 673 } 674 675 static void 676 vs_freemem(struct vs_dma *vd) 677 { 678 679 bus_dmamap_unload(vd->vd_dmat, vd->vd_map); 680 bus_dmamap_destroy(vd->vd_dmat, vd->vd_map); 681 bus_dmamem_unmap(vd->vd_dmat, vd->vd_addr, vd->vd_size); 682 bus_dmamem_free(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs); 683 } 684 685 static int 686 vs_getdev(void *hdl, struct audio_device *retp) 687 { 688 DPRINTF(1, ("vs_getdev\n")); 689 690 *retp = vs_device; 691 return 0; 692 } 693 694 static int 695 vs_set_port(void *hdl, mixer_ctrl_t *cp) 696 { 697 DPRINTF(1, ("vs_set_port\n")); 698 return 0; 699 } 700 701 static int 702 vs_get_port(void *hdl, mixer_ctrl_t *cp) 703 { 704 DPRINTF(1, ("vs_get_port\n")); 705 return 0; 706 } 707 708 static int 709 vs_query_devinfo(void *hdl, mixer_devinfo_t *mi) 710 { 711 DPRINTF(1, ("vs_query_devinfo\n")); 712 switch (mi->index) { 713 default: 714 return EINVAL; 715 } 716 return 0; 717 } 718 719 static void * 720 vs_allocm(void *hdl, int direction, size_t size, struct malloc_type *type, 721 int flags) 722 { 723 struct vs_softc *sc = hdl; 724 struct vs_dma *vd; 725 int error; 726 727 if ((vd = malloc(size, type, flags)) == NULL) 728 return (NULL); 729 730 vd->vd_dmat = sc->sc_dmat; 731 732 error = vs_allocmem(sc, size, 32, 0, flags, vd); 733 if (error) { 734 free(vd, type); 735 return (NULL); 736 } 737 vd->vd_next = sc->sc_dmas; 738 sc->sc_dmas = vd; 739 740 return (KVADDR(vd)); 741 } 742 743 static void 744 vs_freem(void *hdl, void *addr, struct malloc_type *type) 745 { 746 struct vs_softc *sc = hdl; 747 struct vs_dma *p, **pp; 748 749 for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->vd_next) { 750 if (KVADDR(p) == addr) { 751 vs_freemem(p); 752 *pp = p->vd_next; 753 free(p, type); 754 return; 755 } 756 } 757 } 758 759 static size_t 760 vs_round_buffersize(void *hdl, int direction, size_t bufsize) 761 { 762 if (bufsize > DMAC_MAXSEGSZ) 763 bufsize = DMAC_MAXSEGSZ; 764 765 return bufsize; 766 } 767 768 #if 0 769 paddr_t 770 vs_mappage(void *addr, void *mem, off_t off, int prot) 771 { 772 struct vs_softc *sc = addr; 773 struct vs_dma *p; 774 775 if (off < 0) 776 return (-1); 777 for (p = sc->sc_dmas; p != NULL && KVADDR(p) != mem; 778 p = p->vd_next) 779 ; 780 if (p == NULL) { 781 printf("%s: mappage: bad addr %p\n", 782 sc->sc_dev.dv_xname, start); 783 return (-1); 784 } 785 786 return (bus_dmamem_mmap(sc->sc_dmat, p->vd_segs, p->vd_nsegs, 787 off, prot, BUS_DMA_WAITOK)); 788 } 789 #endif 790 791 static int 792 vs_get_props(void *hdl) 793 { 794 DPRINTF(1, ("vs_get_props\n")); 795 return 0 /* | dependent | half duplex | no mmap */; 796 } 797 #endif /* NAUDIO > 0 && NVS > 0*/ 798