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