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