12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400 |
- /*
- * 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,
- };
- Dirtab
- audiodir[] =
- {
- ".", {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 void
- L3_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 void
- L3_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 int
- L3_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 void
- L3_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 void
- L3_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 char
- L3_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 int
- L3_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 used
- static int
- L3_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;
- }
- */
- void
- audiomute(int on)
- {
- egpiobits(EGPIO_audio_mute, on);
- }
- static char Emode[] = "illegal open mode";
- static char Evolume[] = "illegal volume specifier";
- static void
- bufinit(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 void
- setempty(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 int
- audioqnotempty(void *x)
- {
- IOstate *s = x;
- return dmaidle(s->dma) || s->emptying != s->current;
- }
- static int
- audioqnotfull(void *x)
- {
- IOstate *s = x;
- return dmaidle(s->dma) || s->filling != s->current;
- }
- SSPregs *sspregs;
- MCPregs *mcpregs;
- static void
- audioinit(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 void
- enable(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 void
- resetlevel(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 void
- mxvolume(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 void
- setreg(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 void
- outenable(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 void
- outdisable(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);
- }
- static void
- inenable(void) {
- /* turn on ADC, set input gain switch */
- status1[0] |= 0x22;
- L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
- if (debug) {
- print("inenable: status1 = 0x%2.2ux\n", status1[0]);
- }
- }
- static void
- indisable(void) {
- dmastop(audio.i.dma);
- /* turn off ADC, clear input gain switch */
- status1[0] &= ~0x22;
- L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1);
- if (debug) {
- print("indisable: status1 = 0x%2.2ux\n", status1[0]);
- }
- }
- static void
- sendaudio(IOstate *s) {
- /* interrupt routine calls this too */
- int n;
- if (debug > 1) print("#A: sendaudio\n");
- ilock(&s->ilock);
- if ((audio.amode & Aread) && s->next == s->filling && dmaidle(s->dma)) {
- // send an empty buffer to provide an input clock
- zerodma |= dmastart(s->dma, Flushbuf, volumes[Vbufsize].ilval) & 0xff;
- if (zerodma == 0)
- if (debug) print("emptyfail\n");
- iostats.tx.empties++;
- iunlock(&s->ilock);
- return;
- }
- while (s->next != s->filling) {
- s->next->nbytes &= ~0x3; /* must be a multiple of 4 */
- if(s->next->nbytes) {
- if ((n = dmastart(s->dma, s->next->phys, s->next->nbytes)) == 0) {
- iostats.tx.faildma++;
- break;
- }
- iostats.tx.totaldma++;
- switch (n >> 8) {
- case 1:
- iostats.tx.idledma++;
- break;
- case 3:
- iostats.tx.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 void
- recvaudio(IOstate *s) {
- /* interrupt routine calls this too */
- int n;
- if (debug > 1) print("#A: recvaudio\n");
- ilock(&s->ilock);
- while (s->next != s->emptying) {
- assert(s->next->nbytes == 0);
- if ((n = dmastart(s->dma, s->next->phys, volumes[Vbufsize].ilval)) == 0) {
- iostats.rx.faildma++;
- break;
- }
- iostats.rx.totaldma++;
- switch (n >> 8) {
- case 1:
- iostats.rx.idledma++;
- break;
- case 3:
- iostats.rx.faildma++;
- break;
- }
- if (debug) {
- if (debug > 1)
- print("dmastart @%p\n", s->next);
- else
- iprint("+");
- }
- s->next++;
- if (s->next == &s->buf[Nbuf])
- s->next = &s->buf[0];
- }
- iunlock(&s->ilock);
- }
- void
- audiopower(int flag) {
- IOstate *s;
- if (debug) {
- iprint("audiopower %d\n", flag);
- }
- if (flag) {
- /* power on only when necessary */
- if (audio.amode) {
- audioamppower(1);
- audioicpower(1);
- egpiobits(EGPIO_codec_reset, 1);
- enable();
- if (audio.amode & Aread) {
- inenable();
- s = &audio.i;
- dmareset(s->dma, 1, 0, 4, 2, SSPRecvDMA, Port4SSP);
- recvaudio(s);
- }
- if (audio.amode & Awrite) {
- outenable();
- s = &audio.o;
- dmareset(s->dma, 0, 0, 4, 2, SSPXmitDMA, Port4SSP);
- sendaudio(s);
- }
- mxvolume();
- }
- } else {
- /* power off */
- if (audio.amode & Aread)
- indisable();
- if (audio.amode & Awrite)
- outdisable();
- egpiobits(EGPIO_codec_reset, 0);
- audioamppower(0);
- audioicpower(0);
- }
- }
- static void
- audiointr(void *x, ulong ndma) {
- IOstate *s = x;
- if (debug) {
- if (debug > 1)
- iprint("#A: audio interrupt @%p\n", s->current);
- else
- iprint("-");
- }
- if (s == &audio.i || (ndma & ~zerodma)) {
- /* A dma, not of a zero buffer completed, update current
- * Only interrupt routine touches s->current
- */
- s->current->nbytes = (s == &audio.i)? volumes[Vbufsize].ilval: 0;
- s->current++;
- if (s->current == &s->buf[Nbuf])
- s->current = &s->buf[0];
- }
- if (ndma) {
- if (s == &audio.o) {
- zerodma &= ~ndma;
- sendaudio(s);
- } else if (s == &audio.i)
- recvaudio(s);
- }
- wakeup(&s->vous);
- }
- static Chan*
- audioattach(char *param)
- {
- return devattach('A', param);
- }
- static Walkqid*
- audiowalk(Chan *c, Chan *nc, char **name, int nname)
- {
- return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);
- }
- static int
- audiostat(Chan *c, uchar *db, int n)
- {
- return devstat(c, db, n, audiodir, nelem(audiodir), devgen);
- }
- static Chan*
- audioopen(Chan *c, int mode)
- {
- IOstate *s;
- int omode = mode;
- switch((ulong)c->qid.path) {
- default:
- error(Eperm);
- break;
- case Qstatus:
- case Qstats:
- if((omode&7) != OREAD)
- error(Eperm);
- case Qvolume:
- case Qdir:
- break;
- case Qaudio:
- omode = (omode & 0x7) + 1;
- if (omode & ~(Aread | Awrite))
- error(Ebadarg);
- qlock(&audio);
- if(audio.amode & omode){
- qunlock(&audio);
- error(Einuse);
- }
- enable();
- memset(&iostats, 0, sizeof(iostats));
- if (omode & Aread) {
- inenable();
- s = &audio.i;
- if(s->bufinit == 0)
- bufinit(s);
- setempty(s);
- s->emptying = &s->buf[Nbuf-1];
- s->chan = c;
- s->dma = dmaalloc(1, 0, 4, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)s);
- audio.amode |= Aread;
- }
- if (omode & (Aread|Awrite) && (audio.amode & Awrite) == 0) {
- s = &audio.o;
- if(s->bufinit == 0)
- bufinit(s);
- setempty(s);
- s->chan = c;
- s->dma = dmaalloc(0, 0, 4, 2, SSPXmitDMA, Port4SSP, audiointr, (void*)s);
- }
- if (omode & Awrite) {
- audio.amode |= Awrite;
- outenable();
- }
- mxvolume();
- if (audio.amode & Aread)
- sendaudio(&audio.o);
- qunlock(&audio);
-
- if (debug) print("open done\n");
- break;
- }
- c = devopen(c, mode, audiodir, nelem(audiodir), devgen);
- c->mode = openmode(mode);
- c->flag |= COPEN;
- c->offset = 0;
- return c;
- }
- static void
- audioclose(Chan *c)
- {
- IOstate *s;
- switch((ulong)c->qid.path) {
- default:
- error(Eperm);
- break;
- case Qdir:
- case Qvolume:
- case Qstatus:
- case Qstats:
- break;
- case Qaudio:
- if (debug > 1) print("#A: close\n");
- if(c->flag & COPEN) {
- qlock(&audio);
- if (audio.i.chan == c) {
- /* closing the read end */
- audio.amode &= ~Aread;
- s = &audio.i;
- qlock(s);
- indisable();
- setempty(s);
- dmafree(s->dma);
- qunlock(s);
- if ((audio.amode & Awrite) == 0) {
- s = &audio.o;
- qlock(s);
- while(waserror())
- ;
- dmawait(s->dma);
- poperror();
- outdisable();
- setempty(s);
- dmafree(s->dma);
- qunlock(s);
- }
- }
- if (audio.o.chan == c) {
- /* closing the write end */
- audio.amode &= ~Awrite;
- s = &audio.o;
- qlock(s);
- if (s->filling->nbytes) {
- /* send remaining partial buffer */
- s->filling++;
- if (s->filling == &s->buf[Nbuf])
- s->filling = &s->buf[0];
- sendaudio(s);
- }
- while(waserror())
- ;
- dmawait(s->dma);
- poperror();
- outdisable();
- setempty(s);
- if ((audio.amode & Aread) == 0)
- dmafree(s->dma);
- qunlock(s);
- }
- if (audio.amode == 0) {
- /* turn audio off */
- egpiobits(EGPIO_codec_reset, 0);
- audioicpower(0);
- }
- qunlock(&audio);
- }
- break;
- }
- }
- static long
- audioread(Chan *c, void *v, long n, vlong off)
- {
- int liv, riv, lov, rov;
- long m, n0;
- char buf[300];
- int j;
- ulong offset = off;
- char *p;
- IOstate *s;
- n0 = n;
- p = v;
- switch((ulong)c->qid.path) {
- default:
- error(Eperm);
- break;
- case Qdir:
- return devdirread(c, p, n, audiodir, nelem(audiodir), devgen);
- case Qaudio:
- if (debug > 1) print("#A: read %ld\n", n);
- if((audio.amode & Aread) == 0)
- error(Emode);
- s = &audio.i;
- qlock(s);
- if(waserror()){
- qunlock(s);
- nexterror();
- }
- while(n > 0) {
- if(s->emptying->nbytes == 0) {
- if (debug > 1) print("#A: emptied @%p\n", s->emptying);
- recvaudio(s);
- s->emptying++;
- if (s->emptying == &s->buf[Nbuf])
- s->emptying = s->buf;
- }
- /* wait if dma in progress */
- while (!dmaidle(s->dma) && s->emptying == s->current) {
- if (debug > 1) print("#A: sleep\n");
- sleep(&s->vous, audioqnotempty, s);
- }
- m = (s->emptying->nbytes > n)? n: s->emptying->nbytes;
- memmove(p, s->emptying->virt + volumes[Vbufsize].ilval -
- s->emptying->nbytes, m);
- s->emptying->nbytes -= m;
- n -= m;
- p += m;
- }
- poperror();
- qunlock(s);
- break;
- case Qstatus:
- buf[0] = 0;
- snprint(buf, sizeof(buf), "bytes %lud\ntime %lld\n",
- audio.totcount, audio.tottime);
- return readstr(offset, p, n, buf);
- case Qstats:
- buf[0] = 0;
- snprint(buf, sizeof(buf),
- "bytes %lud\nRX dmas %lud, while idle %lud, while busy %lud, "
- "out-of-order %lud, empty dmas %lud\n"
- "TX dmas %lud, while idle %lud, while busy %lud, "
- "out-of-order %lud, empty dmas %lud\n",
- iostats.bytes, iostats.rx.totaldma, iostats.rx.idledma,
- iostats.rx.faildma, iostats.rx.samedma, iostats.rx.empties,
- iostats.tx.totaldma, iostats.tx.idledma,
- iostats.tx.faildma, iostats.tx.samedma, iostats.tx.empties);
- return readstr(offset, p, n, buf);
- case Qvolume:
- j = 0;
- buf[0] = 0;
- for(m=0; volumes[m].name; m++){
- if (m == Vaudio) {
- liv = audio.livol[m];
- riv = audio.rivol[m];
- lov = audio.lovol[m];
- rov = audio.rovol[m];
- }
- else {
- lov = liv = volumes[m].ilval;
- rov = riv = volumes[m].irval;
- }
-
- j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name);
- if((volumes[m].flag & Fmono) || liv==riv && lov==rov){
- if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov)
- j += snprint(buf+j, sizeof(buf)-j, " %d", liv);
- else{
- if(volumes[m].flag & Fin)
- j += snprint(buf+j, sizeof(buf)-j,
- " in %d", liv);
- if(volumes[m].flag & Fout)
- j += snprint(buf+j, sizeof(buf)-j,
- " out %d", lov);
- }
- }else{
- if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) &&
- liv==lov && riv==rov)
- j += snprint(buf+j, sizeof(buf)-j,
- " left %d right %d",
- liv, riv);
- else{
- if(volumes[m].flag & Fin)
- j += snprint(buf+j, sizeof(buf)-j,
- " in left %d right %d",
- liv, riv);
- if(volumes[m].flag & Fout)
- j += snprint(buf+j, sizeof(buf)-j,
- " out left %d right %d",
- lov, rov);
- }
- }
- j += snprint(buf+j, sizeof(buf)-j, "\n");
- }
- return readstr(offset, p, n, buf);
- }
- return n0-n;
- }
- static void
- setaudio(int in, int out, int left, int right, int value)
- {
- if (value < 0 || value > 100)
- error(Evolume);
- if(left && out)
- audio.lovol[Vaudio] = value;
- if(left && in)
- audio.livol[Vaudio] = value;
- if(right && out)
- audio.rovol[Vaudio] = value;
- if(right && in)
- audio.rivol[Vaudio] = value;
- }
- static void
- setspeed(int, int, int, int, int speed)
- {
- uchar clock;
- /* external clock configured for 44100 samples/sec */
- switch (speed) {
- case 32000:
- /* 00 */
- gpioregs->clear = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
- clock = SC384FS; /* Only works in MSB mode! */
- break;
- case 48000:
- /* 00 */
- gpioregs->clear = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
- clock = SC256FS;
- break;
- default:
- speed = 44100;
- case 44100:
- /* 01 */
- gpioregs->set = GPIO_CLK_SET0_o;
- gpioregs->clear = GPIO_CLK_SET1_o;
- clock = SC256FS;
- break;
- case 8000:
- /* 10 */
- gpioregs->set = GPIO_CLK_SET1_o;
- gpioregs->clear = GPIO_CLK_SET0_o;
- clock = SC512FS; /* Only works in MSB mode! */
- break;
- case 16000:
- /* 10 */
- gpioregs->set = GPIO_CLK_SET1_o;
- gpioregs->clear = GPIO_CLK_SET0_o;
- clock = SC256FS;
- break;
- case 11025:
- /* 11 */
- gpioregs->set = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
- clock = SC512FS; /* Only works in MSB mode! */
- break;
- case 22050:
- /* 11 */
- gpioregs->set = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o;
- clock = SC256FS;
- break;
- }
- /* Wait for the UDA1341 to wake up */
- delay(100);
- /* Reset the chip */
- status0[0] &= ~CLOCKMASK;
- status0[0] |=clock;
- volumes[Vspeed].ilval = speed;
- }
- static void
- setbufsize(int, int, int, int, int value)
- {
- if ((value % 8) != 0 || value < 8 || value >= Bufsize)
- error(Ebadarg);
- volumes[Vbufsize].ilval = value;
- }
- static long
- audiowrite(Chan *c, void *vp, long n, vlong)
- {
- long m, n0;
- int i, v, left, right, in, out;
- char *p;
- IOstate *a;
- Cmdbuf *cb;
- p = vp;
- n0 = n;
- switch((ulong)c->qid.path) {
- default:
- error(Eperm);
- break;
- case Qvolume:
- v = Vaudio;
- left = 1;
- right = 1;
- in = 1;
- out = 1;
- cb = parsecmd(p, n);
- if(waserror()){
- free(cb);
- nexterror();
- }
- for(i = 0; i < cb->nf; i++){
- /*
- * a number is volume
- */
- if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') {
- m = strtoul(cb->f[i], 0, 10);
- if (volumes[v].setval)
- volumes[v].setval(in, out, left, right, m);
- goto cont0;
- }
- for(m=0; volumes[m].name; m++) {
- if(strcmp(cb->f[i], volumes[m].name) == 0) {
- v = m;
- in = 1;
- out = 1;
- left = 1;
- right = 1;
- goto cont0;
- }
- }
- if(strcmp(cb->f[i], "reset") == 0) {
- resetlevel();
- goto cont0;
- }
- if(strcmp(cb->f[i], "debug") == 0) {
- debug = debug?0:1;
- goto cont0;
- }
- if(strcmp(cb->f[i], "in") == 0) {
- in = 1;
- out = 0;
- goto cont0;
- }
- if(strcmp(cb->f[i], "out") == 0) {
- in = 0;
- out = 1;
- goto cont0;
- }
- if(strcmp(cb->f[i], "left") == 0) {
- left = 1;
- right = 0;
- goto cont0;
- }
- if(strcmp(cb->f[i], "right") == 0) {
- left = 0;
- right = 1;
- goto cont0;
- }
- if(strcmp(cb->f[i], "reg") == 0) {
- if(cb->nf < 3)
- error(Evolume);
- setreg(cb->f[1], atoi(cb->f[2]), cb->nf == 4 ? atoi(cb->f[3]):1);
- return n0;
- }
- error(Evolume);
- break;
- cont0:;
- }
- mxvolume();
- poperror();
- free(cb);
- break;
- case Qaudio:
- if (debug > 1) print("#A: write %ld\n", n);
- if((audio.amode & Awrite) == 0)
- error(Emode);
- a = &audio.o;
- qlock(a);
- if(waserror()){
- qunlock(a);
- nexterror();
- }
- while(n > 0) {
- /* wait if dma in progress */
- while (!dmaidle(a->dma) && !zerodma && a->filling == a->current) {
- if (debug > 1) print("#A: sleep\n");
- sleep(&a->vous, audioqnotfull, a);
- }
- m = volumes[Vbufsize].ilval - a->filling->nbytes;
- if(m > n)
- m = n;
- memmove(a->filling->virt + a->filling->nbytes, p, m);
- a->filling->nbytes += m;
- n -= m;
- p += m;
- if(a->filling->nbytes >= volumes[Vbufsize].ilval) {
- if (debug > 1) print("#A: filled @%p\n", a->filling);
- a->filling++;
- if (a->filling == &a->buf[Nbuf])
- a->filling = a->buf;
- sendaudio(a);
- }
- }
- poperror();
- qunlock(a);
- break;
- }
- return n0 - n;
- }
- Dev uda1341devtab = {
- 'A',
- "audio",
- devreset,
- audioinit,
- devshutdown,
- audioattach,
- audiowalk,
- audiostat,
- audioopen,
- devcreate,
- audioclose,
- audioread,
- devbread,
- audiowrite,
- devbwrite,
- devremove,
- devwstat,
- audiopower,
- };
|