1 /* $NetBSD: audioamd.c,v 1.26 2011/06/04 01:27:57 tsutsui Exp $ */ 2 /* NetBSD: am7930_sparc.c,v 1.44 1999/03/14 22:29:00 jonathan Exp */ 3 4 /* 5 * Copyright (c) 1995 Rolf Grossmann 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by Rolf Grossmann. 19 * 4. The name of the author may not be used to endorse or promote products 20 * derived from this software without specific prior written permission 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: audioamd.c,v 1.26 2011/06/04 01:27:57 tsutsui Exp $"); 36 37 #include "audio.h" 38 #if NAUDIO > 0 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/errno.h> 43 #include <sys/device.h> 44 #include <sys/bus.h> 45 #include <sys/intr.h> 46 #include <sys/mutex.h> 47 48 #include <machine/autoconf.h> 49 50 #include <sys/audioio.h> 51 #include <dev/audio_if.h> 52 53 #include <dev/ic/am7930reg.h> 54 #include <dev/ic/am7930var.h> 55 #include <sparc/dev/audioamdvar.h> 56 57 #define AUDIO_ROM_NAME "audio" 58 59 #ifdef AUDIO_DEBUG 60 #define DPRINTF(x) if (am7930debug) printf x 61 #define DPRINTFN(n,x) if (am7930debug>(n)) printf x 62 #else 63 #define DPRINTF(x) 64 #define DPRINTFN(n,x) 65 #endif /* AUDIO_DEBUG */ 66 67 68 /* interrupt interfaces */ 69 int am7930hwintr(void *); 70 struct auio *auiop; 71 void am7930swintr(void *); 72 73 /* from amd7930intr.s: */ 74 void amd7930_trap(void); 75 76 /* 77 * interrupt-handler status 78 */ 79 struct am7930_intrhand { 80 int (*ih_fun)(void *); 81 void *ih_arg; 82 }; 83 84 struct audioamd_softc { 85 struct am7930_softc sc_am7930; /* glue to MI code */ 86 87 bus_space_tag_t sc_bt; /* bus cookie */ 88 bus_space_handle_t sc_bh; /* device registers */ 89 90 struct am7930_intrhand sc_ih; /* interrupt vector (hw or sw) */ 91 void (*sc_rintr)(void*); /* input completion intr handler */ 92 void *sc_rarg; /* arg for sc_rintr() */ 93 void (*sc_pintr)(void*); /* output completion intr handler */ 94 void *sc_parg; /* arg for sc_pintr() */ 95 96 /* sc_au is special in that the hardware interrupt handler uses it */ 97 struct auio sc_au; /* recv and xmit buffers, etc */ 98 #define sc_intrcnt sc_au.au_intrcnt /* statistics */ 99 void *sc_sicookie; /* softintr(9) cookie */ 100 kmutex_t sc_lock; 101 }; 102 103 int audioamd_mainbus_match(device_t, cfdata_t, void *); 104 void audioamd_mainbus_attach(device_t, device_t, void *); 105 int audioamd_obio_match(device_t, cfdata_t, void *); 106 void audioamd_obio_attach(device_t, device_t, void *); 107 int audioamd_sbus_match(device_t, cfdata_t, void *); 108 void audioamd_sbus_attach(device_t, device_t, void *); 109 void audioamd_attach(struct audioamd_softc *, int); 110 111 CFATTACH_DECL_NEW(audioamd_mainbus, sizeof(struct audioamd_softc), 112 audioamd_mainbus_match, audioamd_mainbus_attach, NULL, NULL); 113 114 CFATTACH_DECL_NEW(audioamd_obio, sizeof(struct audioamd_softc), 115 audioamd_obio_match, audioamd_obio_attach, NULL, NULL); 116 117 CFATTACH_DECL_NEW(audioamd_sbus, sizeof(struct audioamd_softc), 118 audioamd_sbus_match, audioamd_sbus_attach, NULL, NULL); 119 120 /* 121 * Define our interface into the am7930 MI driver. 122 */ 123 124 uint8_t audioamd_codec_iread(struct am7930_softc *, int); 125 uint16_t audioamd_codec_iread16(struct am7930_softc *, int); 126 uint8_t audioamd_codec_dread(struct audioamd_softc *, int); 127 void audioamd_codec_iwrite(struct am7930_softc *, int, uint8_t); 128 void audioamd_codec_iwrite16(struct am7930_softc *, int, uint16_t); 129 void audioamd_codec_dwrite(struct audioamd_softc *, int, uint8_t); 130 void audioamd_onopen(struct am7930_softc *); 131 void audioamd_onclose(struct am7930_softc *); 132 133 struct am7930_glue audioamd_glue = { 134 audioamd_codec_iread, 135 audioamd_codec_iwrite, 136 audioamd_codec_iread16, 137 audioamd_codec_iwrite16, 138 audioamd_onopen, 139 audioamd_onclose, 140 0, 141 0, 142 0, 143 }; 144 145 /* 146 * Define our interface to the higher level audio driver. 147 */ 148 int audioamd_start_output(void *, void *, int, void (*)(void *), void *); 149 int audioamd_start_input(void *, void *, int, void (*)(void *), void *); 150 int audioamd_getdev(void *, struct audio_device *); 151 152 const struct audio_hw_if sa_hw_if = { 153 am7930_open, 154 am7930_close, 155 0, 156 am7930_query_encoding, 157 am7930_set_params, 158 am7930_round_blocksize, 159 am7930_commit_settings, 160 0, 161 0, 162 audioamd_start_output, /* md */ 163 audioamd_start_input, /* md */ 164 am7930_halt_output, 165 am7930_halt_input, 166 0, 167 audioamd_getdev, 168 0, 169 am7930_set_port, 170 am7930_get_port, 171 am7930_query_devinfo, 172 0, 173 0, 174 0, 175 0, 176 am7930_get_props, 177 0, 178 0, 179 0, 180 }; 181 182 struct audio_device audioamd_device = { 183 "am7930", 184 "x", 185 "audioamd" 186 }; 187 188 189 int 190 audioamd_mainbus_match(device_t parent, cfdata_t cf, void *aux) 191 { 192 struct mainbus_attach_args *ma; 193 194 ma = aux; 195 if (CPU_ISSUN4) 196 return 0; 197 return strcmp(AUDIO_ROM_NAME, ma->ma_name) == 0; 198 } 199 200 int 201 audioamd_obio_match(device_t parent, cfdata_t cf, void *aux) 202 { 203 union obio_attach_args *uoba; 204 205 uoba = aux; 206 if (uoba->uoba_isobio4 != 0) 207 return 0; 208 209 return strcmp("audio", uoba->uoba_sbus.sa_name) == 0; 210 } 211 212 int 213 audioamd_sbus_match(device_t parent, cfdata_t cf, void *aux) 214 { 215 struct sbus_attach_args *sa; 216 217 sa = aux; 218 return strcmp(AUDIO_ROM_NAME, sa->sa_name) == 0; 219 } 220 221 void 222 audioamd_mainbus_attach(device_t parent, device_t self, void *aux) 223 { 224 struct mainbus_attach_args *ma; 225 struct audioamd_softc *sc; 226 bus_space_handle_t bh; 227 228 ma = aux; 229 sc = device_private(self); 230 sc->sc_am7930.sc_dev = self; 231 sc->sc_bt = ma->ma_bustag; 232 233 if (bus_space_map( 234 ma->ma_bustag, 235 ma->ma_paddr, 236 AM7930_DREG_SIZE, 237 BUS_SPACE_MAP_LINEAR, 238 &bh) != 0) { 239 printf("%s: cannot map registers\n", device_xname(self)); 240 return; 241 } 242 sc->sc_bh = bh; 243 audioamd_attach(sc, ma->ma_pri); 244 } 245 246 void 247 audioamd_obio_attach(device_t parent, device_t self, void *aux) 248 { 249 union obio_attach_args *uoba; 250 struct sbus_attach_args *sa; 251 struct audioamd_softc *sc; 252 bus_space_handle_t bh; 253 254 uoba = aux; 255 sa = &uoba->uoba_sbus; 256 sc = device_private(self); 257 sc->sc_am7930.sc_dev = self; 258 sc->sc_bt = sa->sa_bustag; 259 260 if (sbus_bus_map(sa->sa_bustag, 261 sa->sa_slot, sa->sa_offset, 262 AM7930_DREG_SIZE, 263 0, &bh) != 0) { 264 printf("%s: cannot map registers\n", device_xname(self)); 265 return; 266 } 267 sc->sc_bh = bh; 268 audioamd_attach(sc, sa->sa_pri); 269 } 270 271 void 272 audioamd_sbus_attach(device_t parent, device_t self, void *aux) 273 { 274 struct sbus_attach_args *sa; 275 struct audioamd_softc *sc; 276 bus_space_handle_t bh; 277 278 sa = aux; 279 sc = device_private(self); 280 sc->sc_am7930.sc_dev = self; 281 sc->sc_bt = sa->sa_bustag; 282 283 if (sbus_bus_map(sa->sa_bustag, 284 sa->sa_slot, sa->sa_offset, 285 AM7930_DREG_SIZE, 286 0, &bh) != 0) { 287 printf("%s: cannot map registers\n", device_xname(self)); 288 return; 289 } 290 sc->sc_bh = bh; 291 audioamd_attach(sc, sa->sa_pri); 292 } 293 294 void 295 audioamd_attach(struct audioamd_softc *sc, int pri) 296 { 297 device_t self; 298 299 /* 300 * Set up glue for MI code early; we use some of it here. 301 */ 302 self = sc->sc_am7930.sc_dev; 303 sc->sc_am7930.sc_glue = &audioamd_glue; 304 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH); 305 306 am7930_init(&sc->sc_am7930, AUDIOAMD_POLL_MODE); 307 308 auiop = &sc->sc_au; 309 310 /* Copy bus tag & handle for use by am7930_trap */ 311 sc->sc_au.au_bt = sc->sc_bt; 312 sc->sc_au.au_bh = sc->sc_bh; 313 (void)bus_intr_establish2(sc->sc_bt, pri, IPL_HIGH, 314 am7930hwintr, sc, 315 #ifdef notyet /* XXX amd7930intr.s needs to be fixed for MI softint(9) */ 316 amd7930_trap 317 #else 318 NULL 319 #endif 320 ); 321 322 sc->sc_sicookie = softint_establish(SOFTINT_SERIAL, am7930swintr, sc); 323 if (sc->sc_sicookie == NULL) { 324 printf("\n%s: cannot establish software interrupt\n", 325 device_xname(self)); 326 return; 327 } 328 329 printf(" softpri %d\n", IPL_SOFTAUDIO); 330 331 332 evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL, 333 device_xname(self), "intr"); 334 335 audio_attach_mi(&sa_hw_if, sc, self); 336 } 337 338 339 void 340 audioamd_onopen(struct am7930_softc *sc) 341 { 342 struct audioamd_softc *mdsc; 343 344 mdsc = (struct audioamd_softc *)sc; 345 346 /* reset pdma state */ 347 mutex_spin_enter(&mdsc->sc_lock); 348 mdsc->sc_rintr = 0; 349 mdsc->sc_rarg = 0; 350 mdsc->sc_pintr = 0; 351 mdsc->sc_parg = 0; 352 mdsc->sc_au.au_rdata = 0; 353 mdsc->sc_au.au_pdata = 0; 354 mutex_spin_exit(&mdsc->sc_lock); 355 } 356 357 358 void 359 audioamd_onclose(struct am7930_softc *sc) 360 { 361 /* On sparc, just do the chipset-level halt. */ 362 am7930_halt_input(sc); 363 am7930_halt_output(sc); 364 } 365 366 int 367 audioamd_start_output(void *addr, void *p, int cc, 368 void (*intr)(void *), void *arg) 369 { 370 struct audioamd_softc *sc; 371 372 DPRINTFN(1, ("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg)); 373 sc = addr; 374 375 mutex_spin_enter(&sc->sc_lock); 376 audioamd_codec_iwrite(&sc->sc_am7930, 377 AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE); 378 sc->sc_pintr = intr; 379 sc->sc_parg = arg; 380 sc->sc_au.au_pdata = p; 381 sc->sc_au.au_pend = (char *)p + cc - 1; 382 mutex_spin_exit(&sc->sc_lock); 383 384 DPRINTF(("sa_start_output: started intrs.\n")); 385 return(0); 386 } 387 388 int 389 audioamd_start_input(void *addr, void *p, int cc, 390 void (*intr)(void *), void *arg) 391 { 392 struct audioamd_softc *sc; 393 394 DPRINTFN(1, ("sa_start_input: cc=%d %p (%p)\n", cc, intr, arg)); 395 sc = addr; 396 397 mutex_spin_enter(&sc->sc_lock); 398 audioamd_codec_iwrite(&sc->sc_am7930, 399 AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE); 400 sc->sc_rintr = intr; 401 sc->sc_rarg = arg; 402 sc->sc_au.au_rdata = p; 403 sc->sc_au.au_rend = (char *)p + cc -1; 404 mutex_spin_exit(&sc->sc_lock); 405 406 DPRINTF(("sa_start_input: started intrs.\n")); 407 408 return(0); 409 } 410 411 412 /* 413 * Pseudo-DMA support: either C or locore assember. 414 */ 415 416 int 417 am7930hwintr(void *v) 418 { 419 struct audioamd_softc *sc; 420 struct auio *au; 421 uint8_t *d, *e; 422 int k; 423 424 sc = v; 425 au = &sc->sc_au; 426 mutex_spin_enter(&sc->sc_lock); 427 428 /* clear interrupt */ 429 k = audioamd_codec_dread(sc, AM7930_DREG_IR); 430 if ((k & (AM7930_IR_DTTHRSH|AM7930_IR_DRTHRSH|AM7930_IR_DSRI| 431 AM7930_IR_DERI|AM7930_IR_BBUFF)) == 0) { 432 mutex_spin_exit(&sc->sc_lock); 433 return 0; 434 } 435 436 /* receive incoming data */ 437 d = au->au_rdata; 438 e = au->au_rend; 439 if (d && d <= e) { 440 *d = audioamd_codec_dread(sc, AM7930_DREG_BBRB); 441 au->au_rdata++; 442 if (d == e) { 443 DPRINTFN(1, ("am7930hwintr: swintr(r) requested")); 444 softint_schedule(sc->sc_sicookie); 445 } 446 } 447 448 /* send outgoing data */ 449 d = au->au_pdata; 450 e = au->au_pend; 451 if (d && d <= e) { 452 audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d); 453 au->au_pdata++; 454 if (d == e) { 455 DPRINTFN(1, ("am7930hwintr: swintr(p) requested")); 456 softint_schedule(sc->sc_sicookie); 457 } 458 } 459 460 au->au_intrcnt.ev_count++; 461 mutex_spin_exit(&sc->sc_lock); 462 463 return 1; 464 } 465 466 void 467 am7930swintr(void *sc0) 468 { 469 struct audioamd_softc *sc; 470 struct auio *au; 471 bool pint; 472 473 sc = sc0; 474 DPRINTFN(1, ("audiointr: sc=%p\n", sc);); 475 476 au = &sc->sc_au; 477 mutex_spin_enter(&sc->sc_lock); 478 if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) { 479 mutex_spin_exit(&sc->sc_lock); 480 (*sc->sc_rintr)(sc->sc_rarg); 481 mutex_spin_enter(&sc->sc_lock); 482 } 483 pint = (au->au_pdata > au->au_pend && sc->sc_pintr != NULL); 484 mutex_spin_exit(&sc->sc_lock); 485 if (pint) 486 (*sc->sc_pintr)(sc->sc_parg); 487 } 488 489 490 /* indirect write */ 491 void 492 audioamd_codec_iwrite(struct am7930_softc *sc, int reg, uint8_t val) 493 { 494 struct audioamd_softc *mdsc; 495 496 mdsc = (struct audioamd_softc *)sc; 497 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 498 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val); 499 } 500 501 void 502 audioamd_codec_iwrite16(struct am7930_softc *sc, int reg, uint16_t val) 503 { 504 struct audioamd_softc *mdsc; 505 506 mdsc = (struct audioamd_softc *)sc; 507 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 508 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val); 509 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val>>8); 510 } 511 512 513 /* indirect read */ 514 uint8_t 515 audioamd_codec_iread(struct am7930_softc *sc, int reg) 516 { 517 struct audioamd_softc *mdsc; 518 519 mdsc = (struct audioamd_softc *)sc; 520 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 521 return (audioamd_codec_dread(mdsc, AM7930_DREG_DR)); 522 } 523 524 uint16_t 525 audioamd_codec_iread16(struct am7930_softc *sc, int reg) 526 { 527 struct audioamd_softc *mdsc; 528 uint8_t lo, hi; 529 530 mdsc = (struct audioamd_softc *)sc; 531 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 532 lo = audioamd_codec_dread(mdsc, AM7930_DREG_DR); 533 hi = audioamd_codec_dread(mdsc, AM7930_DREG_DR); 534 return (hi << 8) | lo; 535 } 536 537 /* direct read */ 538 uint8_t 539 audioamd_codec_dread(struct audioamd_softc *sc, int reg) 540 { 541 542 return bus_space_read_1(sc->sc_bt, sc->sc_bh, reg); 543 } 544 545 /* direct write */ 546 void 547 audioamd_codec_dwrite(struct audioamd_softc *sc, int reg, uint8_t val) 548 { 549 550 bus_space_write_1(sc->sc_bt, sc->sc_bh, reg, val); 551 } 552 553 int 554 audioamd_getdev(void *addr, struct audio_device *retp) 555 { 556 557 *retp = audioamd_device; 558 return 0; 559 } 560 561 #endif /* NAUDIO > 0 */ 562