xref: /original-bsd/sys/sparc/dev/bsd_audio.c (revision 9c96d15a)
1d29d28f2Storek /*
2d29d28f2Storek  * Copyright (c) 1991, 1992 The Regents of the University of California.
3d29d28f2Storek  * 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
12b248cd2bSbostic  *	California, Lawrence Berkeley Laboratories.
13b248cd2bSbostic  *
14d29d28f2Storek  * %sccs.include.redist.c%
15d29d28f2Storek  *
16*9c96d15aSbostic  *	@(#)bsd_audio.c	7.3 (Berkeley) 10/11/92
17d29d28f2Storek  *
18d29d28f2Storek  * from: $Header: bsd_audio.c,v 1.14 92/07/03 23:21:23 mccanne Exp $ (LBL)
19d29d28f2Storek  */
20d29d28f2Storek #include "bsdaudio.h"
21d29d28f2Storek #if NBSDAUDIO > 0
22d29d28f2Storek 
23*9c96d15aSbostic #include <sys/param.h>
24*9c96d15aSbostic #include <sys/systm.h>
25d29d28f2Storek 
26d29d28f2Storek #if BSD < 199103
27d29d28f2Storek #ifndef SUNOS
28d29d28f2Storek #define SUNOS
29d29d28f2Storek #endif
30d29d28f2Storek #endif
31d29d28f2Storek 
32*9c96d15aSbostic #include <sys/errno.h>
33*9c96d15aSbostic #include <sys/file.h>
34*9c96d15aSbostic #include <sys/proc.h>
35*9c96d15aSbostic #include <sys/user.h>
36*9c96d15aSbostic #include <sys/vnode.h>
37*9c96d15aSbostic #include <sys/ioctl.h>
38*9c96d15aSbostic #include <sys/time.h>
39d29d28f2Storek #ifndef SUNOS
40*9c96d15aSbostic #include <sys/tty.h>
41d29d28f2Storek #endif
42*9c96d15aSbostic #include <sys/uio.h>
43d29d28f2Storek 
44d29d28f2Storek #ifdef SUNOS
45d29d28f2Storek #include <sundev/mbvar.h>
46d29d28f2Storek #include <sun4c/intreg.h>
47d29d28f2Storek #else
48*9c96d15aSbostic #include <sys/device.h>
49*9c96d15aSbostic #include <machine/autoconf.h>
50d29d28f2Storek #endif
51*9c96d15aSbostic #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
58d29d28f2Storek #include "sbusdev/bsd_audioreg.h"
59d29d28f2Storek #include "sbusdev/bsd_audiovar.h"
60d29d28f2Storek #include "sbusdev/bsd_audioio.h"
61d29d28f2Storek struct selinfo {
62d29d28f2Storek 	struct proc *si_proc;
63d29d28f2Storek 	int si_coll;
64d29d28f2Storek };
65d29d28f2Storek #else
66*9c96d15aSbostic #include <sparc/dev/bsd_audioreg.h>
67*9c96d15aSbostic #include <sparc/dev/bsd_audiovar.h>
68*9c96d15aSbostic #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;
79d29d28f2Storek 
80d29d28f2Storek /*
81d29d28f2Storek  * Software state, per AMD79C30 audio chip.
82d29d28f2Storek  */
83d29d28f2Storek struct audio_softc {
84d29d28f2Storek #ifndef SUNOS
85d29d28f2Storek 	struct	device sc_dev;		/* base device */
86d29d28f2Storek 	struct	intrhand sc_hwih;	/* hardware interrupt vector */
87d29d28f2Storek 	struct	intrhand sc_swih;	/* software interrupt vector */
88d29d28f2Storek #endif
89d29d28f2Storek 	int	sc_interrupts;		/* number of interrupts taken */
90d29d28f2Storek 
91d29d28f2Storek 	int	sc_open;		/* single use device */
92d29d28f2Storek 	u_long	sc_wseek;		/* timestamp of last frame written */
93d29d28f2Storek 	u_long	sc_rseek;		/* timestamp of last frame read */
94d29d28f2Storek 	struct	mapreg sc_map;		/* current contents of map registers */
95d29d28f2Storek 	struct	selinfo sc_wsel;	/* write selector */
96d29d28f2Storek 	struct	selinfo sc_rsel;	/* read selector */
97d29d28f2Storek 	/*
98d29d28f2Storek 	 * keep track of levels so we don't have to convert back from
99d29d28f2Storek 	 * MAP gain constants
100d29d28f2Storek 	 */
101d29d28f2Storek 	int	sc_rlevel;		/* record level */
102d29d28f2Storek 	int	sc_plevel;		/* play level */
103d29d28f2Storek 	int	sc_mlevel;		/* monitor level */
104d29d28f2Storek 
105d29d28f2Storek 	/* sc_au is special in that the hardware interrupt handler uses it */
106d29d28f2Storek 	struct	auio sc_au;		/* recv and xmit buffers, etc */
107d29d28f2Storek 
108d29d28f2Storek };
109d29d28f2Storek 
110d29d28f2Storek /* interrupt interfaces */
111d29d28f2Storek #ifndef AUDIO_C_HANDLER
112d29d28f2Storek int	audiohwintr __P((void *));
113d29d28f2Storek #endif
114d29d28f2Storek int	audioswintr __P((void *));
115d29d28f2Storek 
116d29d28f2Storek /* forward declarations */
117d29d28f2Storek int	audio_sleep __P((struct aucb *, int));
118d29d28f2Storek void	audio_setmap __P((volatile struct amd7930 *, struct mapreg *));
119d29d28f2Storek 
120d29d28f2Storek static void init_amd();
121d29d28f2Storek 
122d29d28f2Storek #if !defined(AUDIO_C_HANDLER) || defined(SUNOS)
123d29d28f2Storek struct auio *audio_au;
124d29d28f2Storek extern void audio_trap();
125d29d28f2Storek #endif
126d29d28f2Storek 
127d29d28f2Storek #ifdef SUNOS
128d29d28f2Storek struct audio_softc audio_softc;
129d29d28f2Storek #define SOFTC(dev) &audio_softc
130d29d28f2Storek #define UIOMOVE(cp, len, code, uio) uiomove(cp, len, code, uio)
131d29d28f2Storek 
132d29d28f2Storek #define AUDIOOPEN(d, f, i, p)\
133d29d28f2Storek 	audioopen(d, f, i)\
134d29d28f2Storek 	dev_t d; int f, i;
135d29d28f2Storek #define AUDIOCLOSE(d, f, i, p)\
136d29d28f2Storek 	audioclose(d, f, i)\
137d29d28f2Storek 	dev_t d; int f, i;
138d29d28f2Storek #define AUDIOREAD(d, u, f) \
139d29d28f2Storek 	audioread(d, u) dev_t d; struct uio *u;
140d29d28f2Storek #define AUDIOWRITE(d, u, f) \
141d29d28f2Storek 	audiowrite(d, u) dev_t d; struct uio *u;
142d29d28f2Storek #define AUDIOIOCTL(d, c, a, f, o)\
143d29d28f2Storek 	audioioctl(d, c, a, f)\
144d29d28f2Storek 	dev_t d; int c; caddr_t a; int f;
145d29d28f2Storek #define AUDIOSELECT(d, r, p)\
146d29d28f2Storek 	audio_select(d, r, p)\
147d29d28f2Storek 	dev_t d; int r; struct proc *p;
148d29d28f2Storek 
149d29d28f2Storek 
150d29d28f2Storek #define AUDIO_SET_SWINTR set_intreg(IR_SOFT_INT4, 1)
151d29d28f2Storek 
152d29d28f2Storek int
153d29d28f2Storek audioselect(dev, rw)
154d29d28f2Storek 	register dev_t dev;
155d29d28f2Storek 	int rw;
156d29d28f2Storek {
157d29d28f2Storek 	return (audio_select(dev, rw, u.u_procp));
158d29d28f2Storek }
159d29d28f2Storek 
160d29d28f2Storek static void
161d29d28f2Storek selrecord(p, si)
162d29d28f2Storek 	struct proc *p;
163d29d28f2Storek 	struct selinfo *si;
164d29d28f2Storek {
165d29d28f2Storek 	if (si->si_proc != 0)
166d29d28f2Storek 		si->si_coll = 1;
167d29d28f2Storek 	else
168d29d28f2Storek 		si->si_proc = p;
169d29d28f2Storek }
170d29d28f2Storek #define SELWAKEUP(si) \
171d29d28f2Storek {\
172d29d28f2Storek 	 if ((si)->si_proc != 0) {\
173d29d28f2Storek 		selwakeup((si)->si_proc, (si)->si_coll); \
174d29d28f2Storek 		(si)->si_proc = 0;\
175d29d28f2Storek 		(si)->si_coll = 0;\
176d29d28f2Storek 	}\
177d29d28f2Storek }
178d29d28f2Storek 
179d29d28f2Storek 
180d29d28f2Storek static int audioattach();
181d29d28f2Storek static int audioidentify();
182d29d28f2Storek 
183d29d28f2Storek struct dev_ops bsdaudio_ops = {
184d29d28f2Storek 	0,
185d29d28f2Storek 	audioidentify,
186d29d28f2Storek 	audioattach,
187d29d28f2Storek };
188d29d28f2Storek 
189d29d28f2Storek static int
190d29d28f2Storek audioidentify(cp)
191d29d28f2Storek 	char *cp;
192d29d28f2Storek {
193d29d28f2Storek 	return (strcmp(cp, "audio") == 0);
194d29d28f2Storek }
195d29d28f2Storek 
196d29d28f2Storek static int
197d29d28f2Storek audioattach(dev)
198d29d28f2Storek 	struct dev_info *dev;
199d29d28f2Storek {
200d29d28f2Storek 	register struct audio_softc *sc;
201d29d28f2Storek 	register volatile struct amd7930 *amd;
202d29d28f2Storek 	struct dev_reg *reg;
203d29d28f2Storek 
204d29d28f2Storek 	sc = &audio_softc;
205d29d28f2Storek 	if (dev->devi_nreg != 1 || dev->devi_nintr != 1) {
206d29d28f2Storek 		printf("audio: bad config\n");
207d29d28f2Storek                 return (-1);
208d29d28f2Storek         }
209d29d28f2Storek 	reg = dev->devi_reg;
210d29d28f2Storek 	amd = (struct amd7930 *)map_regs(reg->reg_addr, reg->reg_size,
211d29d28f2Storek 					 reg->reg_bustype);
212d29d28f2Storek 	sc->sc_au.au_amd = amd;
213d29d28f2Storek 	init_amd(amd);
214d29d28f2Storek 
215d29d28f2Storek 	audio_au = &sc->sc_au;
216d29d28f2Storek #ifndef AUDIO_C_HANDLER
217d29d28f2Storek 	settrap(dev->devi_intr->int_pri, audio_trap);
218d29d28f2Storek #else
219d29d28f2Storek 	/* XXX */
220d29d28f2Storek 	addintr(dev->devi_intr->int_pri, audiohwintr, dev->devi_name,
221d29d28f2Storek 		dev->devi_unit);
222d29d28f2Storek #endif
223d29d28f2Storek 	addintr(4, audioswintr, dev->devi_name, dev->devi_unit);
224d29d28f2Storek 	report_dev(dev);
225d29d28f2Storek 
226d29d28f2Storek 	return (0);
227d29d28f2Storek }
228d29d28f2Storek #else
229d29d28f2Storek #define AUDIOOPEN(d, f, i, p) audioopen(dev_t d, int f, int i, struct proc *p)
230d29d28f2Storek #define AUDIOCLOSE(d, f, i, p) audioclose(dev_t d, int f, int i, \
231d29d28f2Storek 					  struct proc *p)
232d29d28f2Storek #define AUDIOREAD(d, u, f) audioread(dev_t d, struct uio *u, int f)
233d29d28f2Storek #define AUDIOWRITE(d, u, f) audiowrite(dev_t d, struct uio *u, int f)
234d29d28f2Storek #define AUDIOIOCTL(d, c, a, f, o)\
235d29d28f2Storek 	audioioctl(dev_t dev, int c, caddr_t a, int f, struct proc *p)
236d29d28f2Storek #define AUDIOSELECT(d, r, p) audioselect(dev_t dev, int rw, struct proc *p)
237d29d28f2Storek #define SELWAKEUP selwakeup
238d29d28f2Storek 
239d29d28f2Storek #define AUDIO_SET_SWINTR ienab_bis(IE_L6)
240d29d28f2Storek 
241d29d28f2Storek /* autoconfiguration driver */
242d29d28f2Storek void	audioattach(struct device *, struct device *, void *);
243d29d28f2Storek struct	cfdriver audiocd =
244d29d28f2Storek     { NULL, "audio", matchbyname, audioattach,
245d29d28f2Storek       DV_DULL, sizeof(struct audio_softc) };
246d29d28f2Storek #define SOFTC(dev) audiocd.cd_devs[minor(dev)]
247d29d28f2Storek #define UIOMOVE(cp, len, code, uio) uiomove(cp, len, uio)
248d29d28f2Storek 
249d29d28f2Storek /*
250d29d28f2Storek  * Audio chip found.
251d29d28f2Storek  */
252d29d28f2Storek void
253d29d28f2Storek audioattach(parent, self, args)
254d29d28f2Storek 	struct device *parent, *self;
255d29d28f2Storek 	void *args;
256d29d28f2Storek {
257d29d28f2Storek 	register struct audio_softc *sc = (struct audio_softc *)self;
258d29d28f2Storek 	register struct romaux *ra = args;
259d29d28f2Storek 	register volatile struct amd7930 *amd;
260d29d28f2Storek 	register int pri;
261d29d28f2Storek 
262d29d28f2Storek 	if (ra->ra_nintr != 1) {
263d29d28f2Storek 		printf(": expected 1 interrupt, got %d\n", ra->ra_nintr);
264d29d28f2Storek 		return;
265d29d28f2Storek 	}
266d29d28f2Storek 	pri = ra->ra_intr[0].int_pri;
267d29d28f2Storek 	printf(" pri %d, softpri %d\n", pri, PIL_AUSOFT);
268d29d28f2Storek 	amd = (volatile struct amd7930 *)(ra->ra_vaddr ?
269d29d28f2Storek 	    ra->ra_vaddr : mapiodev(ra->ra_paddr, sizeof *amd));
270d29d28f2Storek 	sc->sc_au.au_amd = amd;
271d29d28f2Storek 
272d29d28f2Storek 	init_amd(amd);
273d29d28f2Storek 
274d29d28f2Storek #ifndef AUDIO_C_HANDLER
275d29d28f2Storek 	audio_au = &sc->sc_au;
276d29d28f2Storek 	intr_fasttrap(pri, audio_trap);
277d29d28f2Storek #else
278d29d28f2Storek 	sc->sc_hwih.ih_fun = audiohwintr;
279d29d28f2Storek 	sc->sc_hwih.ih_arg = &sc->sc_au;
280d29d28f2Storek 	intr_establish(pri, &sc->sc_hwih);
281d29d28f2Storek #endif
282d29d28f2Storek 	sc->sc_swih.ih_fun = audioswintr;
283d29d28f2Storek 	sc->sc_swih.ih_arg = sc;
284d29d28f2Storek 	intr_establish(PIL_AUSOFT, &sc->sc_swih);
285d29d28f2Storek }
286d29d28f2Storek #endif
287d29d28f2Storek 
288d29d28f2Storek static void
289d29d28f2Storek init_amd(amd)
290d29d28f2Storek 	register volatile struct amd7930 *amd;
291d29d28f2Storek {
292d29d28f2Storek 	/* disable interrupts */
293d29d28f2Storek 	amd->cr = AMDR_INIT;
294d29d28f2Storek 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
295d29d28f2Storek 
296d29d28f2Storek 	/*
297d29d28f2Storek 	 * Initialize the mux unit.  We use MCR3 to route audio (MAP)
298d29d28f2Storek 	 * through channel Bb.  MCR1 and MCR2 are unused.
299d29d28f2Storek 	 * Setting the INT enable bit in MCR4 will generate an interrupt
300d29d28f2Storek 	 * on each converted audio sample.
301d29d28f2Storek 	 */
302d29d28f2Storek 	amd->cr = AMDR_MUX_1_4;
303d29d28f2Storek  	amd->dr = 0;
304d29d28f2Storek 	amd->dr = 0;
305d29d28f2Storek 	amd->dr = (AMD_MCRCHAN_BB << 4) | AMD_MCRCHAN_BA;
306d29d28f2Storek 	amd->dr = AMD_MCR4_INT_ENABLE;
307d29d28f2Storek }
308d29d28f2Storek 
309d29d28f2Storek static int audio_default_level = 150;
310d29d28f2Storek static void ausetrgain __P((struct audio_softc *, int));
311d29d28f2Storek static void ausetpgain __P((struct audio_softc *, int));
312d29d28f2Storek static int audiosetinfo __P((struct audio_softc *, struct audio_info *));
313d29d28f2Storek static int audiogetinfo __P((struct audio_softc *, struct audio_info *));
314d29d28f2Storek struct sun_audio_info;
315d29d28f2Storek static int sunaudiosetinfo __P((struct audio_softc *,
316d29d28f2Storek 				struct sun_audio_info *));
317d29d28f2Storek static int sunaudiogetinfo __P((struct audio_softc *,
318d29d28f2Storek 				struct sun_audio_info *));
319d29d28f2Storek static void audio_setmmr2 __P((volatile struct amd7930 *, int));
320d29d28f2Storek 
321d29d28f2Storek int
322d29d28f2Storek AUDIOOPEN(dev, flags, ifmt, p)
323d29d28f2Storek {
324d29d28f2Storek 	register struct audio_softc *sc;
325d29d28f2Storek 	register volatile struct amd7930 *amd;
326d29d28f2Storek 	int unit = minor(dev), error, s;
327d29d28f2Storek 
328d29d28f2Storek #ifdef SUNOS
329d29d28f2Storek 	if (unit > 0)
330d29d28f2Storek 		return (ENXIO);
331d29d28f2Storek 	sc = &audio_softc;
332d29d28f2Storek #else
333d29d28f2Storek 	if (unit >= audiocd.cd_ndevs || (sc = audiocd.cd_devs[unit]) == NULL)
334d29d28f2Storek 		return (ENXIO);
335d29d28f2Storek #endif
336d29d28f2Storek 	if (sc->sc_open)
337d29d28f2Storek 		return (EBUSY);
338d29d28f2Storek 	sc->sc_open = 1;
339d29d28f2Storek 
340d29d28f2Storek 	sc->sc_au.au_lowat = audio_blocksize;
341d29d28f2Storek 	sc->sc_au.au_hiwat = AUCB_SIZE - sc->sc_au.au_lowat;
342d29d28f2Storek 	sc->sc_au.au_blksize = audio_blocksize;
343d29d28f2Storek 
344d29d28f2Storek 	/* set up read and write blocks and `dead sound' zero value. */
345d29d28f2Storek 	AUCB_INIT(&sc->sc_au.au_rb);
346d29d28f2Storek 	sc->sc_au.au_rb.cb_thresh = AUCB_SIZE;
347d29d28f2Storek 	AUCB_INIT(&sc->sc_au.au_wb);
348d29d28f2Storek 	sc->sc_au.au_wb.cb_thresh = -1;
349d29d28f2Storek 
350d29d28f2Storek 	/* nothing read or written yet */
351d29d28f2Storek 	sc->sc_rseek = 0;
352d29d28f2Storek 	sc->sc_wseek = 0;
353d29d28f2Storek 
354d29d28f2Storek 	bzero((char *)&sc->sc_map, sizeof sc->sc_map);
355d29d28f2Storek 	/* default to speaker */
356d29d28f2Storek 	sc->sc_map.mr_mmr2 = AMD_MMR2_AINB | AMD_MMR2_LS;
357d29d28f2Storek 
358d29d28f2Storek 	/* enable interrupts and set parameters established above */
359d29d28f2Storek 	amd = sc->sc_au.au_amd;
360d29d28f2Storek 	audio_setmmr2(amd, sc->sc_map.mr_mmr2);
361d29d28f2Storek 	ausetrgain(sc, audio_default_level);
362d29d28f2Storek 	ausetpgain(sc, audio_default_level);
363d29d28f2Storek 	amd->cr = AMDR_INIT;
364d29d28f2Storek 	amd->dr = AMD_INIT_PMS_ACTIVE;
365d29d28f2Storek 
366d29d28f2Storek 	return (0);
367d29d28f2Storek }
368d29d28f2Storek 
369d29d28f2Storek static int
370d29d28f2Storek audio_drain(sc)
371d29d28f2Storek 	register struct audio_softc *sc;
372d29d28f2Storek {
373d29d28f2Storek 	register int error;
374d29d28f2Storek 
375d29d28f2Storek 	while (!AUCB_EMPTY(&sc->sc_au.au_wb))
376d29d28f2Storek 		if ((error = audio_sleep(&sc->sc_au.au_wb, 0)) != 0)
377d29d28f2Storek 			return (error);
378d29d28f2Storek 	return (0);
379d29d28f2Storek }
380d29d28f2Storek 
381d29d28f2Storek /*
382d29d28f2Storek  * Close an audio chip.
383d29d28f2Storek  */
384d29d28f2Storek /* ARGSUSED */
385d29d28f2Storek int
386d29d28f2Storek AUDIOCLOSE(dev, flags, ifmt, p)
387d29d28f2Storek {
388d29d28f2Storek 	register struct audio_softc *sc = SOFTC(dev);
389d29d28f2Storek 	register volatile struct amd7930 *amd;
390d29d28f2Storek 	register struct aucb *cb;
391d29d28f2Storek 	register int s;
392d29d28f2Storek 
393d29d28f2Storek 	/*
394d29d28f2Storek 	 * Block until output drains, but allow ^C interrupt.
395d29d28f2Storek 	 */
396d29d28f2Storek 	sc->sc_au.au_lowat = 0;	/* avoid excessive wakeups */
397d29d28f2Storek 	s = splaudio();
398d29d28f2Storek 	/*
399d29d28f2Storek 	 * If there is pending output, let it drain (unless
400d29d28f2Storek 	 * the output is paused).
401d29d28f2Storek 	 */
402d29d28f2Storek 	cb = &sc->sc_au.au_wb;
403d29d28f2Storek 	if (!AUCB_EMPTY(cb) && !cb->cb_pause)
404d29d28f2Storek 		(void)audio_drain(sc);
405d29d28f2Storek 	/*
406d29d28f2Storek 	 * Disable interrupts, clear open flag, and done.
407d29d28f2Storek 	 */
408d29d28f2Storek 	amd = sc->sc_au.au_amd;
409d29d28f2Storek 	amd->cr = AMDR_INIT;
410d29d28f2Storek 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
411d29d28f2Storek 	splx(s);
412d29d28f2Storek 	sc->sc_open = 0;
413d29d28f2Storek 	return (0);
414d29d28f2Storek }
415d29d28f2Storek 
416d29d28f2Storek int
417d29d28f2Storek audio_sleep(cb, thresh)
418d29d28f2Storek 	register struct aucb *cb;
419d29d28f2Storek 	register int thresh;
420d29d28f2Storek {
421d29d28f2Storek 	register int error;
422d29d28f2Storek 
423d29d28f2Storek 	cb->cb_thresh = thresh;
424d29d28f2Storek 	error = tsleep((caddr_t)cb, (PZERO + 1) | PCATCH, "audio", 0);
425d29d28f2Storek 	return (error);
426d29d28f2Storek }
427d29d28f2Storek 
428d29d28f2Storek int
429d29d28f2Storek AUDIOREAD(dev, uio, ioflag)
430d29d28f2Storek {
431d29d28f2Storek 	register struct audio_softc *sc = SOFTC(dev);
432d29d28f2Storek 	register struct aucb *cb;
433d29d28f2Storek 	register int s, n, head, taildata, error;
434d29d28f2Storek 	register int blocksize = sc->sc_au.au_blksize;
435d29d28f2Storek 
436d29d28f2Storek 	if (uio->uio_resid == 0)
437d29d28f2Storek 		return (0);
438d29d28f2Storek 	cb = &sc->sc_au.au_rb;
439d29d28f2Storek 	error = 0;
440d29d28f2Storek 	s = splaudio();
441d29d28f2Storek 	cb->cb_drops = 0;
442d29d28f2Storek 	sc->sc_rseek = sc->sc_au.au_stamp - AUCB_LEN(cb);
443d29d28f2Storek 	do {
444d29d28f2Storek 		while (AUCB_LEN(cb) < blocksize) {
445d29d28f2Storek #ifndef SUNOS
446d29d28f2Storek 			if (ioflag & IO_NDELAY) {
447d29d28f2Storek 				error = EWOULDBLOCK;
448d29d28f2Storek 				goto out;
449d29d28f2Storek 			}
450d29d28f2Storek #endif
451d29d28f2Storek 			if ((error = audio_sleep(cb, blocksize)) != 0)
452d29d28f2Storek 				goto out;
453d29d28f2Storek 		}
454d29d28f2Storek 		splx(s);
455d29d28f2Storek 		/*
456d29d28f2Storek 		 * The space calculation can only err on the short
457d29d28f2Storek 		 * side if an interrupt occurs during processing:
458d29d28f2Storek 		 * only cb_tail is altered in the interrupt code.
459d29d28f2Storek 		 */
460d29d28f2Storek 		head = cb->cb_head;
461d29d28f2Storek 		if ((n = AUCB_LEN(cb)) > uio->uio_resid)
462d29d28f2Storek 			n = uio->uio_resid;
463d29d28f2Storek 		taildata = AUCB_SIZE - head;
464d29d28f2Storek 		if (n > taildata) {
465d29d28f2Storek 			error = UIOMOVE((caddr_t)cb->cb_data + head,
466d29d28f2Storek 					taildata, UIO_READ, uio);
467d29d28f2Storek 			if (error == 0)
468d29d28f2Storek 				error = UIOMOVE((caddr_t)cb->cb_data,
469d29d28f2Storek 						n - taildata, UIO_READ, uio);
470d29d28f2Storek 		} else
471d29d28f2Storek 			error = UIOMOVE((caddr_t)cb->cb_data + head, n,
472d29d28f2Storek 					UIO_READ, uio);
473d29d28f2Storek 		if (error)
474d29d28f2Storek 			return (error);
475d29d28f2Storek 		head = AUCB_MOD(head + n);
476d29d28f2Storek 		(void) splaudio();
477d29d28f2Storek 		cb->cb_head = head;
478d29d28f2Storek 	} while (uio->uio_resid >= blocksize);
479d29d28f2Storek out:
480d29d28f2Storek 	splx(s);
481d29d28f2Storek 	return (error);
482d29d28f2Storek }
483d29d28f2Storek 
484d29d28f2Storek int
485d29d28f2Storek AUDIOWRITE(dev, uio, ioflag)
486d29d28f2Storek {
487d29d28f2Storek 	register struct audio_softc *sc = SOFTC(dev);
488d29d28f2Storek 	register struct aucb *cb = &sc->sc_au.au_wb;
489d29d28f2Storek 	register int s, n, tail, tailspace, error, first, watermark, drops;
490d29d28f2Storek 
491d29d28f2Storek 	error = 0;
492d29d28f2Storek 	first = 1;
493d29d28f2Storek 	s = splaudio();
494d29d28f2Storek 	while (uio->uio_resid > 0) {
495d29d28f2Storek 		watermark = sc->sc_au.au_hiwat;
496d29d28f2Storek 		while (AUCB_LEN(cb) > watermark) {
497d29d28f2Storek #ifndef SUNOS
498d29d28f2Storek 			if (ioflag & IO_NDELAY) {
499d29d28f2Storek 				error = EWOULDBLOCK;
500d29d28f2Storek 				goto out;
501d29d28f2Storek 			}
502d29d28f2Storek #endif
503d29d28f2Storek 			if ((error = audio_sleep(cb, watermark)) != 0)
504d29d28f2Storek 				goto out;
505d29d28f2Storek 			watermark = sc->sc_au.au_lowat;
506d29d28f2Storek 		}
507d29d28f2Storek 		splx(s);
508d29d28f2Storek 		/*
509d29d28f2Storek 		 * The only value that can change on an interrupt is
510d29d28f2Storek 		 * cb->cb_head.  We only pull that out once to decide
511d29d28f2Storek 		 * how much to write into cb_data; if we lose a race
512d29d28f2Storek 		 * and cb_head changes, we will merely be overly
513d29d28f2Storek 		 * conservative.  For a legitimate time stamp,
514d29d28f2Storek 		 * however, we need to synchronize the accesses to
515d29d28f2Storek 		 * au_stamp and cb_head at a high ipl below.
516d29d28f2Storek 		 */
517d29d28f2Storek 		if ((n = AUCB_SIZE - AUCB_LEN(cb) - 1) > uio->uio_resid)
518d29d28f2Storek 			n = uio->uio_resid;
519d29d28f2Storek 		tail = cb->cb_tail;
520d29d28f2Storek 		tailspace = AUCB_SIZE - tail;
521d29d28f2Storek 		if (n > tailspace) {
522d29d28f2Storek 			/* write first part at tail and rest at head */
523d29d28f2Storek 			error = UIOMOVE((caddr_t)cb->cb_data + tail,
524d29d28f2Storek 					tailspace, UIO_WRITE, uio);
525d29d28f2Storek 			if (error == 0)
526d29d28f2Storek 				error = UIOMOVE((caddr_t)cb->cb_data,
527d29d28f2Storek 						n - tailspace, UIO_WRITE, uio);
528d29d28f2Storek 		} else
529d29d28f2Storek 			error = UIOMOVE((caddr_t)cb->cb_data + tail, n,
530d29d28f2Storek 					UIO_WRITE, uio);
531d29d28f2Storek 		if (error)
532d29d28f2Storek 			return (error);
533d29d28f2Storek 		/*
534d29d28f2Storek 		 * We cannot do this outside the loop because if the
535d29d28f2Storek 		 * buffer is empty, an indeterminate amount of time
536d29d28f2Storek 		 * will pass before the output starts to drain.
537d29d28f2Storek 		 */
538d29d28f2Storek 		(void)splaudio();
539d29d28f2Storek 		tail = AUCB_MOD(tail + n);
540d29d28f2Storek 		if (first) {
541d29d28f2Storek 			first = 0;
542d29d28f2Storek 			sc->sc_wseek = sc->sc_au.au_stamp + AUCB_LEN(cb) + 1;
543d29d28f2Storek 			/*
544d29d28f2Storek 			 * To guarantee that a write is contiguous in the
545d29d28f2Storek 			 * sample space, we clear the drop count the first
546d29d28f2Storek 			 * time through.  If we later get drops, we will
547d29d28f2Storek 			 * break out of the loop below, before writing
548d29d28f2Storek 			 * a new frame.
549d29d28f2Storek 			 * XXX I think we're one iteration too late!
550d29d28f2Storek 			 */
551d29d28f2Storek 			cb->cb_drops = 0;
552d29d28f2Storek 		}
553d29d28f2Storek 		cb->cb_tail = tail;
554d29d28f2Storek 		if (cb->cb_drops != 0)
555d29d28f2Storek 			break;
556d29d28f2Storek 	}
557d29d28f2Storek out:
558d29d28f2Storek 	splx(s);
559d29d28f2Storek 	return (error);
560d29d28f2Storek }
561d29d28f2Storek 
562d29d28f2Storek /* Sun audio compatibility */
563d29d28f2Storek struct sun_audio_prinfo {
564d29d28f2Storek 	u_int	sample_rate;
565d29d28f2Storek 	u_int	channels;
566d29d28f2Storek 	u_int	precision;
567d29d28f2Storek 	u_int	encoding;
568d29d28f2Storek 	u_int	gain;
569d29d28f2Storek 	u_int	port;
570d29d28f2Storek 	u_int	reserved0[4];
571d29d28f2Storek 	u_int	samples;
572d29d28f2Storek 	u_int	eof;
573d29d28f2Storek 	u_char	pause;
574d29d28f2Storek 	u_char	error;
575d29d28f2Storek 	u_char	waiting;
576d29d28f2Storek 	u_char	reserved1[3];
577d29d28f2Storek 	u_char	open;
578d29d28f2Storek 	u_char	active;
579d29d28f2Storek };
580d29d28f2Storek struct sun_audio_info {
581d29d28f2Storek 	struct sun_audio_prinfo play;
582d29d28f2Storek 	struct sun_audio_prinfo record;
583d29d28f2Storek 	u_int monitor_gain;
584d29d28f2Storek 	u_int reserved[4];
585d29d28f2Storek };
586d29d28f2Storek 
587d29d28f2Storek #ifndef SUNOS
588d29d28f2Storek #define SUNAUDIO_GETINFO	_IOR('A', 1, struct sun_audio_info)
589d29d28f2Storek #define SUNAUDIO_SETINFO	_IOWR('A', 2, struct sun_audio_info)
590d29d28f2Storek #else
591d29d28f2Storek #define SUNAUDIO_GETINFO	_IOR(A, 1, struct sun_audio_info)
592d29d28f2Storek #define SUNAUDIO_SETINFO	_IOWR(A, 2, struct sun_audio_info)
593d29d28f2Storek #endif
594d29d28f2Storek 
595d29d28f2Storek int
596d29d28f2Storek AUDIOIOCTL(dev, cmd, addr, flag, p)
597d29d28f2Storek {
598d29d28f2Storek 	register struct audio_softc *sc = SOFTC(dev);
599d29d28f2Storek 	int error = 0, i, s;
600d29d28f2Storek 
601d29d28f2Storek 	switch (cmd) {
602d29d28f2Storek 
603d29d28f2Storek 	case AUDIO_GETMAP:
604d29d28f2Storek 		bcopy((caddr_t)&sc->sc_map, addr, sizeof(sc->sc_map));
605d29d28f2Storek 		break;
606d29d28f2Storek 
607d29d28f2Storek 	case AUDIO_SETMAP:
608d29d28f2Storek 		bcopy(addr, (caddr_t)&sc->sc_map, sizeof(sc->sc_map));
609d29d28f2Storek 		sc->sc_map.mr_mmr2 &= 0x7f;
610d29d28f2Storek 		audio_setmap(sc->sc_au.au_amd, &sc->sc_map);
611d29d28f2Storek 		break;
612d29d28f2Storek 
613d29d28f2Storek 	case AUDIO_FLUSH:
614d29d28f2Storek 		s = splaudio();
615d29d28f2Storek 		AUCB_INIT(&sc->sc_au.au_rb);
616d29d28f2Storek 		AUCB_INIT(&sc->sc_au.au_wb);
617d29d28f2Storek 		splx(s);
618d29d28f2Storek 		sc->sc_wseek = 0;
619d29d28f2Storek 		sc->sc_rseek = 0;
620d29d28f2Storek 		break;
621d29d28f2Storek 
622d29d28f2Storek 	/*
623d29d28f2Storek 	 * Number of read samples dropped.  We don't know where or
624d29d28f2Storek 	 * when they were dropped.
625d29d28f2Storek 	 */
626d29d28f2Storek 	case AUDIO_RERROR:
627d29d28f2Storek 		*(int *)addr = sc->sc_au.au_rb.cb_drops != 0;
628d29d28f2Storek 		break;
629d29d28f2Storek 
630d29d28f2Storek 	/*
631d29d28f2Storek 	 * Timestamp of last frame written.
632d29d28f2Storek 	 */
633d29d28f2Storek 	case AUDIO_WSEEK:
634d29d28f2Storek 		*(u_long *)addr = sc->sc_wseek;
635d29d28f2Storek 		break;
636d29d28f2Storek 
637d29d28f2Storek 	case AUDIO_SETINFO:
638d29d28f2Storek 		error = audiosetinfo(sc, (struct audio_info *)addr);
639d29d28f2Storek 		break;
640d29d28f2Storek 
641d29d28f2Storek 	case AUDIO_GETINFO:
642d29d28f2Storek 		error = audiogetinfo(sc, (struct audio_info *)addr);
643d29d28f2Storek 		break;
644d29d28f2Storek 
645d29d28f2Storek 	case SUNAUDIO_GETINFO:
646d29d28f2Storek 		error = sunaudiogetinfo(sc, (struct sun_audio_info *)addr);
647d29d28f2Storek 		break;
648d29d28f2Storek 
649d29d28f2Storek 	case SUNAUDIO_SETINFO:
650d29d28f2Storek 		error = sunaudiosetinfo(sc, (struct sun_audio_info *)addr);
651d29d28f2Storek 		break;
652d29d28f2Storek 
653d29d28f2Storek 	case AUDIO_DRAIN:
654d29d28f2Storek 		s = splaudio();
655d29d28f2Storek 		error = audio_drain(sc);
656d29d28f2Storek 		splx(s);
657d29d28f2Storek 		break;
658d29d28f2Storek 
659d29d28f2Storek 	default:
660d29d28f2Storek 		error = EINVAL;
661d29d28f2Storek 		break;
662d29d28f2Storek 	}
663d29d28f2Storek 	return (error);
664d29d28f2Storek }
665d29d28f2Storek 
666d29d28f2Storek int
667d29d28f2Storek AUDIOSELECT(dev, rw, p)
668d29d28f2Storek {
669d29d28f2Storek 	register struct audio_softc *sc = SOFTC(dev);
670d29d28f2Storek 	register struct aucb *cb;
671d29d28f2Storek 	register int s = splaudio();
672d29d28f2Storek 
673d29d28f2Storek 	switch (rw) {
674d29d28f2Storek 
675d29d28f2Storek 	case FREAD:
676d29d28f2Storek 		cb = &sc->sc_au.au_rb;
677d29d28f2Storek 		if (AUCB_LEN(cb) >= sc->sc_au.au_blksize) {
678d29d28f2Storek 			splx(s);
679d29d28f2Storek 			return (1);
680d29d28f2Storek 		}
681d29d28f2Storek 		selrecord(p, &sc->sc_rsel);
682d29d28f2Storek 		cb->cb_thresh = sc->sc_au.au_blksize;
683d29d28f2Storek 		break;
684d29d28f2Storek 
685d29d28f2Storek 	case FWRITE:
686d29d28f2Storek 		cb = &sc->sc_au.au_wb;
687d29d28f2Storek 		if (AUCB_LEN(cb) <= sc->sc_au.au_lowat) {
688d29d28f2Storek 			splx(s);
689d29d28f2Storek 			return (1);
690d29d28f2Storek 		}
691d29d28f2Storek 		selrecord(p, &sc->sc_wsel);
692d29d28f2Storek 		cb->cb_thresh = sc->sc_au.au_lowat;
693d29d28f2Storek 		break;
694d29d28f2Storek 	}
695d29d28f2Storek 	splx(s);
696d29d28f2Storek 	return (0);
697d29d28f2Storek }
698d29d28f2Storek 
699d29d28f2Storek #ifdef AUDIO_C_HANDLER
700d29d28f2Storek int
701d29d28f2Storek audiohwintr(au0)
702d29d28f2Storek 	void *au0;
703d29d28f2Storek {
704d29d28f2Storek #ifdef SUNOS
705d29d28f2Storek 	register struct auio *au = audio_au;
706d29d28f2Storek #else
707d29d28f2Storek 	register struct auio *au = au0;
708d29d28f2Storek #endif
709d29d28f2Storek 	register volatile struct amd7930 *amd = au->au_amd;
710d29d28f2Storek 	register struct aucb *cb;
711d29d28f2Storek 	register int h, t, k;
712d29d28f2Storek 
713d29d28f2Storek 	k = amd->ir;		/* clear interrupt */
714d29d28f2Storek 	++au->au_stamp;
715d29d28f2Storek 
716d29d28f2Storek 	/* receive incoming data */
717d29d28f2Storek 	cb = &au->au_rb;
718d29d28f2Storek 	h = cb->cb_head;
719d29d28f2Storek 	t = cb->cb_tail;
720d29d28f2Storek 	k = AUCB_MOD(t + 1);
721d29d28f2Storek 	if (h == k)
722d29d28f2Storek 		cb->cb_drops++;
723d29d28f2Storek 	else if  (cb->cb_pause != 0)
724d29d28f2Storek 		cb->cb_pdrops++;
725d29d28f2Storek 	else {
726d29d28f2Storek 		cb->cb_data[t] = amd->bbrb;
727d29d28f2Storek 		cb->cb_tail = t = k;
728d29d28f2Storek 	}
729d29d28f2Storek 	if (AUCB_MOD(t - h) >= cb->cb_thresh) {
730d29d28f2Storek 		cb->cb_thresh = AUCB_SIZE;
731d29d28f2Storek 		cb->cb_waking = 1;
732d29d28f2Storek 		AUDIO_SET_SWINTR;
733d29d28f2Storek 	}
734d29d28f2Storek 	/* send outgoing data */
735d29d28f2Storek 	cb = &au->au_wb;
736d29d28f2Storek 	h = cb->cb_head;
737d29d28f2Storek 	t = cb->cb_tail;
738d29d28f2Storek 	if (h == t)
739d29d28f2Storek 		cb->cb_drops++;
740d29d28f2Storek 	else if (cb->cb_pause != 0)
741d29d28f2Storek 		cb->cb_pdrops++;
742d29d28f2Storek 	else {
743d29d28f2Storek 		cb->cb_head = h = AUCB_MOD(h + 1);
744d29d28f2Storek 		amd->bbtb = cb->cb_data[h];
745d29d28f2Storek 	}
746d29d28f2Storek 	if (AUCB_MOD(t - h) <= cb->cb_thresh) {
747d29d28f2Storek 		cb->cb_thresh = -1;
748d29d28f2Storek 		cb->cb_waking = 1;
749d29d28f2Storek 		AUDIO_SET_SWINTR;
750d29d28f2Storek 	}
751d29d28f2Storek 	return (1);
752d29d28f2Storek }
753d29d28f2Storek #endif
754d29d28f2Storek 
755d29d28f2Storek int
756d29d28f2Storek audioswintr(sc0)
757d29d28f2Storek 	void *sc0;
758d29d28f2Storek {
759d29d28f2Storek 	register struct audio_softc *sc;
760d29d28f2Storek 	register int s, ret = 0;
761d29d28f2Storek #ifdef SUNOS
762d29d28f2Storek 	sc = &audio_softc;
763d29d28f2Storek #else
764d29d28f2Storek 	sc = sc0;
765d29d28f2Storek #endif
766d29d28f2Storek 	s = splaudio();
767d29d28f2Storek 	if (sc->sc_au.au_rb.cb_waking != 0) {
768d29d28f2Storek 		sc->sc_au.au_rb.cb_waking = 0;
769d29d28f2Storek 		splx(s);
770d29d28f2Storek 		ret = 1;
771d29d28f2Storek 		wakeup((caddr_t)&sc->sc_au.au_rb);
772d29d28f2Storek 		SELWAKEUP(&sc->sc_rsel);
773d29d28f2Storek 		(void) splaudio();
774d29d28f2Storek 	}
775d29d28f2Storek 	if (sc->sc_au.au_wb.cb_waking != 0) {
776d29d28f2Storek 		sc->sc_au.au_wb.cb_waking = 0;
777d29d28f2Storek 		splx(s);
778d29d28f2Storek 		ret = 1;
779d29d28f2Storek 		wakeup((caddr_t)&sc->sc_au.au_wb);
780d29d28f2Storek 		SELWAKEUP(&sc->sc_wsel);
781d29d28f2Storek 	} else
782d29d28f2Storek 		splx(s);
783d29d28f2Storek 	return (ret);
784d29d28f2Storek }
785d29d28f2Storek 
786d29d28f2Storek /* Write 16 bits of data from variable v to the data port of the audio chip */
787d29d28f2Storek 
788d29d28f2Storek #define	WAMD16(amd, v) ((amd)->dr = v, (amd)->dr = v >> 8)
789d29d28f2Storek 
790d29d28f2Storek void
791d29d28f2Storek audio_setmap(amd, map)
792d29d28f2Storek 	register volatile struct amd7930 *amd;
793d29d28f2Storek 	register struct mapreg *map;
794d29d28f2Storek {
795d29d28f2Storek 	register int i, s, v;
796d29d28f2Storek 
797d29d28f2Storek 	s = splaudio();
798d29d28f2Storek 	amd->cr = AMDR_MAP_1_10;
799d29d28f2Storek 	for (i = 0; i < 8; i++) {
800d29d28f2Storek 		v = map->mr_x[i];
801d29d28f2Storek 		WAMD16(amd, v);
802d29d28f2Storek 	}
803d29d28f2Storek 	for (i = 0; i < 8; ++i) {
804d29d28f2Storek 		v = map->mr_r[i];
805d29d28f2Storek 		WAMD16(amd, v);
806d29d28f2Storek 	}
807d29d28f2Storek 	v = map->mr_gx; WAMD16(amd, v);
808d29d28f2Storek 	v = map->mr_gr; WAMD16(amd, v);
809d29d28f2Storek 	v = map->mr_ger; WAMD16(amd, v);
810d29d28f2Storek 	v = map->mr_stgr; WAMD16(amd, v);
811d29d28f2Storek 	v = map->mr_ftgr; WAMD16(amd, v);
812d29d28f2Storek 	v = map->mr_atgr; WAMD16(amd, v);
813d29d28f2Storek 	amd->dr = map->mr_mmr1;
814d29d28f2Storek 	amd->dr = map->mr_mmr2;
815d29d28f2Storek 	splx(s);
816d29d28f2Storek }
817d29d28f2Storek 
818d29d28f2Storek /*
819d29d28f2Storek  * Set the mmr1 register and one other 16 bit register in the audio chip.
820d29d28f2Storek  * The other register is indicated by op and val.
821d29d28f2Storek  */
822d29d28f2Storek void
823d29d28f2Storek audio_setmmr1(amd, mmr1, op, val)
824d29d28f2Storek 	register volatile struct amd7930 *amd;
825d29d28f2Storek 	register int mmr1;
826d29d28f2Storek 	register int op;
827d29d28f2Storek 	register int val;
828d29d28f2Storek {
829d29d28f2Storek 	register int s = splaudio();
830d29d28f2Storek 
831d29d28f2Storek 	amd->cr = AMDR_MAP_MMR1;
832d29d28f2Storek 	amd->dr = mmr1;
833d29d28f2Storek 	amd->cr = op;
834d29d28f2Storek 	WAMD16(amd, val);
835d29d28f2Storek 	splx(s);
836d29d28f2Storek }
837d29d28f2Storek 
838d29d28f2Storek /*
839d29d28f2Storek  * Set only the mmr1 regsiter, and one other.
840d29d28f2Storek  */
841d29d28f2Storek static void
842d29d28f2Storek audio_setmmr2(amd, mmr2)
843d29d28f2Storek 	register volatile struct amd7930 *amd;
844d29d28f2Storek 	register int mmr2;
845d29d28f2Storek {
846d29d28f2Storek 	register int s = splaudio();
847d29d28f2Storek 
848d29d28f2Storek 	amd->cr = AMDR_MAP_MMR2;
849d29d28f2Storek 	amd->dr = mmr2;
850d29d28f2Storek 	splx(s);
851d29d28f2Storek }
852d29d28f2Storek 
853d29d28f2Storek static u_short ger_coeff[] = {
854d29d28f2Storek 	0xaaaa, 0x9bbb, 0x79ac, 0x099a, 0x4199, 0x3199, 0x9cde, 0x9def,
855d29d28f2Storek 	0x749c, 0x549d, 0x6aae, 0xabcd, 0xabdf, 0x7429, 0x64ab, 0x6aff,
856d29d28f2Storek 	0x2abd, 0xbeef, 0x5cce, 0x75cd, 0x0099, 0x554c, 0x43dd, 0x33dd,
857d29d28f2Storek 	0x52ef, 0x771b, 0x5542, 0x41dd, 0x31dd, 0x441f, 0x431f, 0x331f,
858d29d28f2Storek 	0x40dd, 0x11dd, 0x440f, 0x411f, 0x311f, 0x5520, 0x10dd, 0x4211,
859d29d28f2Storek 	0x410f, 0x111f, 0x600b, 0x00dd, 0x4210, 0x400f, 0x110f, 0x2210,
860d29d28f2Storek 	0x7200, 0x4200, 0x2110, 0x100f, 0x2200, 0x1110, 0x000b, 0x2100,
861d29d28f2Storek 	0x000f,
862d29d28f2Storek #define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
863d29d28f2Storek };
864d29d28f2Storek 
865d29d28f2Storek static u_short gx_coeff[] = {
866d29d28f2Storek 	0x0808, 0x4cb2, 0x3dac, 0x2ae5, 0x2533, 0x2222, 0x2122, 0x1fd3,
867d29d28f2Storek 	0x12a2, 0x121b, 0x113b, 0x0bc3, 0x10f2, 0x03ba, 0x02ca, 0x021d,
868d29d28f2Storek 	0x015a, 0x0122, 0x0112, 0x00ec, 0x0032, 0x0021, 0x0013, 0x0011,
869d29d28f2Storek 	0x000e,
870d29d28f2Storek #define NGX (sizeof(gx_coeff) / sizeof(gx_coeff[0]))
871d29d28f2Storek };
872d29d28f2Storek 
873d29d28f2Storek static u_short stg_coeff[] = {
874d29d28f2Storek 	0x8b7c, 0x8b44, 0x8b35, 0x8b2a, 0x8b24, 0x8b22, 0x9123, 0x912e,
875d29d28f2Storek 	0x912a, 0x9132, 0x913b, 0x914b, 0x91f9, 0x91c5, 0x91b6, 0x9212,
876d29d28f2Storek 	0x91a4, 0x9222, 0x9232, 0x92fb, 0x92aa, 0x9327, 0x93b3, 0x94b3,
877d29d28f2Storek 	0x9f91, 0x9cea, 0x9bf9, 0x9aac, 0x9a4a, 0xa222, 0xa2a2, 0xa68d,
878d29d28f2Storek 	0xaaa3, 0xb242, 0xbb52, 0xcbb2, 0x0808,
879d29d28f2Storek #define NSTG (sizeof(stg_coeff) / sizeof(stg_coeff[0]))
880d29d28f2Storek };
881d29d28f2Storek 
882d29d28f2Storek static void
883d29d28f2Storek ausetrgain(sc, level)
884d29d28f2Storek 	register struct audio_softc *sc;
885d29d28f2Storek 	register int level;
886d29d28f2Storek {
887d29d28f2Storek 	level &= 0xff;
888d29d28f2Storek 	sc->sc_rlevel = level;
889d29d28f2Storek 	if (level != 0)
890d29d28f2Storek 		sc->sc_map.mr_mmr1 |= AMD_MMR1_GX;
891d29d28f2Storek 	else
892d29d28f2Storek 		sc->sc_map.mr_mmr1 &=~ AMD_MMR1_GX;
893d29d28f2Storek 
894d29d28f2Storek 	sc->sc_map.mr_gx = gx_coeff[(level * NGX) / 256];
895d29d28f2Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
896d29d28f2Storek 		      AMDR_MAP_GX, sc->sc_map.mr_gx);
897d29d28f2Storek }
898d29d28f2Storek 
899d29d28f2Storek static void
900d29d28f2Storek ausetpgain(sc, level)
901d29d28f2Storek 	register struct audio_softc *sc;
902d29d28f2Storek 	register int level;
903d29d28f2Storek {
904d29d28f2Storek 	level &= 0xff;
905d29d28f2Storek 	sc->sc_plevel = level;
906d29d28f2Storek 	if (level != 0)
907d29d28f2Storek 		sc->sc_map.mr_mmr1 |= AMD_MMR1_GER;
908d29d28f2Storek 	else
909d29d28f2Storek 		sc->sc_map.mr_mmr1 &=~ AMD_MMR1_GER;
910d29d28f2Storek 
911d29d28f2Storek 	sc->sc_map.mr_ger = ger_coeff[(level * NGER) / 256];
912d29d28f2Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
913d29d28f2Storek 		      AMDR_MAP_GER, sc->sc_map.mr_ger);
914d29d28f2Storek }
915d29d28f2Storek 
916d29d28f2Storek static void
917d29d28f2Storek ausetmgain(sc, level)
918d29d28f2Storek 	register struct audio_softc *sc;
919d29d28f2Storek 	register int level;
920d29d28f2Storek {
921d29d28f2Storek 	level &= 0xff;
922d29d28f2Storek 	sc->sc_mlevel = level;
923d29d28f2Storek 	if (level != 0)
924d29d28f2Storek 		sc->sc_map.mr_mmr1 |= AMD_MMR1_STG;
925d29d28f2Storek 	else
926d29d28f2Storek 		sc->sc_map.mr_mmr1 &=~ AMD_MMR1_STG;
927d29d28f2Storek 
928d29d28f2Storek 	sc->sc_map.mr_stgr = stg_coeff[(level * NSTG) / 256];
929d29d28f2Storek 	audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1,
930d29d28f2Storek 		      AMDR_MAP_STG, sc->sc_map.mr_stgr);
931d29d28f2Storek }
932d29d28f2Storek 
933d29d28f2Storek static int
934d29d28f2Storek audiosetinfo(sc, ai)
935d29d28f2Storek 	struct audio_softc *sc;
936d29d28f2Storek 	struct audio_info *ai;
937d29d28f2Storek {
938d29d28f2Storek 	struct audio_prinfo *r = &ai->record, *p = &ai->play;
939d29d28f2Storek 	register int s, bsize;
940d29d28f2Storek 
941d29d28f2Storek 	if (p->gain != ~0)
942d29d28f2Storek 		ausetpgain(sc, p->gain);
943d29d28f2Storek 	if (r->gain != ~0)
944d29d28f2Storek 		ausetrgain(sc, r->gain);
945d29d28f2Storek 	if (ai->monitor_gain != ~0)
946d29d28f2Storek 		ausetmgain(sc, p->gain);
947d29d28f2Storek 	if (p->port == AUDIO_SPEAKER) {
948d29d28f2Storek 		sc->sc_map.mr_mmr2 |= AMD_MMR2_LS;
949d29d28f2Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
950d29d28f2Storek 	} else if (p->port == AUDIO_HEADPHONE) {
951d29d28f2Storek 		sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS;
952d29d28f2Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
953d29d28f2Storek 	}
954d29d28f2Storek 	if (p->pause != (u_char)~0)
955d29d28f2Storek 		sc->sc_au.au_wb.cb_pause = p->pause;
956d29d28f2Storek 	if (r->pause != (u_char)~0)
957d29d28f2Storek 		sc->sc_au.au_rb.cb_pause = r->pause;
958d29d28f2Storek 
959d29d28f2Storek 	if (ai->blocksize != ~0) {
960d29d28f2Storek 		if (ai->blocksize == 0)
961d29d28f2Storek 			bsize = ai->blocksize = DEFBLKSIZE;
962d29d28f2Storek 		else if (ai->blocksize > MAXBLKSIZE)
963d29d28f2Storek 			bsize = ai->blocksize = MAXBLKSIZE;
964d29d28f2Storek 		else
965d29d28f2Storek 			bsize = ai->blocksize;
966d29d28f2Storek 
967d29d28f2Storek 		s = splaudio();
968d29d28f2Storek 		sc->sc_au.au_blksize = bsize;
969d29d28f2Storek 		/* AUDIO_FLUSH */
970d29d28f2Storek 		AUCB_INIT(&sc->sc_au.au_rb);
971d29d28f2Storek 		AUCB_INIT(&sc->sc_au.au_wb);
972d29d28f2Storek 		splx(s);
973d29d28f2Storek 
974d29d28f2Storek 	}
975d29d28f2Storek 	if (ai->hiwat != ~0 && (unsigned)ai->hiwat < AUCB_SIZE)
976d29d28f2Storek 		sc->sc_au.au_hiwat = ai->hiwat;
977d29d28f2Storek 	if (ai->lowat != ~0 && ai->lowat < AUCB_SIZE)
978d29d28f2Storek 		sc->sc_au.au_lowat = ai->lowat;
979d29d28f2Storek 
980d29d28f2Storek 	return (0);
981d29d28f2Storek }
982d29d28f2Storek 
983d29d28f2Storek static int
984d29d28f2Storek sunaudiosetinfo(sc, ai)
985d29d28f2Storek 	struct audio_softc *sc;
986d29d28f2Storek 	struct sun_audio_info *ai;
987d29d28f2Storek {
988d29d28f2Storek 	struct sun_audio_prinfo *r = &ai->record, *p = &ai->play;
989d29d28f2Storek 
990d29d28f2Storek 	if (p->gain != ~0)
991d29d28f2Storek 		ausetpgain(sc, p->gain);
992d29d28f2Storek 	if (r->gain != ~0)
993d29d28f2Storek 		ausetrgain(sc, r->gain);
994d29d28f2Storek 	if (ai->monitor_gain != ~0)
995d29d28f2Storek 		ausetmgain(sc, p->gain);
996d29d28f2Storek 	if (p->port == AUDIO_SPEAKER) {
997d29d28f2Storek 		sc->sc_map.mr_mmr2 |= AMD_MMR2_LS;
998d29d28f2Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
999d29d28f2Storek 	} else if (p->port == AUDIO_HEADPHONE) {
1000d29d28f2Storek 		sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS;
1001d29d28f2Storek 		audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2);
1002d29d28f2Storek 	}
1003d29d28f2Storek 	/*
1004d29d28f2Storek 	 * The bsd driver does not distinguish between paused and active.
1005d29d28f2Storek 	 * (In the sun driver, not active means samples are not ouput
1006d29d28f2Storek 	 * at all, but paused means the last streams buffer is drained
1007d29d28f2Storek 	 * and then output stops.)  If either are 0, then when stop output.
1008d29d28f2Storek 	 * Otherwise, if either are non-zero, we resume.
1009d29d28f2Storek 	 */
1010d29d28f2Storek 	if (p->pause == 0 || p->active == 0)
1011d29d28f2Storek 		sc->sc_au.au_wb.cb_pause = 0;
1012d29d28f2Storek 	else if (p->pause != (u_char)~0 || p->active != (u_char)~0)
1013d29d28f2Storek 		sc->sc_au.au_wb.cb_pause = 1;
1014d29d28f2Storek 	if (r->pause == 0 || r->active == 0)
1015d29d28f2Storek 		sc->sc_au.au_rb.cb_pause = 0;
1016d29d28f2Storek 	else if (r->pause != (u_char)~0 || r->active != (u_char)~0)
1017d29d28f2Storek 		sc->sc_au.au_rb.cb_pause = 1;
1018d29d28f2Storek 
1019d29d28f2Storek 	return (0);
1020d29d28f2Storek }
1021d29d28f2Storek 
1022d29d28f2Storek static int
1023d29d28f2Storek audiogetinfo(sc, ai)
1024d29d28f2Storek 	struct audio_softc *sc;
1025d29d28f2Storek 	struct audio_info *ai;
1026d29d28f2Storek {
1027d29d28f2Storek 	struct audio_prinfo *r = &ai->record, *p = &ai->play;
1028d29d28f2Storek 
1029d29d28f2Storek 	p->sample_rate = r->sample_rate = 8000;
1030d29d28f2Storek 	p->channels = r->channels = 1;
1031d29d28f2Storek 	p->precision = r->precision = 8;
1032d29d28f2Storek 	p->encoding = r->encoding = AUDIO_ENCODING_ULAW;
1033d29d28f2Storek 
1034d29d28f2Storek 	ai->monitor_gain = sc->sc_mlevel;
1035d29d28f2Storek 	r->gain = sc->sc_rlevel;
1036d29d28f2Storek 	p->gain = sc->sc_plevel;
1037d29d28f2Storek 	r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ?
1038d29d28f2Storek 		AUDIO_SPEAKER : AUDIO_HEADPHONE;
1039d29d28f2Storek 
1040d29d28f2Storek 	p->pause = sc->sc_au.au_wb.cb_pause;
1041d29d28f2Storek 	r->pause = sc->sc_au.au_rb.cb_pause;
1042d29d28f2Storek 	p->error = sc->sc_au.au_wb.cb_drops != 0;
1043d29d28f2Storek 	r->error = sc->sc_au.au_rb.cb_drops != 0;
1044d29d28f2Storek 
1045d29d28f2Storek 	p->open = sc->sc_open;
1046d29d28f2Storek 	r->open = sc->sc_open;
1047d29d28f2Storek 
1048d29d28f2Storek 	p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops;
1049d29d28f2Storek 	r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops;
1050d29d28f2Storek 
1051d29d28f2Storek 	p->seek = sc->sc_wseek;
1052d29d28f2Storek 	r->seek = sc->sc_rseek;
1053d29d28f2Storek 
1054d29d28f2Storek 	ai->blocksize = sc->sc_au.au_blksize;
1055d29d28f2Storek 	ai->hiwat = sc->sc_au.au_hiwat;
1056d29d28f2Storek 	ai->lowat = sc->sc_au.au_lowat;
1057d29d28f2Storek 
1058d29d28f2Storek 	return (0);
1059d29d28f2Storek }
1060d29d28f2Storek 
1061d29d28f2Storek static int
1062d29d28f2Storek sunaudiogetinfo(sc, ai)
1063d29d28f2Storek 	struct audio_softc *sc;
1064d29d28f2Storek 	struct sun_audio_info *ai;
1065d29d28f2Storek {
1066d29d28f2Storek 	struct sun_audio_prinfo *r = &ai->record, *p = &ai->play;
1067d29d28f2Storek 
1068d29d28f2Storek 	p->sample_rate = r->sample_rate = 8000;
1069d29d28f2Storek 	p->channels = r->channels = 1;
1070d29d28f2Storek 	p->precision = r->precision = 8;
1071d29d28f2Storek 	p->encoding = r->encoding = AUDIO_ENCODING_ULAW;
1072d29d28f2Storek 
1073d29d28f2Storek 	ai->monitor_gain = sc->sc_mlevel;
1074d29d28f2Storek 	r->gain = sc->sc_rlevel;
1075d29d28f2Storek 	p->gain = sc->sc_plevel;
1076d29d28f2Storek 	r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ?
1077d29d28f2Storek 		AUDIO_SPEAKER : AUDIO_HEADPHONE;
1078d29d28f2Storek 
1079d29d28f2Storek 	p->active = p->pause = sc->sc_au.au_wb.cb_pause;
1080d29d28f2Storek 	r->active = r->pause = sc->sc_au.au_rb.cb_pause;
1081d29d28f2Storek 	p->error = sc->sc_au.au_wb.cb_drops != 0;
1082d29d28f2Storek 	r->error = sc->sc_au.au_rb.cb_drops != 0;
1083d29d28f2Storek 
1084d29d28f2Storek 	p->waiting = 0;
1085d29d28f2Storek 	r->waiting = 0;
1086d29d28f2Storek 	p->eof = 0;
1087d29d28f2Storek 	r->eof = 0;
1088d29d28f2Storek 
1089d29d28f2Storek 	p->open = sc->sc_open;
1090d29d28f2Storek 	r->open = sc->sc_open;
1091d29d28f2Storek 
1092d29d28f2Storek 	p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops;
1093d29d28f2Storek 	r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops;
1094d29d28f2Storek 
1095d29d28f2Storek 	return (0);
1096d29d28f2Storek }
1097d29d28f2Storek #endif
1098