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