1 /* $NetBSD: vs.c,v 1.24 2004/05/08 08:38:36 minoura 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.24 2004/05/08 08:38:36 minoura 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 struct audio_hw_if vs_hw_if = { 112 vs_open, 113 vs_close, 114 NULL, /* drain */ 115 116 vs_query_encoding, 117 vs_set_params, 118 NULL, /* round_blocksize */ 119 NULL, /* commit_settings */ 120 121 NULL, /* init_output */ 122 NULL, /* init_input */ 123 NULL, /* start_output */ 124 NULL, /* start_input */ 125 126 vs_halt_output, 127 vs_halt_input, 128 NULL, /* speaker_ctl */ 129 130 vs_getdev, 131 NULL, /* setfd */ 132 133 vs_set_port, 134 vs_get_port, 135 vs_query_devinfo, 136 137 vs_allocm, 138 vs_freem, 139 vs_round_buffersize, 140 NULL, /* mappage */ 141 142 vs_get_props, 143 144 vs_trigger_output, 145 vs_trigger_input, 146 147 NULL, 148 }; 149 150 static struct audio_device vs_device = { 151 "OKI MSM6258", 152 "", 153 "vs" 154 }; 155 156 struct { 157 u_long rate; 158 u_char clk; 159 u_char den; 160 } vs_l2r[] = { 161 { VS_RATE_15K, VS_CLK_8MHZ, VS_SRATE_512 }, 162 { VS_RATE_10K, VS_CLK_8MHZ, VS_SRATE_768 }, 163 { VS_RATE_7K, VS_CLK_8MHZ, VS_SRATE_1024}, 164 { VS_RATE_5K, VS_CLK_4MHZ, VS_SRATE_768 }, 165 { VS_RATE_3K, VS_CLK_4MHZ, VS_SRATE_1024} 166 }; 167 168 #define NUM_RATE (sizeof(vs_l2r)/sizeof(vs_l2r[0])) 169 170 struct { 171 char *name; 172 int encoding; 173 int precision; 174 } vs_encodings[] = { 175 {AudioEadpcm, AUDIO_ENCODING_ADPCM, 4}, 176 {AudioEslinear, AUDIO_ENCODING_SLINEAR, 8}, 177 {AudioEulinear, AUDIO_ENCODING_ULINEAR, 8}, 178 {AudioEmulaw, AUDIO_ENCODING_ULAW, 8}, 179 {AudioEslinear_be, AUDIO_ENCODING_SLINEAR_BE, 16}, 180 {AudioEslinear_le, AUDIO_ENCODING_SLINEAR_LE, 16}, 181 }; 182 183 static int 184 vs_match(struct device *parent, struct cfdata *cf, void *aux) 185 { 186 struct intio_attach_args *ia = aux; 187 188 if (strcmp(ia->ia_name, "vs") || cf->cf_unit > 0) 189 return 0; 190 191 if (ia->ia_addr == INTIOCF_ADDR_DEFAULT) 192 ia->ia_addr = VS_ADDR; 193 if (ia->ia_dma == INTIOCF_DMA_DEFAULT) 194 ia->ia_dma = VS_DMA; 195 if (ia->ia_dmaintr == INTIOCF_DMAINTR_DEFAULT) 196 ia->ia_dmaintr = VS_DMAINTR; 197 198 /* fixed parameters */ 199 if (ia->ia_addr != VS_ADDR) 200 return 0; 201 if (ia->ia_dma != VS_DMA) 202 return 0; 203 if (ia->ia_dmaintr != VS_DMAINTR) 204 return 0; 205 206 #ifdef VS_DEBUG 207 vs_debug = 1; 208 #ifdef AUDIO_DEBUG 209 audiodebug = 2; 210 #endif 211 #endif 212 213 return 1; 214 } 215 216 static void 217 vs_attach(struct device *parent, struct device *self, void *aux) 218 { 219 struct vs_softc *sc = (struct vs_softc *)self; 220 bus_space_tag_t iot; 221 bus_space_handle_t ioh; 222 struct intio_attach_args *ia = aux; 223 224 printf("\n"); 225 226 /* Re-map the I/O space */ 227 iot = ia->ia_bst; 228 bus_space_map(iot, ia->ia_addr, 0x2000, BUS_SPACE_MAP_SHIFTED, &ioh); 229 230 /* Initialize sc */ 231 sc->sc_iot = iot; 232 sc->sc_ioh = ioh; 233 sc->sc_hw_if = &vs_hw_if; 234 sc->sc_addr = (caddr_t) ia->ia_addr; 235 sc->sc_dmas = NULL; 236 237 /* Initialize codec */ 238 sc->sc_codec = msm6258_codec_init(); 239 if (sc->sc_codec == NULL) { 240 printf ("Could not init codec\n"); 241 return; 242 } 243 244 /* XXX */ 245 bus_space_map(iot, PPI_ADDR, PPI_MAPSIZE, BUS_SPACE_MAP_SHIFTED, 246 &sc->sc_ppi); 247 248 /* Initialize DMAC */ 249 sc->sc_dmat = ia->ia_dmat; 250 sc->sc_dma_ch = dmac_alloc_channel(parent, ia->ia_dma, "vs", 251 ia->ia_dmaintr, vs_dmaintr, sc, 252 ia->ia_dmaintr+1, vs_dmaerrintr, sc); 253 254 printf("%s: MSM6258V ADPCM voice synthesizer\n", sc->sc_dev.dv_xname); 255 256 audio_attach_mi(&vs_hw_if, sc, &sc->sc_dev); 257 } 258 259 /* 260 * vs interrupt handler 261 */ 262 static int 263 vs_dmaintr(void *hdl) 264 { 265 struct vs_softc *sc = hdl; 266 267 DPRINTF(2, ("vs_dmaintr\n")); 268 269 if (sc->sc_pintr) { 270 /* start next transfer */ 271 sc->sc_current.dmap += sc->sc_current.blksize; 272 if (sc->sc_current.dmap + sc->sc_current.blksize 273 > sc->sc_current.bufsize) 274 sc->sc_current.dmap -= sc->sc_current.bufsize; 275 dmac_start_xfer_offset(sc->sc_dma_ch->ch_softc, 276 sc->sc_current.xfer, 277 sc->sc_current.dmap, 278 sc->sc_current.blksize); 279 sc->sc_pintr(sc->sc_parg); 280 } else if (sc->sc_rintr) { 281 /* start next transfer */ 282 sc->sc_current.dmap += sc->sc_current.blksize; 283 if (sc->sc_current.dmap + sc->sc_current.blksize 284 > sc->sc_current.bufsize) 285 sc->sc_current.dmap -= sc->sc_current.bufsize; 286 dmac_start_xfer_offset(sc->sc_dma_ch->ch_softc, 287 sc->sc_current.xfer, 288 sc->sc_current.dmap, 289 sc->sc_current.blksize); 290 sc->sc_rintr(sc->sc_rarg); 291 } else { 292 printf("vs_dmaintr: spurious interrupt\n"); 293 } 294 295 return 1; 296 } 297 298 static int 299 vs_dmaerrintr(void *hdl) 300 { 301 struct vs_softc *sc = hdl; 302 303 DPRINTF(1, ("%s: DMA transfer error.\n", sc->sc_dev.dv_xname)); 304 /* XXX */ 305 vs_dmaintr(sc); 306 307 return 1; 308 } 309 310 311 /* 312 * audio MD layer interfaces 313 */ 314 315 static int 316 vs_open(void *hdl, int flags) 317 { 318 struct vs_softc *sc = hdl; 319 320 DPRINTF(1, ("vs_open: flags=%d\n", flags)); 321 322 sc->sc_pintr = NULL; 323 sc->sc_rintr = NULL; 324 325 msm6258_codec_open(sc); 326 327 return 0; 328 } 329 330 static void 331 vs_close(void *hdl) 332 { 333 DPRINTF(1, ("vs_close\n")); 334 } 335 336 static int 337 vs_query_encoding(void *hdl, struct audio_encoding *fp) 338 { 339 DPRINTF(1, ("vs_query_encoding\n")); 340 341 if (fp->index >= sizeof(vs_encodings) / sizeof(vs_encodings[0])) 342 return EINVAL; 343 344 strcpy(fp->name, vs_encodings[fp->index].name); 345 fp->encoding = vs_encodings[fp->index].encoding; 346 fp->precision = vs_encodings[fp->index].precision; 347 if (fp->encoding == AUDIO_ENCODING_ADPCM) 348 fp->flags = 0; 349 else 350 fp->flags = AUDIO_ENCODINGFLAG_EMULATED; 351 return 0; 352 } 353 354 static int 355 vs_round_sr(u_long rate) 356 { 357 int i; 358 int diff = rate; 359 int nearest = 0; 360 361 for (i = 0; i < NUM_RATE; i++) { 362 if (rate >= vs_l2r[i].rate) { 363 if (rate - vs_l2r[i].rate < diff) { 364 diff = rate - vs_l2r[i].rate; 365 nearest = i; 366 } 367 } else { 368 if (vs_l2r[i].rate - rate < diff) { 369 diff = vs_l2r[i].rate - rate; 370 nearest = i; 371 } 372 } 373 } 374 if (diff * 100 / rate > 15) 375 return -1; 376 else 377 return nearest; 378 } 379 380 static int 381 vs_set_params(void *hdl, int setmode, int usemode, 382 struct audio_params *play, struct audio_params *rec) 383 { 384 struct vs_softc *sc = hdl; 385 struct audio_params *p; 386 int mode; 387 int rate; 388 void (*pswcode)(void *, u_char *, int); 389 void (*rswcode)(void *, u_char *, int); 390 391 DPRINTF(1, ("vs_set_params: setmode=%d, usemode=%d\n", 392 setmode, usemode)); 393 394 /* set first record info, then play info */ 395 for (mode = AUMODE_RECORD; mode != -1; 396 mode = (mode == AUMODE_RECORD) ? AUMODE_PLAY : -1) { 397 if ((setmode & mode) == 0) 398 continue; 399 400 p = (mode == AUMODE_PLAY) ? play : rec; 401 402 if (p->channels != 1) 403 return (EINVAL); 404 405 rate = p->sample_rate; 406 pswcode = NULL; 407 rswcode = NULL; 408 p->factor = 1; 409 p->factor_denom = 0; 410 p->hw_precision = 4; 411 p->hw_encoding = AUDIO_ENCODING_ADPCM; 412 DPRINTF(1, ("vs_set_params: encoding=%d, precision=%d\n", 413 p->encoding, p->precision)); 414 switch (p->precision) { 415 case 4: 416 if (p->encoding == AUDIO_ENCODING_ADPCM) 417 p->factor_denom = 1; 418 break; 419 case 8: 420 switch (p->encoding) { 421 case AUDIO_ENCODING_ULAW: 422 p->factor_denom = 2; 423 pswcode = msm6258_mulaw_to_adpcm; 424 rswcode = msm6258_adpcm_to_mulaw; 425 break; 426 case AUDIO_ENCODING_SLINEAR: 427 case AUDIO_ENCODING_SLINEAR_LE: 428 case AUDIO_ENCODING_SLINEAR_BE: 429 p->factor_denom = 2; 430 pswcode = msm6258_slinear8_to_adpcm; 431 rswcode = msm6258_adpcm_to_slinear8; 432 break; 433 case AUDIO_ENCODING_ULINEAR: 434 case AUDIO_ENCODING_ULINEAR_LE: 435 case AUDIO_ENCODING_ULINEAR_BE: 436 p->factor_denom = 2; 437 pswcode = msm6258_ulinear8_to_adpcm; 438 rswcode = msm6258_adpcm_to_ulinear8; 439 break; 440 } 441 break; 442 case 16: 443 switch (p->encoding) { 444 case AUDIO_ENCODING_SLINEAR_LE: 445 p->factor_denom = 4; 446 pswcode = msm6258_slinear16_le_to_adpcm; 447 rswcode = msm6258_adpcm_to_slinear16_le; 448 break; 449 case AUDIO_ENCODING_SLINEAR_BE: 450 p->factor_denom = 4; 451 pswcode = msm6258_slinear16_be_to_adpcm; 452 rswcode = msm6258_adpcm_to_slinear16_be; 453 break; 454 } 455 break; 456 } 457 if (p->factor_denom == 0) { 458 DPRINTF(1, ("vs_set_params: mode=%d, encoding=%d\n", 459 mode, p->encoding)); 460 return EINVAL; 461 } 462 463 DPRINTF(1, ("vs_set_params: rate=%d -> ", rate)); 464 rate = vs_round_sr(rate); 465 DPRINTF(1, ("%d\n", rate)); 466 if (rate < 0) 467 return (EINVAL); 468 if (mode == AUMODE_PLAY) { 469 p->sw_code = pswcode; 470 sc->sc_current.prate = rate; 471 } else { 472 p->sw_code = rswcode; 473 sc->sc_current.rrate = rate; 474 } 475 } 476 477 return 0; 478 } 479 480 static void 481 vs_set_sr(struct vs_softc *sc, int rate) 482 { 483 DPRINTF(1, ("setting sample rate to %d, %d\n", 484 rate, (int)vs_l2r[rate].rate)); 485 bus_space_write_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC, 486 (bus_space_read_1 (sc->sc_iot, sc->sc_ppi, 487 PPI_PORTC) & 0xf0) 488 | vs_l2r[rate].den); 489 adpcm_chgclk(vs_l2r[rate].clk); 490 } 491 492 static inline void 493 vs_set_po(struct vs_softc *sc, u_long po) 494 { 495 bus_space_write_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC, 496 (bus_space_read_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC) 497 & 0xfc) | po); 498 } 499 500 static int 501 vs_trigger_output(void *hdl, void *start, void *end, int bsize, 502 void (*intr)(void *), void *arg, 503 struct audio_params *p) 504 { 505 struct vs_softc *sc = hdl; 506 struct vs_dma *vd; 507 struct dmac_dma_xfer *xf; 508 struct dmac_channel_stat *chan = sc->sc_dma_ch; 509 510 DPRINTF(2, ("vs_trigger_output: start=%p, bsize=%d, intr=%p, arg=%p\n", 511 start, bsize, intr, arg)); 512 513 sc->sc_pintr = intr; 514 sc->sc_parg = arg; 515 sc->sc_current.blksize = bsize; 516 sc->sc_current.bufsize = (char*)end - (char*)start; 517 sc->sc_current.dmap = 0; 518 519 /* Find DMA buffer. */ 520 for (vd = sc->sc_dmas; vd != NULL && KVADDR(vd) != start; 521 vd = vd->vd_next) 522 ; 523 if (vd == NULL) { 524 printf("%s: trigger_output: bad addr %p\n", 525 sc->sc_dev.dv_xname, start); 526 return (EINVAL); 527 } 528 529 vs_set_sr(sc, sc->sc_current.prate); 530 vs_set_po(sc, VS_PANOUT_LR); 531 532 xf = dmac_alloc_xfer(chan, sc->sc_dmat, vd->vd_map); 533 sc->sc_current.xfer = xf; 534 chan->ch_dcr = (DMAC_DCR_XRM_CSWOH | DMAC_DCR_OTYP_EASYNC | 535 DMAC_DCR_OPS_8BIT); 536 chan->ch_ocr = DMAC_OCR_REQG_EXTERNAL; 537 xf->dx_ocr = DMAC_OCR_DIR_MTD; 538 xf->dx_scr = DMAC_SCR_MAC_COUNT_UP | DMAC_SCR_DAC_NO_COUNT; 539 xf->dx_device = sc->sc_addr + MSM6258_DATA*2 + 1; 540 541 dmac_load_xfer(chan->ch_softc, xf); 542 dmac_start_xfer_offset(chan->ch_softc, xf, 0, sc->sc_current.blksize); 543 bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 2); 544 545 return 0; 546 } 547 548 static int 549 vs_trigger_input(void *hdl, void *start, void *end, int bsize, 550 void (*intr)(void *), void *arg, 551 struct audio_params *p) 552 { 553 struct vs_softc *sc = hdl; 554 struct vs_dma *vd; 555 struct dmac_dma_xfer *xf; 556 struct dmac_channel_stat *chan = sc->sc_dma_ch; 557 558 DPRINTF(2, ("vs_trigger_input: start=%p, bsize=%d, intr=%p, arg=%p\n", 559 start, bsize, intr, arg)); 560 561 sc->sc_rintr = intr; 562 sc->sc_rarg = arg; 563 sc->sc_current.blksize = bsize; 564 sc->sc_current.bufsize = (char*)end - (char*)start; 565 sc->sc_current.dmap = 0; 566 567 /* Find DMA buffer. */ 568 for (vd = sc->sc_dmas; vd != NULL && KVADDR(vd) != start; 569 vd = vd->vd_next) 570 ; 571 if (vd == NULL) { 572 printf("%s: trigger_output: bad addr %p\n", 573 sc->sc_dev.dv_xname, start); 574 return (EINVAL); 575 } 576 577 vs_set_sr(sc, sc->sc_current.rrate); 578 xf = dmac_alloc_xfer(chan, sc->sc_dmat, vd->vd_map); 579 sc->sc_current.xfer = xf; 580 chan->ch_dcr = (DMAC_DCR_XRM_CSWOH | DMAC_DCR_OTYP_EASYNC | 581 DMAC_DCR_OPS_8BIT); 582 chan->ch_ocr = DMAC_OCR_REQG_EXTERNAL; 583 xf->dx_ocr = DMAC_OCR_DIR_DTM; 584 xf->dx_scr = DMAC_SCR_MAC_COUNT_UP | DMAC_SCR_DAC_NO_COUNT; 585 xf->dx_device = sc->sc_addr + MSM6258_DATA*2 + 1; 586 587 dmac_load_xfer(chan->ch_softc, xf); 588 dmac_start_xfer_offset(chan->ch_softc, xf, 0, sc->sc_current.blksize); 589 bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 4); 590 591 return 0; 592 } 593 594 static int 595 vs_halt_output(void *hdl) 596 { 597 struct vs_softc *sc = hdl; 598 599 DPRINTF(1, ("vs_halt_output\n")); 600 601 /* stop ADPCM play */ 602 dmac_abort_xfer(sc->sc_dma_ch->ch_softc, sc->sc_current.xfer); 603 bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 1); 604 605 return 0; 606 } 607 608 static int 609 vs_halt_input(void *hdl) 610 { 611 struct vs_softc *sc = hdl; 612 613 DPRINTF(1, ("vs_halt_input\n")); 614 615 /* stop ADPCM recoding */ 616 dmac_abort_xfer(sc->sc_dma_ch->ch_softc, sc->sc_current.xfer); 617 bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSM6258_STAT, 1); 618 619 return 0; 620 } 621 622 static int 623 vs_allocmem(struct vs_softc *sc, size_t size, size_t align, size_t boundary, 624 int flags, struct vs_dma *vd) 625 { 626 int error, wait; 627 628 #ifdef DIAGNOSTIC 629 if (size > DMAC_MAXSEGSZ) 630 panic ("vs_allocmem: maximum size exceeded, %d", (int) size); 631 #endif 632 633 wait = (flags & M_NOWAIT) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK; 634 vd->vd_size = size; 635 636 error = bus_dmamem_alloc(vd->vd_dmat, vd->vd_size, align, boundary, 637 vd->vd_segs, 638 sizeof (vd->vd_segs) / sizeof (vd->vd_segs[0]), 639 &vd->vd_nsegs, wait); 640 if (error) 641 goto out; 642 643 error = bus_dmamem_map(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs, 644 vd->vd_size, &vd->vd_addr, 645 wait | BUS_DMA_COHERENT); 646 if (error) 647 goto free; 648 649 error = bus_dmamap_create(vd->vd_dmat, vd->vd_size, 1, DMAC_MAXSEGSZ, 650 0, wait, &vd->vd_map); 651 if (error) 652 goto unmap; 653 654 error = bus_dmamap_load(vd->vd_dmat, vd->vd_map, vd->vd_addr, 655 vd->vd_size, NULL, wait); 656 if (error) 657 goto destroy; 658 659 return (0); 660 661 destroy: 662 bus_dmamap_destroy(vd->vd_dmat, vd->vd_map); 663 unmap: 664 bus_dmamem_unmap(vd->vd_dmat, vd->vd_addr, vd->vd_size); 665 free: 666 bus_dmamem_free(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs); 667 out: 668 return (error); 669 } 670 671 static void 672 vs_freemem(struct vs_dma *vd) 673 { 674 675 bus_dmamap_unload(vd->vd_dmat, vd->vd_map); 676 bus_dmamap_destroy(vd->vd_dmat, vd->vd_map); 677 bus_dmamem_unmap(vd->vd_dmat, vd->vd_addr, vd->vd_size); 678 bus_dmamem_free(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs); 679 } 680 681 static int 682 vs_getdev(void *hdl, struct audio_device *retp) 683 { 684 DPRINTF(1, ("vs_getdev\n")); 685 686 *retp = vs_device; 687 return 0; 688 } 689 690 static int 691 vs_set_port(void *hdl, mixer_ctrl_t *cp) 692 { 693 DPRINTF(1, ("vs_set_port\n")); 694 return 0; 695 } 696 697 static int 698 vs_get_port(void *hdl, mixer_ctrl_t *cp) 699 { 700 DPRINTF(1, ("vs_get_port\n")); 701 return 0; 702 } 703 704 static int 705 vs_query_devinfo(void *hdl, mixer_devinfo_t *mi) 706 { 707 DPRINTF(1, ("vs_query_devinfo\n")); 708 switch (mi->index) { 709 default: 710 return EINVAL; 711 } 712 return 0; 713 } 714 715 static void * 716 vs_allocm(void *hdl, int direction, size_t size, struct malloc_type *type, 717 int flags) 718 { 719 struct vs_softc *sc = hdl; 720 struct vs_dma *vd; 721 int error; 722 723 if ((vd = malloc(size, type, flags)) == NULL) 724 return (NULL); 725 726 vd->vd_dmat = sc->sc_dmat; 727 728 error = vs_allocmem(sc, size, 32, 0, flags, vd); 729 if (error) { 730 free(vd, type); 731 return (NULL); 732 } 733 vd->vd_next = sc->sc_dmas; 734 sc->sc_dmas = vd; 735 736 return (KVADDR(vd)); 737 } 738 739 static void 740 vs_freem(void *hdl, void *addr, struct malloc_type *type) 741 { 742 struct vs_softc *sc = hdl; 743 struct vs_dma *p, **pp; 744 745 for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->vd_next) { 746 if (KVADDR(p) == addr) { 747 vs_freemem(p); 748 *pp = p->vd_next; 749 free(p, type); 750 return; 751 } 752 } 753 } 754 755 static size_t 756 vs_round_buffersize(void *hdl, int direction, size_t bufsize) 757 { 758 if (bufsize > DMAC_MAXSEGSZ) 759 bufsize = DMAC_MAXSEGSZ; 760 761 return bufsize; 762 } 763 764 #if 0 765 paddr_t 766 vs_mappage(void *addr, void *mem, off_t off, int prot) 767 { 768 struct vs_softc *sc = addr; 769 struct vs_dma *p; 770 771 if (off < 0) 772 return (-1); 773 for (p = sc->sc_dmas; p != NULL && KVADDR(p) != mem; 774 p = p->vd_next) 775 ; 776 if (p == NULL) { 777 printf("%s: mappage: bad addr %p\n", 778 sc->sc_dev.dv_xname, start); 779 return (-1); 780 } 781 782 return (bus_dmamem_mmap(sc->sc_dmat, p->vd_segs, p->vd_nsegs, 783 off, prot, BUS_DMA_WAITOK)); 784 } 785 #endif 786 787 static int 788 vs_get_props(void *hdl) 789 { 790 DPRINTF(1, ("vs_get_props\n")); 791 return 0 /* | dependent | half duplex | no mmap */; 792 } 793 #endif /* NAUDIO > 0 && NVS > 0*/ 794