Charles.Forsyth20060303644188019 years agoHistory
1056 lines · 21.3 KB · C
RawDownload
/* *	SAC/UDA 1341 Audio driver for the Bitsy * *	This code is covered by the Lucent Public Licence 1.02 (http://plan9.bell-labs.com/plan9dist/license.html); *	see the file NOTICE in the current directory.  Modifications for the Inferno environment by Vita Nuova. * *	The Philips UDA 1341 sound chip is accessed through the Serial Audio *	Controller (SAC) of the StrongARM SA-1110. * *	The code morphs Nicolas Pitre's <nico@cam.org> Linux controller *	and Ken's Soundblaster controller. * *	The interface should be identical to that of devaudio.c */#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"../port/error.h"#include	"io.h" static int debug = 0; /* UDA 1341 Registers */enum {	/* Status0 register */	UdaStatusDC		= 0,	/* 1 bit */	UdaStatusIF		= 1,	/* 3 bits */	UdaStatusSC		= 4,	/* 2 bits */	UdaStatusRST		= 6,	/* 1 bit */}; enum {	/* Status1 register */	UdaStatusPC	= 0,	/* 2 bits */	UdaStatusDS	= 2,	/* 1 bit */	UdaStatusPDA	= 3,	/* 1 bit */	UdaStatusPAD	= 4,	/* 1 bit */	UdaStatusIGS	= 5,	/* 1 bit */	UdaStatusOGS	= 6,	/* 1 bit */}; /* * UDA1341 L3 address and command types */ enum {	UDA1341_DATA0 =	0,	UDA1341_DATA1,	UDA1341_STATUS,	UDA1341_L3Addr = 0x14,}; typedef struct	AQueue	AQueue;typedef struct	Buf	Buf;typedef struct	IOstate IOstate; enum{	Qdir		= 0,	Qaudio,	Qvolume,	Qstatus,	Qaudioctl, 	Fmono		= 1,	Fin			= 2,	Fout		= 4, 	Aclosed		= 0,	Aread,	Awrite, 	Vaudio		= 0,	Vmic,	Vtreb,	Vbass,	Vspeed,	Vfilter,	Vinvert,	Nvol, 	Bufsize		= 4*1024,	/* 46 ms each */	Nbuf		= 32,		/* 1.5 seconds total */ 	Speed		= 44100,	Ncmd		= 50,		/* max volume command words */}; Dirtabaudiodir[] ={	".",		{Qdir, 0, QTDIR},	0,	0555,	"audio",	{Qaudio},		0,	0666,	"volume",	{Qvolume},		0,	0666,	"audioctl", {Qaudioctl},		0,	0666,	"audiostat",{Qstatus},		0,	0444,}; struct	Buf{	uchar*	virt;	ulong	phys;	uint	nbytes;}; struct	IOstate{	QLock;	Lock			ilock;	Rendez			vous;	Chan			*chan;			/* chan of open */	Dma*				dma;			/* dma chan, alloc on open, free on close */	int				bufinit;		/* boolean, if buffers allocated */	Buf				buf[Nbuf];		/* buffers and queues */	volatile Buf	*current;		/* next dma to finish */	volatile Buf	*next;			/* next candidate for dma */	volatile Buf	*filling;		/* buffer being filled *//* just be be cute (and to have defines like linux, a real operating system) */#define emptying filling}; static	struct{	QLock;	int		amode;			/* Aclosed/Aread/Awrite for /audio */	int		intr;			/* boolean an interrupt has happened */	int		rivol[Nvol];	/* right/left input/output volumes */	int		livol[Nvol];	int		rovol[Nvol];	int		lovol[Nvol];	uvlong	totcount;		/* how many bytes processed since open */	vlong	tottime;		/* time at which totcount bytes were processed */	int	clockout;	/* need steady output to provide input clock */	IOstate	i;	IOstate	o;} audio; static struct{	ulong	bytes;	ulong	totaldma;	ulong	idledma;	ulong	faildma;	ulong	samedma;} iostats; static	struct{	char*	name;	int	flag;	int	ilval;		/* initial values */	int	irval;} volumes[] ={[Vaudio]	{"audio",	Fout|Fmono,	 80,	 80},[Vmic]		{"mic",		Fin|Fmono,	  0,	  0},[Vtreb]		{"treb",	Fout|Fmono,	 50,	 50},[Vbass]		{"bass",	Fout|Fmono, 	 50,	 50},[Vspeed]	{"speed",	Fin|Fout|Fmono,	Speed,	Speed},[Vfilter]	{"filter",	Fout|Fmono,	  0,	  0},[Vinvert]	{"invert",	Fin|Fout|Fmono,	  0,	  0},[Nvol]		{0}}; static void	setreg(char *name, int val, int n); static	char	Emode[]		= "illegal open mode";static	char	Evolume[]	= "illegal volume specifier"; static voidbufinit(IOstate *b){	int i; 	if (debug) print("bufinit\n");	for (i = 0; i < Nbuf; i++) {		b->buf[i].virt = xspanalloc(Bufsize, CACHELINESZ, 0);		b->buf[i].phys = PADDR(b->buf[i].virt);	}	b->bufinit = 1;}; static voidsetempty(IOstate *b){	int i; 	if (debug) print("setempty\n");	for (i = 0; i < Nbuf; i++) {		b->buf[i].nbytes = 0;	}	b->filling = b->buf;	b->current = b->buf;	b->next = b->buf;} static intaudioqnotempty(void *x){	IOstate *s = x; 	return dmaidle(s->dma) || s->emptying != s->current;} static intaudioqnotfull(void *x){	IOstate *s = x; 	return dmaidle(s->dma) || s->filling != s->current;} static voidaudioreset(void){	/* Turn MCP operations off */	MCPREG->mccr = 0;} uchar	status0[1]		= {0x22};uchar	status1[1]		= {0x80};uchar	data00[1]		= {0x00};		/* volume control, bits 0 – 5 */uchar	data01[1]		= {0x40};uchar	data02[1]		= {0x80};uchar	data0e0[2]	= {0xc0, 0xe0};uchar	data0e1[2]	= {0xc1, 0xe0};uchar	data0e2[2]	= {0xc2, 0xf2};/* there is no data0e3 */uchar	data0e4[2]	= {0xc4, 0xe0};uchar	data0e5[2]	= {0xc5, 0xe0};uchar	data0e6[2]	= {0xc6, 0xe3}; static voidenable(void){	uchar	data[1];	int cs; 	L3init(); 	PPCREG->ppar &= ~PPAR_SPR; 	/* external clock and ssp configured for current samples/sec */	cs = archaudiospeed(audio.livol[Vspeed], 1);	status0[0] = (status0[0] & ~(3<<4)) | (cs<<4); 	/* Enable the audio power */	archaudiopower(1);//	egpiobits(EGPIO_audio_ic_power | EGPIO_codec_reset, 1); 	/* Wait for the UDA1341 to wake up */	delay(100); 	/* Reset the chip */	data[0] = status0[0] | 1<<UdaStatusRST;	L3write(UDA1341_L3Addr | UDA1341_STATUS, data, 1 );	archcodecreset(); 	/* write uda 1341 status[0] */	L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1 );	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);	L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);	L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e2, 2);	L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e6, 2 ); 	if (debug) {		print("enable:	status0	= 0x%2.2ux\n", status0[0]);		print("enable:	status1	= 0x%2.2ux\n", status1[0]);		print("enable:	data02	= 0x%2.2ux\n", data02[0]);		print("enable:	data0e2	= 0x%4.4ux\n", data0e2[0] | data0e2[1]<<8);		print("enable:	data0e4	= 0x%4.4ux\n", data0e4[0] | data0e4[1]<<8);		print("enable:	data0e6	= 0x%4.4ux\n", data0e6[0] | data0e6[1]<<8);	}} static voiddisable(void){	SSPREG->sscr0 = 0x031f;	/* disable */} static voidresetlevel(void){	int i; 	for(i=0; volumes[i].name; i++) {		audio.lovol[i] = volumes[i].ilval;		audio.rovol[i] = volumes[i].irval;		audio.livol[i] = volumes[i].ilval;		audio.rivol[i] = volumes[i].irval;	}} static voidmxvolume(void) {	int *left, *right;	int cs; 	cs = archaudiospeed(audio.livol[Vspeed], 1);	status0[0] = (status0[0] & ~(3<<4)) | (cs<<4);	L3write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1);	if(debug)		print("mxvolume:	status0	= %2.2ux\n", status0[0]);	if(audio.amode & Aread){		left = audio.livol;		right = audio.rivol;		if (left[Vmic]+right[Vmic] == 0) {			/* Turn on automatic gain control (AGC) */			data0e4[1] |= 0x10;			L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );		} else {			int v;			/* Turn on manual gain control */			v = ((left[Vmic]+right[Vmic])*0x7f/200)&0x7f;			data0e4[1] &= ~0x13;			data0e5[1] &= ~0x1f;			data0e4[1] |= v & 0x3;			data0e5[0] |= (v & 0x7c)<<6;			data0e5[1] |= (v & 0x7c)>>2;			L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );			L3write(UDA1341_L3Addr | UDA1341_DATA0, data0e5, 2 );		}		if (left[Vinvert]+right[Vinvert] == 0)			status1[0] &= ~0x10;		else			status1[0] |= 0x10;		L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);		if (debug) {			print("mxvolume:	status1	= 0x%2.2ux\n", status1[0]);			print("mxvolume:	data0e4	= 0x%4.4ux\n", data0e4[0]|data0e4[0]<<8);			print("mxvolume:	data0e5	= 0x%4.4ux\n", data0e5[0]|data0e5[0]<<8);		}	}	if(audio.amode & Awrite){		left = audio.lovol;		right = audio.rovol;		data00[0] &= ~0x3f;		data00[0] |= ((200-left[Vaudio]-right[Vaudio])*0x3f/200)&0x3f;		if (left[Vtreb]+right[Vtreb] <= 100		 && left[Vbass]+right[Vbass] <= 100)			/* settings neutral */			data02[0] &= ~0x03;		else {			data02[0] |= 0x03;			data01[0] &= ~0x3f;			data01[0] |= ((left[Vtreb]+right[Vtreb]-100)*0x3/100)&0x03;			data01[0] |= (((left[Vbass]+right[Vbass]-100)*0xf/100)&0xf)<<2;		}		if (left[Vfilter]+right[Vfilter] == 0)			data02[0] &= ~0x10;		else			data02[0]|= 0x10;		if (left[Vinvert]+right[Vinvert] == 0)			status1[0] &= ~0x8;		else			status1[0] |= 0x8;		L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);		L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);		L3write(UDA1341_L3Addr | UDA1341_DATA0, data01, 1);		L3write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);		if (debug) {			print("mxvolume:	status1	= 0x%2.2ux\n", status1[0]);			print("mxvolume:	data00	= 0x%2.2ux\n", data00[0]);			print("mxvolume:	data01	= 0x%2.2ux\n", data01[0]);			print("mxvolume:	data02	= 0x%2.2ux\n", data02[0]);		}	}} static voidsetreg(char *name, int val, int n){	uchar x[2];	int i; 	if(strcmp(name, "pause") == 0){		for(i = 0; i < n; i++)			microdelay(val);		return;	} 	x[0] = val;	x[1] = val>>8; 	switch(n){	case 1:	case 2:		break;	default:		error("setreg");	} 	if(strcmp(name, "status") == 0){		L3write(UDA1341_L3Addr | UDA1341_STATUS, x, n);	} else if(strcmp(name, "data0") == 0){		L3write(UDA1341_L3Addr | UDA1341_DATA0, x, n);	} else if(strcmp(name, "data1") == 0){		L3write(UDA1341_L3Addr | UDA1341_DATA1, x, n);	} else		error("setreg");} static voidoutenable(void) {	/* turn on DAC, set output gain switch */	archaudioamp(1);	archaudiomute(0);	status1[0] |= 0x41;	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);	/* set volume */	data00[0] |= 0xf;	L3write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);	if (debug) {		print("outenable:	status1	= 0x%2.2ux\n", status1[0]);		print("outenable:	data00	= 0x%2.2ux\n", data00[0]);	}} static voidoutdisable(void) {	archaudiomute(1);	dmastop(audio.o.dma);	/* turn off DAC, clear output gain switch */	archaudioamp(0);	status1[0] &= ~0x41;	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);	if (debug) {		print("outdisable:	status1	= 0x%2.2ux\n", status1[0]);	}//	egpiobits(EGPIO_audio_power, 0);} static voidinenable(void) {	/* turn on ADC, set input gain switch */	status1[0] |= 0x22;	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);	if (debug) {		print("inenable:	status1	= 0x%2.2ux\n", status1[0]);	}} static voidindisable(void) {	dmastop(audio.i.dma);	/* turn off ADC, clear input gain switch */	status1[0] &= ~0x22;	L3write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);	if (debug) {		print("indisable:	status1	= 0x%2.2ux\n", status1[0]);	}} static voidsendaudio(IOstate *s) {	/* interrupt routine calls this too */	int n; 	if (debug > 1) print("#A: sendaudio\n");	ilock(&s->ilock);	while (s->next != s->filling) {		assert(s->next->nbytes);		if ((n = dmastart(s->dma, (void*)s->next->phys, s->next->nbytes)) == 0) {			iostats.faildma++;			break;		}		iostats.totaldma++;		switch (n) {		case 1:			iostats.idledma++;			break;		case 3:			iostats.faildma++;			break;		}		if (debug) {			if (debug > 1)				print("dmastart @%p\n", s->next);			else				iprint("+");		}		s->next->nbytes = 0;		s->next++;		if (s->next == &s->buf[Nbuf])			s->next = &s->buf[0];	}	iunlock(&s->ilock);} static voidrecvaudio(IOstate *s) {	/* interrupt routine calls this too */	int n; 	if (debug > 1) print("#A: recvaudio\n");	ilock(&s->ilock);	while (s->next != s->emptying) {

Lines 1–500 of 1056.