xref: /csrg-svn/sys/sparc/dev/bsd_audio.c (revision 59182)
155102Storek /*
255102Storek  * Copyright (c) 1991, 1992 The Regents of the University of California.
355102Storek  * 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
12*59182Storek  *	California, Lawrence Berkeley Laboratory.
1355499Sbostic  *
1455102Storek  * %sccs.include.redist.c%
1555102Storek  *
16*59182Storek  *	@(#)bsd_audio.c	7.4 (Berkeley) 04/20/93
1755102Storek  *
18*59182Storek  * from: $Header: bsd_audio.c,v 1.17 93/04/20 05:31:28 torek 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
58*59182Storek #include <sbusdev/bsd_audioreg.h>
59*59182Storek #include <sbusdev/bsd_audiovar.h>
60*59182Storek #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;
79*59182Storek 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
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
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
19155102Storek audioidentify(cp)
19255102Storek 	char *cp;
19355102Storek {
19455102Storek 	return (strcmp(cp, "audio") == 0);
19555102Storek }
19655102Storek 
19755102Storek static int
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
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
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));
313*59182Storek 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 
32355102Storek int
32455102Storek AUDIOOPEN(dev, flags, ifmt, p)
32555102Storek {
32655102Storek 	register struct audio_softc *sc;
32755102Storek 	register volatile struct amd7930 *amd;
32855102Storek 	int unit = minor(dev), error, s;
32955102Storek 
33055102Storek #ifdef SUNOS
33155102Storek 	if (unit > 0)
33255102Storek 		return (ENXIO);
33355102Storek 	sc = &audio_softc;
33455102Storek #else
33555102Storek 	if (unit >= audiocd.cd_ndevs || (sc = audiocd.cd_devs[unit]) == NULL)
33655102Storek 		return (ENXIO);
33755102Storek #endif
33855102Storek 	if (sc->sc_open)
33955102Storek 		return (EBUSY);
34055102Storek 	sc->sc_open = 1;
34155102Storek 
34255102Storek 	sc->sc_au.au_lowat = audio_blocksize;
34355102Storek 	sc->sc_au.au_hiwat = AUCB_SIZE - sc->sc_au.au_lowat;
34455102Storek 	sc->sc_au.au_blksize = audio_blocksize;
345*59182Storek 	sc->sc_au.au_backlog = audio_backlog;
34655102Storek 
34755102Storek 	/* set up read and write blocks and `dead sound' zero value. */
34855102Storek 	AUCB_INIT(&sc->sc_au.au_rb);
34955102Storek 	sc->sc_au.au_rb.cb_thresh = AUCB_SIZE;
35055102Storek 	AUCB_INIT(&sc->sc_au.au_wb);
35155102Storek 	sc->sc_au.au_wb.cb_thresh = -1;
35255102Storek 
35355102Storek 	/* nothing read or written yet */
35455102Storek 	sc->sc_rseek = 0;
35555102Storek 	sc->sc_wseek = 0;
35655102Storek 
35755102Storek 	bzero((char *)&sc->sc_map, sizeof sc->sc_map);
35855102Storek 	/* default to speaker */
35955102Storek 	sc->sc_map.mr_mmr2 = AMD_MMR2_AINB | AMD_MMR2_LS;
36055102Storek 
36155102Storek 	/* enable interrupts and set parameters established above */
36255102Storek 	amd = sc->sc_au.au_amd;
36355102Storek 	audio_setmmr2(amd, sc->sc_map.mr_mmr2);
36455102Storek 	ausetrgain(sc, audio_default_level);
36555102Storek 	ausetpgain(sc, audio_default_level);
366*59182Storek 	ausetmgain(sc, 0);
36755102Storek 	amd->cr = AMDR_INIT;
36855102Storek 	amd->dr = AMD_INIT_PMS_ACTIVE;
36955102Storek 
37055102Storek 	return (0);
37155102Storek }
37255102Storek 
37355102Storek static int
37455102Storek audio_drain(sc)
37555102Storek 	register struct audio_softc *sc;
37655102Storek {
37755102Storek 	register int error;
37855102Storek 
37955102Storek 	while (!AUCB_EMPTY(&sc->sc_au.au_wb))
38055102Storek 		if ((error = audio_sleep(&sc->sc_au.au_wb, 0)) != 0)
38155102Storek 			return (error);
38255102Storek 	return (0);
38355102Storek }
38455102Storek 
38555102Storek /*
38655102Storek  * Close an audio chip.
38755102Storek  */
38855102Storek /* ARGSUSED */
38955102Storek int
39055102Storek AUDIOCLOSE(dev, flags, ifmt, p)
39155102Storek {
39255102Storek 	register struct audio_softc *sc = SOFTC(dev);
39355102Storek 	register volatile struct amd7930 *amd;
39455102Storek 	register struct aucb *cb;
39555102Storek 	register int s;
39655102Storek 
39755102Storek 	/*
39855102Storek 	 * Block until output drains, but allow ^C interrupt.
39955102Storek 	 */
40055102Storek 	sc->sc_au.au_lowat = 0;	/* avoid excessive wakeups */
40155102Storek 	s = splaudio();
40255102Storek 	/*
40355102Storek 	 * If there is pending output, let it drain (unless
40455102Storek 	 * the output is paused).
40555102Storek 	 */
40655102Storek 	cb = &sc->sc_au.au_wb;
40755102Storek 	if (!AUCB_EMPTY(cb) && !cb->cb_pause)
40855102Storek 		(void)audio_drain(sc);
40955102Storek 	/*
41055102Storek 	 * Disable interrupts, clear open flag, and done.
41155102Storek 	 */
41255102Storek 	amd = sc->sc_au.au_amd;
41355102Storek 	amd->cr = AMDR_INIT;
41455102Storek 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
41555102Storek 	splx(s);
41655102Storek 	sc->sc_open = 0;
41755102Storek 	return (0);
41855102Storek }
41955102Storek 
42055102Storek int
42155102Storek audio_sleep(cb, thresh)
42255102Storek 	register struct aucb *cb;
42355102Storek 	register int thresh;
42455102Storek {
42555102Storek 	register int error;
426*59182Storek 	register int s = splaudio();
42755102Storek 
42855102Storek 	cb->cb_thresh = thresh;
42955102Storek 	error = tsleep((caddr_t)cb, (PZERO + 1) | PCATCH, "audio", 0);
430*59182Storek 	splx(s);
43155102Storek 	return (error);
43255102Storek }
43355102Storek 
43455102Storek int
43555102Storek AUDIOREAD(dev, uio, ioflag)
43655102Storek {
43755102Storek 	register struct audio_softc *sc = SOFTC(dev);
43855102Storek 	register struct aucb *cb;
439*59182Storek 	register int n, head, taildata, error;
44055102Storek 	register int blocksize = sc->sc_au.au_blksize;
44155102Storek 
44255102Storek 	if (uio->uio_resid == 0)
44355102Storek 		return (0);
44455102Storek 	cb = &sc->sc_au.au_rb;
44555102Storek 	error = 0;
44655102Storek 	cb->cb_drops = 0;
44755102Storek 	sc->sc_rseek = sc->sc_au.au_stamp - AUCB_LEN(cb);
44855102Storek 	do {
44955102Storek 		while (AUCB_LEN(cb) < blocksize) {
45055102Storek #ifndef SUNOS
45155102Storek 			if (ioflag & IO_NDELAY) {
45255102Storek 				error = EWOULDBLOCK;
453*59182Storek 				return (error);
45455102Storek 			}
45555102Storek #endif
45655102Storek 			if ((error = audio_sleep(cb, blocksize)) != 0)
457*59182Storek 				return (error);
45855102Storek 		}
45955102Storek 		/*
46055102Storek 		 * The space calculation can only err on the short
46155102Storek 		 * side if an interrupt occurs during processing:
46255102Storek 		 * only cb_tail is altered in the interrupt code.
46355102Storek 		 */
46455102Storek 		head = cb->cb_head;
46555102Storek 		if ((n = AUCB_LEN(cb)) > uio->uio_resid)
46655102Storek 			n = uio->uio_resid;
46755102Storek 		taildata = AUCB_SIZE - head;
46855102Storek 		if (n > taildata) {
46955102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + head,
47055102Storek 					taildata, UIO_READ, uio);
47155102Storek 			if (error == 0)
47255102Storek 				error = UIOMOVE((caddr_t)cb->cb_data,
47355102Storek 						n - taildata, UIO_READ, uio);
47455102Storek 		} else
47555102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + head, n,
47655102Storek 					UIO_READ, uio);
47755102Storek 		if (error)
478*59182Storek 			break;
47955102Storek 		head = AUCB_MOD(head + n);
48055102Storek 		cb->cb_head = head;
48155102Storek 	} while (uio->uio_resid >= blocksize);
482*59182Storek 
48355102Storek 	return (error);
48455102Storek }
48555102Storek 
48655102Storek int
48755102Storek AUDIOWRITE(dev, uio, ioflag)
48855102Storek {
48955102Storek 	register struct audio_softc *sc = SOFTC(dev);
49055102Storek 	register struct aucb *cb = &sc->sc_au.au_wb;
491*59182Storek 	register int n, tail, tailspace, error, first, watermark, drops;
49255102Storek 
49355102Storek 	error = 0;
49455102Storek 	first = 1;
49555102Storek 	while (uio->uio_resid > 0) {
49655102Storek 		watermark = sc->sc_au.au_hiwat;
49755102Storek 		while (AUCB_LEN(cb) > watermark) {
49855102Storek #ifndef SUNOS
49955102Storek 			if (ioflag & IO_NDELAY) {
50055102Storek 				error = EWOULDBLOCK;
501*59182Storek 				return (error);
50255102Storek 			}
50355102Storek #endif
50455102Storek 			if ((error = audio_sleep(cb, watermark)) != 0)
505*59182Storek 				return (error);
50655102Storek 			watermark = sc->sc_au.au_lowat;
50755102Storek 		}
50855102Storek 		/*
50955102Storek 		 * The only value that can change on an interrupt is
51055102Storek 		 * cb->cb_head.  We only pull that out once to decide
51155102Storek 		 * how much to write into cb_data; if we lose a race
51255102Storek 		 * and cb_head changes, we will merely be overly
51355102Storek 		 * conservative.  For a legitimate time stamp,
51455102Storek 		 * however, we need to synchronize the accesses to
51555102Storek 		 * au_stamp and cb_head at a high ipl below.
51655102Storek 		 */
517*59182Storek 		tail = cb->cb_tail;
518*59182Storek 		if ((n = (AUCB_SIZE - 1) - AUCB_LEN(cb)) > uio->uio_resid) {
51955102Storek 			n = uio->uio_resid;
520*59182Storek 			if (cb->cb_head == tail &&
521*59182Storek 			    n <= sc->sc_au.au_blksize &&
522*59182Storek 			    sc->sc_au.au_stamp - sc->sc_wseek > 400) {
523*59182Storek 				/*
524*59182Storek 				 * the write is 'small', the buffer is empty
525*59182Storek 				 * and we have been silent for at least 50ms
526*59182Storek 				 * so we might be dealing with an application
527*59182Storek 				 * that writes frames synchronously with
528*59182Storek 				 * reading them.  If so, we need an output
529*59182Storek 				 * backlog to cover scheduling delays or
530*59182Storek 				 * there will be gaps in the sound output.
531*59182Storek 				 * Also take this opportunity to reset the
532*59182Storek 				 * buffer pointers in case we ended up on
533*59182Storek 				 * a bad boundary (odd byte, blksize bytes
534*59182Storek 				 * from end, etc.).
535*59182Storek 				 */
536*59182Storek 				register u_int* ip;
537*59182Storek 				register int muzero = 0x7f7f7f7f;
538*59182Storek 				register int i = splaudio();
539*59182Storek 				cb->cb_head = cb->cb_tail = 0;
540*59182Storek 				splx(i);
541*59182Storek 				tail = sc->sc_au.au_backlog;
542*59182Storek 				ip = (u_int*)cb->cb_data;
543*59182Storek 				for (i = tail >> 2; --i >= 0; )
544*59182Storek 					*ip++ = muzero;
545*59182Storek 			}
546*59182Storek 		}
54755102Storek 		tailspace = AUCB_SIZE - tail;
54855102Storek 		if (n > tailspace) {
54955102Storek 			/* write first part at tail and rest at head */
55055102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + tail,
55155102Storek 					tailspace, UIO_WRITE, uio);
55255102Storek 			if (error == 0)
55355102Storek 				error = UIOMOVE((caddr_t)cb->cb_data,
55455102Storek 						n - tailspace, UIO_WRITE, uio);
55555102Storek 		} else
55655102Storek 			error = UIOMOVE((caddr_t)cb->cb_data + tail, n,
55755102Storek 					UIO_WRITE, uio);
55855102Storek 		if (error)
559*59182Storek 			break;
560*59182Storek 
56155102Storek 		tail = AUCB_MOD(tail + n);
56255102Storek 		if (first) {
563*59182Storek 			register int s = splaudio();
564*59182Storek 			sc->sc_wseek = AUCB_LEN(cb) + sc->sc_au.au_stamp + 1;
56555102Storek 			/*
56655102Storek 			 * To guarantee that a write is contiguous in the
56755102Storek 			 * sample space, we clear the drop count the first
56855102Storek 			 * time through.  If we later get drops, we will
56955102Storek 			 * break out of the loop below, before writing
57055102Storek 			 * a new frame.
57155102Storek 			 */
57255102Storek 			cb->cb_drops = 0;
573*59182Storek 			cb->cb_tail = tail;
574*59182Storek 			splx(s);
575*59182Storek 			first = 0;
576*59182Storek 		} else {
577*59182Storek 			if (cb->cb_drops != 0)
578*59182Storek 				break;
579*59182Storek 			cb->cb_tail = tail;
58055102Storek 		}
58155102Storek 	}
58255102Storek 	return (error);
58355102Storek }
58455102Storek 
58555102Storek /* Sun audio compatibility */
58655102Storek struct sun_audio_prinfo {
58755102Storek 	u_int	sample_rate;
58855102Storek 	u_int	channels;
58955102Storek 	u_int	precision;
59055102Storek 	u_int	encoding;
59155102Storek 	u_int	gain;
59255102Storek 	u_int	port;
59355102Storek 	u_int	reserved0[4];
59455102Storek 	u_int	samples;
59555102Storek 	u_int	eof;
59655102Storek 	u_char	pause;
59755102Storek 	u_char	error;
59855102Storek 	u_char	waiting;
59955102Storek 	u_char	reserved1[3];
60055102Storek 	u_char	open;
60155102Storek 	u_char	active;
60255102Storek };
60355102Storek struct sun_audio_info {
60455102Storek 	struct sun_audio_prinfo play;
60555102Storek 	struct sun_audio_prinfo record;
60655102Storek 	u_int monitor_gain;
60755102Storek 	u_int reserved[4];
60855102Storek };
60955102Storek 
61055102Storek #ifndef SUNOS
61155102Storek #define SUNAUDIO_GETINFO	_IOR('A', 1, struct sun_audio_info)
61255102Storek #define SUNAUDIO_SETINFO	_IOWR('A', 2, struct sun_audio_info)
61355102Storek #else
61455102Storek #define SUNAUDIO_GETINFO	_IOR(A, 1, struct sun_audio_info)
61555102Storek #define SUNAUDIO_SETINFO	_IOWR(A, 2, struct sun_audio_info)
61655102Storek #endif
61755102Storek 
61855102Storek int
61955102Storek AUDIOIOCTL(dev, cmd, addr, flag, p)
62055102Storek {
62155102Storek 	register struct audio_softc *sc = SOFTC(dev);
62255102Storek 	int error = 0, i, s;
62355102Storek 
62455102Storek 	switch (cmd) {
62555102Storek 
62655102Storek 	case AUDIO_GETMAP:
62755102Storek 		bcopy((caddr_t)&sc->sc_map, addr, sizeof(sc->sc_map));
62855102Storek 		break;
62955102Storek 
63055102Storek 	case AUDIO_SETMAP:
63155102Storek 		bcopy(addr, (caddr_t)&sc->sc_map, sizeof(sc->sc_map));
63255102Storek 		sc->sc_map.mr_mmr2 &= 0x7f;
63355102Storek 		audio_setmap(sc->sc_au.au_amd, &sc->sc_map);
63455102Storek 		break;
63555102Storek 
63655102Storek 	case AUDIO_FLUSH:
63755102Storek 		s = splaudio();
63855102Storek 		AUCB_INIT(&sc->sc_au.au_rb);
63955102Storek 		AUCB_INIT(&sc->sc_au.au_wb);
640*59182Storek 		sc->sc_au.au_stamp = 0;
64155102Storek 		splx(s);
64255102Storek 		sc->sc_wseek = 0;
64355102Storek 		sc->sc_rseek = 0;
64455102Storek 		break;
64555102Storek 
64655102Storek 	/*
64755102Storek 	 * Number of read samples dropped.  We don't know where or
64855102Storek 	 * when they were dropped.
64955102Storek 	 */
65055102Storek 	case AUDIO_RERROR:
65155102Storek 		*(int *)addr = sc->sc_au.au_rb.cb_drops != 0;
65255102Storek 		break;
65355102Storek 
65455102Storek 	/*
655*59182Storek 	 * How many samples will elapse until mike hears the first
656*59182Storek 	 * sample of what we last wrote?
65755102Storek 	 */
65855102Storek 	case AUDIO_WSEEK:
659*59182Storek 		s = splaudio();
660*59182Storek 		*(u_long *)addr = sc->sc_wseek - sc->sc_au.au_stamp
661*59182Storek 				  + AUCB_LEN(&sc->sc_au.au_rb);
662*59182Storek 		splx(s);
66355102Storek 		break;
66455102Storek 
66555102Storek 	case AUDIO_SETINFO:
66655102Storek 		error = audiosetinfo(sc, (struct audio_info *)addr);
66755102Storek 		break;
66855102Storek 
66955102Storek 	case AUDIO_GETINFO:
67055102Storek 		error = audiogetinfo(sc, (struct audio_info *)addr);
67155102Storek 		break;
67255102Storek 
67355102Storek 	case SUNAUDIO_GETINFO:
67455102Storek 		error = sunaudiogetinfo(sc, (struct sun_audio_info *)addr);
67555102Storek 		break;
67655102Storek 
67755102Storek 	case SUNAUDIO_SETINFO:
67855102Storek 		error = sunaudiosetinfo(sc, (struct sun_audio_info *)addr);
67955102Storek 		break;
68055102Storek 
68155102Storek 	case AUDIO_DRAIN:
68255102Storek 		error = audio_drain(sc);
68355102Storek 		break;
68455102Storek 
68555102Storek 	default:
68655102Storek 		error = EINVAL;
68755102Storek 		break;
68855102Storek 	}
68955102Storek 	return (error);
69055102Storek }
69155102Storek 
69255102Storek int
69355102Storek AUDIOSELECT(dev, rw, p)
69455102Storek {
69555102Storek 	register struct audio_softc *sc = SOFTC(dev);
69655102Storek 	register struct aucb *cb;
69755102Storek 	register int s = splaudio();
69855102Storek 
69955102Storek 	switch (rw) {
70055102Storek 
70155102Storek 	case FREAD:
70255102Storek 		cb = &sc->sc_au.au_rb;
70355102Storek 		if (AUCB_LEN(cb) >= sc->sc_au.au_blksize) {
70455102Storek 			splx(s);
70555102Storek 			return (1);
70655102Storek 		}
70755102Storek 		selrecord(p, &sc->sc_rsel);
70855102Storek 		cb->cb_thresh = sc->sc_au.au_blksize;
70955102Storek 		break;
71055102Storek 
71155102Storek 	case FWRITE:
71255102Storek 		cb = &sc->sc_au.au_wb;
71355102Storek 		if (AUCB_LEN(cb) <= sc->sc_au.au_lowat) {
71455102Storek 			splx(s);
71555102Storek 			return (1);
71655102Storek 		}
71755102Storek 		selrecord(p, &sc->sc_wsel);
71855102Storek 		cb->cb_thresh = sc->sc_au.au_lowat;
71955102Storek 		break;
72055102Storek 	}
72155102Storek 	splx(s);
72255102Storek 	return (0);
72355102Storek }
72455102Storek 
72555102Storek #ifdef AUDIO_C_HANDLER
72655102Storek int
72755102Storek audiohwintr(au0)
72855102Storek 	void *au0;
72955102Storek {
73055102Storek #ifdef SUNOS
73155102Storek 	register struct auio *au = audio_au;
73255102Storek #else
73355102Storek 	register struct auio *au = au0;
73455102Storek #endif
73555102Storek 	register volatile struct amd7930 *amd = au->au_amd;
73655102Storek 	register struct aucb *cb;
73755102Storek 	register int h, t, k;
73855102Storek 
73955102Storek 	k = amd->ir;		/* clear interrupt */
74055102Storek 	++au->au_stamp;
74155102Storek 
74255102Storek 	/* receive incoming data */
74355102Storek 	cb = &au->au_rb;
74455102Storek 	h = cb->cb_head;
74555102Storek 	t = cb->cb_tail;
74655102Storek 	k = AUCB_MOD(t + 1);
74755102Storek 	if (h == k)
74855102Storek 		cb->cb_drops++;
74955102Storek 	else if  (cb->cb_pause != 0)
75055102Storek 		cb->cb_pdrops++;
75155102Storek 	else {
75255102Storek 		cb->cb_data[t] = amd->bbrb;
75355102Storek 		cb->cb_tail = t = k;
75455102Storek 	}
75555102Storek 	if (AUCB_MOD(t - h) >= cb->cb_thresh) {
75655102Storek 		cb->cb_thresh = AUCB_SIZE;
75755102Storek 		cb->cb_waking = 1;
75855102Storek 		AUDIO_SET_SWINTR;
75955102Storek 	}
76055102Storek 	/* send outgoing data */
76155102Storek 	cb = &au->au_wb;
76255102Storek 	h = cb->cb_head;
76355102Storek 	t = cb->cb_tail;
76455102Storek 	if (h == t)
76555102Storek 		cb->cb_drops++;
76655102Storek 	else if (cb->cb_pause != 0)
76755102Storek 		cb->cb_pdrops++;
76855102Storek 	else {
76955102Storek 		cb->cb_head = h = AUCB_MOD(h + 1);
77055102Storek 		amd->bbtb = cb->cb_data[h];
77155102Storek 	}
77255102Storek 	if (AUCB_MOD(t - h) <= cb->cb_thresh) {
77355102Storek 		cb->cb_thresh = -1;
77455102Storek 		cb->cb_waking = 1;
77555102Storek 		AUDIO_SET_SWINTR;
77655102Storek 	}
77755102Storek 	return (1);
77855102Storek }
77955102Storek #endif
78055102Storek 
78155102Storek int
78255102Storek audioswintr(sc0)
78355102Storek 	void *sc0;
78455102Storek {
78555102Storek 	register struct audio_softc *sc;
78655102Storek 	register int s, ret = 0;
78755102Storek #ifdef SUNOS
78855102Storek 	sc = &audio_softc;
78955102Storek #else
79055102Storek 	sc = sc0;
79155102Storek #endif
79255102Storek 	s = splaudio();
79355102Storek 	if (sc->sc_au.au_rb.cb_waking != 0) {
79455102Storek 		sc->sc_au.au_rb.cb_waking = 0;
79555102Storek 		splx(s);
79655102Storek 		ret = 1;
79755102Storek 		wakeup((caddr_t)&sc->sc_au.au_rb);
79855102Storek 		SELWAKEUP(&sc->sc_rsel);
79955102Storek 	}
80055102Storek 	if (sc->sc_au.au_wb.cb_waking != 0) {
80155102Storek 		sc->sc_au.au_wb.cb_waking = 0;
80255102Storek 		splx(s);
80355102Storek 		ret = 1;
80455102Storek 		wakeup((caddr_t)&sc->sc_au.au_wb);
80555102Storek 		SELWAKEUP(&sc->sc_wsel);
80655102Storek 	} else
80755102Storek 		splx(s);
80855102Storek 	return (ret);
80955102Storek }
81055102Storek 
81155102Storek /* Write 16 bits of data from variable v to the data port of the audio chip */
81255102Storek 
813*59182Storek #define	WAMD16(amd, v) ((amd)->dr = (v), (amd)->dr = (v) >> 8)
81455102Storek 
81555102Storek void
81655102Storek audio_setmap(amd, map)
81755102Storek 	register volatile struct amd7930 *amd;
81855102Storek 	register struct mapreg *map;
81955102Storek {
82055102Storek 	register int i, s, v;
82155102Storek 
82255102Storek 	s = splaudio();
82355102Storek 	amd->cr = AMDR_MAP_1_10;
82455102Storek 	for (i = 0; i < 8; i++) {
82555102Storek 		v = map->mr_x[i];
82655102Storek 		WAMD16(amd, v);
82755102Storek 	}
82855102Storek 	for (i = 0; i < 8; ++i) {
82955102Storek 		v = map->mr_r[i];
83055102Storek 		WAMD16(amd, v);
83155102Storek 	}
83255102Storek 	v = map->mr_gx; WAMD16(amd, v);
83355102Storek 	v = map->mr_gr; WAMD16(amd, v);
83455102Storek 	v = map->mr_ger; WAMD16(amd, v);
83555102Storek 	v = map->mr_stgr; WAMD16(amd, v);
83655102Storek 	v = map->mr_ftgr; WAMD16(amd, v);
83755102Storek 	v = map->mr_atgr; WAMD16(amd, v);
83855102Storek 	amd->dr = map->mr_mmr1;
83955102Storek 	amd->dr = map->mr_mmr2;
84055102Storek 	splx(s);
84155102Storek }
84255102Storek 
84355102Storek /*
84455102Storek  * Set the mmr1 register and one other 16 bit register in the audio chip.
84555102Storek  * The other register is indicated by op and val.
84655102Storek  */
84755102Storek void
84855102Storek audio_setmmr1(amd, mmr1, op, val)
84955102Storek 	register volatile struct amd7930 *amd;
85055102Storek 	register int mmr1;
85155102Storek 	register int op;
85255102Storek 	register int val;
85355102Storek {
85455102Storek 	register int s = splaudio();
85555102Storek 
85655102Storek 	amd->cr = AMDR_MAP_MMR1;
85755102Storek 	amd->dr = mmr1;
85855102Storek 	amd->cr = op;
85955102Storek 	WAMD16(amd, val);
86055102Storek 	splx(s);
86155102Storek }
86255102Storek 
86355102Storek /*
864*59182Storek  * Set the mmr2 register.
86555102Storek  */
86655102Storek static void
86755102Storek audio_setmmr2(amd, mmr2)
86855102Storek 	register volatile struct amd7930 *amd;
86955102Storek 	register int mmr2;
87055102Storek {
87155102Storek 	register int s = splaudio();
87255102Storek 
87355102Storek 	amd->cr = AMDR_MAP_MMR2;
87455102Storek 	amd->dr = mmr2;
87555102Storek 	splx(s);
87655102Storek }
87755102Storek 
878*59182Storek /*
879*59182Storek  * gx, gr & stg gains.  this table must contain 256 elements with
880*59182Storek  * the 0th being "infinity" (the magic value 9008).  The remaining
881*59182Storek  * elements match sun's gain curve (but with higher resolution):
882*59182Storek  * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
883*59182Storek  */
884*59182Storek static const u_short gx_coeff[256] = {
885*59182Storek 	0x9008, 0x8b7c, 0x8b51, 0x8b45, 0x8b42, 0x8b3b, 0x8b36, 0x8b33,
886*59182Storek 	0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
887*59182Storek 	0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
888*59182Storek 	0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
889*59182Storek 	0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
890*59182Storek 	0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
891*59182Storek 	0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
892*59182Storek 	0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
893*59182Storek 	0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
894*59182Storek 	0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
895*59182Storek 	0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
896*59182Storek 	0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
897*59182Storek 	0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
898*59182Storek 	0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
899*59182Storek 	0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
900*59182Storek 	0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
901*59182Storek 	0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
902*59182Storek 	0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
903*59182Storek 	0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
904*59182Storek 	0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
905*59182Storek 	0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
906*59182Storek 	0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
907*59182Storek 	0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
908*59182Storek 	0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
909*59182Storek 	0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
910*59182Storek 	0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
911*59182Storek 	0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
912*59182Storek 	0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
913*59182Storek 	0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
914*59182Storek 	0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
915*59182Storek 	0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
916*59182Storek 	0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
91755102Storek };
91855102Storek 
919*59182Storek /*
920*59182Storek  * second stage play gain.
921*59182Storek  */
922*59182Storek static const u_short ger_coeff[] = {
923*59182Storek 	0x431f, /* 5. dB */
924*59182Storek 	0x331f, /* 5.5 dB */
925*59182Storek 	0x40dd, /* 6. dB */
926*59182Storek 	0x11dd, /* 6.5 dB */
927*59182Storek 	0x440f, /* 7. dB */
928*59182Storek 	0x411f, /* 7.5 dB */
929*59182Storek 	0x311f, /* 8. dB */
930*59182Storek 	0x5520, /* 8.5 dB */
931*59182Storek 	0x10dd, /* 9. dB */
932*59182Storek 	0x4211, /* 9.5 dB */
933*59182Storek 	0x410f, /* 10. dB */
934*59182Storek 	0x111f, /* 10.5 dB */
935*59182Storek 	0x600b, /* 11. dB */
936*59182Storek 	0x00dd, /* 11.5 dB */
937*59182Storek 	0x4210, /* 12. dB */
938*59182Storek 	0x110f, /* 13. dB */
939*59182Storek 	0x7200, /* 14. dB */
940*59182Storek 	0x2110, /* 15. dB */
941*59182Storek 	0x2200, /* 15.9 dB */
942*59182Storek 	0x000b, /* 16.9 dB */
943*59182Storek 	0x000f  /* 18. dB */
944*59182Storek #define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
94555102Storek };
94655102Storek 
94755102Storek static void
94855102Storek ausetrgain(sc, level)
94955102Storek 	register struct audio_softc *sc;
95055102Storek 	register int level;
95155102Storek {
95255102Storek 	level &= 0xff;
95355102Storek 	sc->sc_rlevel = level;
954*59182Storek 	sc->sc_map.mr_mmr1 |= AMD_MMR1_GX;
955*59182Storek 	sc->sc_map.mr_gx = gx_coeff[level];
95655102Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
95755102Storek 		      AMDR_MAP_GX, sc->sc_map.mr_gx);
95855102Storek }
95955102Storek 
96055102Storek static void
96155102Storek ausetpgain(sc, level)
96255102Storek 	register struct audio_softc *sc;
96355102Storek 	register int level;
96455102Storek {
965*59182Storek 	register int gi, s;
966*59182Storek 	register volatile struct amd7930 *amd;
967*59182Storek 
96855102Storek 	level &= 0xff;
96955102Storek 	sc->sc_plevel = level;
970*59182Storek 	sc->sc_map.mr_mmr1 |= AMD_MMR1_GER|AMD_MMR1_GR;
971*59182Storek 	level *= 256 + NGER;
972*59182Storek 	level >>= 8;
973*59182Storek 	if (level >= 256) {
974*59182Storek 		gi = level - 256;
975*59182Storek 		level = 255;
976*59182Storek 	} else
977*59182Storek 		gi = 0;
978*59182Storek 	sc->sc_map.mr_ger = ger_coeff[gi];
979*59182Storek 	sc->sc_map.mr_gr = gx_coeff[level];
980*59182Storek 
981*59182Storek 	amd = sc->sc_au.au_amd;
982*59182Storek 	s = splaudio();
983*59182Storek 	amd->cr = AMDR_MAP_MMR1;
984*59182Storek 	amd->dr = sc->sc_map.mr_mmr1;
985*59182Storek 	amd->cr = AMDR_MAP_GR;
986*59182Storek 	gi =  sc->sc_map.mr_gr;
987*59182Storek 	WAMD16(amd, gi);
988*59182Storek 	amd->cr = AMDR_MAP_GER;
989*59182Storek 	gi =  sc->sc_map.mr_ger;
990*59182Storek 	WAMD16(amd, gi);
991*59182Storek 	splx(s);
99255102Storek }
99355102Storek 
99455102Storek static void
99555102Storek ausetmgain(sc, level)
99655102Storek 	register struct audio_softc *sc;
99755102Storek 	register int level;
99855102Storek {
99955102Storek 	level &= 0xff;
100055102Storek 	sc->sc_mlevel = level;
1001*59182Storek 	sc->sc_map.mr_mmr1 |= AMD_MMR1_STG;
1002*59182Storek 	sc->sc_map.mr_stgr = gx_coeff[level];
100355102Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
100455102Storek 		      AMDR_MAP_STG, sc->sc_map.mr_stgr);
100555102Storek }
100655102Storek 
100755102Storek static int
100855102Storek audiosetinfo(sc, ai)
100955102Storek 	struct audio_softc *sc;
101055102Storek 	struct audio_info *ai;
101155102Storek {
101255102Storek 	struct audio_prinfo *r = &ai->record, *p = &ai->play;
101355102Storek 	register int s, bsize;
101455102Storek 
101555102Storek 	if (p->gain != ~0)
101655102Storek 		ausetpgain(sc, p->gain);
101755102Storek 	if (r->gain != ~0)
101855102Storek 		ausetrgain(sc, r->gain);
101955102Storek 	if (ai->monitor_gain != ~0)
1020*59182Storek 		ausetmgain(sc, ai->monitor_gain);
102155102Storek 	if (p->port == AUDIO_SPEAKER) {
102255102Storek 		sc->sc_map.mr_mmr2 |= AMD_MMR2_LS;
102355102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
102455102Storek 	} else if (p->port == AUDIO_HEADPHONE) {
102555102Storek 		sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS;
102655102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
102755102Storek 	}
102855102Storek 	if (p->pause != (u_char)~0)
102955102Storek 		sc->sc_au.au_wb.cb_pause = p->pause;
103055102Storek 	if (r->pause != (u_char)~0)
103155102Storek 		sc->sc_au.au_rb.cb_pause = r->pause;
103255102Storek 
103355102Storek 	if (ai->blocksize != ~0) {
103455102Storek 		if (ai->blocksize == 0)
103555102Storek 			bsize = ai->blocksize = DEFBLKSIZE;
103655102Storek 		else if (ai->blocksize > MAXBLKSIZE)
103755102Storek 			bsize = ai->blocksize = MAXBLKSIZE;
103855102Storek 		else
103955102Storek 			bsize = ai->blocksize;
104055102Storek 
104155102Storek 		s = splaudio();
104255102Storek 		sc->sc_au.au_blksize = bsize;
104355102Storek 		/* AUDIO_FLUSH */
104455102Storek 		AUCB_INIT(&sc->sc_au.au_rb);
104555102Storek 		AUCB_INIT(&sc->sc_au.au_wb);
104655102Storek 		splx(s);
104755102Storek 
104855102Storek 	}
104955102Storek 	if (ai->hiwat != ~0 && (unsigned)ai->hiwat < AUCB_SIZE)
105055102Storek 		sc->sc_au.au_hiwat = ai->hiwat;
105155102Storek 	if (ai->lowat != ~0 && ai->lowat < AUCB_SIZE)
105255102Storek 		sc->sc_au.au_lowat = ai->lowat;
1053*59182Storek 	if (ai->backlog != ~0 && ai->backlog < (AUCB_SIZE/2))
1054*59182Storek 		sc->sc_au.au_backlog = ai->backlog;
105555102Storek 
105655102Storek 	return (0);
105755102Storek }
105855102Storek 
105955102Storek static int
106055102Storek sunaudiosetinfo(sc, ai)
106155102Storek 	struct audio_softc *sc;
106255102Storek 	struct sun_audio_info *ai;
106355102Storek {
106455102Storek 	struct sun_audio_prinfo *r = &ai->record, *p = &ai->play;
106555102Storek 
106655102Storek 	if (p->gain != ~0)
106755102Storek 		ausetpgain(sc, p->gain);
106855102Storek 	if (r->gain != ~0)
106955102Storek 		ausetrgain(sc, r->gain);
107055102Storek 	if (ai->monitor_gain != ~0)
1071*59182Storek 		ausetmgain(sc, ai->monitor_gain);
107255102Storek 	if (p->port == AUDIO_SPEAKER) {
107355102Storek 		sc->sc_map.mr_mmr2 |= AMD_MMR2_LS;
107455102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
107555102Storek 	} else if (p->port == AUDIO_HEADPHONE) {
107655102Storek 		sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS;
107755102Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
107855102Storek 	}
107955102Storek 	/*
108055102Storek 	 * The bsd driver does not distinguish between paused and active.
108155102Storek 	 * (In the sun driver, not active means samples are not ouput
108255102Storek 	 * at all, but paused means the last streams buffer is drained
108355102Storek 	 * and then output stops.)  If either are 0, then when stop output.
108455102Storek 	 * Otherwise, if either are non-zero, we resume.
108555102Storek 	 */
108655102Storek 	if (p->pause == 0 || p->active == 0)
108755102Storek 		sc->sc_au.au_wb.cb_pause = 0;
108855102Storek 	else if (p->pause != (u_char)~0 || p->active != (u_char)~0)
108955102Storek 		sc->sc_au.au_wb.cb_pause = 1;
109055102Storek 	if (r->pause == 0 || r->active == 0)
109155102Storek 		sc->sc_au.au_rb.cb_pause = 0;
109255102Storek 	else if (r->pause != (u_char)~0 || r->active != (u_char)~0)
109355102Storek 		sc->sc_au.au_rb.cb_pause = 1;
109455102Storek 
109555102Storek 	return (0);
109655102Storek }
109755102Storek 
109855102Storek static int
109955102Storek audiogetinfo(sc, ai)
110055102Storek 	struct audio_softc *sc;
110155102Storek 	struct audio_info *ai;
110255102Storek {
110355102Storek 	struct audio_prinfo *r = &ai->record, *p = &ai->play;
110455102Storek 
110555102Storek 	p->sample_rate = r->sample_rate = 8000;
110655102Storek 	p->channels = r->channels = 1;
110755102Storek 	p->precision = r->precision = 8;
110855102Storek 	p->encoding = r->encoding = AUDIO_ENCODING_ULAW;
110955102Storek 
111055102Storek 	ai->monitor_gain = sc->sc_mlevel;
111155102Storek 	r->gain = sc->sc_rlevel;
111255102Storek 	p->gain = sc->sc_plevel;
111355102Storek 	r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ?
111455102Storek 		AUDIO_SPEAKER : AUDIO_HEADPHONE;
111555102Storek 
111655102Storek 	p->pause = sc->sc_au.au_wb.cb_pause;
111755102Storek 	r->pause = sc->sc_au.au_rb.cb_pause;
111855102Storek 	p->error = sc->sc_au.au_wb.cb_drops != 0;
111955102Storek 	r->error = sc->sc_au.au_rb.cb_drops != 0;
112055102Storek 
112155102Storek 	p->open = sc->sc_open;
112255102Storek 	r->open = sc->sc_open;
112355102Storek 
112455102Storek 	p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops;
112555102Storek 	r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops;
112655102Storek 
112755102Storek 	p->seek = sc->sc_wseek;
112855102Storek 	r->seek = sc->sc_rseek;
112955102Storek 
113055102Storek 	ai->blocksize = sc->sc_au.au_blksize;
113155102Storek 	ai->hiwat = sc->sc_au.au_hiwat;
113255102Storek 	ai->lowat = sc->sc_au.au_lowat;
1133*59182Storek 	ai->backlog = sc->sc_au.au_backlog;
113455102Storek 
113555102Storek 	return (0);
113655102Storek }
113755102Storek 
113855102Storek static int
113955102Storek sunaudiogetinfo(sc, ai)
114055102Storek 	struct audio_softc *sc;
114155102Storek 	struct sun_audio_info *ai;
114255102Storek {
114355102Storek 	struct sun_audio_prinfo *r = &ai->record, *p = &ai->play;
114455102Storek 
114555102Storek 	p->sample_rate = r->sample_rate = 8000;
114655102Storek 	p->channels = r->channels = 1;
114755102Storek 	p->precision = r->precision = 8;
114855102Storek 	p->encoding = r->encoding = AUDIO_ENCODING_ULAW;
114955102Storek 
115055102Storek 	ai->monitor_gain = sc->sc_mlevel;
115155102Storek 	r->gain = sc->sc_rlevel;
115255102Storek 	p->gain = sc->sc_plevel;
115355102Storek 	r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ?
115455102Storek 		AUDIO_SPEAKER : AUDIO_HEADPHONE;
115555102Storek 
115655102Storek 	p->active = p->pause = sc->sc_au.au_wb.cb_pause;
115755102Storek 	r->active = r->pause = sc->sc_au.au_rb.cb_pause;
115855102Storek 	p->error = sc->sc_au.au_wb.cb_drops != 0;
115955102Storek 	r->error = sc->sc_au.au_rb.cb_drops != 0;
116055102Storek 
116155102Storek 	p->waiting = 0;
116255102Storek 	r->waiting = 0;
116355102Storek 	p->eof = 0;
116455102Storek 	r->eof = 0;
116555102Storek 
116655102Storek 	p->open = sc->sc_open;
116755102Storek 	r->open = sc->sc_open;
116855102Storek 
116955102Storek 	p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops;
117055102Storek 	r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops;
117155102Storek 
117255102Storek 	return (0);
117355102Storek }
117455102Storek #endif
1175