xref: /netbsd-src/sys/dev/spi/ssdfb_spi.c (revision 490e40c2193e7b89b98f63d459b36f56578bb5bb)
1*490e40c2Sthorpej /* $NetBSD: ssdfb_spi.c,v 1.14 2022/01/19 13:33:49 thorpej Exp $ */
2281830f2Stnn 
3281830f2Stnn /*
4281830f2Stnn  * Copyright (c) 2019 The NetBSD Foundation, Inc.
5281830f2Stnn  * All rights reserved.
6281830f2Stnn  *
7281830f2Stnn  * This code is derived from software contributed to The NetBSD Foundation
8281830f2Stnn  * by Tobias Nygren.
9281830f2Stnn  *
10281830f2Stnn  * Redistribution and use in source and binary forms, with or without
11281830f2Stnn  * modification, are permitted provided that the following conditions
12281830f2Stnn  * are met:
13281830f2Stnn  * 1. Redistributions of source code must retain the above copyright
14281830f2Stnn  *    notice, this list of conditions and the following disclaimer.
15281830f2Stnn  * 2. Redistributions in binary form must reproduce the above copyright
16281830f2Stnn  *    notice, this list of conditions and the following disclaimer in the
17281830f2Stnn  *    documentation and/or other materials provided with the distribution.
18281830f2Stnn  *
19281830f2Stnn  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20281830f2Stnn  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21281830f2Stnn  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22281830f2Stnn  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23281830f2Stnn  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24281830f2Stnn  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25281830f2Stnn  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26281830f2Stnn  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27281830f2Stnn  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28281830f2Stnn  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29281830f2Stnn  * POSSIBILITY OF SUCH DAMAGE.
30281830f2Stnn  */
31281830f2Stnn 
32281830f2Stnn #include <sys/cdefs.h>
33*490e40c2Sthorpej __KERNEL_RCSID(0, "$NetBSD: ssdfb_spi.c,v 1.14 2022/01/19 13:33:49 thorpej Exp $");
34281830f2Stnn 
35281830f2Stnn #include <sys/param.h>
36281830f2Stnn #include <sys/device.h>
37281830f2Stnn #include <sys/kernel.h>
38281830f2Stnn #include <dev/wscons/wsdisplayvar.h>
39281830f2Stnn #include <dev/rasops/rasops.h>
40281830f2Stnn #include <dev/spi/spivar.h>
41281830f2Stnn #include <dev/ic/ssdfbvar.h>
42eafe2c2fStnn #include "opt_fdt.h"
43eafe2c2fStnn #ifdef FDT
44eafe2c2fStnn #include <dev/fdt/fdtvar.h>
45eafe2c2fStnn #endif
46281830f2Stnn 
47281830f2Stnn struct bs_state {
48281830f2Stnn 	uint8_t	*base;
49281830f2Stnn 	uint8_t	*cur;
50281830f2Stnn 	uint8_t	mask;
51281830f2Stnn };
52281830f2Stnn 
53281830f2Stnn struct ssdfb_spi_softc {
54281830f2Stnn 	struct ssdfb_softc	sc;
55281830f2Stnn 	struct spi_handle	*sc_sh;
56eafe2c2fStnn #ifdef FDT
57eafe2c2fStnn 	struct fdtbus_gpio_pin	*sc_gpio_dc;
58c7885d28Stnn 	struct fdtbus_gpio_pin	*sc_gpio_res;
59eafe2c2fStnn #endif
60281830f2Stnn 	bool			sc_3wiremode;
61d15f52d2Stnn 	bool			sc_late_dc_deassert;
62d15f52d2Stnn 	uint8_t			sc_padding_cmd;
63281830f2Stnn };
64281830f2Stnn 
65281830f2Stnn static int	ssdfb_spi_match(device_t, cfdata_t, void *);
66281830f2Stnn static void	ssdfb_spi_attach(device_t, device_t, void *);
67281830f2Stnn 
68281830f2Stnn static int	ssdfb_spi_cmd_3wire(void *, uint8_t *, size_t, bool);
69281830f2Stnn static int	ssdfb_spi_xfer_rect_3wire_ssd1322(void *, uint8_t, uint8_t,
70281830f2Stnn 		    uint8_t, uint8_t, uint8_t *, size_t, bool);
71281830f2Stnn 
72281830f2Stnn static int	ssdfb_spi_cmd_4wire(void *, uint8_t *, size_t, bool);
73d75f2246Stnn static int	ssdfb_spi_xfer_rect_4wire_sh1106(void *, uint8_t, uint8_t,
74d75f2246Stnn 		    uint8_t, uint8_t, uint8_t *, size_t, bool);
75d75f2246Stnn static int	ssdfb_spi_xfer_rect_4wire_ssd1306(void *, uint8_t, uint8_t,
76d75f2246Stnn 		    uint8_t, uint8_t, uint8_t *, size_t, bool);
77281830f2Stnn static int	ssdfb_spi_xfer_rect_4wire_ssd1322(void *, uint8_t, uint8_t,
78281830f2Stnn 		    uint8_t, uint8_t, uint8_t *, size_t, bool);
795db07139Stnn static int	ssdfb_spi_xfer_rect_4wire_ssd1353(void *, uint8_t, uint8_t,
805db07139Stnn 		    uint8_t, uint8_t, uint8_t *, size_t, bool);
81281830f2Stnn 
82281830f2Stnn static void	ssdfb_bitstream_init(struct bs_state *, uint8_t *);
83281830f2Stnn static void	ssdfb_bitstream_append(struct bs_state *, uint8_t, uint8_t);
84281830f2Stnn static void	ssdfb_bitstream_append_cmd(struct bs_state *, uint8_t);
85281830f2Stnn static void	ssdfb_bitstream_append_data(struct bs_state *, uint8_t *,
86281830f2Stnn 		    size_t);
87d15f52d2Stnn static void	ssdfb_bitstream_final(struct bs_state *, uint8_t);
88281830f2Stnn 
89281830f2Stnn CFATTACH_DECL_NEW(ssdfb_spi, sizeof(struct ssdfb_spi_softc),
90281830f2Stnn     ssdfb_spi_match, ssdfb_spi_attach, NULL, NULL);
91281830f2Stnn 
9257e890b1Stnn static const struct device_compatible_entry compat_data[] = {
93eafe2c2fStnn 	{ .compat = "solomon,ssd1306",	.value = SSDFB_PRODUCT_SSD1306_GENERIC },
94d75f2246Stnn 	{ .compat = "sino,sh1106",	.value = SSDFB_PRODUCT_SH1106_GENERIC },
95eafe2c2fStnn 	{ .compat = "solomon,ssd1322",	.value = SSDFB_PRODUCT_SSD1322_GENERIC },
965db07139Stnn 	{ .compat = "solomon,ssd1353",	.value = SSDFB_PRODUCT_SSD1353_GENERIC },
975db07139Stnn 	{ .compat = "dep160128a",	.value = SSDFB_PRODUCT_DEP_160128A_RGB },
9818f3098cSthorpej 	DEVICE_COMPAT_EOL
9957e890b1Stnn };
10057e890b1Stnn 
101281830f2Stnn static int
ssdfb_spi_match(device_t parent,cfdata_t match,void * aux)102281830f2Stnn ssdfb_spi_match(device_t parent, cfdata_t match, void *aux)
103281830f2Stnn {
104281830f2Stnn 	struct spi_attach_args *sa = aux;
10557e890b1Stnn 
106c8694285Sthorpej 	return spi_compatible_match(sa, match, compat_data);
107281830f2Stnn }
108281830f2Stnn 
109281830f2Stnn static void
ssdfb_spi_attach(device_t parent,device_t self,void * aux)110281830f2Stnn ssdfb_spi_attach(device_t parent, device_t self, void *aux)
111281830f2Stnn {
112281830f2Stnn 	struct ssdfb_spi_softc *sc = device_private(self);
113281830f2Stnn 	struct cfdata *cf = device_cfdata(self);
114281830f2Stnn 	struct spi_attach_args *sa = aux;
115281830f2Stnn 	int flags = cf->cf_flags;
116c8694285Sthorpej 	int error;
117281830f2Stnn 
118281830f2Stnn 	sc->sc.sc_dev = self;
119281830f2Stnn 	sc->sc_sh = sa->sa_handle;
120281830f2Stnn 	sc->sc.sc_cookie = (void *)sc;
121eafe2c2fStnn 	if ((flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) == SSDFB_PRODUCT_UNKNOWN) {
122eafe2c2fStnn 		const struct device_compatible_entry *dce =
123*490e40c2Sthorpej 			spi_compatible_lookup(sa, compat_data);
124eafe2c2fStnn 		if (dce)
125eafe2c2fStnn 			flags |= (int)dce->value;
126eafe2c2fStnn 		else
127281830f2Stnn 			flags |= SSDFB_PRODUCT_SSD1322_GENERIC;
128eafe2c2fStnn 	}
129c8694285Sthorpej 
130c8694285Sthorpej 	/*
131c8694285Sthorpej 	 * SSD1306 and SSD1322 data sheets specify 100ns cycle time.
132c8694285Sthorpej 	 */
133f64c887eSthorpej 	error = spi_configure(self, sa->sa_handle, SPI_MODE_0, 10000000);
134c8694285Sthorpej 	if (error) {
135c8694285Sthorpej 		return;
136c8694285Sthorpej 	}
137c8694285Sthorpej 
138281830f2Stnn 	/*
139281830f2Stnn 	 * Note on interface modes.
140281830f2Stnn 	 *
141281830f2Stnn 	 * 3 wire mode sends 9 bit sequences over the MOSI, MSB contains
142281830f2Stnn 	 * the bit that determines if the lower 8 bits are command or data.
143281830f2Stnn 	 *
144281830f2Stnn 	 * 4 wire mode sends 8 bit sequences and requires an auxiliary GPIO
145eafe2c2fStnn 	 * pin for the command/data bit.
146281830f2Stnn 	 */
147eafe2c2fStnn #ifdef FDT
148eafe2c2fStnn 	const int phandle = sa->sa_cookie;
149c7885d28Stnn 	sc->sc_gpio_dc =
150c7885d28Stnn 	    fdtbus_gpio_acquire(phandle, "dc-gpio", GPIO_PIN_OUTPUT);
151eafe2c2fStnn 	if (!sc->sc_gpio_dc)
152c7885d28Stnn 		sc->sc_gpio_dc =
153c7885d28Stnn 		    fdtbus_gpio_acquire(phandle, "cd-gpio", GPIO_PIN_OUTPUT);
154eafe2c2fStnn 	sc->sc_3wiremode = (sc->sc_gpio_dc == NULL);
155c7885d28Stnn 	sc->sc_gpio_res =
156c7885d28Stnn 	    fdtbus_gpio_acquire(phandle, "res-gpio", GPIO_PIN_OUTPUT);
157c7885d28Stnn 	if (sc->sc_gpio_res) {
158c7885d28Stnn 		fdtbus_gpio_write_raw(sc->sc_gpio_res, 0);
159c7885d28Stnn 		DELAY(100);
160c7885d28Stnn 		fdtbus_gpio_write_raw(sc->sc_gpio_res, 1);
161c7885d28Stnn 		DELAY(100);
162c7885d28Stnn 	}
163eafe2c2fStnn #else
164eafe2c2fStnn 	sc->sc_3wiremode = true;
165eafe2c2fStnn #endif
166281830f2Stnn 
1675db07139Stnn 	sc->sc.sc_cmd = sc->sc_3wiremode
1685db07139Stnn 	    ? ssdfb_spi_cmd_3wire
1695db07139Stnn 	    : ssdfb_spi_cmd_4wire;
1705db07139Stnn 
171281830f2Stnn 	switch (flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) {
172d75f2246Stnn 	case SSDFB_PRODUCT_SH1106_GENERIC:
173d75f2246Stnn 		sc->sc.sc_transfer_rect = sc->sc_3wiremode
174d75f2246Stnn 		    ? NULL
175d75f2246Stnn 		    : ssdfb_spi_xfer_rect_4wire_sh1106;
176d75f2246Stnn 		sc->sc_padding_cmd = SSDFB_CMD_NOP;
177d75f2246Stnn 		sc->sc_late_dc_deassert = true;
178d75f2246Stnn 		break;
179d75f2246Stnn 	case SSDFB_PRODUCT_SSD1306_GENERIC:
180d75f2246Stnn 		sc->sc.sc_transfer_rect = sc->sc_3wiremode
181d75f2246Stnn 		    ? NULL
182d75f2246Stnn 		    : ssdfb_spi_xfer_rect_4wire_ssd1306;
183d75f2246Stnn 		sc->sc_padding_cmd = SSDFB_CMD_NOP;
184d75f2246Stnn 		sc->sc_late_dc_deassert = true;
185d75f2246Stnn 		break;
186281830f2Stnn 	case SSDFB_PRODUCT_SSD1322_GENERIC:
1875db07139Stnn 		sc->sc.sc_transfer_rect = sc->sc_3wiremode
1885db07139Stnn 		    ? ssdfb_spi_xfer_rect_3wire_ssd1322
1895db07139Stnn 		    : ssdfb_spi_xfer_rect_4wire_ssd1322;
190d15f52d2Stnn 		sc->sc_padding_cmd = SSD1322_CMD_WRITE_RAM;
191281830f2Stnn 		break;
1925db07139Stnn 	case SSDFB_PRODUCT_SSD1353_GENERIC:
1935db07139Stnn 	case SSDFB_PRODUCT_DEP_160128A_RGB:
1945db07139Stnn 		sc->sc.sc_transfer_rect = sc->sc_3wiremode
1955db07139Stnn 		    ? NULL /* not supported here */
1965db07139Stnn 		    : ssdfb_spi_xfer_rect_4wire_ssd1353;
1975db07139Stnn 		break;
198281830f2Stnn 	}
1995db07139Stnn 
2005db07139Stnn 	if (!sc->sc.sc_transfer_rect) {
2015db07139Stnn 		aprint_error(": sc_transfer_rect not implemented\n");
2025db07139Stnn 		return;
203281830f2Stnn 	}
204281830f2Stnn 
205281830f2Stnn 	ssdfb_attach(&sc->sc, flags);
206281830f2Stnn 
2075db07139Stnn 	aprint_normal_dev(self, "%d-wire SPI interface\n",
208281830f2Stnn 	    sc->sc_3wiremode == true ? 3 : 4);
209281830f2Stnn }
210281830f2Stnn 
211281830f2Stnn static int
ssdfb_spi_cmd_3wire(void * cookie,uint8_t * cmd,size_t len,bool usepoll)212281830f2Stnn ssdfb_spi_cmd_3wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll)
213281830f2Stnn {
214281830f2Stnn 	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
215281830f2Stnn 	uint8_t bitstream[16 * 9 / 8];
216281830f2Stnn 	struct bs_state s;
217281830f2Stnn 
218281830f2Stnn 	KASSERT(len > 0 && len <= 16);
219281830f2Stnn 	ssdfb_bitstream_init(&s, bitstream);
220281830f2Stnn 	ssdfb_bitstream_append_cmd(&s, *cmd);
221281830f2Stnn 	cmd++;
222281830f2Stnn 	len--;
223281830f2Stnn 	ssdfb_bitstream_append_data(&s, cmd, len);
224d15f52d2Stnn 	ssdfb_bitstream_final(&s, sc->sc_padding_cmd);
225281830f2Stnn 
226281830f2Stnn 	return spi_send(sc->sc_sh, s.cur - s.base, bitstream);
227281830f2Stnn }
228281830f2Stnn 
229281830f2Stnn static int
ssdfb_spi_xfer_rect_3wire_ssd1322(void * cookie,uint8_t fromcol,uint8_t tocol,uint8_t fromrow,uint8_t torow,uint8_t * p,size_t stride,bool usepoll)230281830f2Stnn ssdfb_spi_xfer_rect_3wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol,
231281830f2Stnn     uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll)
232281830f2Stnn {
233281830f2Stnn 	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
234281830f2Stnn 	uint8_t bitstream[128 * 9 / 8];
235281830f2Stnn 	struct bs_state s;
236281830f2Stnn 	size_t rlen = (tocol + 1 - fromcol) * 2;
237281830f2Stnn 	int error;
238281830f2Stnn 
239281830f2Stnn 	/*
240281830f2Stnn 	 * Unlike iic(4), there is no way to force spi(4) to use polling.
241281830f2Stnn 	 */
242c022c48eStnn 	if (usepoll && !cold)
243281830f2Stnn 		return 0;
244281830f2Stnn 
245281830f2Stnn 	ssdfb_bitstream_init(&s, bitstream);
246281830f2Stnn 	ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_ROW_ADDRESS);
247281830f2Stnn 	ssdfb_bitstream_append_data(&s, &fromrow, 1);
248281830f2Stnn 	ssdfb_bitstream_append_data(&s, &torow, 1);
249281830f2Stnn 	ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_SET_COLUMN_ADDRESS);
250281830f2Stnn 	ssdfb_bitstream_append_data(&s, &fromcol, 1);
251281830f2Stnn 	ssdfb_bitstream_append_data(&s, &tocol, 1);
252281830f2Stnn 	ssdfb_bitstream_append_cmd(&s, SSD1322_CMD_WRITE_RAM);
253d15f52d2Stnn 	ssdfb_bitstream_final(&s, sc->sc_padding_cmd);
254281830f2Stnn 	error = spi_send(sc->sc_sh, s.cur - s.base, bitstream);
255281830f2Stnn 	if (error)
256281830f2Stnn 		return error;
257281830f2Stnn 
258281830f2Stnn 	KASSERT(rlen <= 128);
259d75f2246Stnn 	while (fromrow <= torow) {
260281830f2Stnn 		ssdfb_bitstream_init(&s, bitstream);
261281830f2Stnn 		ssdfb_bitstream_append_data(&s, p, rlen);
262d15f52d2Stnn 		ssdfb_bitstream_final(&s, sc->sc_padding_cmd);
263281830f2Stnn 		error = spi_send(sc->sc_sh, s.cur - s.base, bitstream);
264281830f2Stnn 		if (error)
265281830f2Stnn 			return error;
266d75f2246Stnn 		fromrow++;
267281830f2Stnn 		p += stride;
268281830f2Stnn 	}
269281830f2Stnn 
270281830f2Stnn 	return 0;
271281830f2Stnn }
272281830f2Stnn 
273281830f2Stnn static void
ssdfb_bitstream_init(struct bs_state * s,uint8_t * dst)274281830f2Stnn ssdfb_bitstream_init(struct bs_state *s, uint8_t *dst)
275281830f2Stnn {
276281830f2Stnn 	s->base = s->cur = dst;
277281830f2Stnn 	s->mask = 0x80;
278281830f2Stnn }
279281830f2Stnn 
280281830f2Stnn static void
ssdfb_bitstream_append(struct bs_state * s,uint8_t b,uint8_t srcmask)281281830f2Stnn ssdfb_bitstream_append(struct bs_state *s, uint8_t b, uint8_t srcmask)
282281830f2Stnn {
283281830f2Stnn 	while(srcmask) {
284281830f2Stnn 		if (b & srcmask)
285281830f2Stnn 			*s->cur |= s->mask;
286281830f2Stnn 		else
287281830f2Stnn 			*s->cur &= ~s->mask;
288281830f2Stnn 		srcmask >>= 1;
289281830f2Stnn 		s->mask >>= 1;
290281830f2Stnn 		if (!s->mask) {
291281830f2Stnn 			s->mask = 0x80;
292281830f2Stnn 			s->cur++;
293281830f2Stnn 		}
294281830f2Stnn 	}
295281830f2Stnn }
296281830f2Stnn 
297281830f2Stnn static void
ssdfb_bitstream_append_cmd(struct bs_state * s,uint8_t cmd)298281830f2Stnn ssdfb_bitstream_append_cmd(struct bs_state *s, uint8_t cmd)
299281830f2Stnn {
300281830f2Stnn 	ssdfb_bitstream_append(s, 0, 1);
301281830f2Stnn 	ssdfb_bitstream_append(s, cmd, 0x80);
302281830f2Stnn }
303281830f2Stnn 
304281830f2Stnn static void
ssdfb_bitstream_append_data(struct bs_state * s,uint8_t * data,size_t len)305281830f2Stnn ssdfb_bitstream_append_data(struct bs_state *s, uint8_t *data, size_t len)
306281830f2Stnn {
307281830f2Stnn 	while(len--) {
308281830f2Stnn 		ssdfb_bitstream_append(s, 1, 1);
309281830f2Stnn 		ssdfb_bitstream_append(s, *data++, 0x80);
310281830f2Stnn 	}
311281830f2Stnn }
312281830f2Stnn 
313281830f2Stnn static void
ssdfb_bitstream_final(struct bs_state * s,uint8_t padding_cmd)314d15f52d2Stnn ssdfb_bitstream_final(struct bs_state *s, uint8_t padding_cmd)
315281830f2Stnn {
316281830f2Stnn 	while (s->mask != 0x80) {
317281830f2Stnn 		ssdfb_bitstream_append_cmd(s, padding_cmd);
318281830f2Stnn 	}
319281830f2Stnn }
320281830f2Stnn 
321281830f2Stnn static void
ssdfb_spi_4wire_set_dc(struct ssdfb_spi_softc * sc,int value)322281830f2Stnn ssdfb_spi_4wire_set_dc(struct ssdfb_spi_softc *sc, int value)
323281830f2Stnn {
324eafe2c2fStnn #ifdef FDT
325eafe2c2fStnn 	fdtbus_gpio_write_raw(sc->sc_gpio_dc, value);
326eafe2c2fStnn #else
327281830f2Stnn 	panic("ssdfb_spi_4wire_set_dc");
328eafe2c2fStnn #endif
329281830f2Stnn }
330281830f2Stnn 
331281830f2Stnn static int
ssdfb_spi_cmd_4wire(void * cookie,uint8_t * cmd,size_t len,bool usepoll)332281830f2Stnn ssdfb_spi_cmd_4wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll)
333281830f2Stnn {
334281830f2Stnn 	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
335281830f2Stnn 	int error;
336281830f2Stnn 
337281830f2Stnn 	ssdfb_spi_4wire_set_dc(sc, 0);
338281830f2Stnn 	error = spi_send(sc->sc_sh, 1, cmd);
339281830f2Stnn 	if (error)
340281830f2Stnn 		return error;
341281830f2Stnn 	if (len > 1) {
342d15f52d2Stnn 		if (!sc->sc_late_dc_deassert)
343281830f2Stnn 			ssdfb_spi_4wire_set_dc(sc, 1);
344281830f2Stnn 		len--;
345281830f2Stnn 		cmd++;
346281830f2Stnn 		error = spi_send(sc->sc_sh, len, cmd);
347281830f2Stnn 		if (error)
348281830f2Stnn 			return error;
349281830f2Stnn 	}
350281830f2Stnn 
351281830f2Stnn 	return 0;
352281830f2Stnn }
353281830f2Stnn 
354281830f2Stnn static int
ssdfb_spi_xfer_rect_4wire_sh1106(void * cookie,uint8_t fromcol,uint8_t tocol,uint8_t frompage,uint8_t topage,uint8_t * p,size_t stride,bool usepoll)355d75f2246Stnn ssdfb_spi_xfer_rect_4wire_sh1106(void *cookie, uint8_t fromcol, uint8_t tocol,
356d75f2246Stnn     uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll)
357d75f2246Stnn {
358d75f2246Stnn 	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
359d75f2246Stnn 	size_t rlen = tocol + 1 - fromcol;
360d75f2246Stnn 	int error;
361d75f2246Stnn 	uint8_t cmd[] = {
362d75f2246Stnn 		SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage,
363d75f2246Stnn 		SSDFB_CMD_SET_HIGHER_COLUMN_START_ADDRESS_BASE + (fromcol >> 4),
364d75f2246Stnn 		SSDFB_CMD_SET_LOWER_COLUMN_START_ADDRESS_BASE + (fromcol & 0xf)
365d75f2246Stnn 	};
366d75f2246Stnn 
367d75f2246Stnn 	if (usepoll && !cold)
368d75f2246Stnn 		return 0;
369d75f2246Stnn 
370d75f2246Stnn 	while (frompage <= topage) {
371d75f2246Stnn 		cmd[0] = SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage;
372d75f2246Stnn 		ssdfb_spi_4wire_set_dc(sc, 0);
373d75f2246Stnn 		error = spi_send(sc->sc_sh, sizeof(cmd), cmd);
374d75f2246Stnn 		if (error)
375d75f2246Stnn 			return error;
376d75f2246Stnn 		ssdfb_spi_4wire_set_dc(sc, 1);
377d75f2246Stnn 		error = spi_send(sc->sc_sh, rlen, p);
378d75f2246Stnn 		if (error)
379d75f2246Stnn 			return error;
380d75f2246Stnn 		frompage++;
381d75f2246Stnn 		p += stride;
382d75f2246Stnn 	}
383d75f2246Stnn 
384d75f2246Stnn 	return 0;
385d75f2246Stnn }
386d75f2246Stnn 
387d75f2246Stnn static int
ssdfb_spi_xfer_rect_4wire_ssd1306(void * cookie,uint8_t fromcol,uint8_t tocol,uint8_t frompage,uint8_t topage,uint8_t * p,size_t stride,bool usepoll)388d75f2246Stnn ssdfb_spi_xfer_rect_4wire_ssd1306(void *cookie, uint8_t fromcol, uint8_t tocol,
389d75f2246Stnn     uint8_t frompage, uint8_t topage, uint8_t *p, size_t stride, bool usepoll)
390d75f2246Stnn {
391d75f2246Stnn 	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
392d75f2246Stnn 	size_t rlen = tocol + 1 - fromcol;
393d75f2246Stnn 	int error;
394d75f2246Stnn 	uint8_t cmd[] = {
395d75f2246Stnn 		SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE,
396d75f2246Stnn 		SSD1306_MEMORY_ADDRESSING_MODE_HORIZONTAL,
397d75f2246Stnn 		SSD1306_CMD_SET_COLUMN_ADDRESS,
398d75f2246Stnn 		fromcol,
399d75f2246Stnn 		tocol,
400d75f2246Stnn 		SSD1306_CMD_SET_PAGE_ADDRESS,
401d75f2246Stnn 		frompage,
402d75f2246Stnn 		topage
403d75f2246Stnn 	};
404d75f2246Stnn 
405d75f2246Stnn 	if (usepoll && !cold)
406d75f2246Stnn 		return 0;
407d75f2246Stnn 
408d75f2246Stnn 	ssdfb_spi_4wire_set_dc(sc, 0);
409d75f2246Stnn 	error = spi_send(sc->sc_sh, sizeof(cmd), cmd);
410d75f2246Stnn 	if (error)
411d75f2246Stnn 		return error;
412d75f2246Stnn 	ssdfb_spi_4wire_set_dc(sc, 1);
413d75f2246Stnn 
414d75f2246Stnn 	while (frompage <= topage) {
415d75f2246Stnn 		error = spi_send(sc->sc_sh, rlen, p);
416d75f2246Stnn 		if (error)
417d75f2246Stnn 			return error;
418d75f2246Stnn 		frompage++;
419d75f2246Stnn 		p += stride;
420d75f2246Stnn 	}
421d75f2246Stnn 
422d75f2246Stnn 	return 0;
423d75f2246Stnn }
424d75f2246Stnn 
425d75f2246Stnn static int
ssdfb_spi_xfer_rect_4wire_ssd1322(void * cookie,uint8_t fromcol,uint8_t tocol,uint8_t fromrow,uint8_t torow,uint8_t * p,size_t stride,bool usepoll)426281830f2Stnn ssdfb_spi_xfer_rect_4wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol,
427281830f2Stnn     uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll)
428281830f2Stnn {
429281830f2Stnn 	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
430281830f2Stnn 	size_t rlen = (tocol + 1 - fromcol) * 2;
431281830f2Stnn 	int error;
432281830f2Stnn 	uint8_t cmd;
433281830f2Stnn 	uint8_t data[2];
434281830f2Stnn 
435c022c48eStnn 	if (usepoll && !cold)
436281830f2Stnn 		return 0;
437281830f2Stnn 
438281830f2Stnn 	ssdfb_spi_4wire_set_dc(sc, 0);
439281830f2Stnn 	cmd = SSD1322_CMD_SET_ROW_ADDRESS;
440281830f2Stnn 	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
441281830f2Stnn 	if (error)
442281830f2Stnn 		return error;
443281830f2Stnn 	ssdfb_spi_4wire_set_dc(sc, 1);
444281830f2Stnn 	data[0] = fromrow;
445281830f2Stnn 	data[1] = torow;
446281830f2Stnn 	error = spi_send(sc->sc_sh, sizeof(data), data);
447281830f2Stnn 	if (error)
448281830f2Stnn 		return error;
449281830f2Stnn 
450281830f2Stnn 	ssdfb_spi_4wire_set_dc(sc, 0);
451281830f2Stnn 	cmd = SSD1322_CMD_SET_COLUMN_ADDRESS;
452281830f2Stnn 	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
453281830f2Stnn 	if (error)
454281830f2Stnn 		return error;
455281830f2Stnn 	ssdfb_spi_4wire_set_dc(sc, 1);
456281830f2Stnn 	data[0] = fromcol;
457281830f2Stnn 	data[1] = tocol;
458281830f2Stnn 	error = spi_send(sc->sc_sh, sizeof(data), data);
459281830f2Stnn 	if (error)
460281830f2Stnn 		return error;
461281830f2Stnn 
462281830f2Stnn 	ssdfb_spi_4wire_set_dc(sc, 0);
463281830f2Stnn 	cmd = SSD1322_CMD_WRITE_RAM;
464281830f2Stnn 	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
465281830f2Stnn 	if (error)
466281830f2Stnn 		return error;
467281830f2Stnn 
468281830f2Stnn 	ssdfb_spi_4wire_set_dc(sc, 1);
469d75f2246Stnn 	while (fromrow <= torow) {
470281830f2Stnn 		error = spi_send(sc->sc_sh, rlen, p);
471281830f2Stnn 		if (error)
472281830f2Stnn 			return error;
473d75f2246Stnn 		fromrow++;
474281830f2Stnn 		p += stride;
475281830f2Stnn 	}
476281830f2Stnn 
477281830f2Stnn 	return 0;
478281830f2Stnn }
4795db07139Stnn 
4805db07139Stnn static int
ssdfb_spi_xfer_rect_4wire_ssd1353(void * cookie,uint8_t fromcol,uint8_t tocol,uint8_t fromrow,uint8_t torow,uint8_t * p,size_t stride,bool usepoll)4815db07139Stnn ssdfb_spi_xfer_rect_4wire_ssd1353(void *cookie, uint8_t fromcol, uint8_t tocol,
4825db07139Stnn     uint8_t fromrow, uint8_t torow, uint8_t *p, size_t stride, bool usepoll)
4835db07139Stnn {
4845db07139Stnn 	struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
4855db07139Stnn 	size_t rlen = (tocol + 1 - fromcol) * 3;
4865db07139Stnn 	uint8_t bitstream[160 * 3];
4875db07139Stnn 	uint8_t *dstp, *srcp, *endp;
4885db07139Stnn 	int error;
4895db07139Stnn 	uint8_t cmd;
4905db07139Stnn 	uint8_t data[2];
4915db07139Stnn 
4925db07139Stnn 	if (usepoll && !cold)
4935db07139Stnn 		return 0;
4945db07139Stnn 
4955db07139Stnn 	ssdfb_spi_4wire_set_dc(sc, 0);
4968a6d0e07Stnn 	cmd = SSD1353_CMD_SET_ROW_ADDRESS;
4975db07139Stnn 	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
4985db07139Stnn 	if (error)
4995db07139Stnn 		return error;
5005db07139Stnn 	ssdfb_spi_4wire_set_dc(sc, 1);
5015db07139Stnn 	data[0] = fromrow;
5025db07139Stnn 	data[1] = torow;
5035db07139Stnn 	if (sc->sc.sc_upsidedown) {
5045db07139Stnn 		/* fix picture outside frame on 160x128 panel */
5055db07139Stnn 		data[0] += 132 - sc->sc.sc_p->p_height;
5065db07139Stnn 		data[1] += 132 - sc->sc.sc_p->p_height;
5075db07139Stnn 	}
5085db07139Stnn 	error = spi_send(sc->sc_sh, sizeof(data), data);
5095db07139Stnn 	if (error)
5105db07139Stnn 		return error;
5115db07139Stnn 
5125db07139Stnn 	ssdfb_spi_4wire_set_dc(sc, 0);
5138a6d0e07Stnn 	cmd = SSD1353_CMD_SET_COLUMN_ADDRESS;
5145db07139Stnn 	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
5155db07139Stnn 	if (error)
5165db07139Stnn 		return error;
5175db07139Stnn 	ssdfb_spi_4wire_set_dc(sc, 1);
5185db07139Stnn 	data[0] = fromcol;
5195db07139Stnn 	data[1] = tocol;
5205db07139Stnn 	error = spi_send(sc->sc_sh, sizeof(data), data);
5215db07139Stnn 	if (error)
5225db07139Stnn 		return error;
5235db07139Stnn 
5245db07139Stnn 	ssdfb_spi_4wire_set_dc(sc, 0);
5258a6d0e07Stnn 	cmd = SSD1353_CMD_WRITE_RAM;
5265db07139Stnn 	error = spi_send(sc->sc_sh, sizeof(cmd), &cmd);
5275db07139Stnn 	if (error)
5285db07139Stnn 		return error;
5295db07139Stnn 
5305db07139Stnn 	ssdfb_spi_4wire_set_dc(sc, 1);
5315db07139Stnn 	KASSERT(rlen <= sizeof(bitstream));
532d75f2246Stnn 	while (fromrow <= torow) {
5335db07139Stnn 		/* downconvert each row from 32bpp rgba to 18bpp panel format */
5345db07139Stnn 		dstp = bitstream;
5355db07139Stnn 		endp = dstp + rlen;
5365db07139Stnn 		srcp = p;
5375db07139Stnn 		while (dstp < endp) {
5385db07139Stnn 			*dstp++ = (*srcp++) >> 2;
5395db07139Stnn 			*dstp++ = (*srcp++) >> 2;
5405db07139Stnn 			*dstp++ = (*srcp++) >> 2;
5415db07139Stnn 			srcp++;
5425db07139Stnn 		}
5435db07139Stnn 		error = spi_send(sc->sc_sh, rlen, bitstream);
5445db07139Stnn 		if (error)
5455db07139Stnn 			return error;
546d75f2246Stnn 		fromrow++;
5475db07139Stnn 		p += stride;
5485db07139Stnn 	}
5495db07139Stnn 
5505db07139Stnn 	return 0;
5515db07139Stnn }
552