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