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