xref: /original-bsd/sys/sparc/dev/bsd_audio.c (revision db00d1de)
1d29d28f2Storek /*
2*db00d1deSbostic  * Copyright (c) 1991, 1992, 1993
3*db00d1deSbostic  *	The Regents of the University of California.  All rights reserved.
4d29d28f2Storek  *
5d29d28f2Storek  * This software was developed by the Computer Systems Engineering group
6d29d28f2Storek  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
7d29d28f2Storek  * contributed to Berkeley.
8d29d28f2Storek  *
9b248cd2bSbostic  * All advertising materials mentioning features or use of this software
10b248cd2bSbostic  * must display the following acknowledgement:
11b248cd2bSbostic  *	This product includes software developed by the University of
121629ecceStorek  *	California, Lawrence Berkeley Laboratory.
13b248cd2bSbostic  *
14d29d28f2Storek  * %sccs.include.redist.c%
15d29d28f2Storek  *
16*db00d1deSbostic  *	@(#)bsd_audio.c	8.1 (Berkeley) 06/11/93
17d29d28f2Storek  *
181fb1e3cbStorek  * from: $Header: bsd_audio.c,v 1.18 93/04/24 16:20:35 leres Exp $ (LBL)
19d29d28f2Storek  */
20d29d28f2Storek #include "bsdaudio.h"
21d29d28f2Storek #if NBSDAUDIO > 0
22d29d28f2Storek 
239c96d15aSbostic #include <sys/param.h>
249c96d15aSbostic #include <sys/systm.h>
25d29d28f2Storek 
26d29d28f2Storek #if BSD < 199103
27d29d28f2Storek #ifndef SUNOS
28d29d28f2Storek #define SUNOS
29d29d28f2Storek #endif
30d29d28f2Storek #endif
31d29d28f2Storek 
329c96d15aSbostic #include <sys/errno.h>
339c96d15aSbostic #include <sys/file.h>
349c96d15aSbostic #include <sys/proc.h>
359c96d15aSbostic #include <sys/user.h>
369c96d15aSbostic #include <sys/vnode.h>
379c96d15aSbostic #include <sys/ioctl.h>
389c96d15aSbostic #include <sys/time.h>
39d29d28f2Storek #ifndef SUNOS
409c96d15aSbostic #include <sys/tty.h>
41d29d28f2Storek #endif
429c96d15aSbostic #include <sys/uio.h>
43d29d28f2Storek 
44d29d28f2Storek #ifdef SUNOS
45d29d28f2Storek #include <sundev/mbvar.h>
46d29d28f2Storek #include <sun4c/intreg.h>
47d29d28f2Storek #else
489c96d15aSbostic #include <sys/device.h>
499c96d15aSbostic #include <machine/autoconf.h>
50d29d28f2Storek #endif
519c96d15aSbostic #include <machine/cpu.h>
52d29d28f2Storek 
53d29d28f2Storek /*
54d29d28f2Storek  * Avoid name clashes with SunOS so we can config either the bsd or sun
55d29d28f2Storek  * streams driver in a SunOS kernel.
56d29d28f2Storek  */
57d29d28f2Storek #ifdef SUNOS
581629ecceStorek #include <sbusdev/bsd_audioreg.h>
591629ecceStorek #include <sbusdev/bsd_audiovar.h>
601629ecceStorek #include <sbusdev/bsd_audioio.h>
61d29d28f2Storek struct selinfo {
62d29d28f2Storek 	struct proc *si_proc;
63d29d28f2Storek 	int si_coll;
64d29d28f2Storek };
65d29d28f2Storek #else
669c96d15aSbostic #include <sparc/dev/bsd_audioreg.h>
679c96d15aSbostic #include <sparc/dev/bsd_audiovar.h>
689c96d15aSbostic #include <machine/bsd_audioio.h>
69d29d28f2Storek #endif
70d29d28f2Storek 
71d29d28f2Storek #ifdef SUNOS
72d29d28f2Storek #include "bsd_audiocompat.h"
73d29d28f2Storek #endif
74d29d28f2Storek 
75d29d28f2Storek /*
76d29d28f2Storek  * Initial/default block size is patchable.
77d29d28f2Storek  */
78d29d28f2Storek int audio_blocksize = DEFBLKSIZE;
791629ecceStorek int audio_backlog = 400;		/* 50ms in samples */
80d29d28f2Storek 
81d29d28f2Storek /*
82d29d28f2Storek  * Software state, per AMD79C30 audio chip.
83d29d28f2Storek  */
84d29d28f2Storek struct audio_softc {
85d29d28f2Storek #ifndef SUNOS
86d29d28f2Storek 	struct	device sc_dev;		/* base device */
87d29d28f2Storek 	struct	intrhand sc_hwih;	/* hardware interrupt vector */
88d29d28f2Storek 	struct	intrhand sc_swih;	/* software interrupt vector */
89d29d28f2Storek #endif
90d29d28f2Storek 	int	sc_interrupts;		/* number of interrupts taken */
91d29d28f2Storek 
92d29d28f2Storek 	int	sc_open;		/* single use device */
93d29d28f2Storek 	u_long	sc_wseek;		/* timestamp of last frame written */
94d29d28f2Storek 	u_long	sc_rseek;		/* timestamp of last frame read */
95d29d28f2Storek 	struct	mapreg sc_map;		/* current contents of map registers */
96d29d28f2Storek 	struct	selinfo sc_wsel;	/* write selector */
97d29d28f2Storek 	struct	selinfo sc_rsel;	/* read selector */
98d29d28f2Storek 	/*
99d29d28f2Storek 	 * keep track of levels so we don't have to convert back from
100d29d28f2Storek 	 * MAP gain constants
101d29d28f2Storek 	 */
102d29d28f2Storek 	int	sc_rlevel;		/* record level */
103d29d28f2Storek 	int	sc_plevel;		/* play level */
104d29d28f2Storek 	int	sc_mlevel;		/* monitor level */
105d29d28f2Storek 
106d29d28f2Storek 	/* sc_au is special in that the hardware interrupt handler uses it */
107d29d28f2Storek 	struct	auio sc_au;		/* recv and xmit buffers, etc */
108d29d28f2Storek 
109d29d28f2Storek };
110d29d28f2Storek 
111d29d28f2Storek /* interrupt interfaces */
112d29d28f2Storek #ifndef AUDIO_C_HANDLER
113d29d28f2Storek int	audiohwintr __P((void *));
114d29d28f2Storek #endif
115d29d28f2Storek int	audioswintr __P((void *));
116d29d28f2Storek 
117d29d28f2Storek /* forward declarations */
118d29d28f2Storek int	audio_sleep __P((struct aucb *, int));
119d29d28f2Storek void	audio_setmap __P((volatile struct amd7930 *, struct mapreg *));
120d29d28f2Storek 
121d29d28f2Storek static void init_amd();
122d29d28f2Storek 
123d29d28f2Storek #if !defined(AUDIO_C_HANDLER) || defined(SUNOS)
124d29d28f2Storek struct auio *audio_au;
125d29d28f2Storek extern void audio_trap();
126d29d28f2Storek #endif
127d29d28f2Storek 
128d29d28f2Storek #ifdef SUNOS
129d29d28f2Storek struct audio_softc audio_softc;
130d29d28f2Storek #define SOFTC(dev) &audio_softc
131d29d28f2Storek #define UIOMOVE(cp, len, code, uio) uiomove(cp, len, code, uio)
132d29d28f2Storek 
133d29d28f2Storek #define AUDIOOPEN(d, f, i, p)\
134d29d28f2Storek 	audioopen(d, f, i)\
135d29d28f2Storek 	dev_t d; int f, i;
136d29d28f2Storek #define AUDIOCLOSE(d, f, i, p)\
137d29d28f2Storek 	audioclose(d, f, i)\
138d29d28f2Storek 	dev_t d; int f, i;
139d29d28f2Storek #define AUDIOREAD(d, u, f) \
140d29d28f2Storek 	audioread(d, u) dev_t d; struct uio *u;
141d29d28f2Storek #define AUDIOWRITE(d, u, f) \
142d29d28f2Storek 	audiowrite(d, u) dev_t d; struct uio *u;
143d29d28f2Storek #define AUDIOIOCTL(d, c, a, f, o)\
144d29d28f2Storek 	audioioctl(d, c, a, f)\
145d29d28f2Storek 	dev_t d; int c; caddr_t a; int f;
146d29d28f2Storek #define AUDIOSELECT(d, r, p)\
147d29d28f2Storek 	audio_select(d, r, p)\
148d29d28f2Storek 	dev_t d; int r; struct proc *p;
149d29d28f2Storek 
150d29d28f2Storek 
151d29d28f2Storek #define AUDIO_SET_SWINTR set_intreg(IR_SOFT_INT4, 1)
152d29d28f2Storek 
153d29d28f2Storek int
audioselect(dev,rw)154d29d28f2Storek audioselect(dev, rw)
155d29d28f2Storek 	register dev_t dev;
156d29d28f2Storek 	int rw;
157d29d28f2Storek {
158d29d28f2Storek 	return (audio_select(dev, rw, u.u_procp));
159d29d28f2Storek }
160d29d28f2Storek 
161d29d28f2Storek static void
selrecord(p,si)162d29d28f2Storek selrecord(p, si)
163d29d28f2Storek 	struct proc *p;
164d29d28f2Storek 	struct selinfo *si;
165d29d28f2Storek {
166d29d28f2Storek 	if (si->si_proc != 0)
167d29d28f2Storek 		si->si_coll = 1;
168d29d28f2Storek 	else
169d29d28f2Storek 		si->si_proc = p;
170d29d28f2Storek }
171d29d28f2Storek #define SELWAKEUP(si) \
172d29d28f2Storek {\
173d29d28f2Storek 	 if ((si)->si_proc != 0) {\
174d29d28f2Storek 		selwakeup((si)->si_proc, (si)->si_coll); \
175d29d28f2Storek 		(si)->si_proc = 0;\
176d29d28f2Storek 		(si)->si_coll = 0;\
177d29d28f2Storek 	}\
178d29d28f2Storek }
179d29d28f2Storek 
180d29d28f2Storek 
181d29d28f2Storek static int audioattach();
182d29d28f2Storek static int audioidentify();
183d29d28f2Storek 
184d29d28f2Storek struct dev_ops bsdaudio_ops = {
185d29d28f2Storek 	0,
186d29d28f2Storek 	audioidentify,
187d29d28f2Storek 	audioattach,
188d29d28f2Storek };
189d29d28f2Storek 
190d29d28f2Storek static int
audioidentify(cp)191d29d28f2Storek audioidentify(cp)
192d29d28f2Storek 	char *cp;
193d29d28f2Storek {
194d29d28f2Storek 	return (strcmp(cp, "audio") == 0);
195d29d28f2Storek }
196d29d28f2Storek 
197d29d28f2Storek static int
audioattach(dev)198d29d28f2Storek audioattach(dev)
199d29d28f2Storek 	struct dev_info *dev;
200d29d28f2Storek {
201d29d28f2Storek 	register struct audio_softc *sc;
202d29d28f2Storek 	register volatile struct amd7930 *amd;
203d29d28f2Storek 	struct dev_reg *reg;
204d29d28f2Storek 
205d29d28f2Storek 	sc = &audio_softc;
206d29d28f2Storek 	if (dev->devi_nreg != 1 || dev->devi_nintr != 1) {
207d29d28f2Storek 		printf("audio: bad config\n");
208d29d28f2Storek                 return (-1);
209d29d28f2Storek         }
210d29d28f2Storek 	reg = dev->devi_reg;
211d29d28f2Storek 	amd = (struct amd7930 *)map_regs(reg->reg_addr, reg->reg_size,
212d29d28f2Storek 					 reg->reg_bustype);
213d29d28f2Storek 	sc->sc_au.au_amd = amd;
214d29d28f2Storek 	init_amd(amd);
215d29d28f2Storek 
216d29d28f2Storek 	audio_au = &sc->sc_au;
217d29d28f2Storek #ifndef AUDIO_C_HANDLER
218d29d28f2Storek 	settrap(dev->devi_intr->int_pri, audio_trap);
219d29d28f2Storek #else
220d29d28f2Storek 	/* XXX */
221d29d28f2Storek 	addintr(dev->devi_intr->int_pri, audiohwintr, dev->devi_name,
222d29d28f2Storek 		dev->devi_unit);
223d29d28f2Storek #endif
224d29d28f2Storek 	addintr(4, audioswintr, dev->devi_name, dev->devi_unit);
225d29d28f2Storek 	report_dev(dev);
226d29d28f2Storek 
227d29d28f2Storek 	return (0);
228d29d28f2Storek }
229d29d28f2Storek #else
230d29d28f2Storek #define AUDIOOPEN(d, f, i, p) audioopen(dev_t d, int f, int i, struct proc *p)
231d29d28f2Storek #define AUDIOCLOSE(d, f, i, p) audioclose(dev_t d, int f, int i, \
232d29d28f2Storek 					  struct proc *p)
233d29d28f2Storek #define AUDIOREAD(d, u, f) audioread(dev_t d, struct uio *u, int f)
234d29d28f2Storek #define AUDIOWRITE(d, u, f) audiowrite(dev_t d, struct uio *u, int f)
235d29d28f2Storek #define AUDIOIOCTL(d, c, a, f, o)\
236d29d28f2Storek 	audioioctl(dev_t dev, int c, caddr_t a, int f, struct proc *p)
237d29d28f2Storek #define AUDIOSELECT(d, r, p) audioselect(dev_t dev, int rw, struct proc *p)
238d29d28f2Storek #define SELWAKEUP selwakeup
239d29d28f2Storek 
240d29d28f2Storek #define AUDIO_SET_SWINTR ienab_bis(IE_L6)
241d29d28f2Storek 
242d29d28f2Storek /* autoconfiguration driver */
243d29d28f2Storek void	audioattach(struct device *, struct device *, void *);
244d29d28f2Storek struct	cfdriver audiocd =
245d29d28f2Storek     { NULL, "audio", matchbyname, audioattach,
246d29d28f2Storek       DV_DULL, sizeof(struct audio_softc) };
247d29d28f2Storek #define SOFTC(dev) audiocd.cd_devs[minor(dev)]
248d29d28f2Storek #define UIOMOVE(cp, len, code, uio) uiomove(cp, len, uio)
249d29d28f2Storek 
250d29d28f2Storek /*
251d29d28f2Storek  * Audio chip found.
252d29d28f2Storek  */
253d29d28f2Storek void
audioattach(parent,self,args)254d29d28f2Storek audioattach(parent, self, args)
255d29d28f2Storek 	struct device *parent, *self;
256d29d28f2Storek 	void *args;
257d29d28f2Storek {
258d29d28f2Storek 	register struct audio_softc *sc = (struct audio_softc *)self;
259d29d28f2Storek 	register struct romaux *ra = args;
260d29d28f2Storek 	register volatile struct amd7930 *amd;
261d29d28f2Storek 	register int pri;
262d29d28f2Storek 
263d29d28f2Storek 	if (ra->ra_nintr != 1) {
264d29d28f2Storek 		printf(": expected 1 interrupt, got %d\n", ra->ra_nintr);
265d29d28f2Storek 		return;
266d29d28f2Storek 	}
267d29d28f2Storek 	pri = ra->ra_intr[0].int_pri;
268d29d28f2Storek 	printf(" pri %d, softpri %d\n", pri, PIL_AUSOFT);
269d29d28f2Storek 	amd = (volatile struct amd7930 *)(ra->ra_vaddr ?
270d29d28f2Storek 	    ra->ra_vaddr : mapiodev(ra->ra_paddr, sizeof *amd));
271d29d28f2Storek 	sc->sc_au.au_amd = amd;
272d29d28f2Storek 
273d29d28f2Storek 	init_amd(amd);
274d29d28f2Storek 
275d29d28f2Storek #ifndef AUDIO_C_HANDLER
276d29d28f2Storek 	audio_au = &sc->sc_au;
277d29d28f2Storek 	intr_fasttrap(pri, audio_trap);
278d29d28f2Storek #else
279d29d28f2Storek 	sc->sc_hwih.ih_fun = audiohwintr;
280d29d28f2Storek 	sc->sc_hwih.ih_arg = &sc->sc_au;
281d29d28f2Storek 	intr_establish(pri, &sc->sc_hwih);
282d29d28f2Storek #endif
283d29d28f2Storek 	sc->sc_swih.ih_fun = audioswintr;
284d29d28f2Storek 	sc->sc_swih.ih_arg = sc;
285d29d28f2Storek 	intr_establish(PIL_AUSOFT, &sc->sc_swih);
286d29d28f2Storek }
287d29d28f2Storek #endif
288d29d28f2Storek 
289d29d28f2Storek static void
init_amd(amd)290d29d28f2Storek init_amd(amd)
291d29d28f2Storek 	register volatile struct amd7930 *amd;
292d29d28f2Storek {
293d29d28f2Storek 	/* disable interrupts */
294d29d28f2Storek 	amd->cr = AMDR_INIT;
295d29d28f2Storek 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
296d29d28f2Storek 
297d29d28f2Storek 	/*
298d29d28f2Storek 	 * Initialize the mux unit.  We use MCR3 to route audio (MAP)
299d29d28f2Storek 	 * through channel Bb.  MCR1 and MCR2 are unused.
300d29d28f2Storek 	 * Setting the INT enable bit in MCR4 will generate an interrupt
301d29d28f2Storek 	 * on each converted audio sample.
302d29d28f2Storek 	 */
303d29d28f2Storek 	amd->cr = AMDR_MUX_1_4;
304d29d28f2Storek  	amd->dr = 0;
305d29d28f2Storek 	amd->dr = 0;
306d29d28f2Storek 	amd->dr = (AMD_MCRCHAN_BB << 4) | AMD_MCRCHAN_BA;
307d29d28f2Storek 	amd->dr = AMD_MCR4_INT_ENABLE;
308d29d28f2Storek }
309d29d28f2Storek 
310d29d28f2Storek static int audio_default_level = 150;
311d29d28f2Storek static void ausetrgain __P((struct audio_softc *, int));
312d29d28f2Storek static void ausetpgain __P((struct audio_softc *, int));
3131629ecceStorek static void ausetmgain __P((struct audio_softc *, int));
314d29d28f2Storek static int audiosetinfo __P((struct audio_softc *, struct audio_info *));
315d29d28f2Storek static int audiogetinfo __P((struct audio_softc *, struct audio_info *));
316d29d28f2Storek struct sun_audio_info;
317d29d28f2Storek static int sunaudiosetinfo __P((struct audio_softc *,
318d29d28f2Storek 				struct sun_audio_info *));
319d29d28f2Storek static int sunaudiogetinfo __P((struct audio_softc *,
320d29d28f2Storek 				struct sun_audio_info *));
321d29d28f2Storek static void audio_setmmr2 __P((volatile struct amd7930 *, int));
322d29d28f2Storek 
3231fb1e3cbStorek /* ARGSUSED */
324d29d28f2Storek int
AUDIOOPEN(dev,flags,ifmt,p)325d29d28f2Storek AUDIOOPEN(dev, flags, ifmt, p)
326d29d28f2Storek {
327d29d28f2Storek 	register struct audio_softc *sc;
328d29d28f2Storek 	register volatile struct amd7930 *amd;
3291fb1e3cbStorek 	int unit = minor(dev);
330d29d28f2Storek 
331d29d28f2Storek #ifdef SUNOS
332d29d28f2Storek 	if (unit > 0)
333d29d28f2Storek 		return (ENXIO);
334d29d28f2Storek 	sc = &audio_softc;
335d29d28f2Storek #else
336d29d28f2Storek 	if (unit >= audiocd.cd_ndevs || (sc = audiocd.cd_devs[unit]) == NULL)
337d29d28f2Storek 		return (ENXIO);
338d29d28f2Storek #endif
339d29d28f2Storek 	if (sc->sc_open)
340d29d28f2Storek 		return (EBUSY);
341d29d28f2Storek 	sc->sc_open = 1;
342d29d28f2Storek 
343d29d28f2Storek 	sc->sc_au.au_lowat = audio_blocksize;
344d29d28f2Storek 	sc->sc_au.au_hiwat = AUCB_SIZE - sc->sc_au.au_lowat;
345d29d28f2Storek 	sc->sc_au.au_blksize = audio_blocksize;
3461629ecceStorek 	sc->sc_au.au_backlog = audio_backlog;
347d29d28f2Storek 
348d29d28f2Storek 	/* set up read and write blocks and `dead sound' zero value. */
349d29d28f2Storek 	AUCB_INIT(&sc->sc_au.au_rb);
350d29d28f2Storek 	sc->sc_au.au_rb.cb_thresh = AUCB_SIZE;
351d29d28f2Storek 	AUCB_INIT(&sc->sc_au.au_wb);
352d29d28f2Storek 	sc->sc_au.au_wb.cb_thresh = -1;
353d29d28f2Storek 
354d29d28f2Storek 	/* nothing read or written yet */
355d29d28f2Storek 	sc->sc_rseek = 0;
356d29d28f2Storek 	sc->sc_wseek = 0;
357d29d28f2Storek 
358d29d28f2Storek 	bzero((char *)&sc->sc_map, sizeof sc->sc_map);
359d29d28f2Storek 	/* default to speaker */
360d29d28f2Storek 	sc->sc_map.mr_mmr2 = AMD_MMR2_AINB | AMD_MMR2_LS;
361d29d28f2Storek 
362d29d28f2Storek 	/* enable interrupts and set parameters established above */
363d29d28f2Storek 	amd = sc->sc_au.au_amd;
364d29d28f2Storek 	audio_setmmr2(amd, sc->sc_map.mr_mmr2);
365d29d28f2Storek 	ausetrgain(sc, audio_default_level);
366d29d28f2Storek 	ausetpgain(sc, audio_default_level);
3671629ecceStorek 	ausetmgain(sc, 0);
368d29d28f2Storek 	amd->cr = AMDR_INIT;
369d29d28f2Storek 	amd->dr = AMD_INIT_PMS_ACTIVE;
370d29d28f2Storek 
371d29d28f2Storek 	return (0);
372d29d28f2Storek }
373d29d28f2Storek 
374d29d28f2Storek static int
audio_drain(sc)375d29d28f2Storek audio_drain(sc)
376d29d28f2Storek 	register struct audio_softc *sc;
377d29d28f2Storek {
378d29d28f2Storek 	register int error;
379d29d28f2Storek 
380d29d28f2Storek 	while (!AUCB_EMPTY(&sc->sc_au.au_wb))
381d29d28f2Storek 		if ((error = audio_sleep(&sc->sc_au.au_wb, 0)) != 0)
382d29d28f2Storek 			return (error);
383d29d28f2Storek 	return (0);
384d29d28f2Storek }
385d29d28f2Storek 
386d29d28f2Storek /*
387d29d28f2Storek  * Close an audio chip.
388d29d28f2Storek  */
389d29d28f2Storek /* ARGSUSED */
390d29d28f2Storek int
AUDIOCLOSE(dev,flags,ifmt,p)391d29d28f2Storek AUDIOCLOSE(dev, flags, ifmt, p)
392d29d28f2Storek {
393d29d28f2Storek 	register struct audio_softc *sc = SOFTC(dev);
394d29d28f2Storek 	register volatile struct amd7930 *amd;
395d29d28f2Storek 	register struct aucb *cb;
396d29d28f2Storek 	register int s;
397d29d28f2Storek 
398d29d28f2Storek 	/*
399d29d28f2Storek 	 * Block until output drains, but allow ^C interrupt.
400d29d28f2Storek 	 */
401d29d28f2Storek 	sc->sc_au.au_lowat = 0;	/* avoid excessive wakeups */
402d29d28f2Storek 	s = splaudio();
403d29d28f2Storek 	/*
404d29d28f2Storek 	 * If there is pending output, let it drain (unless
405d29d28f2Storek 	 * the output is paused).
406d29d28f2Storek 	 */
407d29d28f2Storek 	cb = &sc->sc_au.au_wb;
408d29d28f2Storek 	if (!AUCB_EMPTY(cb) && !cb->cb_pause)
409d29d28f2Storek 		(void)audio_drain(sc);
410d29d28f2Storek 	/*
411d29d28f2Storek 	 * Disable interrupts, clear open flag, and done.
412d29d28f2Storek 	 */
413d29d28f2Storek 	amd = sc->sc_au.au_amd;
414d29d28f2Storek 	amd->cr = AMDR_INIT;
415d29d28f2Storek 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
416d29d28f2Storek 	splx(s);
417d29d28f2Storek 	sc->sc_open = 0;
418d29d28f2Storek 	return (0);
419d29d28f2Storek }
420d29d28f2Storek 
421d29d28f2Storek int
audio_sleep(cb,thresh)422d29d28f2Storek audio_sleep(cb, thresh)
423d29d28f2Storek 	register struct aucb *cb;
424d29d28f2Storek 	register int thresh;
425d29d28f2Storek {
426d29d28f2Storek 	register int error;
4271629ecceStorek 	register int s = splaudio();
428d29d28f2Storek 
429d29d28f2Storek 	cb->cb_thresh = thresh;
430d29d28f2Storek 	error = tsleep((caddr_t)cb, (PZERO + 1) | PCATCH, "audio", 0);
4311629ecceStorek 	splx(s);
432d29d28f2Storek 	return (error);
433d29d28f2Storek }
434d29d28f2Storek 
4351fb1e3cbStorek /* ARGSUSED */
436d29d28f2Storek int
AUDIOREAD(dev,uio,ioflag)437d29d28f2Storek AUDIOREAD(dev, uio, ioflag)
438d29d28f2Storek {
439d29d28f2Storek 	register struct audio_softc *sc = SOFTC(dev);
440d29d28f2Storek 	register struct aucb *cb;
4411629ecceStorek 	register int n, head, taildata, error;
442d29d28f2Storek 	register int blocksize = sc->sc_au.au_blksize;
443d29d28f2Storek 
444d29d28f2Storek 	if (uio->uio_resid == 0)
445d29d28f2Storek 		return (0);
446d29d28f2Storek 	cb = &sc->sc_au.au_rb;
447d29d28f2Storek 	error = 0;
448d29d28f2Storek 	cb->cb_drops = 0;
449d29d28f2Storek 	sc->sc_rseek = sc->sc_au.au_stamp - AUCB_LEN(cb);
450d29d28f2Storek 	do {
451d29d28f2Storek 		while (AUCB_LEN(cb) < blocksize) {
452d29d28f2Storek #ifndef SUNOS
453d29d28f2Storek 			if (ioflag & IO_NDELAY) {
454d29d28f2Storek 				error = EWOULDBLOCK;
4551629ecceStorek 				return (error);
456d29d28f2Storek 			}
457d29d28f2Storek #endif
458d29d28f2Storek 			if ((error = audio_sleep(cb, blocksize)) != 0)
4591629ecceStorek 				return (error);
460d29d28f2Storek 		}
461d29d28f2Storek 		/*
462d29d28f2Storek 		 * The space calculation can only err on the short
463d29d28f2Storek 		 * side if an interrupt occurs during processing:
464d29d28f2Storek 		 * only cb_tail is altered in the interrupt code.
465d29d28f2Storek 		 */
466d29d28f2Storek 		head = cb->cb_head;
467d29d28f2Storek 		if ((n = AUCB_LEN(cb)) > uio->uio_resid)
468d29d28f2Storek 			n = uio->uio_resid;
469d29d28f2Storek 		taildata = AUCB_SIZE - head;
470d29d28f2Storek 		if (n > taildata) {
471d29d28f2Storek 			error = UIOMOVE((caddr_t)cb->cb_data + head,
472d29d28f2Storek 					taildata, UIO_READ, uio);
473d29d28f2Storek 			if (error == 0)
474d29d28f2Storek 				error = UIOMOVE((caddr_t)cb->cb_data,
475d29d28f2Storek 						n - taildata, UIO_READ, uio);
476d29d28f2Storek 		} else
477d29d28f2Storek 			error = UIOMOVE((caddr_t)cb->cb_data + head, n,
478d29d28f2Storek 					UIO_READ, uio);
479d29d28f2Storek 		if (error)
4801629ecceStorek 			break;
481d29d28f2Storek 		head = AUCB_MOD(head + n);
482d29d28f2Storek 		cb->cb_head = head;
483d29d28f2Storek 	} while (uio->uio_resid >= blocksize);
4841629ecceStorek 
485d29d28f2Storek 	return (error);
486d29d28f2Storek }
487d29d28f2Storek 
4881fb1e3cbStorek /* ARGSUSED */
489d29d28f2Storek int
AUDIOWRITE(dev,uio,ioflag)490d29d28f2Storek AUDIOWRITE(dev, uio, ioflag)
491d29d28f2Storek {
492d29d28f2Storek 	register struct audio_softc *sc = SOFTC(dev);
493d29d28f2Storek 	register struct aucb *cb = &sc->sc_au.au_wb;
4941fb1e3cbStorek 	register int n, tail, tailspace, error, first, watermark;
495d29d28f2Storek 
496d29d28f2Storek 	error = 0;
497d29d28f2Storek 	first = 1;
498d29d28f2Storek 	while (uio->uio_resid > 0) {
499d29d28f2Storek 		watermark = sc->sc_au.au_hiwat;
500d29d28f2Storek 		while (AUCB_LEN(cb) > watermark) {
501d29d28f2Storek #ifndef SUNOS
502d29d28f2Storek 			if (ioflag & IO_NDELAY) {
503d29d28f2Storek 				error = EWOULDBLOCK;
5041629ecceStorek 				return (error);
505d29d28f2Storek 			}
506d29d28f2Storek #endif
507d29d28f2Storek 			if ((error = audio_sleep(cb, watermark)) != 0)
5081629ecceStorek 				return (error);
509d29d28f2Storek 			watermark = sc->sc_au.au_lowat;
510d29d28f2Storek 		}
511d29d28f2Storek 		/*
512d29d28f2Storek 		 * The only value that can change on an interrupt is
513d29d28f2Storek 		 * cb->cb_head.  We only pull that out once to decide
514d29d28f2Storek 		 * how much to write into cb_data; if we lose a race
515d29d28f2Storek 		 * and cb_head changes, we will merely be overly
516d29d28f2Storek 		 * conservative.  For a legitimate time stamp,
517d29d28f2Storek 		 * however, we need to synchronize the accesses to
518d29d28f2Storek 		 * au_stamp and cb_head at a high ipl below.
519d29d28f2Storek 		 */
520d29d28f2Storek 		tail = cb->cb_tail;
5211629ecceStorek 		if ((n = (AUCB_SIZE - 1) - AUCB_LEN(cb)) > uio->uio_resid) {
5221629ecceStorek 			n = uio->uio_resid;
5231629ecceStorek 			if (cb->cb_head == tail &&
5241629ecceStorek 			    n <= sc->sc_au.au_blksize &&
5251629ecceStorek 			    sc->sc_au.au_stamp - sc->sc_wseek > 400) {
5261629ecceStorek 				/*
5271629ecceStorek 				 * the write is 'small', the buffer is empty
5281629ecceStorek 				 * and we have been silent for at least 50ms
5291629ecceStorek 				 * so we might be dealing with an application
5301629ecceStorek 				 * that writes frames synchronously with
5311629ecceStorek 				 * reading them.  If so, we need an output
5321629ecceStorek 				 * backlog to cover scheduling delays or
5331629ecceStorek 				 * there will be gaps in the sound output.
5341629ecceStorek 				 * Also take this opportunity to reset the
5351629ecceStorek 				 * buffer pointers in case we ended up on
5361629ecceStorek 				 * a bad boundary (odd byte, blksize bytes
5371629ecceStorek 				 * from end, etc.).
5381629ecceStorek 				 */
5391629ecceStorek 				register u_int* ip;
5401629ecceStorek 				register int muzero = 0x7f7f7f7f;
5411629ecceStorek 				register int i = splaudio();
5421629ecceStorek 				cb->cb_head = cb->cb_tail = 0;
5431629ecceStorek 				splx(i);
5441629ecceStorek 				tail = sc->sc_au.au_backlog;
5451629ecceStorek 				ip = (u_int*)cb->cb_data;
5461629ecceStorek 				for (i = tail >> 2; --i >= 0; )
5471629ecceStorek 					*ip++ = muzero;
5481629ecceStorek 			}
5491629ecceStorek 		}
550d29d28f2Storek 		tailspace = AUCB_SIZE - tail;
551d29d28f2Storek 		if (n > tailspace) {
552d29d28f2Storek 			/* write first part at tail and rest at head */
553d29d28f2Storek 			error = UIOMOVE((caddr_t)cb->cb_data + tail,
554d29d28f2Storek 					tailspace, UIO_WRITE, uio);
555d29d28f2Storek 			if (error == 0)
556d29d28f2Storek 				error = UIOMOVE((caddr_t)cb->cb_data,
557d29d28f2Storek 						n - tailspace, UIO_WRITE, uio);
558d29d28f2Storek 		} else
559d29d28f2Storek 			error = UIOMOVE((caddr_t)cb->cb_data + tail, n,
560d29d28f2Storek 					UIO_WRITE, uio);
561d29d28f2Storek 		if (error)
5621629ecceStorek 			break;
5631629ecceStorek 
564d29d28f2Storek 		tail = AUCB_MOD(tail + n);
565d29d28f2Storek 		if (first) {
5661629ecceStorek 			register int s = splaudio();
5671629ecceStorek 			sc->sc_wseek = AUCB_LEN(cb) + sc->sc_au.au_stamp + 1;
568d29d28f2Storek 			/*
569d29d28f2Storek 			 * To guarantee that a write is contiguous in the
570d29d28f2Storek 			 * sample space, we clear the drop count the first
571d29d28f2Storek 			 * time through.  If we later get drops, we will
572d29d28f2Storek 			 * break out of the loop below, before writing
573d29d28f2Storek 			 * a new frame.
574d29d28f2Storek 			 */
575d29d28f2Storek 			cb->cb_drops = 0;
576d29d28f2Storek 			cb->cb_tail = tail;
5771629ecceStorek 			splx(s);
5781629ecceStorek 			first = 0;
5791629ecceStorek 		} else {
580d29d28f2Storek 			if (cb->cb_drops != 0)
581d29d28f2Storek 				break;
5821629ecceStorek 			cb->cb_tail = tail;
583d29d28f2Storek 		}
5841629ecceStorek 	}
585d29d28f2Storek 	return (error);
586d29d28f2Storek }
587d29d28f2Storek 
588d29d28f2Storek /* Sun audio compatibility */
589d29d28f2Storek struct sun_audio_prinfo {
590d29d28f2Storek 	u_int	sample_rate;
591d29d28f2Storek 	u_int	channels;
592d29d28f2Storek 	u_int	precision;
593d29d28f2Storek 	u_int	encoding;
594d29d28f2Storek 	u_int	gain;
595d29d28f2Storek 	u_int	port;
596d29d28f2Storek 	u_int	reserved0[4];
597d29d28f2Storek 	u_int	samples;
598d29d28f2Storek 	u_int	eof;
599d29d28f2Storek 	u_char	pause;
600d29d28f2Storek 	u_char	error;
601d29d28f2Storek 	u_char	waiting;
602d29d28f2Storek 	u_char	reserved1[3];
603d29d28f2Storek 	u_char	open;
604d29d28f2Storek 	u_char	active;
605d29d28f2Storek };
606d29d28f2Storek struct sun_audio_info {
607d29d28f2Storek 	struct sun_audio_prinfo play;
608d29d28f2Storek 	struct sun_audio_prinfo record;
609d29d28f2Storek 	u_int monitor_gain;
610d29d28f2Storek 	u_int reserved[4];
611d29d28f2Storek };
612d29d28f2Storek 
613d29d28f2Storek #ifndef SUNOS
614d29d28f2Storek #define SUNAUDIO_GETINFO	_IOR('A', 1, struct sun_audio_info)
615d29d28f2Storek #define SUNAUDIO_SETINFO	_IOWR('A', 2, struct sun_audio_info)
616d29d28f2Storek #else
617d29d28f2Storek #define SUNAUDIO_GETINFO	_IOR(A, 1, struct sun_audio_info)
618d29d28f2Storek #define SUNAUDIO_SETINFO	_IOWR(A, 2, struct sun_audio_info)
619d29d28f2Storek #endif
620d29d28f2Storek 
6211fb1e3cbStorek /* ARGSUSED */
622d29d28f2Storek int
AUDIOIOCTL(dev,cmd,addr,flag,p)623d29d28f2Storek AUDIOIOCTL(dev, cmd, addr, flag, p)
624d29d28f2Storek {
625d29d28f2Storek 	register struct audio_softc *sc = SOFTC(dev);
6261fb1e3cbStorek 	int error = 0, s;
627d29d28f2Storek 
628d29d28f2Storek 	switch (cmd) {
629d29d28f2Storek 
630d29d28f2Storek 	case AUDIO_GETMAP:
631d29d28f2Storek 		bcopy((caddr_t)&sc->sc_map, addr, sizeof(sc->sc_map));
632d29d28f2Storek 		break;
633d29d28f2Storek 
634d29d28f2Storek 	case AUDIO_SETMAP:
635d29d28f2Storek 		bcopy(addr, (caddr_t)&sc->sc_map, sizeof(sc->sc_map));
636d29d28f2Storek 		sc->sc_map.mr_mmr2 &= 0x7f;
637d29d28f2Storek 		audio_setmap(sc->sc_au.au_amd, &sc->sc_map);
638d29d28f2Storek 		break;
639d29d28f2Storek 
640d29d28f2Storek 	case AUDIO_FLUSH:
641d29d28f2Storek 		s = splaudio();
642d29d28f2Storek 		AUCB_INIT(&sc->sc_au.au_rb);
643d29d28f2Storek 		AUCB_INIT(&sc->sc_au.au_wb);
6441629ecceStorek 		sc->sc_au.au_stamp = 0;
645d29d28f2Storek 		splx(s);
646d29d28f2Storek 		sc->sc_wseek = 0;
647d29d28f2Storek 		sc->sc_rseek = 0;
648d29d28f2Storek 		break;
649d29d28f2Storek 
650d29d28f2Storek 	/*
651d29d28f2Storek 	 * Number of read samples dropped.  We don't know where or
652d29d28f2Storek 	 * when they were dropped.
653d29d28f2Storek 	 */
654d29d28f2Storek 	case AUDIO_RERROR:
655d29d28f2Storek 		*(int *)addr = sc->sc_au.au_rb.cb_drops != 0;
656d29d28f2Storek 		break;
657d29d28f2Storek 
658d29d28f2Storek 	/*
6591629ecceStorek 	 * How many samples will elapse until mike hears the first
6601629ecceStorek 	 * sample of what we last wrote?
661d29d28f2Storek 	 */
662d29d28f2Storek 	case AUDIO_WSEEK:
6631629ecceStorek 		s = splaudio();
6641629ecceStorek 		*(u_long *)addr = sc->sc_wseek - sc->sc_au.au_stamp
6651629ecceStorek 				  + AUCB_LEN(&sc->sc_au.au_rb);
6661629ecceStorek 		splx(s);
667d29d28f2Storek 		break;
668d29d28f2Storek 
669d29d28f2Storek 	case AUDIO_SETINFO:
670d29d28f2Storek 		error = audiosetinfo(sc, (struct audio_info *)addr);
671d29d28f2Storek 		break;
672d29d28f2Storek 
673d29d28f2Storek 	case AUDIO_GETINFO:
674d29d28f2Storek 		error = audiogetinfo(sc, (struct audio_info *)addr);
675d29d28f2Storek 		break;
676d29d28f2Storek 
677d29d28f2Storek 	case SUNAUDIO_GETINFO:
678d29d28f2Storek 		error = sunaudiogetinfo(sc, (struct sun_audio_info *)addr);
679d29d28f2Storek 		break;
680d29d28f2Storek 
681d29d28f2Storek 	case SUNAUDIO_SETINFO:
682d29d28f2Storek 		error = sunaudiosetinfo(sc, (struct sun_audio_info *)addr);
683d29d28f2Storek 		break;
684d29d28f2Storek 
685d29d28f2Storek 	case AUDIO_DRAIN:
686d29d28f2Storek 		error = audio_drain(sc);
687d29d28f2Storek 		break;
688d29d28f2Storek 
689d29d28f2Storek 	default:
690d29d28f2Storek 		error = EINVAL;
691d29d28f2Storek 		break;
692d29d28f2Storek 	}
693d29d28f2Storek 	return (error);
694d29d28f2Storek }
695d29d28f2Storek 
6961fb1e3cbStorek /* ARGSUSED */
697d29d28f2Storek int
AUDIOSELECT(dev,rw,p)698d29d28f2Storek AUDIOSELECT(dev, rw, p)
699d29d28f2Storek {
700d29d28f2Storek 	register struct audio_softc *sc = SOFTC(dev);
701d29d28f2Storek 	register struct aucb *cb;
702d29d28f2Storek 	register int s = splaudio();
703d29d28f2Storek 
704d29d28f2Storek 	switch (rw) {
705d29d28f2Storek 
706d29d28f2Storek 	case FREAD:
707d29d28f2Storek 		cb = &sc->sc_au.au_rb;
708d29d28f2Storek 		if (AUCB_LEN(cb) >= sc->sc_au.au_blksize) {
709d29d28f2Storek 			splx(s);
710d29d28f2Storek 			return (1);
711d29d28f2Storek 		}
712d29d28f2Storek 		selrecord(p, &sc->sc_rsel);
713d29d28f2Storek 		cb->cb_thresh = sc->sc_au.au_blksize;
714d29d28f2Storek 		break;
715d29d28f2Storek 
716d29d28f2Storek 	case FWRITE:
717d29d28f2Storek 		cb = &sc->sc_au.au_wb;
718d29d28f2Storek 		if (AUCB_LEN(cb) <= sc->sc_au.au_lowat) {
719d29d28f2Storek 			splx(s);
720d29d28f2Storek 			return (1);
721d29d28f2Storek 		}
722d29d28f2Storek 		selrecord(p, &sc->sc_wsel);
723d29d28f2Storek 		cb->cb_thresh = sc->sc_au.au_lowat;
724d29d28f2Storek 		break;
725d29d28f2Storek 	}
726d29d28f2Storek 	splx(s);
727d29d28f2Storek 	return (0);
728d29d28f2Storek }
729d29d28f2Storek 
730d29d28f2Storek #ifdef AUDIO_C_HANDLER
731d29d28f2Storek int
audiohwintr(au0)732d29d28f2Storek audiohwintr(au0)
733d29d28f2Storek 	void *au0;
734d29d28f2Storek {
735d29d28f2Storek #ifdef SUNOS
736d29d28f2Storek 	register struct auio *au = audio_au;
737d29d28f2Storek #else
738d29d28f2Storek 	register struct auio *au = au0;
739d29d28f2Storek #endif
740d29d28f2Storek 	register volatile struct amd7930 *amd = au->au_amd;
741d29d28f2Storek 	register struct aucb *cb;
742d29d28f2Storek 	register int h, t, k;
743d29d28f2Storek 
744d29d28f2Storek 	k = amd->ir;		/* clear interrupt */
745d29d28f2Storek 	++au->au_stamp;
746d29d28f2Storek 
747d29d28f2Storek 	/* receive incoming data */
748d29d28f2Storek 	cb = &au->au_rb;
749d29d28f2Storek 	h = cb->cb_head;
750d29d28f2Storek 	t = cb->cb_tail;
751d29d28f2Storek 	k = AUCB_MOD(t + 1);
752d29d28f2Storek 	if (h == k)
753d29d28f2Storek 		cb->cb_drops++;
754d29d28f2Storek 	else if  (cb->cb_pause != 0)
755d29d28f2Storek 		cb->cb_pdrops++;
756d29d28f2Storek 	else {
757d29d28f2Storek 		cb->cb_data[t] = amd->bbrb;
758d29d28f2Storek 		cb->cb_tail = t = k;
759d29d28f2Storek 	}
760d29d28f2Storek 	if (AUCB_MOD(t - h) >= cb->cb_thresh) {
761d29d28f2Storek 		cb->cb_thresh = AUCB_SIZE;
762d29d28f2Storek 		cb->cb_waking = 1;
763d29d28f2Storek 		AUDIO_SET_SWINTR;
764d29d28f2Storek 	}
765d29d28f2Storek 	/* send outgoing data */
766d29d28f2Storek 	cb = &au->au_wb;
767d29d28f2Storek 	h = cb->cb_head;
768d29d28f2Storek 	t = cb->cb_tail;
769d29d28f2Storek 	if (h == t)
770d29d28f2Storek 		cb->cb_drops++;
771d29d28f2Storek 	else if (cb->cb_pause != 0)
772d29d28f2Storek 		cb->cb_pdrops++;
773d29d28f2Storek 	else {
774d29d28f2Storek 		cb->cb_head = h = AUCB_MOD(h + 1);
775d29d28f2Storek 		amd->bbtb = cb->cb_data[h];
776d29d28f2Storek 	}
777d29d28f2Storek 	if (AUCB_MOD(t - h) <= cb->cb_thresh) {
778d29d28f2Storek 		cb->cb_thresh = -1;
779d29d28f2Storek 		cb->cb_waking = 1;
780d29d28f2Storek 		AUDIO_SET_SWINTR;
781d29d28f2Storek 	}
782d29d28f2Storek 	return (1);
783d29d28f2Storek }
784d29d28f2Storek #endif
785d29d28f2Storek 
7861fb1e3cbStorek /* ARGSUSED */
787d29d28f2Storek int
audioswintr(sc0)788d29d28f2Storek audioswintr(sc0)
789d29d28f2Storek 	void *sc0;
790d29d28f2Storek {
791d29d28f2Storek 	register struct audio_softc *sc;
792d29d28f2Storek 	register int s, ret = 0;
793d29d28f2Storek #ifdef SUNOS
794d29d28f2Storek 	sc = &audio_softc;
795d29d28f2Storek #else
796d29d28f2Storek 	sc = sc0;
797d29d28f2Storek #endif
798d29d28f2Storek 	s = splaudio();
799d29d28f2Storek 	if (sc->sc_au.au_rb.cb_waking != 0) {
800d29d28f2Storek 		sc->sc_au.au_rb.cb_waking = 0;
801d29d28f2Storek 		splx(s);
802d29d28f2Storek 		ret = 1;
803d29d28f2Storek 		wakeup((caddr_t)&sc->sc_au.au_rb);
804d29d28f2Storek 		SELWAKEUP(&sc->sc_rsel);
805d29d28f2Storek 	}
806d29d28f2Storek 	if (sc->sc_au.au_wb.cb_waking != 0) {
807d29d28f2Storek 		sc->sc_au.au_wb.cb_waking = 0;
808d29d28f2Storek 		splx(s);
809d29d28f2Storek 		ret = 1;
810d29d28f2Storek 		wakeup((caddr_t)&sc->sc_au.au_wb);
811d29d28f2Storek 		SELWAKEUP(&sc->sc_wsel);
812d29d28f2Storek 	} else
813d29d28f2Storek 		splx(s);
814d29d28f2Storek 	return (ret);
815d29d28f2Storek }
816d29d28f2Storek 
817d29d28f2Storek /* Write 16 bits of data from variable v to the data port of the audio chip */
818d29d28f2Storek 
8191629ecceStorek #define	WAMD16(amd, v) ((amd)->dr = (v), (amd)->dr = (v) >> 8)
820d29d28f2Storek 
821d29d28f2Storek void
audio_setmap(amd,map)822d29d28f2Storek audio_setmap(amd, map)
823d29d28f2Storek 	register volatile struct amd7930 *amd;
824d29d28f2Storek 	register struct mapreg *map;
825d29d28f2Storek {
826d29d28f2Storek 	register int i, s, v;
827d29d28f2Storek 
828d29d28f2Storek 	s = splaudio();
829d29d28f2Storek 	amd->cr = AMDR_MAP_1_10;
830d29d28f2Storek 	for (i = 0; i < 8; i++) {
831d29d28f2Storek 		v = map->mr_x[i];
832d29d28f2Storek 		WAMD16(amd, v);
833d29d28f2Storek 	}
834d29d28f2Storek 	for (i = 0; i < 8; ++i) {
835d29d28f2Storek 		v = map->mr_r[i];
836d29d28f2Storek 		WAMD16(amd, v);
837d29d28f2Storek 	}
838d29d28f2Storek 	v = map->mr_gx; WAMD16(amd, v);
839d29d28f2Storek 	v = map->mr_gr; WAMD16(amd, v);
840d29d28f2Storek 	v = map->mr_ger; WAMD16(amd, v);
841d29d28f2Storek 	v = map->mr_stgr; WAMD16(amd, v);
842d29d28f2Storek 	v = map->mr_ftgr; WAMD16(amd, v);
843d29d28f2Storek 	v = map->mr_atgr; WAMD16(amd, v);
844d29d28f2Storek 	amd->dr = map->mr_mmr1;
845d29d28f2Storek 	amd->dr = map->mr_mmr2;
846d29d28f2Storek 	splx(s);
847d29d28f2Storek }
848d29d28f2Storek 
849d29d28f2Storek /*
850d29d28f2Storek  * Set the mmr1 register and one other 16 bit register in the audio chip.
851d29d28f2Storek  * The other register is indicated by op and val.
852d29d28f2Storek  */
853d29d28f2Storek void
audio_setmmr1(amd,mmr1,op,val)854d29d28f2Storek audio_setmmr1(amd, mmr1, op, val)
855d29d28f2Storek 	register volatile struct amd7930 *amd;
856d29d28f2Storek 	register int mmr1;
857d29d28f2Storek 	register int op;
858d29d28f2Storek 	register int val;
859d29d28f2Storek {
860d29d28f2Storek 	register int s = splaudio();
861d29d28f2Storek 
862d29d28f2Storek 	amd->cr = AMDR_MAP_MMR1;
863d29d28f2Storek 	amd->dr = mmr1;
864d29d28f2Storek 	amd->cr = op;
865d29d28f2Storek 	WAMD16(amd, val);
866d29d28f2Storek 	splx(s);
867d29d28f2Storek }
868d29d28f2Storek 
869d29d28f2Storek /*
8701629ecceStorek  * Set the mmr2 register.
871d29d28f2Storek  */
872d29d28f2Storek static void
audio_setmmr2(amd,mmr2)873d29d28f2Storek audio_setmmr2(amd, mmr2)
874d29d28f2Storek 	register volatile struct amd7930 *amd;
875d29d28f2Storek 	register int mmr2;
876d29d28f2Storek {
877d29d28f2Storek 	register int s = splaudio();
878d29d28f2Storek 
879d29d28f2Storek 	amd->cr = AMDR_MAP_MMR2;
880d29d28f2Storek 	amd->dr = mmr2;
881d29d28f2Storek 	splx(s);
882d29d28f2Storek }
883d29d28f2Storek 
8841629ecceStorek /*
8851629ecceStorek  * gx, gr & stg gains.  this table must contain 256 elements with
8861629ecceStorek  * the 0th being "infinity" (the magic value 9008).  The remaining
8871629ecceStorek  * elements match sun's gain curve (but with higher resolution):
8881629ecceStorek  * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
8891629ecceStorek  */
8901629ecceStorek static const u_short gx_coeff[256] = {
8911629ecceStorek 	0x9008, 0x8b7c, 0x8b51, 0x8b45, 0x8b42, 0x8b3b, 0x8b36, 0x8b33,
8921629ecceStorek 	0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
8931629ecceStorek 	0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
8941629ecceStorek 	0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
8951629ecceStorek 	0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
8961629ecceStorek 	0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
8971629ecceStorek 	0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
8981629ecceStorek 	0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
8991629ecceStorek 	0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
9001629ecceStorek 	0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
9011629ecceStorek 	0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
9021629ecceStorek 	0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
9031629ecceStorek 	0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
9041629ecceStorek 	0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
9051629ecceStorek 	0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
9061629ecceStorek 	0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
9071629ecceStorek 	0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
9081629ecceStorek 	0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
9091629ecceStorek 	0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
9101629ecceStorek 	0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
9111629ecceStorek 	0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
9121629ecceStorek 	0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
9131629ecceStorek 	0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
9141629ecceStorek 	0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
9151629ecceStorek 	0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
9161629ecceStorek 	0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
9171629ecceStorek 	0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
9181629ecceStorek 	0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
9191629ecceStorek 	0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
9201629ecceStorek 	0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
9211629ecceStorek 	0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
9221629ecceStorek 	0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
9231629ecceStorek };
9241629ecceStorek 
9251629ecceStorek /*
9261629ecceStorek  * second stage play gain.
9271629ecceStorek  */
9281629ecceStorek static const u_short ger_coeff[] = {
9291629ecceStorek 	0x431f, /* 5. dB */
9301629ecceStorek 	0x331f, /* 5.5 dB */
9311629ecceStorek 	0x40dd, /* 6. dB */
9321629ecceStorek 	0x11dd, /* 6.5 dB */
9331629ecceStorek 	0x440f, /* 7. dB */
9341629ecceStorek 	0x411f, /* 7.5 dB */
9351629ecceStorek 	0x311f, /* 8. dB */
9361629ecceStorek 	0x5520, /* 8.5 dB */
9371629ecceStorek 	0x10dd, /* 9. dB */
9381629ecceStorek 	0x4211, /* 9.5 dB */
9391629ecceStorek 	0x410f, /* 10. dB */
9401629ecceStorek 	0x111f, /* 10.5 dB */
9411629ecceStorek 	0x600b, /* 11. dB */
9421629ecceStorek 	0x00dd, /* 11.5 dB */
9431629ecceStorek 	0x4210, /* 12. dB */
9441629ecceStorek 	0x110f, /* 13. dB */
9451629ecceStorek 	0x7200, /* 14. dB */
9461629ecceStorek 	0x2110, /* 15. dB */
9471629ecceStorek 	0x2200, /* 15.9 dB */
9481629ecceStorek 	0x000b, /* 16.9 dB */
9491629ecceStorek 	0x000f  /* 18. dB */
950d29d28f2Storek #define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
951d29d28f2Storek };
952d29d28f2Storek 
953d29d28f2Storek static void
ausetrgain(sc,level)954d29d28f2Storek ausetrgain(sc, level)
955d29d28f2Storek 	register struct audio_softc *sc;
956d29d28f2Storek 	register int level;
957d29d28f2Storek {
958d29d28f2Storek 	level &= 0xff;
959d29d28f2Storek 	sc->sc_rlevel = level;
960d29d28f2Storek 	sc->sc_map.mr_mmr1 |= AMD_MMR1_GX;
9611629ecceStorek 	sc->sc_map.mr_gx = gx_coeff[level];
962d29d28f2Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
963d29d28f2Storek 		      AMDR_MAP_GX, sc->sc_map.mr_gx);
964d29d28f2Storek }
965d29d28f2Storek 
966d29d28f2Storek static void
ausetpgain(sc,level)967d29d28f2Storek ausetpgain(sc, level)
968d29d28f2Storek 	register struct audio_softc *sc;
969d29d28f2Storek 	register int level;
970d29d28f2Storek {
9711629ecceStorek 	register int gi, s;
9721629ecceStorek 	register volatile struct amd7930 *amd;
9731629ecceStorek 
974d29d28f2Storek 	level &= 0xff;
975d29d28f2Storek 	sc->sc_plevel = level;
9761629ecceStorek 	sc->sc_map.mr_mmr1 |= AMD_MMR1_GER|AMD_MMR1_GR;
9771629ecceStorek 	level *= 256 + NGER;
9781629ecceStorek 	level >>= 8;
9791629ecceStorek 	if (level >= 256) {
9801629ecceStorek 		gi = level - 256;
9811629ecceStorek 		level = 255;
9821629ecceStorek 	} else
9831629ecceStorek 		gi = 0;
9841629ecceStorek 	sc->sc_map.mr_ger = ger_coeff[gi];
9851629ecceStorek 	sc->sc_map.mr_gr = gx_coeff[level];
986d29d28f2Storek 
9871629ecceStorek 	amd = sc->sc_au.au_amd;
9881629ecceStorek 	s = splaudio();
9891629ecceStorek 	amd->cr = AMDR_MAP_MMR1;
9901629ecceStorek 	amd->dr = sc->sc_map.mr_mmr1;
9911629ecceStorek 	amd->cr = AMDR_MAP_GR;
9921629ecceStorek 	gi =  sc->sc_map.mr_gr;
9931629ecceStorek 	WAMD16(amd, gi);
9941629ecceStorek 	amd->cr = AMDR_MAP_GER;
9951629ecceStorek 	gi =  sc->sc_map.mr_ger;
9961629ecceStorek 	WAMD16(amd, gi);
9971629ecceStorek 	splx(s);
998d29d28f2Storek }
999d29d28f2Storek 
1000d29d28f2Storek static void
ausetmgain(sc,level)1001d29d28f2Storek ausetmgain(sc, level)
1002d29d28f2Storek 	register struct audio_softc *sc;
1003d29d28f2Storek 	register int level;
1004d29d28f2Storek {
1005d29d28f2Storek 	level &= 0xff;
1006d29d28f2Storek 	sc->sc_mlevel = level;
1007d29d28f2Storek 	sc->sc_map.mr_mmr1 |= AMD_MMR1_STG;
10081629ecceStorek 	sc->sc_map.mr_stgr = gx_coeff[level];
1009d29d28f2Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
1010d29d28f2Storek 		      AMDR_MAP_STG, sc->sc_map.mr_stgr);
1011d29d28f2Storek }
1012d29d28f2Storek 
1013d29d28f2Storek static int
audiosetinfo(sc,ai)1014d29d28f2Storek audiosetinfo(sc, ai)
1015d29d28f2Storek 	struct audio_softc *sc;
1016d29d28f2Storek 	struct audio_info *ai;
1017d29d28f2Storek {
1018d29d28f2Storek 	struct audio_prinfo *r = &ai->record, *p = &ai->play;
1019d29d28f2Storek 	register int s, bsize;
1020d29d28f2Storek 
1021d29d28f2Storek 	if (p->gain != ~0)
1022d29d28f2Storek 		ausetpgain(sc, p->gain);
1023d29d28f2Storek 	if (r->gain != ~0)
1024d29d28f2Storek 		ausetrgain(sc, r->gain);
1025d29d28f2Storek 	if (ai->monitor_gain != ~0)
10261629ecceStorek 		ausetmgain(sc, ai->monitor_gain);
1027d29d28f2Storek 	if (p->port == AUDIO_SPEAKER) {
1028d29d28f2Storek 		sc->sc_map.mr_mmr2 |= AMD_MMR2_LS;
1029d29d28f2Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
1030d29d28f2Storek 	} else if (p->port == AUDIO_HEADPHONE) {
1031d29d28f2Storek 		sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS;
1032d29d28f2Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
1033d29d28f2Storek 	}
1034d29d28f2Storek 	if (p->pause != (u_char)~0)
1035d29d28f2Storek 		sc->sc_au.au_wb.cb_pause = p->pause;
1036d29d28f2Storek 	if (r->pause != (u_char)~0)
1037d29d28f2Storek 		sc->sc_au.au_rb.cb_pause = r->pause;
1038d29d28f2Storek 
1039d29d28f2Storek 	if (ai->blocksize != ~0) {
1040d29d28f2Storek 		if (ai->blocksize == 0)
1041d29d28f2Storek 			bsize = ai->blocksize = DEFBLKSIZE;
1042d29d28f2Storek 		else if (ai->blocksize > MAXBLKSIZE)
1043d29d28f2Storek 			bsize = ai->blocksize = MAXBLKSIZE;
1044d29d28f2Storek 		else
1045d29d28f2Storek 			bsize = ai->blocksize;
1046d29d28f2Storek 
1047d29d28f2Storek 		s = splaudio();
1048d29d28f2Storek 		sc->sc_au.au_blksize = bsize;
1049d29d28f2Storek 		/* AUDIO_FLUSH */
1050d29d28f2Storek 		AUCB_INIT(&sc->sc_au.au_rb);
1051d29d28f2Storek 		AUCB_INIT(&sc->sc_au.au_wb);
1052d29d28f2Storek 		splx(s);
1053d29d28f2Storek 
1054d29d28f2Storek 	}
1055d29d28f2Storek 	if (ai->hiwat != ~0 && (unsigned)ai->hiwat < AUCB_SIZE)
1056d29d28f2Storek 		sc->sc_au.au_hiwat = ai->hiwat;
1057d29d28f2Storek 	if (ai->lowat != ~0 && ai->lowat < AUCB_SIZE)
1058d29d28f2Storek 		sc->sc_au.au_lowat = ai->lowat;
10591629ecceStorek 	if (ai->backlog != ~0 && ai->backlog < (AUCB_SIZE/2))
10601629ecceStorek 		sc->sc_au.au_backlog = ai->backlog;
1061d29d28f2Storek 
1062d29d28f2Storek 	return (0);
1063d29d28f2Storek }
1064d29d28f2Storek 
1065d29d28f2Storek static int
sunaudiosetinfo(sc,ai)1066d29d28f2Storek sunaudiosetinfo(sc, ai)
1067d29d28f2Storek 	struct audio_softc *sc;
1068d29d28f2Storek 	struct sun_audio_info *ai;
1069d29d28f2Storek {
1070d29d28f2Storek 	struct sun_audio_prinfo *r = &ai->record, *p = &ai->play;
1071d29d28f2Storek 
1072d29d28f2Storek 	if (p->gain != ~0)
1073d29d28f2Storek 		ausetpgain(sc, p->gain);
1074d29d28f2Storek 	if (r->gain != ~0)
1075d29d28f2Storek 		ausetrgain(sc, r->gain);
1076d29d28f2Storek 	if (ai->monitor_gain != ~0)
10771629ecceStorek 		ausetmgain(sc, ai->monitor_gain);
1078d29d28f2Storek 	if (p->port == AUDIO_SPEAKER) {
1079d29d28f2Storek 		sc->sc_map.mr_mmr2 |= AMD_MMR2_LS;
1080d29d28f2Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
1081d29d28f2Storek 	} else if (p->port == AUDIO_HEADPHONE) {
1082d29d28f2Storek 		sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS;
1083d29d28f2Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
1084d29d28f2Storek 	}
1085d29d28f2Storek 	/*
1086d29d28f2Storek 	 * The bsd driver does not distinguish between paused and active.
1087d29d28f2Storek 	 * (In the sun driver, not active means samples are not ouput
1088d29d28f2Storek 	 * at all, but paused means the last streams buffer is drained
1089d29d28f2Storek 	 * and then output stops.)  If either are 0, then when stop output.
1090d29d28f2Storek 	 * Otherwise, if either are non-zero, we resume.
1091d29d28f2Storek 	 */
1092d29d28f2Storek 	if (p->pause == 0 || p->active == 0)
1093d29d28f2Storek 		sc->sc_au.au_wb.cb_pause = 0;
1094d29d28f2Storek 	else if (p->pause != (u_char)~0 || p->active != (u_char)~0)
1095d29d28f2Storek 		sc->sc_au.au_wb.cb_pause = 1;
1096d29d28f2Storek 	if (r->pause == 0 || r->active == 0)
1097d29d28f2Storek 		sc->sc_au.au_rb.cb_pause = 0;
1098d29d28f2Storek 	else if (r->pause != (u_char)~0 || r->active != (u_char)~0)
1099d29d28f2Storek 		sc->sc_au.au_rb.cb_pause = 1;
1100d29d28f2Storek 
1101d29d28f2Storek 	return (0);
1102d29d28f2Storek }
1103d29d28f2Storek 
1104d29d28f2Storek static int
audiogetinfo(sc,ai)1105d29d28f2Storek audiogetinfo(sc, ai)
1106d29d28f2Storek 	struct audio_softc *sc;
1107d29d28f2Storek 	struct audio_info *ai;
1108d29d28f2Storek {
1109d29d28f2Storek 	struct audio_prinfo *r = &ai->record, *p = &ai->play;
1110d29d28f2Storek 
1111d29d28f2Storek 	p->sample_rate = r->sample_rate = 8000;
1112d29d28f2Storek 	p->channels = r->channels = 1;
1113d29d28f2Storek 	p->precision = r->precision = 8;
1114d29d28f2Storek 	p->encoding = r->encoding = AUDIO_ENCODING_ULAW;
1115d29d28f2Storek 
1116d29d28f2Storek 	ai->monitor_gain = sc->sc_mlevel;
1117d29d28f2Storek 	r->gain = sc->sc_rlevel;
1118d29d28f2Storek 	p->gain = sc->sc_plevel;
1119d29d28f2Storek 	r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ?
1120d29d28f2Storek 		AUDIO_SPEAKER : AUDIO_HEADPHONE;
1121d29d28f2Storek 
1122d29d28f2Storek 	p->pause = sc->sc_au.au_wb.cb_pause;
1123d29d28f2Storek 	r->pause = sc->sc_au.au_rb.cb_pause;
1124d29d28f2Storek 	p->error = sc->sc_au.au_wb.cb_drops != 0;
1125d29d28f2Storek 	r->error = sc->sc_au.au_rb.cb_drops != 0;
1126d29d28f2Storek 
1127d29d28f2Storek 	p->open = sc->sc_open;
1128d29d28f2Storek 	r->open = sc->sc_open;
1129d29d28f2Storek 
1130d29d28f2Storek 	p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops;
1131d29d28f2Storek 	r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops;
1132d29d28f2Storek 
1133d29d28f2Storek 	p->seek = sc->sc_wseek;
1134d29d28f2Storek 	r->seek = sc->sc_rseek;
1135d29d28f2Storek 
1136d29d28f2Storek 	ai->blocksize = sc->sc_au.au_blksize;
1137d29d28f2Storek 	ai->hiwat = sc->sc_au.au_hiwat;
1138d29d28f2Storek 	ai->lowat = sc->sc_au.au_lowat;
11391629ecceStorek 	ai->backlog = sc->sc_au.au_backlog;
1140d29d28f2Storek 
1141d29d28f2Storek 	return (0);
1142d29d28f2Storek }
1143d29d28f2Storek 
1144d29d28f2Storek static int
sunaudiogetinfo(sc,ai)1145d29d28f2Storek sunaudiogetinfo(sc, ai)
1146d29d28f2Storek 	struct audio_softc *sc;
1147d29d28f2Storek 	struct sun_audio_info *ai;
1148d29d28f2Storek {
1149d29d28f2Storek 	struct sun_audio_prinfo *r = &ai->record, *p = &ai->play;
1150d29d28f2Storek 
1151d29d28f2Storek 	p->sample_rate = r->sample_rate = 8000;
1152d29d28f2Storek 	p->channels = r->channels = 1;
1153d29d28f2Storek 	p->precision = r->precision = 8;
1154d29d28f2Storek 	p->encoding = r->encoding = AUDIO_ENCODING_ULAW;
1155d29d28f2Storek 
1156d29d28f2Storek 	ai->monitor_gain = sc->sc_mlevel;
1157d29d28f2Storek 	r->gain = sc->sc_rlevel;
1158d29d28f2Storek 	p->gain = sc->sc_plevel;
1159d29d28f2Storek 	r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ?
1160d29d28f2Storek 		AUDIO_SPEAKER : AUDIO_HEADPHONE;
1161d29d28f2Storek 
1162d29d28f2Storek 	p->active = p->pause = sc->sc_au.au_wb.cb_pause;
1163d29d28f2Storek 	r->active = r->pause = sc->sc_au.au_rb.cb_pause;
1164d29d28f2Storek 	p->error = sc->sc_au.au_wb.cb_drops != 0;
1165d29d28f2Storek 	r->error = sc->sc_au.au_rb.cb_drops != 0;
1166d29d28f2Storek 
1167d29d28f2Storek 	p->waiting = 0;
1168d29d28f2Storek 	r->waiting = 0;
1169d29d28f2Storek 	p->eof = 0;
1170d29d28f2Storek 	r->eof = 0;
1171d29d28f2Storek 
1172d29d28f2Storek 	p->open = sc->sc_open;
1173d29d28f2Storek 	r->open = sc->sc_open;
1174d29d28f2Storek 
1175d29d28f2Storek 	p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops;
1176d29d28f2Storek 	r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops;
1177d29d28f2Storek 
1178d29d28f2Storek 	return (0);
1179d29d28f2Storek }
1180d29d28f2Storek #endif
1181