ssdfb: add support for SSD1306 and SH1106 at spi attachment

Only support the 4-wire mode for now. Support for 3-wire mode is
theoretically possible but most (all?) modules in circulation have the
BS0 / IM0 3-wire config pin internally grounded within the flat-flex
assembly so it is not easy to enable & test.
This commit is contained in:
tnn 2021-08-19 17:50:18 +00:00
parent 99a5114ced
commit d75f2246b3
1 changed files with 98 additions and 14 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ssdfb_spi.c,v 1.10 2021/08/19 11:04:21 tnn Exp $ */
/* $NetBSD: ssdfb_spi.c,v 1.11 2021/08/19 17:50:18 tnn Exp $ */
/*
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ssdfb_spi.c,v 1.10 2021/08/19 11:04:21 tnn Exp $");
__KERNEL_RCSID(0, "$NetBSD: ssdfb_spi.c,v 1.11 2021/08/19 17:50:18 tnn Exp $");
#include <sys/param.h>
#include <sys/device.h>
@ -70,6 +70,10 @@ static int ssdfb_spi_xfer_rect_3wire_ssd1322(void *, uint8_t, uint8_t,
uint8_t, uint8_t, uint8_t *, size_t, bool);
static int ssdfb_spi_cmd_4wire(void *, uint8_t *, size_t, bool);
static int ssdfb_spi_xfer_rect_4wire_sh1106(void *, uint8_t, uint8_t,
uint8_t, uint8_t, uint8_t *, size_t, bool);
static int ssdfb_spi_xfer_rect_4wire_ssd1306(void *, uint8_t, uint8_t,
uint8_t, uint8_t, uint8_t *, size_t, bool);
static int ssdfb_spi_xfer_rect_4wire_ssd1322(void *, uint8_t, uint8_t,
uint8_t, uint8_t, uint8_t *, size_t, bool);
static int ssdfb_spi_xfer_rect_4wire_ssd1353(void *, uint8_t, uint8_t,
@ -87,6 +91,7 @@ CFATTACH_DECL_NEW(ssdfb_spi, sizeof(struct ssdfb_spi_softc),
static const struct device_compatible_entry compat_data[] = {
{ .compat = "solomon,ssd1306", .value = SSDFB_PRODUCT_SSD1306_GENERIC },
{ .compat = "sino,sh1106", .value = SSDFB_PRODUCT_SH1106_GENERIC },
{ .compat = "solomon,ssd1322", .value = SSDFB_PRODUCT_SSD1322_GENERIC },
{ .compat = "solomon,ssd1353", .value = SSDFB_PRODUCT_SSD1353_GENERIC },
{ .compat = "dep160128a", .value = SSDFB_PRODUCT_DEP_160128A_RGB },
@ -165,6 +170,20 @@ ssdfb_spi_attach(device_t parent, device_t self, void *aux)
: ssdfb_spi_cmd_4wire;
switch (flags & SSDFB_ATTACH_FLAG_PRODUCT_MASK) {
case SSDFB_PRODUCT_SH1106_GENERIC:
sc->sc.sc_transfer_rect = sc->sc_3wiremode
? NULL
: ssdfb_spi_xfer_rect_4wire_sh1106;
sc->sc_padding_cmd = SSDFB_CMD_NOP;
sc->sc_late_dc_deassert = true;
break;
case SSDFB_PRODUCT_SSD1306_GENERIC:
sc->sc.sc_transfer_rect = sc->sc_3wiremode
? NULL
: ssdfb_spi_xfer_rect_4wire_ssd1306;
sc->sc_padding_cmd = SSDFB_CMD_NOP;
sc->sc_late_dc_deassert = true;
break;
case SSDFB_PRODUCT_SSD1322_GENERIC:
sc->sc.sc_transfer_rect = sc->sc_3wiremode
? ssdfb_spi_xfer_rect_3wire_ssd1322
@ -215,7 +234,6 @@ ssdfb_spi_xfer_rect_3wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol,
struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
uint8_t bitstream[128 * 9 / 8];
struct bs_state s;
uint8_t row;
size_t rlen = (tocol + 1 - fromcol) * 2;
int error;
@ -239,13 +257,14 @@ ssdfb_spi_xfer_rect_3wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol,
return error;
KASSERT(rlen <= 128);
for (row = fromrow; row <= torow; row++) {
while (fromrow <= torow) {
ssdfb_bitstream_init(&s, bitstream);
ssdfb_bitstream_append_data(&s, p, rlen);
ssdfb_bitstream_final(&s, sc->sc_padding_cmd);
error = spi_send(sc->sc_sh, s.cur - s.base, bitstream);
if (error)
return error;
fromrow++;
p += stride;
}
@ -333,20 +352,87 @@ ssdfb_spi_cmd_4wire(void *cookie, uint8_t *cmd, size_t len, bool usepoll)
return 0;
}
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)
{
struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
size_t rlen = tocol + 1 - fromcol;
int error;
uint8_t cmd[] = {
SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage,
SSDFB_CMD_SET_HIGHER_COLUMN_START_ADDRESS_BASE + (fromcol >> 4),
SSDFB_CMD_SET_LOWER_COLUMN_START_ADDRESS_BASE + (fromcol & 0xf)
};
if (usepoll && !cold)
return 0;
while (frompage <= topage) {
cmd[0] = SSDFB_CMD_SET_PAGE_START_ADDRESS_BASE + frompage;
ssdfb_spi_4wire_set_dc(sc, 0);
error = spi_send(sc->sc_sh, sizeof(cmd), cmd);
if (error)
return error;
ssdfb_spi_4wire_set_dc(sc, 1);
error = spi_send(sc->sc_sh, rlen, p);
if (error)
return error;
frompage++;
p += stride;
}
return 0;
}
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)
{
struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
size_t rlen = tocol + 1 - fromcol;
int error;
uint8_t cmd[] = {
SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE,
SSD1306_MEMORY_ADDRESSING_MODE_HORIZONTAL,
SSD1306_CMD_SET_COLUMN_ADDRESS,
fromcol,
tocol,
SSD1306_CMD_SET_PAGE_ADDRESS,
frompage,
topage
};
if (usepoll && !cold)
return 0;
ssdfb_spi_4wire_set_dc(sc, 0);
error = spi_send(sc->sc_sh, sizeof(cmd), cmd);
if (error)
return error;
ssdfb_spi_4wire_set_dc(sc, 1);
while (frompage <= topage) {
error = spi_send(sc->sc_sh, rlen, p);
if (error)
return error;
frompage++;
p += stride;
}
return 0;
}
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)
{
struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
uint8_t row;
size_t rlen = (tocol + 1 - fromcol) * 2;
int error;
uint8_t cmd;
uint8_t data[2];
/*
* Unlike iic(4), there is no way to force spi(4) to use polling.
*/
if (usepoll && !cold)
return 0;
@ -381,10 +467,11 @@ ssdfb_spi_xfer_rect_4wire_ssd1322(void *cookie, uint8_t fromcol, uint8_t tocol,
return error;
ssdfb_spi_4wire_set_dc(sc, 1);
for (row = fromrow; row <= torow; row++) {
while (fromrow <= torow) {
error = spi_send(sc->sc_sh, rlen, p);
if (error)
return error;
fromrow++;
p += stride;
}
@ -396,7 +483,6 @@ 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)
{
struct ssdfb_spi_softc *sc = (struct ssdfb_spi_softc *)cookie;
uint8_t row;
size_t rlen = (tocol + 1 - fromcol) * 3;
uint8_t bitstream[160 * 3];
uint8_t *dstp, *srcp, *endp;
@ -404,9 +490,6 @@ ssdfb_spi_xfer_rect_4wire_ssd1353(void *cookie, uint8_t fromcol, uint8_t tocol,
uint8_t cmd;
uint8_t data[2];
/*
* Unlike iic(4), there is no way to force spi(4) to use polling.
*/
if (usepoll && !cold)
return 0;
@ -447,7 +530,7 @@ ssdfb_spi_xfer_rect_4wire_ssd1353(void *cookie, uint8_t fromcol, uint8_t tocol,
ssdfb_spi_4wire_set_dc(sc, 1);
KASSERT(rlen <= sizeof(bitstream));
for (row = fromrow; row <= torow; row++) {
while (fromrow <= torow) {
/* downconvert each row from 32bpp rgba to 18bpp panel format */
dstp = bitstream;
endp = dstp + rlen;
@ -461,6 +544,7 @@ ssdfb_spi_xfer_rect_4wire_ssd1353(void *cookie, uint8_t fromcol, uint8_t tocol,
error = spi_send(sc->sc_sh, rlen, bitstream);
if (error)
return error;
fromrow++;
p += stride;
}