xref: /netbsd-src/sys/dev/ic/am7930.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*	$NetBSD: am7930.c,v 1.14 1996/11/01 23:32:15 pk Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 Rolf Grossmann
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Rolf Grossmann.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "audio.h"
34 #if NAUDIO > 0
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/errno.h>
39 #include <sys/ioctl.h>
40 #include <sys/device.h>
41 #include <sys/proc.h>
42 
43 #include <machine/autoconf.h>
44 #include <machine/cpu.h>
45 
46 #include <sys/audioio.h>
47 #include <dev/audio_if.h>
48 
49 #include <dev/ic/am7930reg.h>
50 #include <sparc/dev/amd7930var.h>
51 
52 #ifdef AUDIO_DEBUG
53 extern void Dprintf __P((const char *, ...));
54 
55 int     amd7930debug = 0;
56 #define DPRINTF(x)      if (amd7930debug) Dprintf x
57 #else
58 #define DPRINTF(x)
59 #endif
60 
61 /*
62  * Software state, per AMD79C30 audio chip.
63  */
64 struct amd7930_softc {
65 	struct	device sc_dev;		/* base device */
66 	struct	intrhand sc_hwih;	/* hardware interrupt vector */
67 	struct	intrhand sc_swih;	/* software interrupt vector */
68 
69 	int	sc_open;		/* single use device */
70 	int	sc_locked;		/* true when transfering data */
71 	struct	mapreg sc_map;		/* current contents of map registers */
72 
73 	u_char	sc_rlevel;		/* record level */
74 	u_char	sc_plevel;		/* play level */
75 	u_char	sc_mlevel;		/* monitor level */
76 	u_char	sc_out_port;		/* output port */
77 
78 	/* interfacing with the interrupt handlers */
79 	void	(*sc_rintr)(void*);	/* input completion intr handler */
80 	void	*sc_rarg;		/* arg for sc_rintr() */
81 	void	(*sc_pintr)(void*);	/* output completion intr handler */
82 	void	*sc_parg;		/* arg for sc_pintr() */
83 
84         /* sc_au is special in that the hardware interrupt handler uses it */
85         struct  auio sc_au;		/* recv and xmit buffers, etc */
86 #define sc_intrcnt	sc_au.au_intrcnt	/* statistics */
87 };
88 
89 /* interrupt interfaces */
90 #ifdef AUDIO_C_HANDLER
91 int	amd7930hwintr __P((void *));
92 #if defined(SUN4M)
93 #define AUDIO_SET_SWINTR do {		\
94 	if (CPU_ISSUN4M)		\
95 		raise(0, 4);		\
96 	else				\
97 		ienab_bis(IE_L4);	\
98 } while(0);
99 #else
100 #define AUDIO_SET_SWINTR ienab_bis(IE_L4)
101 #endif /* defined(SUN4M) */
102 #else
103 struct auio *auiop;
104 #endif /* AUDIO_C_HANDLER */
105 int	amd7930swintr __P((void *));
106 
107 /* forward declarations */
108 void	audio_setmap __P((volatile struct amd7930 *, struct mapreg *));
109 static void init_amd __P((volatile struct amd7930 *));
110 
111 /* autoconfiguration driver */
112 void	amd7930attach __P((struct device *, struct device *, void *));
113 int	amd7930match __P((struct device *, void *, void *));
114 
115 struct cfattach audio_ca = {
116 	sizeof(struct amd7930_softc), amd7930match, amd7930attach
117 };
118 
119 struct	cfdriver audio_cd = {
120 	NULL, "audio", DV_DULL
121 };
122 
123 struct audio_device amd7930_device = {
124 	"amd7930",
125 	"x",
126 	"audio"
127 };
128 
129 /* Write 16 bits of data from variable v to the data port of the audio chip */
130 #define	WAMD16(amd, v) ((amd)->dr = (v), (amd)->dr = (v) >> 8)
131 
132 /* The following tables stolen from former (4.4Lite's) sys/sparc/bsd_audio.c */
133 
134 /*
135  * gx, gr & stg gains.  this table must contain 256 elements with
136  * the 0th being "infinity" (the magic value 9008).  The remaining
137  * elements match sun's gain curve (but with higher resolution):
138  * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
139  */
140 static const u_short gx_coeff[256] = {
141 	0x9008, 0x8b7c, 0x8b51, 0x8b45, 0x8b42, 0x8b3b, 0x8b36, 0x8b33,
142 	0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
143 	0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
144 	0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
145 	0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
146 	0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
147 	0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
148 	0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
149 	0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
150 	0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
151 	0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
152 	0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
153 	0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
154 	0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
155 	0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
156 	0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
157 	0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
158 	0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
159 	0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
160 	0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
161 	0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
162 	0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
163 	0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
164 	0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
165 	0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
166 	0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
167 	0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
168 	0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
169 	0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
170 	0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
171 	0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
172 	0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
173 };
174 
175 /*
176  * second stage play gain.
177  */
178 static const u_short ger_coeff[] = {
179 	0x431f, /* 5. dB */
180 	0x331f, /* 5.5 dB */
181 	0x40dd, /* 6. dB */
182 	0x11dd, /* 6.5 dB */
183 	0x440f, /* 7. dB */
184 	0x411f, /* 7.5 dB */
185 	0x311f, /* 8. dB */
186 	0x5520, /* 8.5 dB */
187 	0x10dd, /* 9. dB */
188 	0x4211, /* 9.5 dB */
189 	0x410f, /* 10. dB */
190 	0x111f, /* 10.5 dB */
191 	0x600b, /* 11. dB */
192 	0x00dd, /* 11.5 dB */
193 	0x4210, /* 12. dB */
194 	0x110f, /* 13. dB */
195 	0x7200, /* 14. dB */
196 	0x2110, /* 15. dB */
197 	0x2200, /* 15.9 dB */
198 	0x000b, /* 16.9 dB */
199 	0x000f  /* 18. dB */
200 #define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
201 };
202 
203 /*
204  * Define our interface to the higher level audio driver.
205  */
206 int	amd7930_open __P((dev_t, int));
207 void	amd7930_close __P((void *));
208 int	amd7930_set_in_sr __P((void *, u_long));
209 u_long	amd7930_get_in_sr __P((void *));
210 int	amd7930_set_out_sr __P((void *, u_long));
211 u_long	amd7930_get_out_sr __P((void *));
212 int	amd7930_query_encoding __P((void *, struct audio_encoding *));
213 int	amd7930_set_encoding __P((void *, u_int));
214 int	amd7930_get_encoding __P((void *));
215 int	amd7930_set_precision __P((void *, u_int));
216 int	amd7930_get_precision __P((void *));
217 int	amd7930_set_channels __P((void *, int));
218 int	amd7930_get_channels __P((void *));
219 int	amd7930_round_blocksize __P((void *, int));
220 int	amd7930_set_out_port __P((void *, int));
221 int	amd7930_get_out_port __P((void *));
222 int	amd7930_set_in_port __P((void *, int));
223 int	amd7930_get_in_port __P((void *));
224 int	amd7930_commit_settings __P((void *));
225 u_int	amd7930_get_silence __P((int));
226 int	amd7930_start_output __P((void *, void *, int, void (*)(void *),
227 				  void *));
228 int	amd7930_start_input __P((void *, void *, int, void (*)(void *),
229 				 void *));
230 int	amd7930_halt_output __P((void *));
231 int	amd7930_halt_input __P((void *));
232 int	amd7930_cont_output __P((void *));
233 int	amd7930_cont_input __P((void *));
234 int	amd7930_getdev __P((void *, struct audio_device *));
235 int	amd7930_setfd __P((void *, int));
236 int	amd7930_set_port __P((void *, mixer_ctrl_t *));
237 int	amd7930_get_port __P((void *, mixer_ctrl_t *));
238 int	amd7930_query_devinfo __P((void *, mixer_devinfo_t *));
239 
240 
241 struct audio_hw_if sa_hw_if = {
242 	amd7930_open,
243 	amd7930_close,
244 	NULL,
245 	amd7930_set_in_sr,
246 	amd7930_get_in_sr,
247 	amd7930_set_out_sr,
248 	amd7930_get_out_sr,
249 	amd7930_query_encoding,
250 	amd7930_set_encoding,
251 	amd7930_get_encoding,
252 	amd7930_set_precision,
253 	amd7930_get_precision,
254 	amd7930_set_channels,
255 	amd7930_get_channels,
256 	amd7930_round_blocksize,
257 	amd7930_set_out_port,
258 	amd7930_get_out_port,
259 	amd7930_set_in_port,
260 	amd7930_get_in_port,
261 	amd7930_commit_settings,
262 	amd7930_get_silence,
263 	NULL,
264 	NULL,
265 	amd7930_start_output,
266 	amd7930_start_input,
267 	amd7930_halt_output,
268 	amd7930_halt_input,
269 	amd7930_cont_output,
270 	amd7930_cont_input,
271 	NULL,
272 	amd7930_getdev,
273 	amd7930_setfd,
274 	amd7930_set_port,
275 	amd7930_get_port,
276 	amd7930_query_devinfo,
277 	1,
278 	0
279 };
280 
281 /* autoconfig routines */
282 
283 int
284 amd7930match(parent, vcf, aux)
285 	struct device *parent;
286 	void *vcf, *aux;
287 {
288 	struct cfdata *cf = vcf;
289 	register struct confargs *ca = aux;
290 	register struct romaux *ra = &ca->ca_ra;
291 
292 	if (CPU_ISSUN4)
293 		return (0);
294 	return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0);
295 }
296 
297 /*
298  * Audio chip found.
299  */
300 void
301 amd7930attach(parent, self, args)
302 	struct device *parent, *self;
303 	void *args;
304 {
305 	register struct amd7930_softc *sc = (struct amd7930_softc *)self;
306 	register struct confargs *ca = args;
307 	register struct romaux *ra = &ca->ca_ra;
308 	register volatile struct amd7930 *amd;
309 	register int pri;
310 
311 	if (ra->ra_nintr != 1) {
312 		printf(": expected 1 interrupt, got %d\n", ra->ra_nintr);
313 		return;
314 	}
315 	pri = ra->ra_intr[0].int_pri;
316 	printf(" pri %d, softpri %d\n", pri, PIL_AUSOFT);
317 	amd = (volatile struct amd7930 *)(ra->ra_vaddr ?
318 		ra->ra_vaddr : mapiodev(ra->ra_reg, 0, sizeof (*amd),
319 					ca->ca_bustype));
320 
321 	sc->sc_map.mr_mmr1 = AMD_MMR1_GX | AMD_MMR1_GER |
322 			     AMD_MMR1_GR | AMD_MMR1_STG;
323 	sc->sc_au.au_amd = amd;
324 	/* set boot defaults */
325 	sc->sc_rlevel = 128;
326 	sc->sc_plevel = 128;
327 	sc->sc_mlevel = 0;
328 	sc->sc_out_port = SUNAUDIO_SPEAKER;
329 
330 	init_amd(amd);
331 
332 #ifndef AUDIO_C_HANDLER
333 	auiop = &sc->sc_au;
334 	intr_fasttrap(pri, amd7930_trap);
335 #else
336 	sc->sc_hwih.ih_fun = amd7930hwintr;
337 	sc->sc_hwih.ih_arg = &sc->sc_au;
338 	intr_establish(pri, &sc->sc_hwih);
339 #endif
340 	sc->sc_swih.ih_fun = amd7930swintr;
341 	sc->sc_swih.ih_arg = sc;
342 	intr_establish(PIL_AUSOFT, &sc->sc_swih);
343 
344 	evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);
345 
346 	if (audio_hardware_attach(&sa_hw_if, sc) != 0)
347 		printf("audio: could not attach to audio pseudo-device driver\n");
348 }
349 
350 static void
351 init_amd(amd)
352 	register volatile struct amd7930 *amd;
353 {
354 	/* disable interrupts */
355 	amd->cr = AMDR_INIT;
356 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
357 
358 	/*
359 	 * Initialize the mux unit.  We use MCR3 to route audio (MAP)
360 	 * through channel Bb.  MCR1 and MCR2 are unused.
361 	 * Setting the INT enable bit in MCR4 will generate an interrupt
362 	 * on each converted audio sample.
363 	 */
364 	amd->cr = AMDR_MUX_1_4;
365  	amd->dr = 0;
366 	amd->dr = 0;
367 	amd->dr = (AMD_MCRCHAN_BB << 4) | AMD_MCRCHAN_BA;
368 	amd->dr = AMD_MCR4_INT_ENABLE;
369 }
370 
371 int
372 amd7930_open(dev, flags)
373 	dev_t dev;
374 	int flags;
375 {
376 	register struct amd7930_softc *sc;
377 	int unit = AUDIOUNIT(dev);
378 
379 	DPRINTF(("sa_open: unit %d\n",unit));
380 
381 	if (unit >= audio_cd.cd_ndevs)
382 		return (ENODEV);
383 	if ((sc = audio_cd.cd_devs[unit]) == NULL)
384 		return (ENXIO);
385 	if (sc->sc_open)
386 		return (EBUSY);
387 	sc->sc_open = 1;
388 	sc->sc_locked = 0;
389 	sc->sc_rintr = 0;
390 	sc->sc_rarg = 0;
391 	sc->sc_pintr = 0;
392 	sc->sc_parg = 0;
393 
394 	sc->sc_au.au_rdata = 0;
395 	sc->sc_au.au_pdata = 0;
396 
397 	DPRINTF(("saopen: ok -> sc=0x%x\n",sc));
398 
399 	return (0);
400 }
401 
402 void
403 amd7930_close(addr)
404 	void *addr;
405 {
406 	register struct amd7930_softc *sc = addr;
407 
408 	DPRINTF(("sa_close: sc=0x%x\n", sc));
409 	/*
410 	 * halt i/o, clear open flag, and done.
411 	 */
412 	amd7930_halt_input(sc);
413 	amd7930_halt_output(sc);
414 	sc->sc_open = 0;
415 
416 	DPRINTF(("sa_close: closed.\n"));
417 }
418 
419 int
420 amd7930_set_in_sr(addr, sr)
421 	void *addr;
422 	u_long sr;
423 {
424 	if (sr != 8000)
425 		return EINVAL;
426 
427 	return(0);	/* no other sampling rates supported by amd chip */
428 }
429 
430 u_long
431 amd7930_get_in_sr(addr)
432 	void *addr;
433 {
434 	return(8000);
435 }
436 
437 int
438 amd7930_set_out_sr(addr, sr)
439 	void *addr;
440 	u_long sr;
441 {
442 	if (sr != 8000)
443 		return(EINVAL);
444 
445 	return(0);	/* no other sampling rates supported by amd chip */
446 }
447 
448 u_long
449 amd7930_get_out_sr(addr)
450 	void *addr;
451 {
452 	return(8000);
453 }
454 
455 int
456 amd7930_query_encoding(addr, fp)
457 	void *addr;
458 	struct audio_encoding *fp;
459 {
460 	switch (fp->index) {	/* ??? */
461 	    case 0:
462 		    strcpy(fp->name, "MU-Law");
463 		    fp->format_id = AUDIO_ENCODING_ULAW;
464 		    break;
465 	    default:
466 		    return(EINVAL);
467 		    /*NOTREACHED*/
468 	}
469 	return(0);
470 }
471 
472 int
473 amd7930_set_encoding(addr, enc)
474 	void *addr;
475 	u_int enc;
476 {
477 	if (enc != AUDIO_ENCODING_ULAW)
478 		return(EINVAL);
479 
480 	return(0);		/* no other encoding supported by amd chip */
481 }
482 
483 int
484 amd7930_get_encoding(addr)
485 	void *addr;
486 {
487 	return(AUDIO_ENCODING_ULAW);
488 }
489 
490 int
491 amd7930_set_precision(addr, prec)
492 	void *addr;
493 	u_int prec;
494 {
495 	if (prec != 8)
496 		return(EINVAL);
497 
498 	return(0);		/* no other precision supported by amd chip */
499 }
500 
501 int
502 amd7930_get_precision(addr)
503 	void *addr;
504 {
505 	return(8);
506 }
507 
508 int
509 amd7930_set_channels(addr, chans)
510 	void *addr;
511 	int chans;
512 {
513 	if (chans != 1)
514 		return(EINVAL);
515 
516 	return(0);		/* only 1 channel supported by amd chip */
517 }
518 
519 int
520 amd7930_get_channels(addr)
521 	void *addr;
522 {
523 	return(1);
524 }
525 
526 int
527 amd7930_round_blocksize(addr, blk)
528 	void *addr;
529 	int blk;
530 {
531 	return(blk);
532 }
533 
534 int
535 amd7930_set_out_port(addr, port)
536 	void *addr;
537 	int port;
538 {
539 	register struct amd7930_softc *sc = addr;
540 
541 	switch(port) {
542 	    case SUNAUDIO_SPEAKER:
543 	    case SUNAUDIO_HEADPHONES:
544 		sc->sc_out_port = port;	/* set on commit */
545 		break;
546 	    default:
547 		return(EINVAL);
548 	}
549 	return(0);
550 }
551 
552 int
553 amd7930_get_out_port(addr)
554 	void *addr;
555 {
556 	register struct amd7930_softc *sc = addr;
557 
558 	return(sc->sc_out_port);
559 }
560 
561 int
562 amd7930_set_in_port(addr, port)
563 	void *addr;
564 	int port;
565 {
566 	if (port != SUNAUDIO_MIC_PORT)
567 		return(EINVAL);
568 
569 	return(0);	/* only microphone input supported by amd chip */
570 }
571 
572 int
573 amd7930_get_in_port(addr)
574 	void *addr;
575 {
576 	return(SUNAUDIO_MIC_PORT);
577 }
578 
579 int
580 amd7930_commit_settings(addr)
581 	void *addr;
582 {
583 	register struct amd7930_softc *sc = addr;
584 	register struct mapreg *map;
585 	register volatile struct amd7930 *amd;
586 	register int s, level;
587 
588 	DPRINTF(("sa_commit.\n"));
589 
590 	map = &sc->sc_map;
591 	amd = sc->sc_au.au_amd;
592 
593 	map->mr_gx = gx_coeff[sc->sc_rlevel];
594 	map->mr_stgr = gx_coeff[sc->sc_mlevel];
595 
596 	level = (sc->sc_plevel * (256 + NGER)) >> 8;
597 	if (level >= 256) {
598 		map->mr_ger = ger_coeff[level - 256];
599 		map->mr_gr = gx_coeff[255];
600 	} else {
601 		map->mr_ger = ger_coeff[0];
602 		map->mr_gr = gx_coeff[level];
603 	}
604 
605 	if (sc->sc_out_port == SUNAUDIO_SPEAKER)
606 		map->mr_mmr2 |= AMD_MMR2_LS;
607 	else
608 		map->mr_mmr2 &= ~AMD_MMR2_LS;
609 
610 	s = splaudio();
611 
612 	amd->cr = AMDR_MAP_MMR1;
613 	amd->dr = map->mr_mmr1;
614 	amd->cr = AMDR_MAP_GX;
615 	WAMD16(amd, map->mr_gx);
616 	amd->cr = AMDR_MAP_STG;
617 	WAMD16(amd, map->mr_stgr);
618 	amd->cr = AMDR_MAP_GR;
619 	WAMD16(amd, map->mr_gr);
620 	amd->cr = AMDR_MAP_GER;
621 	WAMD16(amd, map->mr_ger);
622 	amd->cr = AMDR_MAP_MMR2;
623 	amd->dr = map->mr_mmr2;
624 
625 	splx(s);
626 	return(0);
627 }
628 
629 u_int
630 amd7930_get_silence(enc)
631 	int enc;
632 {
633 	return(0x7f);
634 }
635 
636 int
637 amd7930_start_output(addr, p, cc, intr, arg)
638 	void *addr;
639 	void *p;
640 	int cc;
641 	void (*intr) __P((void *));
642 	void *arg;
643 {
644 	register struct amd7930_softc *sc = addr;
645 
646 #ifdef AUDIO_DEBUG
647 	if (amd7930debug > 1)
648 		Dprintf("sa_start_output: cc=%d 0x%x (0x%x)\n", cc, intr, arg);
649 #endif
650 
651 	if (!sc->sc_locked) {
652 		register volatile struct amd7930 *amd;
653 
654 		amd = sc->sc_au.au_amd;
655 		amd->cr = AMDR_INIT;
656 		amd->dr = AMD_INIT_PMS_ACTIVE;
657 		sc->sc_locked = 1;
658 		DPRINTF(("sa_start_output: started intrs.\n"));
659 	}
660 	sc->sc_pintr = intr;
661 	sc->sc_parg = arg;
662 	sc->sc_au.au_pdata = p;
663 	sc->sc_au.au_pend = p + cc - 1;
664 	return(0);
665 }
666 
667 /* ARGSUSED */
668 int
669 amd7930_start_input(addr, p, cc, intr, arg)
670 	void *addr;
671 	void *p;
672 	int cc;
673 	void (*intr) __P((void *));
674 	void *arg;
675 {
676 	register struct amd7930_softc *sc = addr;
677 
678 #ifdef AUDIO_DEBUG
679 	if (amd7930debug > 1)
680 		Dprintf("sa_start_input: cc=%d 0x%x (0x%x)\n", cc, intr, arg);
681 #endif
682 
683 	if (!sc->sc_locked) {
684 		register volatile struct amd7930 *amd;
685 
686 		amd = sc->sc_au.au_amd;
687 		amd->cr = AMDR_INIT;
688 		amd->dr = AMD_INIT_PMS_ACTIVE;
689 		sc->sc_locked = 1;
690 		DPRINTF(("sa_start_input: started intrs.\n"));
691 	}
692 	sc->sc_rintr = intr;
693 	sc->sc_rarg = arg;
694 	sc->sc_au.au_rdata = p;
695 	sc->sc_au.au_rend = p + cc -1;
696 	return(0);
697 }
698 
699 int
700 amd7930_halt_output(addr)
701 	void *addr;
702 {
703 	register struct amd7930_softc *sc = addr;
704 	register volatile struct amd7930 *amd;
705 
706 	/* XXX only halt, if input is also halted ?? */
707 	amd = sc->sc_au.au_amd;
708 	amd->cr = AMDR_INIT;
709 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
710 	sc->sc_locked = 0;
711 
712 	return(0);
713 }
714 
715 int
716 amd7930_halt_input(addr)
717 	void *addr;
718 {
719 	register struct amd7930_softc *sc = addr;
720 	register volatile struct amd7930 *amd;
721 
722 	/* XXX only halt, if output is also halted ?? */
723 	amd = sc->sc_au.au_amd;
724 	amd->cr = AMDR_INIT;
725 	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
726 	sc->sc_locked = 0;
727 
728 	return(0);
729 }
730 
731 int
732 amd7930_cont_output(addr)
733 	void *addr;
734 {
735 	DPRINTF(("amd7930_cont_output: never called, what should it do?!\n"));
736 	return(0);
737 }
738 
739 int
740 amd7930_cont_input(addr)
741 	void *addr;
742 {
743 	DPRINTF(("amd7930_cont_input: never called, what should it do?!\n"));
744 	return(0);
745 }
746 
747 int
748 amd7930_getdev(addr, retp)
749         void *addr;
750         struct audio_device *retp;
751 {
752         *retp = amd7930_device;
753         return 0;
754 }
755 
756 int
757 amd7930_setfd(addr, flag)
758         void *addr;
759         int flag;
760 {
761         /* Always full-duplex */
762         return(0);
763 }
764 
765 int
766 amd7930_set_port(addr, cp)
767 	void *addr;
768 	mixer_ctrl_t *cp;
769 {
770 	register struct amd7930_softc *sc = addr;
771 
772 	DPRINTF(("amd7930_set_port: port=%d", cp->dev));
773 
774 	if (cp->type != AUDIO_MIXER_VALUE || cp->un.value.num_channels != 1)
775 		return(EINVAL);
776 
777 	switch(cp->dev) {
778 	    case SUNAUDIO_MIC_PORT:
779 		    sc->sc_rlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
780 		    break;
781 	    case SUNAUDIO_SPEAKER:
782 	    case SUNAUDIO_HEADPHONES:
783 		    sc->sc_plevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
784 		    break;
785 	    case SUNAUDIO_MONITOR:
786 		    sc->sc_mlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
787 		    break;
788 	    default:
789 		    return(EINVAL);
790 		    /* NOTREACHED */
791 	}
792 	return(0);
793 }
794 
795 int
796 amd7930_get_port(addr, cp)
797 	void *addr;
798 	mixer_ctrl_t *cp;
799 {
800 	register struct amd7930_softc *sc = addr;
801 
802 	DPRINTF(("amd7930_get_port: port=%d", cp->dev));
803 
804 	if (cp->type != AUDIO_MIXER_VALUE || cp->un.value.num_channels != 1)
805 		return(EINVAL);
806 
807 	switch(cp->dev) {
808 	    case SUNAUDIO_MIC_PORT:
809 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_rlevel;
810 		    break;
811 	    case SUNAUDIO_SPEAKER:
812 	    case SUNAUDIO_HEADPHONES:
813 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_plevel;
814 		    break;
815 	    case SUNAUDIO_MONITOR:
816 		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_mlevel;
817 		    break;
818 	    default:
819 		    return(EINVAL);
820 		    /* NOTREACHED */
821 	}
822 	return(0);
823 }
824 
825 int
826 amd7930_query_devinfo(addr, dip)
827 	void *addr;
828 	register mixer_devinfo_t *dip;
829 {
830 	switch(dip->index) {
831 	    case SUNAUDIO_MIC_PORT:
832 		    dip->type = AUDIO_MIXER_VALUE;
833 		    dip->mixer_class = SUNAUDIO_INPUT_CLASS;
834 		    dip->prev = dip->next = AUDIO_MIXER_LAST;
835 		    strcpy(dip->label.name, AudioNmicrophone);
836 		    dip->un.v.num_channels = 1;
837 		    strcpy(dip->un.v.units.name, AudioNvolume);
838 		    break;
839 	    case SUNAUDIO_SPEAKER:
840 		    dip->type = AUDIO_MIXER_VALUE;
841 		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
842 		    dip->prev = dip->next = AUDIO_MIXER_LAST;
843 		    strcpy(dip->label.name, AudioNspeaker);
844 		    dip->un.v.num_channels = 1;
845 		    strcpy(dip->un.v.units.name, AudioNvolume);
846 		    break;
847 	    case SUNAUDIO_HEADPHONES:
848 		    dip->type = AUDIO_MIXER_VALUE;
849 		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
850 		    dip->prev = dip->next = AUDIO_MIXER_LAST;
851 		    strcpy(dip->label.name, AudioNheadphone);
852 		    dip->un.v.num_channels = 1;
853 		    strcpy(dip->un.v.units.name, AudioNvolume);
854 		    break;
855 	    case SUNAUDIO_MONITOR:
856 		    dip->type = AUDIO_MIXER_VALUE;
857 		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
858 		    dip->next = dip->prev = AUDIO_MIXER_LAST;
859 		    strcpy(dip->label.name, AudioNmonitor);
860 		    dip->un.v.num_channels = 1;
861 		    strcpy(dip->un.v.units.name, AudioNvolume);
862 		    break;
863 	    case SUNAUDIO_INPUT_CLASS:
864 		    dip->type = AUDIO_MIXER_CLASS;
865 		    dip->mixer_class = SUNAUDIO_INPUT_CLASS;
866 		    dip->next = dip->prev = AUDIO_MIXER_LAST;
867 		    strcpy(dip->label.name, AudioCInputs);
868 		    break;
869 	    case SUNAUDIO_OUTPUT_CLASS:
870 		    dip->type = AUDIO_MIXER_CLASS;
871 		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
872 		    dip->next = dip->prev = AUDIO_MIXER_LAST;
873 		    strcpy(dip->label.name, AudioCOutputs);
874 		    break;
875 	    default:
876 		    return ENXIO;
877 		    /*NOTREACHED*/
878 	}
879 
880 	DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
881 
882 	return(0);
883 }
884 
885 #ifdef AUDIO_C_HANDLER
886 int
887 amd7930hwintr(au0)
888 	void *au0;
889 {
890 	register struct auio *au = au0;
891 	register volatile struct amd7930 *amd = au->au_amd;
892 	register u_char *d, *e;
893 	register int k;
894 
895 	k = amd->ir;		/* clear interrupt */
896 
897 	/* receive incoming data */
898 	d = au->au_rdata;
899 	e = au->au_rend;
900 	if (d && d <= e) {
901 		*d = amd->bbrb;
902 		au->au_rdata++;
903 		if (d == e) {
904 #ifdef AUDIO_DEBUG
905 		        if (amd7930debug > 1)
906                 		Dprintf("amd7930hwintr: swintr(r) requested");
907 #endif
908 			AUDIO_SET_SWINTR;
909 		}
910 	}
911 
912 	/* send outgoing data */
913 	d = au->au_pdata;
914 	e = au->au_pend;
915 	if (d && d <= e) {
916 		amd->bbtb = *d;
917 		au->au_pdata++;
918 		if (d == e) {
919 #ifdef AUDIO_DEBUG
920 		        if (amd7930debug > 1)
921                 		Dprintf("amd7930hwintr: swintr(p) requested");
922 #endif
923 			AUDIO_SET_SWINTR;
924 		}
925 	}
926 
927 	*(au->au_intrcnt)++;
928 	return (1);
929 }
930 #endif /* AUDIO_C_HANDLER */
931 
932 int
933 amd7930swintr(sc0)
934 	void *sc0;
935 {
936 	register struct amd7930_softc *sc = sc0;
937 	register struct auio *au;
938 	register int s, ret = 0;
939 
940 #ifdef AUDIO_DEBUG
941 	if (amd7930debug > 1)
942 		Dprintf("audiointr: sc=0x%x\n",sc);
943 #endif
944 
945 	au = &sc->sc_au;
946 	s = splaudio();
947 	if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) {
948 		splx(s);
949 		ret = 1;
950 		(*sc->sc_rintr)(sc->sc_rarg);
951 		s = splaudio();
952 	}
953 	if (au->au_pdata > au->au_pend && sc->sc_pintr != NULL) {
954 		splx(s);
955 		ret = 1;
956 		(*sc->sc_pintr)(sc->sc_parg);
957 	} else
958 		splx(s);
959 	return (ret);
960 }
961 #endif /* NAUDIO > 0 */
962