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