Skip to content

Commit 2d446d9

Browse files
committed
drivers: display: ssd16xx: Add support for the ssd1677 EPD driver chip
Add support for the SSD1677 EPD driver chip with support for up to 960x680 pixel displays. Tested with the Waveshare 4.26" 800x480 display with XIAO BLE board. I believe it is the same as Good Display GDEQ0426T82. Tested with the samples/drivers/display sample. The SSD1677 requires x address to be full address, instead of the byte address used by SSD16XX. Added a new quirk to handle this. The display requires a different GDO control flag as the panel layout might be different, add an option to set this. The display also requires the scan direction for y axis to be reversed, add an option to set this as well. Signed-off-by: Nisarg Jhaveri <[email protected]>
1 parent bfd2ac7 commit 2d446d9

File tree

4 files changed

+144
-39
lines changed

4 files changed

+144
-39
lines changed

drivers/display/Kconfig.ssd16xx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ config SSD16XX
1010
DT_HAS_SOLOMON_SSD1608_ENABLED || \
1111
DT_HAS_SOLOMON_SSD1673_ENABLED || \
1212
DT_HAS_SOLOMON_SSD1675A_ENABLED || \
13+
DT_HAS_SOLOMON_SSD1677_ENABLED || \
1314
DT_HAS_SOLOMON_SSD1680_ENABLED || \
1415
DT_HAS_SOLOMON_SSD1681_ENABLED
1516
select MIPI_DBI

drivers/display/ssd16xx.c

Lines changed: 121 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ struct ssd16xx_quirks {
6060
* SSD16XX_CMD_UPDATE_CTRL2 for a partial refresh.
6161
*/
6262
uint8_t ctrl2_partial;
63+
64+
/*
65+
* Device specific flag deciding whether to pass in the byte address or
66+
* the bit address when setting the RAM x position window or counter in
67+
* SSD16XX_CMD_RAM_XPOS_CTRL or SSD16XX_CMD_RAM_XPOS_CNTR. Expected to
68+
* be `true` for the devices that expects the byte address, `false`
69+
* otherwise.
70+
*/
71+
bool x_addr_in_bytes;
6372
};
6473

