/* * 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};Lines 1–500 of 1400. Load more
/* * 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};