Skip to content

Commit 26de136

Browse files
committed
drivers: ssd16xx: Use device-specific compatibles
The SSD16xx driver currently provides basic support for most chips in the Solomon Systech SSD16xx range of e-paper drivers. We currently use the SSD1608, SSD1673, SSD1675A, and SSD1681 in various boards supported by Zephyr. The main user-facing difference between the various SSD16xx chips is the resolution they support (sources & gates), but there are other differences as well. For example: * 8 or 16 bits used to represent x coordinates * 8 or 16 bits used to represent y coordinates * Differences in refresh configuration (SSD16XX_CMD_UPDATE_CTRL2) * Differences in LUT sizes The driver currently assumes that the user specifies the number of bits used to describe coordinates. However, as we add support for more chips, more of the differences will become apparent and need workaround. Comparing data sheets from different chips in the SSD16xx range suggests that there are (at least) two different generations present. These differ in the size of the LUTs they expect and the way they handle partial refresh. This impacts register layout where SSD16XX_CMD_UPDATE_CTRL2 uses bit 3 selects "mode 2" whereas older devices uses this for a mode referred to as "initial". In order to add support for partial refresh in newer devices, we need to be able to distinguish between the different generations of the chip. It might be possible to add a DT property to indicate the revision, but that seems like a bit of an anti-pattern and it would be hard for users to specify the correct chip generation. This change introduces chip-specific compatible strings instead of the generic SSD16xx. There is unfortunately clear pattern that can be used to distinguish different generations, so the full chip name must be specified. A benefit of this is that we don't need to specify the width of the fields describing coordinates in device trees. Signed-off-by: Andreas Sandberg <[email protected]>
1 parent 8d6a18f commit 26de136

File tree

14 files changed

+151
-91
lines changed

14 files changed

+151
-91
lines changed

