1*5c47fe09SDavid du Colombier /*
2*5c47fe09SDavid du Colombier * Support for a SPI LCD panel from Adafruit
3*5c47fe09SDavid du Colombier * based on HX8357D controller chip
4*5c47fe09SDavid du Colombier */
5*5c47fe09SDavid du Colombier
6*5c47fe09SDavid du Colombier #include "u.h"
7*5c47fe09SDavid du Colombier #include "../port/lib.h"
8*5c47fe09SDavid du Colombier #include "mem.h"
9*5c47fe09SDavid du Colombier #include "dat.h"
10*5c47fe09SDavid du Colombier #include "fns.h"
11*5c47fe09SDavid du Colombier
12*5c47fe09SDavid du Colombier #define Image IMAGE
13*5c47fe09SDavid du Colombier #include <draw.h>
14*5c47fe09SDavid du Colombier #include <memdraw.h>
15*5c47fe09SDavid du Colombier #include <cursor.h>
16*5c47fe09SDavid du Colombier #include "screen.h"
17*5c47fe09SDavid du Colombier
18*5c47fe09SDavid du Colombier enum {
19*5c47fe09SDavid du Colombier TFTWidth = 480,
20*5c47fe09SDavid du Colombier TFTHeight = 320,
21*5c47fe09SDavid du Colombier };
22*5c47fe09SDavid du Colombier
23*5c47fe09SDavid du Colombier static void pitftblank(int);
24*5c47fe09SDavid du Colombier static void pitftdraw(Rectangle);
25*5c47fe09SDavid du Colombier static long pitftread(Chan*, void*, long, vlong);
26*5c47fe09SDavid du Colombier static long pitftwrite(Chan*, void*, long, vlong);
27*5c47fe09SDavid du Colombier static void spicmd(uchar);
28*5c47fe09SDavid du Colombier static void spidata(uchar *, int);
29*5c47fe09SDavid du Colombier static void setwindow(int, int, int, int);
30*5c47fe09SDavid du Colombier static void xpitftdraw(void *);
31*5c47fe09SDavid du Colombier
32*5c47fe09SDavid du Colombier extern Memimage xgscreen;
33*5c47fe09SDavid du Colombier extern Lcd *lcd;
34*5c47fe09SDavid du Colombier
35*5c47fe09SDavid du Colombier static Lcd pitft = {
36*5c47fe09SDavid du Colombier pitftdraw,
37*5c47fe09SDavid du Colombier pitftblank,
38*5c47fe09SDavid du Colombier };
39*5c47fe09SDavid du Colombier
40*5c47fe09SDavid du Colombier static Queue *updateq = nil;
41*5c47fe09SDavid du Colombier
42*5c47fe09SDavid du Colombier void
pitftlink(void)43*5c47fe09SDavid du Colombier pitftlink(void)
44*5c47fe09SDavid du Colombier {
45*5c47fe09SDavid du Colombier addarchfile("pitft", 0666, pitftread, pitftwrite);
46*5c47fe09SDavid du Colombier }
47*5c47fe09SDavid du Colombier
48*5c47fe09SDavid du Colombier static void
pitftsetup(void)49*5c47fe09SDavid du Colombier pitftsetup(void)
50*5c47fe09SDavid du Colombier {
51*5c47fe09SDavid du Colombier uchar spibuf[32];
52*5c47fe09SDavid du Colombier
53*5c47fe09SDavid du Colombier gpiosel(25, Output);
54*5c47fe09SDavid du Colombier spirw(0, spibuf, 1);
55*5c47fe09SDavid du Colombier spicmd(0x01);
56*5c47fe09SDavid du Colombier delay(10);
57*5c47fe09SDavid du Colombier spicmd(0x11);
58*5c47fe09SDavid du Colombier delay(10);
59*5c47fe09SDavid du Colombier spicmd(0x29);
60*5c47fe09SDavid du Colombier spicmd(0x13);
61*5c47fe09SDavid du Colombier spicmd(0x36);
62*5c47fe09SDavid du Colombier spibuf[0] = 0xe8;
63*5c47fe09SDavid du Colombier spidata(spibuf, 1);
64*5c47fe09SDavid du Colombier spicmd(0x3a);
65*5c47fe09SDavid du Colombier spibuf[0] = 0x05;
66*5c47fe09SDavid du Colombier spidata(spibuf, 1);
67*5c47fe09SDavid du Colombier }
68*5c47fe09SDavid du Colombier
69*5c47fe09SDavid du Colombier static long
pitftread(Chan *,void *,long,vlong)70*5c47fe09SDavid du Colombier pitftread(Chan *, void *, long, vlong)
71*5c47fe09SDavid du Colombier {
72*5c47fe09SDavid du Colombier return 0;
73*5c47fe09SDavid du Colombier }
74*5c47fe09SDavid du Colombier
75*5c47fe09SDavid du Colombier static long
pitftwrite(Chan *,void * a,long n,vlong)76*5c47fe09SDavid du Colombier pitftwrite(Chan *, void *a, long n, vlong)
77*5c47fe09SDavid du Colombier {
78*5c47fe09SDavid du Colombier if(strncmp(a, "init", 4) == 0 && updateq == nil) {
79*5c47fe09SDavid du Colombier /*
80*5c47fe09SDavid du Colombier * The HX8357 datasheet shows minimum
81*5c47fe09SDavid du Colombier * clock cycle time of 66nS but the clock high
82*5c47fe09SDavid du Colombier * and low times as 15nS and it seems to
83*5c47fe09SDavid du Colombier * work at around 32MHz.
84*5c47fe09SDavid du Colombier */
85*5c47fe09SDavid du Colombier spiclock(32);
86*5c47fe09SDavid du Colombier pitftsetup();
87*5c47fe09SDavid du Colombier updateq = qopen(16384, 1, nil, nil);
88*5c47fe09SDavid du Colombier kproc("pitft", xpitftdraw, nil);
89*5c47fe09SDavid du Colombier lcd = &pitft;
90*5c47fe09SDavid du Colombier }
91*5c47fe09SDavid du Colombier return n;
92*5c47fe09SDavid du Colombier }
93*5c47fe09SDavid du Colombier
94*5c47fe09SDavid du Colombier static void
pitftblank(int blank)95*5c47fe09SDavid du Colombier pitftblank(int blank)
96*5c47fe09SDavid du Colombier {
97*5c47fe09SDavid du Colombier USED(blank);
98*5c47fe09SDavid du Colombier }
99*5c47fe09SDavid du Colombier
100*5c47fe09SDavid du Colombier static void
pitftdraw(Rectangle r)101*5c47fe09SDavid du Colombier pitftdraw(Rectangle r)
102*5c47fe09SDavid du Colombier {
103*5c47fe09SDavid du Colombier if(updateq == nil)
104*5c47fe09SDavid du Colombier return;
105*5c47fe09SDavid du Colombier if(r.min.x > TFTWidth || r.min.y > TFTHeight)
106*5c47fe09SDavid du Colombier return;
107*5c47fe09SDavid du Colombier /*
108*5c47fe09SDavid du Colombier * using qproduce to make sure we don't block
109*5c47fe09SDavid du Colombier * but if we've got a lot on the queue, it means we're
110*5c47fe09SDavid du Colombier * redrawing the same areas over and over; clear it
111*5c47fe09SDavid du Colombier * out and just draw the whole screen once
112*5c47fe09SDavid du Colombier */
113*5c47fe09SDavid du Colombier if(qproduce(updateq, &r, sizeof(Rectangle)) == -1) {
114*5c47fe09SDavid du Colombier r = Rect(0, 0, TFTWidth, TFTHeight);
115*5c47fe09SDavid du Colombier qflush(updateq);
116*5c47fe09SDavid du Colombier qproduce(updateq, &r, sizeof(Rectangle));
117*5c47fe09SDavid du Colombier }
118*5c47fe09SDavid du Colombier }
119*5c47fe09SDavid du Colombier
120*5c47fe09SDavid du Colombier int
overlap(Rectangle r1,Rectangle r2)121*5c47fe09SDavid du Colombier overlap(Rectangle r1, Rectangle r2)
122*5c47fe09SDavid du Colombier {
123*5c47fe09SDavid du Colombier if(r1.max.x < r2.min.x)
124*5c47fe09SDavid du Colombier return 0;
125*5c47fe09SDavid du Colombier if(r1.min.x > r2.max.x)
126*5c47fe09SDavid du Colombier return 0;
127*5c47fe09SDavid du Colombier if(r1.max.y < r2.min.y)
128*5c47fe09SDavid du Colombier return 0;
129*5c47fe09SDavid du Colombier if(r1.min.y > r2.max.y)
130*5c47fe09SDavid du Colombier return 0;
131*5c47fe09SDavid du Colombier return 1;
132*5c47fe09SDavid du Colombier }
133*5c47fe09SDavid du Colombier
134*5c47fe09SDavid du Colombier int
min(int x,int y)135*5c47fe09SDavid du Colombier min(int x, int y)
136*5c47fe09SDavid du Colombier {
137*5c47fe09SDavid du Colombier if(x < y)
138*5c47fe09SDavid du Colombier return x;
139*5c47fe09SDavid du Colombier return y;
140*5c47fe09SDavid du Colombier }
141*5c47fe09SDavid du Colombier
142*5c47fe09SDavid du Colombier int
max(int x,int y)143*5c47fe09SDavid du Colombier max(int x, int y)
144*5c47fe09SDavid du Colombier {
145*5c47fe09SDavid du Colombier if(x < y)
146*5c47fe09SDavid du Colombier return y;
147*5c47fe09SDavid du Colombier return x;
148*5c47fe09SDavid du Colombier }
149*5c47fe09SDavid du Colombier
150*5c47fe09SDavid du Colombier /*
151*5c47fe09SDavid du Colombier * Because everyone wants to be holding locks when
152*5c47fe09SDavid du Colombier * they update the screen but we need to sleep in the
153*5c47fe09SDavid du Colombier * SPI code, we're decoupling this into a separate kproc().
154*5c47fe09SDavid du Colombier */
155*5c47fe09SDavid du Colombier static void
xpitftdraw(void *)156*5c47fe09SDavid du Colombier xpitftdraw(void *)
157*5c47fe09SDavid du Colombier {
158*5c47fe09SDavid du Colombier Rectangle rec, bb;
159*5c47fe09SDavid du Colombier Point pt;
160*5c47fe09SDavid du Colombier uchar *p;
161*5c47fe09SDavid du Colombier int i, r, c, gotrec;
162*5c47fe09SDavid du Colombier uchar spibuf[32];
163*5c47fe09SDavid du Colombier
164*5c47fe09SDavid du Colombier gotrec = 0;
165*5c47fe09SDavid du Colombier qread(updateq, &rec, sizeof(Rectangle));
166*5c47fe09SDavid du Colombier bb = Rect(0, 0, TFTWidth, TFTHeight);
167*5c47fe09SDavid du Colombier while(1) {
168*5c47fe09SDavid du Colombier setwindow(bb.min.x, bb.min.y,
169*5c47fe09SDavid du Colombier bb.max.x-1, bb.max.y-1);
170*5c47fe09SDavid du Colombier spicmd(0x2c);
171*5c47fe09SDavid du Colombier for(r = bb.min.y; r < bb.max.y; ++r) {
172*5c47fe09SDavid du Colombier for(c = bb.min.x; c < bb.max.x; c += 8) {
173*5c47fe09SDavid du Colombier for(i = 0; i < 8; ++i) {
174*5c47fe09SDavid du Colombier pt.y = r;
175*5c47fe09SDavid du Colombier pt.x = c + i;
176*5c47fe09SDavid du Colombier p = byteaddr(&xgscreen, pt);
177*5c47fe09SDavid du Colombier switch(xgscreen.depth) {
178*5c47fe09SDavid du Colombier case 16: // RGB16
179*5c47fe09SDavid du Colombier spibuf[i*2+1] = p[0];
180*5c47fe09SDavid du Colombier spibuf[i*2] = p[1];
181*5c47fe09SDavid du Colombier break;
182*5c47fe09SDavid du Colombier case 24: // BGR24
183*5c47fe09SDavid du Colombier spibuf[i*2] = (p[2] & 0xf8) |
184*5c47fe09SDavid du Colombier (p[1] >> 5);
185*5c47fe09SDavid du Colombier spibuf[i*2+1] = (p[0] >> 3) |
186*5c47fe09SDavid du Colombier (p[1] << 3);
187*5c47fe09SDavid du Colombier break;
188*5c47fe09SDavid du Colombier case 32: // ARGB32
189*5c47fe09SDavid du Colombier spibuf[i*2] = (p[0] & 0xf8) |
190*5c47fe09SDavid du Colombier (p[1] >> 5);
191*5c47fe09SDavid du Colombier spibuf[i*2+1] = (p[1] >> 3) |
192*5c47fe09SDavid du Colombier (p[1] << 3);
193*5c47fe09SDavid du Colombier break;
194*5c47fe09SDavid du Colombier }
195*5c47fe09SDavid du Colombier }
196*5c47fe09SDavid du Colombier spidata(spibuf, 16);
197*5c47fe09SDavid du Colombier }
198*5c47fe09SDavid du Colombier }
199*5c47fe09SDavid du Colombier bb.max.y = -1;
200*5c47fe09SDavid du Colombier while(1) {
201*5c47fe09SDavid du Colombier if(!gotrec) {
202*5c47fe09SDavid du Colombier qread(updateq, &rec, sizeof(Rectangle));
203*5c47fe09SDavid du Colombier gotrec = 1;
204*5c47fe09SDavid du Colombier }
205*5c47fe09SDavid du Colombier if(bb.max.y != -1) {
206*5c47fe09SDavid du Colombier if(!overlap(bb, rec))
207*5c47fe09SDavid du Colombier break;
208*5c47fe09SDavid du Colombier rec.min.x = min(rec.min.x, bb.min.x);
209*5c47fe09SDavid du Colombier rec.min.y = min(rec.min.y, bb.min.y);
210*5c47fe09SDavid du Colombier rec.max.x = max(rec.max.x, bb.max.x);
211*5c47fe09SDavid du Colombier rec.max.y = max(rec.max.y, bb.max.y);
212*5c47fe09SDavid du Colombier }
213*5c47fe09SDavid du Colombier gotrec = 0;
214*5c47fe09SDavid du Colombier // Expand rows to 8 pixel alignment
215*5c47fe09SDavid du Colombier bb.min.x = rec.min.x & ~7;
216*5c47fe09SDavid du Colombier if(bb.min.x < 0)
217*5c47fe09SDavid du Colombier bb.min.x = 0;
218*5c47fe09SDavid du Colombier bb.max.x = (rec.max.x + 7) & ~7;
219*5c47fe09SDavid du Colombier if(bb.max.x > TFTWidth)
220*5c47fe09SDavid du Colombier bb.max.x = TFTWidth;
221*5c47fe09SDavid du Colombier bb.min.y = rec.min.y;
222*5c47fe09SDavid du Colombier if(bb.min.y < 0)
223*5c47fe09SDavid du Colombier bb.min.y = 0;
224*5c47fe09SDavid du Colombier bb.max.y = rec.max.y;
225*5c47fe09SDavid du Colombier if(bb.max.y > TFTHeight)
226*5c47fe09SDavid du Colombier bb.max.y = TFTHeight;
227*5c47fe09SDavid du Colombier if(qcanread(updateq)) {
228*5c47fe09SDavid du Colombier qread(updateq, &rec, sizeof(Rectangle));
229*5c47fe09SDavid du Colombier gotrec = 1;
230*5c47fe09SDavid du Colombier }
231*5c47fe09SDavid du Colombier else
232*5c47fe09SDavid du Colombier break;
233*5c47fe09SDavid du Colombier }
234*5c47fe09SDavid du Colombier }
235*5c47fe09SDavid du Colombier }
236*5c47fe09SDavid du Colombier
237*5c47fe09SDavid du Colombier static void
spicmd(uchar c)238*5c47fe09SDavid du Colombier spicmd(uchar c)
239*5c47fe09SDavid du Colombier {
240*5c47fe09SDavid du Colombier char buf;
241*5c47fe09SDavid du Colombier
242*5c47fe09SDavid du Colombier gpioout(25, 0);
243*5c47fe09SDavid du Colombier buf = c;
244*5c47fe09SDavid du Colombier spirw(0, &buf, 1);
245*5c47fe09SDavid du Colombier }
246*5c47fe09SDavid du Colombier
247*5c47fe09SDavid du Colombier static void
spidata(uchar * p,int n)248*5c47fe09SDavid du Colombier spidata(uchar *p, int n)
249*5c47fe09SDavid du Colombier {
250*5c47fe09SDavid du Colombier char buf[128];
251*5c47fe09SDavid du Colombier
252*5c47fe09SDavid du Colombier if(n > 128)
253*5c47fe09SDavid du Colombier n = 128;
254*5c47fe09SDavid du Colombier gpioout(25, 1);
255*5c47fe09SDavid du Colombier memmove(buf, p, n);
256*5c47fe09SDavid du Colombier spirw(0, buf, n);
257*5c47fe09SDavid du Colombier gpioout(25, 0);
258*5c47fe09SDavid du Colombier }
259*5c47fe09SDavid du Colombier
260*5c47fe09SDavid du Colombier static void
setwindow(int minc,int minr,int maxc,int maxr)261*5c47fe09SDavid du Colombier setwindow(int minc, int minr, int maxc, int maxr)
262*5c47fe09SDavid du Colombier {
263*5c47fe09SDavid du Colombier uchar spibuf[4];
264*5c47fe09SDavid du Colombier
265*5c47fe09SDavid du Colombier spicmd(0x2a);
266*5c47fe09SDavid du Colombier spibuf[0] = minc >> 8;
267*5c47fe09SDavid du Colombier spibuf[1] = minc & 0xff;
268*5c47fe09SDavid du Colombier spibuf[2] = maxc >> 8;
269*5c47fe09SDavid du Colombier spibuf[3] = maxc & 0xff;
270*5c47fe09SDavid du Colombier spidata(spibuf, 4);
271*5c47fe09SDavid du Colombier spicmd(0x2b);
272*5c47fe09SDavid du Colombier spibuf[0] = minr >> 8;
273*5c47fe09SDavid du Colombier spibuf[1] = minr & 0xff;
274*5c47fe09SDavid du Colombier spibuf[2] = maxr >> 8;
275*5c47fe09SDavid du Colombier spibuf[3] = maxr & 0xff;
276*5c47fe09SDavid du Colombier spidata(spibuf, 4);
277*5c47fe09SDavid du Colombier }
278