Home | History | Annotate | Line # | Download | only in dev
audioamd.c revision 1.18
      1 /*	$NetBSD: audioamd.c,v 1.18 2004/07/11 20:01:04 mycroft 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.18 2004/07/11 20:01:04 mycroft 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 __P((void *));
     69 struct auio *auiop;
     70 void	am7930swintr __P((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) __P((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 __P((struct device *,
    102 		struct device *, void *));
    103 int	audioamd_mainbus_match __P((struct device *, struct cfdata *, void *));
    104 void	audioamd_obio_attach __P((struct device *, struct device *, void *));
    105 int	audioamd_obio_match __P((struct device *, struct cfdata *, void *));
    106 void	audioamd_sbus_attach __P((struct device *, struct device *, void *));
    107 int	audioamd_sbus_match __P((struct device *, struct cfdata *, void *));
    108 void	audioamd_attach(struct audioamd_softc *sc, int);
    109 
    110 CFATTACH_DECL(audioamd_mainbus, sizeof(struct audioamd_softc),
    111     audioamd_mainbus_match, audioamd_mainbus_attach, NULL, NULL);
    112 
    113 CFATTACH_DECL(audioamd_obio, sizeof(struct audioamd_softc),
    114     audioamd_obio_match, audioamd_obio_attach, NULL, NULL);
    115 
    116 CFATTACH_DECL(audioamd_sbus, sizeof(struct audioamd_softc),
    117     audioamd_sbus_match, audioamd_sbus_attach, NULL, NULL);
    118 
    119 /*
    120  * Define our interface into the am7930 MI driver.
    121  */
    122 
    123 u_int8_t	audioamd_codec_iread __P((struct am7930_softc *, int));
    124 u_int16_t	audioamd_codec_iread16 __P((struct am7930_softc *, int));
    125 u_int8_t	audioamd_codec_dread __P((struct audioamd_softc *, int));
    126 void	audioamd_codec_iwrite __P((struct am7930_softc *, int, u_int8_t));
    127 void	audioamd_codec_iwrite16 __P((struct am7930_softc *, int, u_int16_t));
    128 void	audioamd_codec_dwrite __P((struct audioamd_softc *, int, u_int8_t));
    129 void	audioamd_onopen __P((struct am7930_softc *sc));
    130 void	audioamd_onclose __P((struct am7930_softc *sc));
    131 
    132 struct am7930_glue audioamd_glue = {
    133 	audioamd_codec_iread,
    134 	audioamd_codec_iwrite,
    135 	audioamd_codec_iread16,
    136 	audioamd_codec_iwrite16,
    137 	audioamd_onopen,
    138 	audioamd_onclose,
    139 	0,
    140 	0,
    141 	0,
    142 };
    143 
    144 /*
    145  * Define our interface to the higher level audio driver.
    146  */
    147 int	audioamd_start_output __P((void *, void *, int, void (*)(void *),
    148 				  void *));
    149 int	audioamd_start_input __P((void *, void *, int, void (*)(void *),
    150 				 void *));
    151 int	audioamd_getdev __P((void *, struct audio_device *));
    152 
    153 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 };
    182 
    183 struct audio_device audioamd_device = {
    184 	"am7930",
    185 	"x",
    186 	"audioamd"
    187 };
    188 
    189 
    190 int
    191 audioamd_mainbus_match(parent, cf, aux)
    192 	struct device *parent;
    193 	struct cfdata *cf;
    194 	void *aux;
    195 {
    196 	struct mainbus_attach_args *ma = aux;
    197 
    198 	if (CPU_ISSUN4)
    199 		return (0);
    200 	return (strcmp(AUDIO_ROM_NAME, ma->ma_name) == 0);
    201 }
    202 
    203 int
    204 audioamd_obio_match(parent, cf, aux)
    205 	struct device *parent;
    206 	struct cfdata *cf;
    207 	void *aux;
    208 {
    209 	union obio_attach_args *uoba = aux;
    210 
    211 	if (uoba->uoba_isobio4 != 0)
    212 		return (0);
    213 
    214 	return (strcmp("audio", uoba->uoba_sbus.sa_name) == 0);
    215 }
    216 
    217 int
    218 audioamd_sbus_match(parent, cf, aux)
    219 	struct device *parent;
    220 	struct cfdata *cf;
    221 	void *aux;
    222 {
    223 	struct sbus_attach_args *sa = aux;
    224 
    225 	return (strcmp(AUDIO_ROM_NAME, sa->sa_name) == 0);
    226 }
    227 
    228 void
    229 audioamd_mainbus_attach(parent, self, aux)
    230 	struct device *parent, *self;
    231 	void *aux;
    232 {
    233 	struct mainbus_attach_args *ma = aux;
    234 	struct audioamd_softc *sc = (struct audioamd_softc *)self;
    235 	bus_space_handle_t bh;
    236 
    237 	sc->sc_bt = ma->ma_bustag;
    238 
    239 	if (bus_space_map(
    240 			ma->ma_bustag,
    241 			ma->ma_paddr,
    242 			AM7930_DREG_SIZE,
    243 			BUS_SPACE_MAP_LINEAR,
    244 			&bh) != 0) {
    245 		printf("%s: cannot map registers\n", self->dv_xname);
    246 		return;
    247 	}
    248 	sc->sc_bh = bh;
    249 	audioamd_attach(sc, ma->ma_pri);
    250 }
    251 
    252 void
    253 audioamd_obio_attach(parent, self, aux)
    254 	struct device *parent, *self;
    255 	void *aux;
    256 {
    257 	union obio_attach_args *uoba = aux;
    258 	struct sbus_attach_args *sa = &uoba->uoba_sbus;
    259 	struct audioamd_softc *sc = (struct audioamd_softc *)self;
    260 	bus_space_handle_t bh;
    261 
    262 	sc->sc_bt = sa->sa_bustag;
    263 
    264 	if (sbus_bus_map(sa->sa_bustag,
    265 			 sa->sa_slot, sa->sa_offset,
    266 			 AM7930_DREG_SIZE,
    267 			 0, &bh) != 0) {
    268 		printf("%s: cannot map registers\n", self->dv_xname);
    269 		return;
    270 	}
    271 	sc->sc_bh = bh;
    272 	audioamd_attach(sc, sa->sa_pri);
    273 }
    274 
    275 void
    276 audioamd_sbus_attach(parent, self, aux)
    277 	struct device *parent, *self;
    278 	void *aux;
    279 {
    280 	struct sbus_attach_args *sa = aux;
    281 	struct audioamd_softc *sc = (struct audioamd_softc *)self;
    282 	bus_space_handle_t bh;
    283 
    284 	sc->sc_bt = sa->sa_bustag;
    285 
    286 	if (sbus_bus_map(sa->sa_bustag,
    287 			 sa->sa_slot, sa->sa_offset,
    288 			 AM7930_DREG_SIZE,
    289 			 0, &bh) != 0) {
    290 		printf("%s: cannot map registers\n", self->dv_xname);
    291 		return;
    292 	}
    293 	sc->sc_bh = bh;
    294 	audioamd_attach(sc, sa->sa_pri);
    295 }
    296 
    297 void
    298 audioamd_attach(sc, pri)
    299 	struct audioamd_softc *sc;
    300 	int pri;
    301 {
    302 
    303 	/*
    304 	 * Set up glue for MI code early; we use some of it here.
    305 	 */
    306 	sc->sc_am7930.sc_glue = &audioamd_glue;
    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_AUDIO,
    316 				  am7930hwintr, sc, amd7930_trap);
    317 
    318 	sc->sc_sicookie = softintr_establish(IPL_SOFTAUDIO, am7930swintr, sc);
    319 	if (sc->sc_sicookie == NULL) {
    320 		printf("\n%s: cannot establish software interrupt\n",
    321 			sc->sc_am7930.sc_dev.dv_xname);
    322 		return;
    323 	}
    324 
    325 	printf(" softpri %d\n", IPL_SOFTAUDIO);
    326 
    327 
    328 	evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
    329 	    sc->sc_am7930.sc_dev.dv_xname, "intr");
    330 
    331 	audio_attach_mi(&sa_hw_if, sc, &sc->sc_am7930.sc_dev);
    332 }
    333 
    334 
    335 void
    336 audioamd_onopen(sc)
    337 	struct am7930_softc *sc;
    338 {
    339 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
    340 
    341 	/* reset pdma state */
    342 	mdsc->sc_rintr = 0;
    343 	mdsc->sc_rarg = 0;
    344 	mdsc->sc_pintr = 0;
    345 	mdsc->sc_parg = 0;
    346 
    347 	mdsc->sc_au.au_rdata = 0;
    348 	mdsc->sc_au.au_pdata = 0;
    349 }
    350 
    351 
    352 void
    353 audioamd_onclose(sc)
    354 	struct am7930_softc *sc;
    355 {
    356 	/* On sparc, just do the chipset-level halt. */
    357 	am7930_halt_input(sc);
    358 	am7930_halt_output(sc);
    359 }
    360 
    361 int
    362 audioamd_start_output(addr, p, cc, intr, arg)
    363 	void *addr;
    364 	void *p;
    365 	int cc;
    366 	void (*intr) __P((void *));
    367 	void *arg;
    368 {
    369 	struct audioamd_softc *sc = addr;
    370 
    371 	DPRINTFN(1, ("sa_start_output: cc=%d %p (%p)\n", cc, intr, arg));
    372 
    373 	audioamd_codec_iwrite(&sc->sc_am7930,
    374 		AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
    375 	DPRINTF(("sa_start_output: started intrs.\n"));
    376 	sc->sc_pintr = intr;
    377 	sc->sc_parg = arg;
    378 	sc->sc_au.au_pdata = p;
    379 	sc->sc_au.au_pend = (char *)p + cc - 1;
    380 	return(0);
    381 }
    382 
    383 int
    384 audioamd_start_input(addr, p, cc, intr, arg)
    385 	void *addr;
    386 	void *p;
    387 	int cc;
    388 	void (*intr) __P((void *));
    389 	void *arg;
    390 {
    391 	struct audioamd_softc *sc = addr;
    392 
    393 	DPRINTFN(1, ("sa_start_input: cc=%d %p (%p)\n", cc, intr, arg));
    394 
    395 	audioamd_codec_iwrite(&sc->sc_am7930,
    396 		AM7930_IREG_INIT, AM7930_INIT_PMS_ACTIVE);
    397 	DPRINTF(("sa_start_input: started intrs.\n"));
    398 	sc->sc_rintr = intr;
    399 	sc->sc_rarg = arg;
    400 	sc->sc_au.au_rdata = p;
    401 	sc->sc_au.au_rend = (char *)p + cc -1;
    402 	return(0);
    403 }
    404 
    405 
    406 /*
    407  * Pseudo-DMA support: either C or locore assember.
    408  */
    409 
    410 int
    411 am7930hwintr(v)
    412 	void *v;
    413 {
    414 	struct audioamd_softc *sc = v;
    415 	struct auio *au = &sc->sc_au;
    416 	u_int8_t *d, *e;
    417 	int k;
    418 
    419 	/* clear interrupt */
    420 	k = audioamd_codec_dread(sc, AM7930_DREG_IR);
    421 	if ((k & (AM7930_IR_DTTHRSH|AM7930_IR_DRTHRSH|AM7930_IR_DSRI|
    422 		  AM7930_IR_DERI|AM7930_IR_BBUFF)) == 0)
    423 		return (0);
    424 
    425 	/* receive incoming data */
    426 	d = au->au_rdata;
    427 	e = au->au_rend;
    428 	if (d && d <= e) {
    429 		*d = audioamd_codec_dread(sc, AM7930_DREG_BBRB);
    430 		au->au_rdata++;
    431 		if (d == e) {
    432 			DPRINTFN(1, ("am7930hwintr: swintr(r) requested"));
    433 			softintr_schedule(sc->sc_sicookie);
    434 		}
    435 	}
    436 
    437 	/* send outgoing data */
    438 	d = au->au_pdata;
    439 	e = au->au_pend;
    440 	if (d && d <= e) {
    441 		audioamd_codec_dwrite(sc, AM7930_DREG_BBTB, *d);
    442 		au->au_pdata++;
    443 		if (d == e) {
    444 			DPRINTFN(1, ("am7930hwintr: swintr(p) requested"));
    445 			softintr_schedule(sc->sc_sicookie);
    446 		}
    447 	}
    448 
    449 	au->au_intrcnt.ev_count++;
    450 	return (1);
    451 }
    452 
    453 void
    454 am7930swintr(sc0)
    455 	void *sc0;
    456 {
    457 	struct audioamd_softc *sc = sc0;
    458 	struct auio *au;
    459 	int s;
    460 
    461 	DPRINTFN(1, ("audiointr: sc=%p\n", sc););
    462 
    463 	au = &sc->sc_au;
    464 	s = splaudio();
    465 	if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) {
    466 		splx(s);
    467 		(*sc->sc_rintr)(sc->sc_rarg);
    468 		s = splaudio();
    469 	}
    470 	if (au->au_pdata > au->au_pend && sc->sc_pintr != NULL) {
    471 		splx(s);
    472 		(*sc->sc_pintr)(sc->sc_parg);
    473 	} else
    474 		splx(s);
    475 }
    476 
    477 
    478 /* indirect write */
    479 void
    480 audioamd_codec_iwrite(sc, reg, val)
    481 	struct am7930_softc *sc;
    482 	int reg;
    483 	u_int8_t val;
    484 {
    485 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
    486 
    487 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
    488 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
    489 }
    490 
    491 void
    492 audioamd_codec_iwrite16(sc, reg, val)
    493 	struct am7930_softc *sc;
    494 	int reg;
    495 	u_int16_t val;
    496 {
    497 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
    498 
    499 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
    500 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val);
    501 	audioamd_codec_dwrite(mdsc, AM7930_DREG_DR, val>>8);
    502 }
    503 
    504 
    505 /* indirect read */
    506 u_int8_t
    507 audioamd_codec_iread(sc, reg)
    508 	struct am7930_softc *sc;
    509 	int reg;
    510 {
    511 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
    512 
    513 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
    514 	return (audioamd_codec_dread(mdsc, AM7930_DREG_DR));
    515 }
    516 
    517 u_int16_t
    518 audioamd_codec_iread16(sc, reg)
    519 	struct am7930_softc *sc;
    520 	int reg;
    521 {
    522 	struct audioamd_softc *mdsc = (struct audioamd_softc *)sc;
    523 	u_int8_t lo, hi;
    524 
    525 	audioamd_codec_dwrite(mdsc, AM7930_DREG_CR, reg);
    526 	lo = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
    527 	hi = audioamd_codec_dread(mdsc, AM7930_DREG_DR);
    528 	return ((hi << 8) | lo);
    529 }
    530 
    531 /* direct read */
    532 u_int8_t
    533 audioamd_codec_dread(sc, reg)
    534 	struct audioamd_softc *sc;
    535 	int reg;
    536 {
    537 	return (bus_space_read_1(sc->sc_bt, sc->sc_bh, reg));
    538 }
    539 
    540 /* direct write */
    541 void
    542 audioamd_codec_dwrite(sc, reg, val)
    543 	struct audioamd_softc *sc;
    544 	int reg;
    545 	u_int8_t val;
    546 {
    547 	bus_space_write_1(sc->sc_bt, sc->sc_bh, reg, val);
    548 }
    549 
    550 int
    551 audioamd_getdev(addr, retp)
    552 	void *addr;
    553 	struct audio_device *retp;
    554 {
    555 
    556 	*retp = audioamd_device;
    557 	return (0);
    558 }
    559 
    560 #endif /* NAUDIO > 0 */
    561