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