1 /* $NetBSD: audioamd.c,v 1.29 2019/05/08 13:40:16 isaki 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.29 2019/05/08 13:40:16 isaki 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/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 }; 141 142 /* 143 * Define our interface to the higher level audio driver. 144 */ 145 int audioamd_start_output(void *, void *, int, void (*)(void *), void *); 146 int audioamd_start_input(void *, void *, int, void (*)(void *), void *); 147 int audioamd_getdev(void *, struct audio_device *); 148 void audioamd_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread); 149 150 const struct audio_hw_if sa_hw_if = { 151 .open = am7930_open, 152 .close = am7930_close, 153 .query_format = am7930_query_format, 154 .set_format = am7930_set_format, 155 .commit_settings = am7930_commit_settings, 156 .start_output = audioamd_start_output, /* md */ 157 .start_input = audioamd_start_input, /* md */ 158 .halt_output = am7930_halt_output, 159 .halt_input = am7930_halt_input, 160 .getdev = audioamd_getdev, 161 .set_port = am7930_set_port, 162 .get_port = am7930_get_port, 163 .query_devinfo = am7930_query_devinfo, 164 .get_props = am7930_get_props, 165 .get_locks = audioamd_get_locks, 166 }; 167 168 struct audio_device audioamd_device = { 169 "am7930", 170 "x", 171 "audioamd" 172 }; 173 174 175 int 176 audioamd_mainbus_match(device_t parent, cfdata_t cf, void *aux) 177 { 178 struct mainbus_attach_args *ma; 179 180 ma = aux; 181 if (CPU_ISSUN4) 182 return 0; 183 return strcmp(AUDIO_ROM_NAME, ma->ma_name) == 0; 184 } 185 186 int 187 audioamd_obio_match(device_t parent, cfdata_t cf, void *aux) 188 { 189 union obio_attach_args *uoba; 190 191 uoba = aux; 192 if (uoba->uoba_isobio4 != 0) 193 return 0; 194 195 return strcmp("audio", uoba->uoba_sbus.sa_name) == 0; 196 } 197 198 int 199 audioamd_sbus_match(device_t parent, cfdata_t cf, void *aux) 200 { 201 struct sbus_attach_args *sa; 202 203 sa = aux; 204 return strcmp(AUDIO_ROM_NAME, sa->sa_name) == 0; 205 } 206 207 void 208 audioamd_mainbus_attach(device_t parent, device_t self, void *aux) 209 { 210 struct mainbus_attach_args *ma; 211 struct audioamd_softc *sc; 212 bus_space_handle_t bh; 213 214 ma = aux; 215 sc = device_private(self); 216 sc->sc_am7930.sc_dev = self; 217 sc->sc_bt = ma->ma_bustag; 218 219 if (bus_space_map( 220 ma->ma_bustag, 221 ma->ma_paddr, 222 AM7930_DREG_SIZE, 223 BUS_SPACE_MAP_LINEAR, 224 &bh) != 0) { 225 printf("%s: cannot map registers\n", device_xname(self)); 226 return; 227 } 228 sc->sc_bh = bh; 229 audioamd_attach(sc, ma->ma_pri); 230 } 231 232 void 233 audioamd_obio_attach(device_t parent, device_t self, void *aux) 234 { 235 union obio_attach_args *uoba; 236 struct sbus_attach_args *sa; 237 struct audioamd_softc *sc; 238 bus_space_handle_t bh; 239 240 uoba = aux; 241 sa = &uoba->uoba_sbus; 242 sc = device_private(self); 243 sc->sc_am7930.sc_dev = self; 244 sc->sc_bt = sa->sa_bustag; 245 246 if (sbus_bus_map(sa->sa_bustag, 247 sa->sa_slot, sa->sa_offset, 248 AM7930_DREG_SIZE, 249 0, &bh) != 0) { 250 printf("%s: cannot map registers\n", device_xname(self)); 251 return; 252 } 253 sc->sc_bh = bh; 254 audioamd_attach(sc, sa->sa_pri); 255 } 256 257 void 258 audioamd_sbus_attach(device_t parent, device_t self, void *aux) 259 { 260 struct sbus_attach_args *sa; 261 struct audioamd_softc *sc; 262 bus_space_handle_t bh; 263 264 sa = aux; 265 sc = device_private(self); 266 sc->sc_am7930.sc_dev = self; 267 sc->sc_bt = sa->sa_bustag; 268 269 if (sbus_bus_map(sa->sa_bustag, 270 sa->sa_slot, sa->sa_offset, 271 AM7930_DREG_SIZE, 272 0, &bh) != 0) { 273 printf("%s: cannot map registers\n", device_xname(self)); 274 return; 275 } 276 sc->sc_bh = bh; 277 audioamd_attach(sc, sa->sa_pri); 278 } 279 280 void 281 audioamd_attach(struct audioamd_softc *sc, int pri) 282 { 283 device_t self; 284 285 /* 286 * Set up glue for MI code early; we use some of it here. 287 */ 288 self = sc->sc_am7930.sc_dev; 289 sc->sc_am7930.sc_glue = &audioamd_glue; 290 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH); 291 292 am7930_init(&sc->sc_am7930, AUDIOAMD_POLL_MODE); 293 294 auiop = &sc->sc_au; 295 296 /* Copy bus tag & handle for use by am7930_trap */ 297 sc->sc_au.au_bt = sc->sc_bt; 298 sc->sc_au.au_bh = sc->sc_bh; 299 (void)bus_intr_establish2(sc->sc_bt, pri, IPL_HIGH, 300 am7930hwintr, sc, 301 #ifdef notyet /* XXX amd7930intr.s needs to be fixed for MI softint(9) */ 302 amd7930_trap 303 #else 304 NULL 305 #endif 306 ); 307 308 sc->sc_sicookie = softint_establish(SOFTINT_SERIAL, am7930swintr, sc); 309 if (sc->sc_sicookie == NULL) { 310 printf("\n%s: cannot establish software interrupt\n", 311 device_xname(self)); 312 return; 313 } 314 315 printf(" softpri %d\n", IPL_SOFTAUDIO); 316 317 318 evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL, 319 device_xname(self), "intr"); 320 321 audio_attach_mi(&sa_hw_if, sc, self); 322 } 323 324 325 void 326 audioamd_onopen(struct am7930_softc *sc) 327 { 328 struct audioamd_softc *mdsc; 329 330 mdsc = (struct audioamd_softc *)sc; 331 332 /* reset pdma state */ 333 mutex_spin_enter(&mdsc->sc_lock); 334 mdsc->sc_rintr = 0; 335 mdsc->sc_rarg = 0; 336 mdsc->sc_pintr = 0; 337 mdsc->sc_parg = 0; 338 mdsc->sc_au.au_rdata = 0; 339 mdsc->sc_au.au_pdata = 0; 340 mutex_spin_exit(&mdsc->sc_lock); 341 } 342 343 344 void 345 audioamd_onclose(struct am7930_softc *sc) 346 { 347 /* On sparc, just do the chipset-level halt. */ 348 am7930_halt_input(sc); 349 am7930_halt_output(sc); 350 } 351 352 int 353 audioamd_start_output(void *addr, void *p, int cc, 354 void (*intr)(void *), void *arg) 355 { 356 struct audioamd_softc *sc; 357 358 DPRINTFN(1, ("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg)); 359 sc = addr; 360 361 mutex_spin_enter(&sc->sc_lock); 362 audioamd_codec_iwrite(&sc->sc_am7930, 363 AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE); 364 sc->sc_pintr = intr; 365 sc->sc_parg = arg; 366 sc->sc_au.au_pdata = p; 367 sc->sc_au.au_pend = (char *)p + cc - 1; 368 mutex_spin_exit(&sc->sc_lock); 369 370 DPRINTF(("sa_start_output: started intrs.\n")); 371 return(0); 372 } 373 374 int 375 audioamd_start_input(void *addr, void *p, int cc, 376 void (*intr)(void *), void *arg) 377 { 378 struct audioamd_softc *sc; 379 380 DPRINTFN(1, ("sa_start_input: cc=%d %p (%p)\n", cc, intr, arg)); 381 sc = addr; 382 383 mutex_spin_enter(&sc->sc_lock); 384 audioamd_codec_iwrite(&sc->sc_am7930, 385 AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE); 386 sc->sc_rintr = intr; 387 sc->sc_rarg = arg; 388 sc->sc_au.au_rdata = p; 389 sc->sc_au.au_rend = (char *)p + cc -1; 390 mutex_spin_exit(&sc->sc_lock); 391 392 DPRINTF(("sa_start_input: started intrs.\n")); 393 394 return(0); 395 } 396 397 398 /* 399 * Pseudo-DMA support: either C or locore assember. 400 */ 401 402 int 403 am7930hwintr(void *v) 404 { 405 struct audioamd_softc *sc; 406 struct auio *au; 407 uint8_t *d, *e; 408 int k; 409 410 sc = v; 411 au = &sc->sc_au; 412 mutex_spin_enter(&sc->sc_lock); 413 414 /* clear interrupt */ 415 k = audioamd_codec_dread(sc, AM7930_DREG_IR); 416 if ((k & (AM7930_IR_DTTHRSH|AM7930_IR_DRTHRSH|AM7930_IR_DSRI| 417 AM7930_IR_DERI|AM7930_IR_BBUFF)) == 0) { 418 mutex_spin_exit(&sc->sc_lock); 419 return 0; 420 } 421 422 /* receive incoming data */ 423 d = au->au_rdata; 424 e = au->au_rend; 425 if (d && d <= e) { 426 *d = audioamd_codec_dread(sc, AM7930_DREG_BBRB); 427 au->au_rdata++; 428 if (d == e) { 429 DPRINTFN(1, ("am7930hwintr: swintr(r) requested")); 430 softint_schedule(sc->sc_sicookie); 431 } 432 } 433 434 /* send outgoing data */ 435 d = au->au_pdata; 436 e = au->au_pend; 437 if (d && d <= e) { 438 audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d); 439 au->au_pdata++; 440 if (d == e) { 441 DPRINTFN(1, ("am7930hwintr: swintr(p) requested")); 442 softint_schedule(sc->sc_sicookie); 443 } 444 } 445 446 au->au_intrcnt.ev_count++; 447 mutex_spin_exit(&sc->sc_lock); 448 449 return 1; 450 } 451 452 void 453 am7930swintr(void *sc0) 454 { 455 struct audioamd_softc *sc; 456 struct auio *au; 457 bool pint; 458 459 sc = sc0; 460 DPRINTFN(1, ("audiointr: sc=%p\n", sc);); 461 462 au = &sc->sc_au; 463 464 mutex_spin_enter(&sc->sc_am7930.sc_lock); 465 if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) { 466 (*sc->sc_rintr)(sc->sc_rarg); 467 } 468 pint = (au->au_pdata > au->au_pend && sc->sc_pintr != NULL); 469 if (pint) 470 (*sc->sc_pintr)(sc->sc_parg); 471 472 mutex_spin_exit(&sc->sc_am7930.sc_lock); 473 } 474 475 476 /* indirect write */ 477 void 478 audioamd_codec_iwrite(struct am7930_softc *sc, int reg, uint8_t val) 479 { 480 struct audioamd_softc *mdsc; 481 482 mdsc = (struct audioamd_softc *)sc; 483 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 484 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val); 485 } 486 487 void 488 audioamd_codec_iwrite16(struct am7930_softc *sc, int reg, uint16_t val) 489 { 490 struct audioamd_softc *mdsc; 491 492 mdsc = (struct audioamd_softc *)sc; 493 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 494 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val); 495 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val>>8); 496 } 497 498 499 /* indirect read */ 500 uint8_t 501 audioamd_codec_iread(struct am7930_softc *sc, int reg) 502 { 503 struct audioamd_softc *mdsc; 504 505 mdsc = (struct audioamd_softc *)sc; 506 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 507 return (audioamd_codec_dread(mdsc, AM7930_DREG_DR)); 508 } 509 510 uint16_t 511 audioamd_codec_iread16(struct am7930_softc *sc, int reg) 512 { 513 struct audioamd_softc *mdsc; 514 uint8_t lo, hi; 515 516 mdsc = (struct audioamd_softc *)sc; 517 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 518 lo = audioamd_codec_dread(mdsc, AM7930_DREG_DR); 519 hi = audioamd_codec_dread(mdsc, AM7930_DREG_DR); 520 return (hi << 8) | lo; 521 } 522 523 /* direct read */ 524 uint8_t 525 audioamd_codec_dread(struct audioamd_softc *sc, int reg) 526 { 527 528 return bus_space_read_1(sc->sc_bt, sc->sc_bh, reg); 529 } 530 531 /* direct write */ 532 void 533 audioamd_codec_dwrite(struct audioamd_softc *sc, int reg, uint8_t val) 534 { 535 536 bus_space_write_1(sc->sc_bt, sc->sc_bh, reg, val); 537 } 538 539 int 540 audioamd_getdev(void *addr, struct audio_device *retp) 541 { 542 543 *retp = audioamd_device; 544 return 0; 545 } 546 547 void 548 audioamd_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread) 549 { 550 struct audioamd_softc *asc = opaque; 551 struct am7930_softc *sc = &asc->sc_am7930; 552 553 *intr = &sc->sc_intr_lock; 554 *thread = &sc->sc_lock; 555 } 556 557 #endif /* NAUDIO > 0 */ 558