1 /* $NetBSD: audioamd.c,v 1.16 2002/12/10 13:44:49 pk 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 "audio.h" 35 #if NAUDIO > 0 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/errno.h> 40 #include <sys/device.h> 41 42 #include <machine/bus.h> 43 #include <machine/intr.h> 44 #include <machine/autoconf.h> 45 46 #include <sys/audioio.h> 47 #include <dev/audio_if.h> 48 49 #include <dev/ic/am7930reg.h> 50 #include <dev/ic/am7930var.h> 51 #include <sparc/dev/audioamdvar.h> 52 53 #define AUDIO_ROM_NAME "audio" 54 55 #ifdef AUDIO_DEBUG 56 #define DPRINTF(x) if (am7930debug) printf x 57 #define DPRINTFN(n,x) if (am7930debug>(n)) printf x 58 #else 59 #define DPRINTF(x) 60 #define DPRINTFN(n,x) 61 #endif /* AUDIO_DEBUG */ 62 63 64 /* interrupt interfaces */ 65 int am7930hwintr __P((void *)); 66 struct auio *auiop; 67 void am7930swintr __P((void *)); 68 69 /* from amd7930intr.s: */ 70 void amd7930_trap(void); 71 72 /* 73 * interrupt-handler status 74 */ 75 struct am7930_intrhand { 76 int (*ih_fun) __P((void *)); 77 void *ih_arg; 78 }; 79 80 struct audioamd_softc { 81 struct am7930_softc sc_am7930; /* glue to MI code */ 82 83 bus_space_tag_t sc_bt; /* bus cookie */ 84 bus_space_handle_t sc_bh; /* device registers */ 85 86 struct am7930_intrhand sc_ih; /* interrupt vector (hw or sw) */ 87 void (*sc_rintr)(void*); /* input completion intr handler */ 88 void *sc_rarg; /* arg for sc_rintr() */ 89 void (*sc_pintr)(void*); /* output completion intr handler */ 90 void *sc_parg; /* arg for sc_pintr() */ 91 92 /* sc_au is special in that the hardware interrupt handler uses it */ 93 struct auio sc_au; /* recv and xmit buffers, etc */ 94 #define sc_intrcnt sc_au.au_intrcnt /* statistics */ 95 void *sc_sicookie; /* softintr(9) cookie */ 96 }; 97 98 void audioamd_mainbus_attach __P((struct device *, 99 struct device *, void *)); 100 int audioamd_mainbus_match __P((struct device *, struct cfdata *, void *)); 101 void audioamd_obio_attach __P((struct device *, struct device *, void *)); 102 int audioamd_obio_match __P((struct device *, struct cfdata *, void *)); 103 void audioamd_sbus_attach __P((struct device *, struct device *, void *)); 104 int audioamd_sbus_match __P((struct device *, struct cfdata *, void *)); 105 void audioamd_attach(struct audioamd_softc *sc, int); 106 107 CFATTACH_DECL(audioamd_mainbus, sizeof(struct audioamd_softc), 108 audioamd_mainbus_match, audioamd_mainbus_attach, NULL, NULL); 109 110 CFATTACH_DECL(audioamd_obio, sizeof(struct audioamd_softc), 111 audioamd_obio_match, audioamd_obio_attach, NULL, NULL); 112 113 CFATTACH_DECL(audioamd_sbus, sizeof(struct audioamd_softc), 114 audioamd_sbus_match, audioamd_sbus_attach, NULL, NULL); 115 116 /* 117 * Define our interface into the am7930 MI driver. 118 */ 119 120 u_int8_t audioamd_codec_iread __P((struct am7930_softc *, int)); 121 u_int16_t audioamd_codec_iread16 __P((struct am7930_softc *, int)); 122 u_int8_t audioamd_codec_dread __P((struct audioamd_softc *, int)); 123 void audioamd_codec_iwrite __P((struct am7930_softc *, int, u_int8_t)); 124 void audioamd_codec_iwrite16 __P((struct am7930_softc *, int, u_int16_t)); 125 void audioamd_codec_dwrite __P((struct audioamd_softc *, int, u_int8_t)); 126 void audioamd_onopen __P((struct am7930_softc *sc)); 127 void audioamd_onclose __P((struct am7930_softc *sc)); 128 129 struct am7930_glue audioamd_glue = { 130 audioamd_codec_iread, 131 audioamd_codec_iwrite, 132 audioamd_codec_iread16, 133 audioamd_codec_iwrite16, 134 audioamd_onopen, 135 audioamd_onclose, 136 0, 137 0, 138 0, 139 }; 140 141 /* 142 * Define our interface to the higher level audio driver. 143 */ 144 int audioamd_start_output __P((void *, void *, int, void (*)(void *), 145 void *)); 146 int audioamd_start_input __P((void *, void *, int, void (*)(void *), 147 void *)); 148 int audioamd_getdev __P((void *, struct audio_device *)); 149 150 struct audio_hw_if sa_hw_if = { 151 am7930_open, 152 am7930_close, 153 0, 154 am7930_query_encoding, 155 am7930_set_params, 156 am7930_round_blocksize, 157 am7930_commit_settings, 158 0, 159 0, 160 audioamd_start_output, /* md */ 161 audioamd_start_input, /* md */ 162 am7930_halt_output, 163 am7930_halt_input, 164 0, 165 audioamd_getdev, 166 0, 167 am7930_set_port, 168 am7930_get_port, 169 am7930_query_devinfo, 170 0, 171 0, 172 0, 173 0, 174 am7930_get_props, 175 0, 176 0, 177 0, 178 }; 179 180 struct audio_device audioamd_device = { 181 "am7930", 182 "x", 183 "audioamd" 184 }; 185 186 187 int 188 audioamd_mainbus_match(parent, cf, aux) 189 struct device *parent; 190 struct cfdata *cf; 191 void *aux; 192 { 193 struct mainbus_attach_args *ma = aux; 194 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(parent, cf, aux) 202 struct device *parent; 203 struct cfdata *cf; 204 void *aux; 205 { 206 union obio_attach_args *uoba = aux; 207 208 if (uoba->uoba_isobio4 != 0) 209 return (0); 210 211 return (strcmp("audio", uoba->uoba_sbus.sa_name) == 0); 212 } 213 214 int 215 audioamd_sbus_match(parent, cf, aux) 216 struct device *parent; 217 struct cfdata *cf; 218 void *aux; 219 { 220 struct sbus_attach_args *sa = aux; 221 222 return (strcmp(AUDIO_ROM_NAME, sa->sa_name) == 0); 223 } 224 225 void 226 audioamd_mainbus_attach(parent, self, aux) 227 struct device *parent, *self; 228 void *aux; 229 { 230 struct mainbus_attach_args *ma = aux; 231 struct audioamd_softc *sc = (struct audioamd_softc *)self; 232 bus_space_handle_t bh; 233 234 sc->sc_bt = ma->ma_bustag; 235 236 if (bus_space_map( 237 ma->ma_bustag, 238 ma->ma_paddr, 239 AM7930_DREG_SIZE, 240 BUS_SPACE_MAP_LINEAR, 241 &bh) != 0) { 242 printf("%s: cannot map registers\n", self->dv_xname); 243 return; 244 } 245 sc->sc_bh = bh; 246 audioamd_attach(sc, ma->ma_pri); 247 } 248 249 void 250 audioamd_obio_attach(parent, self, aux) 251 struct device *parent, *self; 252 void *aux; 253 { 254 union obio_attach_args *uoba = aux; 255 struct sbus_attach_args *sa = &uoba->uoba_sbus; 256 struct audioamd_softc *sc = (struct audioamd_softc *)self; 257 bus_space_handle_t bh; 258 259 sc->sc_bt = sa->sa_bustag; 260 261 if (sbus_bus_map(sa->sa_bustag, 262 sa->sa_slot, sa->sa_offset, 263 AM7930_DREG_SIZE, 264 0, &bh) != 0) { 265 printf("%s: cannot map registers\n", self->dv_xname); 266 return; 267 } 268 sc->sc_bh = bh; 269 audioamd_attach(sc, sa->sa_pri); 270 } 271 272 void 273 audioamd_sbus_attach(parent, self, aux) 274 struct device *parent, *self; 275 void *aux; 276 { 277 struct sbus_attach_args *sa = aux; 278 struct audioamd_softc *sc = (struct audioamd_softc *)self; 279 bus_space_handle_t bh; 280 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", self->dv_xname); 288 return; 289 } 290 sc->sc_bh = bh; 291 audioamd_attach(sc, sa->sa_pri); 292 } 293 294 void 295 audioamd_attach(sc, pri) 296 struct audioamd_softc *sc; 297 int pri; 298 { 299 300 /* 301 * Set up glue for MI code early; we use some of it here. 302 */ 303 sc->sc_am7930.sc_glue = &audioamd_glue; 304 305 am7930_init(&sc->sc_am7930, AUDIOAMD_POLL_MODE); 306 307 auiop = &sc->sc_au; 308 309 /* Copy bus tag & handle for use by am7930_trap */ 310 sc->sc_au.au_bt = sc->sc_bt; 311 sc->sc_au.au_bh = sc->sc_bh; 312 (void)bus_intr_establish2(sc->sc_bt, pri, IPL_AUDIO, 313 am7930hwintr, sc, amd7930_trap); 314 315 sc->sc_sicookie = softintr_establish(IPL_SOFTAUDIO, am7930swintr, sc); 316 if (sc->sc_sicookie == NULL) { 317 printf("\n%s: cannot establish software interrupt\n", 318 sc->sc_am7930.sc_dev.dv_xname); 319 return; 320 } 321 322 printf(" softpri %d\n", IPL_SOFTAUDIO); 323 324 325 evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL, 326 sc->sc_am7930.sc_dev.dv_xname, "intr"); 327 328 audio_attach_mi(&sa_hw_if, sc, &sc->sc_am7930.sc_dev); 329 } 330 331 332 void 333 audioamd_onopen(sc) 334 struct am7930_softc *sc; 335 { 336 struct audioamd_softc *mdsc = (struct audioamd_softc *)sc; 337 338 /* reset pdma state */ 339 mdsc->sc_rintr = 0; 340 mdsc->sc_rarg = 0; 341 mdsc->sc_pintr = 0; 342 mdsc->sc_parg = 0; 343 344 mdsc->sc_au.au_rdata = 0; 345 mdsc->sc_au.au_pdata = 0; 346 } 347 348 349 void 350 audioamd_onclose(sc) 351 struct am7930_softc *sc; 352 { 353 /* On sparc, just do the chipset-level halt. */ 354 am7930_halt_input(sc); 355 am7930_halt_output(sc); 356 } 357 358 int 359 audioamd_start_output(addr, p, cc, intr, arg) 360 void *addr; 361 void *p; 362 int cc; 363 void (*intr) __P((void *)); 364 void *arg; 365 { 366 struct audioamd_softc *sc = addr; 367 368 DPRINTFN(1, ("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg)); 369 370 if (!sc->sc_am7930.sc_locked) { 371 audioamd_codec_iwrite(&sc->sc_am7930, 372 AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE); 373 sc->sc_am7930.sc_locked = 1; 374 DPRINTF(("sa_start_output: started intrs.\n")); 375 } 376 sc->sc_pintr = intr; 377 sc->sc_parg = arg; 378 sc->sc_au.au_pdata = p; 379 sc->sc_au.au_pend = (char *)p + cc - 1; 380 return(0); 381 } 382 383 int 384 audioamd_start_input(addr, p, cc, intr, arg) 385 void *addr; 386 void *p; 387 int cc; 388 void (*intr) __P((void *)); 389 void *arg; 390 { 391 struct audioamd_softc *sc = addr; 392 393 DPRINTFN(1, ("sa_start_input: cc=%d %p (%p)\n", cc, intr, arg)); 394 395 if (!sc->sc_am7930.sc_locked) { 396 audioamd_codec_iwrite(&sc->sc_am7930, 397 AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE); 398 sc->sc_am7930.sc_locked = 1; 399 DPRINTF(("sa_start_input: started intrs.\n")); 400 } 401 sc->sc_rintr = intr; 402 sc->sc_rarg = arg; 403 sc->sc_au.au_rdata = p; 404 sc->sc_au.au_rend = (char *)p + cc -1; 405 return(0); 406 } 407 408 409 /* 410 * Pseudo-DMA support: either C or locore assember. 411 */ 412 413 int 414 am7930hwintr(v) 415 void *v; 416 { 417 struct audioamd_softc *sc = v; 418 struct auio *au = &sc->sc_au; 419 u_int8_t *d, *e; 420 int k; 421 422 /* clear interrupt */ 423 k = audioamd_codec_dread(sc, AM7930_DREG_IR); 424 if ((k & (AM7930_IR_DTTHRSH|AM7930_IR_DRTHRSH|AM7930_IR_DSRI| 425 AM7930_IR_DERI|AM7930_IR_BBUFF)) == 0) 426 return (0); 427 428 /* receive incoming data */ 429 d = au->au_rdata; 430 e = au->au_rend; 431 if (d && d <= e) { 432 *d = audioamd_codec_dread(sc, AM7930_DREG_BBRB); 433 au->au_rdata++; 434 if (d == e) { 435 DPRINTFN(1, ("am7930hwintr: swintr(r) requested")); 436 softintr_schedule(sc->sc_sicookie); 437 } 438 } 439 440 /* send outgoing data */ 441 d = au->au_pdata; 442 e = au->au_pend; 443 if (d && d <= e) { 444 audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d); 445 au->au_pdata++; 446 if (d == e) { 447 DPRINTFN(1, ("am7930hwintr: swintr(p) requested")); 448 softintr_schedule(sc->sc_sicookie); 449 } 450 } 451 452 au->au_intrcnt.ev_count++; 453 return (1); 454 } 455 456 void 457 am7930swintr(sc0) 458 void *sc0; 459 { 460 struct audioamd_softc *sc = sc0; 461 struct auio *au; 462 int s; 463 464 DPRINTFN(1, ("audiointr: sc=%p\n", sc);); 465 466 au = &sc->sc_au; 467 s = splaudio(); 468 if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) { 469 splx(s); 470 (*sc->sc_rintr)(sc->sc_rarg); 471 s = splaudio(); 472 } 473 if (au->au_pdata > au->au_pend && sc->sc_pintr != NULL) { 474 splx(s); 475 (*sc->sc_pintr)(sc->sc_parg); 476 } else 477 splx(s); 478 } 479 480 481 /* indirect write */ 482 void 483 audioamd_codec_iwrite(sc, reg, val) 484 struct am7930_softc *sc; 485 int reg; 486 u_int8_t val; 487 { 488 struct audioamd_softc *mdsc = (struct audioamd_softc *)sc; 489 490 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 491 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val); 492 } 493 494 void 495 audioamd_codec_iwrite16(sc, reg, val) 496 struct am7930_softc *sc; 497 int reg; 498 u_int16_t val; 499 { 500 struct audioamd_softc *mdsc = (struct audioamd_softc *)sc; 501 502 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 503 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val); 504 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val>>8); 505 } 506 507 508 /* indirect read */ 509 u_int8_t 510 audioamd_codec_iread(sc, reg) 511 struct am7930_softc *sc; 512 int reg; 513 { 514 struct audioamd_softc *mdsc = (struct audioamd_softc *)sc; 515 516 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 517 return (audioamd_codec_dread(mdsc, AM7930_DREG_DR)); 518 } 519 520 u_int16_t 521 audioamd_codec_iread16(sc, reg) 522 struct am7930_softc *sc; 523 int reg; 524 { 525 struct audioamd_softc *mdsc = (struct audioamd_softc *)sc; 526 u_int8_t lo, hi; 527 528 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 529 lo = audioamd_codec_dread(mdsc, AM7930_DREG_DR); 530 hi = audioamd_codec_dread(mdsc, AM7930_DREG_DR); 531 return ((hi << 8) | lo); 532 } 533 534 /* direct read */ 535 u_int8_t 536 audioamd_codec_dread(sc, reg) 537 struct audioamd_softc *sc; 538 int reg; 539 { 540 return (bus_space_read_1(sc->sc_bt, sc->sc_bh, reg)); 541 } 542 543 /* direct write */ 544 void 545 audioamd_codec_dwrite(sc, reg, val) 546 struct audioamd_softc *sc; 547 int reg; 548 u_int8_t val; 549 { 550 bus_space_write_1(sc->sc_bt, sc->sc_bh, reg, val); 551 } 552 553 int 554 audioamd_getdev(addr, retp) 555 void *addr; 556 struct audio_device *retp; 557 { 558 559 *retp = audioamd_device; 560 return (0); 561 } 562 563 #endif /* NAUDIO > 0 */ 564