From c188ee75798a1166272f077d3567337aa28f9fe6 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 17 Nov 2000 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2000-11-17 --- bitsy/devflash.c | 578 +++++++++++++++++++++++++++++++++++++++------ bitsy/devuda1341.c | 280 ++++++++++++++-------- bitsy/main.c | 3 +- bitsy/sa1110dma.c | 42 ++-- 4 files changed, 710 insertions(+), 193 deletions(-) diff --git a/bitsy/devflash.c b/bitsy/devflash.c index a1cc58bdbc6223e8698b62093fec2967570b1073..613728a600a4c353c6ce4714217880f6b867e348 100644 --- a/bitsy/devflash.c +++ b/bitsy/devflash.c @@ -6,18 +6,6 @@ #include "io.h" #include "../port/error.h" -/* flash partitions */ -typedef struct FlashPart FlashPart; -struct FlashPart -{ - QLock; - char name[NAMELEN]; - ulong start; /* byte offsets */ - ulong end; -}; - -static ulong *flash = (ulong*)FLASHZERO; - /* * on the bitsy, all 32 bit accesses to flash are mapped to two 16 bit * accesses, one to the low half of the chip and the other to the high @@ -27,66 +15,117 @@ static ulong *flash = (ulong*)FLASHZERO; * sectors for each request erase request. */ -/* - * common flash memory interface - */ -struct CFIid -{ - ulong q; - ulong r; - ulong y; - ulong cmd_set; - ulong vendor_alg; - ulong ext_alg_addr[2]; - ulong alt_cmd_set; - ulong alt_vendor_alg; - ulong alt_ext_ald_addr[2]; - +#define mirror(x) (((x)<<16)|(x)) + +/* this defines a contiguous set of erase blocks of one size */ +typedef struct FlashRegion FlashRegion; +struct FlashRegion +{ + ulong addr; /* start of region */ + ulong end; /* end of region + 1 */ + ulong n; /* number of blocks */ + ulong size; /* size of each block */ }; -struct CFIsys -{ - ulong vcc_min; /* 100 mv */ - ulong vcc_max; - ulong vpp_min; - ulong vpp_max; - ulong word_wr_to; /* 2**n µs */ - ulong buf_wr_to; /* 2**n µs */ - ulong block_erase_to; /* 2**n ms */ - ulong chip_erase_to; /* 2**n ms */ - ulong max_word_wr_to; /* 2**n µs */ - ulong max_buf_wr_to; /* 2**n µs */ - ulong max_block_erase_to; /* 2**n ms */ - ulong max_chip_erase_to; /* 2**n ms */ +/* this defines a particular access algorithm */ +typedef struct FlashAlg FlashAlg; +struct FlashAlg +{ + int id; + char *name; + void (*identify)(void); /* identify device */ + void (*erase)(ulong); /* erase a region */ + void (*write)(void*, long, ulong); /* write a region */ }; -struct CFIgeom +static void ise_id(void); +static void ise_erase(ulong); +static void ise_write(void*, long, ulong); + +static void afs_id(void); +static void afs_erase(ulong); +static void afs_write(void*, long, ulong); + +FlashAlg falg[] = { - ulong size; /* 2**n bytes */ - ulong dev_code; /* ??? */ - ulong max_multi; /* max bytes in a multibyte write */ - ulong nregion; /* number of erase regions */ - ulong region[1]; /* erase region info */ + { 1, "Intel/Sharp Extended", ise_id, ise_erase, ise_write }, + { 2, "AMD/Fujitsu Standard", afs_id, afs_erase, afs_write }, }; -#define mirror(x) (((x)<<16)|(x)) +struct +{ + RWlock; + ulong *p; + ushort algid; /* access algorithm */ + FlashAlg *alg; + ushort manid; /* manufacturer id */ + ushort devid; /* device id */ + ulong size; /* size in bytes */ + int wbsize; /* size of write buffer */ + ulong nr; /* number of regions */ + uchar bootprotect; + FlashRegion r[32]; + ulong *wb; /* staging area for write buffer */ +} flash; -void -cfiquery(void) +/* + * common flash interface + */ +static uchar +cfigetc(int off) { - struct CFIid *id; + uchar rv; - flash[0x55] = mirror(0x98); - id = (struct CFIid*)&flash[0x10]; - if(id.q != 'q' || id.r != 'r' || id.y != 'y') - print("CFI not supported by flash\n"); - - flash[0x55] = mirror(0xFF); + flash.p[0x55] = mirror(0x98); + rv = flash.p[off]; + flash.p[0x55] = mirror(0xFF); + return rv; } -void -cfigeom(void) +static ushort +cfigets(int off) +{ + return (cfigetc(off+1)<<8)|cfigetc(off); +} + +static ulong +cfigetl(int off) +{ + return (cfigetc(off+3)<<24)|(cfigetc(off+2)<<16)| + (cfigetc(off+1)<<8)|cfigetc(off); +} + +static void +cfiquery(void) { + uchar q, r, y; + ulong x, addr; + + q = cfigetc(0x10); + r = cfigetc(0x11); + y = cfigetc(0x12); + if(q != 'Q' || r != 'R' || y != 'Y'){ + print("cfi query failed: %ux %ux %ux\n", q, r, y); + return; + } + flash.algid = cfigetc(0x13); + flash.size = 1<<(cfigetc(0x27)+1); + flash.wbsize = 1<<(cfigetc(0x2a)+1); + flash.nr = cfigetc(0x2c); + if(flash.nr > nelem(flash.r)){ + print("cfi reports > %d regions\n", nelem(flash.r)); + flash.nr = nelem(flash.r); + } + addr = 0; + for(q = 0; q < flash.nr; q++){ + x = cfigetl(q+0x2d); + flash.r[q].size = 2*256*(x>>16); + flash.r[q].n = (x&0xffff)+1; + flash.r[q].addr = addr; + addr += flash.r[q].size*flash.r[q].n; + flash.r[q].end = addr; + } + flash.wb = malloc(flash.wbsize); } /* @@ -95,30 +134,35 @@ cfigeom(void) enum { - Qf0=1, - Qf1, - Qf2, - Qf3, + Qfctl=1, + Qfdata, }; Dirtab flashdir[]={ - "f0", { Qf0, 0 }, 0, 0664, - "f1", { Qf1, 0 }, 0, 0664, - "f2", { Qf2, 0 }, 0, 0664, - "f3", { Qf3, 0 }, 0, 0664, + "flashctl", { Qfctl, 0 }, 0, 0664, + "flashdata", { Qfdata, 0 }, 0, 0660, }; void flashinit(void) { + int i; + + flash.p = (ulong*)FLASHZERO; cfiquery(); - cfigeom(); + for(i = 0; i < nelem(falg); i++) + if(flash.algid == falg[i].id){ + flash.alg = &falg[i]; + (*flash.alg->identify)(); + break; + } + flash.bootprotect = 1; } static Chan* flashattach(char* spec) { - return devattach('r', spec); + return devattach('F', spec); } static int @@ -150,16 +194,185 @@ flashclose(Chan*) static long flashread(Chan* c, void* a, long n, vlong off) { - USED(c, a, off); - error("UUO"); + char *buf, *p, *e; + int i; + + if(c->qid.path&CHDIR) + return devdirread(c, a, n, flashdir, nelem(flashdir), devgen); + switch(c->qid.path){ + default: + error(Eperm); + case Qfctl: + buf = smalloc(1024); + e = buf + 1024; + p = seprint(buf, e, "0x%-9lux 0x%-9lux 0x%-9lux 0x%-9lux\n", flash.size, + flash.wbsize, flash.manid, flash.devid); + for(i = 0; i < flash.nr; i++) + p = seprint(p, e, "0x%-9lux 0x%-9lux 0x%-9lux\n", flash.r[i].addr, + flash.r[i].n, flash.r[i].size); + n = readstr(off, a, n, buf); + free(buf); + break; + case Qfdata: + if(!iseve()) + error(Eperm); + if(off >= flash.size) + return 0; + if(off + n > flash.size) + n = flash.size - off; + rlock(&flash); + if(waserror()){ + runlock(&flash); + nexterror(); + } + memmove(a, ((uchar*)FLASHZERO)+off, n); + runlock(&flash); + poperror(); + break; + } + return n; +} + +static void +bootprotect(ulong addr) +{ + FlashRegion *r; + + if(flash.bootprotect == 0) + return; + if(flash.nr == 0) + error("writing over boot loader disallowed"); + r = flash.r; + if(addr >= r->addr && addr < r->addr + r->size) + error("writing over boot loader disallowed"); +} + +ulong +blockstart(ulong addr) +{ + FlashRegion *r, *e; + ulong x; + + r = flash.r; + for(e = &flash.r[flash.nr]; r < e; r++) + if(addr >= r->addr && addr < r->end){ + x = addr - r->addr; + x /= r->size; + return r->addr + x*r->size; + } + + return (ulong)-1; +} + +ulong +blockend(ulong addr) +{ + FlashRegion *r, *e; + ulong x; + + r = flash.r; + for(e = &flash.r[flash.nr]; r < e; r++) + if(addr >= r->addr && addr < r->end){ + x = addr - r->addr; + x /= r->size; + return r->addr + (x+1)*r->size; + } + + return (ulong)-1; +} + +static long +flashctlwrite(char *p, long n) +{ + Cmdbuf *cmd; + ulong addr; + + cmd = parsecmd(p, n); + wlock(&flash); + if(waserror()){ + wunlock(&flash); + nexterror(); + } + if(strcmp(cmd->f[0], "erase") == 0){ + if(cmd->nf != 2) + error(Ebadarg); + addr = atoi(cmd->f[1]); + if(addr != blockstart(addr)) + error("erase must be a block boundary"); + bootprotect(addr); + (*flash.alg->erase)(addr); + } else if(strcmp(cmd->f[0], "protectboot") == 0){ + if(cmd->nf == 0 || strcmp(cmd->f[1], "off") != 0) + flash.bootprotect = 1; + else + flash.bootprotect = 0; + } else + error(Ebadarg); + poperror(); + wunlock(&flash); + free(cmd); + + return n; +} + +static long +flashdatawrite(uchar *p, long n, long off) +{ + uchar *end; + int m; + long ooff = off; + uchar *op = p; + + if((off & 0x3) || (n & 0x3)) + error("only quad writes"); + if(off >= flash.size || off+n > flash.size || n <= 0) + error(Ebadarg); + + wlock(&flash); + if(waserror()){ + wunlock(&flash); + nexterror(); + } + + /* make sure we're not writing the boot sector */ + bootprotect(off); + + /* (*flash.alg->write) can't cross blocks */ + for(end = p + n; p < end; p += m){ + m = blockend(off) - off; + if(m > end - p) + m = end - p; + (*flash.alg->write)(p, m, off); + off += m; + } + + /* make sure write succeeded */ + if(memcmp(op, &flash.p[ooff>>2], n) != 0) + error("written bytes don't match"); + + wunlock(&flash); + poperror(); + return n; } static long -flashwrite(Chan* c, void* a, long n, vlong) +flashwrite(Chan* c, void* a, long n, vlong off) { - USED(c, a, off); - error("UUO"); + if(c->qid.path & CHDIR) + error(Eperm); + + if(!iseve()) + error(Eperm); + + switch(c->qid.path){ + default: + panic("flashwrite"); + case Qfctl: + return flashctlwrite(a, n); + case Qfdata: + return flashdatawrite(a, n, off); + } return n; } @@ -183,3 +396,220 @@ Dev flashdevtab = { devremove, devwstat, }; + + +/* intel/sharp extended command set */ +static void +ise_reset(void) +{ + flash.p[0x55] = mirror(0xff); /* reset */ +} +static void +ise_id(void) +{ + ise_reset(); + flash.p[0x555] = mirror(0x90); /* uncover vendor info */ + flash.manid = flash.p[00]; + flash.devid = flash.p[01]; + ise_reset(); +} +static void +ise_clearerror(void) +{ + flash.p[0x100] = mirror(0x50); + +} +static void +ise_error(int bank, ulong status) +{ + char err[ERRLEN]; + + if(status & (1<<3)){ + sprint(err, "flash%d: low prog voltage", bank); + error(err); + } + if(status & (1<<1)){ + sprint(err, "flash%d: block locked", bank); + error(err); + } + if(status & (1<<5)){ + sprint(err, "flash%d: i/o error", bank); + error(err); + } +} +static void +ise_erase(ulong addr) +{ + ulong start; + ulong x; + + addr >>= 2; /* convert to ulong offset */ + + flashprogpower(1); + flash.p[addr] = mirror(0x20); + flash.p[addr] = mirror(0xd0); + start = m->ticks; + do { + x = flash.p[addr]; + if((x & mirror(1<<7)) == mirror(1<<7)) + break; + } while(TK2MS(m->ticks-start) < 1500); + flashprogpower(0); + + ise_clearerror(); + ise_error(0, x); + ise_error(1, x>>16); + + ise_reset(); +} +/* + * flash writing goes about 16 times faster if we use + * the write buffer. We fill the write buffer and then + * issue the write request. After the write request, + * subsequent reads will yield the status register or, + * since error bits are sticky, another write buffer can + * be filled and written. + * + * On timeout, we issue a read status register request so + * that the status register can be read no matter how we + * exit. + */ +static int +ise_wbwrite(ulong *p, int n, ulong off) +{ + ulong start; + int i; + + /* copy out of user space to avoid faults later */ + memmove(flash.wb, p, n*4); + p = flash.wb; + + /* put flash into write buffer mode */ + start = m->ticks; + for(;;) { + /* request write buffer mode */ + flash.p[off] = mirror(0xe8); + + /* look at extended status reg for status */ + if((flash.p[off] & mirror(1<<7)) == mirror(1<<7)) + break; + + /* didn't work, keep trying for 2 secs */ + if(TK2MS(m->ticks-start) > 2000){ + /* set up to read status */ + flash.p[off] = mirror(0x70); + return -1; + } + } + + /* fill write buffer */ + flash.p[off] = mirror(n-1); + for(i = 0; i < n; i++) + flash.p[off+i] = *p++; + + /* program from buffer */ + flash.p[off] = mirror(0xd0); + + /* subsequent reads will return status about the write */ + + return n; +} +static void +ise_write(void *a, long n, ulong off) +{ + ulong *p, *end; + int i, wbsize; + ulong x, start, ooff; + + /* everything in terms of ulongs */ + wbsize = flash.wbsize>>2; + off >>= 2; + n >>= 2; + p = a; + ooff = off; + + /* first see if write will succeed */ + for(i = 0; i < n; i++) + if((p[i] & flash.p[off+i]) != p[i]) + error("flash needs erase"); + + if(waserror()){ + ise_reset(); + flashprogpower(0); + nexterror(); + } + flashprogpower(1); + + /* + * use the first write to reach + * a write buffer boundary. the intel maunal + * says writes startng at wb boundaries + * maximize speed. + */ + i = wbsize - (off & (wbsize-1)); + for(end = p + n; p < end;){ + if(i > end - p) + i = end - p; + + if(ise_wbwrite(p, i, off) != i) + break; + + off += i; + p += i; + i = wbsize; + } + + /* wait till the programming is done */ + start = m->ticks; + do { + x = flash.p[ooff]; + if((x & mirror(1<<7)) == mirror(1<<7)) + break; + } while(TK2MS(m->ticks-start) < 1000); + + ise_clearerror(); + ise_error(0, x); + ise_error(1, x>>16); + + ise_reset(); + flashprogpower(0); + poperror(); +} + +/* amd/fujitsu standard command set + * I don't have an amd chipset to work with + * so I'm loathe to write this yet. If someone + * else does, please send it to me and I'll + * incorporate it -- presotto@bell-labs.com + */ +static void +afs_reset(void) +{ + flash.p[0x55] = mirror(0xf0); /* reset */ +} +static void +afs_id(void) +{ + afs_reset(); + flash.p[0x55] = mirror(0xf0); /* reset */ + flash.p[0x555] = mirror(0xaa); /* query vendor block */ + flash.p[0x2aa] = mirror(0x55); + flash.p[0x555] = mirror(0x90); + flash.manid = flash.p[00]; + afs_reset(); + flash.p[0x555] = mirror(0xaa); /* query vendor block */ + flash.p[0x2aa] = mirror(0x55); + flash.p[0x555] = mirror(0x90); + flash.devid = flash.p[01]; + afs_reset(); +} +static void +afs_erase(ulong) +{ + error("amd/fujistsu erase not implemented"); +} +static void +afs_write(void*, long, ulong) +{ + error("amd/fujistsu write not implemented"); +} diff --git a/bitsy/devuda1341.c b/bitsy/devuda1341.c index bd3e2a79ca07901f3869e5ef285e0e1ef2fc470a..cb78a1a30fe702cb66fbb4886e35aa0c8611fa15 100644 --- a/bitsy/devuda1341.c +++ b/bitsy/devuda1341.c @@ -41,14 +41,14 @@ static int debug = 1; */ /* - * L3 setup and hold times (expressed in us) + * L3 setup and hold times (expressed in µs) */ #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_ClockHighTime 10 /* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */ +#define L3_ClockLowTime 10 /* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */ #define L3_HaltTime 1 /* 190 ns */ /* UDA 1341 Registers */ @@ -76,7 +76,7 @@ enum { #define UDA1341_DATA0 0 #define UDA1341_DATA1 1 #define UDA1341_STATUS 2 -#define UDA1341_L3Addr 5 +#define UDA1341_L3Addr 0x14 typedef struct AQueue AQueue; typedef struct Buf Buf; @@ -90,7 +90,7 @@ enum Qstatus, Fmono = 1, - Fin = 2, + Fin = 2, Fout = 4, Aclosed = 0, @@ -98,11 +98,7 @@ enum Awrite, Vaudio = 0, - Vsynth, - Vcd, - Vline, Vmic, - Vspeaker, Vtreb, Vbass, Vspeed, @@ -164,6 +160,7 @@ static struct ulong totaldma; ulong idledma; ulong faildma; + ulong samedma; } iostats; static struct @@ -174,15 +171,11 @@ static struct 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, +[Vaudio] "audio", Fout|Fmono, 50, 50, +[Vmic] "mic", Fin, 0, 0, -[Vtreb] "treb", Fout, 50, 50, -[Vbass] "bass", Fout, 50, 50, +[Vtreb] "treb", Fout|Fmono, 50, 50, +[Vbass] "bass", Fout|Fmono, 50, 50, [Vspeed] "speed", Fin|Fout|Fmono, Speed, Speed, 0 @@ -420,19 +413,24 @@ audioinit(void) /* do nothing */ } +uchar status0 = 0x22; +uchar status1 = 0x80; +uchar data00 = 0x00; /* volume control, bits 0 – 5 */ +uchar data01 = 0x40; +uchar data02 = 0x90; +ushort data0e2 = 0xf2c2; +ushort data0e4 = 0xf3c4; +ushort data0e6 = 0xe3c6; + static void -audioenable(void) +enable(void) { - uchar data[2]; + ushort data; L3_init(); /* Setup the uarts */ - ppcregs->assignment &= ~(1<<18); - - gpioregs->altfunc &= ~(GPIO_SSP_TXD_o | GPIO_SSP_RXD_i | GPIO_SSP_SCLK_o | GPIO_SSP_SFRM_o); - gpioregs->altfunc |= (GPIO_SSP_CLK_i); - gpioregs->direction &= ~(GPIO_SSP_CLK_i); + ppcregs->assignment &= ~(1<<18); sspregs->control0 = 0; sspregs->control0 = 0x031f; /* 16 bits, TI frames, serial clock rate 3 */ @@ -445,79 +443,159 @@ audioenable(void) audiomute(0); /* external clock configured for 44100 samples/sec */ - gpioregs->direction |= (GPIO_CLK_SET0_o|GPIO_CLK_SET1_o); -/* This is purportedly the wrong way round: 0 should be set, 1 cleared */ - gpioregs->set = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o; -// gpioregs->clear = GPIO_CLK_SET1_o; + gpioregs->set = GPIO_CLK_SET0_o; + gpioregs->clear = GPIO_CLK_SET1_o; /* Wait for the UDA1341 to wake up */ delay(100); /* Reset the chip */ - data[0] = 2<set = EGPIO_codec_reset; + data = status0 | 1<clear = EGPIO_codec_reset; + gpioregs->set = EGPIO_codec_reset; /* write uda 1341 status[0] */ - data[0] &= ~(1<control0 = 0x%lux\n", sspregs->control0); + print("\tsspregs->control1 = 0x%lux\n", sspregs->control1); + } +} - /* write uda 1341 data0[0] (volume) */ - data[0] = 15 & 0x3f; /* 6 bits, others must be 0 */ - L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 1); +static void +resetlevel(void) +{ + int i; - /* write uda 1341 data0[2] (mode switch) */ - data[0] = 0x80 | 3; /* mode = 3 */ - L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 1); + 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; + } +} - /* set mixer mode and level */ - data[0] = 0xc0 | 2 /* ext address */; - data[1] = 0xe0 | 2 | 4 << 2; /* mixer mode and mic level */ - L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2); +static void +mxvolume(void) { + int *left, *right; - /* set agc control and input amplifier gain */ - data[0] = 0xc0 | 4 /* ext address */; - data[1] = 0xe0 | 1<<4 | 3; /* AGC control and input ampl gain */ - L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2 ); + if(audio.amode & Aread){ + left = audio.livol; + right = audio.rivol; + } + if(audio.amode & Awrite){ + left = audio.lovol; + right = audio.rovol; + data00 &= ~0x3f; + data00 |= ((200-left[Vaudio]-right[Vaudio])*0x3f/200)&0x3f; + L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data00, 1); + if (debug) print("uchar data00 = 0x%2.2ux (audio out)\n", data00); + if (left[Vtreb]+right[Vtreb] <= 100 + && left[Vbass]+right[Vbass] <= 100) { + /* settings neutral */ + data02 &= ~0x03; + L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data02, 1); + if (debug) print("uchar data02 = 0x%2.2ux (mode flat)\n", data02); + } else { + data02 |= 0x03; + L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data02, 1); + if (debug) print("uchar data02 = 0x%2.2ux (mode boost)\n", data02); + data01 |= ~0x3f; + data01 |= ((left[Vtreb]+right[Vtreb]-100)*0x3/100)&0x03; + data01 |= (((left[Vbass]+right[Vbass]-100)*0xf/100)&0xf)<<2; + L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data01, 1); + if (debug) print("uchar data01 = 0x%2.2ux (bass&treb)\n", data01); + } + } - /* set agc time constant and output level */ - data[0] = 0xc0 | 6 /* ext address */; - data[1] = 0xe0 | 0<<2 | 3; /* agc time constant and output level */ - L3_write(UDA1341_L3Addr<<2 | UDA1341_DATA0, data, 2 ); +} +static void +outenable(void) { + /* turn on DAC, set output gain switch */ + status1 |= 0x41; + L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1); + /* set volume */ + data00 |= 0xf; + L3_write(UDA1341_L3Addr | UDA1341_DATA0, (uchar*)&data00, 1); if (debug) { - print("#A: audio enabled\n"); - print("\tsspregs->control0 = 0x%lux\n", sspregs->control0); - print("\tsspregs->control1 = 0x%lux\n", sspregs->control1); + print("uchar status1 = 0x%2.2ux\n", status1); + print("uchar data00 = 0x%2.2ux\n", data00); } } static void -sendaudio(IOstate *b) { +outdisable(void) { + /* turn off DAC, clear output gain switch */ + status1 &= ~0x41; + L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1); + if (debug) { + print("uchar status1 = 0x%2.2ux\n", status1); + } +} + +static void +inenable(void) { + /* turn on ADC, set input gain switch */ + status1 |= 0x22; + L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1); + if (debug) { + print("uchar status1 = 0x%2.2ux\n", status1); + } +} + +static void +indisable(void) { + /* turn off ADC, clear input gain switch */ + status1 &= ~0x22; + L3_write(UDA1341_L3Addr | UDA1341_STATUS, (uchar*)&status1, 1); + if (debug) { + print("uchar status1 = 0x%2.2ux\n", status1); + } +} + +static void +sendaudio(IOstate *s) { /* interrupt routine calls this too */ int n; if (debug > 1) print("#A: sendaudio\n"); - ilock(&b->ilock); - while (b->next != b->filling) { - assert(b->next->nbytes); - if ((n = dmastart(b->dma, b->next->virt, b->next->nbytes)) == 0) { + ilock(&s->ilock); + while (s->next != s->filling) { + assert(s->next->nbytes); + if ((n = dmastart(s->dma, s->next->virt, s->next->nbytes)) == 0) { iostats.faildma++; break; } iostats.totaldma++; - if (n == 1) iostats.idledma++; - if (debug > 1) print("#A: dmastart @%p\n", b->next); - b->next->nbytes = 0; - b->next++; - if (b->next == &b->buf[Nbuf]) - b->next = &b->buf[0]; + switch (n) { + case 1: + iostats.idledma++; + break; + case 3: + iostats.faildma++; + break; + } + if (debug > 1) print("#A: dmastart @%p\n", s->next); + s->next->nbytes = 0; + s->next++; + if (s->next == &s->buf[Nbuf]) + s->next = &s->buf[0]; } - iunlock(&b->ilock); + iunlock(&s->ilock); } static void @@ -557,6 +635,7 @@ audiostat(Chan *c, char *db) static Chan* audioopen(Chan *c, int omode) { + IOstate *s; switch(c->qid.path & ~CHDIR) { default: @@ -580,29 +659,33 @@ audioopen(Chan *c, int omode) qunlock(&audio); error(Einuse); } - audioenable(); + enable(); memset(&iostats, 0, sizeof(iostats)); if (omode & Aread) { + inenable(); + s = &audio.i; /* read */ audio.amode |= Aread; if(audio.i.bufinit == 0) bufinit(&audio.i); setempty(&audio.i); - audio.i.chan = c; - audio.i.dma = dmaalloc(0, 0, 8, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)&audio.o); + s->chan = c; + s->dma = dmaalloc(0, 0, 4, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)&audio.o); } if (omode & 0x2) { + outenable(); + s = &audio.o; /* write */ // amplifierpower(1); // audiomute(0); audio.amode |= Awrite; - if(audio.o.bufinit == 0) - bufinit(&audio.o); - setempty(&audio.o); - audio.o.chan = c; - audio.o.dma = dmaalloc(0, 0, 8, 2, SSPXmitDMA, Port4SSP, audiointr, (void*)&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); } -// mxvolume(); + mxvolume(); qunlock(&audio); if (debug) print("#A: open done\n"); break; @@ -618,6 +701,7 @@ audioopen(Chan *c, int omode) static void audioclose(Chan *c) { + IOstate *s; switch(c->qid.path & ~CHDIR) { default: @@ -633,45 +717,51 @@ audioclose(Chan *c) if (debug > 1) print("#A: close\n"); if(c->flag & COPEN) { qlock(&audio); - qlock(&audio.o); if(waserror()){ - qunlock(&audio.o); qunlock(&audio); nexterror(); } if (audio.o.chan == c) { + s = &audio.o; + qlock(s); + if(waserror()){ + qunlock(s); + nexterror(); + } /* closing the write end */ audio.amode &= ~Awrite; - if (audio.o.filling->nbytes) { - audio.o.filling++; - if (audio.o.filling == &audio.o.buf[Nbuf]) - audio.o.filling = &audio.o.buf[0]; - sendaudio(&audio.o); + if (s->filling->nbytes) { + /* send remaining partial buffer */ + s->filling++; + if (s->filling == &s->buf[Nbuf]) + s->filling = &s->buf[0]; + sendaudio(s); } - delay(2000); - // dmawait(audio.o.dma); - if (!dmaidle(audio.o.dma)) - print("dma still busy\n"); + dmawait(s->dma); + outdisable(); amplifierpower(0); - setempty(&audio.o); - dmafree(audio.o.dma); + setempty(s); + dmafree(s->dma); + qunlock(s); + poperror(); } if (audio.i.chan == c) { /* closing the read end */ audio.amode &= ~Aread; + indisable(); setempty(&audio.i); } if (audio.amode == 0) { /* turn audio off */ audiopower(0); } - poperror(); - qunlock(&audio.o); qunlock(&audio); + poperror(); print("total dmas: %lud\n", iostats.totaldma); print("dmas while idle: %lud\n", iostats.idledma); print("dmas while busy: %lud\n", iostats.faildma); + print("out of order dma: %lud\n", iostats.samedma); } break; } @@ -807,8 +897,8 @@ audiowrite(Chan *c, void *vp, long n, vlong) } if(strcmp(field[i], "reset") == 0) { -// resetlevel(); -// mxvolume(); + resetlevel(); + mxvolume(); goto cont0; } if(strcmp(field[i], "in") == 0) { diff --git a/bitsy/main.c b/bitsy/main.c index 3acb36cef473ec1ffee025a2bffaf41eca840d80..1585eeaf97901c6dfa7ff6cb9826456507ba176e 100644 --- a/bitsy/main.c +++ b/bitsy/main.c @@ -376,7 +376,8 @@ gpioinit(void) gpioregs->falling = 0; gpioregs->altfunc |= GPIO_LDD8_o|GPIO_LDD9_o|GPIO_LDD10_o|GPIO_LDD11_o - |GPIO_LDD12_o|GPIO_LDD13_o|GPIO_LDD14_o|GPIO_LDD15_o; + |GPIO_LDD12_o|GPIO_LDD13_o|GPIO_LDD14_o|GPIO_LDD15_o + |GPIO_SSP_CLK_i; egpioreg = mapspecial(EGPIOREGS, 4); } diff --git a/bitsy/sa1110dma.c b/bitsy/sa1110dma.c index 0e0e2b58f708737872ee23b2941e2e53feb9f379..dcf3e32a751f422df1fdb75a174d04283a5a19d2 100644 --- a/bitsy/sa1110dma.c +++ b/bitsy/sa1110dma.c @@ -41,7 +41,6 @@ enum { }; typedef struct DMAchan { - Lock; int allocated; Rendez r; void (*intr)(void*, ulong); @@ -71,7 +70,7 @@ dmainit(void) { int i; /* map the lcd regs into the kernel's virtual space */ - dmaregs = (struct dmaregs*)mapspecial(DMAREGS, NDMA*sizeof(struct dmaregs));; + dmaregs = (struct dmaregs*)mapspecial(DMAREGS, NDMA*sizeof(struct dmaregs)); if (debug) print("dma: dmaalloc registers 0x%ux mapped at 0x%p\n", DMAREGS, dmaregs); for (i = 0; i < NDMA; i++) { @@ -110,6 +109,8 @@ dmaalloc(int rd, int bigendian, int burstsize, int datumsize, int device, ulong void dmafree(int i) { + dmaregs[i].dcsr_clr = 0xff; + dmaregs[i].ddar = 0; dma.chan[i].allocated = 0; dma.chan[i].intr = nil; } @@ -117,35 +118,38 @@ dmafree(int i) { ulong dmastart(int chan, void *addr, int count) { ulong status, n; + static int last; - ilock(&dma.chan[chan]); + /* If this gets called from interrupt routines, make sure ilocks are used */ status = dmaregs[chan].dcsr_rd; if (debug > 1) iprint("dma: dmastart 0x%lux\n", status); if ((status & (1< 1) print("dmaidle: 0x%lux\n", status); + return (status & (1<dcsr_rd; if (debug > 1) iprint("dma: interrupt channel %d, status 0x%lux\n", i, dcsr); @@ -191,19 +195,11 @@ dmaintr(Ureg*, void *x) donebit = 1<<((dcsr&1<dcsr_clr = donebit; - /* try the other bit as well */ - donebit ^= 1<dcsr_clr = donebit; - } - iunlock(&dma.chan[i]); if (dma.chan[i].intr) { - dcsr &= 1<