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