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