1/* $NetBSD: ssdfb_spi.c,v 1.14 2022/01/19 13:33:49 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.14 2022/01/19 13:33:49 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#include <dev/spi/spivar.h>
41#include <dev/ic/ssdfbvar.h>
42#include "opt_fdt.h"
43#ifdef FDT
44#include <dev/fdt/fdtvar.h>
45#endif
46
47struct bs_state {
48	uint8_t	*base;
49	uint8_t	*cur;
50	uint8_t	mask;
51};
52
53struct ssdfb_spi_softc {
54	struct ssdfb_softc	sc;
55	struct spi_handle	*sc_sh;
56#ifdef FDT
57	struct fdtbus_gpio_pin	*sc_gpio_dc;
58	struct fdtbus_gpio_pin	*sc_gpio_res;
59#endif
60	bool			sc_3wiremode;
61	bool			sc_late_dc_deassert;
62	uint8_t			sc_padding_cmd;
63};
64
65static int	ssdfb_spi_match(device_t, cfdata_t, void *);
66static void	ssdfb_spi_attach(device_t, device_t, void *);
67
68static int	ssdfb_spi_cmd_3wire(void *, uint8_t *, size_t, bool);
69static int	ssdfb_spi_xfer_rect_3wire_ssd1322(void *, uint8_t, uint8_t,
70		    uint8_t, uint8_t, uint8_t *, size_t, bool);
71
72static int	ssdfb_spi_cmd_4wire(void *, uint8_t *, size_t, bool);
73static int	ssdfb_spi_xfer_rect_4wire_sh1106(void *, uint8_t, uint8_t,
74		    uint8_t, uint8_t, uint8_t *, size_t, bool);
75static int	ssdfb_spi_xfer_rect_4wire_ssd1306(void *, uint8_t, uint8_t,
76		    uint8_t, uint8_t, uint8_t *, size_t, bool);
77static int	ssdfb_spi_xfer_rect_4wire_ssd1322(void *, uint8_t, uint8_t,
78		    uint8_t, uint8_t, uint8_t *, size_t, bool);
79static int	ssdfb_spi_xfer_rect_4wire_ssd1353(void *, uint8_t, uint8_t,
80		    uint8_t, uint8_t, uint8_t *, size_t, bool);
81
82static void	ssdfb_bitstream_init(struct bs_state *, uint8_t *);
83static void	ssdfb_bitstream_append(struct bs_state *, uint8_t, uint8_t);
84static void	ssdfb_bitstream_append_cmd(struct bs_state *, uint8_t);
85static void	ssdfb_bitstream_append_data(struct bs_state *, uint8_t *,
86		    size_t);
87static void	ssdfb_bitstream_final(struct bs_state *, uint8_t);
88
89CFATTACH_DECL_NEW(ssdfb_spi, sizeof(struct ssdfb_spi_softc),
90    ssdfb_spi_match, ssdfb_spi_attach, NULL, NULL);
91
92static const struct device_compatible_entry compat_data[] = {
93	{ .compat = "solomon,ssd1306",	.value = SSDFB_PRODUCT_SSD1306_GENERIC },
94	{ .compat = "sino,sh1106",	.value = SSDFB_PRODUCT_SH1106_GENERIC },
95	{ .compat = "solomon,ssd1322",	.value = SSDFB_PRODUCT_SSD1322_GENERIC },
96	{ .compat = "solomon,ssd1353",	.value = SSDFB_PRODUCT_SSD1353_GENERIC },
97	{ .compat = "dep160128a",	.value = SSDFB_PRODUCT_DEP_160128A_RGB },
98	DEVICE_COMPAT_EOL
99};
100
101static int
102ssdfb_spi_match(device_t parent, cfdata_t match, void *aux)
103{
104	struct spi_attach_args *sa = aux;
105
106	return spi_compatible_match(sa, match, compat_data);
107}
108
109static void
110ssdfb_spi_attach(device_t parent, device_t self, void *aux)
111{
112	struct ssdfb_spi_softc *sc = device_private(self);
113	struct cfdata *cf = device_cfdata(self);
114	struct spi_attach_args *sa = aux;
115	int flags = cf->cf_flags;
116	int error;
117
118	sc->sc.sc_dev = self;
119	sc->sc_sh = sa->sa_handle;
120	sc->sc.sc_cookie = (void *)sc;
121	if ((flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) == SSDFB_PRODUCT_UNKNOWN) {
122		const struct device_compatible_entry *dce =
123			spi_compatible_lookup(sa, compat_data);
124		if (dce)
125			flags |= (int)dce->value;
126		else
127			flags |= SSDFB_PRODUCT_SSD1322_GENERIC;
128	}
129
130	/*
131	 * SSD1306 and SSD1322 data sheets specify 100ns cycle time.
132	 */
133	error = spi_configure(self, sa->sa_handle, SPI_MODE_0, 10000000);
134	if (error) {
135		return;
136	}
137
138	/*
139	 * Note on interface modes.
140	 *
141	 * 3 wire mode sends 9 bit sequences over the MOSI, MSB contains
142	 * the bit that determines if the lower 8 bits are command or data.
143	 *
144	 * 4 wire mode sends 8 bit sequences and requires an auxiliary GPIO
145	 * pin for the command/data bit.
146	 */
147#ifdef FDT
148	const int phandle = sa->sa_cookie;
149	sc->sc_gpio_dc =
150	    fdtbus_gpio_acquire(phandle, "dc-gpio", GPIO_PIN_OUTPUT);
151	if (!sc->sc_gpio_dc)
152		sc->sc_gpio_dc =
153		    fdtbus_gpio_acquire(phandle, "cd-gpio", GPIO_PIN_OUTPUT);
154	sc->sc_3wiremode = (sc->sc_gpio_dc == NULL);
155	sc->sc_gpio_res =
156	    fdtbus_gpio_acquire(phandle, "res-gpio", GPIO_PIN_OUTPUT);
157	if (sc->sc_gpio_res) {
158		fdtbus_gpio_write_raw(sc->sc_gpio_res, 0);
159		DELAY(100);
160		fdtbus_gpio_write_raw(sc->sc_gpio_res, 1);
161		DELAY(100);
162	}
163#else
164	sc->sc_3wiremode = true;
165#endif
166
167	sc->sc.sc_cmd = sc->sc_3wiremode
168	    ? ssdfb_spi_cmd_3wire
169	    : ssdfb_spi_cmd_4wire;
170
171	switch (flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) {
172	case SSDFB_PRODUCT_SH1106_GENERIC:
173		sc->sc.sc_transfer_rect = sc->sc_3wiremode
174		    ? NULL
175		    : ssdfb_spi_xfer_rect_4wire_sh1106;
176		sc->sc_padding_cmd = SSDFB_CMD_NOP;
177		sc->sc_late_dc_deassert = true;
178		break;
179	case SSDFB_PRODUCT_SSD1306_GENERIC:
180		sc->sc.sc_transfer_rect = sc->sc_3wiremode
181		    ? NULL
182		    : ssdfb_spi_xfer_rect_4wire_ssd1306;
183		sc->sc_padding_cmd = SSDFB_CMD_NOP;
184		sc->sc_late_dc_deassert = true;
185		break;
186	case SSDFB_PRODUCT_SSD1322_GENERIC:
187		sc->sc.sc_transfer_rect = sc->sc_3wiremode
188		    ? ssdfb_spi_xfer_rect_3wire_ssd1322
189		    : ssdfb_spi_xfer_rect_4wire_ssd1322;
190		sc->sc_padding_cmd = SSD1322_CMD_WRITE_RAM;
191		break;
192	case SSDFB_PRODUCT_SSD1353_GENERIC:
193	case SSDFB_PRODUCT_DEP_160128A_RGB:
194		sc->sc.sc_transfer_rect = sc->sc_3wiremode
195		    ? NULL /* not supported here */
196		    : ssdfb_spi_xfer_rect_4wire_ssd1353;
197		break;
198	}
199
200	if (!sc->sc.sc_transfer_rect) {
201		aprint_error(": sc_transfer_rect not implemented\n");
202		return;
203	}
204
205	ssdfb_attach(&sc->sc, flags);
206
207	aprint_normal_dev(self, "%d-wire SPI interface\n",
208	    sc->sc_3wiremode == true ? 3 : 4);
209}
210
211static int
212ssdfb_spi_cmd_3wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll)
213{
214	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
215	uint8_t bitstream[16 * 9 / 8];
216	struct bs_state s;
217
218	KASSERT(len > 0 && len <= 16);
219	ssdfb_bitstream_init(&s, bitstream);
220	ssdfb_bitstream_append_cmd(&s, *cmd);
221	cmd++;
222	len--;
223	ssdfb_bitstream_append_data(&s, cmd, len);
224	ssdfb_bitstream_final(&s, sc->sc_padding_cmd);
225
226	return spi_send(sc->sc_sh, s.cur - s.base, bitstream);
227}
228
229static int
230ssdfb_spi_xfer_rect_3wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol,
231    uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll)
232{
233	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
234	uint8_t bitstream[128 * 9 / 8];
235	struct bs_state s;
236	size_t rlen = (tocol + 1 - fromcol) * 2;
237	int error;
238
239	/*
240	 * Unlike iic(4), there is no way to force spi(4) to use polling.
241	 */
242	if (usepoll && !cold)
243		return 0;
244
245	ssdfb_bitstream_init(&s, bitstream);
246	ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_ROW_ADDRESS);
247	ssdfb_bitstream_append_data(&s, &fromrow, 1);
248	ssdfb_bitstream_append_data(&s, &torow, 1);
249	ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_COLUMN_ADDRESS);
250	ssdfb_bitstream_append_data(&s, &fromcol, 1);
251	ssdfb_bitstream_append_data(&s, &tocol, 1);
252	ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_WRITE_RAM);
253	ssdfb_bitstream_final(&s, sc->sc_padding_cmd);
254	error = spi_send(sc->sc_sh, s.cur - s.base, bitstream);
255	if (error)
256		return error;
257
258	KASSERT(rlen <= 128);
259	while (fromrow <= torow) {
260		ssdfb_bitstream_init(&s, bitstream);
261		ssdfb_bitstream_append_data(&s, p, rlen);
262		ssdfb_bitstream_final(&s, sc->sc_padding_cmd);
263		error = spi_send(sc->sc_sh, s.cur - s.base, bitstream);
264		if (error)
265			return error;
266		fromrow++;
267		p += stride;
268	}
269
270	return 0;
271}
272
273static void
274ssdfb_bitstream_init(struct bs_state *s, uint8_t *dst)
275{
276	s->base = s->cur = dst;
277	s->mask = 0x80;
278}
279
280static void
281ssdfb_bitstream_append(struct bs_state *s, uint8_t b, uint8_t srcmask)
282{
283	while(srcmask) {
284		if (b & srcmask)
285			*s->cur |= s->mask;
286		else
287			*s->cur &= ~s->mask;
288		srcmask >>= 1;
289		s->mask >>= 1;
290		if (!s->mask) {
291			s->mask = 0x80;
292			s->cur++;
293		}
294	}
295}
296
297static void
298ssdfb_bitstream_append_cmd(struct bs_state *s, uint8_t cmd)
299{
300	ssdfb_bitstream_append(s, 0, 1);
301	ssdfb_bitstream_append(s, cmd, 0x80);
302}
303
304static void
305ssdfb_bitstream_append_data(struct bs_state *s, uint8_t *data, size_t len)
306{
307	while(len--) {
308		ssdfb_bitstream_append(s, 1, 1);
309		ssdfb_bitstream_append(s, *data++, 0x80);
310	}
311}
312
313static void
314ssdfb_bitstream_final(struct bs_state *s, uint8_t padding_cmd)
315{
316	while (s->mask != 0x80) {
317		ssdfb_bitstream_append_cmd(s, padding_cmd);
318	}
319}
320
321static void
322ssdfb_spi_4wire_set_dc(struct ssdfb_spi_softc *sc, int value)
323{
324#ifdef FDT
325	fdtbus_gpio_write_raw(sc->sc_gpio_dc, value);
326#else
327	panic("ssdfb_spi_4wire_set_dc");
328#endif
329}
330
331static int
332ssdfb_spi_cmd_4wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll)
333{
334	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
335	int error;
336
337	ssdfb_spi_4wire_set_dc(sc, 0);
338	error = spi_send(sc->sc_sh, 1, cmd);
339	if (error)
340		return error;
341	if (len > 1) {
342		if (!sc->sc_late_dc_deassert)
343			ssdfb_spi_4wire_set_dc(sc, 1);
344		len--;
345		cmd++;
346		error = spi_send(sc->sc_sh, len, cmd);
347		if (error)
348			return error;
349	}
350
351	return 0;
352}
353
354static int
355ssdfb_spi_xfer_rect_4wire_sh1106(void *cookie, uint8_t fromcol, uint8_t tocol,
356    uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll)
357{
358	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
359	size_t rlen = tocol + 1 - fromcol;
360	int error;
361	uint8_t cmd[] = {
362		SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage,
363		SSDFB_CMD_SET_HIGHER_COLUMN_START_ADDRESS_BASE + (fromcol >> 4),
364		SSDFB_CMD_SET_LOWER_COLUMN_START_ADDRESS_BASE + (fromcol & 0xf)
365	};
366
367	if (usepoll && !cold)
368		return 0;
369
370	while (frompage <= topage) {
371		cmd[0] = SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage;
372		ssdfb_spi_4wire_set_dc(sc, 0);
373		error = spi_send(sc->sc_sh, sizeof(cmd), cmd);
374		if (error)
375			return error;
376		ssdfb_spi_4wire_set_dc(sc, 1);
377		error = spi_send(sc->sc_sh, rlen, p);
378		if (error)
379			return error;
380		frompage++;
381		p += stride;
382	}
383
384	return 0;
385}
386
387static int
388ssdfb_spi_xfer_rect_4wire_ssd1306(void *cookie, uint8_t fromcol, uint8_t tocol,
389    uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll)
390{
391	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
392	size_t rlen = tocol + 1 - fromcol;
393	int error;
394	uint8_t cmd[] = {
395		SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE,
396		SSD1306_MEMORY_ADDRESSING_MODE_HORIZONTAL,
397		SSD1306_CMD_SET_COLUMN_ADDRESS,
398		fromcol,
399		tocol,
400		SSD1306_CMD_SET_PAGE_ADDRESS,
401		frompage,
402		topage
403	};
404
405	if (usepoll && !cold)
406		return 0;
407
408	ssdfb_spi_4wire_set_dc(sc, 0);
409	error = spi_send(sc->sc_sh, sizeof(cmd), cmd);
410	if (error)
411		return error;
412	ssdfb_spi_4wire_set_dc(sc, 1);
413
414	while (frompage <= topage) {
415		error = spi_send(sc->sc_sh, rlen, p);
416		if (error)
417			return error;
418		frompage++;
419		p += stride;
420	}
421
422	return 0;
423}
424
425static int
426ssdfb_spi_xfer_rect_4wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol,
427    uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll)
428{
429	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
430	size_t rlen = (tocol + 1 - fromcol) * 2;
431	int error;
432	uint8_t cmd;
433	uint8_t data[2];
434
435	if (usepoll && !cold)
436		return 0;
437
438	ssdfb_spi_4wire_set_dc(sc, 0);
439	cmd = SSD1322_CMD_SET_ROW_ADDRESS;
440	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
441	if (error)
442		return error;
443	ssdfb_spi_4wire_set_dc(sc, 1);
444	data[0] = fromrow;
445	data[1] = torow;
446	error = spi_send(sc->sc_sh, sizeof(data), data);
447	if (error)
448		return error;
449
450	ssdfb_spi_4wire_set_dc(sc, 0);
451	cmd = SSD1322_CMD_SET_COLUMN_ADDRESS;
452	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
453	if (error)
454		return error;
455	ssdfb_spi_4wire_set_dc(sc, 1);
456	data[0] = fromcol;
457	data[1] = tocol;
458	error = spi_send(sc->sc_sh, sizeof(data), data);
459	if (error)
460		return error;
461
462	ssdfb_spi_4wire_set_dc(sc, 0);
463	cmd = SSD1322_CMD_WRITE_RAM;
464	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
465	if (error)
466		return error;
467
468	ssdfb_spi_4wire_set_dc(sc, 1);
469	while (fromrow <= torow) {
470		error = spi_send(sc->sc_sh, rlen, p);
471		if (error)
472			return error;
473		fromrow++;
474		p += stride;
475	}
476
477	return 0;
478}
479
480static int
481ssdfb_spi_xfer_rect_4wire_ssd1353(void *cookie, uint8_t fromcol, uint8_t tocol,
482    uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll)
483{
484	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
485	size_t rlen = (tocol + 1 - fromcol) * 3;
486	uint8_t bitstream[160 * 3];
487	uint8_t *dstp, *srcp, *endp;
488	int error;
489	uint8_t cmd;
490	uint8_t data[2];
491
492	if (usepoll && !cold)
493		return 0;
494
495	ssdfb_spi_4wire_set_dc(sc, 0);
496	cmd = SSD1353_CMD_SET_ROW_ADDRESS;
497	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
498	if (error)
499		return error;
500	ssdfb_spi_4wire_set_dc(sc, 1);
501	data[0] = fromrow;
502	data[1] = torow;
503	if (sc->sc.sc_upsidedown) {
504		/* fix picture outside frame on 160x128 panel */
505		data[0] += 132 - sc->sc.sc_p->p_height;
506		data[1] += 132 - sc->sc.sc_p->p_height;
507	}
508	error = spi_send(sc->sc_sh, sizeof(data), data);
509	if (error)
510		return error;
511
512	ssdfb_spi_4wire_set_dc(sc, 0);
513	cmd = SSD1353_CMD_SET_COLUMN_ADDRESS;
514	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
515	if (error)
516		return error;
517	ssdfb_spi_4wire_set_dc(sc, 1);
518	data[0] = fromcol;
519	data[1] = tocol;
520	error = spi_send(sc->sc_sh, sizeof(data), data);
521	if (error)
522		return error;
523
524	ssdfb_spi_4wire_set_dc(sc, 0);
525	cmd = SSD1353_CMD_WRITE_RAM;
526	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
527	if (error)
528		return error;
529
530	ssdfb_spi_4wire_set_dc(sc, 1);
531	KASSERT(rlen <= sizeof(bitstream));
532	while (fromrow <= torow) {
533		/* downconvert each row from 32bpp rgba to 18bpp panel format */
534		dstp = bitstream;
535		endp = dstp + rlen;
536		srcp = p;
537		while (dstp < endp) {
538			*dstp++ = (*srcp++) >> 2;
539			*dstp++ = (*srcp++) >> 2;
540			*dstp++ = (*srcp++) >> 2;
541			srcp++;
542		}
543		error = spi_send(sc->sc_sh, rlen, bitstream);
544		if (error)
545			return error;
546		fromrow++;
547		p += stride;
548	}
549
550	return 0;
551}
552