forsyth20121205-174063a80cc13 years agoHistory
1186 lines · 20.0 KB · C
RawDownload
/* * preliminary Crystal CS4231 audio driver, * initially based on SB16 driver, and therefore needs work. * for instance, i suspect the linked-list buffering is excessive: * a rolling buffering scheme or double buffering should be fine, * and possibly simpler. * * To do: *	stop/start? *	is the linux mix_cvt ideal? *	ad1845 differences *	adpcm freezing */ #include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"../port/error.h"#include	"devtab.h"#include	"io.h"#include	"audio.h" #define	DPRINT	if(chatty)print typedef struct	AChan	AChan;typedef struct	AQueue	AQueue;typedef struct	Buf	Buf;typedef struct	Vol	Vol; enum{	Qdir		= 0,	Qaudio,	Qaudioctl, 	Fmono		= 1,	Fin		= 2,	Fout		= 4, 	Vaudio		= 0,	Vaux1,	Vaux2,	Vline,	Vmic,	Vmono,	Vspeed,	Vchans,	Vbits,	Nvol, 	Speed		= 22050,	Ncmd		= 50,		/* max volume command words */}; enum {	Paddr=	0,		TRD=	1<<5,		MCE=	1<<6,	Pdata=	1,	Pstatus=	2,	Pio=		3, 	LeftADC=	0,		MGE=	1<<5,		ISline=	0<<6,		ISaux1=	1<<6,		ISmic=	2<<6,		ISloop=	3<<6,		ISmask=	3<<6,	RightADC= 1,	LeftAux1= 2,		Mute=	1<<7,	RightAux1= 3,	LeftAux2= 4,	RightAux2= 5,	LeftDAC= 6,	RightDAC= 7,	OutFormat=	8,		Stereo=	1<<4,		Linear8=	0<<5,		uLaw=	1<<5,		Linear16=	2<<5,		aLaw=	3<<5,		ADPCM=	5<<5,		Fmask=	7<<5,	Config=	9,		PEN=	1<<0,		CEN=	1<<1,		Nocal=	0<<3,		Convcal=	1<<3,		DACcal=	2<<3,		Fullcal=	3<<3,	PinControl=	10,		IEN=		1<<1,		DEN=	1<<3,		Xctl0=	1<<6,		Xctl1=	1<<7,	Status=	11,		ACI=		1<<5,	Mode=	12,		Mode2=	1<<6,	Loopback=	13,		LBE=		1<<0,	PlayCount1=	14,	PlayCount0=	15,	Feature1=	16,		PMCE=	1<<4,		CMCE=	1<<5,	Feature2=	17,	LeftLine=	18,	RightLine=	19,	Timer0=	20,	Timer1=	21,	Feature3=	23,	FeatureStatus=	24,		PI=	1<<4,	/* playback interrupt */		CI=	1<<5,	/* capture interrupt */		TI=	1<<6,	/* timer interrupt */	ChipID= 25,	MonoCtl=	26,		MBY=	1<<5,	/* mono bypass */		MOM=	1<<6,	InFormat=	28,	RecCount1=	30,	RecCount0=	31,}; #define	csdelay()	microdelay(1) static Dirtab audiodir[] ={	"audio",	{Qaudio},		0,	0666,	"audioctl",	{Qaudioctl},		0,	0666,};#define	NPORT		(sizeof audiodir/sizeof(Dirtab)) struct Buf{	uchar*	virt;	int	count;	Buf*	next;};struct AQueue{	Lock;	Buf*	first;	Buf*	last;};struct AChan{	QLock;	Rendez	r;	Buf	buf[Nbuf];	/* buffers and queues */	AQueue	empty;	AQueue	full;	Buf*	current;	Buf*	filling;	int	flushing;};static struct{	QLock;	int	opened;	int	bufinit;	/* boolean if buffers allocated */	int	rivol[Nvol];		/* right/left input/output volumes */	int	livol[Nvol];	int	rovol[Nvol];	int	lovol[Nvol];	int	loopback; 	AChan	in;	AChan	out;} audio; static	char*	encname(int); static	int	dacload(int, int);static	int	auxload(int, int);static	int	adcload(int, int);static	int	monoload(int, int); struct Vol{	char*	name;	int	flag;	int	ilval;		/* initial values */	int	irval;	int	reg;	int	(*load)(int, int);}; static	Vol	volumes[] = {[Vaudio]	{"audio",	    Fout, 	50,	50,	LeftDAC, dacload},[Vaux1]		{"aux1",		Fin,	0,	0,	LeftAux1, auxload},[Vaux2]		{"aux2",		Fin,	0,	0,	LeftAux2, auxload},[Vline]		{"line",		Fin,	0,	0,	LeftLine, auxload},[Vmono]		{"mono",		Fin|Fout|Fmono,	0,	0,	MonoCtl, monoload},[Vmic]		{"mic",		Fin,	0,	0,	LeftADC, adcload}, [Vspeed]	{"rate",	Fin|Fout|Fmono,	Speed,	Speed,},[Vchans]	{"chans",	Fin|Fout|Fmono,	2,	2,},[Vbits]	{"bits", Fin|Fout|Fmono, 8, 8,},	{0},}; static struct{	Lock;	int	port;	int	irq;	uchar	sticky;	uchar	regs[32];} csdev; static	void	contininput(void);static	void	continoutput(void); static	char	Evolume[]	= "illegal audioctl specifier"; static	int	chatty; #include "cs4231.h" static intxin(int r){	int i; 	for(i=100; --i >= 0 && IN(Paddr) & 0x80;)		csdelay();	OUT(Paddr, r|csdev.sticky);	csdelay();	return IN(Pdata);} static voidxout(int r, int v){	int i; 	for(i=100; --i >= 0 && IN(Paddr) & 0x80;)		csdelay();	OUT(Paddr, r|csdev.sticky);	csdelay();	OUT(Pdata, v);	//csdelay();} static voidspeaker(int on){	int s; 	s = xin(PinControl);	if(on)		s |= Xctl0;	else		s &= ~Xctl0;	xout(PinControl, s);} static Buf*getbuf(AQueue *q){	Buf *b; 	ilock(q);	b = q->first;	if(b)		q->first = b->next;	iunlock(q); 	return b;} static voidputbuf(AQueue *q, Buf *b){	ilock(q);	b->next = 0;	if(q->first)		q->last->next = b;	else		q->first = b;	q->last = b;	iunlock(q);} static voidachanreset(AChan *ac){	int i; 	ac->filling = 0;	ac->flushing = 0;	ac->current = 0;	ac->empty.first = 0;	ac->empty.last = 0;	ac->full.first = 0;	ac->full.last = 0;	for(i=0; i<Nbuf; i++){		ac->buf[i].count = 0;		putbuf(&ac->empty, &ac->buf[i]);	}} static voidstartoutput(void){	ilock(&csdev);	if(audio.out.current == 0)		continoutput();	iunlock(&csdev);} static voidcontinoutput(void){	Buf *b;	int f;	ulong n; 	b = getbuf(&audio.out.full);	audio.out.current = b;	//xout(Config, xin(Config)&~PEN);	if(b){		n = b->count;		dmasetup(Wdma, b->virt, n, 0);		f = xin(OutFormat);		if((f & Fmask) == ADPCM)			n >>= 2;		else{			if((f & Fmask) == Linear16)				n >>= 1;			if(f & Stereo)				n >>= 1;		}		n--;		xout(PlayCount0, n);		xout(PlayCount1, n>>8);		xout(Config, xin(Config)|PEN);		DPRINT("cs: out %d\n", n);	} else		xout(Config, xin(Config)&~PEN);} static voidstartinput(void){	ilock(&csdev);	if(audio.in.current == 0)		contininput();	iunlock(&csdev);} static voidcontininput(void){	Buf *b;	int f;	ulong n; 	xout(Config, xin(Config)&~CEN);	if(!audio.opened || audio.in.flushing){		return;	}	b = getbuf(&audio.in.empty);	audio.in.current = b;	if(b){		n = Bufsize;		dmasetup(Rdma, b->virt, Bufsize, 1);		f = xin(InFormat);		if((f & Fmask) == ADPCM)			n >>= 2;		else{			if((f & Fmask) == Linear16)				n >>= 1;			if(f & Stereo)				n >>= 1;		}		n--;		xout(RecCount0, n);		xout(RecCount1, n>>8);		xout(Config, xin(Config)|CEN);		DPRINT("cs: in %d\n", n);	}} static voidcswait(void){	int i; 	for(i=50; --i >= 0 && IN(Paddr) & 0x80;)		microdelay(2000);	if(i < 0)		print("cswait1\n");	for(i=1000; --i >= 0 && (xin(Status) & ACI) == 0;)		csdelay();	for(i=1000; --i >= 0 && xin(Status) & ACI;)		microdelay(2000);	/* could give error(Eio) if i < 0 */	if(i < 0)		print("cswait2\n");} static intcsspeed(int freq){	int i;	static int freqtab[] = {	/* p. 33 CFS2-CFS0 */		/* xtal1 xtal2 */		8000, 5510,		16000, 11025,		27420, 18900,		32000, 22050,		0, 37800,		0, 44100,		48000, 33075,		9600, 6620,	};	for(i=0; i<16; i++)		if(freqtab[i] == freq){			xout(OutFormat, (xin(OutFormat)&~0xF) | i);			return 1;		}	return 0;} static voidcsformat(int r, int flag, int form, int *vec){	int v; 	if(form == Linear8){		if(vec[Vbits] == 16)			form = Linear16;		else if(vec[Vbits] == 4)			form = ADPCM;	}	if(vec[Vchans] == 2)		form |= Stereo;	DPRINT("csformat(%x,%x,%x)\n", r, flag, form);	if((xin(r)&0xF0) != form){		v = xin(Feature1);		xout(Feature1, v|flag);		xout(r, (xin(r)&~0xF0)|form);		xout(Feature1, v);	}	csdev.regs[r] = form;} static voidcs4231intr(Ureg*, void*){	int ir, s;	Buf *b; 	lock(&csdev);	csdev.sticky |= TRD;	ir = IN(Pstatus);	s = xin(FeatureStatus);	if(s & PI){		b = audio.out.current;		audio.out.current = 0;		dmaend(Wdma);		continoutput();		if(b)			putbuf(&audio.out.empty, b);		wakeup(&audio.out.r);	}	if(s & CI){		b = audio.in.current;		audio.in.current = 0;		dmaend(Rdma);		contininput();		if(b){			b->count = Bufsize;			putbuf(&audio.in.full, b);		}		wakeup(&audio.in.r);	}	OUT(Pstatus, 0);	csdev.sticky &= ~TRD;	unlock(&csdev);	if(s & 0xF)		DPRINT("audiointr: #%x\n", s);} static intanybuf(void *p){	return ((AChan*)p)->empty.first != 0;} static intanyinput(void *p){

Lines 1–500 of 1186.