@@ 0,0 1,990 @@
+/*
+ * 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"
+
+/*
+ * 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 us)
+ */
+#define L3_DataSetupTime 1 /* 190 ns */
+#define L3_DataHoldTime 1 /* 30 ns */
+#define L3_ModeSetupTime 1 /* 190 ns */
+#define L3_ModeHoldTime 1 /* 190 ns */
+#define L3_ClockHighTime 100 /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
+#define L3_ClockLowTime 100 /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
+#define L3_HaltTime 1 /* 190 ns */
+
+/* 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
+ */
+#define UDA1341_L3Addr 5
+#define UDA1341_DATA0 0
+#define UDA1341_DATA1 1
+#define UDA1341_STATUS 2
+
+typedef struct AQueue AQueue;
+typedef struct Buf Buf;
+
+enum
+{
+ Qdir = 0,
+ Qaudio,
+ Qvolume,
+ Qstatus,
+
+ Fmono = 1,
+ Fin = 2,
+ Fout = 4,
+
+ Aclosed = 0,
+ Aread,
+ Awrite,
+
+ Vaudio = 0,
+ Vsynth,
+ Vcd,
+ Vline,
+ Vmic,
+ Vspeaker,
+ Vtreb,
+ Vbass,
+ Vspeed,
+ Nvol,
+
+ Bufsize = 16*1024, /* 92 ms each */
+ Nbuf = 16, /* 1.5 seconds total */
+
+ Speed = 44100,
+ Ncmd = 50, /* max volume command words */
+};
+
+Dirtab
+audiodir[] =
+{
+ "audio", {Qaudio}, 0, 0666,
+ "volume", {Qvolume}, 0, 0666,
+ "audiostat",{Qstatus}, 0, 0444,
+};
+
+struct Buf
+{
+ uchar* virt;
+ ulong phys;
+ Buf* next;
+};
+struct AQueue
+{
+ Lock;
+ Buf* first;
+ Buf* last;
+};
+static struct
+{
+ QLock;
+ Rendez vous;
+ int bufinit; /* boolean if buffers allocated */
+ int curcount; /* how much data in current buffer */
+ int active; /* boolean dma running */
+ int intr; /* boolean an interrupt has happened */
+ int amode; /* Aclosed/Aread/Awrite for /audio */
+ int rivol[Nvol]; /* right/left input/output volumes */
+ int livol[Nvol];
+ int rovol[Nvol];
+ int lovol[Nvol];
+ int major; /* SB16 major version number (sb 4) */
+ int minor; /* SB16 minor version number */
+ ulong totcount; /* how many bytes processed since open */
+ vlong tottime; /* time at which totcount bytes were processed */
+
+ Buf buf[Nbuf]; /* buffers and queues */
+ AQueue empty;
+ AQueue full;
+ Buf* current;
+ Buf* filling;
+} audio;
+
+static struct
+{
+ char* name;
+ int flag;
+ int ilval; /* initial values */
+ int irval;
+} volumes[] =
+{
+[Vaudio] "audio", Fout, 50, 50,
+[Vsynth] "synth", Fin|Fout, 0, 0,
+[Vcd] "cd", Fin|Fout, 0, 0,
+[Vline] "line", Fin|Fout, 0, 0,
+[Vmic] "mic", Fin|Fout|Fmono, 0, 0,
+[Vspeaker] "speaker", Fout|Fmono, 0, 0,
+
+[Vtreb] "treb", Fout, 50, 50,
+[Vbass] "bass", Fout, 50, 50,
+
+[Vspeed] "speed", Fin|Fout|Fmono, Speed, Speed,
+ 0
+};
+
+/*
+ * Grab control of the IIC/L3 shared pins
+ */
+static inline void L3_acquirepins(void)
+{
+ GPSR = (GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
+ GPDR |= (GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
+}
+
+/*
+ * Release control of the IIC/L3 shared pins
+ */
+static inline void L3_releasepins(void)
+{
+ GPDR &= ~(GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
+ GPCR = (GPIO_L3_SCLK_o | GPIO_L3_SDA_io);
+}
+
+/*
+ * Initialize the interface
+ */
+static void __init L3_init(void)
+{
+ GAFR &= ~(GPIO_L3_SDA_io | GPIO_L3_SCLK_o | GPIO_L3_MODE_o);
+ GPSR = GPIO_L3_MODE_o;
+ GPDR |= 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 inline int L3_getbit(void)
+{
+ int data;
+
+ GPCR = GPIO_L3_SCLK_o;
+ udelay(L3_ClockLowTime);
+
+ data = (GPLR & GPIO_L3_SDA_io) ? 1 : 0;
+
+ GPSR = GPIO_L3_SCLK_o;
+ udelay(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 inline void L3_sendbit(int bit)
+{
+ GPCR = GPIO_L3_SCLK_o;
+
+ if (bit & 1)
+ GPSR = GPIO_L3_SDA_io;
+ else
+ GPCR = GPIO_L3_SDA_io;
+
+ /* Assumes L3_DataSetupTime < L3_ClockLowTime */
+ udelay(L3_ClockLowTime);
+
+ GPSR = GPIO_L3_SCLK_o;
+ udelay(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;
+
+ L3_acquirepins();
+
+ switch(mode) {
+ case 0: /* Address mode */
+ GPCR = GPIO_L3_MODE_o;
+ break;
+ case 1: /* First data byte */
+ break;
+ default: /* Subsequent bytes */
+ GPCR = GPIO_L3_MODE_o;
+ udelay(L3_HaltTime);
+ GPSR = GPIO_L3_MODE_o;
+ break;
+ }
+
+ udelay(L3_ModeSetupTime);
+
+ for (i = 0; i < 8; i++)
+ L3_sendbit(data >> i);
+
+ if (mode == 0) /* Address mode */
+ GPSR = GPIO_L3_MODE_o;
+
+ udelay(L3_ModeHoldTime);
+
+ L3_releasepins();
+}
+
+/*
+ * 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;
+
+ L3_acquirepins();
+ GPDR &= ~(GPIO_L3_SDA_io);
+
+ switch(mode) {
+ case 0: /* Address mode - never valid */
+ break;
+ case 1: /* First data byte */
+ break;
+ default: /* Subsequent bytes */
+ GPCR = GPIO_L3_MODE_o;
+ udelay(L3_HaltTime);
+ GPSR = GPIO_L3_MODE_o;
+ break;
+ }
+
+ udelay(L3_ModeSetupTime);
+
+ for (i = 0; i < 8; i++)
+ data |= (L3_getbit() << i);
+
+ udelay(L3_ModeHoldTime);
+
+ L3_releasepins();
+
+ 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(char addr, char *data, int len)
+{
+ int mode = 0;
+ int bytes = len;
+
+ L3_sendbyte(addr, mode++);
+ while(len--)
+ L3_sendbyte(*data++, mode++);
+
+ 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).
+ */
+static int L3_read(char addr, char * data, int len)
+{
+ int mode = 0;
+ int bytes = len;
+
+ L3_sendbyte(addr, mode++);
+ while(len--)
+ *data++ = L3_getbyte(mode++);
+
+ return bytes;
+}
+
+static void swab(uchar*);
+
+static char Emajor[] = "soundblaster not responding/wrong version";
+static char Emode[] = "illegal open mode";
+static char Evolume[] = "illegal volume specifier";
+
+static int
+sbcmd(int val)
+{
+ int i, s;
+
+ for(i=1<<16; i!=0; i--) {
+ s = inb(blaster.wstatus);
+ if((s & 0x80) == 0) {
+ outb(blaster.write, val);
+ return 0;
+ }
+ }
+/* print("#A: sbcmd (0x%.2x) timeout\n", val); /**/
+ return 1;
+}
+
+static int
+sbread(void)
+{
+ int i, s;
+
+ for(i=1<<16; i!=0; i--) {
+ s = inb(blaster.rstatus);
+ if((s & 0x80) != 0) {
+ return inb(blaster.read);
+ }
+ }
+/* print("#A: sbread did not respond\n"); /**/
+ return -1;
+}
+
+static int
+ess1688w(int reg, int val)
+{
+ if(sbcmd(reg) || sbcmd(val))
+ return 1;
+
+ return 0;
+}
+
+static int
+ess1688r(int reg)
+{
+ if(sbcmd(0xC0) || sbcmd(reg))
+ return -1;
+
+ return sbread();
+}
+
+static int
+mxcmd(int addr, int val)
+{
+
+ outb(blaster.mixaddr, addr);
+ outb(blaster.mixdata, val);
+ return 1;
+}
+
+static int
+mxread(int addr)
+{
+ int s;
+
+ outb(blaster.mixaddr, addr);
+ s = inb(blaster.mixdata);
+ return s;
+}
+
+static void
+mxcmds(int s, int v)
+{
+
+ if(v > 100)
+ v = 100;
+ if(v < 0)
+ v = 0;
+ mxcmd(s, (v*255)/100);
+}
+
+static void
+mxcmdt(int s, int v)
+{
+
+ if(v > 100)
+ v = 100;
+ if(v <= 0)
+ mxcmd(s, 0);
+ else
+ mxcmd(s, 255-100+v);
+}
+
+static void
+mxcmdu(int s, int v)
+{
+
+ if(v > 100)
+ v = 100;
+ if(v <= 0)
+ v = 0;
+ mxcmd(s, 128-50+v);
+}
+
+static void
+mxvolume(void)
+{
+ int *left, *right;
+ int source;
+
+ if(audio.amode == Aread){
+ left = audio.livol;
+ right = audio.rivol;
+ }else{
+ left = audio.lovol;
+ right = audio.rovol;
+ }
+
+ ilock(&blaster);
+
+ mxcmd(0x30, 255); /* left master */
+ mxcmd(0x31, 255); /* right master */
+ mxcmd(0x3f, 0); /* left igain */
+ mxcmd(0x40, 0); /* right igain */
+ mxcmd(0x41, 0); /* left ogain */
+ mxcmd(0x42, 0); /* right ogain */
+
+ mxcmds(0x32, left[Vaudio]);
+ mxcmds(0x33, right[Vaudio]);
+
+ mxcmds(0x34, left[Vsynth]);
+ mxcmds(0x35, right[Vsynth]);
+
+ mxcmds(0x36, left[Vcd]);
+ mxcmds(0x37, right[Vcd]);
+
+ mxcmds(0x38, left[Vline]);
+ mxcmds(0x39, right[Vline]);
+
+ mxcmds(0x3a, left[Vmic]);
+ mxcmds(0x3b, left[Vspeaker]);
+
+ mxcmdu(0x44, left[Vtreb]);
+ mxcmdu(0x45, right[Vtreb]);
+
+ mxcmdu(0x46, left[Vbass]);
+ mxcmdu(0x47, right[Vbass]);
+
+ source = 0;
+ if(left[Vsynth])
+ source |= 1<<6;
+ if(right[Vsynth])
+ source |= 1<<5;
+ if(left[Vaudio])
+ source |= 1<<4;
+ if(right[Vaudio])
+ source |= 1<<3;
+ if(left[Vcd])
+ source |= 1<<2;
+ if(right[Vcd])
+ source |= 1<<1;
+ if(left[Vmic])
+ source |= 1<<0;
+ if(audio.amode == Aread)
+ mxcmd(0x3c, 0); /* output switch */
+ else
+ mxcmd(0x3c, source);
+ mxcmd(0x3d, source); /* input left switch */
+ mxcmd(0x3e, source); /* input right switch */
+ iunlock(&blaster);
+}
+
+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);
+}
+
+/*
+ * move the dma to the next buffer
+ */
+static void
+contindma(void)
+{
+ Buf *b;
+
+ if(!audio.active)
+ goto shutdown;
+
+ b = audio.current;
+ if(audio.amode == Aread) {
+ if(b) /* shouldn't happen */
+ putbuf(&audio.full, b);
+ b = getbuf(&audio.empty);
+ } else {
+ if(b) /* shouldn't happen */
+ putbuf(&audio.empty, b);
+ b = getbuf(&audio.full);
+ }
+ audio.current = b;
+ if(b == 0)
+ goto shutdown;
+
+ if(dmasetup(blaster.dma, b->virt, Bufsize, audio.amode == Aread) >= 0)
+ return;
+ print("#A: dmasetup fail\n");
+ putbuf(&audio.empty, b);
+
+shutdown:
+ dmaend(blaster.dma);
+ sbcmd(0xd9); /* exit at end of count */
+ sbcmd(0xd5); /* pause */
+ audio.curcount = 0;
+ audio.active = 0;
+}
+
+/*
+ * cause sb to get an interrupt per buffer.
+ * start first dma
+ */
+static void
+sb16startdma(void)
+{
+ ulong count;
+ int speed;
+
+ ilock(&blaster);
+ dmaend(blaster.dma);
+ if(audio.amode == Aread) {
+ sbcmd(0x42); /* input sampling rate */
+ speed = audio.livol[Vspeed];
+ } else {
+ sbcmd(0x41); /* output sampling rate */
+ speed = audio.lovol[Vspeed];
+ }
+ sbcmd(speed>>8);
+ sbcmd(speed);
+
+ count = (Bufsize >> 1) - 1;
+ if(audio.amode == Aread)
+ sbcmd(0xbe); /* A/D, autoinit */
+ else
+ sbcmd(0xb6); /* D/A, autoinit */
+ sbcmd(0x30); /* stereo, 16 bit */
+ sbcmd(count);
+ sbcmd(count>>8);
+
+ audio.active = 1;
+ audio.tottime = todget(nil);
+ contindma();
+ iunlock(&blaster);
+}
+
+static int
+ess1688reset(void)
+{
+ int i;
+
+ outb(blaster.reset, 3);
+ delay(1); /* >3 υs */
+ outb(blaster.reset, 0);
+ delay(1);
+
+ i = sbread();
+ if(i != 0xAA) {
+ print("#A: no response 0x%.2x\n", i);
+ return 1;
+ }
+
+ if(sbcmd(0xC6)){ /* extended mode */
+ print("#A: barf 3\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+ess1688startdma(void)
+{
+ ulong count;
+ int speed, x;
+
+ ilock(&blaster);
+ dmaend(blaster.dma);
+
+ if(audio.amode == Awrite)
+ ess1688reset();
+ if(audio.amode == Aread)
+ sbcmd(0xD3); /* speaker off */
+
+ /*
+ * Set the speed.
+ */
+ if(audio.amode == Aread)
+ speed = audio.livol[Vspeed];
+ else
+ speed = audio.lovol[Vspeed];
+ if(speed < 4000)
+ speed = 4000;
+ else if(speed > 48000)
+ speed = 48000;
+
+ if(speed > 22000)
+ x = 0x80|(256-(795500+speed/2)/speed);
+ else
+ x = 128-(397700+speed/2)/speed;
+ ess1688w(0xA1, x & 0xFF);
+
+ speed = (speed * 9) / 20;
+ x = 256 - 7160000 / (speed * 82);
+ ess1688w(0xA2, x & 0xFF);
+
+ if(audio.amode == Aread)
+ ess1688w(0xB8, 0x0E); /* A/D, autoinit */
+ else
+ ess1688w(0xB8, 0x04); /* D/A, autoinit */
+ x = ess1688r(0xA8) & ~0x03;
+ ess1688w(0xA8, x|0x01); /* 2 channels */
+ ess1688w(0xB9, 2); /* demand mode, 4 bytes per request */
+
+ if(audio.amode == Awrite)
+ ess1688w(0xB6, 0);
+ ess1688w(0xB7, 0x71);
+ ess1688w(0xB7, 0xBC);
+
+ x = ess1688r(0xB1) & 0x0F;
+ ess1688w(0xB1, x|0x50);
+ x = ess1688r(0xB2) & 0x0F;
+ ess1688w(0xB2, x|0x50);
+ if(audio.amode == Awrite)
+ sbcmd(0xD1); /* speaker on */
+
+ count = -Bufsize;
+ ess1688w(0xA4, count & 0xFF);
+ ess1688w(0xA5, (count>>8) & 0xFF);
+ x = ess1688r(0xB8);
+ ess1688w(0xB8, x|0x05);
+
+ audio.active = 1;
+ audio.tottime = todget(nil);
+ contindma();
+ iunlock(&blaster);
+}
+
+/*
+ * if audio is stopped,
+ * start it up again.
+ */
+static void
+pokeaudio(void)
+{
+ if(!audio.active)
+ blaster.startdma();
+}
+
+static void
+sb16intr(void)
+{
+ int stat, dummy;
+
+ stat = mxread(0x82) & 7; /* get irq status */
+ if(stat) {
+ dummy = 0;
+ if(stat & 2) {
+ ilock(&blaster);
+ dummy = inb(blaster.clri16);
+ audio.totcount += Bufsize;
+ audio.tottime = todget(nil);
+ contindma();
+ iunlock(&blaster);
+ audio.intr = 1;
+ wakeup(&audio.vous);
+ }
+ if(stat & 1) {
+ dummy = inb(blaster.clri8);
+ }
+ if(stat & 4) {
+ dummy = inb(blaster.clri401);
+ }
+ USED(dummy);
+ }
+}
+
+static void
+ess1688intr(void)
+{
+ int dummy;
+
+ if(audio.active){
+ ilock(&blaster);
+ audio.totcount += Bufsize;
+ audio.tottime = todget(nil);
+ contindma();
+ dummy = inb(blaster.clri8);
+ iunlock(&blaster);
+ audio.intr = 1;
+ wakeup(&audio.vous);
+ USED(dummy);
+ }
+ else
+ print("#A: unexpected ess1688 interrupt\n");
+}
+
+void
+audiosbintr(void)
+{
+ /*
+ * Carrera interrupt interface.
+ */
+ blaster.intr();
+}
+
+static void
+pcaudiosbintr(Ureg*, void*)
+{
+ /*
+ * x86 interrupt interface.
+ */
+ blaster.intr();
+}
+
+void
+audiodmaintr(void)
+{
+/* print("#A: dma interrupt\n"); /**/
+}
+
+static int
+anybuf(void*)
+{
+ return audio.intr;
+}
+
+/*
+ * wait for some output to get
+ * empty buffers back.
+ */
+static void
+waitaudio(void)
+{
+
+ audio.intr = 0;
+ pokeaudio();
+ tsleep(&audio.vous, anybuf, 0, 10*1000);
+ if(audio.intr == 0) {
+/* print("#A: audio timeout\n"); /**/
+ audio.active = 0;
+ pokeaudio();
+ }
+}
+
+static void
+sbbufinit(void)
+{
+ int i;
+ void *p;
+
+ for(i=0; i<Nbuf; i++) {
+ p = xspanalloc(Bufsize, CACHELINESZ, 64*1024);
+ dcflush(p, Bufsize);
+ audio.buf[i].virt = UNCACHED(uchar, p);
+ audio.buf[i].phys = (ulong)PADDR(p);
+ }
+}
+
+static void
+setempty(void)
+{
+ int i;
+
+ ilock(&blaster);
+ audio.empty.first = 0;
+ audio.empty.last = 0;
+ audio.full.first = 0;
+ audio.full.last = 0;
+ audio.current = 0;
+ audio.filling = 0;
+ for(i=0; i<Nbuf; i++)
+ putbuf(&audio.empty, &audio.buf[i]);
+ audio.totcount = 0;
+ audio.tottime = 0LL;
+ iunlock(&blaster);
+}
+
+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 int
+ess1688(ISAConf* sbconf)
+{
+ int i, major, minor;
+
+ /*
+ * Try for ESS1688.
+ */
+ sbcmd(0xE7); /* get version */
+ major = sbread();
+ minor = sbread();
+ if(major != 0x68 || minor != 0x8B){
+ print("#A: model 0x%.2x 0x%.2x; not ESS1688 compatible\n", major, minor);
+ return 1;
+ }
+
+ ess1688reset();
+
+ switch(sbconf->irq){
+ case 2:
+ case 9:
+ i = 0x50|(0<<2);
+ break;
+ case 5:
+ i = 0x50|(1<<2);
+ break;
+ case 7:
+ i = 0x50|(2<<2);
+ break;
+ case 10:
+ i = 0x50|(3<<2);
+ break;
+ default:
+ print("#A: bad ESS1688 irq %lud\n", sbconf->irq);
+ return 1;
+ }
+ ess1688w(0xB1, i);
+
+ switch(sbconf->dma){
+ case 0:
+ i = 0x50|(1<<2);
+ break;
+ case 1:
+ i = 0xF0|(2<<2);
+ break;
+ case 3:
+ i = 0x50|(3<<2);
+ break;
+ default:
+ print("#A: bad ESS1688 dma %lud\n", sbconf->dma);
+ return 1;
+ }
+ ess1688w(0xB2, i);
+
+ ess1688reset();
+
+ blaster.startdma = ess1688startdma;
+ blaster.intr = ess1688intr;
+
+ return 0;
+}
+
+static void
+audioinit(void)
+{
+ int err;
+
+ /* Acquire and initialize DMA */
+ if( audio_init_dma( &output_stream, "UDA1341 DMA out" ) ||
+ audio_init_dma( &input_stream, "UDA1341 DMA in" ) ){
+ audio_clear_dma( &output_stream );
+ audio_clear_dma( &input_stream );
+ return -EBUSY;
+ }
+
+ L3_init();
+ audio_ssp_init();
+
+ audio_uda1341_reset();
+
+ init_waitqueue_head(&audio_waitq);
+
+ /* Set some default mixer values... */
+ STATUS_1.DAC_gain = 1;
+ STATUS_1.ADC_gain = 1;
+ L3_write( (UDA1341_L3Addr<<2)|UDA1341_STATUS, (char*)&STATUS_1, 1 );
+ DATA0_0.volume = 15;
+ L3_write( (UDA1341_L3Addr<<2)|UDA1341_DATA0, (char*)&DATA0_0, 1 );
+ DATA0_2.mode = 3;
+ L3_write( (UDA1341_L3Addr<<2)|UDA1341_DATA0, (char*)&DATA0_2, 1 );
+ DATA0_ext2.mixer_mode = 2;
+ DATA0_ext2.mic_level = 4;
+ L3_write( (UDA1341_L3Addr<<2)|UDA1341_DATA0, (char*)&DATA0_ext2, 2 );
+ DATA0_ext4.AGC_ctrl = 1;
+ L3_write( (UDA1341_L3Addr<<2)|UDA1341_DATA0, (char*)&DATA0_ext4, 2 );
+ DATA0_ext6.AGC_level = 3;
+ L3_write( (UDA1341_L3Addr<<2)|UDA1341_DATA0, (char*)&DATA0_ext6, 2 );
+
+ /* register devices */
+ audio_dev_dsp = register_sound_dsp(&UDA1341_dsp_fops, -1);
+ audio_dev_mixer = register_sound_mixer(&UDA1341_mixer_fops, -1);
+
+ printk( AUDIO_NAME_VERBOSE " initialized\n" );
+
+ return 0;
+}
@@ 10,6 10,7 @@ enum{
Qbacklight = 1,
Qbattery,
Qbuttons,
+ Qcruft,
Qkbdin,
Qled,
Qversion,
@@ 35,6 36,7 @@ Dirtab µcdir[]={
"backlight", { Qbacklight, 0 }, 0, 0664,
"battery", { Qbattery, 0 }, 0, 0664,
"buttons", { Qbuttons, 0 }, 0, 0664,
+ "cruft", { Qcruft, 0 }, 0, 0664,
"kbdin", { Qkbdin, 0 }, 0, 0664,
"led", { Qled, 0 }, 0, 0664,
"version", { Qversion, 0 }, 0, 0664,
@@ 157,7 159,7 @@ int
wakeup(&ctlr.r);
break;
default:
- print("unknown µc message: %ux");
+ print("unknown µc message: %ux", ctlr.buf[1] >> 4);
for(i = 0; i < len; i++)
print(" %ux", p[i]);
print("\n");
@@ 310,7 312,7 @@ static long
#define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4)
static long
-µcwrite(Chan* c, void* a, long n, vlong off)
+µcwrite(Chan* c, void* a, long n, vlong)
{
Cmdbuf *cmd;
uchar data[16];
@@ 343,6 345,9 @@ static long
case Qbacklight:
sendmsgwithack(BLbacklight, data, cmd->nf);
break;
+ case Qcruft:
+ lcdtweak(cmd);
+ break;
default:
error(Ebadarg);
}
@@ 18,13 18,13 @@
enum {
Wid = 320,
Ht = 240,
- Pal0 = 0x2000, /* 16-bit pixel data in active mode (12 in passive) */
+ Pal0 = 0x2000, /* 16-bit pixel data in active mode (12 in passive) */
- hsw = 0x04,
- elw = 0x11,
- blw = 0x0c,
+ hsw = 0x00,
+ elw = 0x0e,
+ blw = 0x0d,
- vsw = 0x03,
+ vsw = 0x02,
efw = 0x01,
bfw = 0x0a,
@@ 109,14 109,7 @@ struct sa1110regs {
Point ZP = {0, 0};
-static Memdata xgdata =
-{
- nil, /* *base */
- nil, /* *bdata */
- 1, /* ref */
- nil, /* *imref */
- 0, /* allocd */
-};
+static Memdata xgdata;
static Memimage xgscreen =
{
@@ 177,18 170,35 @@ lcdinit(void)
}
void
+lcdtweak(Cmdbuf *cmd)
+{
+ if(cmd->nf < 4)
+ return;
+ if(*cmd->f[0] == 'h')
+ lcd->lccr1 = ((Wid-16)<<PPL)
+ | (atoi(cmd->f[1])<<HSW)
+ | (atoi(cmd->f[2])<<ELW)
+ | (atoi(cmd->f[3])<<BLW);
+ if(*cmd->f[0] == 'v')
+ lcd->lccr2 = ((Ht-1)<<LPP)
+ | (atoi(cmd->f[1])<<VSW)
+ | (atoi(cmd->f[2])<<EFW)
+ | (atoi(cmd->f[3])<<BFW);
+}
+
+void
screeninit(void)
{
int i;
-iprint("%lux %lux\n", (ulong)framebuf, (ulong)framebuf->pixel);
-
/* map the lcd regs into the kernel's virtual space */
lcd = (struct sa1110regs*)mapspecial(LCDREGS, sizeof(struct sa1110regs));;
- framebuf = xspanalloc(sizeof *framebuf, 0x20, 0);
+ framebuf = xspanalloc(sizeof *framebuf, 0x100, 0);
/* the following works because main memory is direct mapped */
+iprint("framebuf %lux\n", framebuf);
+
framebuf->palette[0] = Pal0;
lcdpower(1);
@@ 196,11 206,16 @@ iprint("%lux %lux\n", (ulong)framebuf, (ulong)framebuf->pixel);
gscreen = &xgscreen;
xgdata.bdata = (uchar *)framebuf->pixel;
+ xgdata.ref = 1;
i = 0;
while (i < Wid*Ht*1/3) framebuf->pixel[i++] = 0xf800; /* red */
while (i < Wid*Ht*2/3) framebuf->pixel[i++] = 0xffff; /* white */
while (i < Wid*Ht*3/3) framebuf->pixel[i++] = 0x001f; /* blue */
+ for(i = 0; i < Wid*Ht; i += Wid)
+ framebuf->pixel[i] = 0xffff; /* white */
+ for(i = Wid-1; i < Wid*Ht; i += Wid)
+ framebuf->pixel[i] = 0x001f; /* blue */
memimageinit();
memdefont = getmemdefont();