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