boards/arm/reel_board/reel_board.dts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,11 @@
5757
pinctrl-1 = <&spi1_sleep>;
5858
pinctrl-names = "default", "sleep";
5959
ssd16xx: ssd16xxfb@0 {
60-
compatible = "gooddisplay,gdeh0213b1", "solomon,ssd1673", "solomon,ssd16xxfb";
61-
label = "SSD16XX";
60+
compatible = "gooddisplay,gdeh0213b1", "solomon,ssd1673";
6261
spi-max-frequency = <4000000>;
6362
reg = <0>;
6463
width = <250>;
6564
height = <122>;
66-
pp-width-bits = <8>;
67-
pp-height-bits = <8>;
6865
reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;
6966
dc-gpios = <&gpio0 16 GPIO_ACTIVE_LOW>;
7067
busy-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;

boards/arm/reel_board/reel_board_v2.dts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,11 @@
4040
pinctrl-1 = <&spi1_sleep>;
4141
pinctrl-names = "default", "sleep";
4242
ssd16xx: ssd16xxfb@0 {
43-
compatible = "gooddisplay,gdeh0213b72", "solomon,ssd1675a", "solomon,ssd16xxfb";
44-
label = "SSD16XX";
43+
compatible = "gooddisplay,gdeh0213b72", "solomon,ssd1675a";
4544
spi-max-frequency = <4000000>;
4645
reg = <0>;
4746
width = <250>;
4847
height = <122>;
49-
pp-width-bits = <8>;
50-
pp-height-bits = <16>;
5148
reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>;
5249
dc-gpios = <&gpio0 16 GPIO_ACTIVE_LOW>;
5350
busy-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;

boards/shields/waveshare_epaper/waveshare_epaper_gdeh0154a07.overlay

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,11 @@
1414

1515
&arduino_spi {
1616
ssd16xx: ssd16xxfb@0 {
17-
compatible = "gooddisplay,gdeh0154a07", "solomon,ssd1681", "solomon,ssd16xxfb";
18-
label = "SSD16XX";
17+
compatible = "gooddisplay,gdeh0154a07", "solomon,ssd1681";
1918
spi-max-frequency = <4000000>;
2019
reg = <0>;
2120
width = <200>;
2221
height = <200>;
23-
pp-width-bits = <8>;
24-
pp-height-bits = <16>;
2522
dc-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */
2623
reset-gpios = <&arduino_header 14 GPIO_ACTIVE_LOW>; /* D8 */
2724
busy-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */

boards/shields/waveshare_epaper/waveshare_epaper_gdeh0213b1.overlay

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,11 @@
1414

1515
&arduino_spi {
1616
ssd16xx: ssd16xxfb@0 {
17-
compatible = "gooddisplay,gdeh0213b1", "solomon,ssd1673", "solomon,ssd16xxfb";
18-
label = "SSD16XX";
17+
compatible = "gooddisplay,gdeh0213b1", "solomon,ssd1673";
1918
spi-max-frequency = <4000000>;
2019
reg = <0>;
2120
width = <250>;
2221
height = <120>;
23-
pp-width-bits = <8>;
24-
pp-height-bits = <8>;
2522
dc-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */
2623
reset-gpios = <&arduino_header 14 GPIO_ACTIVE_LOW>; /* D8 */
2724
busy-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */

boards/shields/waveshare_epaper/waveshare_epaper_gdeh0213b72.overlay

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,11 @@
1414

1515
&arduino_spi {
1616
ssd16xx: ssd16xxfb@0 {
17-
compatible = "gooddisplay,gdeh0213b72", "solomon,ssd1675a", "solomon,ssd16xxfb";
18-
label = "SSD16XX";
17+
compatible = "gooddisplay,gdeh0213b72", "solomon,ssd1675a";
1918
spi-max-frequency = <4000000>;
2019
reg = <0>;
2120
width = <250>;
2221
height = <120>;
23-
pp-width-bits = <8>;
24-
pp-height-bits = <16>;
2522
dc-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */
2623
reset-gpios = <&arduino_header 14 GPIO_ACTIVE_LOW>; /* D8 */
2724
busy-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */

boards/shields/waveshare_epaper/waveshare_epaper_gdeh029a1.overlay

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,11 @@
1414

1515
&arduino_spi {
1616
ssd16xx: ssd16xxfb@0 {
17-
compatible = "gooddisplay,gdeh029a1", "solomon,ssd1608", "solomon,ssd16xxfb";
18-
label = "SSD16XX";
17+
compatible = "gooddisplay,gdeh029a1", "solomon,ssd1608";
1918
spi-max-frequency = <4000000>;
2019
reg = <0>;
2120
width = <296>;
2221
height = <128>;
23-
pp-width-bits = <16>;
24-
pp-height-bits = <16>;
2522
dc-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */
2623
reset-gpios = <&arduino_header 14 GPIO_ACTIVE_LOW>; /* D8 */
2724
busy-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */

drivers/display/ssd16xx.c

Lines changed: 110 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
* SPDX-License-Identifier: Apache-2.0
66
*/
77

8-
#define DT_DRV_COMPAT solomon_ssd16xxfb
9-
108
#define LOG_LEVEL CONFIG_DISPLAY_LOG_LEVEL
119
#include <zephyr/logging/log.h>
1210
LOG_MODULE_REGISTER(ssd16xx);
@@ -23,7 +21,7 @@ LOG_MODULE_REGISTER(ssd16xx);
2321
#include "ssd16xx_regs.h"
2422

2523
/**
26-
* SSD1673, SSD1608, SSD1681, ILI3897 compatible EPD controller driver.
24+
* SSD16xx compatible EPD controller driver.
2725
*/
2826

2927
#define EPD_PANEL_NUMOF_ROWS_PER_PAGE 8
@@ -33,6 +31,17 @@ LOG_MODULE_REGISTER(ssd16xx);
3331
#define SSD16XX_DEFAULT_TR_VALUE 25U
3432
#define SSD16XX_TR_SCALE_FACTOR 256U
3533

34+
struct ssd16xx_quirks {
35+
/* Gates */
36+
uint16_t max_width;
37+
/* Sources */
38+
uint16_t max_height;
39+
/* Width (bits) of integer type representing an x coordinate */
40+
uint8_t pp_width_bits;
41+
/* Width (bits) of integer type representing a y coordinate */
42+
uint8_t pp_height_bits;
43+
};
44+
3645
struct ssd16xx_data {
3746
bool read_supported;
3847
uint8_t scan_mode;
@@ -62,6 +71,8 @@ struct ssd16xx_config {
6271
struct gpio_dt_spec busy_gpio;
6372
struct gpio_dt_spec reset_gpio;
6473

74+
const struct ssd16xx_quirks *quirks;
75+
6576
struct ssd16xx_dt_array softstart;
6677

6778
struct ssd16xx_profile profile_initial;
@@ -71,8 +82,6 @@ struct ssd16xx_config {
7182
uint16_t height;
7283
uint16_t width;
7384
uint8_t tssv;
74-
uint8_t pp_width_bits;
75-
uint8_t pp_height_bits;
7685

7786
uint8_t dummy_line;
7887
bool override_dummy_line;
@@ -189,17 +198,18 @@ static inline size_t push_x_param(const struct device *dev,
189198
{
190199
const struct ssd16xx_config *config = dev->config;
191200

192-
if (config->pp_width_bits == 8) {
201+
if (config->quirks->pp_width_bits == 8) {
193202
data[0] = (uint8_t)x;
194203
return 1;
195204
}
196205

197-
if (config->pp_width_bits == 16) {
206+
if (config->quirks->pp_width_bits == 16) {
198207
sys_put_le16(sys_cpu_to_le16(x), data);
199208
return 2;
200209
}
201210

202-
LOG_ERR("Unsupported pp_width_bits %u", config->pp_width_bits);
211+
LOG_ERR("Unsupported pp_width_bits %u",
212+
config->quirks->pp_width_bits);
203213
return 0;
204214
}
205215

@@ -208,17 +218,18 @@ static inline size_t push_y_param(const struct device *dev,
208218
{
209219
const struct ssd16xx_config *config = dev->config;
210220

211-
if (config->pp_height_bits == 8) {
221+
if (config->quirks->pp_height_bits == 8) {
212222
data[0] = (uint8_t)y;
213223
return 1;
214224
}
215225

216-
if (config->pp_height_bits == 16) {
226+
if (config->quirks->pp_height_bits == 16) {
217227
sys_put_le16(sys_cpu_to_le16(y), data);
218228
return 2;
219229
}
220230

221-
LOG_ERR("Unsupported pp_height_bitsa %u", config->pp_height_bits);
231+
LOG_ERR("Unsupported pp_height_bitsa %u",
232+
config->quirks->pp_height_bits);
222233
return 0;
223234
}
224235

@@ -885,6 +896,12 @@ static int ssd16xx_init(const struct device *dev)
885896
return err;
886897
}
887898

899+
if (config->width > config->quirks->max_width ||
900+
config->height > config->quirks->max_height) {
901+
LOG_ERR("Display size out of range.");
902+
return -EINVAL;
903+
}
904+
888905
return ssd16xx_controller_init(dev);
889906
}
890907

@@ -901,6 +918,42 @@ static struct display_driver_api ssd16xx_driver_api = {
901918
.set_orientation = ssd16xx_set_orientation,
902919
};
903920

921+
#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1608)
922+
static struct ssd16xx_quirks quirks_solomon_ssd1608 = {
923+
.max_width = 320,
924+
.max_height = 240,
925+
.pp_width_bits = 16,
926+
.pp_height_bits = 16,
927+
};
928+
#endif
929+
930+
#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1673)
931+
static struct ssd16xx_quirks quirks_solomon_ssd1673 = {
932+
.max_width = 250,
933+
.max_height = 150,
934+
.pp_width_bits = 8,
935+
.pp_height_bits = 8,
936+
};
937+
#endif
938+
939+
#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1675a)
940+
static struct ssd16xx_quirks quirks_solomon_ssd1675a = {
941+
.max_width = 296,
942+
.max_height = 160,
943+
.pp_width_bits = 8,
944+
.pp_height_bits = 16,
945+
};
946+
#endif
947+
948+
#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1681)
949+
static struct ssd16xx_quirks quirks_solomon_ssd1681 = {
950+
.max_width = 200,
951+
.max_height = 200,
952+
.pp_width_bits = 8,
953+
.pp_height_bits = 16,
954+
};
955+
#endif
956+
904957
#define LUT_DEFAULT_ASSIGN(n) \
905958
.lut_default = { \
906959
.data = lut_default_##n, \
@@ -913,70 +966,75 @@ static struct display_driver_api ssd16xx_driver_api = {
913966
.len = sizeof(softstart_##n), \
914967
},
915968

916-
#define SSD16XX_MAKE_INST_ARRAY_OPT(n, p) \
917-
static uint8_t data_ ## n ## _ ## p[] = DT_INST_PROP_OR(n, p, {})
969+
#define SSD16XX_MAKE_ARRAY_OPT(n, p) \
970+
static uint8_t data_ ## n ## _ ## p[] = \
971+
DT_PROP_OR(n, p, {})
918972

919973
#define SSD16XX_ASSIGN_ARRAY(n, p) \
920974
{ \
921975
.data = data_ ## n ## _ ## p, \
922-
.len = sizeof(data_ ## n ## _ ## p), \
976+
.len = sizeof(data_ ## n ## _ ## p), \
923977
}
924978

925979
#define SSD16XX_INITIAL_PROFILE_DEFINE(n) \
926-
SSD16XX_MAKE_INST_ARRAY_OPT(n, lut_initial); \
927-
SSD16XX_MAKE_INST_ARRAY_OPT(n, gdv); \
928-
SSD16XX_MAKE_INST_ARRAY_OPT(n, sdv)
980+
SSD16XX_MAKE_ARRAY_OPT(n, lut_initial); \
981+
SSD16XX_MAKE_ARRAY_OPT(n, gdv); \
982+
SSD16XX_MAKE_ARRAY_OPT(n, sdv)
929983

930984
#define SSD16XX_INITIAL_PROFILE(n) \
931985
{ \
932986
.lut = SSD16XX_ASSIGN_ARRAY(n, lut_initial), \
933987
.gdv = SSD16XX_ASSIGN_ARRAY(n, gdv), \
934988
.sdv = SSD16XX_ASSIGN_ARRAY(n, sdv), \
935-
.vcom = DT_INST_PROP_OR(n, vcom, 0), \
936-
.override_vcom = DT_INST_NODE_HAS_PROP(n, vcom), \
937-
.bwf = DT_INST_PROP_OR(n, border_waveform, 0), \
938-
.override_bwf = DT_INST_NODE_HAS_PROP(n, border_waveform), \
989+
.vcom = DT_PROP_OR(n, vcom, 0), \
990+
.override_vcom = DT_NODE_HAS_PROP(n, vcom), \
991+
.bwf = DT_PROP_OR(n, border_waveform, 0), \
992+
.override_bwf = DT_NODE_HAS_PROP(n, border_waveform), \
939993
}
940994

941-
#define SSD16XX_DEFINE(n) \
942-
SSD16XX_MAKE_INST_ARRAY_OPT(n, lut_default); \
943-
SSD16XX_MAKE_INST_ARRAY_OPT(n, softstart); \
995+
#define SSD16XX_DEFINE(n, quirks_ptr) \
996+
SSD16XX_MAKE_ARRAY_OPT(n, lut_default); \
997+
SSD16XX_MAKE_ARRAY_OPT(n, softstart); \
944998
SSD16XX_INITIAL_PROFILE_DEFINE(n); \
945999
\
946-
static const struct ssd16xx_config ssd16xx_cfg_##n = { \
947-
.bus = SPI_DT_SPEC_INST_GET(n, \
1000+
static const struct ssd16xx_config ssd16xx_cfg_ ## n = { \
1001+
.bus = SPI_DT_SPEC_GET(n, \
9481002
SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | \
9491003
SPI_HOLD_ON_CS | SPI_LOCK_ON, \
9501004
0), \
951-
.reset_gpio = GPIO_DT_SPEC_INST_GET(n, reset_gpios), \
952-
.dc_gpio = GPIO_DT_SPEC_INST_GET(n, dc_gpios), \
953-
.busy_gpio = GPIO_DT_SPEC_INST_GET(n, busy_gpios), \
954-
.height = DT_INST_PROP(n, height), \
955-
.width = DT_INST_PROP(n, width), \
956-
.orientation = DT_INST_PROP(n, orientation_flipped), \
957-
.pp_width_bits = DT_INST_PROP(n, pp_width_bits), \
958-
.pp_height_bits = DT_INST_PROP(n, pp_height_bits), \
959-
.tssv = DT_INST_PROP_OR(n, tssv, 0), \
1005+
.reset_gpio = GPIO_DT_SPEC_GET(n, reset_gpios), \
1006+
.dc_gpio = GPIO_DT_SPEC_GET(n, dc_gpios), \
1007+
.busy_gpio = GPIO_DT_SPEC_GET(n, busy_gpios), \
1008+
.quirks = quirks_ptr, \
1009+
.height = DT_PROP(n, height), \
1010+
.width = DT_PROP(n, width), \
1011+
.orientation = DT_PROP(n, orientation_flipped), \
1012+
.tssv = DT_PROP_OR(n, tssv, 0), \
9601013
.softstart = SSD16XX_ASSIGN_ARRAY(n, softstart), \
9611014
.lut_default = SSD16XX_ASSIGN_ARRAY(n, lut_default), \
9621015
.profile_initial = SSD16XX_INITIAL_PROFILE(n), \
963-
.dummy_line = DT_INST_PROP_OR(n, dummy_line, 0), \
964-
.override_dummy_line = \
965-
DT_INST_NODE_HAS_PROP(n, dummy_line), \
966-
.gate_line_width = \
967-
DT_INST_PROP_OR(n, gate_line_width, 0), \
968-
.override_gate_line_width = \
969-
DT_INST_NODE_HAS_PROP(n, gate_line_width), \
1016+
.dummy_line = DT_PROP_OR(n, dummy_line, 0), \
1017+
.override_dummy_line = DT_NODE_HAS_PROP(n, dummy_line), \
1018+
.gate_line_width = DT_PROP_OR(n, gate_line_width, 0), \
1019+
.override_gate_line_width = DT_NODE_HAS_PROP( \
1020+
n, gate_line_width), \
9701021
}; \
9711022
\
972-
static struct ssd16xx_data ssd16xx_data_##n; \
1023+
static struct ssd16xx_data ssd16xx_data_ ## n; \
9731024
\
974-
DEVICE_DT_INST_DEFINE(n, ssd16xx_init, NULL, \
975-
&ssd16xx_data_##n, \
976-
&ssd16xx_cfg_##n, \
977-
POST_KERNEL, \
978-
CONFIG_DISPLAY_INIT_PRIORITY, \
979-
&ssd16xx_driver_api); \
980-
981-
982-
DT_INST_FOREACH_STATUS_OKAY(SSD16XX_DEFINE)
1025+
DEVICE_DT_DEFINE(n, \
1026+
ssd16xx_init, NULL, \
1027+
&ssd16xx_data_ ## n, \
1028+
&ssd16xx_cfg_ ## n, \
1029+
POST_KERNEL, \
1030+
CONFIG_DISPLAY_INIT_PRIORITY, \
1031+
&ssd16xx_driver_api)
1032+
1033+
DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1608, SSD16XX_DEFINE,
1034+
&quirks_solomon_ssd1608);
1035+
DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1673, SSD16XX_DEFINE,
1036+
&quirks_solomon_ssd1673);
1037+
DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1675a, SSD16XX_DEFINE,
1038+
&quirks_solomon_ssd1675a);
1039+
DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1681, SSD16XX_DEFINE,
1040+
&quirks_solomon_ssd1681);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2022 Andreas Sandberg
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Solomon Systech SSD1608 320x240 EPD display controller
5+
6+
compatible: "solomon,ssd1608"
7+
8+
include: solomon,ssd16xx-common.yaml
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2022 Andreas Sandberg
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Solomon Systech SSD1673 250x150 EPD display controller
5+
6+
compatible: "solomon,ssd1673"
7+
8+
include: solomon,ssd16xx-common.yaml
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2022 Andreas Sandberg
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Solomon Systech SSD1675A 296x160 EPD display controller
5+
6+
compatible: "solomon,ssd1675a"
7+
8+
include: solomon,ssd16xx-common.yaml

0 commit comments

Comments
 (0)