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