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