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