6574
struct ssd16xx_data {
@@ -105,6 +114,8 @@ struct ssd16xx_config {
105114
uint16_t height;
106115
uint16_t width;
107116
uint8_t tssv;
117+
uint8_t gdo_flags;
118+
bool scan_y_reverse;
108119
};
109120

110121
static int ssd16xx_set_profile(const struct device *dev,
@@ -369,33 +380,69 @@ static int ssd16xx_set_window(const struct device *dev,
369380
}
370381
}
371382

372-
switch (data->orientation) {
373-
case DISPLAY_ORIENTATION_NORMAL:
374-
x_start = (panel_h - 1 - y) / SSD16XX_PIXELS_PER_BYTE;
375-
x_end = (panel_h - 1 - (y + desc->height - 1)) / SSD16XX_PIXELS_PER_BYTE;
376-
y_start = x;
377-
y_end = (x + desc->width - 1);
378-
break;
379-
case DISPLAY_ORIENTATION_ROTATED_90:
380-
x_start = (panel_h - 1 - x) / SSD16XX_PIXELS_PER_BYTE;
381-
x_end = (panel_h - 1 - (x + desc->width - 1)) / SSD16XX_PIXELS_PER_BYTE;
382-
y_start = (config->width - 1 - y);
383-
y_end = (config->width - 1 - (y + desc->height - 1));
384-
break;
385-
case DISPLAY_ORIENTATION_ROTATED_180:
386-
x_start = y / SSD16XX_PIXELS_PER_BYTE;
387-
x_end = (y + desc->height - 1) / SSD16XX_PIXELS_PER_BYTE;
388-
y_start = (x + desc->width - 1);
389-
y_end = x;
390-
break;
391-
case DISPLAY_ORIENTATION_ROTATED_270:
392-
x_start = x / SSD16XX_PIXELS_PER_BYTE;
393-
x_end = (x + desc->width - 1) / SSD16XX_PIXELS_PER_BYTE;
394-
y_start = y;
395-
y_end = (y + desc->height - 1);
396-
break;
397-
default:
398-
return -EINVAL;
383+
if (config->scan_y_reverse) {
384+
switch (data->orientation) {
385+
case DISPLAY_ORIENTATION_NORMAL:
386+
x_start = (panel_h - 1 - y);
387+
x_end = (panel_h - 1 - (y + desc->height - 1));
388+
y_start = (config->width - 1 - x);
389+
y_end = (config->width - 1 - (x + desc->width - 1));
390+
break;
391+
case DISPLAY_ORIENTATION_ROTATED_90:
392+
x_start = x;
393+
x_end = (x + desc->width - 1);
394+
y_start = (config->width - 1 - y);
395+
y_end = (config->width - 1 - (y + desc->height - 1));
396+
break;
397+
case DISPLAY_ORIENTATION_ROTATED_180:
398+
x_start = y;
399+
x_end = (y + desc->height - 1);
400+
y_start = x;
401+
y_end = (x + desc->width - 1);
402+
break;
403+
case DISPLAY_ORIENTATION_ROTATED_270:
404+
x_start = (panel_h - 1 - x);
405+
x_end = (panel_h - 1 - (x + desc->width - 1));
406+
y_start = y;
407+
y_end = (y + desc->height - 1);
408+
break;
409+
default:
410+
return -EINVAL;
411+
}
412+
} else {
413+
switch (data->orientation) {
414+
case DISPLAY_ORIENTATION_NORMAL:
415+
x_start = (panel_h - 1 - y);
416+
x_end = (panel_h - 1 - (y + desc->height - 1));
417+
y_start = x;
418+
y_end = (x + desc->width - 1);
419+
break;
420+
case DISPLAY_ORIENTATION_ROTATED_90:
421+
x_start = (panel_h - 1 - x);
422+
x_end = (panel_h - 1 - (x + desc->width - 1));
423+
y_start = (config->width - 1 - y);
424+
y_end = (config->width - 1 - (y + desc->height - 1));
425+
break;
426+
case DISPLAY_ORIENTATION_ROTATED_180:
427+
x_start = y;
428+
x_end = (y + desc->height - 1);
429+
y_start = (x + desc->width - 1);
430+
y_end = x;
431+
break;
432+
case DISPLAY_ORIENTATION_ROTATED_270:
433+
x_start = x;
434+
x_end = (x + desc->width - 1);
435+
y_start = y;
436+
y_end = (y + desc->height - 1);
437+
break;
438+
default:
439+
return -EINVAL;
440+
}
441+
}
442+
443+
if (config->quirks->x_addr_in_bytes) {
444+
x_start /= SSD16XX_PIXELS_PER_BYTE;
445+
x_end /= SSD16XX_PIXELS_PER_BYTE;
399446
}
400447

401448
err = ssd16xx_set_ram_param(dev, x_start, x_end, y_start, y_end);
@@ -588,17 +635,30 @@ static int ssd16xx_set_pixel_format(const struct device *dev,
588635
static int ssd16xx_set_orientation(const struct device *dev,
589636
const enum display_orientation orientation)
590637
{
638+
const struct ssd16xx_config *config = dev->config;
591639
struct ssd16xx_data *data = dev->data;
592640
int err;
593641

594-
if (orientation == DISPLAY_ORIENTATION_NORMAL) {
595-
data->scan_mode = SSD16XX_DATA_ENTRY_XDYIY;
596-
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
597-
data->scan_mode = SSD16XX_DATA_ENTRY_XDYDX;
598-
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
599-
data->scan_mode = SSD16XX_DATA_ENTRY_XIYDY;
600-
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
601-
data->scan_mode = SSD16XX_DATA_ENTRY_XIYIX;
642+
if (config->scan_y_reverse) {
643+
if (orientation == DISPLAY_ORIENTATION_NORMAL) {
644+
data->scan_mode = SSD16XX_DATA_ENTRY_XDYDY;
645+
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
646+
data->scan_mode = SSD16XX_DATA_ENTRY_XIYDX;
647+
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
648+
data->scan_mode = SSD16XX_DATA_ENTRY_XIYIY;
649+
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
650+
data->scan_mode = SSD16XX_DATA_ENTRY_XDYIX;
651+
}
652+
} else {
653+
if (orientation == DISPLAY_ORIENTATION_NORMAL) {
654+
data->scan_mode = SSD16XX_DATA_ENTRY_XDYIY;
655+
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
656+
data->scan_mode = SSD16XX_DATA_ENTRY_XDYDX;
657+
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
658+
data->scan_mode = SSD16XX_DATA_ENTRY_XIYDY;
659+
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
660+
data->scan_mode = SSD16XX_DATA_ENTRY_XIYIX;
661+
}
602662
}
603663

604664
err = ssd16xx_write_uint8(dev, SSD16XX_CMD_ENTRY_MODE, data->scan_mode);
@@ -614,7 +674,7 @@ static int ssd16xx_set_orientation(const struct device *dev,
614674
static int ssd16xx_clear_cntlr_mem(const struct device *dev, uint8_t ram_cmd)
615675
{
616676
const struct ssd16xx_config *config = dev->config;
617-
uint16_t panel_h = config->height / EPD_PANEL_NUMOF_ROWS_PER_PAGE;
677+
uint16_t panel_h = config->height;
618678
uint16_t last_gate = config->width - 1;
619679
uint8_t clear_page[64];
620680
int err;
@@ -623,8 +683,9 @@ static int ssd16xx_clear_cntlr_mem(const struct device *dev, uint8_t ram_cmd)
623683
* Clear unusable memory area when the resolution of the panel is not
624684
* multiple of an octet.
625685
*/
626-
if (config->height % EPD_PANEL_NUMOF_ROWS_PER_PAGE) {
627-
panel_h += 1;
686+
if (config->quirks->x_addr_in_bytes) {
687+
panel_h = (config->height + (EPD_PANEL_NUMOF_ROWS_PER_PAGE - 1)) /
688+
EPD_PANEL_NUMOF_ROWS_PER_PAGE;
628689
}
629690

630691
err = ssd16xx_write_uint8(dev, SSD16XX_CMD_ENTRY_MODE,
@@ -771,7 +832,7 @@ static int ssd16xx_set_profile(const struct device *dev,
771832
}
772833

773834
gdo_len = push_y_param(dev, gdo, last_gate);
774-
gdo[gdo_len++] = 0U;
835+
gdo[gdo_len++] = config->gdo_flags;
775836
err = ssd16xx_write_cmd(dev, SSD16XX_CMD_GDO_CTRL, gdo, gdo_len);
776837
if (err < 0) {
777838
return err;
@@ -964,6 +1025,7 @@ static struct ssd16xx_quirks quirks_solomon_ssd1608 = {
9641025
.pp_height_bits = 16,
9651026
.ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN,
9661027
.ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN,
1028+
.x_addr_in_bytes = true,
9671029
};
9681030
#endif
9691031

@@ -975,6 +1037,7 @@ static struct ssd16xx_quirks quirks_solomon_ssd1673 = {
9751037
.pp_height_bits = 8,
9761038
.ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN,
9771039
.ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN,
1040+
.x_addr_in_bytes = true,
9781041
};
9791042
#endif
9801043

@@ -986,6 +1049,19 @@ static struct ssd16xx_quirks quirks_solomon_ssd1675a = {
9861049
.pp_height_bits = 16,
9871050
.ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN,
9881051
.ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN,
1052+
.x_addr_in_bytes = true,
1053+
};
1054+
#endif
1055+
1056+
#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1677)
1057+
static const struct ssd16xx_quirks quirks_solomon_ssd1677 = {
1058+
.max_width = 680,
1059+
.max_height = 960,
1060+
.pp_width_bits = 16,
1061+
.pp_height_bits = 16,
1062+
.ctrl2_full = SSD16XX_GEN2_CTRL2_DISPLAY,
1063+
.ctrl2_partial = SSD16XX_GEN2_CTRL2_DISPLAY | SSD16XX_GEN2_CTRL2_MODE2,
1064+
.x_addr_in_bytes = false,
9891065
};
9901066
#endif
9911067

@@ -997,6 +1073,7 @@ static const struct ssd16xx_quirks quirks_solomon_ssd1680 = {
9971073
.pp_height_bits = 16,
9981074
.ctrl2_full = SSD16XX_GEN2_CTRL2_DISPLAY,
9991075
.ctrl2_partial = SSD16XX_GEN2_CTRL2_DISPLAY | SSD16XX_GEN2_CTRL2_MODE2,
1076+
.x_addr_in_bytes = true,
10001077
};
10011078
#endif
10021079

@@ -1008,6 +1085,7 @@ static struct ssd16xx_quirks quirks_solomon_ssd1681 = {
10081085
.pp_height_bits = 16,
10091086
.ctrl2_full = SSD16XX_GEN2_CTRL2_DISPLAY,
10101087
.ctrl2_partial = SSD16XX_GEN2_CTRL2_DISPLAY | SSD16XX_GEN2_CTRL2_MODE2,
1088+
.x_addr_in_bytes = true,
10111089
};
10121090
#endif
10131091

@@ -1072,7 +1150,9 @@ static struct ssd16xx_quirks quirks_solomon_ssd1681 = {
10721150
.height = DT_PROP(n, height), \
10731151
.width = DT_PROP(n, width), \
10741152
.rotation = DT_PROP(n, rotation), \
1153+
.scan_y_reverse = DT_PROP(n, scan_y_reverse), \
10751154
.tssv = DT_PROP_OR(n, tssv, 0), \
1155+
.gdo_flags = DT_PROP_OR(n, gdo_flags, 0), \
10761156
.softstart = SSD16XX_ASSIGN_ARRAY(n, softstart), \
10771157
.profiles = { \
10781158
[SSD16XX_PROFILE_FULL] = \
@@ -1098,6 +1178,8 @@ DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1673, SSD16XX_DEFINE,
10981178
&quirks_solomon_ssd1673);
10991179
DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1675a, SSD16XX_DEFINE,
11001180
&quirks_solomon_ssd1675a);
1181+
DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1677, SSD16XX_DEFINE,
1182+
&quirks_solomon_ssd1677);
11011183
DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1680, SSD16XX_DEFINE,
11021184
&quirks_solomon_ssd1680);
11031185
DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1681, SSD16XX_DEFINE,
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 SSD1677 960x680 EPD display controller
5+
6+
compatible: "solomon,ssd1677"
7+
8+
include: solomon,ssd16xx-common.yaml

dts/bindings/display/solomon,ssd16xx-common.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ properties:
4343
Display rotation (CW) in degrees.
4444
If not defined, rotation is off by default.
4545

46+
scan-y-reverse:
47+
type: boolean
48+
description: Reverse the scan direction along Y axis for the display.
49+
50+
Some displays have the scan direction reversed along the Y axis.
51+
52+
gdo-flags:
53+
type: int
54+
description: Gate scanning sequence and direction
55+
56+
Additional flags passed into Driver Output control command to
57+
determine the how the output pin layout for the panel is and
58+
what should be scanning sequence and the direction.
59+
4660
child-binding:
4761
description: |
4862
Child nodes describe refresh profiles. Each refresh profile

0 commit comments

Comments
 (0)