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