From 98fa67bbffa0242d369873fc94f8dd8e7c6b4307 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 3 Nov 2000 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2000-11-03 --- bitsy/devuda1341.c | 990 +++++++++++++++++++++++++++++++++++++++++ "bitsy/dev\302\265c.c" | 9 +- bitsy/sa1110dma.c | 131 ++++++ bitsy/screen.c | 47 +- 4 files changed, 1159 insertions(+), 18 deletions(-) create mode 100644 bitsy/devuda1341.c create mode 100644 bitsy/sa1110dma.c diff --git a/bitsy/devuda1341.c b/bitsy/devuda1341.c new file mode 100644 index 0000000000000000000000000000000000000000..758c66a7e05f7f91c4813adef8f272f0c273a0c6 --- /dev/null +++ b/bitsy/devuda1341.c @@ -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 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; iirq){ + 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; +} diff --git "a/bitsy/dev\302\265c.c" "b/bitsy/dev\302\265c.c" index e5204363aacb7af81495f43d6969a995b721f27e..a06ebdaf5c9bb6fb8fbb83a663c4530db303c6d0 100644 --- "a/bitsy/dev\302\265c.c" +++ "b/bitsy/dev\302\265c.c" @@ -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); } diff --git a/bitsy/sa1110dma.c b/bitsy/sa1110dma.c new file mode 100644 index 0000000000000000000000000000000000000000..24770fcf71ba7934384ac3e4b0a32a758de443be --- /dev/null +++ b/bitsy/sa1110dma.c @@ -0,0 +1,131 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +/* + * DMA helper routines + */ + +enum { + NDMA = 6, /* Number of DMA channels */ + DMAREGS = 0xb0000000, /* DMA registers, physical */ +}; + +enum { + /* Device Address Register, DDAR */ + RW = 0, + E = 1, + BS = 2, + DW = 3, + DS = 4, /* bits 4 - 7 */ + DA = 8 /* bits 8 - 31 */ +}; + +enum { + /* Device Control & Status Register, DCSR */ + RUN = 0, + IE = 1, + ERROR = 2, + DONEA = 3, + STRTA = 4, + DONEB = 5, + STRTB = 6, + BIU = 7 +}; + +struct { + Lock; + Rendez r; + int channels; +} dma; + +struct dmaregs { + ulong ddar; + ulong dcsr_set; + ulong dcsr_clr; + ulong dcsr_rd; + ulong dstrtA; + ulong dxcntA; + ulong dstrtB; + ulong dxcntB; +} *dmaregs; + +void +dmainit(void) { + /* map the lcd regs into the kernel's virtual space */ + dmaregs = (struct dmaregs*)mapspecial(DMAREGS, NDMA*sizeof(struct dmaregs));; +} + +int +dmaalloc(int rd, int bigendian, int burstsize, int datumsize, int device, void *port) { + int i; + + lock(&dma); + for (i = 0; i < NDMA; i++) { + if (dma.channels & (1 << i)) + continue; + dma.channels |= 1 << i; + unlock(&dma); + dmaregs[i].ddar = + (rd?1:0)<lccr0 = 1<nf < 4) + return; + if(*cmd->f[0] == 'h') + lcd->lccr1 = ((Wid-16)<f[1])<f[2])<f[3])<f[0] == 'v') + lcd->lccr2 = ((Ht-1)<f[1])<f[2])<f[3])<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();