1 /* $NetBSD: audioamd.c,v 1.4 2000/06/04 19:15:01 cgd 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/autoconf.h> 44 #include <machine/cpu.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, 288 BUS_INTR_ESTABLISH_FASTTRAP, 289 (int (*) __P((void *)))amd7930_trap, NULL); 290 #else 291 (void)bus_intr_establish(sc->sc_bt, pri, 0, 292 am7930hwintr, &sc->sc_au); 293 #endif 294 (void)bus_intr_establish(sc->sc_bt, PIL_AUSOFT, 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 sc->sc_au.au_pdata = p; 352 sc->sc_au.au_pend = (char *)p + cc - 1; 353 return(0); 354 } 355 356 int 357 audioamd_start_input(addr, p, cc, intr, arg) 358 void *addr; 359 void *p; 360 int cc; 361 void (*intr) __P((void *)); 362 void *arg; 363 { 364 struct audioamd_softc *sc = addr; 365 366 DPRINTFN(1, ("sa_start_input: cc=%d %p (%p)\n", cc, intr, arg)); 367 368 if (!sc->sc_am7930.sc_locked) { 369 audioamd_codec_iwrite(&sc->sc_am7930, 370 AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE); 371 sc->sc_am7930.sc_locked = 1; 372 DPRINTF(("sa_start_input: started intrs.\n")); 373 } 374 sc->sc_rintr = intr; 375 sc->sc_rarg = arg; 376 sc->sc_au.au_rdata = p; 377 sc->sc_au.au_rend = (char *)p + cc -1; 378 return(0); 379 } 380 381 382 /* 383 * Pseudo-DMA support: either C or locore assember. 384 */ 385 386 #ifdef AUDIO_C_HANDLER 387 int 388 am7930hwintr(au0) 389 void *au0; 390 { 391 struct auio *au = au0; 392 u_int8_t *d, *e; 393 int k; 394 395 /* XXX where is sc? */ 396 397 /* clear interrupt */ 398 k = audioamd_codec_dread(sc, AM7930_DREG_IR); 399 400 /* receive incoming data */ 401 d = au->au_rdata; 402 e = au->au_rend; 403 if (d && d <= e) { 404 *d = audioamd_codec_dread(sc, AM7930_DREG_BBRB); 405 au->au_rdata++; 406 if (d == e) { 407 DPRINTFN(1, ("am7930hwintr: swintr(r) requested")); 408 AUDIO_SET_SWINTR; 409 } 410 } 411 412 /* send outgoing data */ 413 d = au->au_pdata; 414 e = au->au_pend; 415 if (d && d <= e) { 416 audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d); 417 au->au_pdata++; 418 if (d == e) { 419 DPRINTFN(1, ("am7930hwintr: swintr(p) requested")); 420 AUDIO_SET_SWINTR; 421 } 422 } 423 424 *(au->au_intrcnt)++; 425 return (1); 426 } 427 #endif /* AUDIO_C_HANDLER */ 428 429 int 430 am7930swintr(sc0) 431 void *sc0; 432 { 433 struct audioamd_softc *sc = sc0; 434 struct auio *au; 435 int s, ret = 0; 436 437 DPRINTFN(1, ("audiointr: sc=%p\n", sc);); 438 439 au = &sc->sc_au; 440 s = splaudio(); 441 if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) { 442 splx(s); 443 ret = 1; 444 (*sc->sc_rintr)(sc->sc_rarg); 445 s = splaudio(); 446 } 447 if (au->au_pdata > au->au_pend && sc->sc_pintr != NULL) { 448 splx(s); 449 ret = 1; 450 (*sc->sc_pintr)(sc->sc_parg); 451 } else 452 splx(s); 453 return (ret); 454 } 455 456 457 /* indirect write */ 458 void 459 audioamd_codec_iwrite(sc, reg, val) 460 struct am7930_softc *sc; 461 int reg; 462 u_int8_t val; 463 { 464 struct audioamd_softc *mdsc = (struct audioamd_softc *)sc; 465 466 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 467 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val); 468 } 469 470 void 471 audioamd_codec_iwrite16(sc, reg, val) 472 struct am7930_softc *sc; 473 int reg; 474 u_int16_t val; 475 { 476 struct audioamd_softc *mdsc = (struct audioamd_softc *)sc; 477 478 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 479 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val); 480 audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val>>8); 481 } 482 483 484 /* indirect read */ 485 u_int8_t 486 audioamd_codec_iread(sc, reg) 487 struct am7930_softc *sc; 488 int reg; 489 { 490 struct audioamd_softc *mdsc = (struct audioamd_softc *)sc; 491 492 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 493 return (audioamd_codec_dread(mdsc, AM7930_DREG_DR)); 494 } 495 496 u_int16_t 497 audioamd_codec_iread16(sc, reg) 498 struct am7930_softc *sc; 499 int reg; 500 { 501 struct audioamd_softc *mdsc = (struct audioamd_softc *)sc; 502 u_int8_t lo, hi; 503 504 audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg); 505 lo = audioamd_codec_dread(mdsc, AM7930_DREG_DR); 506 hi = audioamd_codec_dread(mdsc, AM7930_DREG_DR); 507 return ((hi << 8) | lo); 508 } 509 510 /* direct read */ 511 u_int8_t 512 audioamd_codec_dread(sc, reg) 513 struct audioamd_softc *sc; 514 int reg; 515 { 516 return (bus_space_read_1(sc->sc_bt, sc->sc_bh, reg)); 517 } 518 519 /* direct write */ 520 void 521 audioamd_codec_dwrite(sc, reg, val) 522 struct audioamd_softc *sc; 523 int reg; 524 u_int8_t val; 525 { 526 bus_space_write_1(sc->sc_bt, sc->sc_bh, reg, val); 527 } 528 529 int 530 audioamd_getdev(addr, retp) 531 void *addr; 532 struct audio_device *retp; 533 { 534 535 *retp = audioamd_device; 536 return (0); 537 } 538 539 #endif /* NAUDIO > 0 */ 540