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