commit 57757f8f44e39416074ba18be74d30d88b85e978 Author: David du Colombier <0intro@gmail.com> Date: Thu Apr 7 17:23:20 2016 +0200 bcm diff --git a/sys/src/9/bcm/dat.h b/sys/src/9/bcm/dat.h index ce1272e..351a3ae 100644 --- a/sys/src/9/bcm/dat.h +++ b/sys/src/9/bcm/dat.h @@ -15,6 +15,7 @@ enum { typedef struct Conf Conf; typedef struct Confmem Confmem; typedef struct FPsave FPsave; +typedef struct I2Cdev I2Cdev; typedef struct ISAConf ISAConf; typedef struct Label Label; typedef struct Lock Lock; @@ -120,6 +121,28 @@ struct Conf int monitor; /* flag */ }; +struct I2Cdev { + int salen; + int addr; + int tenbit; +}; + +/* + * GPIO + */ +enum { + Input = 0x0, + Output = 0x1, + Alt0 = 0x4, + Alt1 = 0x5, + Alt2 = 0x6, + Alt3 = 0x7, + Alt4 = 0x3, + Alt5 = 0x2, +}; + + + /* * things saved in the Proc structure during a notify */ diff --git a/sys/src/9/bcm/devgpio.c b/sys/src/9/bcm/devgpio.c new file mode 100644 index 0000000..37a1f85 --- /dev/null +++ b/sys/src/9/bcm/devgpio.c @@ -0,0 +1,161 @@ +/* + * Raspberry Pi (BCM2835) GPIO + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum { + // GPIO registers + GPLEV = 0x7e200034, +}; + +enum{ + Qdir = 0, + Qgpio, +}; + +Dirtab gpiodir[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "gpio", {Qgpio, 0}, 0, 0664, +}; + +enum { + // commands + CMfunc, + CMset, + CMpullup, + CMpulldown, + CMfloat, +}; + +static Cmdtab gpiocmd[] = { + {CMfunc, "function", 3}, + {CMset, "set", 3}, + {CMpullup, "pullup", 2}, + {CMpulldown, "pulldown", 2}, + {CMfloat, "float", 2}, +}; + +static char *funcs[] = { "in", "out", "alt5", "alt4", "alt0", + "alt1", "alt2", "alt3", "pulse"}; +static int ifuncs[] = { Input, Output, Alt5, Alt4, Alt0, + Alt1, Alt2, Alt3, -1}; + +static Chan* +gpioattach(char* spec) +{ + return devattach('G', spec); +} + +static Walkqid* +gpiowalk(Chan* c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, gpiodir, nelem(gpiodir), devgen); +} + +static int +gpiostat(Chan* c, uchar* dp, int n) +{ + return devstat(c, dp, n, gpiodir, nelem(gpiodir), devgen); +} + +static Chan* +gpioopen(Chan* c, int omode) +{ + return devopen(c, omode, gpiodir, nelem(gpiodir), devgen); +} + +static void +gpioclose(Chan*) +{ +} + +static long +gpioread(Chan* c, void *buf, long n, vlong) +{ + char lbuf[20]; + char *e; + + USED(c); + if(c->qid.path == Qdir) + return devdirread(c, buf, n, gpiodir, nelem(gpiodir), devgen); + e = lbuf + sizeof(lbuf); + seprint(lbuf, e, "%08ulx%08ulx", ((ulong *)GPLEV)[1], ((ulong *)GPLEV)[0]); + return readstr(0, buf, n, lbuf); +} + +static long +gpiowrite(Chan* c, void *buf, long n, vlong) +{ + Cmdbuf *cb; + Cmdtab *ct; + int pin, i; + + if(c->qid.type & QTDIR) + error(Eperm); + cb = parsecmd(buf, n); + if(waserror()) { + free(cb); + nexterror(); + } + ct = lookupcmd(cb, gpiocmd, nelem(gpiocmd)); + pin = atoi(cb->f[1]); + switch(ct->index) { + case CMfunc: + for(i = 0; i < nelem(funcs); i++) + if(strcmp(funcs[i], cb->f[2]) == 0) + break; + if(i >= nelem(funcs)) + error(Ebadctl); + if(ifuncs[i] == -1) { + gpiosel(pin, Output); + microdelay(2); + gpiosel(pin, Input); + } + else { + gpiosel(pin, ifuncs[i]); + } + break; + case CMset: + gpioout(pin, atoi(cb->f[2])); + break; + case CMpullup: + gpiopullup(pin); + break; + case CMpulldown: + gpiopulldown(pin); + break; + case CMfloat: + gpiopulloff(pin); + break; + } + free(cb); + poperror(); + return n; +} + +Dev gpiodevtab = { + 'G', + "gpio", + + devreset, + devinit, + devshutdown, + gpioattach, + gpiowalk, + gpiostat, + gpioopen, + devcreate, + gpioclose, + gpioread, + devbread, + gpiowrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/sys/src/9/bcm/devi2c.c b/sys/src/9/bcm/devi2c.c new file mode 100644 index 0000000..5051224 --- /dev/null +++ b/sys/src/9/bcm/devi2c.c @@ -0,0 +1,227 @@ +/* + * i2c + * + * Copyright © 1998, 2003 Vita Nuova Limited. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +typedef struct I2Cdir I2Cdir; + +enum{ + Qdir, + Qdata, + Qctl, +}; + +static +Dirtab i2ctab[]={ + ".", {Qdir, 0, QTDIR}, 0, 0555, + "i2cdata", {Qdata, 0}, 256, 0660, + "i2cctl", {Qctl, 0}, 0, 0660, +}; + +struct I2Cdir { + Ref; + I2Cdev; + Dirtab tab[nelem(i2ctab)]; +}; + +static void +i2creset(void) +{ + i2csetup(0); +} + +static Chan* +i2cattach(char* spec) +{ + char *s; + ulong addr; + I2Cdir *d; + Chan *c; + + addr = strtoul(spec, &s, 16); + if(*spec == 0 || *s || addr >= (1<<10)) + error("invalid i2c address"); + d = malloc(sizeof(I2Cdir)); + if(d == nil) + error(Enomem); + d->ref = 1; + d->addr = addr; + d->salen = 0; + d->tenbit = addr >= 128; + memmove(d->tab, i2ctab, sizeof(d->tab)); + sprint(d->tab[1].name, "i2c.%lux.data", addr); + sprint(d->tab[2].name, "i2c.%lux.ctl", addr); + + c = devattach('J', spec); + c->aux = d; + return c; +} + +static Walkqid* +i2cwalk(Chan* c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + I2Cdir *d; + + d = c->aux; + wq = devwalk(c, nc, name, nname, d->tab, nelem(d->tab), devgen); + if(wq != nil && wq->clone != nil && wq->clone != c) + incref(d); + return wq; +} + +static int +i2cstat(Chan* c, uchar *dp, int n) +{ + I2Cdir *d; + + d = c->aux; + return devstat(c, dp, n, d->tab, nelem(d->tab), devgen); +} + +static Chan* +i2copen(Chan* c, int omode) +{ + I2Cdir *d; + + d = c->aux; + return devopen(c, omode, d->tab, nelem(d->tab), devgen); +} + +static void +i2cclose(Chan *c) +{ + I2Cdir *d; + + d = c->aux; + if(decref(d) == 0) + free(d); +} + +static long +i2cread(Chan *c, void *a, long n, vlong offset) +{ + I2Cdir *d; + char *s, *e; + ulong len; + + d = c->aux; + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, a, n, d->tab, nelem(d->tab), devgen); + case Qdata: + len = d->tab[1].length; + if(offset+n >= len){ + n = len - offset; + if(n <= 0) + return 0; + } + n = i2crecv(d, a, n, offset); + break; + case Qctl: + s = smalloc(READSTR); + if(waserror()){ + free(s); + nexterror(); + } + e = seprint(s, s+READSTR, "size %lud\n", (ulong)d->tab[1].length); + if(d->salen) + e = seprint(e, s+READSTR, "subaddress %d\n", d->salen); + if(d->tenbit) + seprint(e, s+READSTR, "a10\n"); + n = readstr(offset, a, n, s); + poperror(); + free(s); + return n; + default: + n=0; + break; + } + return n; +} + +static long +i2cwrite(Chan *c, void *a, long n, vlong offset) +{ + I2Cdir *d; + long len; + Cmdbuf *cb; + + USED(offset); + switch((ulong)c->qid.path){ + case Qdata: + d = c->aux; + len = d->tab[1].length; + if(offset+n >= len){ + n = len - offset; + if(n <= 0) + return 0; + } + n = i2csend(d, a, n, offset); + break; + case Qctl: + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + if(cb->nf < 1) + error(Ebadctl); + d = c->aux; + if(strcmp(cb->f[0], "subaddress") == 0){ + if(cb->nf > 1){ + len = strtol(cb->f[1], nil, 0); + if(len <= 0) + len = 0; + if(len > 4) + cmderror(cb, "subaddress too long"); + }else + len = 1; + d->salen = len; + }else if(cb->nf > 1 && strcmp(cb->f[0], "size") == 0){ + len = strtol(cb->f[1], nil, 0); + if(len < 0) + cmderror(cb, "size is negative"); + d->tab[1].length = len; + }else if(strcmp(cb->f[0], "a10") == 0) + d->tenbit = 1; + else + cmderror(cb, "unknown control request"); + poperror(); + free(cb); + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev i2cdevtab = { + 'J', + "i2c", + + i2creset, + devinit, + devshutdown, + i2cattach, + i2cwalk, + i2cstat, + i2copen, + devcreate, + i2cclose, + i2cread, + devbread, + i2cwrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/sys/src/9/bcm/devrtc3231.c b/sys/src/9/bcm/devrtc3231.c index 676b364..0a7454a 100644 --- a/sys/src/9/bcm/devrtc3231.c +++ b/sys/src/9/bcm/devrtc3231.c @@ -53,6 +53,34 @@ Dirtab rtcdir[]={ static ulong rtc2sec(Rtc*); static void sec2rtc(ulong, Rtc*); +static void +i2cread(uint addr, void *buf, int len) +{ + I2Cdev d; + + d.addr = addr; + d.tenbit = 0; + d.salen = 0; + i2crecv(&d, buf, len, 0); +} + +static void +i2cwrite(uint addr, void *buf, int len) +{ + I2Cdev d; + + d.addr = addr; + d.tenbit = 0; + d.salen = 0; + i2csend(&d, buf, len, 0); +} + +static void +rtcinit() +{ + i2csetup(0); +} + static Chan* rtcattach(char* spec) { @@ -211,7 +239,7 @@ Dev rtc3231devtab = { "rtc", devreset, - devinit, + rtcinit, devshutdown, rtcattach, rtcwalk, diff --git a/sys/src/9/bcm/devspi.c b/sys/src/9/bcm/devspi.c index 736dc30..ca44668 100644 --- a/sys/src/9/bcm/devspi.c +++ b/sys/src/9/bcm/devspi.c @@ -29,17 +29,31 @@ Spi spidev[Nspislave]; enum{ Qdir = 0, + Qctl, Qspi, }; Dirtab spidir[]={ ".", {Qdir, 0, QTDIR}, 0, 0555, + "spictl", {Qctl, 0}, 0, 0664, "spi0", {Qspi+0, 0}, 0, 0664, "spi1", {Qspi+1, 0}, 0, 0664, }; #define DEVID(path) ((ulong)path - Qspi) +enum { + CMclock, + CMmode, + CMlossi, +}; + +Cmdtab spitab[] = { + {CMclock, "clock", 2}, + {CMmode, "mode", 2}, + {CMlossi, "lossi", 1}, +}; + static void spikick(void *a) { @@ -65,13 +79,28 @@ spiinit(void) } static long -spiread(Chan *c, void *a, long n, vlong) +spiread(Chan *c, void *a, long n, vlong off) { Spi *spi; + u32int *sp; + char *p, *e; + char buf[256]; if(c->qid.type & QTDIR) return devdirread(c, a, n, spidir, nelem(spidir), devgen); + if(c->qid.path == Qctl) { + sp = (u32int *)0x7e204000; + p = buf; + e = p + sizeof(buf); + p = seprint(p, e, "CS: %08x\n", sp[0]); + p = seprint(p, e, "CLK: %08x\n", sp[2]); + p = seprint(p, e, "DLEN: %08x\n", sp[3]); + p = seprint(p, e, "LTOH: %08x\n", sp[4]); + seprint(p, e, "DC: %08x\n", sp[5]); + return readstr(off, a, n, buf); + } + spi = &spidev[DEVID(c->qid.path)]; n = qread(spi->iq, a, n); @@ -82,10 +111,32 @@ static long spiwrite(Chan*c, void *a, long n, vlong) { Spi *spi; + Cmdbuf *cb; + Cmdtab *ct; if(c->qid.type & QTDIR) error(Eperm); + if(c->qid.path == Qctl) { + cb = parsecmd(a, n); + if(waserror()) { + free(cb); + nexterror(); + } + ct = lookupcmd(cb, spitab, nelem(spitab)); + switch(ct->index) { + case CMclock: + spiclock(atoi(cb->f[1])); + break; + case CMmode: + spimode(atoi(cb->f[1])); + break; + case CMlossi: + break; + } + return n; + } + spi = &spidev[DEVID(c->qid.path)]; n = qwrite(spi->oq, a, n); diff --git a/sys/src/9/bcm/fns.h b/sys/src/9/bcm/fns.h index 02738c4..0fbaf26 100644 --- a/sys/src/9/bcm/fns.h +++ b/sys/src/9/bcm/fns.h @@ -56,8 +56,11 @@ extern void gpiosel(uint, int); extern void gpiopullup(uint); extern void gpiopulloff(uint); extern void gpiopulldown(uint); -extern void i2cread(uint, void*, int); -extern void i2cwrite(uint, void*, int); +extern void gpioout(uint, int); +extern int gpioin(unit); +extern void i2csetup(int); +extern long i2crecv(I2Cdev*, void*, long, ulong); +extern long i2csend(I2Cdev*, void*, long, ulong); extern u32int ifsrget(void); extern void irqenable(int, void (*)(Ureg*, void*), void*); #define intrenable(i, f, a, b, n) irqenable((i), (f), (a)) @@ -83,6 +86,8 @@ extern void screeninit(void); extern void setclkrate(int, ulong); extern void setpower(int, int); extern void setr13(int, u32int*); +extern void spiclock(uint); +extern void spimode(int); extern void spirw(uint, void*, int); extern int splfhi(void); extern int splflo(void); diff --git a/sys/src/9/bcm/gpio.c b/sys/src/9/bcm/gpio.c new file mode 100644 index 0000000..842b832 --- /dev/null +++ b/sys/src/9/bcm/gpio.c @@ -0,0 +1,95 @@ +/* + * Raspberry Pi GPIO support + */ + +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define GPIOREGS (VIRTIO+0x200000) + +/* GPIO regs */ +enum { + Fsel0 = 0x00>>2, + FuncMask= 0x7, + Set0 = 0x1c>>2, + Clr0 = 0x28>>2, + Lev0 = 0x34>>2, + PUD = 0x94>>2, + Off = 0x0, + Pulldown= 0x1, + Pullup = 0x2, + PUDclk0 = 0x98>>2, + PUDclk1 = 0x9c>>2, +}; + +void +gpiosel(uint pin, int func) +{ + u32int *gp, *fsel; + int off; + + gp = (u32int*)GPIOREGS; + fsel = &gp[Fsel0 + pin/10]; + off = (pin % 10) * 3; + *fsel = (*fsel & ~(FuncMask<clkdiv = 2500; + gpiosel(SDA0Pin, Alt0); gpiosel(SCL0Pin, Alt0); gpiopullup(SDA0Pin); gpiopullup(SCL0Pin); + intrenable(IRQi2c, i2cinterrupt, 0, 0, "i2c"); } +/* + * To do subaddressing avoiding a STOP condition between the address and payload. + * - write the subaddress, + * - poll until the transfer starts, + * - overwrite the registers for the payload transfer, before the subaddress + * transaction has completed. + * + * FIXME: neither 10bit adressing nor 100Khz transfers implemented yet. + */ static void -i2cio(int rw, uint addr, void *buf, int len) +i2cio(int rw, int tenbit, uint addr, void *buf, int len, int salen, uint subaddr) { Bsc *r; uchar *p; int st; + if(tenbit) + error("10bit addressing not supported"); + if(salen == 0 && subaddr) /* default subaddress len == 1byte */ + salen = 1; + qlock(&i2c.lock); - if(i2c.regs == 0) - i2cinit(); r = i2c.regs; - p = buf; r->ctrl = I2cen | Clear; r->addr = addr; - r->dlen = len; r->stat = Clkt|Err|Done; - r->ctrl = I2cen | Start | Intd | rw; + + if(salen){ + r->dlen = salen; + r->ctrl = I2cen | Start | Write; + while((r->stat & Ta) == 0) { + if(r->stat & (Err|Clkt)) { + qunlock(&i2c.lock); + error(Eio); + } + } + r->dlen = len; + r->ctrl = I2cen | Start | Intd | rw; + for(; salen > 0; salen--) + r->fifo = subaddr >> ((salen-1)*8); + /* + * Adapted from Linux code...uses undocumented + * status information. + */ + if(rw == Read) { + do { + if(r->stat & (Err|Clkt)) { + qunlock(&i2c.lock); + error(Eio); + } + st = r->stat >> 28; + } while(st != 0 && st != 4 && st != 5); + } + } + else { + r->dlen = len; + r->ctrl = I2cen | Start | Intd | rw; + } + + p = buf; st = rw == Read? Rxd : Txd; while(len > 0){ while((r->stat & (st|Done)) == 0){ @@ -159,14 +213,24 @@ i2cio(int rw, uint addr, void *buf, int len) qunlock(&i2c.lock); } + void -i2cread(uint addr, void *buf, int len) +i2csetup(int) { - i2cio(Read, addr, buf, len); + //print("i2csetup\n"); + i2cinit(); } -void -i2cwrite(uint addr, void *buf, int len) +long +i2csend(I2Cdev *d, void *buf, long len, ulong offset) +{ + i2cio(Write, d->tenbit, d->addr, buf, len, d->salen, offset); + return len; +} + +long +i2crecv(I2Cdev *d, void *buf, long len, ulong offset) { - i2cio(Write, addr, buf, len); + i2cio(Read, d->tenbit, d->addr, buf, len, d->salen, offset); + return len; } diff --git a/sys/src/9/bcm/main.c b/sys/src/9/bcm/main.c index 54462a5..0277a34 100644 --- a/sys/src/9/bcm/main.c +++ b/sys/src/9/bcm/main.c @@ -354,6 +354,7 @@ void init0(void) { int i; + Chan *c; char buf[2*KNAMELEN]; up->nerrlab = 0; @@ -387,6 +388,14 @@ init0(void) ksetenv(confname[i], confval[i], 0); ksetenv(confname[i], confval[i], 1); } + if(getconf("pitft")){ + c = namec("#P/pitft", Aopen, OWRITE, 0); + if(!waserror()){ + devtab[c->type]->write(c, "init", 4, 0); + poperror(); + } + cclose(c); + } poperror(); } kproc("alarm", alarmkproc, 0); @@ -561,7 +570,7 @@ confinit(void) + conf.nproc*sizeof(Proc) + conf.nimage*sizeof(Image) + conf.nswap - + conf.nswppo*sizeof(Page*); + + conf.nswppo*sizeof(Page); mainmem->maxsize = kpages; if(!cpuserver) /* diff --git a/sys/src/9/bcm/pi b/sys/src/9/bcm/pi index ee529fc..fe96ae1 100644 --- a/sys/src/9/bcm/pi +++ b/sys/src/9/bcm/pi @@ -18,8 +18,12 @@ dev kbmap kbin kbd latin1 uart + gpio gpio + spi spi + i2c i2c fakertc +# rtc3231 i2c ether netif sd usb @@ -30,6 +34,7 @@ link ethermedium usbdwc etherusb + pitft ip tcp @@ -41,11 +46,12 @@ ip misc armv6 - uartmini + uartmini gpio sdmmc emmc dma vcore vfp3 coproc +# spi port int cpuserver = 0; diff --git a/sys/src/9/bcm/pi2 b/sys/src/9/bcm/pi2 index 6122f8b..44f9f55 100644 --- a/sys/src/9/bcm/pi2 +++ b/sys/src/9/bcm/pi2 @@ -18,7 +18,9 @@ dev kbmap kbin kbd latin1 uart + gpio gpio spi spi + i2c i2c fakertc # rtc3231 i2c @@ -32,6 +34,7 @@ link ethermedium usbdwc etherusb + pitft ip tcp @@ -43,11 +46,12 @@ ip misc armv7 - uartmini + uartmini gpio sdmmc emmc dma vcore vfp3 coproc +# spi port int cpuserver = 0; diff --git a/sys/src/9/bcm/pi2cpu b/sys/src/9/bcm/pi2cpu index 41b65f5..4c029ad 100644 --- a/sys/src/9/bcm/pi2cpu +++ b/sys/src/9/bcm/pi2cpu @@ -18,8 +18,12 @@ dev kbmap kbin kbd latin1 uart + gpio gpio + spi spi + i2c i2c fakertc +# rtc3231 i2c ether netif sd usb @@ -30,6 +34,7 @@ link ethermedium usbdwc etherusb + pitft ip tcp @@ -41,11 +46,12 @@ ip misc armv7 - uartmini + uartmini gpio sdmmc emmc dma vcore vfp3 coproc +# spi port int cpuserver = 1; diff --git a/sys/src/9/bcm/picpu b/sys/src/9/bcm/picpu index e357780..2f7446b 100644 --- a/sys/src/9/bcm/picpu +++ b/sys/src/9/bcm/picpu @@ -18,8 +18,12 @@ dev kbmap kbin kbd latin1 uart + gpio gpio + spi spi + i2c i2c fakertc +# rtc3231 i2c ether netif sd usb @@ -30,6 +34,7 @@ link ethermedium usbdwc etherusb + pitft ip tcp @@ -41,11 +46,12 @@ ip misc armv6 - uartmini + uartmini gpio sdmmc emmc dma vcore vfp3 coproc +# spi port int cpuserver = 1; diff --git a/sys/src/9/bcm/pitft.c b/sys/src/9/bcm/pitft.c new file mode 100644 index 0000000..580a36f --- /dev/null +++ b/sys/src/9/bcm/pitft.c @@ -0,0 +1,277 @@ +/* + * Support for a SPI LCD panel from Adafruit + * based on HX8357D controller chip + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +enum { + TFTWidth = 480, + TFTHeight = 320, +}; + +static void pitftblank(int); +static void pitftdraw(Rectangle); +static long pitftread(Chan*, void*, long, vlong); +static long pitftwrite(Chan*, void*, long, vlong); +static void spicmd(uchar); +static void spidata(uchar *, int); +static void setwindow(int, int, int, int); +static void xpitftdraw(void *); + +extern Memimage xgscreen; +extern Lcd *lcd; + +static Lcd pitft = { + pitftdraw, + pitftblank, +}; + +static Queue *updateq = nil; + +void +pitftlink(void) +{ + addarchfile("pitft", 0666, pitftread, pitftwrite); +} + +static void +pitftsetup(void) +{ + uchar spibuf[32]; + + gpiosel(25, Output); + spirw(0, spibuf, 1); + spicmd(0x01); + delay(10); + spicmd(0x11); + delay(10); + spicmd(0x29); + spicmd(0x13); + spicmd(0x36); + spibuf[0] = 0xe8; + spidata(spibuf, 1); + spicmd(0x3a); + spibuf[0] = 0x05; + spidata(spibuf, 1); +} + +static long +pitftread(Chan *, void *, long, vlong) +{ + return 0; +} + +static long +pitftwrite(Chan *, void *a, long n, vlong) +{ + if(strncmp(a, "init", 4) == 0 && updateq == nil) { + /* + * The HX8357 datasheet shows minimum + * clock cycle time of 66nS but the clock high + * and low times as 15nS and it seems to + * work at around 32MHz. + */ + spiclock(32); + pitftsetup(); + updateq = qopen(16384, 1, nil, nil); + kproc("pitft", xpitftdraw, nil); + lcd = &pitft; + } + return n; +} + +static void +pitftblank(int blank) +{ + USED(blank); +} + +static void +pitftdraw(Rectangle r) +{ + if(updateq == nil) + return; + if(r.min.x > TFTWidth || r.min.y > TFTHeight) + return; + /* + * using qproduce to make sure we don't block + * but if we've got a lot on the queue, it means we're + * redrawing the same areas over and over; clear it + * out and just draw the whole screen once + */ + if(qproduce(updateq, &r, sizeof(Rectangle)) == -1) { + r = Rect(0, 0, TFTWidth, TFTHeight); + qflush(updateq); + qproduce(updateq, &r, sizeof(Rectangle)); + } +} + +int +overlap(Rectangle r1, Rectangle r2) +{ + if(r1.max.x < r2.min.x) + return 0; + if(r1.min.x > r2.max.x) + return 0; + if(r1.max.y < r2.min.y) + return 0; + if(r1.min.y > r2.max.y) + return 0; + return 1; +} + +int +min(int x, int y) +{ + if(x < y) + return x; + return y; +} + +int +max(int x, int y) +{ + if(x < y) + return y; + return x; +} + +/* + * Because everyone wants to be holding locks when + * they update the screen but we need to sleep in the + * SPI code, we're decoupling this into a separate kproc(). + */ +static void +xpitftdraw(void *) +{ + Rectangle rec, bb; + Point pt; + uchar *p; + int i, r, c, gotrec; + uchar spibuf[32]; + + gotrec = 0; + qread(updateq, &rec, sizeof(Rectangle)); + bb = Rect(0, 0, TFTWidth, TFTHeight); + while(1) { + setwindow(bb.min.x, bb.min.y, + bb.max.x-1, bb.max.y-1); + spicmd(0x2c); + for(r = bb.min.y; r < bb.max.y; ++r) { + for(c = bb.min.x; c < bb.max.x; c += 8) { + for(i = 0; i < 8; ++i) { + pt.y = r; + pt.x = c + i; + p = byteaddr(&xgscreen, pt); + switch(xgscreen.depth) { + case 16: // RGB16 + spibuf[i*2+1] = p[0]; + spibuf[i*2] = p[1]; + break; + case 24: // BGR24 + spibuf[i*2] = (p[2] & 0xf8) | + (p[1] >> 5); + spibuf[i*2+1] = (p[0] >> 3) | + (p[1] << 3); + break; + case 32: // ARGB32 + spibuf[i*2] = (p[0] & 0xf8) | + (p[1] >> 5); + spibuf[i*2+1] = (p[1] >> 3) | + (p[1] << 3); + break; + } + } + spidata(spibuf, 16); + } + } + bb.max.y = -1; + while(1) { + if(!gotrec) { + qread(updateq, &rec, sizeof(Rectangle)); + gotrec = 1; + } + if(bb.max.y != -1) { + if(!overlap(bb, rec)) + break; + rec.min.x = min(rec.min.x, bb.min.x); + rec.min.y = min(rec.min.y, bb.min.y); + rec.max.x = max(rec.max.x, bb.max.x); + rec.max.y = max(rec.max.y, bb.max.y); + } + gotrec = 0; + // Expand rows to 8 pixel alignment + bb.min.x = rec.min.x & ~7; + if(bb.min.x < 0) + bb.min.x = 0; + bb.max.x = (rec.max.x + 7) & ~7; + if(bb.max.x > TFTWidth) + bb.max.x = TFTWidth; + bb.min.y = rec.min.y; + if(bb.min.y < 0) + bb.min.y = 0; + bb.max.y = rec.max.y; + if(bb.max.y > TFTHeight) + bb.max.y = TFTHeight; + if(qcanread(updateq)) { + qread(updateq, &rec, sizeof(Rectangle)); + gotrec = 1; + } + else + break; + } + } +} + +static void +spicmd(uchar c) +{ + char buf; + + gpioout(25, 0); + buf = c; + spirw(0, &buf, 1); +} + +static void +spidata(uchar *p, int n) +{ + char buf[128]; + + if(n > 128) + n = 128; + gpioout(25, 1); + memmove(buf, p, n); + spirw(0, buf, n); + gpioout(25, 0); +} + +static void +setwindow(int minc, int minr, int maxc, int maxr) +{ + uchar spibuf[4]; + + spicmd(0x2a); + spibuf[0] = minc >> 8; + spibuf[1] = minc & 0xff; + spibuf[2] = maxc >> 8; + spibuf[3] = maxc & 0xff; + spidata(spibuf, 4); + spicmd(0x2b); + spibuf[0] = minr >> 8; + spibuf[1] = minr & 0xff; + spibuf[2] = maxr >> 8; + spibuf[3] = maxr & 0xff; + spidata(spibuf, 4); +} diff --git a/sys/src/9/bcm/screen.c b/sys/src/9/bcm/screen.c index 082a82d..fd6c91d 100644 --- a/sys/src/9/bcm/screen.c +++ b/sys/src/9/bcm/screen.c @@ -37,10 +37,11 @@ Cursor arrow = { }; Memimage *gscreen; +Lcd *lcd; static Memdata xgdata; -static Memimage xgscreen = +/*static*/ Memimage xgscreen = { { 0, 0, Wid, Ht }, /* r */ { 0, 0, Wid, Ht }, /* clipr */ @@ -287,6 +288,9 @@ hwdraw(Memdrawparam *par) if(mask->data->bdata == xgdata.bdata) swcursoravoid(par->mr); + if(lcd) + lcd->draw(par->r); + return 0; } @@ -388,6 +392,8 @@ void blankscreen(int blank) { fbblank(blank); + if(lcd) + lcd->blank(blank); } static void @@ -474,9 +480,13 @@ scroll(void) p = Pt(window.min.x, window.min.y+o); memimagedraw(gscreen, r, gscreen, p, nil, p, S); flushmemscreen(r); + if(lcd) + lcd->draw(r); r = Rpt(Pt(window.min.x, window.max.y-o), window.max); memimagedraw(gscreen, r, back, ZP, nil, ZP, S); flushmemscreen(r); + if(lcd) + lcd->draw(r); curpos.y -= o; } diff --git a/sys/src/9/bcm/screen.h b/sys/src/9/bcm/screen.h index 285478c..b79f33b 100644 --- a/sys/src/9/bcm/screen.h +++ b/sys/src/9/bcm/screen.h @@ -1,10 +1,17 @@ typedef struct Cursor Cursor; typedef struct Cursorinfo Cursorinfo; +typedef struct Lcd Lcd; + struct Cursorinfo { Cursor; Lock; }; +struct Lcd { + void (*draw)(Rectangle); + void (*blank)(int); +}; + /* devmouse.c */ extern void mousetrack(int, int, int, int); extern Point mousexy(void); diff --git a/sys/src/9/bcm/spi.c b/sys/src/9/bcm/spi.c index e96e016..80317ed 100644 --- a/sys/src/9/bcm/spi.c +++ b/sys/src/9/bcm/spi.c @@ -16,7 +16,6 @@ #define SPI0_MISO 9 /* P1 pin 21 */ #define SPI0_MOSI 10 /* P1 pin 19 */ #define SPI0_SCLK 11 /* P1 pin 23 */ -#define Alt0 0x4 typedef struct Ctlr Ctlr; typedef struct Spiregs Spiregs; @@ -92,6 +91,30 @@ spiinit(void) } void +spimode(int mode) +{ + spi.regs->cs = (spi.regs->cs & ~(Cpha | Cpol)) | (mode << 2); +} + +/* + * According the Broadcom docs, the divisor has to + * be a power of 2, but an errata says that should + * be multiple of 2 and scope observations confirm + * that restricting it to a power of 2 is unnecessary. + */ +void +spiclock(uint mhz) +{ + if(spi.regs == 0) + spiinit(); + if(mhz == 0) { + spi.regs->clkdiv = 32768; /* about 8 KHz */ + return; + } + spi.regs->clkdiv = 2 * ((125 + (mhz - 1)) / mhz); +} + +void spirw(uint cs, void *buf, int len) { Spiregs *r; diff --git a/sys/src/9/bcm/uartmini.c b/sys/src/9/bcm/uartmini.c index f2fbe25..20d4d2b 100644 --- a/sys/src/9/bcm/uartmini.c +++ b/sys/src/9/bcm/uartmini.c @@ -10,35 +10,11 @@ #include "fns.h" #include "io.h" -#define GPIOREGS (VIRTIO+0x200000) #define AUXREGS (VIRTIO+0x215000) #define OkLed 16 #define TxPin 14 #define RxPin 15 -/* GPIO regs */ -enum { - Fsel0 = 0x00>>2, - FuncMask= 0x7, - Input = 0x0, - Output = 0x1, - Alt0 = 0x4, - Alt1 = 0x5, - Alt2 = 0x6, - Alt3 = 0x7, - Alt4 = 0x3, - Alt5 = 0x2, - Set0 = 0x1c>>2, - Clr0 = 0x28>>2, - Lev0 = 0x34>>2, - PUD = 0x94>>2, - Off = 0x0, - Pulldown= 0x1, - Pullup = 0x2, - PUDclk0 = 0x98>>2, - PUDclk1 = 0x9c>>2, -}; - /* AUX regs */ enum { Irq = 0x00>>2, @@ -76,72 +52,6 @@ static Uart miniuart = { .phys = &miniphysuart, }; -void -gpiosel(uint pin, int func) -{ - u32int *gp, *fsel; - int off; - - gp = (u32int*)GPIOREGS; - fsel = &gp[Fsel0 + pin/10]; - off = (pin % 10) * 3; - *fsel = (*fsel & ~(FuncMask<hcintmsk = mask; fiunlock(ctlr); tsleep(&ctlr->chanintr[n], chandone, hc, 1000); - if(hc->hcint == 0) + if((intr = hc->hcint) == 0) goto restart; hc->hcintmsk = 0; - intr = hc->hcint; if(intr & Chhltd) return intr; start = fastticks(0); @@ -235,14 +234,14 @@ restart: if((ointr != Ack && ointr != (Ack|Xfercomp)) || intr != (Ack|Chhltd|Xfercomp) || (now - start) > 60) - dprint("await %x after %ld %x -> %x\n", + dprint("await %x after %ldµs %x -> %x\n", mask, now - start, ointr, intr); return intr; } if((intr & mask) == 0){ if(intr != Nak) - dprint("ep%d.%d await %x intr %x -> %x\n", - ep->dev->nb, ep->nb, mask, ointr, intr); + dprint("ep%d.%d await %x after %ldµs intr %x -> %x\n", + ep->dev->nb, ep->nb, mask, now - start, ointr, intr); goto restart; } now = fastticks(0); @@ -398,6 +397,7 @@ chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len) } hc->hcchar = (hc->hcchar &~ Chdis) | Chen; clog(ep, hc); +wait: if(ep->ttype == Tbulk && dir == Epin) i = chanwait(ep, ctlr, hc, Chhltd); else if(ep->ttype == Tintr && (hc->hcsplt & Spltena)) @@ -407,7 +407,8 @@ chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len) clog(ep, hc); if(hc->hcint != i){ dprint("chanwait intr %ux->%ux\n", i, hc->hcint); - i = hc->hcint; + if((i = hc->hcint) == 0) + goto wait; } hc->hcint = i;