diff -Nru /sys/src/9/bcm/dat.h /sys/src/9/bcm/dat.h --- /sys/src/9/bcm/dat.h Tue Dec 29 00:00:00 2015 +++ /sys/src/9/bcm/dat.h Fri Dec 25 00:00:00 2015 @@ -15,6 +15,7 @@ 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; @@ -119,6 +120,28 @@ ulong mhz; 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 -Nru /sys/src/9/bcm/devgpio.c /sys/src/9/bcm/devgpio.c --- /sys/src/9/bcm/devgpio.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/devgpio.c Fri Dec 25 00:00:00 2015 @@ -0,0 +1,162 @@ +/* + * 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) + && strcmp(funcs[i], cb->f[2]) != 0; + ++i); + 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 -Nru /sys/src/9/bcm/devi2c.c /sys/src/9/bcm/devi2c.c --- /sys/src/9/bcm/devi2c.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/devi2c.c Fri Dec 25 00:00:00 2015 @@ -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 -Nru /sys/src/9/bcm/devspi.c /sys/src/9/bcm/devspi.c --- /sys/src/9/bcm/devspi.c Tue Dec 29 00:00:00 2015 +++ /sys/src/9/bcm/devspi.c Fri Dec 25 00:00:00 2015 @@ -29,17 +29,31 @@ 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 @@ } 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,9 +111,31 @@ 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 -Nru /sys/src/9/bcm/fns.h /sys/src/9/bcm/fns.h --- /sys/src/9/bcm/fns.h Tue Dec 29 00:00:00 2015 +++ /sys/src/9/bcm/fns.h Fri Dec 25 00:00:00 2015 @@ -54,8 +54,13 @@ 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 i2cread(uint, void*, int); +//extern void i2cwrite(uint, void*, int); +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)) @@ -81,6 +86,8 @@ 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 -Nru /sys/src/9/bcm/gpio.c /sys/src/9/bcm/gpio.c --- /sys/src/9/bcm/gpio.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/gpio.c Fri Dec 25 00:00:00 2015 @@ -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,25 @@ 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, addr, buf, len); + i2cio(Write, d->tenbit, d->addr, buf, len, d->salen, offset); + return len; } + +long +i2crecv(I2Cdev *d, void *buf, long len, ulong offset) +{ + i2cio(Read, d->tenbit, d->addr, buf, len, d->salen, offset); + return len; +} + diff -Nru /sys/src/9/bcm/pi /sys/src/9/bcm/pi --- /sys/src/9/bcm/pi Tue Dec 29 00:00:00 2015 +++ /sys/src/9/bcm/pi Fri Dec 25 00:00:00 2015 @@ -18,6 +18,9 @@ kbmap kbin kbd latin1 uart + spi spi + gpio gpio + i2c i2c # fakertc sd @@ -41,7 +44,7 @@ misc armv6 - uartmini + uartmini gpio sdmmc emmc dma vcore --- /sys/src/9/bcm/picpu Dec 29 00:00:00 2015 +++ /sys/src/9/bcm/picpu Dec 29 00:00:00 2015 @@ -18,6 +18,9 @@ kbmap kbin kbd latin1 uart + spi spi + gpio gpio + i2c i2c fakertc sd @@ -41,7 +44,7 @@ misc armv6 - uartmini + uartmini gpio sdmmc emmc dma vcore diff -Nru /sys/src/9/bcm/pi2 /sys/src/9/bcm/pi2 --- /sys/src/9/bcm/pi2 Tue Dec 29 00:00:00 2015 +++ /sys/src/9/bcm/pi2 Fri Dec 25 00:00:00 2015 @@ -18,6 +18,9 @@ kbmap kbin kbd latin1 uart + gpio gpio + spi spi + i2c i2c fakertc sd @@ -41,7 +44,7 @@ misc armv7 - uartmini + uartmini gpio sdmmc emmc dma vcore --- /sys/src/9/bcm/pi2cpu Tue Dec 29 00:00:00 2015 +++ /sys/src/9/bcm/pi2cpu Tue Dec 29 00:00:00 2015 @@ -18,6 +18,9 @@ kbmap kbin kbd latin1 uart + gpio gpio + spi spi + i2c i2c fakertc sd @@ -41,7 +44,7 @@ misc armv7 - uartmini + uartmini gpio sdmmc emmc dma vcore diff -Nru /sys/src/9/bcm/screen.c /sys/src/9/bcm/screen.c --- /sys/src/9/bcm/screen.c Tue Dec 29 00:00:00 2015 +++ /sys/src/9/bcm/screen.c Tue Dec 29 00:00:00 2015 @@ -88,6 +88,27 @@ static int swvisvers; /* the version on the screen */ /* + * Support for a SPI LCD panel from Adafruit + * based on HX8357D controller chip + */ +enum { + TFTWidth = 480, + TFTHeight = 320, +}; + +static void pitftblank(int); +static void pitftdraw(Rectangle); +static void pitftinit(void); +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 *); + +static Queue *updateq = nil; + +/* * called with drawlock locked for us, most of the time. * kernel prints at inopportune times might mean we don't * hold the lock, but memimagedraw is now reentrant so @@ -287,6 +308,8 @@ if(mask->data->bdata == xgdata.bdata) swcursoravoid(par->mr); + pitftdraw(par->r); + return 0; } @@ -349,6 +372,7 @@ memdefont = getmemdefont(); screenwin(); screenputs = myscreenputs; + pitftinit(); } void @@ -385,6 +409,7 @@ blankscreen(int blank) { fbblank(blank); + pitftblank(blank); } static void @@ -471,9 +496,11 @@ p = Pt(window.min.x, window.min.y+o); memimagedraw(gscreen, r, gscreen, p, nil, p, S); flushmemscreen(r); + pitftdraw(r); r = Rpt(Pt(window.min.x, window.max.y-o), window.max); memimagedraw(gscreen, r, back, ZP, nil, ZP, S); flushmemscreen(r); + pitftdraw(r); curpos.y -= o; } @@ -542,4 +569,243 @@ curpos.x += w; break; } +} + +static void +pitftinit(void) +{ + addarchfile("pitft", 0666, pitftread, pitftwrite); +} + +static long +pitftread(Chan *c, void *a, long n, vlong) +{ + USED(c); + USED(a); + USED(n); + return 0; +} + +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 +pitftwrite(Chan *, void *a, long n, vlong) +{ + if(strncmp(a, "init", 4) == 0) { + /* + * 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); + } + 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 -Nru /sys/src/9/bcm/spi.c /sys/src/9/bcm/spi.c --- /sys/src/9/bcm/spi.c Tue Dec 29 00:00:00 2015 +++ /sys/src/9/bcm/spi.c Tue Dec 29 00:00:00 2015 @@ -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; @@ -89,6 +88,30 @@ gpiosel(SPI0_SCLK, Alt0); gpiosel(SPI0_CE0_N, Alt0); gpiosel(SPI0_CE1_N, Alt0); +} + +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 diff -Nru /sys/src/9/bcm/uartmini.c /sys/src/9/bcm/uartmini.c --- /sys/src/9/bcm/uartmini.c Tue Dec 29 00:00:00 2015 +++ /sys/src/9/bcm/uartmini.c Fri Dec 25 00:00:00 2015 @@ -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, @@ -75,72 +51,6 @@ .freq = 250000000, .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<