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