/* * 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. Load more
/* * 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 int xin(int r) { int i; for(i=100; --i >= 0 && IN(Paddr) & 0x80;) csdelay(); OUT(Paddr, r|csdev.sticky); csdelay(); return IN(Pdata); } static void xout(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 void speaker(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 void putbuf(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 void achanreset(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 void startoutput(void) { ilock(&csdev); if(audio.out.current == 0) continoutput(); iunlock(&csdev); } static void continoutput(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 void startinput(void) { ilock(&csdev); if(audio.in.current == 0) contininput(); iunlock(&csdev); } static void contininput(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 void cswait(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 int csspeed(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 void csformat(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 void cs4231intr(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 int anybuf(void *p) { return ((AChan*)p)->empty.first != 0; } static int anyinput(void *p) {