1 /* $NetBSD: vidcaudio.c,v 1.46 2008/03/01 16:17:47 chris Exp $ */ 2 3 /* 4 * Copyright (c) 1995 Melvin Tang-Richardson 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. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by the RiscBSD team. 17 * 4. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /*- 33 * Copyright (c) 2003 Ben Harris 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. The name of the author may not be used to endorse or promote products 45 * derived from this software without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 48 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 49 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 50 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 51 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 52 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 53 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 54 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 55 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 56 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 */ 58 59 /* 60 * audio driver for the RiscPC 8/16 bit sound 61 * 62 * Interfaces with the NetBSD generic audio driver to provide SUN 63 * /dev/audio (partial) compatibility. 64 */ 65 66 #include <sys/param.h> /* proc.h */ 67 68 __KERNEL_RCSID(0, "$NetBSD: vidcaudio.c,v 1.46 2008/03/01 16:17:47 chris Exp $"); 69 70 #include <sys/audioio.h> 71 #include <sys/conf.h> /* autoconfig functions */ 72 #include <sys/device.h> /* device calls */ 73 #include <sys/errno.h> 74 #include <sys/malloc.h> 75 #include <sys/proc.h> /* device calls */ 76 #include <sys/systm.h> 77 78 #include <uvm/uvm_extern.h> 79 80 #include <dev/audio_if.h> 81 #include <dev/audiobellvar.h> 82 #include <dev/auconv.h> 83 #include <dev/mulaw.h> 84 85 #include <machine/intr.h> 86 #include <machine/machdep.h> 87 #include <arm/arm32/katelib.h> 88 89 #include <arm/iomd/vidcaudiovar.h> 90 #include <arm/iomd/iomdreg.h> 91 #include <arm/iomd/iomdvar.h> 92 #include <arm/iomd/vidc.h> 93 #include <arm/mainbus/mainbus.h> 94 95 #include "pckbd.h" 96 #if NPCKBD > 0 97 #include <dev/pckbport/pckbdvar.h> 98 #endif 99 100 extern int *vidc_base; 101 102 #ifdef VIDCAUDIO_DEBUG 103 #define DPRINTF(x) printf x 104 #else 105 #define DPRINTF(x) 106 #endif 107 108 struct vidcaudio_softc { 109 struct device sc_dev; 110 111 irqhandler_t sc_ih; 112 int sc_dma_intr; 113 114 int sc_is16bit; 115 116 size_t sc_pblksize; 117 vm_offset_t sc_poffset; 118 vm_offset_t sc_pbufsize; 119 paddr_t *sc_ppages; 120 void (*sc_pintr)(void *); 121 void *sc_parg; 122 int sc_pcountdown; 123 }; 124 125 static int vidcaudio_probe(struct device *, struct cfdata *, void *); 126 static void vidcaudio_attach(struct device *, struct device *, void *); 127 static void vidcaudio_close(void *); 128 129 static int vidcaudio_intr(void *); 130 static void vidcaudio_rate(int); 131 static void vidcaudio_ctrl(int); 132 static void vidcaudio_stereo(int, int); 133 static stream_filter_factory_t mulaw_to_vidc; 134 static stream_filter_factory_t mulaw_to_vidc_stereo; 135 static int mulaw_to_vidc_fetch_to(stream_fetcher_t *, audio_stream_t *, int); 136 static int mulaw_to_vidc_stereo_fetch_to(stream_fetcher_t *, 137 audio_stream_t *, int); 138 139 CFATTACH_DECL(vidcaudio, sizeof(struct vidcaudio_softc), 140 vidcaudio_probe, vidcaudio_attach, NULL, NULL); 141 142 static int vidcaudio_query_encoding(void *, struct audio_encoding *); 143 static int vidcaudio_set_params(void *, int, int, audio_params_t *, 144 audio_params_t *, stream_filter_list_t *, stream_filter_list_t *); 145 static int vidcaudio_round_blocksize(void *, int, int, const audio_params_t *); 146 static int vidcaudio_trigger_output(void *, void *, void *, int, 147 void (*)(void *), void *, const audio_params_t *); 148 static int vidcaudio_trigger_input(void *, void *, void *, int, 149 void (*)(void *), void *, const audio_params_t *); 150 static int vidcaudio_halt_output(void *); 151 static int vidcaudio_halt_input(void *); 152 static int vidcaudio_getdev(void *, struct audio_device *); 153 static int vidcaudio_set_port(void *, mixer_ctrl_t *); 154 static int vidcaudio_get_port(void *, mixer_ctrl_t *); 155 static int vidcaudio_query_devinfo(void *, mixer_devinfo_t *); 156 static int vidcaudio_get_props(void *); 157 158 static struct audio_device vidcaudio_device = { 159 "ARM VIDC", 160 "", 161 "vidcaudio" 162 }; 163 164 static const struct audio_hw_if vidcaudio_hw_if = { 165 NULL, /* open */ 166 vidcaudio_close, 167 NULL, 168 vidcaudio_query_encoding, 169 vidcaudio_set_params, 170 vidcaudio_round_blocksize, 171 NULL, 172 NULL, 173 NULL, 174 NULL, 175 NULL, 176 vidcaudio_halt_output, 177 vidcaudio_halt_input, 178 NULL, 179 vidcaudio_getdev, 180 NULL, 181 vidcaudio_set_port, 182 vidcaudio_get_port, 183 vidcaudio_query_devinfo, 184 NULL, 185 NULL, 186 NULL, 187 NULL, 188 vidcaudio_get_props, 189 vidcaudio_trigger_output, 190 vidcaudio_trigger_input, 191 NULL, 192 }; 193 194 static int 195 vidcaudio_probe(struct device *parent, struct cfdata *cf, void *aux) 196 { 197 int id; 198 199 id = IOMD_ID; 200 201 /* So far I only know about this IOMD */ 202 switch (id) { 203 case RPC600_IOMD_ID: 204 case ARM7500_IOC_ID: 205 case ARM7500FE_IOC_ID: 206 return 1; 207 default: 208 aprint_error("vidcaudio: Unknown IOMD id=%04x", id); 209 return 0; 210 } 211 } 212 213 214 static void 215 vidcaudio_attach(struct device *parent, struct device *self, void *aux) 216 { 217 struct vidcaudio_softc *sc; 218 struct device *beepdev; 219 220 sc = (void *)self; 221 switch (IOMD_ID) { 222 #ifndef EB7500ATX 223 case RPC600_IOMD_ID: 224 sc->sc_is16bit = (cmos_read(0xc4) >> 5) & 1; 225 sc->sc_dma_intr = IRQ_DMASCH0; 226 break; 227 #endif 228 case ARM7500_IOC_ID: 229 case ARM7500FE_IOC_ID: 230 sc->sc_is16bit = true; 231 sc->sc_dma_intr = IRQ_SDMA; 232 break; 233 default: 234 aprint_error(": strange IOMD\n"); 235 return; 236 } 237 238 if (sc->sc_is16bit) 239 aprint_normal(": 16-bit external DAC\n"); 240 else 241 aprint_normal(": 8-bit internal DAC\n"); 242 243 /* Install the irq handler for the DMA interrupt */ 244 sc->sc_ih.ih_func = vidcaudio_intr; 245 sc->sc_ih.ih_arg = sc; 246 sc->sc_ih.ih_level = IPL_AUDIO; 247 sc->sc_ih.ih_name = self->dv_xname; 248 249 if (irq_claim(sc->sc_dma_intr, &sc->sc_ih) != 0) { 250 aprint_error("%s: couldn't claim IRQ %d\n", 251 self->dv_xname, sc->sc_dma_intr); 252 return; 253 } 254 255 disable_irq(sc->sc_dma_intr); 256 257 beepdev = audio_attach_mi(&vidcaudio_hw_if, sc, self); 258 #if NPCKBD > 0 259 pckbd_hookup_bell(audiobell, beepdev); 260 #endif 261 } 262 263 static void 264 vidcaudio_close(void *addr) 265 { 266 struct vidcaudio_softc *sc; 267 268 DPRINTF(("DEBUG: vidcaudio_close called\n")); 269 sc = addr; 270 /* 271 * We do this here rather than in vidcaudio_halt_output() 272 * because the latter can be called from interrupt context 273 * (audio_pint()->audio_clear()->vidcaudio_halt_output()). 274 */ 275 if (sc->sc_ppages != NULL) { 276 free(sc->sc_ppages, M_DEVBUF); 277 sc->sc_ppages = NULL; 278 } 279 } 280 281 /* 282 * Interface to the generic audio driver 283 */ 284 285 static int 286 vidcaudio_query_encoding(void *addr, struct audio_encoding *fp) 287 { 288 struct vidcaudio_softc *sc; 289 290 sc = addr; 291 switch (fp->index) { 292 case 0: 293 strcpy(fp->name, AudioEmulaw); 294 fp->encoding = AUDIO_ENCODING_ULAW; 295 fp->precision = 8; 296 fp->flags = AUDIO_ENCODINGFLAG_EMULATED; 297 break; 298 299 case 1: 300 if (sc->sc_is16bit) { 301 strcpy(fp->name, AudioEslinear_le); 302 fp->encoding = AUDIO_ENCODING_SLINEAR_LE; 303 fp->precision = 16; 304 fp->flags = 0; 305 break; 306 } 307 /* FALLTHROUGH */ 308 default: 309 return EINVAL; 310 } 311 return 0; 312 } 313 314 #define MULAW_TO_VIDC(m) (~((m) << 1 | (m) >> 7)) 315 316 static stream_filter_t * 317 mulaw_to_vidc(struct audio_softc *sc, const audio_params_t *from, 318 const audio_params_t *to) 319 { 320 321 return auconv_nocontext_filter_factory(mulaw_to_vidc_fetch_to); 322 } 323 324 static int 325 mulaw_to_vidc_fetch_to(stream_fetcher_t *self, audio_stream_t *dst, int max_used) 326 { 327 stream_filter_t *this; 328 int m, err; 329 330 this = (stream_filter_t *)self; 331 if ((err = this->prev->fetch_to(this->prev, this->src, max_used))) 332 return err; 333 m = dst->end - dst->start; 334 m = min(m, max_used); 335 FILTER_LOOP_PROLOGUE(this->src, 1, dst, 1, m) { 336 *d = MULAW_TO_VIDC(*s); 337 } FILTER_LOOP_EPILOGUE(this->src, dst); 338 return 0; 339 } 340 341 static stream_filter_t * 342 mulaw_to_vidc_stereo(struct audio_softc *sc, const audio_params_t *from, 343 const audio_params_t *to) 344 { 345 346 return auconv_nocontext_filter_factory(mulaw_to_vidc_stereo_fetch_to); 347 } 348 349 static int 350 mulaw_to_vidc_stereo_fetch_to(stream_fetcher_t *self, audio_stream_t *dst, 351 int max_used) 352 { 353 stream_filter_t *this; 354 int m, err; 355 356 this = (stream_filter_t *)self; 357 max_used = (max_used + 1) & ~1; 358 if ((err = this->prev->fetch_to(this->prev, this->src, max_used / 2))) 359 return err; 360 m = (dst->end - dst->start) & ~1; 361 m = min(m, max_used); 362 FILTER_LOOP_PROLOGUE(this->src, 1, dst, 2, m) { 363 d[0] = d[1] = MULAW_TO_VIDC(*s); 364 } FILTER_LOOP_EPILOGUE(this->src, dst); 365 return 0; 366 } 367 368 static int 369 vidcaudio_set_params(void *addr, int setmode, int usemode, 370 audio_params_t *p, audio_params_t *r, 371 stream_filter_list_t *pfil, stream_filter_list_t *rfil) 372 { 373 audio_params_t hw; 374 struct vidcaudio_softc *sc; 375 int sample_period, ch; 376 377 if ((setmode & AUMODE_PLAY) == 0) 378 return 0; 379 380 sc = addr; 381 if (sc->sc_is16bit) { 382 /* ARM7500ish, 16-bit, two-channel */ 383 hw = *p; 384 if (p->encoding == AUDIO_ENCODING_ULAW && p->precision == 8) { 385 hw.encoding = AUDIO_ENCODING_SLINEAR_LE; 386 hw.precision = hw.validbits = 16; 387 pfil->append(pfil, mulaw_to_linear16, &hw); 388 } else if (p->encoding != AUDIO_ENCODING_SLINEAR_LE || 389 p->precision != 16) 390 return EINVAL; 391 sample_period = 705600 / 4 / p->sample_rate; 392 if (sample_period < 3) sample_period = 3; 393 vidcaudio_rate(sample_period - 2); 394 vidcaudio_ctrl(SCR_SERIAL); 395 hw.sample_rate = 705600 / 4 / sample_period; 396 hw.channels = 2; 397 pfil->prepend(pfil, aurateconv, &hw); 398 } else { 399 /* VIDC20ish, u-law, 8-channel */ 400 if (p->encoding != AUDIO_ENCODING_ULAW || p->precision != 8) 401 return EINVAL; 402 /* 403 * We always use two hardware channels, because using 404 * one at 8kHz gives a nasty whining sound from the 405 * speaker. The aurateconv mechanism doesn't support 406 * ulaw, so we do the channel duplication ourselves, 407 * and don't try to do rate conversion. 408 */ 409 sample_period = 1000000 / 2 / p->sample_rate; 410 if (sample_period < 3) sample_period = 3; 411 p->sample_rate = 1000000 / 2 / sample_period; 412 hw = *p; 413 hw.encoding = AUDIO_ENCODING_NONE; 414 hw.precision = 8; 415 vidcaudio_rate(sample_period - 2); 416 vidcaudio_ctrl(SCR_SDAC | SCR_CLKSEL); 417 if (p->channels == 1) { 418 pfil->append(pfil, mulaw_to_vidc_stereo, &hw); 419 for (ch = 0; ch < 8; ch++) 420 vidcaudio_stereo(ch, SIR_CENTRE); 421 } else { 422 pfil->append(pfil, mulaw_to_vidc, &hw); 423 for (ch = 0; ch < 8; ch += 2) 424 vidcaudio_stereo(ch, SIR_LEFT_100); 425 for (ch = 1; ch < 8; ch += 2) 426 vidcaudio_stereo(ch, SIR_RIGHT_100); 427 } 428 } 429 return 0; 430 } 431 432 static int 433 vidcaudio_round_blocksize(void *addr, int wantblk, 434 int mode, const audio_params_t *param) 435 { 436 int blk; 437 438 /* 439 * Find the smallest power of two that's larger than the 440 * requested block size, but don't allow < 32 (DMA burst is 16 441 * bytes, and single bursts are tricky) or > PAGE_SIZE (DMA is 442 * confined to a page). 443 */ 444 445 for (blk = 32; blk < PAGE_SIZE; blk <<= 1) 446 if (blk >= wantblk) 447 return blk; 448 return blk; 449 } 450 451 static int 452 vidcaudio_trigger_output(void *addr, void *start, void *end, int blksize, 453 void (*intr)(void *), void *arg, const audio_params_t *params) 454 { 455 struct vidcaudio_softc *sc; 456 size_t npages, i; 457 458 DPRINTF(("vidcaudio_trigger_output %p-%p/0x%x\n", 459 start, end, blksize)); 460 461 sc = addr; 462 KASSERT(blksize == vidcaudio_round_blocksize(addr, blksize, 0, NULL)); 463 KASSERT((vaddr_t)start % blksize == 0); 464 465 sc->sc_pblksize = blksize; 466 sc->sc_pbufsize = (char *)end - (char *)start; 467 npages = sc->sc_pbufsize >> PGSHIFT; 468 if (sc->sc_ppages != NULL) 469 free(sc->sc_ppages, M_DEVBUF); 470 sc->sc_ppages = malloc(npages * sizeof(paddr_t), M_DEVBUF, M_WAITOK); 471 if (sc->sc_ppages == NULL) return ENOMEM; 472 for (i = 0; i < npages; i++) 473 if (!pmap_extract(pmap_kernel(), 474 (vaddr_t)start + i * PAGE_SIZE, &sc->sc_ppages[i])) 475 return EIO; 476 sc->sc_poffset = 0; 477 sc->sc_pintr = intr; 478 sc->sc_parg = arg; 479 480 IOMD_WRITE_WORD(IOMD_SD0CR, IOMD_DMACR_CLEAR | IOMD_DMACR_QUADWORD); 481 IOMD_WRITE_WORD(IOMD_SD0CR, IOMD_DMACR_ENABLE | IOMD_DMACR_QUADWORD); 482 483 /* 484 * Queue up the first two blocks, but don't tell audio code 485 * we're finished with them yet. 486 */ 487 sc->sc_pcountdown = 2; 488 489 enable_irq(sc->sc_dma_intr); 490 491 return 0; 492 } 493 494 static int 495 vidcaudio_trigger_input(void *addr, void *start, void *end, int blksize, 496 void (*intr)(void *), void *arg, const audio_params_t *params) 497 { 498 499 return ENODEV; 500 } 501 502 static int 503 vidcaudio_halt_output(void *addr) 504 { 505 struct vidcaudio_softc *sc; 506 507 DPRINTF(("vidcaudio_halt_output\n")); 508 sc = addr; 509 disable_irq(sc->sc_dma_intr); 510 IOMD_WRITE_WORD(IOMD_SD0CR, IOMD_DMACR_CLEAR | IOMD_DMACR_QUADWORD); 511 return 0; 512 } 513 514 static int 515 vidcaudio_halt_input(void *addr) 516 { 517 518 return ENODEV; 519 } 520 521 static int 522 vidcaudio_getdev(void *addr, struct audio_device *retp) 523 { 524 525 *retp = vidcaudio_device; 526 return 0; 527 } 528 529 530 static int 531 vidcaudio_set_port(void *addr, mixer_ctrl_t *cp) 532 { 533 534 return EINVAL; 535 } 536 537 static int 538 vidcaudio_get_port(void *addr, mixer_ctrl_t *cp) 539 { 540 541 return EINVAL; 542 } 543 544 static int 545 vidcaudio_query_devinfo(void *addr, mixer_devinfo_t *dip) 546 { 547 548 return ENXIO; 549 } 550 551 static int 552 vidcaudio_get_props(void *addr) 553 { 554 555 return 0; 556 } 557 558 static void 559 vidcaudio_rate(int rate) 560 { 561 562 WriteWord(vidc_base, VIDC_SFR | rate); 563 } 564 565 static void 566 vidcaudio_ctrl(int ctrl) 567 { 568 569 WriteWord(vidc_base, VIDC_SCR | ctrl); 570 } 571 572 static void 573 vidcaudio_stereo(int channel, int position) 574 { 575 576 channel = channel << 24 | VIDC_SIR0; 577 WriteWord(vidc_base, channel | position); 578 } 579 580 static int 581 vidcaudio_intr(void *arg) 582 { 583 struct vidcaudio_softc *sc; 584 int status; 585 paddr_t pnext, pend; 586 587 sc = arg; 588 status = IOMD_READ_BYTE(IOMD_SD0ST); 589 DPRINTF(("I[%x]", status)); 590 if ((status & IOMD_DMAST_INT) == 0) 591 return 0; 592 593 pnext = sc->sc_ppages[sc->sc_poffset >> PGSHIFT] | 594 (sc->sc_poffset & PGOFSET); 595 pend = (pnext + sc->sc_pblksize - 16) & IOMD_DMAEND_OFFSET; 596 597 switch (status & 598 (IOMD_DMAST_OVERRUN | IOMD_DMAST_INT | IOMD_DMAST_BANKB)) { 599 600 case (IOMD_DMAST_INT | IOMD_DMAST_BANKA): 601 case (IOMD_DMAST_OVERRUN | IOMD_DMAST_INT | IOMD_DMAST_BANKB): 602 DPRINTF(("B<0x%08lx,0x%03lx>", pnext, pend)); 603 IOMD_WRITE_WORD(IOMD_SD0CURB, pnext); 604 IOMD_WRITE_WORD(IOMD_SD0ENDB, pend); 605 break; 606 607 case (IOMD_DMAST_INT | IOMD_DMAST_BANKB): 608 case (IOMD_DMAST_OVERRUN | IOMD_DMAST_INT | IOMD_DMAST_BANKA): 609 DPRINTF(("A<0x%08lx,0x%03lx>", pnext, pend)); 610 IOMD_WRITE_WORD(IOMD_SD0CURA, pnext); 611 IOMD_WRITE_WORD(IOMD_SD0ENDA, pend); 612 break; 613 } 614 615 sc->sc_poffset += sc->sc_pblksize; 616 if (sc->sc_poffset >= sc->sc_pbufsize) 617 sc->sc_poffset = 0; 618 619 if (sc->sc_pcountdown > 0) 620 sc->sc_pcountdown--; 621 else 622 (*sc->sc_pintr)(sc->sc_parg); 623 624 return 1; 625 } 626