1 /* $NetBSD: vs.c,v 1.52 2019/06/08 08:02:37 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 * 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.52 2019/06/08 08:02:37 isaki 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/audio_if.h> 46 47 #include <machine/bus.h> 48 #include <machine/cpu.h> 49 50 #include <dev/ic/msm6258var.h> 51 52 #include <arch/x68k/dev/dmacvar.h> 53 #include <arch/x68k/dev/intiovar.h> 54 #include <arch/x68k/dev/opmvar.h> 55 56 #include <arch/x68k/dev/vsvar.h> 57 58 #ifdef VS_DEBUG 59 #define DPRINTF(y,x) if (vs_debug >= (y)) printf x 60 static int vs_debug; 61 #ifdef AUDIO_DEBUG 62 extern int audiodebug; 63 #endif 64 #else 65 #define DPRINTF(y,x) 66 #endif 67 68 static int vs_match(device_t, cfdata_t, void *); 69 static void vs_attach(device_t, device_t, void *); 70 71 static int vs_dmaintr(void *); 72 static int vs_dmaerrintr(void *); 73 74 /* MI audio layer interface */ 75 static int vs_open(void *, int); 76 static void vs_close(void *); 77 static int vs_query_format(void *, audio_format_query_t *); 78 static int vs_set_format(void *, int, 79 const audio_params_t *, const audio_params_t *, 80 audio_filter_reg_t *, audio_filter_reg_t *); 81 static int vs_commit_settings(void *); 82 static int vs_start_input(void *, void *, int, void (*)(void *), void *); 83 static int vs_start_output(void *, void *, int, void (*)(void *), void *); 84 static int vs_halt_output(void *); 85 static int vs_halt_input(void *); 86 static int vs_allocmem(struct vs_softc *, size_t, size_t, size_t, 87 struct vs_dma *); 88 static void vs_freemem(struct vs_dma *); 89 static int vs_getdev(void *, struct audio_device *); 90 static int vs_set_port(void *, mixer_ctrl_t *); 91 static int vs_get_port(void *, mixer_ctrl_t *); 92 static int vs_query_devinfo(void *, mixer_devinfo_t *); 93 static void *vs_allocm(void *, int, size_t); 94 static void vs_freem(void *, void *, size_t); 95 static size_t vs_round_buffersize(void *, int, size_t); 96 static int vs_get_props(void *); 97 static void vs_get_locks(void *, kmutex_t **, kmutex_t **); 98 99 /* lower functions */ 100 static int vs_round_sr(u_long); 101 static inline void vs_set_panout(struct vs_softc *, u_long); 102 103 extern struct cfdriver vs_cd; 104 105 CFATTACH_DECL_NEW(vs, sizeof(struct vs_softc), 106 vs_match, vs_attach, NULL, NULL); 107 108 static int vs_attached; 109 110 static const struct audio_hw_if vs_hw_if = { 111 .open = vs_open, 112 .close = vs_close, 113 .query_format = vs_query_format, 114 .set_format = vs_set_format, 115 .commit_settings = vs_commit_settings, 116 .start_output = vs_start_output, 117 .start_input = vs_start_input, 118 .halt_output = vs_halt_output, 119 .halt_input = vs_halt_input, 120 .getdev = vs_getdev, 121 .set_port = vs_set_port, 122 .get_port = vs_get_port, 123 .query_devinfo = vs_query_devinfo, 124 .allocm = vs_allocm, 125 .freem = vs_freem, 126 .round_buffersize = vs_round_buffersize, 127 .get_props = vs_get_props, 128 .get_locks = vs_get_locks, 129 }; 130 131 static struct audio_device vs_device = { 132 "OKI MSM6258", 133 "", 134 "vs" 135 }; 136 137 static const struct audio_format vs_formats = { 138 .mode = AUMODE_PLAY | AUMODE_RECORD, 139 .encoding = AUDIO_ENCODING_ADPCM, 140 .validbits = 4, 141 .precision = 4, 142 .channels = 1, 143 .channel_mask = AUFMT_MONAURAL, 144 .frequency_type = 5, 145 .frequency = { VS_RATE_3K, VS_RATE_5K, VS_RATE_7K, 146 VS_RATE_10K, VS_RATE_15K }, 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 static int 164 vs_match(device_t parent, cfdata_t cf, void *aux) 165 { 166 struct intio_attach_args *ia; 167 168 ia = aux; 169 if (strcmp(ia->ia_name, "vs") || vs_attached) 170 return 0; 171 172 if (ia->ia_addr == INTIOCF_ADDR_DEFAULT) 173 ia->ia_addr = VS_ADDR; 174 if (ia->ia_dma == INTIOCF_DMA_DEFAULT) 175 ia->ia_dma = VS_DMA; 176 if (ia->ia_dmaintr == INTIOCF_DMAINTR_DEFAULT) 177 ia->ia_dmaintr = VS_DMAINTR; 178 179 /* fixed parameters */ 180 if (ia->ia_addr != VS_ADDR) 181 return 0; 182 if (ia->ia_dma != VS_DMA) 183 return 0; 184 if (ia->ia_dmaintr != VS_DMAINTR) 185 return 0; 186 187 #ifdef VS_DEBUG 188 vs_debug = 1; 189 #ifdef AUDIO_DEBUG 190 audiodebug = 2; 191 #endif 192 #endif 193 194 return 1; 195 } 196 197 static void 198 vs_attach(device_t parent, device_t self, void *aux) 199 { 200 struct vs_softc *sc; 201 bus_space_tag_t iot; 202 bus_space_handle_t ioh; 203 struct intio_attach_args *ia; 204 205 sc = device_private(self); 206 sc->sc_dev = self; 207 ia = aux; 208 vs_attached = 1; 209 210 printf("\n"); 211 212 /* Re-map the I/O space */ 213 iot = ia->ia_bst; 214 bus_space_map(iot, ia->ia_addr, 0x2000, BUS_SPACE_MAP_SHIFTED, &ioh); 215 216 /* Initialize sc */ 217 sc->sc_iot = iot; 218 sc->sc_ioh = ioh; 219 sc->sc_hw_if = &vs_hw_if; 220 sc->sc_addr = (void *) ia->ia_addr; 221 sc->sc_dmas = NULL; 222 sc->sc_prev_vd = NULL; 223 sc->sc_active = 0; 224 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 225 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM); 226 227 /* XXX */ 228 bus_space_map(iot, PPI_ADDR, PPI_MAPSIZE, BUS_SPACE_MAP_SHIFTED, 229 &sc->sc_ppi); 230 231 /* Initialize DMAC */ 232 sc->sc_dmat = ia->ia_dmat; 233 sc->sc_dma_ch = dmac_alloc_channel(parent, ia->ia_dma, "vs", 234 ia->ia_dmaintr, vs_dmaintr, sc, 235 ia->ia_dmaintr+1, vs_dmaerrintr, sc, 236 (DMAC_DCR_XRM_CSWOH | DMAC_DCR_OTYP_EASYNC | DMAC_DCR_OPS_8BIT), 237 (DMAC_OCR_SIZE_BYTE | DMAC_OCR_REQG_EXTERNAL)); 238 239 aprint_normal_dev(self, "MSM6258V ADPCM voice synthesizer\n"); 240 241 audio_attach_mi(&vs_hw_if, sc, sc->sc_dev); 242 } 243 244 /* 245 * vs interrupt handler 246 */ 247 static int 248 vs_dmaintr(void *hdl) 249 { 250 struct vs_softc *sc; 251 252 DPRINTF(2, ("vs_dmaintr\n")); 253 sc = hdl; 254 255 mutex_spin_enter(&sc->sc_intr_lock); 256 257 if (sc->sc_pintr) { 258 sc->sc_pintr(sc->sc_parg); 259 } else if (sc->sc_rintr) { 260 sc->sc_rintr(sc->sc_rarg); 261 } else { 262 printf("vs_dmaintr: spurious interrupt\n"); 263 } 264 265 mutex_spin_exit(&sc->sc_intr_lock); 266 267 return 1; 268 } 269 270 static int 271 vs_dmaerrintr(void *hdl) 272 { 273 struct vs_softc *sc; 274 275 sc = hdl; 276 DPRINTF(1, ("%s: DMA transfer error.\n", device_xname(sc->sc_dev))); 277 /* XXX */ 278 vs_dmaintr(sc); 279 280 return 1; 281 } 282 283 284 /* 285 * audio MD layer interfaces 286 */ 287 288 static int 289 vs_open(void *hdl, int flags) 290 { 291 struct vs_softc *sc; 292 293 DPRINTF(1, ("vs_open: flags=%d\n", flags)); 294 sc = hdl; 295 sc->sc_pintr = NULL; 296 sc->sc_rintr = NULL; 297 sc->sc_active = 0; 298 299 return 0; 300 } 301 302 static void 303 vs_close(void *hdl) 304 { 305 306 DPRINTF(1, ("vs_close\n")); 307 } 308 309 static int 310 vs_query_format(void *hdl, audio_format_query_t *afp) 311 { 312 313 return audio_query_format(&vs_formats, 1, afp); 314 } 315 316 static int 317 vs_round_sr(u_long rate) 318 { 319 int i; 320 321 for (i = 0; i < NUM_RATE; i++) { 322 if (rate == vs_l2r[i].rate) 323 return i; 324 } 325 return -1; 326 } 327 328 static int 329 vs_set_format(void *hdl, int setmode, 330 const audio_params_t *play, const audio_params_t *rec, 331 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) 332 { 333 struct vs_softc *sc; 334 int rate; 335 336 sc = hdl; 337 338 DPRINTF(1, ("%s: mode=%d %s/%dbit/%dch/%dHz: ", __func__, 339 setmode, audio_encoding_name(play->encoding), 340 play->precision, play->channels, play->sample_rate)); 341 342 /* *play and *rec are identical because !AUDIO_PROP_INDEPENDENT */ 343 344 rate = vs_round_sr(play->sample_rate); 345 KASSERT(rate >= 0); 346 sc->sc_current.rate = rate; 347 348 if ((setmode & AUMODE_PLAY) != 0) { 349 pfil->codec = msm6258_internal_to_adpcm; 350 pfil->context = &sc->sc_codecvar; 351 } 352 if ((setmode & AUMODE_RECORD) != 0) { 353 rfil->codec = msm6258_adpcm_to_internal; 354 rfil->context = &sc->sc_codecvar; 355 } 356 357 DPRINTF(1, ("accepted\n")); 358 return 0; 359 } 360 361 static int 362 vs_commit_settings(void *hdl) 363 { 364 struct vs_softc *sc; 365 int rate; 366 367 sc = hdl; 368 rate = sc->sc_current.rate; 369 370 DPRINTF(1, ("commit_settings: sample rate to %d, %d\n", 371 rate, (int)vs_l2r[rate].rate)); 372 bus_space_write_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC, 373 (bus_space_read_1 (sc->sc_iot, sc->sc_ppi, 374 PPI_PORTC) & 0xf0) 375 | vs_l2r[rate].den); 376 adpcm_chgclk(vs_l2r[rate].clk); 377 378 return 0; 379 } 380 381 static inline void 382 vs_set_panout(struct vs_softc *sc, u_long po) 383 { 384 385 bus_space_write_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC, 386 (bus_space_read_1(sc->sc_iot, sc->sc_ppi, PPI_PORTC) 387 & 0xfc) | po); 388 } 389 390 static int 391 vs_start_output(void *hdl, void *block, int blksize, void (*intr)(void *), 392 void *arg) 393 { 394 struct vs_softc *sc; 395 struct vs_dma *vd; 396 struct dmac_channel_stat *chan; 397 398 DPRINTF(2, ("%s: block=%p blksize=%d\n", __func__, block, blksize)); 399 sc = hdl; 400 401 sc->sc_pintr = intr; 402 sc->sc_parg = arg; 403 404 /* Find DMA buffer. */ 405 for (vd = sc->sc_dmas; vd != NULL; vd = vd->vd_next) { 406 if (KVADDR(vd) <= block && block < KVADDR_END(vd) 407 break; 408 } 409 if (vd == NULL) { 410 printf("%s: start_output: bad addr %p\n", 411 device_xname(sc->sc_dev), block); 412 return EINVAL; 413 } 414 415 chan = sc->sc_dma_ch; 416 417 if (vd != sc->sc_prev_vd) { 418 sc->sc_current.xfer = dmac_prepare_xfer(chan, sc->sc_dmat, 419 vd->vd_map, DMAC_OCR_DIR_MTD, 420 (DMAC_SCR_MAC_COUNT_UP | DMAC_SCR_DAC_NO_COUNT), 421 sc->sc_addr + MSM6258_DATA * 2 + 1); 422 sc->sc_prev_vd = vd; 423 } 424 dmac_start_xfer_offset(chan->ch_softc, sc->sc_current.xfer, 425 (int)block - (int)KVADDR(vd), blksize); 426 427 if (sc->sc_active == 0) { 428 vs_set_panout(sc, VS_PANOUT_LR); 429 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 430 MSM6258_CMD, MSM6258_CMD_PLAY_START); 431 sc->sc_active = 1; 432 } 433 434 return 0; 435 } 436 437 static int 438 vs_start_input(void *hdl, void *block, int blksize, void (*intr)(void *), 439 void *arg) 440 { 441 struct vs_softc *sc; 442 struct vs_dma *vd; 443 struct dmac_channel_stat *chan; 444 445 DPRINTF(2, ("%s: block=%p blksize=%d\n", __func__, block, blksize)); 446 sc = hdl; 447 448 sc->sc_rintr = intr; 449 sc->sc_rarg = arg; 450 451 /* Find DMA buffer. */ 452 for (vd = sc->sc_dmas; vd != NULL; vd = vd->vd_next) { 453 if (KVADDR(vd) <= block && block < KVADDR_END(vd) 454 break; 455 } 456 if (vd == NULL) { 457 printf("%s: start_output: bad addr %p\n", 458 device_xname(sc->sc_dev), block); 459 return EINVAL; 460 } 461 462 chan = sc->sc_dma_ch; 463 464 if (vd != sc->sc_prev_vd) { 465 sc->sc_current.xfer = dmac_prepare_xfer(chan, sc->sc_dmat, 466 vd->vd_map, DMAC_OCR_DIR_DTM, 467 (DMAC_SCR_MAC_COUNT_UP | DMAC_SCR_DAC_NO_COUNT), 468 sc->sc_addr + MSM6258_DATA * 2 + 1); 469 sc->sc_prev_vd = vd; 470 } 471 dmac_start_xfer_offset(chan->ch_softc, sc->sc_current.xfer, 472 (int)block - (int)KVADDR(vd), blksize); 473 474 if (sc->sc_active == 0) { 475 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 476 MSM6258_CMD, MSM6258_CMD_REC_START); 477 sc->sc_active = 1; 478 } 479 480 return 0; 481 } 482 483 static int 484 vs_halt_output(void *hdl) 485 { 486 struct vs_softc *sc; 487 488 DPRINTF(1, ("vs_halt_output\n")); 489 sc = hdl; 490 if (sc->sc_active) { 491 /* stop ADPCM play */ 492 dmac_abort_xfer(sc->sc_dma_ch->ch_softc, sc->sc_current.xfer); 493 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 494 MSM6258_CMD, MSM6258_CMD_STOP); 495 sc->sc_active = 0; 496 } 497 498 return 0; 499 } 500 501 static int 502 vs_halt_input(void *hdl) 503 { 504 struct vs_softc *sc; 505 506 DPRINTF(1, ("vs_halt_input\n")); 507 sc = hdl; 508 if (sc->sc_active) { 509 /* stop ADPCM recoding */ 510 dmac_abort_xfer(sc->sc_dma_ch->ch_softc, sc->sc_current.xfer); 511 bus_space_write_1(sc->sc_iot, sc->sc_ioh, 512 MSM6258_CMD, MSM6258_CMD_STOP); 513 sc->sc_active = 0; 514 } 515 516 return 0; 517 } 518 519 static int 520 vs_allocmem(struct vs_softc *sc, size_t size, size_t align, size_t boundary, 521 struct vs_dma *vd) 522 { 523 int error; 524 525 #ifdef DIAGNOSTIC 526 if (size > DMAC_MAXSEGSZ) 527 panic ("vs_allocmem: maximum size exceeded, %d", (int) size); 528 #endif 529 530 vd->vd_size = size; 531 532 error = bus_dmamem_alloc(vd->vd_dmat, vd->vd_size, align, boundary, 533 vd->vd_segs, 534 sizeof (vd->vd_segs) / sizeof (vd->vd_segs[0]), 535 &vd->vd_nsegs, BUS_DMA_WAITOK); 536 if (error) 537 goto out; 538 539 error = bus_dmamem_map(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs, 540 vd->vd_size, &vd->vd_addr, 541 BUS_DMA_WAITOK | BUS_DMA_COHERENT); 542 if (error) 543 goto free; 544 545 error = bus_dmamap_create(vd->vd_dmat, vd->vd_size, 1, DMAC_MAXSEGSZ, 546 0, BUS_DMA_WAITOK, &vd->vd_map); 547 if (error) 548 goto unmap; 549 550 error = bus_dmamap_load(vd->vd_dmat, vd->vd_map, vd->vd_addr, 551 vd->vd_size, NULL, BUS_DMA_WAITOK); 552 if (error) 553 goto destroy; 554 555 return 0; 556 557 destroy: 558 bus_dmamap_destroy(vd->vd_dmat, vd->vd_map); 559 unmap: 560 bus_dmamem_unmap(vd->vd_dmat, vd->vd_addr, vd->vd_size); 561 free: 562 bus_dmamem_free(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs); 563 out: 564 return error; 565 } 566 567 static void 568 vs_freemem(struct vs_dma *vd) 569 { 570 571 bus_dmamap_unload(vd->vd_dmat, vd->vd_map); 572 bus_dmamap_destroy(vd->vd_dmat, vd->vd_map); 573 bus_dmamem_unmap(vd->vd_dmat, vd->vd_addr, vd->vd_size); 574 bus_dmamem_free(vd->vd_dmat, vd->vd_segs, vd->vd_nsegs); 575 } 576 577 static int 578 vs_getdev(void *hdl, struct audio_device *retp) 579 { 580 581 DPRINTF(1, ("vs_getdev\n")); 582 *retp = vs_device; 583 return 0; 584 } 585 586 static int 587 vs_set_port(void *hdl, mixer_ctrl_t *cp) 588 { 589 590 DPRINTF(1, ("vs_set_port\n")); 591 return 0; 592 } 593 594 static int 595 vs_get_port(void *hdl, mixer_ctrl_t *cp) 596 { 597 598 DPRINTF(1, ("vs_get_port\n")); 599 return 0; 600 } 601 602 static int 603 vs_query_devinfo(void *hdl, mixer_devinfo_t *mi) 604 { 605 606 DPRINTF(1, ("vs_query_devinfo\n")); 607 switch (mi->index) { 608 default: 609 return EINVAL; 610 } 611 return 0; 612 } 613 614 static void * 615 vs_allocm(void *hdl, int direction, size_t size) 616 { 617 struct vs_softc *sc; 618 struct vs_dma *vd; 619 int error; 620 621 vd = kmem_alloc(sizeof(*vd), KM_SLEEP); 622 sc = hdl; 623 vd->vd_dmat = sc->sc_dmat; 624 625 error = vs_allocmem(sc, size, 32, 0, vd); 626 if (error) { 627 kmem_free(vd, sizeof(*vd)); 628 return NULL; 629 } 630 vd->vd_next = sc->sc_dmas; 631 sc->sc_dmas = vd; 632 633 return KVADDR(vd); 634 } 635 636 static void 637 vs_freem(void *hdl, void *addr, size_t size) 638 { 639 struct vs_softc *sc; 640 struct vs_dma *p, **pp; 641 642 sc = hdl; 643 for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->vd_next) { 644 if (KVADDR(p) == addr) { 645 vs_freemem(p); 646 *pp = p->vd_next; 647 kmem_free(p, sizeof(*p)); 648 return; 649 } 650 } 651 } 652 653 static size_t 654 vs_round_buffersize(void *hdl, int direction, size_t bufsize) 655 { 656 657 if (bufsize > DMAC_MAXSEGSZ) 658 bufsize = DMAC_MAXSEGSZ; 659 return bufsize; 660 } 661 662 static int 663 vs_get_props(void *hdl) 664 { 665 666 DPRINTF(1, ("vs_get_props\n")); 667 return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE; 668 } 669 670 static void 671 vs_get_locks(void *hdl, kmutex_t **intr, kmutex_t **thread) 672 { 673 struct vs_softc *sc; 674 675 DPRINTF(1, ("vs_get_locks\n")); 676 sc = hdl; 677 *intr = &sc->sc_intr_lock; 678 *thread = &sc->sc_lock; 679 } 680 681 #endif /* NAUDIO > 0 && NVS > 0*/ 682