xref: /plan9-contrib/sys/src/9/bcm/pitft.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
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