ssdfb_spi.c revision 1.5.4.3 1 /* $NetBSD: ssdfb_spi.c,v 1.5.4.3 2021/08/01 22:42:31 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 2019 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Tobias Nygren.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: ssdfb_spi.c,v 1.5.4.3 2021/08/01 22:42:31 thorpej Exp $");
34
35 #include <sys/param.h>
36 #include <sys/device.h>
37 #include <sys/kernel.h>
38 #include <dev/wscons/wsdisplayvar.h>
39 #include <dev/rasops/rasops.h>
40
41 #include <dev/spi/spivar.h>
42 #include <dev/ic/ssdfbvar.h>
43 #include "opt_fdt.h"
44 #ifdef FDT
45 #include <dev/fdt/fdtvar.h>
46 #endif /* FDT */
47
48 struct bs_state {
49 uint8_t *base;
50 uint8_t *cur;
51 uint8_t mask;
52 };
53
54 struct ssdfb_spi_softc {
55 struct ssdfb_softc sc;
56 struct spi_handle *sc_sh;
57 #ifdef FDT
58 struct fdtbus_gpio_pin *sc_gpio_dc;
59 #endif /* FDT */
60 bool sc_3wiremode;
61 };
62
63 static int ssdfb_spi_match(device_t, cfdata_t, void *);
64 static void ssdfb_spi_attach(device_t, device_t, void *);
65
66 static int ssdfb_spi_cmd_3wire(void *, uint8_t *, size_t, bool);
67 static int ssdfb_spi_xfer_rect_3wire_ssd1322(void *, uint8_t, uint8_t,
68 uint8_t, uint8_t, uint8_t *, size_t, bool);
69
70 static int ssdfb_spi_cmd_4wire(void *, uint8_t *, size_t, bool);
71 static int ssdfb_spi_xfer_rect_4wire_ssd1322(void *, uint8_t, uint8_t,
72 uint8_t, uint8_t, uint8_t *, size_t, bool);
73
74 static void ssdfb_bitstream_init(struct bs_state *, uint8_t *);
75 static void ssdfb_bitstream_append(struct bs_state *, uint8_t, uint8_t);
76 static void ssdfb_bitstream_append_cmd(struct bs_state *, uint8_t);
77 static void ssdfb_bitstream_append_data(struct bs_state *, uint8_t *,
78 size_t);
79 static void ssdfb_bitstream_final(struct bs_state *);
80
81 CFATTACH_DECL_NEW(ssdfb_spi, sizeof(struct ssdfb_spi_softc),
82 ssdfb_spi_match, ssdfb_spi_attach, NULL, NULL);
83
84 static const struct device_compatible_entry compat_data[] = {
85 { .compat = "solomon,ssd1306", .value = SSDFB_PRODUCT_SSD1306_GENERIC },
86 { .compat = "solomon,ssd1322", .value = SSDFB_PRODUCT_SSD1322_GENERIC },
87 DEVICE_COMPAT_EOL
88 };
89
90 static int
91 ssdfb_spi_match(device_t parent, cfdata_t match, void *aux)
92 {
93 struct spi_attach_args *sa = aux;
94
95 return spi_compatible_match(sa, match, compat_data);
96 }
97
98 #ifdef FDT
99 static void
100 ssdfb_spi_dc_gpio_fdt(struct ssdfb_spi_softc *sc)
101 {
102 devhandle_t devhandle = device_handle(sc->sc.sc_dev);
103 int phandle = devhandle_to_of(devhandle);
104
105 sc->sc_gpio_dc = fdtbus_gpio_acquire(phandle, "dc-gpio",
106 GPIO_PIN_OUTPUT);
107 if (sc->sc_dc_gpio == NULL) {
108 sc->sc_gpio_dc = fdtbus_gpio_acquire(phandle, "cd-gpio",
109 GPIO_PIN_OUTPUT);
110 }
111 if (sc->sc_dc_gpio != NULL) {
112 sc->sc_3wiremode = false;
113 }
114 }
115 #endif /* FDT */
116
117 static void
118 ssdfb_spi_attach(device_t parent, device_t self, void *aux)
119 {
120 struct ssdfb_spi_softc *sc = device_private(self);
121 devhandle_t devhandle = device_handle(self);
122 struct cfdata *cf = device_cfdata(self);
123 struct spi_attach_args *sa = aux;
124 int flags = cf->cf_flags;
125 int error;
126
127 sc->sc.sc_dev = self;
128 sc->sc_sh = sa->sa_handle;
129 sc->sc.sc_cookie = (void *)sc;
130
131 /*
132 * SSD1306 and SSD1322 data sheets specify 100ns cycle time.
133 */
134 error = spi_configure(sa->sa_handle, SPI_MODE_0, 10000000);
135 if (error) {
136 aprint_error(": spi_configure failed (error = %d)\n",
137 error);
138 return;
139 }
140
141 if ((flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) == SSDFB_PRODUCT_UNKNOWN) {
142 const struct device_compatible_entry *dce =
143 device_compatible_lookup(sa->sa_compat, sa->sa_ncompat,
144 compat_data);
145 if (dce)
146 flags |= (int)dce->value;
147 else
148 flags |= SSDFB_PRODUCT_SSD1322_GENERIC;
149 }
150
151 /*
152 * Note on interface modes.
153 *
154 * 3 wire mode sends 9 bit sequences over the MOSI, MSB contains
155 * the bit that determines if the lower 8 bits are command or data.
156 *
157 * 4 wire mode sends 8 bit sequences and requires an auxiliary GPIO
158 * pin for the command/data bit.
159 *
160 * Default to 3 wire mode. If the device tree specifies a
161 * D/C GPIO pin, then we will use 4 wire mode.
162 */
163 sc->sc_3wiremode = true;
164 switch (devhandle_type(devhandle)) {
165 #ifdef FDT
166 case DEVHANDLE_TYPE_OF:
167 ssdfb_spi_dc_gpio_fdt(sc);
168 break;
169 #endif /* FDT */
170 default:
171 break;
172 }
173
174 switch (flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) {
175 case SSDFB_PRODUCT_SSD1322_GENERIC:
176 if (sc->sc_3wiremode) {
177 sc->sc.sc_transfer_rect =
178 ssdfb_spi_xfer_rect_3wire_ssd1322;
179 } else {
180 sc->sc.sc_transfer_rect =
181 ssdfb_spi_xfer_rect_4wire_ssd1322;
182 }
183 break;
184 default:
185 panic("ssdfb_spi_attach: product not implemented");
186 }
187 if (sc->sc_3wiremode) {
188 sc->sc.sc_cmd = ssdfb_spi_cmd_3wire;
189 } else {
190 sc->sc.sc_cmd = ssdfb_spi_cmd_4wire;
191 }
192
193 ssdfb_attach(&sc->sc, flags);
194
195 device_printf(sc->sc.sc_dev, "%d-wire SPI interface\n",
196 sc->sc_3wiremode == true ? 3 : 4);
197 }
198
199 static int
200 ssdfb_spi_cmd_3wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll)
201 {
202 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
203 uint8_t bitstream[16 * 9 / 8];
204 struct bs_state s;
205
206 KASSERT(len > 0 && len <= 16);
207 ssdfb_bitstream_init(&s, bitstream);
208 ssdfb_bitstream_append_cmd(&s, *cmd);
209 cmd++;
210 len--;
211 ssdfb_bitstream_append_data(&s, cmd, len);
212 ssdfb_bitstream_final(&s);
213
214 return spi_send(sc->sc_sh, s.cur - s.base, bitstream);
215 }
216
217 static int
218 ssdfb_spi_xfer_rect_3wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol,
219 uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll)
220 {
221 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
222 uint8_t bitstream[128 * 9 / 8];
223 struct bs_state s;
224 uint8_t row;
225 size_t rlen = (tocol + 1 - fromcol) * 2;
226 int error;
227
228 /*
229 * Unlike iic(4), there is no way to force spi(4) to use polling.
230 */
231 if (usepoll && !cold)
232 return 0;
233
234 ssdfb_bitstream_init(&s, bitstream);
235 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_ROW_ADDRESS);
236 ssdfb_bitstream_append_data(&s, &fromrow, 1);
237 ssdfb_bitstream_append_data(&s, &torow, 1);
238 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_COLUMN_ADDRESS);
239 ssdfb_bitstream_append_data(&s, &fromcol, 1);
240 ssdfb_bitstream_append_data(&s, &tocol, 1);
241 ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_WRITE_RAM);
242 ssdfb_bitstream_final(&s);
243 error = spi_send(sc->sc_sh, s.cur - s.base, bitstream);
244 if (error)
245 return error;
246
247 KASSERT(rlen <= 128);
248 for (row = fromrow; row <= torow; row++) {
249 ssdfb_bitstream_init(&s, bitstream);
250 ssdfb_bitstream_append_data(&s, p, rlen);
251 ssdfb_bitstream_final(&s);
252 error = spi_send(sc->sc_sh, s.cur - s.base, bitstream);
253 if (error)
254 return error;
255 p += stride;
256 }
257
258 return 0;
259 }
260
261 static void
262 ssdfb_bitstream_init(struct bs_state *s, uint8_t *dst)
263 {
264 s->base = s->cur = dst;
265 s->mask = 0x80;
266 }
267
268 static void
269 ssdfb_bitstream_append(struct bs_state *s, uint8_t b, uint8_t srcmask)
270 {
271 while(srcmask) {
272 if (b & srcmask)
273 *s->cur |= s->mask;
274 else
275 *s->cur &= ~s->mask;
276 srcmask >>= 1;
277 s->mask >>= 1;
278 if (!s->mask) {
279 s->mask = 0x80;
280 s->cur++;
281 }
282 }
283 }
284
285 static void
286 ssdfb_bitstream_append_cmd(struct bs_state *s, uint8_t cmd)
287 {
288 ssdfb_bitstream_append(s, 0, 1);
289 ssdfb_bitstream_append(s, cmd, 0x80);
290 }
291
292 static void
293 ssdfb_bitstream_append_data(struct bs_state *s, uint8_t *data, size_t len)
294 {
295 while(len--) {
296 ssdfb_bitstream_append(s, 1, 1);
297 ssdfb_bitstream_append(s, *data++, 0x80);
298 }
299 }
300
301 static void
302 ssdfb_bitstream_final(struct bs_state *s)
303 {
304 uint8_t padding_cmd = SSD1322_CMD_WRITE_RAM;
305 /* padding_cmd = SSDFB_NOP_CMD; */
306
307 while (s->mask != 0x80) {
308 ssdfb_bitstream_append_cmd(s, padding_cmd);
309 }
310 }
311
312 static void
313 ssdfb_spi_4wire_set_dc(struct ssdfb_spi_softc *sc, int value)
314 {
315 /* TODO: refactor this if we ever support more that just FDT. */
316
317 #ifdef FDT
318 KASSERT(sc->sc_gpio_dc != NULL);
319 fdtbus_gpio_write(sc->sc_dc_gpio, value);
320 #else
321 /* TODO: this should toggle an auxilliary GPIO pin */
322 panic("ssdfb_spi_4wire_set_dc");
323 #endif /* FDT */
324 }
325
326 static int
327 ssdfb_spi_cmd_4wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll)
328 {
329 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
330 int error;
331
332 ssdfb_spi_4wire_set_dc(sc, 0);
333 error = spi_send(sc->sc_sh, 1, cmd);
334 if (error)
335 return error;
336 if (len > 1) {
337 ssdfb_spi_4wire_set_dc(sc, 1);
338 len--;
339 cmd++;
340 error = spi_send(sc->sc_sh, len, cmd);
341 if (error)
342 return error;
343 }
344
345 return 0;
346 }
347
348 static int
349 ssdfb_spi_xfer_rect_4wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol,
350 uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll)
351 {
352 struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
353 uint8_t row;
354 size_t rlen = (tocol + 1 - fromcol) * 2;
355 int error;
356 uint8_t cmd;
357 uint8_t data[2];
358
359 /*
360 * Unlike iic(4), there is no way to force spi(4) to use polling.
361 */
362 if (usepoll && !cold)
363 return 0;
364
365 ssdfb_spi_4wire_set_dc(sc, 0);
366 cmd = SSD1322_CMD_SET_ROW_ADDRESS;
367 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
368 if (error)
369 return error;
370 ssdfb_spi_4wire_set_dc(sc, 1);
371 data[0] = fromrow;
372 data[1] = torow;
373 error = spi_send(sc->sc_sh, sizeof(data), data);
374 if (error)
375 return error;
376
377 ssdfb_spi_4wire_set_dc(sc, 0);
378 cmd = SSD1322_CMD_SET_COLUMN_ADDRESS;
379 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
380 if (error)
381 return error;
382 ssdfb_spi_4wire_set_dc(sc, 1);
383 data[0] = fromcol;
384 data[1] = tocol;
385 error = spi_send(sc->sc_sh, sizeof(data), data);
386 if (error)
387 return error;
388
389 ssdfb_spi_4wire_set_dc(sc, 0);
390 cmd = SSD1322_CMD_WRITE_RAM;
391 error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
392 if (error)
393 return error;
394
395 ssdfb_spi_4wire_set_dc(sc, 1);
396 for (row = fromrow; row <= torow; row++) {
397 error = spi_send(sc->sc_sh, rlen, p);
398 if (error)
399 return error;
400 p += stride;
401 }
402
403 return 0;
404 }
405