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