⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 devuda1341.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	SAC/UDA 1341 Audio driver for the Bitsy * *	The Philips UDA 1341 sound chip is accessed through the Serial Audio *	Controller (SAC) of the StrongARM SA-1110.  This is much more a SAC *	controller than a UDA controller, but we have a devsac.c already. * *	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"#include	"sa1110dma.h"static int debug = 0;/* * GPIO based L3 bus support. * * This provides control of Philips L3 type devices.  * GPIO lines are used for clock, data and mode pins. * * Note: The L3 pins are shared with I2C devices. This should not present * any problems as long as an I2C start sequence is not generated. This is * defined as a 1->0 transition on the data lines when the clock is high. * It is critical this code only allow data transitions when the clock * is low. This is always legal in L3. * * The IIC interface requires the clock and data pin to be LOW when idle. We * must make sure we leave them in this state. * * It appears the read data is generated on the falling edge of the clock * and should be held stable during the clock high time. *//*  * L3 setup and hold times (expressed in µs) */enum {	L3_AcquireTime =		1,	L3_ReleaseTime =		1,	L3_DataSetupTime =	(190+999)/1000,	/* 190 ns */	L3_DataHoldTime =		( 30+999)/1000,	L3_ModeSetupTime =	(190+999)/1000,	L3_ModeHoldTime =	(190+999)/1000,	L3_ClockHighTime =	(100+999)/1000,	L3_ClockLowTime =		(100+999)/1000,	L3_HaltTime =			(190+999)/1000,};/* 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,	Qstats,	Fmono		= 1,	Fin			= 2,	Fout			= 4,	Aclosed		= 0,	Aread,	Awrite,	Vaudio		= 0,	Vmic,	Vtreb,	Vbass,	Vspeed,	Vbufsize,	Vfilter,	Vinvert,	Nvol,	Bufsize		= 4* 1024,	/* 46 ms each */	Nbuf			= 10,			/* 1.5 seconds total */	Speed		= 44100,	Ncmd		= 50,			/* max volume command words */};enum {	Flushbuf = 0xe0000000,};/* System Clock -- according to the manual, it seems that when the UDA is    configured in non MSB/I2S mode, it uses a divisor of 256 to the 12.288MHz    clock.  The other rates are only supported in MSB mode, which should be     implemented at some point */enum {	SC512FS = 0 << 2,	SC384FS = 1 << 2,	SC256FS = 2 << 2,	CLOCKMASK = 3 << 2,};/* Format */enum {	LSB16 =	1 << 1,	LSB18 =	2 << 1,	LSB20 =	3 << 1,	MSB =	4 << 1,	MSB16 =	5 << 1,	MSB18 =	6 << 1,	MSB20 =	7 << 1,};Dirtabaudiodir[] ={ 	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,	"audio",		{Qaudio},			0,	0666,	"volume",		{Qvolume},		0,	0666,	"audiostatus",	{Qstatus},			0,	0444,	"audiostats",	{Qstats},			0,	0444,};struct	Buf{	uchar*	virt;	ulong	phys;	uint	nbytes;};struct	IOstate{	QLock;	Lock			ilock;	Rendez		vous;	Chan		*chan;		/* chan of open */	int			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 *//* to have defines just like linux — there's 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];	ulong	totcount;		/* how many bytes processed since open */	vlong	tottime;		/* time at which totcount bytes were processed */	IOstate	i;	IOstate	o;} audio;int	zerodma;	/* dma buffer used for sending zero */typedef struct Iostats Iostats;struct Iostats {	ulong	totaldma;	ulong	idledma;	ulong	faildma;	ulong	samedma;	ulong	empties;};static struct{	ulong	bytes;	Iostats	rx, tx;} iostats;static void setaudio(int in, int out, int left, int right, int value);static void setspeed(int in, int out, int left, int right, int value);static void setbufsize(int in, int out, int left, int right, int value);static	struct{	char*	name;	int		flag;	int		ilval;		/* initial  values */	int		irval;	void		(*setval)(int, int, int, int, int);} volumes[] ={[Vaudio]	{"audio",		Fout|Fmono,	 	80,		80,	setaudio },[Vmic]	{"mic",		Fin|Fmono,		  0,		  0,	nil },[Vtreb]	{"treb",		Fout|Fmono,		50,		50,	nil },[Vbass]	{"bass",		Fout|Fmono, 		50,		50,	nil },[Vspeed]	{"speed",		Fin|Fout|Fmono,	Speed,	Speed,	setspeed },[Vbufsize]	{"bufsize",	Fin|Fout|Fmono,	Bufsize,	Bufsize,	setbufsize },[Vfilter]	{"filter",		Fout|Fmono,		  0,		  0,	nil },[Vinvert]	{"invert",		Fin|Fout|Fmono,	  0,		  0,	nil },[Nvol]	{0}};static void	setreg(char *name, int val, int n);/* * Grab control of the IIC/L3 shared pins */static voidL3_acquirepins(void){	gpioregs->set = (GPIO_L3_MODE_o | GPIO_L3_SCLK_o | GPIO_L3_SDA_io);	gpioregs->direction |=  (GPIO_L3_MODE_o | GPIO_L3_SCLK_o | GPIO_L3_SDA_io);	microdelay(L3_AcquireTime);}/* * Release control of the IIC/L3 shared pins */static voidL3_releasepins(void){	gpioregs->direction &= ~(GPIO_L3_MODE_o | GPIO_L3_SCLK_o | GPIO_L3_SDA_io);	microdelay(L3_ReleaseTime);}/* * Initialize the interface */static void L3_init(void){	gpioregs->altfunc &= ~(GPIO_L3_SDA_io | GPIO_L3_SCLK_o | GPIO_L3_MODE_o);	L3_releasepins();}/* * Get a bit. The clock is high on entry and on exit. Data is read after * the clock low time has expired. */static intL3_getbit(void){	int data;	gpioregs->clear = GPIO_L3_SCLK_o;	microdelay(L3_ClockLowTime);	data = (gpioregs->level & GPIO_L3_SDA_io) ? 1 : 0; 	gpioregs->set = GPIO_L3_SCLK_o;	microdelay(L3_ClockHighTime);	return data;}/* * Send a bit. The clock is high on entry and on exit. Data is sent only * when the clock is low (I2C compatibility). */static voidL3_sendbit(int bit){	gpioregs->clear = GPIO_L3_SCLK_o;	if (bit & 1)		gpioregs->set = GPIO_L3_SDA_io;	else		gpioregs->clear = GPIO_L3_SDA_io;	/* Assumes L3_DataSetupTime < L3_ClockLowTime */	microdelay(L3_ClockLowTime);	gpioregs->set = GPIO_L3_SCLK_o;	microdelay(L3_ClockHighTime);}/* * Send a byte. The mode line is set or pulsed based on the mode sequence * count. The mode line is high on entry and exit. The mod line is pulsed * before the second data byte and before ech byte thereafter. */static voidL3_sendbyte(char data, int mode){	int i;	switch(mode) {	case 0: /* Address mode */		gpioregs->clear = GPIO_L3_MODE_o;		break;	case 1: /* First data byte */		break;	default: /* Subsequent bytes */		gpioregs->clear = GPIO_L3_MODE_o;		microdelay(L3_HaltTime);		gpioregs->set = GPIO_L3_MODE_o;		break;	}	microdelay(L3_ModeSetupTime);	for (i = 0; i < 8; i++)		L3_sendbit(data >> i);	if (mode == 0)  /* Address mode */		gpioregs->set = GPIO_L3_MODE_o;	microdelay(L3_ModeHoldTime);}/* * Get a byte. The mode line is set or pulsed based on the mode sequence * count. The mode line is high on entry and exit. The mod line is pulsed * before the second data byte and before each byte thereafter. This * function is never valid with mode == 0 (address cycle) as the address * is always sent on the bus, not read. */static charL3_getbyte(int mode){	char data = 0;	int i;	switch(mode) {	case 0: /* Address mode - never valid */		break;	case 1: /* First data byte */		break;	default: /* Subsequent bytes */		gpioregs->clear = GPIO_L3_MODE_o;		microdelay(L3_HaltTime);		gpioregs->set = GPIO_L3_MODE_o;		break;	}	microdelay(L3_ModeSetupTime);	for (i = 0; i < 8; i++)		data |= (L3_getbit() << i);	microdelay(L3_ModeHoldTime);	return data;}/* * Write data to a device on the L3 bus. The address is passed as well as * the data and length. The length written is returned. The register space * is encoded in the address (low two bits are set and device address is * in the upper 6 bits). */static intL3_write(uchar addr, uchar *data, int len){	int mode = 0;	int bytes = len;	L3_acquirepins();	L3_sendbyte(addr, mode++);	while(len--)		L3_sendbyte(*data++, mode++);	L3_releasepins();	return bytes;}/* * Read data from a device on the L3 bus. The address is passed as well as * the data and length. The length read is returned. The register space * is encoded in the address (low two bits are set and device address is * in the upper 6 bits). * Commented out, not usedstatic intL3_read(uchar addr, uchar *data, int len){	int mode = 0;	int bytes = len;	L3_acquirepins();	L3_sendbyte(addr, mode++);	gpioregs->direction &= ~(GPIO_L3_SDA_io);	while(len--)		*data++ = L3_getbyte(mode++);	L3_releasepins();	return bytes;} */voidaudiomute(int on){	egpiobits(EGPIO_audio_mute, on);}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 = xalloc(Bufsize);		b->buf[i].phys = PADDR(b->buf[i].virt);		memset(b->buf[i].virt, 0xAA, Bufsize);	}	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;}SSPregs *sspregs;MCPregs	*mcpregs;static voidaudioinit(void){	/* Turn MCP operations off */	mcpregs = mapspecial(MCPREGS, sizeof(MCPregs));	mcpregs->status &= ~(1<<16);	sspregs = mapspecial(SSPREGS, sizeof(SSPregs));}uchar	status0[1]		= {0x02};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];	L3_init();	/* Setup the uarts */	ppcregs->assignment &= ~(1<<18);	sspregs->control0 = 0;	sspregs->control0 = 0x031f; /* 16 bits, TI frames, serial clock rate 3 */	sspregs->control1 = 0x0020; /* ext clock */	sspregs->control0 = 0x039f;	/* enable */	/* Enable the audio power */	audioicpower(1);	egpiobits(EGPIO_codec_reset, 1);	setspeed(0, 0, 0, 0, volumes[Vspeed].ilval);	data[0] = status0[0] | 1 << UdaStatusRST;	L3_write(UDA1341_L3Addr | UDA1341_STATUS, data, 1 );	gpioregs->clear = EGPIO_codec_reset;	gpioregs->set = EGPIO_codec_reset;	/* write uda 1341 status[0] */	data[0] = status0[0];	L3_write(UDA1341_L3Addr | UDA1341_STATUS, data, 1);	if (debug)		print("enable:	status0	= 0x%2.2ux\n", data[0]);	L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);	L3_write(UDA1341_L3Addr | UDA1341_DATA0, data02, 1);	L3_write(UDA1341_L3Addr | UDA1341_DATA0, data0e2, 2);	L3_write(UDA1341_L3Addr | UDA1341_DATA0, data0e6, 2 );	if (debug) {		print("enable:	status0	= 0x%2.2ux\n", data[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);		print("enable:	sspregs->control0 = 0x%lux\n", sspregs->control0);		print("enable:	sspregs->control1 = 0x%lux\n", sspregs->control1);	}}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;	setspeed(0, 0, 0, 0, volumes[Vspeed].ilval);	if (!dmaidle(audio.i.dma) || !dmaidle(audio.o.dma))		L3_write(UDA1341_L3Addr | UDA1341_STATUS, status0, 1);	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;			L3_write(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;			L3_write(UDA1341_L3Addr | UDA1341_DATA0, data0e4, 2 );			L3_write(UDA1341_L3Addr | UDA1341_DATA0, data0e5, 2 );		}		if (left[Vinvert]+right[Vinvert] == 0)			status1[0] &= ~0x04;		else			status1[0] |= 0x04;		L3_write(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] &= ~0x10;		else			status1[0] |= 0x10;		L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);		L3_write(UDA1341_L3Addr | UDA1341_DATA0, data00, 1);		L3_write(UDA1341_L3Addr | UDA1341_DATA0, data01, 1);		L3_write(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;	x[0] = val;	x[1] = val>>8;	if(strcmp(name, "pause") == 0){		for(i = 0; i < n; i++)			microdelay(val);		return;	}	switch(n){	case 1:	case 2:		break;	default:		error("setreg");	}	if(strcmp(name, "status") == 0){		L3_write(UDA1341_L3Addr | UDA1341_STATUS, x, n);	} else if(strcmp(name, "data0") == 0){		L3_write(UDA1341_L3Addr | UDA1341_DATA0, x, n);	} else if(strcmp(name, "data1") == 0){		L3_write(UDA1341_L3Addr | UDA1341_DATA1, x, n);	} else		error("setreg");}static voidoutenable(void) {	/* turn on DAC, set output gain switch */	audioamppower(1);	audiomute(0);	status1[0] |= 0x41;	L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);	/* set volume */	data00[0] |= 0xf;	L3_write(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) {	dmastop(audio.o.dma);	/* turn off DAC, clear output gain switch */	audiomute(1);	status1[0] &= ~0x41;	L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);	if (debug) {		print("outdisable:	status1	= 0x%2.2ux\n", status1[0]);	}	audioamppower(0);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -