xref: /csrg-svn/sys/sparc/dev/bsd_audio.c (revision 63318)
155102Storek /*
2*63318Sbostic  * Copyright (c) 1991, 1992, 1993
3*63318Sbostic  *	The Regents of the University of California.  All rights reserved.
455102Storek  *
555102Storek  * This software was developed by the Computer Systems Engineering group
655102Storek  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
755102Storek  * contributed to Berkeley.
855102Storek  *
955499Sbostic  * All advertising materials mentioning features or use of this software
1055499Sbostic  * must display the following acknowledgement:
1155499Sbostic  *	This product includes software developed by the University of
1259182Storek  *	California, Lawrence Berkeley Laboratory.
1355499Sbostic  *
1455102Storek  * %sccs.include.redist.c%
1555102Storek  *
16*63318Sbostic  *	@(#)bsd_audio.c	8.1 (Berkeley) 06/11/93
1755102Storek  *
1859328Storek  * from: $Header: bsd_audio.c,v 1.18 93/04/24 16:20:35 leres Exp $ (LBL)
1955102Storek  */
2055102Storek #include "bsdaudio.h"
2155102Storek #if NBSDAUDIO > 0
2255102Storek 
2356536Sbostic #include <sys/param.h>
2456536Sbostic #include <sys/systm.h>
2555102Storek 
2655102Storek #if BSD < 199103
2755102Storek #ifndef SUNOS
2855102Storek #define SUNOS
2955102Storek #endif
3055102Storek #endif
3155102Storek 
3256536Sbostic #include <sys/errno.h>
3356536Sbostic #include <sys/file.h>
3456536Sbostic #include <sys/proc.h>
3556536Sbostic #include <sys/user.h>
3656536Sbostic #include <sys/vnode.h>
3756536Sbostic #include <sys/ioctl.h>
3856536Sbostic #include <sys/time.h>
3955102Storek #ifndef SUNOS
4056536Sbostic #include <sys/tty.h>
4155102Storek #endif
4256536Sbostic #include <sys/uio.h>
4355102Storek 
4455102Storek #ifdef SUNOS
4555102Storek #include <sundev/mbvar.h>
4655102Storek #include <sun4c/intreg.h>
4755102Storek #else
4856536Sbostic #include <sys/device.h>
4956536Sbostic #include <machine/autoconf.h>
5055102Storek #endif
5156536Sbostic #include <machine/cpu.h>
5255102Storek 
5355102Storek /*
5455102Storek  * Avoid name clashes with SunOS so we can config either the bsd or sun
5555102Storek  * streams driver in a SunOS kernel.
5655102Storek  */
5755102Storek #ifdef SUNOS
5859182Storek #include <sbusdev/bsd_audioreg.h>
5959182Storek #include <sbusdev/bsd_audiovar.h>
6059182Storek #include <sbusdev/bsd_audioio.h>
6155102Storek struct selinfo {
6255102Storek 	struct proc *si_proc;
6355102Storek 	int si_coll;
6455102Storek };
6555102Storek #else
6656536Sbostic #include <sparc/dev/bsd_audioreg.h>
6756536Sbostic #include <sparc/dev/bsd_audiovar.h>
6856536Sbostic #include <machine/bsd_audioio.h>
6955102Storek #endif
7055102Storek 
7155102Storek #ifdef SUNOS
7255102Storek #include "bsd_audiocompat.h"
7355102Storek #endif
7455102Storek 
7555102Storek /*
7655102Storek  * Initial/default block size is patchable.
7755102Storek  */
7855102Storek int audio_blocksize = DEFBLKSIZE;
7959182Storek int audio_backlog = 400;		/* 50ms in samples */
8055102Storek 
8155102Storek /*
8255102Storek  * Software state, per AMD79C30 audio chip.
8355102Storek  */
8455102Storek struct audio_softc {
8555102Storek #ifndef SUNOS
8655102Storek 	struct	device sc_dev;		/* base device */
8755102Storek 	struct	intrhand sc_hwih;	/* hardware interrupt vector */
8855102Storek 	struct	intrhand sc_swih;	/* software interrupt vector */
8955102Storek #endif
9055102Storek 	int	sc_interrupts;		/* number of interrupts taken */
9155102Storek 
9255102Storek 	int	sc_open;		/* single use device */
9355102Storek 	u_long	sc_wseek;		/* timestamp of last frame written */
9455102Storek 	u_long	sc_rseek;		/* timestamp of last frame read */
9555102Storek 	struct	mapreg sc_map;		/* current contents of map registers */
9655102Storek 	struct	selinfo sc_wsel;	/* write selector */
9755102Storek 	struct	selinfo sc_rsel;	/* read selector */
9855102Storek 	/*
9955102Storek 	 * keep track of levels so we don't have to convert back from
10055102Storek 	 * MAP gain constants
10155102Storek 	 */
10255102Storek 	int	sc_rlevel;		/* record level */
10355102Storek 	int	sc_plevel;		/* play level */
10455102Storek 	int	sc_mlevel;		/* monitor level */
10555102Storek 
10655102Storek 	/* sc_au is special in that the hardware interrupt handler uses it */
10755102Storek 	struct	auio sc_au;		/* recv and xmit buffers, etc */
10855102Storek 
10955102Storek };
11055102Storek 
11155102Storek /* interrupt interfaces */
11255102Storek #ifndef AUDIO_C_HANDLER
11355102Storek int	audiohwintr __P((void *));
11455102Storek #endif
11555102Storek int	audioswintr __P((void *));
11655102Storek 
11755102Storek /* forward declarations */
11855102Storek int	audio_sleep __P((struct aucb *, int));
11955102Storek void	audio_setmap __P((volatile struct amd7930 *, struct mapreg *));
12055102Storek 
12155102Storek static void init_amd();
12255102Storek 
12355102Storek #if !defined(AUDIO_C_HANDLER) || defined(SUNOS)
12455102Storek struct auio *audio_au;
12555102Storek extern void audio_trap();
12655102Storek #endif
12755102Storek 
12855102Storek #ifdef SUNOS
12955102Storek struct audio_softc audio_softc;
13055102Storek #define SOFTC(dev) &audio_softc
13155102Storek #define UIOMOVE(cp, len, code, uio) uiomove(cp, len, code, uio)
13255102Storek 
13355102Storek #define AUDIOOPEN(d, f, i, p)\
13455102Storek 	audioopen(d, f, i)\
13555102Storek 	dev_t d; int f, i;
13655102Storek #define AUDIOCLOSE(d, f, i, p)\
13755102Storek 	audioclose(d, f, i)\
13855102Storek 	dev_t d; int f, i;
13955102Storek #define AUDIOREAD(d, u, f) \
14055102Storek 	audioread(d, u) dev_t d; struct uio *u;
14155102Storek #define AUDIOWRITE(d, u, f) \
14255102Storek 	audiowrite(d, u) dev_t d; struct uio *u;
14355102Storek #define AUDIOIOCTL(d, c, a, f, o)\
14455102Storek 	audioioctl(d, c, a, f)\
14555102Storek 	dev_t d; int c; caddr_t a; int f;
14655102Storek #define AUDIOSELECT(d, r, p)\
14755102Storek 	audio_select(d, r, p)\
14855102Storek 	dev_t d; int r; struct proc *p;
14955102Storek 
15055102Storek 
15155102Storek #define AUDIO_SET_SWINTR set_intreg(IR_SOFT_INT4, 1)
15255102Storek 
15355102Storek int
audioselect(dev,rw)15455102Storek audioselect(dev, rw)
15555102Storek 	register dev_t dev;
15655102Storek 	int rw;
15755102Storek {
15855102Storek 	return (audio_select(dev, rw, u.u_procp));
15955102Storek }
16055102Storek 
16155102Storek static void
selrecord(p,si)16255102Storek selrecord(p, si)
16355102Storek 	struct proc *p;
16455102Storek 	struct selinfo *si;
16555102Storek {
16655102Storek 	if (si->si_proc != 0)
16755102Storek 		si->si_coll = 1;
16855102Storek 	else
16955102Storek 		si->si_proc = p;
17055102Storek }
17155102Storek #define SELWAKEUP(si) \
17255102Storek {\
17355102Storek 	 if ((si)->si_proc != 0) {\
17455102Storek 		selwakeup((si)->si_proc, (si)->si_coll); \
17555102Storek 		(si)->si_proc = 0;\
17655102Storek 		(si)->si_coll = 0;\
17755102Storek 	}\
17855102Storek }
17955102Storek 
18055102Storek 
18155102Storek static int audioattach();
18255102Storek static int audioidentify();
18355102Storek 
18455102Storek struct dev_ops bsdaudio_ops = {
18555102Storek 	0,
18655102Storek 	audioidentify,
18755102Storek 	audioattach,
18855102Storek };
18955102Storek 
19055102Storek static int
audioidentify(cp)19155102Storek audioidentify(cp)
19255102Storek 	char *cp;
19355102Storek {
19455102Storek 	return (strcmp(cp, "audio") == 0);
19555102Storek }
19655102Storek 
19755102Storek static int
audioattach(dev)19855102Storek audioattach(dev)
19955102Storek 	struct dev_info *dev;
20055102Storek {
20155102Storek 	register struct audio_softc *sc;
20255102Storek 	register volatile struct amd7930 *amd;
20355102Storek 	struct dev_reg *reg;
20455102Storek 
20555102Storek 	sc = &audio_softc;
20655102Storek 	if (dev->devi_nreg != 1 || dev->devi_nintr != 1) {
20755102Storek 		printf("audio: bad config\n");
20855102Storek                 return (-1);
20955102Storek         }
21055102Storek 	reg = dev->devi_reg;
21155102Storek 	amd = (struct amd7930 *)map_regs(reg->reg_addr, reg->reg_size,
21255102Storek 					 reg->reg_bustype);
21355102Storek 	sc->sc_au.au_amd = amd;
21455102Storek 	init_amd(amd);
21555102Storek 
21655102Storek 	audio_au = &sc->sc_au;
21755102Storek #ifndef AUDIO_C_HANDLER
21855102Storek 	settrap(dev->devi_intr->int_pri, audio_trap);
21955102Storek #else
22055102Storek 	/* XXX */
22155102Storek 	addintr(dev->devi_intr->int_pri, audiohwintr, dev->devi_name,
22255102Storek 		dev->devi_unit);
22355102Storek #endif
22455102Storek 	addintr(4, audioswintr, dev->devi_name, dev->devi_unit);
22555102Storek 	report_dev(dev);
22655102Storek 
22755102Storek 	return (0);
22855102Storek }
22955102Storek #else
23055102Storek #define AUDIOOPEN(d, f, i, p) audioopen(dev_t d, int f, int i, struct proc *p)
23155102Storek #define AUDIOCLOSE(d, f, i, p) audioclose(dev_t d, int f, int i, \
23255102Storek 					  struct proc *p)
23355102Storek #define AUDIOREAD(d, u, f) audioread(dev_t d, struct uio *u, int f)
23455102Storek #define AUDIOWRITE(d, u, f) audiowrite(dev_t d, struct uio *u, int f)
23555102Storek #define AUDIOIOCTL(d, c, a, f, o)\
23655102Storek 	audioioctl(dev_t dev, int c, caddr_t a, int f, struct proc *p)
23755102Storek #define AUDIOSELECT(d, r, p) audioselect(dev_t dev, int rw, struct proc *p)
23855102Storek #define SELWAKEUP selwakeup
23955102Storek 
24055102Storek #define AUDIO_SET_SWINTR ienab_bis(IE_L6)
24155102Storek 
24255102Storek /* autoconfiguration driver */
24355102Storek void	audioattach(struct device *, struct device *, void *);
24455102Storek struct	cfdriver audiocd =
24555102Storek     { NULL, "audio", matchbyname, audioattach,
24655102Storek       DV_DULL, sizeof(struct audio_softc) };
24755102Storek #define SOFTC(dev) audiocd.cd_devs[minor(dev)]
24855102Storek #define UIOMOVE(cp, len, code, uio) uiomove(cp, len, uio)
24955102Storek 
25055102Storek /*
25155102Storek  * Audio chip found.
25255102Storek  */
25355102Storek void
audioattach(parent,self,args)25455102Storek audioattach(parent, self, args)
25555102Storek 	struct device *parent, *self;
25655102Storek 	void *args;
25755102Storek {
25855102Storek 	register struct audio_softc *sc = (struct audio_softc *)self;
25955102Storek 	register struct romaux *ra = args;
26055102Storek 	register volatile struct amd7930 *amd;
26155102Storek 	register int pri;
26255102Storek 
26355102Storek 	if (ra->ra_nintr != 1) {
26455102Storek 		printf(": expected 1 interrupt, got %d\n", ra->ra_nintr);
26555102Storek 		return;
26655102Storek 	}
26755102Storek 	pri = ra->ra_intr[0].int_pri;
26855102Storek 	printf(" pri %d, softpri %d\n", pri, PIL_AUSOFT);
26955102Storek 	amd = (volatile struct amd7930 *)(ra->ra_vaddr ?
27055102Storek 	    ra->ra_vaddr : mapiodev(ra->ra_paddr, sizeof *amd));
27155102Storek 	sc->sc_au.au_amd = amd;
27255102Storek 
27355102Storek 	init_amd(amd);
27455102Storek 
27555102Storek #ifndef AUDIO_C_HANDLER
27655102Storek 	audio_au = &sc->sc_au;
27755102Storek 	intr_fasttrap(pri, audio_trap);
27855102Storek #else
27955102Storek 	sc->sc_hwih.ih_fun = audiohwintr;
28055102Storek 	sc->sc_hwih.ih_arg = &sc->sc_au;
28155102Storek 	intr_establish(pri, &sc->sc_hwih);
28255102Storek #endif
28355102Storek 	sc->sc_swih.ih_fun = audioswintr;
28455102Storek 	sc->sc_swih.ih_arg = sc;
28555102Storek 	intr_establish(PIL_AUSOFT, &sc->sc_swih);
28655102Storek }
28755102Storek #endif
28855102Storek 
28955102Storek static void
init_amd(amd)29055102Storek init_amd(amd)
29155102Storek 	register volatile struct amd7930 *amd;
29255102Storek {
29355102Storek 	/* disable interrupts */
29455102Storek 	amd->cr = AMDR_INIT;
29555102Storek 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
29655102Storek 
29755102Storek 	/*
29855102Storek 	 * Initialize the mux unit.  We use MCR3 to route audio (MAP)
29955102Storek 	 * through channel Bb.  MCR1 and MCR2 are unused.
30055102Storek 	 * Setting the INT enable bit in MCR4 will generate an interrupt
30155102Storek 	 * on each converted audio sample.
30255102Storek 	 */
30355102Storek 	amd->cr = AMDR_MUX_1_4;
30455102Storek  	amd->dr = 0;
30555102Storek 	amd->dr = 0;
30655102Storek 	amd->dr = (AMD_MCRCHAN_BB << 4) | AMD_MCRCHAN_BA;
30755102Storek 	amd->dr = AMD_MCR4_INT_ENABLE;
30855102Storek }
30955102Storek 
31055102Storek static int audio_default_level = 150;
31155102Storek static void ausetrgain __P((struct audio_softc *, int));
31255102Storek static void ausetpgain __P((struct audio_softc *, int));
31359182Storek static void ausetmgain __P((struct audio_softc *, int));
31455102Storek static int audiosetinfo __P((struct audio_softc *, struct audio_info *));
31555102Storek static int audiogetinfo __P((struct audio_softc *, struct audio_info *));
31655102Storek struct sun_audio_info;
31755102Storek static int sunaudiosetinfo __P((struct audio_softc *,
31855102Storek 				struct sun_audio_info *));
31955102Storek static int sunaudiogetinfo __P((struct audio_softc *,
32055102Storek 				struct sun_audio_info *));
32155102Storek static void audio_setmmr2 __P((volatile struct amd7930 *, int));
32255102Storek 
32359328Storek /* ARGSUSED */
32455102Storek int
AUDIOOPEN(dev,flags,ifmt,p)32555102Storek AUDIOOPEN(dev, flags, ifmt, p)
32655102Storek {
32755102Storek 	register struct audio_softc *sc;
32855102Storek 	register volatile struct amd7930 *amd;
32959328Storek 	int unit = minor(dev);
33055102Storek 
33155102Storek #ifdef SUNOS
33255102Storek 	if (unit > 0)
33355102Storek 		return (ENXIO);
33455102Storek 	sc = &audio_softc;
33555102Storek #else
33655102Storek 	if (unit >= audiocd.cd_ndevs || (sc = audiocd.cd_devs[unit]) == NULL)
33755102Storek 		return (ENXIO);
33855102Storek #endif
33955102Storek 	if (sc->sc_open)
34055102Storek 		return (EBUSY);
34155102Storek 	sc->sc_open = 1;
34255102Storek 
34355102Storek 	sc->sc_au.au_lowat = audio_blocksize;
34455102Storek 	sc->sc_au.au_hiwat = AUCB_SIZE - sc->sc_au.au_lowat;
34555102Storek 	sc->sc_au.au_blksize = audio_blocksize;
34659182Storek 	sc->sc_au.au_backlog = audio_backlog;
34755102Storek 
34855102Storek 	/* set up read and write blocks and `dead sound' zero value. */
34955102Storek 	AUCB_INIT(&sc->sc_au.au_rb);
35055102Storek 	sc->sc_au.au_rb.cb_thresh = AUCB_SIZE;
35155102Storek 	AUCB_INIT(&sc->sc_au.au_wb);
35255102Storek 	sc->sc_au.au_wb.cb_thresh = -1;
35355102Storek 
35455102Storek 	/* nothing read or written yet */
35555102Storek 	sc->sc_rseek = 0;
35655102Storek 	sc->sc_wseek = 0;
35755102Storek 
35855102Storek 	bzero((char *)&sc->sc_map, sizeof sc->sc_map);
35955102Storek 	/* default to speaker */
36055102Storek 	sc->sc_map.mr_mmr2 = AMD_MMR2_AINB | AMD_MMR2_LS;
36155102Storek 
36255102Storek 	/* enable interrupts and set parameters established above */
36355102Storek 	amd = sc->sc_au.au_amd;
36455102Storek 	audio_setmmr2(amd, sc->sc_map.mr_mmr2);
36555102Storek 	ausetrgain(sc, audio_default_level);
36655102Storek 	ausetpgain(sc, audio_default_level);
36759182Storek 	ausetmgain(sc, 0);
36855102Storek 	amd->cr = AMDR_INIT;
36955102Storek 	amd->dr = AMD_INIT_PMS_ACTIVE;
37055102Storek 
37155102Storek 	return (0);
37255102Storek }
37355102Storek 
37455102Storek static int
audio_drain(sc)37555102Storek audio_drain(sc)
37655102Storek 	register struct audio_softc *sc;
37755102Storek {
37855102Storek 	register int error;
37955102Storek 
38055102Storek 	while (!AUCB_EMPTY(&sc->sc_au.au_wb))
38155102Storek 		if ((error = audio_sleep(&sc->sc_au.au_wb, 0)) != 0)
38255102Storek 			return (error);
38355102Storek 	return (0);
38455102Storek }
38555102Storek 
38655102Storek /*
38755102Storek  * Close an audio chip.
38855102Storek  */
38955102Storek /* ARGSUSED */
39055102Storek int
AUDIOCLOSE(dev,flags,ifmt,p)39155102Storek AUDIOCLOSE(dev, flags, ifmt, p)
39255102Storek {
39355102Storek 	register struct audio_softc *sc = SOFTC(dev);
39455102Storek 	register volatile struct amd7930 *amd;
39555102Storek 	register struct aucb *cb;
39655102Storek 	register int s;
39755102Storek 
39855102Storek 	/*
39955102Storek 	 * Block until output drains, but allow ^C interrupt.
40055102Storek 	 */
40155102Storek 	sc->sc_au.au_lowat = 0;	/* avoid excessive wakeups */
40255102Storek 	s = splaudio();
40355102Storek 	/*
40455102Storek 	 * If there is pending output, let it drain (unless
40555102Storek 	 * the output is paused).
40655102Storek 	 */
40755102Storek 	cb = &sc->sc_au.au_wb;
40855102Storek 	if (!AUCB_EMPTY(cb) && !cb->cb_pause)
40955102Storek 		(void)audio_drain(sc);
41055102Storek 	/*
41155102Storek 	 * Disable interrupts, clear open flag, and done.
41255102Storek 	 */
41355102Storek 	amd = sc->sc_au.au_amd;
41455102Storek 	amd->cr = AMDR_INIT;
41555102Storek 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
41655102Storek 	splx(s);
41755102Storek 	sc->sc_open = 0;
41855102Storek 	return (0);
41955102Storek }
42055102Storek 
42155102Storek int
audio_sleep(cb,thresh)42255102Storek audio_sleep(cb, thresh)
42355102Storek 	register struct aucb *cb;
42455102Storek 	register int thresh;
42555102Storek {
42655102Storek 	register int error;
42759182Storek 	register int s = splaudio();
42855102Storek 
42955102Storek 	cb->cb_thresh = thresh;
43055102Storek 	error = tsleep((caddr_t)cb, (PZERO + 1) | PCATCH, "audio", 0);
43159182Storek 	splx(s);
43255102Storek 	return (error);
43355102Storek }
43455102Storek 
43559328Storek /* ARGSUSED */
43655102Storek int
AUDIOREAD(dev,uio,ioflag)43755102Storek AUDIOREAD(dev, uio, ioflag)
43855102Storek {
43955102Storek 	register struct audio_softc *sc = SOFTC(dev);
44055102Storek 	register struct aucb *cb;
44159182Storek 	register int n, head, taildata, error;
44255102Storek 	register int blocksize = sc->sc_au.au_blksize;
44355102Storek 
44455102Storek 	if (uio->uio_resid == 0)
44555102Storek 		return (0);
44655102Storek 	cb = &sc->sc_au.au_rb;
44755102Storek 	error = 0;
44855102Storek 	cb->cb_drops = 0;
44955102Storek 	sc->sc_rseek = sc->sc_au.au_stamp - AUCB_LEN(cb);
45055102Storek 	do {
45155102Storek 		while (AUCB_LEN(cb) < blocksize) {
45255102Storek #ifndef SUNOS
45355102Storek 			if (ioflag & IO_NDELAY) {
45455102Storek 				error = EWOULDBLOCK;
45559182Storek 				return (error);
45655102Storek 			}
45755102Storek #endif
45855102Storek 			if ((error = audio_sleep(cb, blocksize)) != 0)
45959182Storek 				return (error);
46055102Storek 		}
46155102Storek 		/*
46255102Storek 		 * The space calculation can only err on the short
46355102Storek 		 * side if an interrupt occurs during processing:
46455102Storek 		 * only cb_tail is altered in the interrupt code.
46555102Storek 		 */
46655102Storek 		head = cb->cb_head;
46755102Storek 		if ((n = AUCB_LEN(cb)) > uio->uio_resid)
46855102Storek 			n = uio->uio_resid;
46955102Storek 		taildata = AUCB_SIZE - head;
47055102Storek 		if (n > taildata) {
47155102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + head,
47255102Storek 					taildata, UIO_READ, uio);
47355102Storek 			if (error == 0)
47455102Storek 				error = UIOMOVE((caddr_t)cb->cb_data,
47555102Storek 						n - taildata, UIO_READ, uio);
47655102Storek 		} else
47755102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + head, n,
47855102Storek 					UIO_READ, uio);
47955102Storek 		if (error)
48059182Storek 			break;
48155102Storek 		head = AUCB_MOD(head + n);
48255102Storek 		cb->cb_head = head;
48355102Storek 	} while (uio->uio_resid >= blocksize);
48459182Storek 
48555102Storek 	return (error);
48655102Storek }
48755102Storek 
48859328Storek /* ARGSUSED */
48955102Storek int
AUDIOWRITE(dev,uio,ioflag)49055102Storek AUDIOWRITE(dev, uio, ioflag)
49155102Storek {
49255102Storek 	register struct audio_softc *sc = SOFTC(dev);
49355102Storek 	register struct aucb *cb = &sc->sc_au.au_wb;
49459328Storek 	register int n, tail, tailspace, error, first, watermark;
49555102Storek 
49655102Storek 	error = 0;
49755102Storek 	first = 1;
49855102Storek 	while (uio->uio_resid > 0) {
49955102Storek 		watermark = sc->sc_au.au_hiwat;
50055102Storek 		while (AUCB_LEN(cb) > watermark) {
50155102Storek #ifndef SUNOS
50255102Storek 			if (ioflag & IO_NDELAY) {
50355102Storek 				error = EWOULDBLOCK;
50459182Storek 				return (error);
50555102Storek 			}
50655102Storek #endif
50755102Storek 			if ((error = audio_sleep(cb, watermark)) != 0)
50859182Storek 				return (error);
50955102Storek 			watermark = sc->sc_au.au_lowat;
51055102Storek 		}
51155102Storek 		/*
51255102Storek 		 * The only value that can change on an interrupt is
51355102Storek 		 * cb->cb_head.  We only pull that out once to decide
51455102Storek 		 * how much to write into cb_data; if we lose a race
51555102Storek 		 * and cb_head changes, we will merely be overly
51655102Storek 		 * conservative.  For a legitimate time stamp,
51755102Storek 		 * however, we need to synchronize the accesses to
51855102Storek 		 * au_stamp and cb_head at a high ipl below.
51955102Storek 		 */
52059182Storek 		tail = cb->cb_tail;
52159182Storek 		if ((n = (AUCB_SIZE - 1) - AUCB_LEN(cb)) > uio->uio_resid) {
52255102Storek 			n = uio->uio_resid;
52359182Storek 			if (cb->cb_head == tail &&
52459182Storek 			    n <= sc->sc_au.au_blksize &&
52559182Storek 			    sc->sc_au.au_stamp - sc->sc_wseek > 400) {
52659182Storek 				/*
52759182Storek 				 * the write is 'small', the buffer is empty
52859182Storek 				 * and we have been silent for at least 50ms
52959182Storek 				 * so we might be dealing with an application
53059182Storek 				 * that writes frames synchronously with
53159182Storek 				 * reading them.  If so, we need an output
53259182Storek 				 * backlog to cover scheduling delays or
53359182Storek 				 * there will be gaps in the sound output.
53459182Storek 				 * Also take this opportunity to reset the
53559182Storek 				 * buffer pointers in case we ended up on
53659182Storek 				 * a bad boundary (odd byte, blksize bytes
53759182Storek 				 * from end, etc.).
53859182Storek 				 */
53959182Storek 				register u_int* ip;
54059182Storek 				register int muzero = 0x7f7f7f7f;
54159182Storek 				register int i = splaudio();
54259182Storek 				cb->cb_head = cb->cb_tail = 0;
54359182Storek 				splx(i);
54459182Storek 				tail = sc->sc_au.au_backlog;
54559182Storek 				ip = (u_int*)cb->cb_data;
54659182Storek 				for (i = tail >> 2; --i >= 0; )
54759182Storek 					*ip++ = muzero;
54859182Storek 			}
54959182Storek 		}
55055102Storek 		tailspace = AUCB_SIZE - tail;
55155102Storek 		if (n > tailspace) {
55255102Storek 			/* write first part at tail and rest at head */
55355102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + tail,
55455102Storek 					tailspace, UIO_WRITE, uio);
55555102Storek 			if (error == 0)
55655102Storek 				error = UIOMOVE((caddr_t)cb->cb_data,
55755102Storek 						n - tailspace, UIO_WRITE, uio);
55855102Storek 		} else
55955102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + tail, n,
56055102Storek 					UIO_WRITE, uio);
56155102Storek 		if (error)
56259182Storek 			break;
56359182Storek 
56455102Storek 		tail = AUCB_MOD(tail + n);
56555102Storek 		if (first) {
56659182Storek 			register int s = splaudio();
56759182Storek 			sc->sc_wseek = AUCB_LEN(cb) + sc->sc_au.au_stamp + 1;
56855102Storek 			/*
56955102Storek 			 * To guarantee that a write is contiguous in the
57055102Storek 			 * sample space, we clear the drop count the first
57155102Storek 			 * time through.  If we later get drops, we will
57255102Storek 			 * break out of the loop below, before writing
57355102Storek 			 * a new frame.
57455102Storek 			 */
57555102Storek 			cb->cb_drops = 0;
57659182Storek 			cb->cb_tail = tail;
57759182Storek 			splx(s);
57859182Storek 			first = 0;
57959182Storek 		} else {
58059182Storek 			if (cb->cb_drops != 0)
58159182Storek 				break;
58259182Storek 			cb->cb_tail = tail;
58355102Storek 		}
58455102Storek 	}
58555102Storek 	return (error);
58655102Storek }
58755102Storek 
58855102Storek /* Sun audio compatibility */
58955102Storek struct sun_audio_prinfo {
59055102Storek 	u_int	sample_rate;
59155102Storek 	u_int	channels;
59255102Storek 	u_int	precision;
59355102Storek 	u_int	encoding;
59455102Storek 	u_int	gain;
59555102Storek 	u_int	port;
59655102Storek 	u_int	reserved0[4];
59755102Storek 	u_int	samples;
59855102Storek 	u_int	eof;
59955102Storek 	u_char	pause;
60055102Storek 	u_char	error;
60155102Storek 	u_char	waiting;
60255102Storek 	u_char	reserved1[3];
60355102Storek 	u_char	open;
60455102Storek 	u_char	active;
60555102Storek };
60655102Storek struct sun_audio_info {
60755102Storek 	struct sun_audio_prinfo play;
60855102Storek 	struct sun_audio_prinfo record;
60955102Storek 	u_int monitor_gain;
61055102Storek 	u_int reserved[4];
61155102Storek };
61255102Storek 
61355102Storek #ifndef SUNOS
61455102Storek #define SUNAUDIO_GETINFO	_IOR('A', 1, struct sun_audio_info)
61555102Storek #define SUNAUDIO_SETINFO	_IOWR('A', 2, struct sun_audio_info)
61655102Storek #else
61755102Storek #define SUNAUDIO_GETINFO	_IOR(A, 1, struct sun_audio_info)
61855102Storek #define SUNAUDIO_SETINFO	_IOWR(A, 2, struct sun_audio_info)
61955102Storek #endif
62055102Storek 
62159328Storek /* ARGSUSED */
62255102Storek int
AUDIOIOCTL(dev,cmd,addr,flag,p)62355102Storek AUDIOIOCTL(dev, cmd, addr, flag, p)
62455102Storek {
62555102Storek 	register struct audio_softc *sc = SOFTC(dev);
62659328Storek 	int error = 0, s;
62755102Storek 
62855102Storek 	switch (cmd) {
62955102Storek 
63055102Storek 	case AUDIO_GETMAP:
63155102Storek 		bcopy((caddr_t)&sc->sc_map, addr, sizeof(sc->sc_map));
63255102Storek 		break;
63355102Storek 
63455102Storek 	case AUDIO_SETMAP:
63555102Storek 		bcopy(addr, (caddr_t)&sc->sc_map, sizeof(sc->sc_map));
63655102Storek 		sc->sc_map.mr_mmr2 &= 0x7f;
63755102Storek 		audio_setmap(sc->sc_au.au_amd, &sc->sc_map);
63855102Storek 		break;
63955102Storek 
64055102Storek 	case AUDIO_FLUSH:
64155102Storek 		s = splaudio();
64255102Storek 		AUCB_INIT(&sc->sc_au.au_rb);
64355102Storek 		AUCB_INIT(&sc->sc_au.au_wb);
64459182Storek 		sc->sc_au.au_stamp = 0;
64555102Storek 		splx(s);
64655102Storek 		sc->sc_wseek = 0;
64755102Storek 		sc->sc_rseek = 0;
64855102Storek 		break;
64955102Storek 
65055102Storek 	/*
65155102Storek 	 * Number of read samples dropped.  We don't know where or
65255102Storek 	 * when they were dropped.
65355102Storek 	 */
65455102Storek 	case AUDIO_RERROR:
65555102Storek 		*(int *)addr = sc->sc_au.au_rb.cb_drops != 0;
65655102Storek 		break;
65755102Storek 
65855102Storek 	/*
65959182Storek 	 * How many samples will elapse until mike hears the first
66059182Storek 	 * sample of what we last wrote?
66155102Storek 	 */
66255102Storek 	case AUDIO_WSEEK:
66359182Storek 		s = splaudio();
66459182Storek 		*(u_long *)addr = sc->sc_wseek - sc->sc_au.au_stamp
66559182Storek 				  + AUCB_LEN(&sc->sc_au.au_rb);
66659182Storek 		splx(s);
66755102Storek 		break;
66855102Storek 
66955102Storek 	case AUDIO_SETINFO:
67055102Storek 		error = audiosetinfo(sc, (struct audio_info *)addr);
67155102Storek 		break;
67255102Storek 
67355102Storek 	case AUDIO_GETINFO:
67455102Storek 		error = audiogetinfo(sc, (struct audio_info *)addr);
67555102Storek 		break;
67655102Storek 
67755102Storek 	case SUNAUDIO_GETINFO:
67855102Storek 		error = sunaudiogetinfo(sc, (struct sun_audio_info *)addr);
67955102Storek 		break;
68055102Storek 
68155102Storek 	case SUNAUDIO_SETINFO:
68255102Storek 		error = sunaudiosetinfo(sc, (struct sun_audio_info *)addr);
68355102Storek 		break;
68455102Storek 
68555102Storek 	case AUDIO_DRAIN:
68655102Storek 		error = audio_drain(sc);
68755102Storek 		break;
68855102Storek 
68955102Storek 	default:
69055102Storek 		error = EINVAL;
69155102Storek 		break;
69255102Storek 	}
69355102Storek 	return (error);
69455102Storek }
69555102Storek 
69659328Storek /* ARGSUSED */
69755102Storek int
AUDIOSELECT(dev,rw,p)69855102Storek AUDIOSELECT(dev, rw, p)
69955102Storek {
70055102Storek 	register struct audio_softc *sc = SOFTC(dev);
70155102Storek 	register struct aucb *cb;
70255102Storek 	register int s = splaudio();
70355102Storek 
70455102Storek 	switch (rw) {
70555102Storek 
70655102Storek 	case FREAD:
70755102Storek 		cb = &sc->sc_au.au_rb;
70855102Storek 		if (AUCB_LEN(cb) >= sc->sc_au.au_blksize) {
70955102Storek 			splx(s);
71055102Storek 			return (1);
71155102Storek 		}
71255102Storek 		selrecord(p, &sc->sc_rsel);
71355102Storek 		cb->cb_thresh = sc->sc_au.au_blksize;
71455102Storek 		break;
71555102Storek 
71655102Storek 	case FWRITE:
71755102Storek 		cb = &sc->sc_au.au_wb;
71855102Storek 		if (AUCB_LEN(cb) <= sc->sc_au.au_lowat) {
71955102Storek 			splx(s);
72055102Storek 			return (1);
72155102Storek 		}
72255102Storek 		selrecord(p, &sc->sc_wsel);
72355102Storek 		cb->cb_thresh = sc->sc_au.au_lowat;
72455102Storek 		break;
72555102Storek 	}
72655102Storek 	splx(s);
72755102Storek 	return (0);
72855102Storek }
72955102Storek 
73055102Storek #ifdef AUDIO_C_HANDLER
73155102Storek int
audiohwintr(au0)73255102Storek audiohwintr(au0)
73355102Storek 	void *au0;
73455102Storek {
73555102Storek #ifdef SUNOS
73655102Storek 	register struct auio *au = audio_au;
73755102Storek #else
73855102Storek 	register struct auio *au = au0;
73955102Storek #endif
74055102Storek 	register volatile struct amd7930 *amd = au->au_amd;
74155102Storek 	register struct aucb *cb;
74255102Storek 	register int h, t, k;
74355102Storek 
74455102Storek 	k = amd->ir;		/* clear interrupt */
74555102Storek 	++au->au_stamp;
74655102Storek 
74755102Storek 	/* receive incoming data */
74855102Storek 	cb = &au->au_rb;
74955102Storek 	h = cb->cb_head;
75055102Storek 	t = cb->cb_tail;
75155102Storek 	k = AUCB_MOD(t + 1);
75255102Storek 	if (h == k)
75355102Storek 		cb->cb_drops++;
75455102Storek 	else if  (cb->cb_pause != 0)
75555102Storek 		cb->cb_pdrops++;
75655102Storek 	else {
75755102Storek 		cb->cb_data[t] = amd->bbrb;
75855102Storek 		cb->cb_tail = t = k;
75955102Storek 	}
76055102Storek 	if (AUCB_MOD(t - h) >= cb->cb_thresh) {
76155102Storek 		cb->cb_thresh = AUCB_SIZE;
76255102Storek 		cb->cb_waking = 1;
76355102Storek 		AUDIO_SET_SWINTR;
76455102Storek 	}
76555102Storek 	/* send outgoing data */
76655102Storek 	cb = &au->au_wb;
76755102Storek 	h = cb->cb_head;
76855102Storek 	t = cb->cb_tail;
76955102Storek 	if (h == t)
77055102Storek 		cb->cb_drops++;
77155102Storek 	else if (cb->cb_pause != 0)
77255102Storek 		cb->cb_pdrops++;
77355102Storek 	else {
77455102Storek 		cb->cb_head = h = AUCB_MOD(h + 1);
77555102Storek 		amd->bbtb = cb->cb_data[h];
77655102Storek 	}
77755102Storek 	if (AUCB_MOD(t - h) <= cb->cb_thresh) {
77855102Storek 		cb->cb_thresh = -1;
77955102Storek 		cb->cb_waking = 1;
78055102Storek 		AUDIO_SET_SWINTR;
78155102Storek 	}
78255102Storek 	return (1);
78355102Storek }
78455102Storek #endif
78555102Storek 
78659328Storek /* ARGSUSED */
78755102Storek int
audioswintr(sc0)78855102Storek audioswintr(sc0)
78955102Storek 	void *sc0;
79055102Storek {
79155102Storek 	register struct audio_softc *sc;
79255102Storek 	register int s, ret = 0;
79355102Storek #ifdef SUNOS
79455102Storek 	sc = &audio_softc;
79555102Storek #else
79655102Storek 	sc = sc0;
79755102Storek #endif
79855102Storek 	s = splaudio();
79955102Storek 	if (sc->sc_au.au_rb.cb_waking != 0) {
80055102Storek 		sc->sc_au.au_rb.cb_waking = 0;
80155102Storek 		splx(s);
80255102Storek 		ret = 1;
80355102Storek 		wakeup((caddr_t)&sc->sc_au.au_rb);
80455102Storek 		SELWAKEUP(&sc->sc_rsel);
80555102Storek 	}
80655102Storek 	if (sc->sc_au.au_wb.cb_waking != 0) {
80755102Storek 		sc->sc_au.au_wb.cb_waking = 0;
80855102Storek 		splx(s);
80955102Storek 		ret = 1;
81055102Storek 		wakeup((caddr_t)&sc->sc_au.au_wb);
81155102Storek 		SELWAKEUP(&sc->sc_wsel);
81255102Storek 	} else
81355102Storek 		splx(s);
81455102Storek 	return (ret);
81555102Storek }
81655102Storek 
81755102Storek /* Write 16 bits of data from variable v to the data port of the audio chip */
81855102Storek 
81959182Storek #define	WAMD16(amd, v) ((amd)->dr = (v), (amd)->dr = (v) >> 8)
82055102Storek 
82155102Storek void
audio_setmap(amd,map)82255102Storek audio_setmap(amd, map)
82355102Storek 	register volatile struct amd7930 *amd;
82455102Storek 	register struct mapreg *map;
82555102Storek {
82655102Storek 	register int i, s, v;
82755102Storek 
82855102Storek 	s = splaudio();
82955102Storek 	amd->cr = AMDR_MAP_1_10;
83055102Storek 	for (i = 0; i < 8; i++) {
83155102Storek 		v = map->mr_x[i];
83255102Storek 		WAMD16(amd, v);
83355102Storek 	}
83455102Storek 	for (i = 0; i < 8; ++i) {
83555102Storek 		v = map->mr_r[i];
83655102Storek 		WAMD16(amd, v);
83755102Storek 	}
83855102Storek 	v = map->mr_gx; WAMD16(amd, v);
83955102Storek 	v = map->mr_gr; WAMD16(amd, v);
84055102Storek 	v = map->mr_ger; WAMD16(amd, v);
84155102Storek 	v = map->mr_stgr; WAMD16(amd, v);
84255102Storek 	v = map->mr_ftgr; WAMD16(amd, v);
84355102Storek 	v = map->mr_atgr; WAMD16(amd, v);
84455102Storek 	amd->dr = map->mr_mmr1;
84555102Storek 	amd->dr = map->mr_mmr2;
84655102Storek 	splx(s);
84755102Storek }
84855102Storek 
84955102Storek /*
85055102Storek  * Set the mmr1 register and one other 16 bit register in the audio chip.
85155102Storek  * The other register is indicated by op and val.
85255102Storek  */
85355102Storek void
audio_setmmr1(amd,mmr1,op,val)85455102Storek audio_setmmr1(amd, mmr1, op, val)
85555102Storek 	register volatile struct amd7930 *amd;
85655102Storek 	register int mmr1;
85755102Storek 	register int op;
85855102Storek 	register int val;
85955102Storek {
86055102Storek 	register int s = splaudio();
86155102Storek 
86255102Storek 	amd->cr = AMDR_MAP_MMR1;
86355102Storek 	amd->dr = mmr1;
86455102Storek 	amd->cr = op;
86555102Storek 	WAMD16(amd, val);
86655102Storek 	splx(s);
86755102Storek }
86855102Storek 
86955102Storek /*
87059182Storek  * Set the mmr2 register.
87155102Storek  */
87255102Storek static void
audio_setmmr2(amd,mmr2)87355102Storek audio_setmmr2(amd, mmr2)
87455102Storek 	register volatile struct amd7930 *amd;
87555102Storek 	register int mmr2;
87655102Storek {
87755102Storek 	register int s = splaudio();
87855102Storek 
87955102Storek 	amd->cr = AMDR_MAP_MMR2;
88055102Storek 	amd->dr = mmr2;
88155102Storek 	splx(s);
88255102Storek }
88355102Storek 
88459182Storek /*
88559182Storek  * gx, gr & stg gains.  this table must contain 256 elements with
88659182Storek  * the 0th being "infinity" (the magic value 9008).  The remaining
88759182Storek  * elements match sun's gain curve (but with higher resolution):
88859182Storek  * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
88959182Storek  */
89059182Storek static const u_short gx_coeff[256] = {
89159182Storek 	0x9008, 0x8b7c, 0x8b51, 0x8b45, 0x8b42, 0x8b3b, 0x8b36, 0x8b33,
89259182Storek 	0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
89359182Storek 	0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
89459182Storek 	0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
89559182Storek 	0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
89659182Storek 	0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
89759182Storek 	0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
89859182Storek 	0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
89959182Storek 	0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
90059182Storek 	0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
90159182Storek 	0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
90259182Storek 	0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
90359182Storek 	0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
90459182Storek 	0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
90559182Storek 	0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
90659182Storek 	0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
90759182Storek 	0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
90859182Storek 	0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
90959182Storek 	0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
91059182Storek 	0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
91159182Storek 	0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
91259182Storek 	0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
91359182Storek 	0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
91459182Storek 	0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
91559182Storek 	0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
91659182Storek 	0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
91759182Storek 	0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
91859182Storek 	0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
91959182Storek 	0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
92059182Storek 	0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
92159182Storek 	0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
92259182Storek 	0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
92355102Storek };
92455102Storek 
92559182Storek /*
92659182Storek  * second stage play gain.
92759182Storek  */
92859182Storek static const u_short ger_coeff[] = {
92959182Storek 	0x431f, /* 5. dB */
93059182Storek 	0x331f, /* 5.5 dB */
93159182Storek 	0x40dd, /* 6. dB */
93259182Storek 	0x11dd, /* 6.5 dB */
93359182Storek 	0x440f, /* 7. dB */
93459182Storek 	0x411f, /* 7.5 dB */
93559182Storek 	0x311f, /* 8. dB */
93659182Storek 	0x5520, /* 8.5 dB */
93759182Storek 	0x10dd, /* 9. dB */
93859182Storek 	0x4211, /* 9.5 dB */
93959182Storek 	0x410f, /* 10. dB */
94059182Storek 	0x111f, /* 10.5 dB */
94159182Storek 	0x600b, /* 11. dB */
94259182Storek 	0x00dd, /* 11.5 dB */
94359182Storek 	0x4210, /* 12. dB */
94459182Storek 	0x110f, /* 13. dB */
94559182Storek 	0x7200, /* 14. dB */
94659182Storek 	0x2110, /* 15. dB */
94759182Storek 	0x2200, /* 15.9 dB */
94859182Storek 	0x000b, /* 16.9 dB */
94959182Storek 	0x000f  /* 18. dB */
95059182Storek #define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
95155102Storek };
95255102Storek 
95355102Storek static void
ausetrgain(sc,level)95455102Storek ausetrgain(sc, level)
95555102Storek 	register struct audio_softc *sc;
95655102Storek 	register int level;
95755102Storek {
95855102Storek 	level &= 0xff;
95955102Storek 	sc->sc_rlevel = level;
96059182Storek 	sc->sc_map.mr_mmr1 |= AMD_MMR1_GX;
96159182Storek 	sc->sc_map.mr_gx = gx_coeff[level];
96255102Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
96355102Storek 		      AMDR_MAP_GX, sc->sc_map.mr_gx);
96455102Storek }
96555102Storek 
96655102Storek static void
ausetpgain(sc,level)96755102Storek ausetpgain(sc, level)
96855102Storek 	register struct audio_softc *sc;
96955102Storek 	register int level;
97055102Storek {
97159182Storek 	register int gi, s;
97259182Storek 	register volatile struct amd7930 *amd;
97359182Storek 
97455102Storek 	level &= 0xff;
97555102Storek 	sc->sc_plevel = level;
97659182Storek 	sc->sc_map.mr_mmr1 |= AMD_MMR1_GER|AMD_MMR1_GR;
97759182Storek 	level *= 256 + NGER;
97859182Storek 	level >>= 8;
97959182Storek 	if (level >= 256) {
98059182Storek 		gi = level - 256;
98159182Storek 		level = 255;
98259182Storek 	} else
98359182Storek 		gi = 0;
98459182Storek 	sc->sc_map.mr_ger = ger_coeff[gi];
98559182Storek 	sc->sc_map.mr_gr = gx_coeff[level];
98659182Storek 
98759182Storek 	amd = sc->sc_au.au_amd;
98859182Storek 	s = splaudio();
98959182Storek 	amd->cr = AMDR_MAP_MMR1;
99059182Storek 	amd->dr = sc->sc_map.mr_mmr1;
99159182Storek 	amd->cr = AMDR_MAP_GR;
99259182Storek 	gi =  sc->sc_map.mr_gr;
99359182Storek 	WAMD16(amd, gi);
99459182Storek 	amd->cr = AMDR_MAP_GER;
99559182Storek 	gi =  sc->sc_map.mr_ger;
99659182Storek 	WAMD16(amd, gi);
99759182Storek 	splx(s);
99855102Storek }
99955102Storek 
100055102Storek static void
ausetmgain(sc,level)100155102Storek ausetmgain(sc, level)
100255102Storek 	register struct audio_softc *sc;
100355102Storek 	register int level;
100455102Storek {
100555102Storek 	level &= 0xff;
100655102Storek 	sc->sc_mlevel = level;
100759182Storek 	sc->sc_map.mr_mmr1 |= AMD_MMR1_STG;
100859182Storek 	sc->sc_map.mr_stgr = gx_coeff[level];
100955102Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
101055102Storek 		      AMDR_MAP_STG, sc->sc_map.mr_stgr);
101155102Storek }
101255102Storek 
101355102Storek static int
audiosetinfo(sc,ai)101455102Storek audiosetinfo(sc, ai)
101555102Storek 	struct audio_softc *sc;
101655102Storek 	struct audio_info *ai;
101755102Storek {
101855102Storek 	struct audio_prinfo *r = &ai->record, *p = &ai->play;
101955102Storek 	register int s, bsize;
102055102Storek 
102155102Storek 	if (p->gain != ~0)
102255102Storek 		ausetpgain(sc, p->gain);
102355102Storek 	if (r->gain != ~0)
102455102Storek 		ausetrgain(sc, r->gain);
102555102Storek 	if (ai->monitor_gain != ~0)
102659182Storek 		ausetmgain(sc, ai->monitor_gain);
102755102Storek 	if (p->port == AUDIO_SPEAKER) {
102855102Storek 		sc->sc_map.mr_mmr2 |= AMD_MMR2_LS;
102955102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
103055102Storek 	} else if (p->port == AUDIO_HEADPHONE) {
103155102Storek 		sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS;
103255102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
103355102Storek 	}
103455102Storek 	if (p->pause != (u_char)~0)
103555102Storek 		sc->sc_au.au_wb.cb_pause = p->pause;
103655102Storek 	if (r->pause != (u_char)~0)
103755102Storek 		sc->sc_au.au_rb.cb_pause = r->pause;
103855102Storek 
103955102Storek 	if (ai->blocksize != ~0) {
104055102Storek 		if (ai->blocksize == 0)
104155102Storek 			bsize = ai->blocksize = DEFBLKSIZE;
104255102Storek 		else if (ai->blocksize > MAXBLKSIZE)
104355102Storek 			bsize = ai->blocksize = MAXBLKSIZE;
104455102Storek 		else
104555102Storek 			bsize = ai->blocksize;
104655102Storek 
104755102Storek 		s = splaudio();
104855102Storek 		sc->sc_au.au_blksize = bsize;
104955102Storek 		/* AUDIO_FLUSH */
105055102Storek 		AUCB_INIT(&sc->sc_au.au_rb);
105155102Storek 		AUCB_INIT(&sc->sc_au.au_wb);
105255102Storek 		splx(s);
105355102Storek 
105455102Storek 	}
105555102Storek 	if (ai->hiwat != ~0 && (unsigned)ai->hiwat < AUCB_SIZE)
105655102Storek 		sc->sc_au.au_hiwat = ai->hiwat;
105755102Storek 	if (ai->lowat != ~0 && ai->lowat < AUCB_SIZE)
105855102Storek 		sc->sc_au.au_lowat = ai->lowat;
105959182Storek 	if (ai->backlog != ~0 && ai->backlog < (AUCB_SIZE/2))
106059182Storek 		sc->sc_au.au_backlog = ai->backlog;
106155102Storek 
106255102Storek 	return (0);
106355102Storek }
106455102Storek 
106555102Storek static int
sunaudiosetinfo(sc,ai)106655102Storek sunaudiosetinfo(sc, ai)
106755102Storek 	struct audio_softc *sc;
106855102Storek 	struct sun_audio_info *ai;
106955102Storek {
107055102Storek 	struct sun_audio_prinfo *r = &ai->record, *p = &ai->play;
107155102Storek 
107255102Storek 	if (p->gain != ~0)
107355102Storek 		ausetpgain(sc, p->gain);
107455102Storek 	if (r->gain != ~0)
107555102Storek 		ausetrgain(sc, r->gain);
107655102Storek 	if (ai->monitor_gain != ~0)
107759182Storek 		ausetmgain(sc, ai->monitor_gain);
107855102Storek 	if (p->port == AUDIO_SPEAKER) {
107955102Storek 		sc->sc_map.mr_mmr2 |= AMD_MMR2_LS;
108055102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
108155102Storek 	} else if (p->port == AUDIO_HEADPHONE) {
108255102Storek 		sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS;
108355102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
108455102Storek 	}
108555102Storek 	/*
108655102Storek 	 * The bsd driver does not distinguish between paused and active.
108755102Storek 	 * (In the sun driver, not active means samples are not ouput
108855102Storek 	 * at all, but paused means the last streams buffer is drained
108955102Storek 	 * and then output stops.)  If either are 0, then when stop output.
109055102Storek 	 * Otherwise, if either are non-zero, we resume.
109155102Storek 	 */
109255102Storek 	if (p->pause == 0 || p->active == 0)
109355102Storek 		sc->sc_au.au_wb.cb_pause = 0;
109455102Storek 	else if (p->pause != (u_char)~0 || p->active != (u_char)~0)
109555102Storek 		sc->sc_au.au_wb.cb_pause = 1;
109655102Storek 	if (r->pause == 0 || r->active == 0)
109755102Storek 		sc->sc_au.au_rb.cb_pause = 0;
109855102Storek 	else if (r->pause != (u_char)~0 || r->active != (u_char)~0)
109955102Storek 		sc->sc_au.au_rb.cb_pause = 1;
110055102Storek 
110155102Storek 	return (0);
110255102Storek }
110355102Storek 
110455102Storek static int
audiogetinfo(sc,ai)110555102Storek audiogetinfo(sc, ai)
110655102Storek 	struct audio_softc *sc;
110755102Storek 	struct audio_info *ai;
110855102Storek {
110955102Storek 	struct audio_prinfo *r = &ai->record, *p = &ai->play;
111055102Storek 
111155102Storek 	p->sample_rate = r->sample_rate = 8000;
111255102Storek 	p->channels = r->channels = 1;
111355102Storek 	p->precision = r->precision = 8;
111455102Storek 	p->encoding = r->encoding = AUDIO_ENCODING_ULAW;
111555102Storek 
111655102Storek 	ai->monitor_gain = sc->sc_mlevel;
111755102Storek 	r->gain = sc->sc_rlevel;
111855102Storek 	p->gain = sc->sc_plevel;
111955102Storek 	r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ?
112055102Storek 		AUDIO_SPEAKER : AUDIO_HEADPHONE;
112155102Storek 
112255102Storek 	p->pause = sc->sc_au.au_wb.cb_pause;
112355102Storek 	r->pause = sc->sc_au.au_rb.cb_pause;
112455102Storek 	p->error = sc->sc_au.au_wb.cb_drops != 0;
112555102Storek 	r->error = sc->sc_au.au_rb.cb_drops != 0;
112655102Storek 
112755102Storek 	p->open = sc->sc_open;
112855102Storek 	r->open = sc->sc_open;
112955102Storek 
113055102Storek 	p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops;
113155102Storek 	r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops;
113255102Storek 
113355102Storek 	p->seek = sc->sc_wseek;
113455102Storek 	r->seek = sc->sc_rseek;
113555102Storek 
113655102Storek 	ai->blocksize = sc->sc_au.au_blksize;
113755102Storek 	ai->hiwat = sc->sc_au.au_hiwat;
113855102Storek 	ai->lowat = sc->sc_au.au_lowat;
113959182Storek 	ai->backlog = sc->sc_au.au_backlog;
114055102Storek 
114155102Storek 	return (0);
114255102Storek }
114355102Storek 
114455102Storek static int
sunaudiogetinfo(sc,ai)114555102Storek sunaudiogetinfo(sc, ai)
114655102Storek 	struct audio_softc *sc;
114755102Storek 	struct sun_audio_info *ai;
114855102Storek {
114955102Storek 	struct sun_audio_prinfo *r = &ai->record, *p = &ai->play;
115055102Storek 
115155102Storek 	p->sample_rate = r->sample_rate = 8000;
115255102Storek 	p->channels = r->channels = 1;
115355102Storek 	p->precision = r->precision = 8;
115455102Storek 	p->encoding = r->encoding = AUDIO_ENCODING_ULAW;
115555102Storek 
115655102Storek 	ai->monitor_gain = sc->sc_mlevel;
115755102Storek 	r->gain = sc->sc_rlevel;
115855102Storek 	p->gain = sc->sc_plevel;
115955102Storek 	r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ?
116055102Storek 		AUDIO_SPEAKER : AUDIO_HEADPHONE;
116155102Storek 
116255102Storek 	p->active = p->pause = sc->sc_au.au_wb.cb_pause;
116355102Storek 	r->active = r->pause = sc->sc_au.au_rb.cb_pause;
116455102Storek 	p->error = sc->sc_au.au_wb.cb_drops != 0;
116555102Storek 	r->error = sc->sc_au.au_rb.cb_drops != 0;
116655102Storek 
116755102Storek 	p->waiting = 0;
116855102Storek 	r->waiting = 0;
116955102Storek 	p->eof = 0;
117055102Storek 	r->eof = 0;
117155102Storek 
117255102Storek 	p->open = sc->sc_open;
117355102Storek 	r->open = sc->sc_open;
117455102Storek 
117555102Storek 	p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops;
117655102Storek 	r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops;
117755102Storek 
117855102Storek 	return (0);
117955102Storek }
118055102Storek #endif
1181