diff --git a/drivers/display/Kconfig.ssd16xx b/drivers/display/Kconfig.ssd16xx index 097351fec5ea16a..c9fe019f32604a1 100644 --- a/drivers/display/Kconfig.ssd16xx +++ b/drivers/display/Kconfig.ssd16xx @@ -10,6 +10,7 @@ config SSD16XX DT_HAS_SOLOMON_SSD1608_ENABLED || \ DT_HAS_SOLOMON_SSD1673_ENABLED || \ DT_HAS_SOLOMON_SSD1675A_ENABLED || \ + DT_HAS_SOLOMON_SSD1677_ENABLED || \ DT_HAS_SOLOMON_SSD1680_ENABLED || \ DT_HAS_SOLOMON_SSD1681_ENABLED select MIPI_DBI diff --git a/drivers/display/ssd16xx.c b/drivers/display/ssd16xx.c index 7dfaa1211771be7..aa7200195337e51 100644 --- a/drivers/display/ssd16xx.c +++ b/drivers/display/ssd16xx.c @@ -60,6 +60,13 @@ struct ssd16xx_quirks { * SSD16XX_CMD_UPDATE_CTRL2 for a partial refresh. */ uint8_t ctrl2_partial; + + /* + * Device specific flag deciding whether to divide pass in the byte address or the bit address + * when setting the RAM x position window or counter in SSD16XX_CMD_RAM_XPOS_CTRL or SSD16XX_CMD_RAM_XPOS_CNTR. + * Expected to be `true` for the devices the byte address, `false` otherwise. + */ + bool x_addr_in_bytes; }; struct ssd16xx_data { @@ -105,6 +112,8 @@ struct ssd16xx_config { uint16_t height; uint16_t width; uint8_t tssv; + uint8_t gdo_flags; + bool scan_y_reverse; }; static int ssd16xx_set_profile(const struct device *dev, @@ -369,33 +378,69 @@ static int ssd16xx_set_window(const struct device *dev, } } - switch (data->orientation) { - case DISPLAY_ORIENTATION_NORMAL: - x_start = (panel_h - 1 - y) / SSD16XX_PIXELS_PER_BYTE; - x_end = (panel_h - 1 - (y + desc->height - 1)) / SSD16XX_PIXELS_PER_BYTE; - y_start = x; - y_end = (x + desc->width - 1); - break; - case DISPLAY_ORIENTATION_ROTATED_90: - x_start = (panel_h - 1 - x) / SSD16XX_PIXELS_PER_BYTE; - x_end = (panel_h - 1 - (x + desc->width - 1)) / SSD16XX_PIXELS_PER_BYTE; - y_start = (config->width - 1 - y); - y_end = (config->width - 1 - (y + desc->height - 1)); - break; - case DISPLAY_ORIENTATION_ROTATED_180: - x_start = y / SSD16XX_PIXELS_PER_BYTE; - x_end = (y + desc->height - 1) / SSD16XX_PIXELS_PER_BYTE; - y_start = (x + desc->width - 1); - y_end = x; - break; - case DISPLAY_ORIENTATION_ROTATED_270: - x_start = x / SSD16XX_PIXELS_PER_BYTE; - x_end = (x + desc->width - 1) / SSD16XX_PIXELS_PER_BYTE; - y_start = y; - y_end = (y + desc->height - 1); - break; - default: - return -EINVAL; + if (config->scan_y_reverse) { + switch (data->orientation) { + case DISPLAY_ORIENTATION_NORMAL: + x_start = (panel_h - 1 - y); + x_end = (panel_h - 1 - (y + desc->height - 1)); + y_start = (config->width - 1 - x); + y_end = (config->width - 1 - (x + desc->width - 1)); + break; + case DISPLAY_ORIENTATION_ROTATED_90: + x_start = x; + x_end = (x + desc->width - 1); + y_start = (config->width - 1 - y); + y_end = (config->width - 1 - (y + desc->height - 1)); + break; + case DISPLAY_ORIENTATION_ROTATED_180: + x_start = y; + x_end = (y + desc->height - 1); + y_start = x; + y_end = (x + desc->width - 1); + break; + case DISPLAY_ORIENTATION_ROTATED_270: + x_start = (panel_h - 1 - x); + x_end = (panel_h - 1 - (x + desc->width - 1)); + y_start = y; + y_end = (y + desc->height - 1); + break; + default: + return -EINVAL; + } + } else { + switch (data->orientation) { + case DISPLAY_ORIENTATION_NORMAL: + x_start = (panel_h - 1 - y); + x_end = (panel_h - 1 - (y + desc->height - 1)); + y_start = x; + y_end = (x + desc->width - 1); + break; + case DISPLAY_ORIENTATION_ROTATED_90: + x_start = (panel_h - 1 - x); + x_end = (panel_h - 1 - (x + desc->width - 1)); + y_start = (config->width - 1 - y); + y_end = (config->width - 1 - (y + desc->height - 1)); + break; + case DISPLAY_ORIENTATION_ROTATED_180: + x_start = y; + x_end = (y + desc->height - 1); + y_start = (x + desc->width - 1); + y_end = x; + break; + case DISPLAY_ORIENTATION_ROTATED_270: + x_start = x; + x_end = (x + desc->width - 1); + y_start = y; + y_end = (y + desc->height - 1); + break; + default: + return -EINVAL; + } + } + + if (config->quirks->x_addr_in_bytes) { + x_start /= SSD16XX_PIXELS_PER_BYTE; + x_end /= SSD16XX_PIXELS_PER_BYTE; } err = ssd16xx_set_ram_param(dev, x_start, x_end, y_start, y_end); @@ -588,17 +633,30 @@ static int ssd16xx_set_pixel_format(const struct device *dev, static int ssd16xx_set_orientation(const struct device *dev, const enum display_orientation orientation) { + const struct ssd16xx_config *config = dev->config; struct ssd16xx_data *data = dev->data; int err; - if (orientation == DISPLAY_ORIENTATION_NORMAL) { - data->scan_mode = SSD16XX_DATA_ENTRY_XDYIY; - } else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) { - data->scan_mode = SSD16XX_DATA_ENTRY_XDYDX; - } else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) { - data->scan_mode = SSD16XX_DATA_ENTRY_XIYDY; - } else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) { - data->scan_mode = SSD16XX_DATA_ENTRY_XIYIX; + if (config->scan_y_reverse) { + if (orientation == DISPLAY_ORIENTATION_NORMAL) { + data->scan_mode = SSD16XX_DATA_ENTRY_XDYDY; + } else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) { + data->scan_mode = SSD16XX_DATA_ENTRY_XIYDX; + } else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) { + data->scan_mode = SSD16XX_DATA_ENTRY_XIYIY; + } else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) { + data->scan_mode = SSD16XX_DATA_ENTRY_XDYIX; + } + } else { + if (orientation == DISPLAY_ORIENTATION_NORMAL) { + data->scan_mode = SSD16XX_DATA_ENTRY_XDYIY; + } else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) { + data->scan_mode = SSD16XX_DATA_ENTRY_XDYDX; + } else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) { + data->scan_mode = SSD16XX_DATA_ENTRY_XIYDY; + } else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) { + data->scan_mode = SSD16XX_DATA_ENTRY_XIYIX; + } } err = ssd16xx_write_uint8(dev, SSD16XX_CMD_ENTRY_MODE, data->scan_mode); @@ -614,7 +672,7 @@ static int ssd16xx_set_orientation(const struct device *dev, static int ssd16xx_clear_cntlr_mem(const struct device *dev, uint8_t ram_cmd) { const struct ssd16xx_config *config = dev->config; - uint16_t panel_h = config->height / EPD_PANEL_NUMOF_ROWS_PER_PAGE; + uint16_t panel_h = config->height; uint16_t last_gate = config->width - 1; uint8_t clear_page[64]; int err; @@ -623,8 +681,8 @@ static int ssd16xx_clear_cntlr_mem(const struct device *dev, uint8_t ram_cmd) * Clear unusable memory area when the resolution of the panel is not * multiple of an octet. */ - if (config->height % EPD_PANEL_NUMOF_ROWS_PER_PAGE) { - panel_h += 1; + if (config->quirks->x_addr_in_bytes) { + panel_h = (config->height / EPD_PANEL_NUMOF_ROWS_PER_PAGE) + (config->height % EPD_PANEL_NUMOF_ROWS_PER_PAGE ? 1 : 0); } err = ssd16xx_write_uint8(dev, SSD16XX_CMD_ENTRY_MODE, @@ -762,7 +820,7 @@ static int ssd16xx_set_profile(const struct device *dev, } gdo_len = push_y_param(dev, gdo, last_gate); - gdo[gdo_len++] = 0U; + gdo[gdo_len++] = config->gdo_flags; err = ssd16xx_write_cmd(dev, SSD16XX_CMD_GDO_CTRL, gdo, gdo_len); if (err < 0) { return err; @@ -955,6 +1013,7 @@ static struct ssd16xx_quirks quirks_solomon_ssd1608 = { .pp_height_bits = 16, .ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN, .ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN, + .x_addr_in_bytes = true, }; #endif @@ -966,6 +1025,7 @@ static struct ssd16xx_quirks quirks_solomon_ssd1673 = { .pp_height_bits = 8, .ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN, .ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN, + .x_addr_in_bytes = true, }; #endif @@ -977,6 +1037,19 @@ static struct ssd16xx_quirks quirks_solomon_ssd1675a = { .pp_height_bits = 16, .ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN, .ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN, + .x_addr_in_bytes = true, +}; +#endif + +#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1677) +static const struct ssd16xx_quirks quirks_solomon_ssd1677 = { + .max_width = 680, + .max_height = 960, + .pp_width_bits = 16, + .pp_height_bits = 16, + .ctrl2_full = SSD16XX_GEN2_CTRL2_DISPLAY, + .ctrl2_partial = SSD16XX_GEN2_CTRL2_DISPLAY | SSD16XX_GEN2_CTRL2_MODE2, + .x_addr_in_bytes = false, }; #endif @@ -988,6 +1061,7 @@ static const struct ssd16xx_quirks quirks_solomon_ssd1680 = { .pp_height_bits = 16, .ctrl2_full = SSD16XX_GEN2_CTRL2_DISPLAY, .ctrl2_partial = SSD16XX_GEN2_CTRL2_DISPLAY | SSD16XX_GEN2_CTRL2_MODE2, + .x_addr_in_bytes = true, }; #endif @@ -999,6 +1073,7 @@ static struct ssd16xx_quirks quirks_solomon_ssd1681 = { .pp_height_bits = 16, .ctrl2_full = SSD16XX_GEN2_CTRL2_DISPLAY, .ctrl2_partial = SSD16XX_GEN2_CTRL2_DISPLAY | SSD16XX_GEN2_CTRL2_MODE2, + .x_addr_in_bytes = true, }; #endif @@ -1063,7 +1138,9 @@ static struct ssd16xx_quirks quirks_solomon_ssd1681 = { .height = DT_PROP(n, height), \ .width = DT_PROP(n, width), \ .rotation = DT_PROP(n, rotation), \ + .scan_y_reverse = DT_PROP(n, scan_y_reverse), \ .tssv = DT_PROP_OR(n, tssv, 0), \ + .gdo_flags = DT_PROP_OR(n, gdo_flags, 0), \ .softstart = SSD16XX_ASSIGN_ARRAY(n, softstart), \ .profiles = { \ [SSD16XX_PROFILE_FULL] = \ @@ -1089,6 +1166,8 @@ DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1673, SSD16XX_DEFINE, &quirks_solomon_ssd1673); DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1675a, SSD16XX_DEFINE, &quirks_solomon_ssd1675a); +DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1677, SSD16XX_DEFINE, + &quirks_solomon_ssd1677); DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1680, SSD16XX_DEFINE, &quirks_solomon_ssd1680); DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1681, SSD16XX_DEFINE, diff --git a/dts/bindings/display/solomon,ssd1677.yaml b/dts/bindings/display/solomon,ssd1677.yaml new file mode 100644 index 000000000000000..6867c2c004842e9 --- /dev/null +++ b/dts/bindings/display/solomon,ssd1677.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2022 Andreas Sandberg +# SPDX-License-Identifier: Apache-2.0 + +description: Solomon Systech SSD1677 960x680 EPD display controller + +compatible: "solomon,ssd1677" + +include: solomon,ssd16xx-common.yaml diff --git a/dts/bindings/display/solomon,ssd16xx-common.yaml b/dts/bindings/display/solomon,ssd16xx-common.yaml index 343f9a9c69b488f..e211ea9794b85c2 100644 --- a/dts/bindings/display/solomon,ssd16xx-common.yaml +++ b/dts/bindings/display/solomon,ssd16xx-common.yaml @@ -38,6 +38,20 @@ properties: description: Display rotation (CW) in degrees. If not defined, rotation is off by default. + + scan-y-reverse: + type: boolean + description: Reverse the scan direction along Y axis for the display. + + Some displays have the scan direction reversed along the Y axis. + + gdo-flags: + type: int + description: Gate scanning sequence and direction + + Additional flags passed into Driver Output control command to + determine the how the output pin layout for the panel is and + what should be scanning sequence and the direction. child-binding: description: |