am7930.c revision 1.32
1/*	$NetBSD: am7930.c,v 1.32 1997/10/19 07:41:46 augustss 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#define AUDIO_ROM_NAME "audio"
53
54#ifdef AUDIO_DEBUG
55extern void Dprintf __P((const char *, ...));
56
57int     amd7930debug = 0;
58#define DPRINTF(x)      if (amd7930debug) Dprintf x
59#else
60#define DPRINTF(x)
61#endif
62
63/*
64 * Software state, per AMD79C30 audio chip.
65 */
66struct amd7930_softc {
67	struct	device sc_dev;		/* base device */
68	struct	intrhand sc_hwih;	/* hardware interrupt vector */
69	struct	intrhand sc_swih;	/* software interrupt vector */
70
71	int	sc_open;		/* single use device */
72	int	sc_locked;		/* true when transfering data */
73	struct	mapreg sc_map;		/* current contents of map registers */
74
75	u_char	sc_rlevel;		/* record level */
76	u_char	sc_plevel;		/* play level */
77	u_char	sc_mlevel;		/* monitor level */
78	u_char	sc_out_port;		/* output port */
79
80	/* interfacing with the interrupt handlers */
81	void	(*sc_rintr)(void*);	/* input completion intr handler */
82	void	*sc_rarg;		/* arg for sc_rintr() */
83	void	(*sc_pintr)(void*);	/* output completion intr handler */
84	void	*sc_parg;		/* arg for sc_pintr() */
85
86        /* sc_au is special in that the hardware interrupt handler uses it */
87        struct  auio sc_au;		/* recv and xmit buffers, etc */
88#define sc_intrcnt	sc_au.au_intrcnt	/* statistics */
89};
90
91/* interrupt interfaces */
92#ifdef AUDIO_C_HANDLER
93int	amd7930hwintr __P((void *));
94#if defined(SUN4M)
95#define AUDIO_SET_SWINTR do {		\
96	if (CPU_ISSUN4M)		\
97		raise(0, 4);		\
98	else				\
99		ienab_bis(IE_L4);	\
100} while(0);
101#else
102#define AUDIO_SET_SWINTR ienab_bis(IE_L4)
103#endif /* defined(SUN4M) */
104#else
105struct auio *auiop;
106#endif /* AUDIO_C_HANDLER */
107int	amd7930swintr __P((void *));
108
109/* forward declarations */
110void	audio_setmap __P((volatile struct amd7930 *, struct mapreg *));
111static void init_amd __P((volatile struct amd7930 *));
112
113/* autoconfiguration driver */
114void	amd7930attach __P((struct device *, struct device *, void *));
115int	amd7930match __P((struct device *, struct cfdata *, void *));
116
117struct cfattach audioamd_ca = {
118	sizeof(struct amd7930_softc), amd7930match, amd7930attach
119};
120
121struct	cfdriver audioamd_cd = {
122	NULL, "audioamd", DV_DULL
123};
124
125struct audio_device amd7930_device = {
126	"amd7930",
127	"x",
128	"audioamd"
129};
130
131/* Write 16 bits of data from variable v to the data port of the audio chip */
132#define	WAMD16(amd, v) ((amd)->dr = (v), (amd)->dr = (v) >> 8)
133
134/* The following tables stolen from former (4.4Lite's) sys/sparc/bsd_audio.c */
135
136/*
137 * gx, gr & stg gains.  this table must contain 256 elements with
138 * the 0th being "infinity" (the magic value 9008).  The remaining
139 * elements match sun's gain curve (but with higher resolution):
140 * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
141 */
142static const u_short gx_coeff[256] = {
143	0x9008, 0x8e7c, 0x8e51, 0x8e45, 0x8d42, 0x8d3b, 0x8c36, 0x8c33,
144	0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
145	0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
146	0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
147	0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
148	0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
149	0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
150	0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
151	0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
152	0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
153	0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
154	0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
155	0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
156	0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
157	0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
158	0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
159	0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
160	0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
161	0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
162	0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
163	0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
164	0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
165	0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
166	0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
167	0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
168	0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
169	0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
170	0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
171	0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
172	0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
173	0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
174	0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
175};
176
177/*
178 * second stage play gain.
179 */
180static const u_short ger_coeff[] = {
181	0x431f, /* 5. dB */
182	0x331f, /* 5.5 dB */
183	0x40dd, /* 6. dB */
184	0x11dd, /* 6.5 dB */
185	0x440f, /* 7. dB */
186	0x411f, /* 7.5 dB */
187	0x311f, /* 8. dB */
188	0x5520, /* 8.5 dB */
189	0x10dd, /* 9. dB */
190	0x4211, /* 9.5 dB */
191	0x410f, /* 10. dB */
192	0x111f, /* 10.5 dB */
193	0x600b, /* 11. dB */
194	0x00dd, /* 11.5 dB */
195	0x4210, /* 12. dB */
196	0x110f, /* 13. dB */
197	0x7200, /* 14. dB */
198	0x2110, /* 15. dB */
199	0x2200, /* 15.9 dB */
200	0x000b, /* 16.9 dB */
201	0x000f  /* 18. dB */
202#define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
203};
204
205/*
206 * Define our interface to the higher level audio driver.
207 */
208int	amd7930_open __P((void *, int));
209void	amd7930_close __P((void *));
210int	amd7930_query_encoding __P((void *, struct audio_encoding *));
211int	amd7930_set_params __P((void *, int, int, struct audio_params *, struct audio_params *));
212int	amd7930_round_blocksize __P((void *, int));
213int	amd7930_commit_settings __P((void *));
214int	amd7930_start_output __P((void *, void *, int, void (*)(void *),
215				  void *));
216int	amd7930_start_input __P((void *, void *, int, void (*)(void *),
217				 void *));
218int	amd7930_halt_output __P((void *));
219int	amd7930_halt_input __P((void *));
220int	amd7930_getdev __P((void *, struct audio_device *));
221int	amd7930_set_port __P((void *, mixer_ctrl_t *));
222int	amd7930_get_port __P((void *, mixer_ctrl_t *));
223int	amd7930_query_devinfo __P((void *, mixer_devinfo_t *));
224int	amd7930_get_props __P((void *));
225
226
227struct audio_hw_if sa_hw_if = {
228	amd7930_open,
229	amd7930_close,
230	0,
231	amd7930_query_encoding,
232	amd7930_set_params,
233	amd7930_round_blocksize,
234	amd7930_commit_settings,
235	0,
236	0,
237	amd7930_start_output,
238	amd7930_start_input,
239	amd7930_halt_output,
240	amd7930_halt_input,
241	0,
242	amd7930_getdev,
243	0,
244	amd7930_set_port,
245	amd7930_get_port,
246	amd7930_query_devinfo,
247	0,
248	0,
249	0,
250        0,
251	amd7930_get_props,
252};
253
254/* autoconfig routines */
255
256int
257amd7930match(parent, cf, aux)
258	struct device *parent;
259	struct cfdata *cf;
260	void *aux;
261{
262	register struct confargs *ca = aux;
263	register struct romaux *ra = &ca->ca_ra;
264
265	if (CPU_ISSUN4)
266		return (0);
267	return (strcmp(AUDIO_ROM_NAME, ra->ra_name) == 0);
268}
269
270/*
271 * Audio chip found.
272 */
273void
274amd7930attach(parent, self, args)
275	struct device *parent, *self;
276	void *args;
277{
278	register struct amd7930_softc *sc = (struct amd7930_softc *)self;
279	register struct confargs *ca = args;
280	register struct romaux *ra = &ca->ca_ra;
281	register volatile struct amd7930 *amd;
282	register int pri;
283
284	if (ra->ra_nintr != 1) {
285		printf(": expected 1 interrupt, got %d\n", ra->ra_nintr);
286		return;
287	}
288	pri = ra->ra_intr[0].int_pri;
289	printf(" pri %d, softpri %d\n", pri, PIL_AUSOFT);
290	amd = (volatile struct amd7930 *)(ra->ra_vaddr ?
291		ra->ra_vaddr : mapiodev(ra->ra_reg, 0, sizeof (*amd)));
292
293	sc->sc_map.mr_mmr1 = AMD_MMR1_GX | AMD_MMR1_GER |
294			     AMD_MMR1_GR | AMD_MMR1_STG;
295	sc->sc_au.au_amd = amd;
296	/* set boot defaults */
297	sc->sc_rlevel = 128;
298	sc->sc_plevel = 128;
299	sc->sc_mlevel = 0;
300	sc->sc_out_port = SUNAUDIO_SPEAKER;
301
302	init_amd(amd);
303
304#ifndef AUDIO_C_HANDLER
305	auiop = &sc->sc_au;
306	intr_fasttrap(pri, amd7930_trap);
307#else
308	sc->sc_hwih.ih_fun = amd7930hwintr;
309	sc->sc_hwih.ih_arg = &sc->sc_au;
310	intr_establish(pri, &sc->sc_hwih);
311#endif
312	sc->sc_swih.ih_fun = amd7930swintr;
313	sc->sc_swih.ih_arg = sc;
314	intr_establish(PIL_AUSOFT, &sc->sc_swih);
315
316	evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);
317
318	audio_attach_mi(&sa_hw_if, 0, sc, &sc->sc_dev);
319}
320
321static void
322init_amd(amd)
323	register volatile struct amd7930 *amd;
324{
325	/* disable interrupts */
326	amd->cr = AMDR_INIT;
327	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
328
329	/*
330	 * Initialize the mux unit.  We use MCR3 to route audio (MAP)
331	 * through channel Bb.  MCR1 and MCR2 are unused.
332	 * Setting the INT enable bit in MCR4 will generate an interrupt
333	 * on each converted audio sample.
334	 */
335	amd->cr = AMDR_MUX_1_4;
336 	amd->dr = 0;
337	amd->dr = 0;
338	amd->dr = (AMD_MCRCHAN_BB << 4) | AMD_MCRCHAN_BA;
339	amd->dr = AMD_MCR4_INT_ENABLE;
340}
341
342int
343amd7930_open(addr, flags)
344	void *addr;
345	int flags;
346{
347	struct amd7930_softc *sc = addr;
348
349	DPRINTF(("sa_open: unit %p\n", sc));
350
351	if (sc->sc_open)
352		return (EBUSY);
353	sc->sc_open = 1;
354	sc->sc_locked = 0;
355	sc->sc_rintr = 0;
356	sc->sc_rarg = 0;
357	sc->sc_pintr = 0;
358	sc->sc_parg = 0;
359
360	sc->sc_au.au_rdata = 0;
361	sc->sc_au.au_pdata = 0;
362
363	DPRINTF(("saopen: ok -> sc=0x%x\n",sc));
364
365	return (0);
366}
367
368void
369amd7930_close(addr)
370	void *addr;
371{
372	register struct amd7930_softc *sc = addr;
373
374	DPRINTF(("sa_close: sc=0x%x\n", sc));
375	/*
376	 * halt i/o, clear open flag, and done.
377	 */
378	amd7930_halt_input(sc);
379	amd7930_halt_output(sc);
380	sc->sc_open = 0;
381
382	DPRINTF(("sa_close: closed.\n"));
383}
384
385int
386amd7930_set_params(addr, setmode, usemode, p, r)
387	void *addr;
388	int setmode, usemode;
389	struct audio_params *p, *r;
390{
391	if (p->sample_rate < 7500 || p->sample_rate > 8500 ||
392	    p->encoding != AUDIO_ENCODING_ULAW ||
393	    p->precision != 8 ||
394	    p->channels != 1)
395		return EINVAL;
396	p->sample_rate = 8000;	/* no other sampling rates supported by amd chip */
397
398	return 0;
399}
400
401int
402amd7930_query_encoding(addr, fp)
403	void *addr;
404	struct audio_encoding *fp;
405{
406	switch (fp->index) {	/* ??? */
407	    case 0:
408		    strcpy(fp->name, AudioEmulaw);
409		    fp->encoding = AUDIO_ENCODING_ULAW;
410		    fp->precision = 8;
411		    fp->flags = 0;
412		    break;
413	    default:
414		    return(EINVAL);
415		    /*NOTREACHED*/
416	}
417	return(0);
418}
419
420int
421amd7930_round_blocksize(addr, blk)
422	void *addr;
423	int blk;
424{
425	return(blk);
426}
427
428int
429amd7930_commit_settings(addr)
430	void *addr;
431{
432	register struct amd7930_softc *sc = addr;
433	register struct mapreg *map;
434	register volatile struct amd7930 *amd;
435	register int s, level;
436
437	DPRINTF(("sa_commit.\n"));
438
439	map = &sc->sc_map;
440	amd = sc->sc_au.au_amd;
441
442	map->mr_gx = gx_coeff[sc->sc_rlevel];
443	map->mr_stgr = gx_coeff[sc->sc_mlevel];
444
445	level = (sc->sc_plevel * (256 + NGER)) >> 8;
446	if (level >= 256) {
447		map->mr_ger = ger_coeff[level - 256];
448		map->mr_gr = gx_coeff[255];
449	} else {
450		map->mr_ger = ger_coeff[0];
451		map->mr_gr = gx_coeff[level];
452	}
453
454	if (sc->sc_out_port == SUNAUDIO_SPEAKER)
455		map->mr_mmr2 |= AMD_MMR2_LS;
456	else
457		map->mr_mmr2 &= ~AMD_MMR2_LS;
458
459	s = splaudio();
460
461	amd->cr = AMDR_MAP_MMR1;
462	amd->dr = map->mr_mmr1;
463	amd->cr = AMDR_MAP_GX;
464	WAMD16(amd, map->mr_gx);
465	amd->cr = AMDR_MAP_STG;
466	WAMD16(amd, map->mr_stgr);
467	amd->cr = AMDR_MAP_GR;
468	WAMD16(amd, map->mr_gr);
469	amd->cr = AMDR_MAP_GER;
470	WAMD16(amd, map->mr_ger);
471	amd->cr = AMDR_MAP_MMR2;
472	amd->dr = map->mr_mmr2;
473
474	splx(s);
475	return(0);
476}
477
478int
479amd7930_start_output(addr, p, cc, intr, arg)
480	void *addr;
481	void *p;
482	int cc;
483	void (*intr) __P((void *));
484	void *arg;
485{
486	register struct amd7930_softc *sc = addr;
487
488#ifdef AUDIO_DEBUG
489	if (amd7930debug > 1)
490		Dprintf("sa_start_output: cc=%d 0x%x (0x%x)\n", cc, intr, arg);
491#endif
492
493	if (!sc->sc_locked) {
494		register volatile struct amd7930 *amd;
495
496		amd = sc->sc_au.au_amd;
497		amd->cr = AMDR_INIT;
498		amd->dr = AMD_INIT_PMS_ACTIVE;
499		sc->sc_locked = 1;
500		DPRINTF(("sa_start_output: started intrs.\n"));
501	}
502	sc->sc_pintr = intr;
503	sc->sc_parg = arg;
504	sc->sc_au.au_pdata = p;
505	sc->sc_au.au_pend = p + cc - 1;
506	return(0);
507}
508
509/* ARGSUSED */
510int
511amd7930_start_input(addr, p, cc, intr, arg)
512	void *addr;
513	void *p;
514	int cc;
515	void (*intr) __P((void *));
516	void *arg;
517{
518	register struct amd7930_softc *sc = addr;
519
520#ifdef AUDIO_DEBUG
521	if (amd7930debug > 1)
522		Dprintf("sa_start_input: cc=%d 0x%x (0x%x)\n", cc, intr, arg);
523#endif
524
525	if (!sc->sc_locked) {
526		register volatile struct amd7930 *amd;
527
528		amd = sc->sc_au.au_amd;
529		amd->cr = AMDR_INIT;
530		amd->dr = AMD_INIT_PMS_ACTIVE;
531		sc->sc_locked = 1;
532		DPRINTF(("sa_start_input: started intrs.\n"));
533	}
534	sc->sc_rintr = intr;
535	sc->sc_rarg = arg;
536	sc->sc_au.au_rdata = p;
537	sc->sc_au.au_rend = p + cc -1;
538	return(0);
539}
540
541int
542amd7930_halt_output(addr)
543	void *addr;
544{
545	register struct amd7930_softc *sc = addr;
546	register volatile struct amd7930 *amd;
547
548	/* XXX only halt, if input is also halted ?? */
549	amd = sc->sc_au.au_amd;
550	amd->cr = AMDR_INIT;
551	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
552	sc->sc_locked = 0;
553
554	return(0);
555}
556
557int
558amd7930_halt_input(addr)
559	void *addr;
560{
561	register struct amd7930_softc *sc = addr;
562	register volatile struct amd7930 *amd;
563
564	/* XXX only halt, if output is also halted ?? */
565	amd = sc->sc_au.au_amd;
566	amd->cr = AMDR_INIT;
567	amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE;
568	sc->sc_locked = 0;
569
570	return(0);
571}
572
573int
574amd7930_getdev(addr, retp)
575        void *addr;
576        struct audio_device *retp;
577{
578        *retp = amd7930_device;
579        return 0;
580}
581
582int
583amd7930_set_port(addr, cp)
584	void *addr;
585	mixer_ctrl_t *cp;
586{
587	register struct amd7930_softc *sc = addr;
588
589	DPRINTF(("amd7930_set_port: port=%d", cp->dev));
590
591	if (cp->type != AUDIO_MIXER_VALUE || cp->un.value.num_channels != 1)
592		return(EINVAL);
593
594	switch(cp->dev) {
595	    case SUNAUDIO_MIC_PORT:
596		    sc->sc_rlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
597		    break;
598	    case SUNAUDIO_SPEAKER:
599	    case SUNAUDIO_HEADPHONES:
600		    sc->sc_plevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
601		    break;
602	    case SUNAUDIO_MONITOR:
603		    sc->sc_mlevel = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
604		    break;
605	    case SUNAUDIO_SOURCE:
606		    if (cp->un.e.ord != SUNAUDIO_MIC_PORT)
607			    return EINVAL;
608		    break;
609	    case SUNAUDIO_OUTPUT:
610		    if (cp->un.e.ord != SUNAUDIO_SPEAKER &&
611			cp->un.e.ord != SUNAUDIO_HEADPHONES)
612			    return EINVAL;
613			sc->sc_out_port = cp->un.e.ord;
614		    break;
615	    default:
616		    return(EINVAL);
617		    /* NOTREACHED */
618	}
619	return(0);
620}
621
622int
623amd7930_get_port(addr, cp)
624	void *addr;
625	mixer_ctrl_t *cp;
626{
627	register struct amd7930_softc *sc = addr;
628
629	DPRINTF(("amd7930_get_port: port=%d", cp->dev));
630
631	if (cp->type != AUDIO_MIXER_VALUE || cp->un.value.num_channels != 1)
632		return(EINVAL);
633
634	switch(cp->dev) {
635	    case SUNAUDIO_MIC_PORT:
636		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_rlevel;
637		    break;
638	    case SUNAUDIO_SPEAKER:
639	    case SUNAUDIO_HEADPHONES:
640		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_plevel;
641		    break;
642	    case SUNAUDIO_MONITOR:
643		    cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_mlevel;
644		    break;
645	    case SUNAUDIO_SOURCE:
646		    cp->un.e.ord = SUNAUDIO_MIC_PORT;
647		    break;
648	    case SUNAUDIO_OUTPUT:
649		    cp->un.e.ord = sc->sc_out_port;
650		    break;
651	    default:
652		    return(EINVAL);
653		    /* NOTREACHED */
654	}
655	return(0);
656}
657
658int
659amd7930_get_props(addr)
660	void *addr;
661{
662	return AUDIO_PROP_FULLDUPLEX;
663}
664
665int
666amd7930_query_devinfo(addr, dip)
667	void *addr;
668	register mixer_devinfo_t *dip;
669{
670	switch(dip->index) {
671	    case SUNAUDIO_MIC_PORT:
672		    dip->type = AUDIO_MIXER_VALUE;
673		    dip->mixer_class = SUNAUDIO_INPUT_CLASS;
674		    dip->prev = dip->next = AUDIO_MIXER_LAST;
675		    strcpy(dip->label.name, AudioNmicrophone);
676		    dip->un.v.num_channels = 1;
677		    strcpy(dip->un.v.units.name, AudioNvolume);
678		    break;
679	    case SUNAUDIO_SPEAKER:
680		    dip->type = AUDIO_MIXER_VALUE;
681		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
682		    dip->prev = dip->next = AUDIO_MIXER_LAST;
683		    strcpy(dip->label.name, AudioNspeaker);
684		    dip->un.v.num_channels = 1;
685		    strcpy(dip->un.v.units.name, AudioNvolume);
686		    break;
687	    case SUNAUDIO_HEADPHONES:
688		    dip->type = AUDIO_MIXER_VALUE;
689		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
690		    dip->prev = dip->next = AUDIO_MIXER_LAST;
691		    strcpy(dip->label.name, AudioNheadphone);
692		    dip->un.v.num_channels = 1;
693		    strcpy(dip->un.v.units.name, AudioNvolume);
694		    break;
695	    case SUNAUDIO_MONITOR:
696		    dip->type = AUDIO_MIXER_VALUE;
697		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
698		    dip->next = dip->prev = AUDIO_MIXER_LAST;
699		    strcpy(dip->label.name, AudioNmonitor);
700		    dip->un.v.num_channels = 1;
701		    strcpy(dip->un.v.units.name, AudioNvolume);
702		    break;
703	    case SUNAUDIO_SOURCE:
704		    dip->type = AUDIO_MIXER_ENUM;
705		    dip->mixer_class = SUNAUDIO_RECORD_CLASS;
706		    dip->next = dip->prev = AUDIO_MIXER_LAST;
707		    strcpy(dip->label.name, AudioNsource);
708		    dip->un.e.num_mem = 1;
709		    strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
710		    dip->un.e.member[0].ord = SUNAUDIO_MIC_PORT;
711		    break;
712	    case SUNAUDIO_OUTPUT:
713		    dip->type = AUDIO_MIXER_ENUM;
714		    dip->mixer_class = SUNAUDIO_MONITOR_CLASS;
715		    dip->next = dip->prev = AUDIO_MIXER_LAST;
716		    strcpy(dip->label.name, AudioNoutput);
717		    dip->un.e.num_mem = 2;
718		    strcpy(dip->un.e.member[0].label.name, AudioNspeaker);
719		    dip->un.e.member[0].ord = SUNAUDIO_SPEAKER;
720		    strcpy(dip->un.e.member[1].label.name, AudioNheadphone);
721		    dip->un.e.member[1].ord = SUNAUDIO_HEADPHONES;
722		    break;
723	    case SUNAUDIO_INPUT_CLASS:
724		    dip->type = AUDIO_MIXER_CLASS;
725		    dip->mixer_class = SUNAUDIO_INPUT_CLASS;
726		    dip->next = dip->prev = AUDIO_MIXER_LAST;
727		    strcpy(dip->label.name, AudioCinputs);
728		    break;
729	    case SUNAUDIO_OUTPUT_CLASS:
730		    dip->type = AUDIO_MIXER_CLASS;
731		    dip->mixer_class = SUNAUDIO_OUTPUT_CLASS;
732		    dip->next = dip->prev = AUDIO_MIXER_LAST;
733		    strcpy(dip->label.name, AudioCrecord);
734		    break;
735	    case SUNAUDIO_RECORD_CLASS:
736		    dip->type = AUDIO_MIXER_CLASS;
737		    dip->mixer_class = SUNAUDIO_RECORD_CLASS;
738		    dip->next = dip->prev = AUDIO_MIXER_LAST;
739		    strcpy(dip->label.name, AudioCoutputs);
740		    break;
741	    case SUNAUDIO_MONITOR:
742		    dip->type = AUDIO_MIXER_CLASS;
743		    dip->mixer_class = SUNAUDIO_MONITOR_CLASS;
744		    dip->next = dip->prev = AUDIO_MIXER_LAST;
745		    strcpy(dip->label.name, AudioCmonitor);
746		    break;
747	    default:
748		    return ENXIO;
749		    /*NOTREACHED*/
750	}
751
752	DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
753
754	return(0);
755}
756
757#ifdef AUDIO_C_HANDLER
758int
759amd7930hwintr(au0)
760	void *au0;
761{
762	register struct auio *au = au0;
763	register volatile struct amd7930 *amd = au->au_amd;
764	register u_char *d, *e;
765	register int k;
766
767	k = amd->ir;		/* clear interrupt */
768
769	/* receive incoming data */
770	d = au->au_rdata;
771	e = au->au_rend;
772	if (d && d <= e) {
773		*d = amd->bbrb;
774		au->au_rdata++;
775		if (d == e) {
776#ifdef AUDIO_DEBUG
777		        if (amd7930debug > 1)
778                		Dprintf("amd7930hwintr: swintr(r) requested");
779#endif
780			AUDIO_SET_SWINTR;
781		}
782	}
783
784	/* send outgoing data */
785	d = au->au_pdata;
786	e = au->au_pend;
787	if (d && d <= e) {
788		amd->bbtb = *d;
789		au->au_pdata++;
790		if (d == e) {
791#ifdef AUDIO_DEBUG
792		        if (amd7930debug > 1)
793                		Dprintf("amd7930hwintr: swintr(p) requested");
794#endif
795			AUDIO_SET_SWINTR;
796		}
797	}
798
799	*(au->au_intrcnt)++;
800	return (1);
801}
802#endif /* AUDIO_C_HANDLER */
803
804int
805amd7930swintr(sc0)
806	void *sc0;
807{
808	register struct amd7930_softc *sc = sc0;
809	register struct auio *au;
810	register int s, ret = 0;
811
812#ifdef AUDIO_DEBUG
813	if (amd7930debug > 1)
814		Dprintf("audiointr: sc=0x%x\n",sc);
815#endif
816
817	au = &sc->sc_au;
818	s = splaudio();
819	if (au->au_rdata > au->au_rend && sc->sc_rintr != NULL) {
820		splx(s);
821		ret = 1;
822		(*sc->sc_rintr)(sc->sc_rarg);
823		s = splaudio();
824	}
825	if (au->au_pdata > au->au_pend && sc->sc_pintr != NULL) {
826		splx(s);
827		ret = 1;
828		(*sc->sc_pintr)(sc->sc_parg);
829	} else
830		splx(s);
831	return (ret);
832}
833#endif /* NAUDIO > 0 */
834