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