xref: /inferno-os/os/ipaq1110/devaudio.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1*74a4d8c2SCharles.Forsyth /*
2*74a4d8c2SCharles.Forsyth  *	SAC/UDA 1341 Audio driver for the Bitsy
3*74a4d8c2SCharles.Forsyth  *
4*74a4d8c2SCharles.Forsyth  *	This code is covered by the Lucent Public Licence 1.02 (http://plan9.bell-labs.com/plan9dist/license.html);
5*74a4d8c2SCharles.Forsyth  *	see the file NOTICE in the current directory.  Modifications for the Inferno environment by Vita Nuova.
6*74a4d8c2SCharles.Forsyth  *
7*74a4d8c2SCharles.Forsyth  *	The Philips UDA 1341 sound chip is accessed through the Serial Audio
8*74a4d8c2SCharles.Forsyth  *	Controller (SAC) of the StrongARM SA-1110.
9*74a4d8c2SCharles.Forsyth  *
10*74a4d8c2SCharles.Forsyth  *	The code morphs Nicolas Pitre's <nico@cam.org> Linux controller
11*74a4d8c2SCharles.Forsyth  *	and Ken's Soundblaster controller.
12*74a4d8c2SCharles.Forsyth  *
13*74a4d8c2SCharles.Forsyth  *	The interface should be identical to that of devaudio.c
14*74a4d8c2SCharles.Forsyth  */
15*74a4d8c2SCharles.Forsyth #include	"u.h"
16*74a4d8c2SCharles.Forsyth #include	"../port/lib.h"
17*74a4d8c2SCharles.Forsyth #include	"mem.h"
18*74a4d8c2SCharles.Forsyth #include	"dat.h"
19*74a4d8c2SCharles.Forsyth #include	"fns.h"
20*74a4d8c2SCharles.Forsyth #include	"../port/error.h"
21*74a4d8c2SCharles.Forsyth #include	"io.h"
22*74a4d8c2SCharles.Forsyth 
23*74a4d8c2SCharles.Forsyth static int debug = 0;
24*74a4d8c2SCharles.Forsyth 
25*74a4d8c2SCharles.Forsyth /* UDA 1341 Registers */
26*74a4d8c2SCharles.Forsyth enum {
27*74a4d8c2SCharles.Forsyth 	/* Status0 register */
28*74a4d8c2SCharles.Forsyth 	UdaStatusDC		= 0,	/* 1 bit */
29*74a4d8c2SCharles.Forsyth 	UdaStatusIF		= 1,	/* 3 bits */
30*74a4d8c2SCharles.Forsyth 	UdaStatusSC		= 4,	/* 2 bits */
31*74a4d8c2SCharles.Forsyth 	UdaStatusRST		= 6,	/* 1 bit */
32*74a4d8c2SCharles.Forsyth };
33*74a4d8c2SCharles.Forsyth 
34*74a4d8c2SCharles.Forsyth enum {
35*74a4d8c2SCharles.Forsyth 	/* Status1 register */
36*74a4d8c2SCharles.Forsyth 	UdaStatusPC	= 0,	/* 2 bits */
37*74a4d8c2SCharles.Forsyth 	UdaStatusDS	= 2,	/* 1 bit */
38*74a4d8c2SCharles.Forsyth 	UdaStatusPDA	= 3,	/* 1 bit */
39*74a4d8c2SCharles.Forsyth 	UdaStatusPAD	= 4,	/* 1 bit */
40*74a4d8c2SCharles.Forsyth 	UdaStatusIGS	= 5,	/* 1 bit */
41*74a4d8c2SCharles.Forsyth 	UdaStatusOGS	= 6,	/* 1 bit */
42*74a4d8c2SCharles.Forsyth };
43*74a4d8c2SCharles.Forsyth 
44*74a4d8c2SCharles.Forsyth /*
45*74a4d8c2SCharles.Forsyth  * UDA1341 L3 address and command types
46*74a4d8c2SCharles.Forsyth  */
47*74a4d8c2SCharles.Forsyth 
48*74a4d8c2SCharles.Forsyth enum {
49*74a4d8c2SCharles.Forsyth 	UDA1341_DATA0 =	0,
50*74a4d8c2SCharles.Forsyth 	UDA1341_DATA1,
51*74a4d8c2SCharles.Forsyth 	UDA1341_STATUS,
52*74a4d8c2SCharles.Forsyth 	UDA1341_L3Addr = 0x14,
53*74a4d8c2SCharles.Forsyth };
54*74a4d8c2SCharles.Forsyth 
55*74a4d8c2SCharles.Forsyth typedef struct	AQueue	AQueue;
56*74a4d8c2SCharles.Forsyth typedef struct	Buf	Buf;
57*74a4d8c2SCharles.Forsyth typedef struct	IOstate IOstate;
58*74a4d8c2SCharles.Forsyth 
59*74a4d8c2SCharles.Forsyth enum
60*74a4d8c2SCharles.Forsyth {
61*74a4d8c2SCharles.Forsyth 	Qdir		= 0,
62*74a4d8c2SCharles.Forsyth 	Qaudio,
63*74a4d8c2SCharles.Forsyth 	Qvolume,
64*74a4d8c2SCharles.Forsyth 	Qstatus,
65*74a4d8c2SCharles.Forsyth 	Qaudioctl,
66*74a4d8c2SCharles.Forsyth 
67*74a4d8c2SCharles.Forsyth 	Fmono		= 1,
68*74a4d8c2SCharles.Forsyth 	Fin			= 2,
69*74a4d8c2SCharles.Forsyth 	Fout		= 4,
70*74a4d8c2SCharles.Forsyth 
71*74a4d8c2SCharles.Forsyth 	Aclosed		= 0,
72*74a4d8c2SCharles.Forsyth 	Aread,
73*74a4d8c2SCharles.Forsyth 	Awrite,
74*74a4d8c2SCharles.Forsyth 
75*74a4d8c2SCharles.Forsyth 	Vaudio		= 0,
76*74a4d8c2SCharles.Forsyth 	Vmic,
77*74a4d8c2SCharles.Forsyth 	Vtreb,
78*74a4d8c2SCharles.Forsyth 	Vbass,
79*74a4d8c2SCharles.Forsyth 	Vspeed,
80*74a4d8c2SCharles.Forsyth 	Vfilter,
81*74a4d8c2SCharles.Forsyth 	Vinvert,
82*74a4d8c2SCharles.Forsyth 	Nvol,
83*74a4d8c2SCharles.Forsyth 
84*74a4d8c2SCharles.Forsyth 	Bufsize		= 4*1024,	/* 46 ms each */
85*74a4d8c2SCharles.Forsyth 	Nbuf		= 32,		/* 1.5 seconds total */
86*74a4d8c2SCharles.Forsyth 
87*74a4d8c2SCharles.Forsyth 	Speed		= 44100,
88*74a4d8c2SCharles.Forsyth 	Ncmd		= 50,		/* max volume command words */
89*74a4d8c2SCharles.Forsyth };
90*74a4d8c2SCharles.Forsyth 
91*74a4d8c2SCharles.Forsyth Dirtab
92*74a4d8c2SCharles.Forsyth audiodir[] =
93*74a4d8c2SCharles.Forsyth {
94*74a4d8c2SCharles.Forsyth 	".",		{Qdir, 0, QTDIR},	0,	0555,
95*74a4d8c2SCharles.Forsyth 	"audio",	{Qaudio},		0,	0666,
96*74a4d8c2SCharles.Forsyth 	"volume",	{Qvolume},		0,	0666,
97*74a4d8c2SCharles.Forsyth 	"audioctl", {Qaudioctl},		0,	0666,
98*74a4d8c2SCharles.Forsyth 	"audiostat",{Qstatus},		0,	0444,
99*74a4d8c2SCharles.Forsyth };
100*74a4d8c2SCharles.Forsyth 
101*74a4d8c2SCharles.Forsyth struct	Buf
102*74a4d8c2SCharles.Forsyth {
103*74a4d8c2SCharles.Forsyth 	uchar*	virt;
104*74a4d8c2SCharles.Forsyth 	ulong	phys;
105*74a4d8c2SCharles.Forsyth 	uint	nbytes;
106*74a4d8c2SCharles.Forsyth };
107*74a4d8c2SCharles.Forsyth 
108*74a4d8c2SCharles.Forsyth struct	IOstate
109*74a4d8c2SCharles.Forsyth {
110*74a4d8c2SCharles.Forsyth 	QLock;
111*74a4d8c2SCharles.Forsyth 	Lock			ilock;
112*74a4d8c2SCharles.Forsyth 	Rendez			vous;
113*74a4d8c2SCharles.Forsyth 	Chan			*chan;			/* chan of open */
114*74a4d8c2SCharles.Forsyth 	Dma*				dma;			/* dma chan, alloc on open, free on close */
115*74a4d8c2SCharles.Forsyth 	int				bufinit;		/* boolean, if buffers allocated */
116*74a4d8c2SCharles.Forsyth 	Buf				buf[Nbuf];		/* buffers and queues */
117*74a4d8c2SCharles.Forsyth 	volatile Buf	*current;		/* next dma to finish */
118*74a4d8c2SCharles.Forsyth 	volatile Buf	*next;			/* next candidate for dma */
119*74a4d8c2SCharles.Forsyth 	volatile Buf	*filling;		/* buffer being filled */
120*74a4d8c2SCharles.Forsyth /* just be be cute (and to have defines like linux, a real operating system) */
121*74a4d8c2SCharles.Forsyth #define emptying filling
122*74a4d8c2SCharles.Forsyth };
123*74a4d8c2SCharles.Forsyth 
124*74a4d8c2SCharles.Forsyth static	struct
125*74a4d8c2SCharles.Forsyth {
126*74a4d8c2SCharles.Forsyth 	QLock;
127*74a4d8c2SCharles.Forsyth 	int		amode;			/* Aclosed/Aread/Awrite for /audio */
128*74a4d8c2SCharles.Forsyth 	int		intr;			/* boolean an interrupt has happened */
129*74a4d8c2SCharles.Forsyth 	int		rivol[Nvol];	/* right/left input/output volumes */
130*74a4d8c2SCharles.Forsyth 	int		livol[Nvol];
131*74a4d8c2SCharles.Forsyth 	int		rovol[Nvol];
132*74a4d8c2SCharles.Forsyth 	int		lovol[Nvol];
133*74a4d8c2SCharles.Forsyth 	uvlong	totcount;		/* how many bytes processed since open */
134*74a4d8c2SCharles.Forsyth 	vlong	tottime;		/* time at which totcount bytes were processed */
135*74a4d8c2SCharles.Forsyth 	int	clockout;	/* need steady output to provide input clock */
136*74a4d8c2SCharles.Forsyth 	IOstate	i;
137*74a4d8c2SCharles.Forsyth 	IOstate	o;
138*74a4d8c2SCharles.Forsyth } audio;
139*74a4d8c2SCharles.Forsyth 
140*74a4d8c2SCharles.Forsyth static struct
141*74a4d8c2SCharles.Forsyth {
142*74a4d8c2SCharles.Forsyth 	ulong	bytes;
143*74a4d8c2SCharles.Forsyth 	ulong	totaldma;
144*74a4d8c2SCharles.Forsyth 	ulong	idledma;
145*74a4d8c2SCharles.Forsyth 	ulong	faildma;
146*74a4d8c2SCharles.Forsyth 	ulong	samedma;
147*74a4d8c2SCharles.Forsyth } iostats;
148*74a4d8c2SCharles.Forsyth 
149*74a4d8c2SCharles.Forsyth static	struct
150*74a4d8c2SCharles.Forsyth {
151*74a4d8c2SCharles.Forsyth 	char*	name;
152*74a4d8c2SCharles.Forsyth 	int	flag;
153*74a4d8c2SCharles.Forsyth 	int	ilval;		/* initial values */
154*74a4d8c2SCharles.Forsyth 	int	irval;
155*74a4d8c2SCharles.Forsyth } volumes[] =
156*74a4d8c2SCharles.Forsyth {
157*74a4d8c2SCharles.Forsyth [Vaudio]	{"audio",	Fout|Fmono,	 80,	 80},
158*74a4d8c2SCharles.Forsyth [Vmic]		{"mic",		Fin|Fmono,	  0,	  0},
159*74a4d8c2SCharles.Forsyth [Vtreb]		{"treb",	Fout|Fmono,	 50,	 50},
160*74a4d8c2SCharles.Forsyth [Vbass]		{"bass",	Fout|Fmono, 	 50,	 50},
161*74a4d8c2SCharles.Forsyth [Vspeed]	{"speed",	Fin|Fout|Fmono,	Speed,	Speed},
162*74a4d8c2SCharles.Forsyth [Vfilter]	{"filter",	Fout|Fmono,	  0,	  0},
163*74a4d8c2SCharles.Forsyth [Vinvert]	{"invert",	Fin|Fout|Fmono,	  0,	  0},
164*74a4d8c2SCharles.Forsyth [Nvol]		{0}
165*74a4d8c2SCharles.Forsyth };
166*74a4d8c2SCharles.Forsyth 
167*74a4d8c2SCharles.Forsyth static void	setreg(char *name, int val, int n);
168*74a4d8c2SCharles.Forsyth 
169*74a4d8c2SCharles.Forsyth static	char	Emode[]		= "illegal open mode";
170*74a4d8c2SCharles.Forsyth static	char	Evolume[]	= "illegal volume specifier";
171*74a4d8c2SCharles.Forsyth 
172*74a4d8c2SCharles.Forsyth static void
bufinit(IOstate * b)173*74a4d8c2SCharles.Forsyth bufinit(IOstate *b)
174*74a4d8c2SCharles.Forsyth {
175*74a4d8c2SCharles.Forsyth 	int i;
176*74a4d8c2SCharles.Forsyth 
177*74a4d8c2SCharles.Forsyth 	if (debug) print("bufinit\n");
178*74a4d8c2SCharles.Forsyth 	for (i = 0; i < Nbuf; i++) {
179*74a4d8c2SCharles.Forsyth 		b->buf[i].virt = xspanalloc(Bufsize, CACHELINESZ, 0);
180*74a4d8c2SCharles.Forsyth 		b->buf[i].phys = PADDR(b->buf[i].virt);
181*74a4d8c2SCharles.Forsyth 	}
182*74a4d8c2SCharles.Forsyth 	b->bufinit = 1;
183*74a4d8c2SCharles.Forsyth };
184*74a4d8c2SCharles.Forsyth 
185*74a4d8c2SCharles.Forsyth static void
setempty(IOstate * b)186*74a4d8c2SCharles.Forsyth setempty(IOstate *b)
187*74a4d8c2SCharles.Forsyth {
188*74a4d8c2SCharles.Forsyth 	int i;
189*74a4d8c2SCharles.Forsyth 
190*74a4d8c2SCharles.Forsyth 	if (debug) print("setempty\n");
191*74a4d8c2SCharles.Forsyth 	for (i = 0; i < Nbuf; i++) {
192*74a4d8c2SCharles.Forsyth 		b->buf[i].nbytes = 0;
193*74a4d8c2SCharles.Forsyth 	}
194*74a4d8c2SCharles.Forsyth 	b->filling = b->buf;
195*74a4d8c2SCharles.Forsyth 	b->current = b->buf;
196*74a4d8c2SCharles.Forsyth 	b->next = b->buf;
197*74a4d8c2SCharles.Forsyth }
198*74a4d8c2SCharles.Forsyth 
199*74a4d8c2SCharles.Forsyth static int
audioqnotempty(void * x)200*74a4d8c2SCharles.Forsyth audioqnotempty(void *x)
201*74a4d8c2SCharles.Forsyth {
202*74a4d8c2SCharles.Forsyth 	IOstate *s = x;
203*74a4d8c2SCharles.Forsyth 
204*74a4d8c2SCharles.Forsyth 	return dmaidle(s->dma) || s->emptying != s->current;
205*74a4d8c2SCharles.Forsyth }
206*74a4d8c2SCharles.Forsyth 
207*74a4d8c2SCharles.Forsyth static int
audioqnotfull(void * x)208*74a4d8c2SCharles.Forsyth audioqnotfull(void *x)
209*74a4d8c2SCharles.Forsyth {
210*74a4d8c2SCharles.Forsyth 	IOstate *s = x;
211*74a4d8c2SCharles.Forsyth 
212*74a4d8c2SCharles.Forsyth 	return dmaidle(s->dma) || s->filling != s->current;
213*74a4d8c2SCharles.Forsyth }
214*74a4d8c2SCharles.Forsyth 
215*74a4d8c2SCharles.Forsyth static void
audioreset(void)216*74a4d8c2SCharles.Forsyth audioreset(void)
217*74a4d8c2SCharles.Forsyth {
218*74a4d8c2SCharles.Forsyth 	/* Turn MCP operations off */
219*74a4d8c2SCharles.Forsyth 	MCPREG->mccr = 0;
220*74a4d8c2SCharles.Forsyth }
221*74a4d8c2SCharles.Forsyth 
222*74a4d8c2SCharles.Forsyth uchar	status0[1]		= {0x22};
223*74a4d8c2SCharles.Forsyth uchar	status1[1]		= {0x80};
224*74a4d8c2SCharles.Forsyth uchar	data00[1]		= {0x00};		/* volume control, bits 0 – 5 */
225*74a4d8c2SCharles.Forsyth uchar	data01[1]		= {0x40};
226*74a4d8c2SCharles.Forsyth uchar	data02[1]		= {0x80};
227*74a4d8c2SCharles.Forsyth uchar	data0e0[2]	= {0xc0, 0xe0};
228*74a4d8c2SCharles.Forsyth uchar	data0e1[2]	= {0xc1, 0xe0};
229*74a4d8c2SCharles.Forsyth uchar	data0e2[2]	= {0xc2, 0xf2};
230*74a4d8c2SCharles.Forsyth /* there is no data0e3 */
231*74a4d8c2SCharles.Forsyth uchar	data0e4[2]	= {0xc4, 0xe0};
232*74a4d8c2SCharles.Forsyth uchar	data0e5[2]	= {0xc5, 0xe0};
233*74a4d8c2SCharles.Forsyth uchar	data0e6[2]	= {0xc6, 0xe3};
234*74a4d8c2SCharles.Forsyth 
235*74a4d8c2SCharles.Forsyth static void
enable(void)236*74a4d8c2SCharles.Forsyth enable(void)
237*74a4d8c2SCharles.Forsyth {
238*74a4d8c2SCharles.Forsyth 	uchar	data[1];
239*74a4d8c2SCharles.Forsyth 	int cs;
240*74a4d8c2SCharles.Forsyth 
241*74a4d8c2SCharles.Forsyth 	L3init();
242*74a4d8c2SCharles.Forsyth 
243*74a4d8c2SCharles.Forsyth 	PPCREG->ppar &= ~PPAR_SPR;
244*74a4d8c2SCharles.Forsyth 
245*74a4d8c2SCharles.Forsyth 	/* external clock and ssp configured for current samples/sec */
246*74a4d8c2SCharles.Forsyth 	cs = archaudiospeed(audio.livol[Vspeed], 1);
247*74a4d8c2SCharles.Forsyth 	status0[0] = (status0[0] & ~(3<<4)) | (cs<<4);
248*74a4d8c2SCharles.Forsyth 
249*74a4d8c2SCharles.Forsyth 	/* Enable the audio power */
250*74a4d8c2SCharles.Forsyth 	archaudiopower(1);
251*74a4d8c2SCharles.Forsyth //	egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 1);
252*74a4d8c2SCharles.Forsyth 
253*74a4d8c2SCharles.Forsyth 	/* Wait for the UDA1341 to wake up */
254*74a4d8c2SCharles.Forsyth 	delay(100);
255*74a4d8c2SCharles.Forsyth 
256*74a4d8c2SCharles.Forsyth 	/* Reset the chip */
257*74a4d8c2SCharles.Forsyth 	data[0] = status0[0] | 1<<UdaStatusRST;
258*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_STATUS, data, 1 );
259*74a4d8c2SCharles.Forsyth 	archcodecreset();
260*74a4d8c2SCharles.Forsyth 
261*74a4d8c2SCharles.Forsyth 	/* write uda 1341 status[0] */
262*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1 );
263*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
264*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
265*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e2, 2);
266*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e6, 2 );
267*74a4d8c2SCharles.Forsyth 
268*74a4d8c2SCharles.Forsyth 	if (debug) {
269*74a4d8c2SCharles.Forsyth 		print("enable:	status0	= 0x%2.2ux\n", status0[0]);
270*74a4d8c2SCharles.Forsyth 		print("enable:	status1	= 0x%2.2ux\n", status1[0]);
271*74a4d8c2SCharles.Forsyth 		print("enable:	data02	= 0x%2.2ux\n", data02[0]);
272*74a4d8c2SCharles.Forsyth 		print("enable:	data0e2	= 0x%4.4ux\n", data0e2[0] | data0e2[1]<<8);
273*74a4d8c2SCharles.Forsyth 		print("enable:	data0e4	= 0x%4.4ux\n", data0e4[0] | data0e4[1]<<8);
274*74a4d8c2SCharles.Forsyth 		print("enable:	data0e6	= 0x%4.4ux\n", data0e6[0] | data0e6[1]<<8);
275*74a4d8c2SCharles.Forsyth 	}
276*74a4d8c2SCharles.Forsyth }
277*74a4d8c2SCharles.Forsyth 
278*74a4d8c2SCharles.Forsyth static void
disable(void)279*74a4d8c2SCharles.Forsyth disable(void)
280*74a4d8c2SCharles.Forsyth {
281*74a4d8c2SCharles.Forsyth 	SSPREG->sscr0 = 0x031f;	/* disable */
282*74a4d8c2SCharles.Forsyth }
283*74a4d8c2SCharles.Forsyth 
284*74a4d8c2SCharles.Forsyth static void
resetlevel(void)285*74a4d8c2SCharles.Forsyth resetlevel(void)
286*74a4d8c2SCharles.Forsyth {
287*74a4d8c2SCharles.Forsyth 	int i;
288*74a4d8c2SCharles.Forsyth 
289*74a4d8c2SCharles.Forsyth 	for(i=0; volumes[i].name; i++) {
290*74a4d8c2SCharles.Forsyth 		audio.lovol[i] = volumes[i].ilval;
291*74a4d8c2SCharles.Forsyth 		audio.rovol[i] = volumes[i].irval;
292*74a4d8c2SCharles.Forsyth 		audio.livol[i] = volumes[i].ilval;
293*74a4d8c2SCharles.Forsyth 		audio.rivol[i] = volumes[i].irval;
294*74a4d8c2SCharles.Forsyth 	}
295*74a4d8c2SCharles.Forsyth }
296*74a4d8c2SCharles.Forsyth 
297*74a4d8c2SCharles.Forsyth static void
mxvolume(void)298*74a4d8c2SCharles.Forsyth mxvolume(void) {
299*74a4d8c2SCharles.Forsyth 	int *left, *right;
300*74a4d8c2SCharles.Forsyth 	int cs;
301*74a4d8c2SCharles.Forsyth 
302*74a4d8c2SCharles.Forsyth 	cs = archaudiospeed(audio.livol[Vspeed], 1);
303*74a4d8c2SCharles.Forsyth 	status0[0] = (status0[0] & ~(3<<4)) | (cs<<4);
304*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1);
305*74a4d8c2SCharles.Forsyth 	if(debug)
306*74a4d8c2SCharles.Forsyth 		print("mxvolume:	status0	= %2.2ux\n", status0[0]);
307*74a4d8c2SCharles.Forsyth 	if(audio.amode & Aread){
308*74a4d8c2SCharles.Forsyth 		left = audio.livol;
309*74a4d8c2SCharles.Forsyth 		right = audio.rivol;
310*74a4d8c2SCharles.Forsyth 		if (left[Vmic]+right[Vmic] == 0) {
311*74a4d8c2SCharles.Forsyth 			/* Turn on automatic gain control (AGC) */
312*74a4d8c2SCharles.Forsyth 			data0e4[1] |= 0x10;
313*74a4d8c2SCharles.Forsyth 			L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
314*74a4d8c2SCharles.Forsyth 		} else {
315*74a4d8c2SCharles.Forsyth 			int v;
316*74a4d8c2SCharles.Forsyth 			/* Turn on manual gain control */
317*74a4d8c2SCharles.Forsyth 			v = ((left[Vmic]+right[Vmic])*0x7f/200)&0x7f;
318*74a4d8c2SCharles.Forsyth 			data0e4[1] &= ~0x13;
319*74a4d8c2SCharles.Forsyth 			data0e5[1] &= ~0x1f;
320*74a4d8c2SCharles.Forsyth 			data0e4[1] |= v & 0x3;
321*74a4d8c2SCharles.Forsyth 			data0e5[0] |= (v & 0x7c)<<6;
322*74a4d8c2SCharles.Forsyth 			data0e5[1] |= (v & 0x7c)>>2;
323*74a4d8c2SCharles.Forsyth 			L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );
324*74a4d8c2SCharles.Forsyth 			L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e5, 2 );
325*74a4d8c2SCharles.Forsyth 		}
326*74a4d8c2SCharles.Forsyth 		if (left[Vinvert]+right[Vinvert] == 0)
327*74a4d8c2SCharles.Forsyth 			status1[0] &= ~0x10;
328*74a4d8c2SCharles.Forsyth 		else
329*74a4d8c2SCharles.Forsyth 			status1[0] |= 0x10;
330*74a4d8c2SCharles.Forsyth 		L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
331*74a4d8c2SCharles.Forsyth 		if (debug) {
332*74a4d8c2SCharles.Forsyth 			print("mxvolume:	status1	= 0x%2.2ux\n", status1[0]);
333*74a4d8c2SCharles.Forsyth 			print("mxvolume:	data0e4	= 0x%4.4ux\n", data0e4[0]|data0e4[0]<<8);
334*74a4d8c2SCharles.Forsyth 			print("mxvolume:	data0e5	= 0x%4.4ux\n", data0e5[0]|data0e5[0]<<8);
335*74a4d8c2SCharles.Forsyth 		}
336*74a4d8c2SCharles.Forsyth 	}
337*74a4d8c2SCharles.Forsyth 	if(audio.amode & Awrite){
338*74a4d8c2SCharles.Forsyth 		left = audio.lovol;
339*74a4d8c2SCharles.Forsyth 		right = audio.rovol;
340*74a4d8c2SCharles.Forsyth 		data00[0] &= ~0x3f;
341*74a4d8c2SCharles.Forsyth 		data00[0] |= ((200-left[Vaudio]-right[Vaudio])*0x3f/200)&0x3f;
342*74a4d8c2SCharles.Forsyth 		if (left[Vtreb]+right[Vtreb] <= 100
343*74a4d8c2SCharles.Forsyth 		 && left[Vbass]+right[Vbass] <= 100)
344*74a4d8c2SCharles.Forsyth 			/* settings neutral */
345*74a4d8c2SCharles.Forsyth 			data02[0] &= ~0x03;
346*74a4d8c2SCharles.Forsyth 		else {
347*74a4d8c2SCharles.Forsyth 			data02[0] |= 0x03;
348*74a4d8c2SCharles.Forsyth 			data01[0] &= ~0x3f;
349*74a4d8c2SCharles.Forsyth 			data01[0] |= ((left[Vtreb]+right[Vtreb]-100)*0x3/100)&0x03;
350*74a4d8c2SCharles.Forsyth 			data01[0] |= (((left[Vbass]+right[Vbass]-100)*0xf/100)&0xf)<<2;
351*74a4d8c2SCharles.Forsyth 		}
352*74a4d8c2SCharles.Forsyth 		if (left[Vfilter]+right[Vfilter] == 0)
353*74a4d8c2SCharles.Forsyth 			data02[0] &= ~0x10;
354*74a4d8c2SCharles.Forsyth 		else
355*74a4d8c2SCharles.Forsyth 			data02[0]|= 0x10;
356*74a4d8c2SCharles.Forsyth 		if (left[Vinvert]+right[Vinvert] == 0)
357*74a4d8c2SCharles.Forsyth 			status1[0] &= ~0x8;
358*74a4d8c2SCharles.Forsyth 		else
359*74a4d8c2SCharles.Forsyth 			status1[0] |= 0x8;
360*74a4d8c2SCharles.Forsyth 		L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
361*74a4d8c2SCharles.Forsyth 		L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
362*74a4d8c2SCharles.Forsyth 		L3write(UDA1341_L3Addr | UDA1341_DATA0, data01, 1);
363*74a4d8c2SCharles.Forsyth 		L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);
364*74a4d8c2SCharles.Forsyth 		if (debug) {
365*74a4d8c2SCharles.Forsyth 			print("mxvolume:	status1	= 0x%2.2ux\n", status1[0]);
366*74a4d8c2SCharles.Forsyth 			print("mxvolume:	data00	= 0x%2.2ux\n", data00[0]);
367*74a4d8c2SCharles.Forsyth 			print("mxvolume:	data01	= 0x%2.2ux\n", data01[0]);
368*74a4d8c2SCharles.Forsyth 			print("mxvolume:	data02	= 0x%2.2ux\n", data02[0]);
369*74a4d8c2SCharles.Forsyth 		}
370*74a4d8c2SCharles.Forsyth 	}
371*74a4d8c2SCharles.Forsyth }
372*74a4d8c2SCharles.Forsyth 
373*74a4d8c2SCharles.Forsyth static void
setreg(char * name,int val,int n)374*74a4d8c2SCharles.Forsyth setreg(char *name, int val, int n)
375*74a4d8c2SCharles.Forsyth {
376*74a4d8c2SCharles.Forsyth 	uchar x[2];
377*74a4d8c2SCharles.Forsyth 	int i;
378*74a4d8c2SCharles.Forsyth 
379*74a4d8c2SCharles.Forsyth 	if(strcmp(name, "pause") == 0){
380*74a4d8c2SCharles.Forsyth 		for(i = 0; i < n; i++)
381*74a4d8c2SCharles.Forsyth 			microdelay(val);
382*74a4d8c2SCharles.Forsyth 		return;
383*74a4d8c2SCharles.Forsyth 	}
384*74a4d8c2SCharles.Forsyth 
385*74a4d8c2SCharles.Forsyth 	x[0] = val;
386*74a4d8c2SCharles.Forsyth 	x[1] = val>>8;
387*74a4d8c2SCharles.Forsyth 
388*74a4d8c2SCharles.Forsyth 	switch(n){
389*74a4d8c2SCharles.Forsyth 	case 1:
390*74a4d8c2SCharles.Forsyth 	case 2:
391*74a4d8c2SCharles.Forsyth 		break;
392*74a4d8c2SCharles.Forsyth 	default:
393*74a4d8c2SCharles.Forsyth 		error("setreg");
394*74a4d8c2SCharles.Forsyth 	}
395*74a4d8c2SCharles.Forsyth 
396*74a4d8c2SCharles.Forsyth 	if(strcmp(name, "status") == 0){
397*74a4d8c2SCharles.Forsyth 		L3write(UDA1341_L3Addr | UDA1341_STATUS, x, n);
398*74a4d8c2SCharles.Forsyth 	} else if(strcmp(name, "data0") == 0){
399*74a4d8c2SCharles.Forsyth 		L3write(UDA1341_L3Addr | UDA1341_DATA0, x, n);
400*74a4d8c2SCharles.Forsyth 	} else if(strcmp(name, "data1") == 0){
401*74a4d8c2SCharles.Forsyth 		L3write(UDA1341_L3Addr | UDA1341_DATA1, x, n);
402*74a4d8c2SCharles.Forsyth 	} else
403*74a4d8c2SCharles.Forsyth 		error("setreg");
404*74a4d8c2SCharles.Forsyth }
405*74a4d8c2SCharles.Forsyth 
406*74a4d8c2SCharles.Forsyth static void
outenable(void)407*74a4d8c2SCharles.Forsyth outenable(void) {
408*74a4d8c2SCharles.Forsyth 	/* turn on DAC, set output gain switch */
409*74a4d8c2SCharles.Forsyth 	archaudioamp(1);
410*74a4d8c2SCharles.Forsyth 	archaudiomute(0);
411*74a4d8c2SCharles.Forsyth 	status1[0] |= 0x41;
412*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
413*74a4d8c2SCharles.Forsyth 	/* set volume */
414*74a4d8c2SCharles.Forsyth 	data00[0] |= 0xf;
415*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);
416*74a4d8c2SCharles.Forsyth 	if (debug) {
417*74a4d8c2SCharles.Forsyth 		print("outenable:	status1	= 0x%2.2ux\n", status1[0]);
418*74a4d8c2SCharles.Forsyth 		print("outenable:	data00	= 0x%2.2ux\n", data00[0]);
419*74a4d8c2SCharles.Forsyth 	}
420*74a4d8c2SCharles.Forsyth }
421*74a4d8c2SCharles.Forsyth 
422*74a4d8c2SCharles.Forsyth static void
outdisable(void)423*74a4d8c2SCharles.Forsyth outdisable(void) {
424*74a4d8c2SCharles.Forsyth 	archaudiomute(1);
425*74a4d8c2SCharles.Forsyth 	dmastop(audio.o.dma);
426*74a4d8c2SCharles.Forsyth 	/* turn off DAC, clear output gain switch */
427*74a4d8c2SCharles.Forsyth 	archaudioamp(0);
428*74a4d8c2SCharles.Forsyth 	status1[0] &= ~0x41;
429*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
430*74a4d8c2SCharles.Forsyth 	if (debug) {
431*74a4d8c2SCharles.Forsyth 		print("outdisable:	status1	= 0x%2.2ux\n", status1[0]);
432*74a4d8c2SCharles.Forsyth 	}
433*74a4d8c2SCharles.Forsyth //	egpiobits(EGPIO_audio_power, 0);
434*74a4d8c2SCharles.Forsyth }
435*74a4d8c2SCharles.Forsyth 
436*74a4d8c2SCharles.Forsyth static void
inenable(void)437*74a4d8c2SCharles.Forsyth inenable(void) {
438*74a4d8c2SCharles.Forsyth 	/* turn on ADC, set input gain switch */
439*74a4d8c2SCharles.Forsyth 	status1[0] |= 0x22;
440*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
441*74a4d8c2SCharles.Forsyth 	if (debug) {
442*74a4d8c2SCharles.Forsyth 		print("inenable:	status1	= 0x%2.2ux\n", status1[0]);
443*74a4d8c2SCharles.Forsyth 	}
444*74a4d8c2SCharles.Forsyth }
445*74a4d8c2SCharles.Forsyth 
446*74a4d8c2SCharles.Forsyth static void
indisable(void)447*74a4d8c2SCharles.Forsyth indisable(void) {
448*74a4d8c2SCharles.Forsyth 	dmastop(audio.i.dma);
449*74a4d8c2SCharles.Forsyth 	/* turn off ADC, clear input gain switch */
450*74a4d8c2SCharles.Forsyth 	status1[0] &= ~0x22;
451*74a4d8c2SCharles.Forsyth 	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
452*74a4d8c2SCharles.Forsyth 	if (debug) {
453*74a4d8c2SCharles.Forsyth 		print("indisable:	status1	= 0x%2.2ux\n", status1[0]);
454*74a4d8c2SCharles.Forsyth 	}
455*74a4d8c2SCharles.Forsyth }
456*74a4d8c2SCharles.Forsyth 
457*74a4d8c2SCharles.Forsyth static void
sendaudio(IOstate * s)458*74a4d8c2SCharles.Forsyth sendaudio(IOstate *s) {
459*74a4d8c2SCharles.Forsyth 	/* interrupt routine calls this too */
460*74a4d8c2SCharles.Forsyth 	int n;
461*74a4d8c2SCharles.Forsyth 
462*74a4d8c2SCharles.Forsyth 	if (debug > 1) print("#A: sendaudio\n");
463*74a4d8c2SCharles.Forsyth 	ilock(&s->ilock);
464*74a4d8c2SCharles.Forsyth 	while (s->next != s->filling) {
465*74a4d8c2SCharles.Forsyth 		assert(s->next->nbytes);
466*74a4d8c2SCharles.Forsyth 		if ((n = dmastart(s->dma, (void*)s->next->phys, s->next->nbytes)) == 0) {
467*74a4d8c2SCharles.Forsyth 			iostats.faildma++;
468*74a4d8c2SCharles.Forsyth 			break;
469*74a4d8c2SCharles.Forsyth 		}
470*74a4d8c2SCharles.Forsyth 		iostats.totaldma++;
471*74a4d8c2SCharles.Forsyth 		switch (n) {
472*74a4d8c2SCharles.Forsyth 		case 1:
473*74a4d8c2SCharles.Forsyth 			iostats.idledma++;
474*74a4d8c2SCharles.Forsyth 			break;
475*74a4d8c2SCharles.Forsyth 		case 3:
476*74a4d8c2SCharles.Forsyth 			iostats.faildma++;
477*74a4d8c2SCharles.Forsyth 			break;
478*74a4d8c2SCharles.Forsyth 		}
479*74a4d8c2SCharles.Forsyth 		if (debug) {
480*74a4d8c2SCharles.Forsyth 			if (debug > 1)
481*74a4d8c2SCharles.Forsyth 				print("dmastart @%p\n", s->next);
482*74a4d8c2SCharles.Forsyth 			else
483*74a4d8c2SCharles.Forsyth 				iprint("+");
484*74a4d8c2SCharles.Forsyth 		}
485*74a4d8c2SCharles.Forsyth 		s->next->nbytes = 0;
486*74a4d8c2SCharles.Forsyth 		s->next++;
487*74a4d8c2SCharles.Forsyth 		if (s->next == &s->buf[Nbuf])
488*74a4d8c2SCharles.Forsyth 			s->next = &s->buf[0];
489*74a4d8c2SCharles.Forsyth 	}
490*74a4d8c2SCharles.Forsyth 	iunlock(&s->ilock);
491*74a4d8c2SCharles.Forsyth }
492*74a4d8c2SCharles.Forsyth 
493*74a4d8c2SCharles.Forsyth static void
recvaudio(IOstate * s)494*74a4d8c2SCharles.Forsyth recvaudio(IOstate *s) {
495*74a4d8c2SCharles.Forsyth 	/* interrupt routine calls this too */
496*74a4d8c2SCharles.Forsyth 	int n;
497*74a4d8c2SCharles.Forsyth 
498*74a4d8c2SCharles.Forsyth 	if (debug > 1) print("#A: recvaudio\n");
499*74a4d8c2SCharles.Forsyth 	ilock(&s->ilock);
500*74a4d8c2SCharles.Forsyth 	while (s->next != s->emptying) {
501*74a4d8c2SCharles.Forsyth 		assert(s->next->nbytes == 0);
502*74a4d8c2SCharles.Forsyth 		if ((n = dmastart(s->dma, (void*)s->next->phys, Bufsize)) == 0) {
503*74a4d8c2SCharles.Forsyth 			iostats.faildma++;
504*74a4d8c2SCharles.Forsyth 			break;
505*74a4d8c2SCharles.Forsyth 		}
506*74a4d8c2SCharles.Forsyth 		iostats.totaldma++;
507*74a4d8c2SCharles.Forsyth 		switch (n) {
508*74a4d8c2SCharles.Forsyth 		case 1:
509*74a4d8c2SCharles.Forsyth 			iostats.idledma++;
510*74a4d8c2SCharles.Forsyth 			break;
511*74a4d8c2SCharles.Forsyth 		case 3:
512*74a4d8c2SCharles.Forsyth 			iostats.faildma++;
513*74a4d8c2SCharles.Forsyth 			break;
514*74a4d8c2SCharles.Forsyth 		}
515*74a4d8c2SCharles.Forsyth 		if (debug) {
516*74a4d8c2SCharles.Forsyth 			if (debug > 1)
517*74a4d8c2SCharles.Forsyth 				print("dmastart @%p\n", s->next);
518*74a4d8c2SCharles.Forsyth 			else
519*74a4d8c2SCharles.Forsyth 				iprint("+");
520*74a4d8c2SCharles.Forsyth 		}
521*74a4d8c2SCharles.Forsyth 		s->next++;
522*74a4d8c2SCharles.Forsyth 		if (s->next == &s->buf[Nbuf])
523*74a4d8c2SCharles.Forsyth 			s->next = &s->buf[0];
524*74a4d8c2SCharles.Forsyth 	}
525*74a4d8c2SCharles.Forsyth 	iunlock(&s->ilock);
526*74a4d8c2SCharles.Forsyth }
527*74a4d8c2SCharles.Forsyth 
528*74a4d8c2SCharles.Forsyth static void
audiopower(int flag)529*74a4d8c2SCharles.Forsyth audiopower(int flag) {
530*74a4d8c2SCharles.Forsyth 	IOstate *s;
531*74a4d8c2SCharles.Forsyth 
532*74a4d8c2SCharles.Forsyth 	if (debug) {
533*74a4d8c2SCharles.Forsyth 		iprint("audiopower %d\n", flag);
534*74a4d8c2SCharles.Forsyth 	}
535*74a4d8c2SCharles.Forsyth 	if (flag) {
536*74a4d8c2SCharles.Forsyth 		/* power on only when necessary */
537*74a4d8c2SCharles.Forsyth 		if (audio.amode) {
538*74a4d8c2SCharles.Forsyth 			archaudiopower(1);
539*74a4d8c2SCharles.Forsyth 			enable();
540*74a4d8c2SCharles.Forsyth 			if (audio.amode & Aread) {
541*74a4d8c2SCharles.Forsyth 				inenable();
542*74a4d8c2SCharles.Forsyth 				s = &audio.i;
543*74a4d8c2SCharles.Forsyth 				dmastop(s->dma);
544*74a4d8c2SCharles.Forsyth 				recvaudio(s);
545*74a4d8c2SCharles.Forsyth 			}
546*74a4d8c2SCharles.Forsyth 			if (audio.amode & Awrite) {
547*74a4d8c2SCharles.Forsyth 				outenable();
548*74a4d8c2SCharles.Forsyth 				s = &audio.o;
549*74a4d8c2SCharles.Forsyth 				dmastop(s->dma);
550*74a4d8c2SCharles.Forsyth 				sendaudio(s);
551*74a4d8c2SCharles.Forsyth 			}
552*74a4d8c2SCharles.Forsyth 			mxvolume();
553*74a4d8c2SCharles.Forsyth 		}
554*74a4d8c2SCharles.Forsyth 	} else {
555*74a4d8c2SCharles.Forsyth 		/* power off */
556*74a4d8c2SCharles.Forsyth 		if (audio.amode & Aread)
557*74a4d8c2SCharles.Forsyth 			indisable();
558*74a4d8c2SCharles.Forsyth 		if (audio.amode & Awrite)
559*74a4d8c2SCharles.Forsyth 			outdisable();
560*74a4d8c2SCharles.Forsyth 		disable();
561*74a4d8c2SCharles.Forsyth 		archaudiopower(0);
562*74a4d8c2SCharles.Forsyth 	}
563*74a4d8c2SCharles.Forsyth }
564*74a4d8c2SCharles.Forsyth 
565*74a4d8c2SCharles.Forsyth static void
audiointr(void * x,ulong ndma)566*74a4d8c2SCharles.Forsyth audiointr(void *x, ulong ndma) {
567*74a4d8c2SCharles.Forsyth 	IOstate *s = x;
568*74a4d8c2SCharles.Forsyth 
569*74a4d8c2SCharles.Forsyth 	if (debug) {
570*74a4d8c2SCharles.Forsyth 		if (debug > 1)
571*74a4d8c2SCharles.Forsyth 			iprint("#A: audio interrupt @%p\n", s->current);
572*74a4d8c2SCharles.Forsyth 		else
573*74a4d8c2SCharles.Forsyth 			iprint("-");
574*74a4d8c2SCharles.Forsyth 	}
575*74a4d8c2SCharles.Forsyth 	/* Only interrupt routine touches s->current */
576*74a4d8c2SCharles.Forsyth 	s->current++;
577*74a4d8c2SCharles.Forsyth 	if (s->current == &s->buf[Nbuf])
578*74a4d8c2SCharles.Forsyth 		s->current = &s->buf[0];
579*74a4d8c2SCharles.Forsyth 	if (ndma > 0) {
580*74a4d8c2SCharles.Forsyth 		if (s == &audio.o)
581*74a4d8c2SCharles.Forsyth 			sendaudio(s);
582*74a4d8c2SCharles.Forsyth 		else if (s == &audio.i)
583*74a4d8c2SCharles.Forsyth 			recvaudio(s);
584*74a4d8c2SCharles.Forsyth 	}
585*74a4d8c2SCharles.Forsyth 	wakeup(&s->vous);
586*74a4d8c2SCharles.Forsyth }
587*74a4d8c2SCharles.Forsyth 
588*74a4d8c2SCharles.Forsyth static void
audioinit(void)589*74a4d8c2SCharles.Forsyth audioinit(void)
590*74a4d8c2SCharles.Forsyth {
591*74a4d8c2SCharles.Forsyth 	audio.amode = Aclosed;
592*74a4d8c2SCharles.Forsyth 	resetlevel();
593*74a4d8c2SCharles.Forsyth //	powerenable(audiopower);
594*74a4d8c2SCharles.Forsyth }
595*74a4d8c2SCharles.Forsyth 
596*74a4d8c2SCharles.Forsyth static Chan*
audioattach(char * param)597*74a4d8c2SCharles.Forsyth audioattach(char *param)
598*74a4d8c2SCharles.Forsyth {
599*74a4d8c2SCharles.Forsyth 	return devattach('A', param);
600*74a4d8c2SCharles.Forsyth }
601*74a4d8c2SCharles.Forsyth 
602*74a4d8c2SCharles.Forsyth static Walkqid*
audiowalk(Chan * c,Chan * nc,char ** name,int nname)603*74a4d8c2SCharles.Forsyth audiowalk(Chan *c, Chan *nc, char **name, int nname)
604*74a4d8c2SCharles.Forsyth {
605*74a4d8c2SCharles.Forsyth 	return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
606*74a4d8c2SCharles.Forsyth }
607*74a4d8c2SCharles.Forsyth 
608*74a4d8c2SCharles.Forsyth static int
audiostat(Chan * c,uchar * db,int n)609*74a4d8c2SCharles.Forsyth audiostat(Chan *c, uchar *db, int n)
610*74a4d8c2SCharles.Forsyth {
611*74a4d8c2SCharles.Forsyth 	return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
612*74a4d8c2SCharles.Forsyth }
613*74a4d8c2SCharles.Forsyth 
614*74a4d8c2SCharles.Forsyth static Chan*
audioopen(Chan * c,int mode)615*74a4d8c2SCharles.Forsyth audioopen(Chan *c, int mode)
616*74a4d8c2SCharles.Forsyth {
617*74a4d8c2SCharles.Forsyth 	IOstate *s;
618*74a4d8c2SCharles.Forsyth 	int omode = mode;
619*74a4d8c2SCharles.Forsyth 
620*74a4d8c2SCharles.Forsyth 	switch((ulong)c->qid.path) {
621*74a4d8c2SCharles.Forsyth 	default:
622*74a4d8c2SCharles.Forsyth 		error(Eperm);
623*74a4d8c2SCharles.Forsyth 		break;
624*74a4d8c2SCharles.Forsyth 
625*74a4d8c2SCharles.Forsyth 	case Qstatus:
626*74a4d8c2SCharles.Forsyth 		if((omode&7) != OREAD)
627*74a4d8c2SCharles.Forsyth 			error(Eperm);
628*74a4d8c2SCharles.Forsyth 	case Qvolume:
629*74a4d8c2SCharles.Forsyth 	case Qaudioctl:
630*74a4d8c2SCharles.Forsyth 	case Qdir:
631*74a4d8c2SCharles.Forsyth 		break;
632*74a4d8c2SCharles.Forsyth 
633*74a4d8c2SCharles.Forsyth 	case Qaudio:
634*74a4d8c2SCharles.Forsyth 		omode = (omode & 0x7) + 1;
635*74a4d8c2SCharles.Forsyth 		if (omode & ~(Aread | Awrite))
636*74a4d8c2SCharles.Forsyth 			error(Ebadarg);
637*74a4d8c2SCharles.Forsyth 		qlock(&audio);
638*74a4d8c2SCharles.Forsyth 		if(audio.amode & omode){
639*74a4d8c2SCharles.Forsyth 			qunlock(&audio);
640*74a4d8c2SCharles.Forsyth 			error(Einuse);
641*74a4d8c2SCharles.Forsyth 		}
642*74a4d8c2SCharles.Forsyth 		enable();
643*74a4d8c2SCharles.Forsyth 		memset(&iostats, 0, sizeof(iostats));
644*74a4d8c2SCharles.Forsyth 		if (omode & Aread) {
645*74a4d8c2SCharles.Forsyth 			inenable();
646*74a4d8c2SCharles.Forsyth 			s = &audio.i;
647*74a4d8c2SCharles.Forsyth 			if(s->bufinit == 0)
648*74a4d8c2SCharles.Forsyth 				bufinit(s);
649*74a4d8c2SCharles.Forsyth 			setempty(s);
650*74a4d8c2SCharles.Forsyth 			s->emptying = &s->buf[Nbuf-1];
651*74a4d8c2SCharles.Forsyth 			s->chan = c;
652*74a4d8c2SCharles.Forsyth 			s->dma = dmasetup(DmaSSP, 1, 0, audiointr, (void*)s);
653*74a4d8c2SCharles.Forsyth 			audio.amode |= Aread;
654*74a4d8c2SCharles.Forsyth 			audio.clockout = 1;
655*74a4d8c2SCharles.Forsyth 		}
656*74a4d8c2SCharles.Forsyth 		if (omode & Awrite) {
657*74a4d8c2SCharles.Forsyth 			outenable();
658*74a4d8c2SCharles.Forsyth 			s = &audio.o;
659*74a4d8c2SCharles.Forsyth 			audio.amode |= Awrite;
660*74a4d8c2SCharles.Forsyth 			if(s->bufinit == 0)
661*74a4d8c2SCharles.Forsyth 				bufinit(s);
662*74a4d8c2SCharles.Forsyth 			setempty(s);
663*74a4d8c2SCharles.Forsyth 			s->chan = c;
664*74a4d8c2SCharles.Forsyth 			s->dma = dmasetup(DmaSSP, 0, 0, audiointr, (void*)s);
665*74a4d8c2SCharles.Forsyth 			audio.amode |= Awrite;
666*74a4d8c2SCharles.Forsyth 		}
667*74a4d8c2SCharles.Forsyth 		mxvolume();
668*74a4d8c2SCharles.Forsyth 		qunlock(&audio);
669*74a4d8c2SCharles.Forsyth 		if (debug) print("open done\n");
670*74a4d8c2SCharles.Forsyth 		break;
671*74a4d8c2SCharles.Forsyth 	}
672*74a4d8c2SCharles.Forsyth 	c = devopen(c, mode, audiodir, nelem(audiodir), devgen);
673*74a4d8c2SCharles.Forsyth 	c->mode = openmode(mode);
674*74a4d8c2SCharles.Forsyth 	c->flag |= COPEN;
675*74a4d8c2SCharles.Forsyth 	c->offset = 0;
676*74a4d8c2SCharles.Forsyth 
677*74a4d8c2SCharles.Forsyth 	return c;
678*74a4d8c2SCharles.Forsyth }
679*74a4d8c2SCharles.Forsyth 
680*74a4d8c2SCharles.Forsyth static void
audioclose(Chan * c)681*74a4d8c2SCharles.Forsyth audioclose(Chan *c)
682*74a4d8c2SCharles.Forsyth {
683*74a4d8c2SCharles.Forsyth 	IOstate *s;
684*74a4d8c2SCharles.Forsyth 
685*74a4d8c2SCharles.Forsyth 	switch((ulong)c->qid.path) {
686*74a4d8c2SCharles.Forsyth 	default:
687*74a4d8c2SCharles.Forsyth 		error(Eperm);
688*74a4d8c2SCharles.Forsyth 		break;
689*74a4d8c2SCharles.Forsyth 
690*74a4d8c2SCharles.Forsyth 	case Qdir:
691*74a4d8c2SCharles.Forsyth 	case Qvolume:
692*74a4d8c2SCharles.Forsyth 	case Qaudioctl:
693*74a4d8c2SCharles.Forsyth 	case Qstatus:
694*74a4d8c2SCharles.Forsyth 		break;
695*74a4d8c2SCharles.Forsyth 
696*74a4d8c2SCharles.Forsyth 	case Qaudio:
697*74a4d8c2SCharles.Forsyth 		if (debug > 1) print("#A: close\n");
698*74a4d8c2SCharles.Forsyth 		if(c->flag & COPEN) {
699*74a4d8c2SCharles.Forsyth 			qlock(&audio);
700*74a4d8c2SCharles.Forsyth 			if(waserror()){
701*74a4d8c2SCharles.Forsyth 				qunlock(&audio);
702*74a4d8c2SCharles.Forsyth 				nexterror();
703*74a4d8c2SCharles.Forsyth 			}
704*74a4d8c2SCharles.Forsyth 			if (audio.o.chan == c) {
705*74a4d8c2SCharles.Forsyth 				/* closing the write end */
706*74a4d8c2SCharles.Forsyth 				audio.amode &= ~Awrite;
707*74a4d8c2SCharles.Forsyth 				s = &audio.o;
708*74a4d8c2SCharles.Forsyth 				qlock(s);
709*74a4d8c2SCharles.Forsyth 				if(waserror()){
710*74a4d8c2SCharles.Forsyth 					qunlock(s);
711*74a4d8c2SCharles.Forsyth 					nexterror();
712*74a4d8c2SCharles.Forsyth 				}
713*74a4d8c2SCharles.Forsyth 				if (s->filling->nbytes) {
714*74a4d8c2SCharles.Forsyth 					/* send remaining partial buffer */
715*74a4d8c2SCharles.Forsyth 					s->filling++;
716*74a4d8c2SCharles.Forsyth 					if (s->filling == &s->buf[Nbuf])
717*74a4d8c2SCharles.Forsyth 						s->filling = &s->buf[0];
718*74a4d8c2SCharles.Forsyth 					sendaudio(s);
719*74a4d8c2SCharles.Forsyth 				}
720*74a4d8c2SCharles.Forsyth 				dmawait(s->dma);
721*74a4d8c2SCharles.Forsyth 				outdisable();
722*74a4d8c2SCharles.Forsyth 				setempty(s);
723*74a4d8c2SCharles.Forsyth 				dmafree(s->dma);
724*74a4d8c2SCharles.Forsyth 				qunlock(s);
725*74a4d8c2SCharles.Forsyth 				poperror();
726*74a4d8c2SCharles.Forsyth 			}
727*74a4d8c2SCharles.Forsyth 			if (audio.i.chan == c) {
728*74a4d8c2SCharles.Forsyth 				/* closing the read end */
729*74a4d8c2SCharles.Forsyth 				audio.amode &= ~Aread;
730*74a4d8c2SCharles.Forsyth 				s = &audio.i;
731*74a4d8c2SCharles.Forsyth 				qlock(s);
732*74a4d8c2SCharles.Forsyth 				if(waserror()){
733*74a4d8c2SCharles.Forsyth 					qunlock(s);
734*74a4d8c2SCharles.Forsyth 					nexterror();
735*74a4d8c2SCharles.Forsyth 				}
736*74a4d8c2SCharles.Forsyth 				indisable();
737*74a4d8c2SCharles.Forsyth 				setempty(s);
738*74a4d8c2SCharles.Forsyth 				dmafree(s->dma);
739*74a4d8c2SCharles.Forsyth 				qunlock(s);
740*74a4d8c2SCharles.Forsyth 				poperror();
741*74a4d8c2SCharles.Forsyth 			}
742*74a4d8c2SCharles.Forsyth 			if (audio.amode == 0) {
743*74a4d8c2SCharles.Forsyth 				/* turn audio off */
744*74a4d8c2SCharles.Forsyth 				archaudiopower(0);
745*74a4d8c2SCharles.Forsyth 			}
746*74a4d8c2SCharles.Forsyth 			qunlock(&audio);
747*74a4d8c2SCharles.Forsyth 			poperror();
748*74a4d8c2SCharles.Forsyth 			if (debug) {
749*74a4d8c2SCharles.Forsyth 				print("total dmas: %lud\n", iostats.totaldma);
750*74a4d8c2SCharles.Forsyth 				print("dmas while idle: %lud\n", iostats.idledma);
751*74a4d8c2SCharles.Forsyth 				print("dmas while busy: %lud\n", iostats.faildma);
752*74a4d8c2SCharles.Forsyth 				print("out of order dma: %lud\n", iostats.samedma);
753*74a4d8c2SCharles.Forsyth 			}
754*74a4d8c2SCharles.Forsyth 		}
755*74a4d8c2SCharles.Forsyth 		break;
756*74a4d8c2SCharles.Forsyth 	}
757*74a4d8c2SCharles.Forsyth }
758*74a4d8c2SCharles.Forsyth 
759*74a4d8c2SCharles.Forsyth static long
audioread(Chan * c,void * v,long n,vlong off)760*74a4d8c2SCharles.Forsyth audioread(Chan *c, void *v, long n, vlong off)
761*74a4d8c2SCharles.Forsyth {
762*74a4d8c2SCharles.Forsyth 	int liv, riv, lov, rov;
763*74a4d8c2SCharles.Forsyth 	long m, n0;
764*74a4d8c2SCharles.Forsyth 	char buf[300];
765*74a4d8c2SCharles.Forsyth 	int j;
766*74a4d8c2SCharles.Forsyth 	ulong offset = off;
767*74a4d8c2SCharles.Forsyth 	char *p;
768*74a4d8c2SCharles.Forsyth 	IOstate *s;
769*74a4d8c2SCharles.Forsyth 
770*74a4d8c2SCharles.Forsyth 	n0 = n;
771*74a4d8c2SCharles.Forsyth 	p = v;
772*74a4d8c2SCharles.Forsyth 	switch((ulong)c->qid.path) {
773*74a4d8c2SCharles.Forsyth 	default:
774*74a4d8c2SCharles.Forsyth 		error(Eperm);
775*74a4d8c2SCharles.Forsyth 		break;
776*74a4d8c2SCharles.Forsyth 
777*74a4d8c2SCharles.Forsyth 	case Qdir:
778*74a4d8c2SCharles.Forsyth 		return devdirread(c, p, n, audiodir, nelem(audiodir), devgen);
779*74a4d8c2SCharles.Forsyth 
780*74a4d8c2SCharles.Forsyth 	case Qaudio:
781*74a4d8c2SCharles.Forsyth 		if (debug > 1) print("#A: read %ld\n", n);
782*74a4d8c2SCharles.Forsyth 		if((audio.amode & Aread) == 0)
783*74a4d8c2SCharles.Forsyth 			error(Emode);
784*74a4d8c2SCharles.Forsyth 		s = &audio.i;
785*74a4d8c2SCharles.Forsyth 		qlock(s);
786*74a4d8c2SCharles.Forsyth 		if(waserror()){
787*74a4d8c2SCharles.Forsyth 			qunlock(s);
788*74a4d8c2SCharles.Forsyth 			nexterror();
789*74a4d8c2SCharles.Forsyth 		}
790*74a4d8c2SCharles.Forsyth 		while(n > 0) {
791*74a4d8c2SCharles.Forsyth 			if(s->emptying->nbytes == 0) {
792*74a4d8c2SCharles.Forsyth 				if (debug > 1) print("#A: emptied @%p\n", s->emptying);
793*74a4d8c2SCharles.Forsyth 				recvaudio(s);
794*74a4d8c2SCharles.Forsyth 				s->emptying++;
795*74a4d8c2SCharles.Forsyth 				if (s->emptying == &s->buf[Nbuf])
796*74a4d8c2SCharles.Forsyth 					s->emptying = s->buf;
797*74a4d8c2SCharles.Forsyth 			}
798*74a4d8c2SCharles.Forsyth 			/* wait if dma in progress */
799*74a4d8c2SCharles.Forsyth 			while (!dmaidle(s->dma) && s->emptying == s->current) {
800*74a4d8c2SCharles.Forsyth 				if (debug > 1) print("#A: sleep\n");
801*74a4d8c2SCharles.Forsyth 				sleep(&s->vous, audioqnotempty, s);
802*74a4d8c2SCharles.Forsyth 			}
803*74a4d8c2SCharles.Forsyth 
804*74a4d8c2SCharles.Forsyth 			m = Bufsize - s->emptying->nbytes;
805*74a4d8c2SCharles.Forsyth 			if(m > n)
806*74a4d8c2SCharles.Forsyth 				m = n;
807*74a4d8c2SCharles.Forsyth 			memmove(p, s->emptying->virt + s->emptying->nbytes, m);
808*74a4d8c2SCharles.Forsyth 
809*74a4d8c2SCharles.Forsyth 			s->emptying->nbytes -= m;
810*74a4d8c2SCharles.Forsyth 			n -= m;
811*74a4d8c2SCharles.Forsyth 			p += m;
812*74a4d8c2SCharles.Forsyth 		}
813*74a4d8c2SCharles.Forsyth 		poperror();
814*74a4d8c2SCharles.Forsyth 		qunlock(s);
815*74a4d8c2SCharles.Forsyth 		break;
816*74a4d8c2SCharles.Forsyth 		break;
817*74a4d8c2SCharles.Forsyth 
818*74a4d8c2SCharles.Forsyth 	case Qstatus:
819*74a4d8c2SCharles.Forsyth 		buf[0] = 0;
820*74a4d8c2SCharles.Forsyth 		snprint(buf, sizeof(buf), "bytes %llud\ntime %lld\n",
821*74a4d8c2SCharles.Forsyth 			audio.totcount, audio.tottime);
822*74a4d8c2SCharles.Forsyth 		return readstr(offset, p, n, buf);
823*74a4d8c2SCharles.Forsyth 
824*74a4d8c2SCharles.Forsyth 	case Qvolume:
825*74a4d8c2SCharles.Forsyth 	case Qaudioctl:
826*74a4d8c2SCharles.Forsyth 		j = 0;
827*74a4d8c2SCharles.Forsyth 		buf[0] = 0;
828*74a4d8c2SCharles.Forsyth 		for(m=0; volumes[m].name; m++){
829*74a4d8c2SCharles.Forsyth 			liv = audio.livol[m];
830*74a4d8c2SCharles.Forsyth 			riv = audio.rivol[m];
831*74a4d8c2SCharles.Forsyth 			lov = audio.lovol[m];
832*74a4d8c2SCharles.Forsyth 			rov = audio.rovol[m];
833*74a4d8c2SCharles.Forsyth 			j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
834*74a4d8c2SCharles.Forsyth 			if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
835*74a4d8c2SCharles.Forsyth 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
836*74a4d8c2SCharles.Forsyth 					j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
837*74a4d8c2SCharles.Forsyth 				else{
838*74a4d8c2SCharles.Forsyth 					if(volumes[m].flag & Fin)
839*74a4d8c2SCharles.Forsyth 						j += snprint(buf+j, sizeof(buf)-j,
840*74a4d8c2SCharles.Forsyth 							" in %d", liv);
841*74a4d8c2SCharles.Forsyth 					if(volumes[m].flag & Fout)
842*74a4d8c2SCharles.Forsyth 						j += snprint(buf+j, sizeof(buf)-j,
843*74a4d8c2SCharles.Forsyth 							" out %d", lov);
844*74a4d8c2SCharles.Forsyth 				}
845*74a4d8c2SCharles.Forsyth 			}else{
846*74a4d8c2SCharles.Forsyth 				if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
847*74a4d8c2SCharles.Forsyth 				    liv==lov && riv==rov)
848*74a4d8c2SCharles.Forsyth 					j += snprint(buf+j, sizeof(buf)-j,
849*74a4d8c2SCharles.Forsyth 						" left %d right %d",
850*74a4d8c2SCharles.Forsyth 						liv, riv);
851*74a4d8c2SCharles.Forsyth 				else{
852*74a4d8c2SCharles.Forsyth 					if(volumes[m].flag & Fin)
853*74a4d8c2SCharles.Forsyth 						j += snprint(buf+j, sizeof(buf)-j,
854*74a4d8c2SCharles.Forsyth 							" in left %d right %d",
855*74a4d8c2SCharles.Forsyth 							liv, riv);
856*74a4d8c2SCharles.Forsyth 					if(volumes[m].flag & Fout)
857*74a4d8c2SCharles.Forsyth 						j += snprint(buf+j, sizeof(buf)-j,
858*74a4d8c2SCharles.Forsyth 							" out left %d right %d",
859*74a4d8c2SCharles.Forsyth 							lov, rov);
860*74a4d8c2SCharles.Forsyth 				}
861*74a4d8c2SCharles.Forsyth 			}
862*74a4d8c2SCharles.Forsyth 			j += snprint(buf+j, sizeof(buf)-j, "\n");
863*74a4d8c2SCharles.Forsyth 		}
864*74a4d8c2SCharles.Forsyth 		return readstr(offset, p, n, buf);
865*74a4d8c2SCharles.Forsyth 	}
866*74a4d8c2SCharles.Forsyth 	return n0-n;
867*74a4d8c2SCharles.Forsyth }
868*74a4d8c2SCharles.Forsyth 
869*74a4d8c2SCharles.Forsyth static long
audiowrite(Chan * c,void * vp,long n,vlong)870*74a4d8c2SCharles.Forsyth audiowrite(Chan *c, void *vp, long n, vlong)
871*74a4d8c2SCharles.Forsyth {
872*74a4d8c2SCharles.Forsyth 	long m, n0;
873*74a4d8c2SCharles.Forsyth 	int i, nf, v, left, right, in, out;
874*74a4d8c2SCharles.Forsyth 	char buf[255], *field[Ncmd];
875*74a4d8c2SCharles.Forsyth 	char *p;
876*74a4d8c2SCharles.Forsyth 	IOstate *a;
877*74a4d8c2SCharles.Forsyth 
878*74a4d8c2SCharles.Forsyth 	p = vp;
879*74a4d8c2SCharles.Forsyth 	n0 = n;
880*74a4d8c2SCharles.Forsyth 	switch((ulong)c->qid.path) {
881*74a4d8c2SCharles.Forsyth 	default:
882*74a4d8c2SCharles.Forsyth 		error(Eperm);
883*74a4d8c2SCharles.Forsyth 		break;
884*74a4d8c2SCharles.Forsyth 
885*74a4d8c2SCharles.Forsyth 	case Qvolume:
886*74a4d8c2SCharles.Forsyth 	case Qaudioctl:
887*74a4d8c2SCharles.Forsyth 		v = Vaudio;
888*74a4d8c2SCharles.Forsyth 		left = 1;
889*74a4d8c2SCharles.Forsyth 		right = 1;
890*74a4d8c2SCharles.Forsyth 		in = 1;
891*74a4d8c2SCharles.Forsyth 		out = 1;
892*74a4d8c2SCharles.Forsyth 		if(n > sizeof(buf)-1)
893*74a4d8c2SCharles.Forsyth 			n = sizeof(buf)-1;
894*74a4d8c2SCharles.Forsyth 		memmove(buf, p, n);
895*74a4d8c2SCharles.Forsyth 		buf[n] = '\0';
896*74a4d8c2SCharles.Forsyth 		n = 0;
897*74a4d8c2SCharles.Forsyth 
898*74a4d8c2SCharles.Forsyth 		nf = getfields(buf, field, Ncmd, 1, " \t\n");
899*74a4d8c2SCharles.Forsyth 		for(i = 0; i < nf; i++){
900*74a4d8c2SCharles.Forsyth 			/*
901*74a4d8c2SCharles.Forsyth 			 * a number is volume
902*74a4d8c2SCharles.Forsyth 			 */
903*74a4d8c2SCharles.Forsyth 			if(field[i][0] >= '0' && field[i][0] <= '9') {
904*74a4d8c2SCharles.Forsyth 				m = strtoul(field[i], 0, 10);
905*74a4d8c2SCharles.Forsyth 				if(v == Vspeed){
906*74a4d8c2SCharles.Forsyth 					if(archaudiospeed(m, 0) < 0)
907*74a4d8c2SCharles.Forsyth 						error(Evolume);
908*74a4d8c2SCharles.Forsyth 				}else
909*74a4d8c2SCharles.Forsyth 					if(m < 0 || m > 100)
910*74a4d8c2SCharles.Forsyth 						error(Evolume);
911*74a4d8c2SCharles.Forsyth 				if(left && out)
912*74a4d8c2SCharles.Forsyth 					audio.lovol[v] = m;
913*74a4d8c2SCharles.Forsyth 				if(left && in)
914*74a4d8c2SCharles.Forsyth 					audio.livol[v] = m;
915*74a4d8c2SCharles.Forsyth 				if(right && out)
916*74a4d8c2SCharles.Forsyth 					audio.rovol[v] = m;
917*74a4d8c2SCharles.Forsyth 				if(right && in)
918*74a4d8c2SCharles.Forsyth 					audio.rivol[v] = m;
919*74a4d8c2SCharles.Forsyth 				goto cont0;
920*74a4d8c2SCharles.Forsyth 			}
921*74a4d8c2SCharles.Forsyth 			if(strcmp(field[i], "rate") == 0)
922*74a4d8c2SCharles.Forsyth 				field[i] = "speed";	/* honestly ... */
923*74a4d8c2SCharles.Forsyth 
924*74a4d8c2SCharles.Forsyth 			for(m=0; volumes[m].name; m++) {
925*74a4d8c2SCharles.Forsyth 				if(strcmp(field[i], volumes[m].name) == 0) {
926*74a4d8c2SCharles.Forsyth 					v = m;
927*74a4d8c2SCharles.Forsyth 					in = 1;
928*74a4d8c2SCharles.Forsyth 					out = 1;
929*74a4d8c2SCharles.Forsyth 					left = 1;
930*74a4d8c2SCharles.Forsyth 					right = 1;
931*74a4d8c2SCharles.Forsyth 					goto cont0;
932*74a4d8c2SCharles.Forsyth 				}
933*74a4d8c2SCharles.Forsyth 			}
934*74a4d8c2SCharles.Forsyth 			if(strcmp(field[i], "enc") == 0) {
935*74a4d8c2SCharles.Forsyth 				if(++i >= nf)
936*74a4d8c2SCharles.Forsyth 					error(Evolume);
937*74a4d8c2SCharles.Forsyth 				if(strcmp(field[i], "pcm") != 0)
938*74a4d8c2SCharles.Forsyth 					error(Evolume);
939*74a4d8c2SCharles.Forsyth 				goto cont0;
940*74a4d8c2SCharles.Forsyth 			}
941*74a4d8c2SCharles.Forsyth 			if(strcmp(field[i], "bits") == 0) {
942*74a4d8c2SCharles.Forsyth 				if(++i >= nf)
943*74a4d8c2SCharles.Forsyth 					error(Evolume);
944*74a4d8c2SCharles.Forsyth 				if(strtol(field[i], 0, 0) != 16)
945*74a4d8c2SCharles.Forsyth 					error(Evolume);
946*74a4d8c2SCharles.Forsyth 				goto cont0;
947*74a4d8c2SCharles.Forsyth 			}
948*74a4d8c2SCharles.Forsyth 			if(strcmp(field[i], "chans") == 0) {
949*74a4d8c2SCharles.Forsyth 				if(++i >= nf)
950*74a4d8c2SCharles.Forsyth 					error(Evolume);
951*74a4d8c2SCharles.Forsyth 				if(strtol(field[i], 0, 0) != 2)
952*74a4d8c2SCharles.Forsyth 					error(Evolume);
953*74a4d8c2SCharles.Forsyth 				goto cont0;
954*74a4d8c2SCharles.Forsyth 			}
955*74a4d8c2SCharles.Forsyth 			if(strcmp(field[i], "reset") == 0) {
956*74a4d8c2SCharles.Forsyth 				resetlevel();
957*74a4d8c2SCharles.Forsyth 				goto cont0;
958*74a4d8c2SCharles.Forsyth 			}
959*74a4d8c2SCharles.Forsyth 			if(strcmp(field[i], "debug") == 0) {
960*74a4d8c2SCharles.Forsyth 				debug = debug?0:1;
961*74a4d8c2SCharles.Forsyth 				goto cont0;
962*74a4d8c2SCharles.Forsyth 			}
963*74a4d8c2SCharles.Forsyth 			if(strcmp(field[i], "in") == 0) {
964*74a4d8c2SCharles.Forsyth 				in = 1;
965*74a4d8c2SCharles.Forsyth 				out = 0;
966*74a4d8c2SCharles.Forsyth 				goto cont0;
967*74a4d8c2SCharles.Forsyth 			}
968*74a4d8c2SCharles.Forsyth 			if(strcmp(field[i], "out") == 0) {
969*74a4d8c2SCharles.Forsyth 				in = 0;
970*74a4d8c2SCharles.Forsyth 				out = 1;
971*74a4d8c2SCharles.Forsyth 				goto cont0;
972*74a4d8c2SCharles.Forsyth 			}
973*74a4d8c2SCharles.Forsyth 			if(strcmp(field[i], "left") == 0) {
974*74a4d8c2SCharles.Forsyth 				left = 1;
975*74a4d8c2SCharles.Forsyth 				right = 0;
976*74a4d8c2SCharles.Forsyth 				goto cont0;
977*74a4d8c2SCharles.Forsyth 			}
978*74a4d8c2SCharles.Forsyth 			if(strcmp(field[i], "right") == 0) {
979*74a4d8c2SCharles.Forsyth 				left = 0;
980*74a4d8c2SCharles.Forsyth 				right = 1;
981*74a4d8c2SCharles.Forsyth 				goto cont0;
982*74a4d8c2SCharles.Forsyth 			}
983*74a4d8c2SCharles.Forsyth 			if(strcmp(field[i], "reg") == 0) {
984*74a4d8c2SCharles.Forsyth 				if(nf < 3)
985*74a4d8c2SCharles.Forsyth 					error(Evolume);
986*74a4d8c2SCharles.Forsyth 				setreg(field[1], atoi(field[2]), nf == 4 ? atoi(field[3]):1);
987*74a4d8c2SCharles.Forsyth 				return n0;
988*74a4d8c2SCharles.Forsyth 			}
989*74a4d8c2SCharles.Forsyth 			error(Evolume);
990*74a4d8c2SCharles.Forsyth 			break;
991*74a4d8c2SCharles.Forsyth 		cont0:;
992*74a4d8c2SCharles.Forsyth 		}
993*74a4d8c2SCharles.Forsyth 		mxvolume();
994*74a4d8c2SCharles.Forsyth 		break;
995*74a4d8c2SCharles.Forsyth 
996*74a4d8c2SCharles.Forsyth 	case Qaudio:
997*74a4d8c2SCharles.Forsyth 		if (debug > 1) print("#A: write %ld\n", n);
998*74a4d8c2SCharles.Forsyth 		if((audio.amode & Awrite) == 0)
999*74a4d8c2SCharles.Forsyth 			error(Emode);
1000*74a4d8c2SCharles.Forsyth 		a = &audio.o;
1001*74a4d8c2SCharles.Forsyth 		qlock(a);
1002*74a4d8c2SCharles.Forsyth 		if(waserror()){
1003*74a4d8c2SCharles.Forsyth 			qunlock(a);
1004*74a4d8c2SCharles.Forsyth 			nexterror();
1005*74a4d8c2SCharles.Forsyth 		}
1006*74a4d8c2SCharles.Forsyth 		while(n > 0) {
1007*74a4d8c2SCharles.Forsyth 			/* wait if dma in progress */
1008*74a4d8c2SCharles.Forsyth 			while (!dmaidle(a->dma) && a->filling == a->current) {
1009*74a4d8c2SCharles.Forsyth 				if (debug > 1) print("#A: sleep\n");
1010*74a4d8c2SCharles.Forsyth 				sleep(&a->vous, audioqnotfull, a);
1011*74a4d8c2SCharles.Forsyth 			}
1012*74a4d8c2SCharles.Forsyth 
1013*74a4d8c2SCharles.Forsyth 			m = Bufsize - a->filling->nbytes;
1014*74a4d8c2SCharles.Forsyth 			if(m > n)
1015*74a4d8c2SCharles.Forsyth 				m = n;
1016*74a4d8c2SCharles.Forsyth 			memmove(a->filling->virt + a->filling->nbytes, p, m);
1017*74a4d8c2SCharles.Forsyth 
1018*74a4d8c2SCharles.Forsyth 			a->filling->nbytes += m;
1019*74a4d8c2SCharles.Forsyth 			n -= m;
1020*74a4d8c2SCharles.Forsyth 			p += m;
1021*74a4d8c2SCharles.Forsyth 			if(a->filling->nbytes >= Bufsize) {
1022*74a4d8c2SCharles.Forsyth 				if (debug > 1) print("#A: filled @%p\n", a->filling);
1023*74a4d8c2SCharles.Forsyth 				a->filling++;
1024*74a4d8c2SCharles.Forsyth 				if (a->filling == &a->buf[Nbuf])
1025*74a4d8c2SCharles.Forsyth 					a->filling = a->buf;
1026*74a4d8c2SCharles.Forsyth 				sendaudio(a);
1027*74a4d8c2SCharles.Forsyth 			}
1028*74a4d8c2SCharles.Forsyth 		}
1029*74a4d8c2SCharles.Forsyth 		poperror();
1030*74a4d8c2SCharles.Forsyth 		qunlock(a);
1031*74a4d8c2SCharles.Forsyth 		break;
1032*74a4d8c2SCharles.Forsyth 	}
1033*74a4d8c2SCharles.Forsyth 	return n0 - n;
1034*74a4d8c2SCharles.Forsyth }
1035*74a4d8c2SCharles.Forsyth 
1036*74a4d8c2SCharles.Forsyth Dev audiodevtab = {
1037*74a4d8c2SCharles.Forsyth 	'A',
1038*74a4d8c2SCharles.Forsyth 	"audio",
1039*74a4d8c2SCharles.Forsyth 
1040*74a4d8c2SCharles.Forsyth 	audioreset,
1041*74a4d8c2SCharles.Forsyth 	audioinit,
1042*74a4d8c2SCharles.Forsyth 	devshutdown,
1043*74a4d8c2SCharles.Forsyth 	audioattach,
1044*74a4d8c2SCharles.Forsyth 	audiowalk,
1045*74a4d8c2SCharles.Forsyth 	audiostat,
1046*74a4d8c2SCharles.Forsyth 	audioopen,
1047*74a4d8c2SCharles.Forsyth 	devcreate,
1048*74a4d8c2SCharles.Forsyth 	audioclose,
1049*74a4d8c2SCharles.Forsyth 	audioread,
1050*74a4d8c2SCharles.Forsyth 	devbread,
1051*74a4d8c2SCharles.Forsyth 	audiowrite,
1052*74a4d8c2SCharles.Forsyth 	devbwrite,
1053*74a4d8c2SCharles.Forsyth 	devremove,
1054*74a4d8c2SCharles.Forsyth 	devwstat,
1055*74a4d8c2SCharles.Forsyth 	audiopower,
1056*74a4d8c2SCharles.Forsyth };
1057