Skip to content

Commit deb1b18

Browse files
committed
drivers: display: ssd16xx: implement display_set_orientation API
I chose this approach instead of software rotation because I need it for an ultra low power project. Rotation in steps of 0, 90, 180, 270 degrees is done by changing the data entry modes. This commit removes the orientation-flipped property as it is redundant. I made the assumption that the current driver implementation is right about controller X/Y versus display width/height. The display controller X parameter matches with the display height (dts) and display controller Y parameter matches with the display width (dts). It looks like display manufacturers choose to have it like this because a wider screen probably makes more sense. Tested on the reel_board with a 250x122 display (ssd1673 controller) and a custom PCB with a 200x200 display (ssd1681 controller). Also tested all orientations with various width/height dts overrides. Signed-off-by: Sven Depoorter <[email protected]>
1 parent 91054be commit deb1b18

File tree

2 files changed

+124
-41
lines changed

2 files changed

+124
-41
lines changed

drivers/display/ssd16xx.c

Lines changed: 112 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ struct ssd16xx_data {
6666
uint8_t scan_mode;
6767
bool blanking_on;
6868
enum ssd16xx_profile_type profile;
69+
enum display_orientation orientation;
6970
};
7071

7172
struct ssd16xx_dt_array {
@@ -100,7 +101,7 @@ struct ssd16xx_config {
100101

101102
const struct ssd16xx_profile *profiles[SSD16XX_NUM_PROFILES];
102103

103-
bool orientation;
104+
uint16_t rotation;
104105
uint16_t height;
105106
uint16_t width;
106107
uint8_t tssv;
@@ -380,42 +381,73 @@ static int ssd16xx_set_window(const struct device *dev,
380381
return -ENOTSUP;
381382
}
382383

383-
if ((y + desc->height) > panel_h) {
384-
LOG_ERR("Buffer out of bounds (height)");
385-
return -EINVAL;
386-
}
384+
if (data->orientation == DISPLAY_ORIENTATION_NORMAL ||
385+
data->orientation == DISPLAY_ORIENTATION_ROTATED_180) {
386+
if ((y + desc->height) > panel_h) {
387+
LOG_ERR("Buffer out of bounds (height)");
388+
return -EINVAL;
389+
}
387390

388-
if ((x + desc->width) > config->width) {
389-
LOG_ERR("Buffer out of bounds (width)");
390-
return -EINVAL;
391-
}
391+
if ((x + desc->width) > config->width) {
392+
LOG_ERR("Buffer out of bounds (width)");
393+
return -EINVAL;
394+
}
392395

393-
if ((desc->height % EPD_PANEL_NUMOF_ROWS_PER_PAGE) != 0U) {
394-
LOG_ERR("Buffer height not multiple of %d",
395-
EPD_PANEL_NUMOF_ROWS_PER_PAGE);
396-
return -EINVAL;
397-
}
396+
if ((desc->height % EPD_PANEL_NUMOF_ROWS_PER_PAGE) != 0U) {
397+
LOG_ERR("Buffer height not multiple of %d", EPD_PANEL_NUMOF_ROWS_PER_PAGE);
398+
return -EINVAL;
399+
}
398400

399-
if ((y % EPD_PANEL_NUMOF_ROWS_PER_PAGE) != 0U) {
400-
LOG_ERR("Y coordinate not multiple of %d",
401-
EPD_PANEL_NUMOF_ROWS_PER_PAGE);
402-
return -EINVAL;
401+
if ((y % EPD_PANEL_NUMOF_ROWS_PER_PAGE) != 0U) {
402+
LOG_ERR("Y coordinate not multiple of %d", EPD_PANEL_NUMOF_ROWS_PER_PAGE);
403+
return -EINVAL;
404+
}
405+
} else {
406+
if ((y + desc->height) > config->width) {
407+
LOG_ERR("Buffer out of bounds (height)");
408+
return -EINVAL;
409+
}
410+
411+
if ((x + desc->width) > panel_h) {
412+
LOG_ERR("Buffer out of bounds (width)");
413+
return -EINVAL;
414+
}
415+
416+
if ((desc->width % SSD16XX_PIXELS_PER_BYTE) != 0U) {
417+
LOG_ERR("Buffer width not multiple of %d", SSD16XX_PIXELS_PER_BYTE);
418+
return -EINVAL;
419+
}
420+
421+
if ((x % SSD16XX_PIXELS_PER_BYTE) != 0U) {
422+
LOG_ERR("X coordinate not multiple of %d", SSD16XX_PIXELS_PER_BYTE);
423+
return -EINVAL;
424+
}
403425
}
404426

405-
switch (data->scan_mode) {
406-
case SSD16XX_DATA_ENTRY_XIYDY:
427+
switch (data->orientation) {
428+
case DISPLAY_ORIENTATION_NORMAL:
429+
x_start = (panel_h - 1 - y) / SSD16XX_PIXELS_PER_BYTE;
430+
x_end = (panel_h - 1 - (y + desc->height - 1)) / SSD16XX_PIXELS_PER_BYTE;
431+
y_start = x;
432+
y_end = (x + desc->width - 1);
433+
break;
434+
case DISPLAY_ORIENTATION_ROTATED_90:
435+
x_start = (panel_h - 1 - x) / SSD16XX_PIXELS_PER_BYTE;
436+
x_end = (panel_h - 1 - (x + desc->width - 1)) / SSD16XX_PIXELS_PER_BYTE;
437+
y_start = (config->width - 1 - y);
438+
y_end = (config->width - 1 - (y + desc->height - 1));
439+
break;
440+
case DISPLAY_ORIENTATION_ROTATED_180:
407441
x_start = y / SSD16XX_PIXELS_PER_BYTE;
408442
x_end = (y + desc->height - 1) / SSD16XX_PIXELS_PER_BYTE;
409443
y_start = (x + desc->width - 1);
410444
y_end = x;
411445
break;
412-
413-
case SSD16XX_DATA_ENTRY_XDYIY:
414-
x_start = (panel_h - 1 - y) / SSD16XX_PIXELS_PER_BYTE;
415-
x_end = (panel_h - 1 - (y + desc->height - 1)) /
416-
SSD16XX_PIXELS_PER_BYTE;
417-
y_start = x;
418-
y_end = (x + desc->width - 1);
446+
case DISPLAY_ORIENTATION_ROTATED_270:
447+
x_start = x / SSD16XX_PIXELS_PER_BYTE;
448+
x_end = (x + desc->width - 1) / SSD16XX_PIXELS_PER_BYTE;
449+
y_start = y;
450+
y_end = (y + desc->height - 1);
419451
break;
420452
default:
421453
return -EINVAL;
@@ -585,16 +617,22 @@ static void ssd16xx_get_capabilities(const struct device *dev,
585617
struct display_capabilities *caps)
586618
{
587619
const struct ssd16xx_config *config = dev->config;
620+
struct ssd16xx_data *data = dev->data;
588621

589622
memset(caps, 0, sizeof(struct display_capabilities));
590623
caps->x_resolution = config->width;
591624
caps->y_resolution = config->height -
592625
config->height % EPD_PANEL_NUMOF_ROWS_PER_PAGE;
593626
caps->supported_pixel_formats = PIXEL_FORMAT_MONO10;
594627
caps->current_pixel_format = PIXEL_FORMAT_MONO10;
595-
caps->screen_info = SCREEN_INFO_MONO_VTILED |
596-
SCREEN_INFO_MONO_MSB_FIRST |
597-
SCREEN_INFO_EPD;
628+
caps->screen_info = SCREEN_INFO_MONO_MSB_FIRST | SCREEN_INFO_EPD;
629+
630+
if (data->orientation == DISPLAY_ORIENTATION_NORMAL ||
631+
data->orientation == DISPLAY_ORIENTATION_ROTATED_180) {
632+
caps->screen_info |= SCREEN_INFO_MONO_VTILED;
633+
}
634+
635+
caps->current_orientation = data->orientation;
598636
}
599637

600638
static int ssd16xx_set_pixel_format(const struct device *dev,
@@ -608,6 +646,32 @@ static int ssd16xx_set_pixel_format(const struct device *dev,
608646
return -ENOTSUP;
609647
}
610648

649+
static int ssd16xx_set_orientation(const struct device *dev,
650+
const enum display_orientation orientation)
651+
{
652+
struct ssd16xx_data *data = dev->data;
653+
int err;
654+
655+
if (orientation == DISPLAY_ORIENTATION_NORMAL) {
656+
data->scan_mode = SSD16XX_DATA_ENTRY_XDYIY;
657+
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
658+
data->scan_mode = SSD16XX_DATA_ENTRY_XDYDX;
659+
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
660+
data->scan_mode = SSD16XX_DATA_ENTRY_XIYDY;
661+
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
662+
data->scan_mode = SSD16XX_DATA_ENTRY_XIYIX;
663+
}
664+
665+
err = ssd16xx_write_uint8(dev, SSD16XX_CMD_ENTRY_MODE, data->scan_mode);
666+
if (err < 0) {
667+
return err;
668+
}
669+
670+
data->orientation = orientation;
671+
672+
return 0;
673+
}
674+
611675
static int ssd16xx_clear_cntlr_mem(const struct device *dev, uint8_t ram_cmd)
612676
{
613677
const struct ssd16xx_config *config = dev->config;
@@ -840,6 +904,7 @@ static int ssd16xx_controller_init(const struct device *dev)
840904
{
841905
const struct ssd16xx_config *config = dev->config;
842906
struct ssd16xx_data *data = dev->data;
907+
enum display_orientation orientation;
843908
int err;
844909

845910
LOG_DBG("");
@@ -860,12 +925,6 @@ static int ssd16xx_controller_init(const struct device *dev)
860925

861926
k_msleep(SSD16XX_RESET_DELAY);
862927

863-
if (config->orientation == 1) {
864-
data->scan_mode = SSD16XX_DATA_ENTRY_XIYDY;
865-
} else {
866-
data->scan_mode = SSD16XX_DATA_ENTRY_XDYIY;
867-
}
868-
869928
err = ssd16xx_set_profile(dev, SSD16XX_PROFILE_FULL);
870929
if (err < 0) {
871930
return err;
@@ -881,6 +940,21 @@ static int ssd16xx_controller_init(const struct device *dev)
881940
return err;
882941
}
883942

943+
if (config->rotation == 0U) {
944+
orientation = DISPLAY_ORIENTATION_NORMAL;
945+
} else if (config->rotation == 90U) {
946+
orientation = DISPLAY_ORIENTATION_ROTATED_90;
947+
} else if (config->rotation == 180U) {
948+
orientation = DISPLAY_ORIENTATION_ROTATED_180;
949+
} else {
950+
orientation = DISPLAY_ORIENTATION_ROTATED_270;
951+
}
952+
953+
err = ssd16xx_set_orientation(dev, orientation);
954+
if (err < 0) {
955+
return err;
956+
}
957+
884958
err = ssd16xx_update_display(dev);
885959
if (err < 0) {
886960
return err;
@@ -954,6 +1028,7 @@ static struct display_driver_api ssd16xx_driver_api = {
9541028
.read = ssd16xx_read,
9551029
.get_capabilities = ssd16xx_get_capabilities,
9561030
.set_pixel_format = ssd16xx_set_pixel_format,
1031+
.set_orientation = ssd16xx_set_orientation,
9571032
};
9581033

9591034
#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1608)
@@ -1070,7 +1145,7 @@ static struct ssd16xx_quirks quirks_solomon_ssd1681 = {
10701145
.quirks = quirks_ptr, \
10711146
.height = DT_PROP(n, height), \
10721147
.width = DT_PROP(n, width), \
1073-
.orientation = DT_PROP(n, orientation_flipped), \
1148+
.rotation = DT_PROP(n, rotation), \
10741149
.tssv = DT_PROP_OR(n, tssv, 0), \
10751150
.softstart = SSD16XX_ASSIGN_ARRAY(n, softstart), \
10761151
.profiles = { \

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ properties:
1010
type: uint8-array
1111
description: Booster soft start values
1212

13-
orientation-flipped:
14-
type: boolean
15-
description: Last column address is mapped to first segment
16-
1713
reset-gpios:
1814
type: phandle-array
1915
required: true
@@ -49,6 +45,18 @@ properties:
4945
an external temperature sensor is connected to the controller.
5046
The value selects which sensor should be used.
5147

48+
rotation:
49+
type: int
50+
default: 0
51+
enum:
52+
- 0
53+
- 90
54+
- 180
55+
- 270
56+
description:
57+
Display rotation (CW) in degrees.
58+
If not defined, rotation is off by default.
59+
5260
child-binding:
5361
description: |
5462
Child nodes describe refresh profiles. Each refresh profile

0 commit comments

Comments
 (0)