diff --git a/boards/arm/reel_board/reel_board.dts b/boards/arm/reel_board/reel_board.dts index ca884e11756b2..c20cf01c0ef00 100644 --- a/boards/arm/reel_board/reel_board.dts +++ b/boards/arm/reel_board/reel_board.dts @@ -57,33 +57,43 @@ pinctrl-1 = <&spi1_sleep>; pinctrl-names = "default", "sleep"; ssd16xx: ssd16xxfb@0 { - compatible = "gooddisplay,gdeh0213b1", "solomon,ssd1673", "solomon,ssd16xxfb"; + compatible = "gooddisplay,gdeh0213b1", "solomon,ssd1673"; spi-max-frequency = <4000000>; reg = <0>; width = <250>; height = <122>; - pp-width-bits = <8>; - pp-height-bits = <8>; reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>; dc-gpios = <&gpio0 16 GPIO_ACTIVE_LOW>; busy-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; - gdv = [10 0a]; - sdv = [19]; - vcom = <0xa8>; - border-waveform = <0x71>; - dummy-line = <0x1a>; - gate-line-width = <0x08>; - lut-initial = [ - 22 55 AA 55 AA 55 AA 11 - 00 00 00 00 00 00 00 00 - 1E 1E 1E 1E 1E 1E 1E 1E - 01 00 00 00 00 - ]; - lut-default = [ - 18 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 - 0F 01 00 00 00 00 00 00 - 00 00 00 00 00 - ]; + + full { + gdv = [10 0a]; + sdv = [19]; + vcom = <0xa8>; + border-waveform = <0x71>; + dummy-line = <0x1a>; + gate-line-width = <0x08>; + lut = [ + 22 55 AA 55 AA 55 AA 11 + 00 00 00 00 00 00 00 00 + 1E 1E 1E 1E 1E 1E 1E 1E + 01 00 00 00 00 + ]; + }; + + partial { + gdv = [10 0a]; + sdv = [19]; + vcom = <0xa8>; + border-waveform = <0x71>; + dummy-line = <0x1a>; + gate-line-width = <0x08>; + lut = [ + 18 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + 0F 01 00 00 00 00 00 00 + 00 00 00 00 00 + ]; + }; }; }; diff --git a/boards/arm/reel_board/reel_board_v2.dts b/boards/arm/reel_board/reel_board_v2.dts index 95615ea9348df..37c0f6c1397fc 100644 --- a/boards/arm/reel_board/reel_board_v2.dts +++ b/boards/arm/reel_board/reel_board_v2.dts @@ -40,71 +40,80 @@ pinctrl-1 = <&spi1_sleep>; pinctrl-names = "default", "sleep"; ssd16xx: ssd16xxfb@0 { - compatible = "gooddisplay,gdeh0213b72", "solomon,ssd1675a", "solomon,ssd16xxfb"; + compatible = "gooddisplay,gdeh0213b72", "solomon,ssd1675a"; spi-max-frequency = <4000000>; reg = <0>; width = <250>; height = <122>; - pp-width-bits = <8>; - pp-height-bits = <16>; reset-gpios = <&gpio0 15 GPIO_ACTIVE_LOW>; dc-gpios = <&gpio0 16 GPIO_ACTIVE_LOW>; busy-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; - gdv = [15]; - sdv = [41 a8 32]; - vcom = <0x26>; - border-waveform = <0x03>; - dummy-line = <0x30>; - gate-line-width = <0x0a>; - lut-initial = [ - /* - * Waveform Composition - * - * There are 7 Voltage Source (VS) Level groups - * n = {0,1,2...6}, each group contains - * 4 phases x = {A,B,C,D}. - * 2 bits represent the voltage in a phase: - * 00 – VSS, 01 – VSH1, 10 – VSL, 11 - VSH2 - * - * For example 0x80 represents sequence VSL-VSS-VSS-VSS, - */ - 80 60 40 00 00 00 00 /* LUT0: BB: VS 0..6 */ - 10 60 20 00 00 00 00 /* LUT1: BW: VS 0..6 */ - 80 60 40 00 00 00 00 /* LUT2: WB: VS 0..6 */ - 10 60 20 00 00 00 00 /* LUT3: WW: VS 0..6 */ - 00 00 00 00 00 00 00 /* LUT4: VCOM: VS 0..6 */ - /* - * TPnx determines the length of each phase, - * and RPn repeat count of a sequence. - * TPnA, TPnB, TPnC, TPnD, RPn - * - * For example TP0A=3, TP0B=3, and RP0=2: - * VS sequence : VSL-VSS-VSS-VSS - * number of Gate Pulses (length) : 3 3 0 0 - * repeat count : 2 - */ - 03 03 00 00 02 /* TP0A TP0B TP0C TP0D RP0 */ - 09 09 00 00 02 /* TP1A TP1B TP1C TP1D RP1 */ - 03 03 00 00 02 /* TP2A TP2B TP2C TP2D RP2 */ - 00 00 00 00 00 /* TP3A TP3B TP3C TP3D RP3 */ - 00 00 00 00 00 /* TP4A TP4B TP4C TP4D RP4 */ - 00 00 00 00 00 /* TP5A TP5B TP5C TP5D RP5 */ - 00 00 00 00 00 /* TP6A TP6B TP6C TP6D RP6 */ - ]; - lut-default = [ - 00 00 00 00 00 00 00 /* LUT0: BB: VS0..6 */ - 80 00 00 00 00 00 00 /* LUT1: BW: VS0..6 */ - 40 00 00 00 00 00 00 /* LUT2: WB: VS0..6 */ - 80 00 00 00 00 00 00 /* LUT3: WW: VS0..6 */ - 00 00 00 00 00 00 00 /* LUT4: VCOM: VS0..6 */ - 0A 00 00 00 04 /* TP0A TP0B TP0C TP0D RP0 */ - 00 00 00 00 00 /* TP1A TP1B TP1C TP1D RP1 */ - 00 00 00 00 00 /* TP2A TP2B TP2C TP2D RP2 */ - 00 00 00 00 00 /* TP3A TP3B TP3C TP3D RP3 */ - 00 00 00 00 00 /* TP4A TP4B TP4C TP4D RP4 */ - 00 00 00 00 00 /* TP5A TP5B TP5C TP5D RP5 */ - 00 00 00 00 00 /* TP6A TP6B TP6C TP6D RP6 */ - ]; + full { + gdv = [15]; + sdv = [41 a8 32]; + vcom = <0x26>; + border-waveform = <0x03>; + dummy-line = <0x30>; + gate-line-width = <0x0a>; + lut = [ + /* + * Waveform Composition + * + * There are 7 Voltage Source (VS) Level groups + * n = {0,1,2...6}, each group contains + * 4 phases x = {A,B,C,D}. + * 2 bits represent the voltage in a phase: + * 00 – VSS, 01 – VSH1, 10 – VSL, 11 - VSH2 + * + * For example 0x80 represents sequence VSL-VSS-VSS-VSS, + */ + 80 60 40 00 00 00 00 /* LUT0: BB: VS 0..6 */ + 10 60 20 00 00 00 00 /* LUT1: BW: VS 0..6 */ + 80 60 40 00 00 00 00 /* LUT2: WB: VS 0..6 */ + 10 60 20 00 00 00 00 /* LUT3: WW: VS 0..6 */ + 00 00 00 00 00 00 00 /* LUT4: VCOM: VS 0..6 */ + /* + * TPnx determines the length of each phase, + * and RPn repeat count of a sequence. + * TPnA, TPnB, TPnC, TPnD, RPn + * + * For example TP0A=3, TP0B=3, and RP0=2: + * VS sequence : VSL-VSS-VSS-VSS + * number of Gate Pulses (length) : 3 3 0 0 + * repeat count : 2 + */ + 03 03 00 00 02 /* TP0A TP0B TP0C TP0D RP0 */ + 09 09 00 00 02 /* TP1A TP1B TP1C TP1D RP1 */ + 03 03 00 00 02 /* TP2A TP2B TP2C TP2D RP2 */ + 00 00 00 00 00 /* TP3A TP3B TP3C TP3D RP3 */ + 00 00 00 00 00 /* TP4A TP4B TP4C TP4D RP4 */ + 00 00 00 00 00 /* TP5A TP5B TP5C TP5D RP5 */ + 00 00 00 00 00 /* TP6A TP6B TP6C TP6D RP6 */ + ]; + }; + + partial { + gdv = [15]; + sdv = [41 a8 32]; + vcom = <0x26>; + border-waveform = <0x01>; + dummy-line = <0x30>; + gate-line-width = <0x0a>; + lut = [ + 00 00 00 00 00 00 00 /* LUT0: BB: VS0..6 */ + 80 00 00 00 00 00 00 /* LUT1: BW: VS0..6 */ + 40 00 00 00 00 00 00 /* LUT2: WB: VS0..6 */ + 80 00 00 00 00 00 00 /* LUT3: WW: VS0..6 */ + 00 00 00 00 00 00 00 /* LUT4: VCOM: VS0..6 */ + 0A 00 00 00 04 /* TP0A TP0B TP0C TP0D RP0 */ + 00 00 00 00 00 /* TP1A TP1B TP1C TP1D RP1 */ + 00 00 00 00 00 /* TP2A TP2B TP2C TP2D RP2 */ + 00 00 00 00 00 /* TP3A TP3B TP3C TP3D RP3 */ + 00 00 00 00 00 /* TP4A TP4B TP4C TP4D RP4 */ + 00 00 00 00 00 /* TP5A TP5B TP5C TP5D RP5 */ + 00 00 00 00 00 /* TP6A TP6B TP6C TP6D RP6 */ + ]; + }; }; }; diff --git a/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0154a07.overlay b/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0154a07.overlay index 75f4d68c1b101..7a4dc21f0b70d 100644 --- a/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0154a07.overlay +++ b/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0154a07.overlay @@ -14,17 +14,23 @@ &arduino_spi { ssd16xx_waveshare_epaper_gdeh0154a07: ssd16xxfb@0 { - compatible = "gooddisplay,gdeh0154a07", "solomon,ssd1681", "solomon,ssd16xxfb"; + compatible = "gooddisplay,gdeh0154a07", "solomon,ssd1681"; spi-max-frequency = <4000000>; reg = <0>; width = <200>; height = <200>; - pp-width-bits = <8>; - pp-height-bits = <16>; dc-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */ reset-gpios = <&arduino_header 14 GPIO_ACTIVE_LOW>; /* D8 */ busy-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */ - border-waveform = <0x3c>; + tssv = <0x80>; + + full { + border-waveform = <0x05>; + }; + + partial { + border-waveform = <0x3c>; + }; }; }; diff --git a/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0213b1.overlay b/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0213b1.overlay index 4731ea97ab844..d7c873df0c3ce 100644 --- a/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0213b1.overlay +++ b/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0213b1.overlay @@ -14,33 +14,43 @@ &arduino_spi { ssd16xx_waveshare_epaper_gdeh0213b1: ssd16xxfb@0 { - compatible = "gooddisplay,gdeh0213b1", "solomon,ssd1673", "solomon,ssd16xxfb"; + compatible = "gooddisplay,gdeh0213b1", "solomon,ssd1673"; spi-max-frequency = <4000000>; reg = <0>; width = <250>; height = <120>; - pp-width-bits = <8>; - pp-height-bits = <8>; dc-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */ reset-gpios = <&arduino_header 14 GPIO_ACTIVE_LOW>; /* D8 */ busy-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */ - gdv = [10 0a]; - sdv = [19]; - vcom = <0xa8>; - border-waveform = <0x71>; - dummy-line = <0x1a>; - gate-line-width = <0x08>; - lut-initial = [ - 22 55 AA 55 AA 55 AA 11 - 00 00 00 00 00 00 00 00 - 1E 1E 1E 1E 1E 1E 1E 1E - 01 00 00 00 00 - ]; - lut-default = [ - 18 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 - 0F 01 00 00 00 00 00 00 - 00 00 00 00 00 - ]; + + full { + gdv = [10 0a]; + sdv = [19]; + vcom = <0xa8>; + border-waveform = <0x71>; + dummy-line = <0x1a>; + gate-line-width = <0x08>; + lut = [ + 22 55 AA 55 AA 55 AA 11 + 00 00 00 00 00 00 00 00 + 1E 1E 1E 1E 1E 1E 1E 1E + 01 00 00 00 00 + ]; + }; + + partial { + gdv = [10 0a]; + sdv = [19]; + vcom = <0xa8>; + border-waveform = <0x71>; + dummy-line = <0x1a>; + gate-line-width = <0x08>; + lut = [ + 18 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 + 0F 01 00 00 00 00 00 00 + 00 00 00 00 00 + ]; + }; }; }; diff --git a/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0213b72.overlay b/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0213b72.overlay index 330b4bc654c9a..37954c57cf19f 100644 --- a/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0213b72.overlay +++ b/boards/shields/waveshare_epaper/waveshare_epaper_gdeh0213b72.overlay @@ -14,50 +14,59 @@ &arduino_spi { ssd16xx_waveshare_epaper_gdeh0213b72: ssd16xxfb@0 { - compatible = "gooddisplay,gdeh0213b72", "solomon,ssd1675a", "solomon,ssd16xxfb"; + compatible = "gooddisplay,gdeh0213b72", "solomon,ssd1675a"; spi-max-frequency = <4000000>; reg = <0>; width = <250>; height = <120>; - pp-width-bits = <8>; - pp-height-bits = <16>; dc-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */ reset-gpios = <&arduino_header 14 GPIO_ACTIVE_LOW>; /* D8 */ busy-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */ - gdv = [15]; - sdv = [41 a8 32]; - vcom = <0x26>; - border-waveform = <0x03>; - dummy-line = <0x30>; - gate-line-width = <0x0a>; - lut-initial = [ - 80 60 40 00 00 00 00 - 10 60 20 00 00 00 00 - 80 60 40 00 00 00 00 - 10 60 20 00 00 00 00 - 00 00 00 00 00 00 00 - 03 03 00 00 02 - 09 09 00 00 02 - 03 03 00 00 02 - 00 00 00 00 00 - 00 00 00 00 00 - 00 00 00 00 00 - 00 00 00 00 00 - ]; - lut-default = [ - 00 00 00 00 00 00 00 - 80 00 00 00 00 00 00 - 40 00 00 00 00 00 00 - 80 00 00 00 00 00 00 - 00 00 00 00 00 00 00 - 0A 00 00 00 04 - 00 00 00 00 00 - 00 00 00 00 00 - 00 00 00 00 00 - 00 00 00 00 00 - 00 00 00 00 00 - 00 00 00 00 00 - ]; + full { + gdv = [15]; + sdv = [41 a8 32]; + vcom = <0x55>; + border-waveform = <0x03>; + dummy-line = <0x30>; + gate-line-width = <0x0a>; + lut = [ + 80 60 40 00 00 00 00 + 10 60 20 00 00 00 00 + 80 60 40 00 00 00 00 + 10 60 20 00 00 00 00 + 00 00 00 00 00 00 00 + 03 03 00 00 02 + 09 09 00 00 02 + 03 03 00 00 02 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + ]; + }; + + partial { + gdv = [15]; + sdv = [41 a8 32]; + vcom = <0x26>; + border-waveform = <0x01>; + dummy-line = <0x30>; + gate-line-width = <0x0a>; + lut = [ + 00 00 00 00 00 00 00 + 80 00 00 00 00 00 00 + 40 00 00 00 00 00 00 + 80 00 00 00 00 00 00 + 00 00 00 00 00 00 00 + 0A 00 00 00 04 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + 00 00 00 00 00 + ]; + }; }; }; diff --git a/boards/shields/waveshare_epaper/waveshare_epaper_gdeh029a1.overlay b/boards/shields/waveshare_epaper/waveshare_epaper_gdeh029a1.overlay index 912e333de4921..0b1b6f4d14f42 100644 --- a/boards/shields/waveshare_epaper/waveshare_epaper_gdeh029a1.overlay +++ b/boards/shields/waveshare_epaper/waveshare_epaper_gdeh029a1.overlay @@ -14,34 +14,41 @@ &arduino_spi { ssd16xx_waveshare_epaper_gdeh029a1: ssd16xxfb@0 { - compatible = "gooddisplay,gdeh029a1", "solomon,ssd1608", "solomon,ssd16xxfb"; + compatible = "gooddisplay,gdeh029a1", "solomon,ssd1608"; spi-max-frequency = <4000000>; reg = <0>; width = <296>; height = <128>; - pp-width-bits = <16>; - pp-height-bits = <16>; dc-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */ reset-gpios = <&arduino_header 14 GPIO_ACTIVE_LOW>; /* D8 */ busy-gpios = <&arduino_header 13 GPIO_ACTIVE_HIGH>; /* D7 */ - gdv = [16]; - sdv = [0a]; - vcom = <0xa8>; - border-waveform = <0x33>; + softstart = [d7 d6 9d]; - dummy-line = <0x1a>; - gate-line-width = <0x08>; - lut-initial = [ - 50 AA 55 AA 11 00 00 00 - 00 00 00 00 00 00 00 00 - 00 00 00 00 FF FF 1F 00 - 00 00 00 00 00 00 - ]; - lut-default = [ - 10 18 18 08 18 18 08 00 - 00 00 00 00 00 00 00 00 - 00 00 00 00 13 14 44 12 - 00 00 00 00 00 00 - ]; + + full { + vcom = <0x9a>; + border-waveform = <0x33>; + dummy-line = <0x1a>; + gate-line-width = <0x08>; + lut = [ + 50 AA 55 AA 11 00 00 00 + 00 00 00 00 00 00 00 00 + 00 00 00 00 FF FF 1F 00 + 00 00 00 00 00 00 + ]; + }; + + partial { + vcom = <0xa8>; + border-waveform = <0x01>; + dummy-line = <0x1a>; + gate-line-width = <0x08>; + lut = [ + 10 18 18 08 18 18 08 00 + 00 00 00 00 00 00 00 00 + 00 00 00 00 13 14 44 12 + 00 00 00 00 00 00 + ]; + }; }; }; diff --git a/drivers/display/Kconfig.ssd16xx b/drivers/display/Kconfig.ssd16xx index e666dff11adb1..1149ff03692e1 100644 --- a/drivers/display/Kconfig.ssd16xx +++ b/drivers/display/Kconfig.ssd16xx @@ -6,7 +6,12 @@ config SSD16XX bool "SSD16XX compatible display controller driver" default y - depends on DT_HAS_SOLOMON_SSD16XXFB_ENABLED + depends on \ + DT_HAS_SOLOMON_SSD1608_ENABLED || \ + DT_HAS_SOLOMON_SSD1673_ENABLED || \ + DT_HAS_SOLOMON_SSD1675A_ENABLED || \ + DT_HAS_SOLOMON_SSD1680_ENABLED || \ + DT_HAS_SOLOMON_SSD1681_ENABLED select SPI help Enable driver for SSD16XX compatible controller. diff --git a/drivers/display/ssd16xx.c b/drivers/display/ssd16xx.c index 103fe52435fec..d302dd85accc7 100644 --- a/drivers/display/ssd16xx.c +++ b/drivers/display/ssd16xx.c @@ -5,8 +5,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#define DT_DRV_COMPAT solomon_ssd16xxfb - #define LOG_LEVEL CONFIG_DISPLAY_LOG_LEVEL #include LOG_MODULE_REGISTER(ssd16xx); @@ -23,7 +21,7 @@ LOG_MODULE_REGISTER(ssd16xx); #include "ssd16xx_regs.h" /** - * SSD1673, SSD1608, SSD1681, ILI3897 compatible EPD controller driver. + * SSD16xx compatible EPD controller driver. */ #define EPD_PANEL_NUMOF_ROWS_PER_PAGE 8 @@ -33,11 +31,41 @@ LOG_MODULE_REGISTER(ssd16xx); #define SSD16XX_DEFAULT_TR_VALUE 25U #define SSD16XX_TR_SCALE_FACTOR 256U + +enum ssd16xx_profile_type { + SSD16XX_PROFILE_FULL = 0, + SSD16XX_PROFILE_PARTIAL, + SSD16XX_NUM_PROFILES, + SSD16XX_PROFILE_INVALID = SSD16XX_NUM_PROFILES, +}; + +struct ssd16xx_quirks { + /* Gates */ + uint16_t max_width; + /* Sources */ + uint16_t max_height; + /* Width (bits) of integer type representing an x coordinate */ + uint8_t pp_width_bits; + /* Width (bits) of integer type representing a y coordinate */ + uint8_t pp_height_bits; + + /* + * Device specific flags to be included in + * SSD16XX_CMD_UPDATE_CTRL2 for a full refresh. + */ + uint8_t ctrl2_full; + /* + * Device specific flags to be included in + * SSD16XX_CMD_UPDATE_CTRL2 for a partial refresh. + */ + uint8_t ctrl2_partial; +}; + struct ssd16xx_data { bool read_supported; uint8_t scan_mode; - uint8_t update_cmd; bool blanking_on; + enum ssd16xx_profile_type profile; }; struct ssd16xx_dt_array { @@ -51,9 +79,13 @@ struct ssd16xx_profile { struct ssd16xx_dt_array sdv; uint8_t vcom; uint8_t bwf; + uint8_t dummy_line; + uint8_t gate_line_width; bool override_vcom; bool override_bwf; + bool override_dummy_line; + bool override_gate_line_width; }; struct ssd16xx_config { @@ -62,25 +94,21 @@ struct ssd16xx_config { struct gpio_dt_spec busy_gpio; struct gpio_dt_spec reset_gpio; + const struct ssd16xx_quirks *quirks; + struct ssd16xx_dt_array softstart; - struct ssd16xx_profile profile_initial; - struct ssd16xx_dt_array lut_default; + const struct ssd16xx_profile *profiles[SSD16XX_NUM_PROFILES]; bool orientation; uint16_t height; uint16_t width; uint8_t tssv; - uint8_t pp_width_bits; - uint8_t pp_height_bits; - - uint8_t dummy_line; - bool override_dummy_line; - - uint8_t gate_line_width; - bool override_gate_line_width; }; +static int ssd16xx_set_profile(const struct device *dev, + enum ssd16xx_profile_type type); + static inline void ssd16xx_busy_wait(const struct device *dev) { const struct ssd16xx_config *config = dev->config; @@ -189,17 +217,18 @@ static inline size_t push_x_param(const struct device *dev, { const struct ssd16xx_config *config = dev->config; - if (config->pp_width_bits == 8) { + if (config->quirks->pp_width_bits == 8) { data[0] = (uint8_t)x; return 1; } - if (config->pp_width_bits == 16) { + if (config->quirks->pp_width_bits == 16) { sys_put_le16(sys_cpu_to_le16(x), data); return 2; } - LOG_ERR("Unsupported pp_width_bits %u", config->pp_width_bits); + LOG_ERR("Unsupported pp_width_bits %u", + config->quirks->pp_width_bits); return 0; } @@ -208,17 +237,18 @@ static inline size_t push_y_param(const struct device *dev, { const struct ssd16xx_config *config = dev->config; - if (config->pp_height_bits == 8) { + if (config->quirks->pp_height_bits == 8) { data[0] = (uint8_t)y; return 1; } - if (config->pp_height_bits == 16) { + if (config->quirks->pp_height_bits == 16) { sys_put_le16(sys_cpu_to_le16(y), data); return 2; } - LOG_ERR("Unsupported pp_height_bitsa %u", config->pp_height_bits); + LOG_ERR("Unsupported pp_height_bitsa %u", + config->quirks->pp_height_bits); return 0; } @@ -280,9 +310,23 @@ static int ssd16xx_activate(const struct device *dev, uint8_t ctrl2) static int ssd16xx_update_display(const struct device *dev) { - struct ssd16xx_data *data = dev->data; - - return ssd16xx_activate(dev, data->update_cmd); + const struct ssd16xx_config *config = dev->config; + const struct ssd16xx_data *data = dev->data; + const struct ssd16xx_profile *p = config->profiles[data->profile]; + const struct ssd16xx_quirks *quirks = config->quirks; + const bool load_lut = !p || p->lut.len == 0; + const bool load_temp = load_lut && config->tssv; + const bool partial = data->profile == SSD16XX_PROFILE_PARTIAL; + const uint8_t update_cmd = + SSD16XX_CTRL2_ENABLE_CLK | + SSD16XX_CTRL2_ENABLE_ANALOG | + (load_lut ? SSD16XX_CTRL2_LOAD_LUT : 0) | + (load_temp ? SSD16XX_CTRL2_LOAD_TEMPERATURE : 0) | + (partial ? quirks->ctrl2_partial : quirks->ctrl2_full) | + SSD16XX_CTRL2_DISABLE_ANALOG | + SSD16XX_CTRL2_DISABLE_CLK; + + return ssd16xx_activate(dev, update_cmd); } static int ssd16xx_blanking_off(const struct device *dev) @@ -301,6 +345,12 @@ static int ssd16xx_blanking_on(const struct device *dev) { struct ssd16xx_data *data = dev->data; + if (!data->blanking_on) { + if (ssd16xx_set_profile(dev, SSD16XX_PROFILE_FULL)) { + return -EIO; + } + } + data->blanking_on = true; return 0; @@ -311,7 +361,7 @@ static int ssd16xx_set_window(const struct device *dev, const struct display_buffer_descriptor *desc) { const struct ssd16xx_config *config = dev->config; - struct ssd16xx_data *data = dev->data; + const struct ssd16xx_data *data = dev->data; int err; uint16_t x_start; uint16_t x_end; @@ -395,7 +445,11 @@ static int ssd16xx_write(const struct device *dev, const uint16_t x, const struct display_buffer_descriptor *desc, const void *buf) { + const struct ssd16xx_config *config = dev->config; const struct ssd16xx_data *data = dev->data; + const bool have_partial_refresh = + config->profiles[SSD16XX_PROFILE_PARTIAL] != NULL; + const bool partial_refresh = !data->blanking_on && have_partial_refresh; const size_t buf_len = MIN(desc->buf_size, desc->height * desc->width / 8); int err; @@ -405,6 +459,17 @@ static int ssd16xx_write(const struct device *dev, const uint16_t x, return -EINVAL; } + if (partial_refresh) { + /* + * Request the partial profile. This operation becomes + * a no-op if the profile is already active. + */ + err = ssd16xx_set_profile(dev, SSD16XX_PROFILE_PARTIAL); + if (err < 0) { + return -EIO; + } + } + err = ssd16xx_set_window(dev, x, y, desc); if (err < 0) { return err; @@ -423,6 +488,35 @@ static int ssd16xx_write(const struct device *dev, const uint16_t x, } } + if (data->blanking_on && have_partial_refresh) { + /* + * We will trigger a full refresh when blanking is + * turned off. The controller won't keep track of the + * old frame buffer, which is needed to perform a + * partial update, when this happens. Maintain the old + * frame buffer manually here to make sure future + * partial updates will work as expected. + */ + err = ssd16xx_write_cmd(dev, SSD16XX_CMD_WRITE_RED_RAM, + (uint8_t *)buf, buf_len); + if (err < 0) { + return err; + } + } else if (partial_refresh) { + /* + * We just performed a partial refresh. After the + * refresh, the controller swaps the black/red buffers + * containing the current and new image. We need to + * perform a second write here to ensure that future + * updates work on an up-to-date framebuffer. + */ + err = ssd16xx_write_cmd(dev, SSD16XX_CMD_WRITE_RAM, + (uint8_t *)buf, buf_len); + if (err < 0) { + return err; + } + } + return 0; } @@ -519,8 +613,7 @@ static void ssd16xx_get_capabilities(const struct device *dev, caps->current_pixel_format = PIXEL_FORMAT_MONO10; caps->screen_info = SCREEN_INFO_MONO_VTILED | SCREEN_INFO_MONO_MSB_FIRST | - SCREEN_INFO_EPD | - SCREEN_INFO_DOUBLE_BUFFER; + SCREEN_INFO_EPD; } static int ssd16xx_set_orientation(const struct device *dev, @@ -542,8 +635,7 @@ static int ssd16xx_set_pixel_format(const struct device *dev, return -ENOTSUP; } -static int ssd16xx_clear_cntlr_mem(const struct device *dev, uint8_t ram_cmd, - bool update) +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; @@ -592,38 +684,25 @@ static int ssd16xx_clear_cntlr_mem(const struct device *dev, uint8_t ram_cmd, } } - if (update) { - return ssd16xx_update_display(dev); - } - return 0; } static inline int ssd16xx_load_ws_from_otp_tssv(const struct device *dev) { const struct ssd16xx_config *config = dev->config; - struct ssd16xx_data *data = dev->data; - int err; /* * Controller has an integrated temperature sensor or external * temperature sensor is connected to the controller. */ LOG_INF("Select and load WS from OTP"); - err = ssd16xx_write_uint8(dev, SSD16XX_CMD_TSENSOR_SELECTION, - config->tssv); - if (err == 0) { - data->update_cmd |= SSD16XX_CTRL2_LOAD_LUT | - SSD16XX_CTRL2_LOAD_TEMPERATURE; - } - - return err; + return ssd16xx_write_uint8(dev, SSD16XX_CMD_TSENSOR_SELECTION, + config->tssv); } static inline int ssd16xx_load_ws_from_otp(const struct device *dev) { int16_t t = (SSD16XX_DEFAULT_TR_VALUE * SSD16XX_TR_SCALE_FACTOR); - struct ssd16xx_data *data = dev->data; uint8_t tmp[2]; int err; @@ -646,8 +725,6 @@ static inline int ssd16xx_load_ws_from_otp(const struct device *dev) return err; } - data->update_cmd |= SSD16XX_CTRL2_LOAD_LUT; - return 0; } @@ -656,12 +733,9 @@ static int ssd16xx_load_lut(const struct device *dev, const struct ssd16xx_dt_array *lut) { const struct ssd16xx_config *config = dev->config; - struct ssd16xx_data *data = dev->data; if (lut && lut->len) { LOG_DBG("Using user-provided LUT"); - /* Don't load the default LUT on the next refresh */ - data->update_cmd &= ~SSD16XX_CTRL2_LOAD_LUT; return ssd16xx_write_cmd(dev, SSD16XX_CMD_UPDATE_LUT, lut->data, lut->len); } else { @@ -673,17 +747,82 @@ static int ssd16xx_load_lut(const struct device *dev, } } -static int ssd16xx_load_profile(const struct device *dev, - const struct ssd16xx_profile *p) +static int ssd16xx_set_profile(const struct device *dev, + enum ssd16xx_profile_type type) { + const struct ssd16xx_config *config = dev->config; + struct ssd16xx_data *data = dev->data; + const struct ssd16xx_profile *p; + const uint16_t last_gate = config->width - 1; + uint8_t gdo[3]; + size_t gdo_len; int err = 0; - err = ssd16xx_load_lut(dev, &p->lut); + if (type >= SSD16XX_NUM_PROFILES) { + return -EINVAL; + } + + p = config->profiles[type]; + + /* + * The full profile is the only one that always exists. If it + * hasn't been specified, we use the defaults. + */ + if (!p && type != SSD16XX_PROFILE_FULL) { + return -ENOENT; + } + + if (type == data->profile) { + return 0; + } + + /* + * Perform a soft reset to make sure registers are reset. This + * will leave the RAM contents intact. + */ + err = ssd16xx_write_cmd(dev, SSD16XX_CMD_SW_RESET, NULL, 0); + if (err < 0) { + return err; + } + + gdo_len = push_y_param(dev, gdo, last_gate); + gdo[gdo_len++] = 0U; + err = ssd16xx_write_cmd(dev, SSD16XX_CMD_GDO_CTRL, gdo, gdo_len); + if (err < 0) { + return err; + } + + if (config->softstart.len) { + err = ssd16xx_write_cmd(dev, SSD16XX_CMD_SOFTSTART, + config->softstart.data, + config->softstart.len); + if (err < 0) { + return err; + } + } + + err = ssd16xx_load_lut(dev, p ? &p->lut : NULL); if (err < 0) { return err; } - if (p->gdv.len) { + if (p && p->override_dummy_line) { + err = ssd16xx_write_uint8(dev, SSD16XX_CMD_DUMMY_LINE, + p->dummy_line); + if (err < 0) { + return err; + } + } + + if (p && p->override_gate_line_width) { + err = ssd16xx_write_uint8(dev, SSD16XX_CMD_GATE_LINE_WIDTH, + p->override_gate_line_width); + if (err < 0) { + return err; + } + } + + if (p && p->gdv.len) { LOG_DBG("Setting GDV"); err = ssd16xx_write_cmd(dev, SSD16XX_CMD_GDV_CTRL, p->gdv.data, p->gdv.len); @@ -692,7 +831,7 @@ static int ssd16xx_load_profile(const struct device *dev, } } - if (p->sdv.len) { + if (p && p->sdv.len) { LOG_DBG("Setting SDV"); err = ssd16xx_write_cmd(dev, SSD16XX_CMD_SDV_CTRL, p->sdv.data, p->sdv.len); @@ -701,7 +840,7 @@ static int ssd16xx_load_profile(const struct device *dev, } } - if (p->override_vcom) { + if (p && p->override_vcom) { LOG_DBG("Setting VCOM"); err = ssd16xx_write_cmd(dev, SSD16XX_CMD_VCOM_VOLTAGE, &p->vcom, 1); @@ -710,7 +849,7 @@ static int ssd16xx_load_profile(const struct device *dev, } } - if (p->override_bwf) { + if (p && p->override_bwf) { LOG_DBG("Setting BWF"); err = ssd16xx_write_cmd(dev, SSD16XX_CMD_BWF_CTRL, &p->bwf, 1); @@ -719,35 +858,21 @@ static int ssd16xx_load_profile(const struct device *dev, } } - return 0; -} - -static int ssd16xx_load_ws_default(const struct device *dev) -{ - const struct ssd16xx_config *config = dev->config; - - if (config->lut_default.len) { - return ssd16xx_write_cmd(dev, SSD16XX_CMD_UPDATE_LUT, - config->lut_default.data, - config->lut_default.len); - } + data->profile = type; return 0; } - static int ssd16xx_controller_init(const struct device *dev) { const struct ssd16xx_config *config = dev->config; struct ssd16xx_data *data = dev->data; - uint16_t last_gate = config->width - 1; int err; - uint8_t tmp[3]; - size_t len; LOG_DBG(""); data->blanking_on = false; + data->profile = SSD16XX_PROFILE_INVALID; err = gpio_pin_set_dt(&config->reset_gpio, 1); if (err < 0) { @@ -762,78 +887,33 @@ static int ssd16xx_controller_init(const struct device *dev) k_msleep(SSD16XX_RESET_DELAY); - err = ssd16xx_write_cmd(dev, SSD16XX_CMD_SW_RESET, NULL, 0); - if (err < 0) { - return err; - } - - len = push_y_param(dev, tmp, last_gate); - tmp[len++] = 0U; - err = ssd16xx_write_cmd(dev, SSD16XX_CMD_GDO_CTRL, tmp, len); - if (err < 0) { - return err; - } - - if (config->softstart.len) { - err = ssd16xx_write_cmd(dev, SSD16XX_CMD_SOFTSTART, - config->softstart.data, - config->softstart.len); - if (err < 0) { - return err; - } - } - - if (config->override_dummy_line) { - tmp[0] = config->dummy_line; - err = ssd16xx_write_uint8(dev, SSD16XX_CMD_DUMMY_LINE, - config->dummy_line); - if (err < 0) { - return err; - } - } - - if (config->override_gate_line_width) { - err = ssd16xx_write_uint8(dev, SSD16XX_CMD_GATE_LINE_WIDTH, - config->override_gate_line_width); - if (err < 0) { - return err; - } - } - if (config->orientation == 1) { data->scan_mode = SSD16XX_DATA_ENTRY_XIYDY; } else { data->scan_mode = SSD16XX_DATA_ENTRY_XDYIY; } - data->update_cmd = (SSD16XX_CTRL2_ENABLE_CLK | - SSD16XX_CTRL2_ENABLE_ANALOG | - SSD16XX_CTRL2_TO_PATTERN | - SSD16XX_CTRL2_DISABLE_ANALOG | - SSD16XX_CTRL2_DISABLE_CLK); - - err = ssd16xx_load_profile(dev, &config->profile_initial); + err = ssd16xx_set_profile(dev, SSD16XX_PROFILE_FULL); if (err < 0) { return err; } - err = ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RAM, true); + err = ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RAM); if (err < 0) { return err; } - err = ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RED_RAM, - false); + err = ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RED_RAM); if (err < 0) { return err; } - err = ssd16xx_load_ws_default(dev); + err = ssd16xx_update_display(dev); if (err < 0) { return err; } - return ssd16xx_clear_cntlr_mem(dev, SSD16XX_CMD_WRITE_RAM, true); + return 0; } static int ssd16xx_init(const struct device *dev) @@ -885,6 +965,12 @@ static int ssd16xx_init(const struct device *dev) return err; } + if (config->width > config->quirks->max_width || + config->height > config->quirks->max_height) { + LOG_ERR("Display size out of range."); + return -EINVAL; + } + return ssd16xx_controller_init(dev); } @@ -901,11 +987,60 @@ static struct display_driver_api ssd16xx_driver_api = { .set_orientation = ssd16xx_set_orientation, }; -#define LUT_DEFAULT_ASSIGN(n) \ - .lut_default = { \ - .data = lut_default_##n, \ - .len = sizeof(lut_default_##n), \ - }, +#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1608) +static struct ssd16xx_quirks quirks_solomon_ssd1608 = { + .max_width = 320, + .max_height = 240, + .pp_width_bits = 16, + .pp_height_bits = 16, + .ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN, + .ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN, +}; +#endif + +#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1673) +static struct ssd16xx_quirks quirks_solomon_ssd1673 = { + .max_width = 250, + .max_height = 150, + .pp_width_bits = 8, + .pp_height_bits = 8, + .ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN, + .ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN, +}; +#endif + +#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1675a) +static struct ssd16xx_quirks quirks_solomon_ssd1675a = { + .max_width = 296, + .max_height = 160, + .pp_width_bits = 8, + .pp_height_bits = 16, + .ctrl2_full = SSD16XX_GEN1_CTRL2_TO_PATTERN, + .ctrl2_partial = SSD16XX_GEN1_CTRL2_TO_PATTERN, +}; +#endif + +#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1680) +static const struct ssd16xx_quirks quirks_solomon_ssd1680 = { + .max_width = 296, + .max_height = 176, + .pp_width_bits = 8, + .pp_height_bits = 16, + .ctrl2_full = SSD16XX_GEN2_CTRL2_DISPLAY, + .ctrl2_partial = SSD16XX_GEN2_CTRL2_DISPLAY | SSD16XX_GEN2_CTRL2_MODE2, +}; +#endif + +#if DT_HAS_COMPAT_STATUS_OKAY(solomon_ssd1681) +static struct ssd16xx_quirks quirks_solomon_ssd1681 = { + .max_width = 200, + .max_height = 200, + .pp_width_bits = 8, + .pp_height_bits = 16, + .ctrl2_full = SSD16XX_GEN2_CTRL2_DISPLAY, + .ctrl2_partial = SSD16XX_GEN2_CTRL2_DISPLAY | SSD16XX_GEN2_CTRL2_MODE2, +}; +#endif #define SOFTSTART_ASSIGN(n) \ .softstart = { \ @@ -913,8 +1048,8 @@ static struct display_driver_api ssd16xx_driver_api = { .len = sizeof(softstart_##n), \ }, -#define SSD16XX_MAKE_INST_ARRAY_OPT(n, p) \ - static uint8_t data_ ## n ## _ ## p[] = DT_INST_PROP_OR(n, p, {}) +#define SSD16XX_MAKE_ARRAY_OPT(n, p) \ + static uint8_t data_ ## n ## _ ## p[] = DT_PROP_OR(n, p, {}) #define SSD16XX_ASSIGN_ARRAY(n, p) \ { \ @@ -922,61 +1057,78 @@ static struct display_driver_api ssd16xx_driver_api = { .len = sizeof(data_ ## n ## _ ## p), \ } -#define SSD16XX_INITIAL_PROFILE_DEFINE(n) \ - SSD16XX_MAKE_INST_ARRAY_OPT(n, lut_initial); \ - SSD16XX_MAKE_INST_ARRAY_OPT(n, gdv); \ - SSD16XX_MAKE_INST_ARRAY_OPT(n, sdv) - -#define SSD16XX_INITIAL_PROFILE(n) \ - { \ - .lut = SSD16XX_ASSIGN_ARRAY(n, lut_initial), \ +#define SSD16XX_PROFILE(n) \ + SSD16XX_MAKE_ARRAY_OPT(n, lut); \ + SSD16XX_MAKE_ARRAY_OPT(n, gdv); \ + SSD16XX_MAKE_ARRAY_OPT(n, sdv); \ + \ + static const struct ssd16xx_profile ssd16xx_profile_ ## n = { \ + .lut = SSD16XX_ASSIGN_ARRAY(n, lut), \ .gdv = SSD16XX_ASSIGN_ARRAY(n, gdv), \ .sdv = SSD16XX_ASSIGN_ARRAY(n, sdv), \ - .vcom = DT_INST_PROP_OR(n, vcom, 0), \ - .override_vcom = DT_INST_NODE_HAS_PROP(n, vcom), \ - .bwf = DT_INST_PROP_OR(n, border_waveform, 0), \ - .override_bwf = DT_INST_NODE_HAS_PROP(n, border_waveform), \ - } - -#define SSD16XX_DEFINE(n) \ - SSD16XX_MAKE_INST_ARRAY_OPT(n, lut_default); \ - SSD16XX_MAKE_INST_ARRAY_OPT(n, softstart); \ - SSD16XX_INITIAL_PROFILE_DEFINE(n); \ + .vcom = DT_PROP_OR(n, vcom, 0), \ + .override_vcom = DT_NODE_HAS_PROP(n, vcom), \ + .bwf = DT_PROP_OR(n, border_waveform, 0), \ + .override_bwf = DT_NODE_HAS_PROP(n, border_waveform), \ + .dummy_line = DT_PROP_OR(n, dummy_line, 0), \ + .override_dummy_line = DT_NODE_HAS_PROP(n, dummy_line), \ + .gate_line_width = DT_PROP_OR(n, gate_line_width, 0), \ + .override_gate_line_width = DT_NODE_HAS_PROP( \ + n, gate_line_width), \ + }; + + +#define _SSD16XX_PROFILE_PTR(n) &ssd16xx_profile_ ## n + +#define SSD16XX_PROFILE_PTR(n) \ + COND_CODE_1(DT_NODE_EXISTS(n), \ + (_SSD16XX_PROFILE_PTR(n)), \ + NULL) + +#define SSD16XX_DEFINE(n, quirks_ptr) \ + SSD16XX_MAKE_ARRAY_OPT(n, softstart); \ + \ + DT_FOREACH_CHILD(n, SSD16XX_PROFILE); \ \ - static const struct ssd16xx_config ssd16xx_cfg_##n = { \ - .bus = SPI_DT_SPEC_INST_GET(n, \ + static const struct ssd16xx_config ssd16xx_cfg_ ## n = { \ + .bus = SPI_DT_SPEC_GET(n, \ SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | \ SPI_HOLD_ON_CS | SPI_LOCK_ON, \ 0), \ - .reset_gpio = GPIO_DT_SPEC_INST_GET(n, reset_gpios), \ - .dc_gpio = GPIO_DT_SPEC_INST_GET(n, dc_gpios), \ - .busy_gpio = GPIO_DT_SPEC_INST_GET(n, busy_gpios), \ - .height = DT_INST_PROP(n, height), \ - .width = DT_INST_PROP(n, width), \ - .orientation = DT_INST_PROP(n, orientation_flipped), \ - .pp_width_bits = DT_INST_PROP(n, pp_width_bits), \ - .pp_height_bits = DT_INST_PROP(n, pp_height_bits), \ - .tssv = DT_INST_PROP_OR(n, tssv, 0), \ + .reset_gpio = GPIO_DT_SPEC_GET(n, reset_gpios), \ + .dc_gpio = GPIO_DT_SPEC_GET(n, dc_gpios), \ + .busy_gpio = GPIO_DT_SPEC_GET(n, busy_gpios), \ + .quirks = quirks_ptr, \ + .height = DT_PROP(n, height), \ + .width = DT_PROP(n, width), \ + .orientation = DT_PROP(n, orientation_flipped), \ + .tssv = DT_PROP_OR(n, tssv, 0), \ .softstart = SSD16XX_ASSIGN_ARRAY(n, softstart), \ - .lut_default = SSD16XX_ASSIGN_ARRAY(n, lut_default), \ - .profile_initial = SSD16XX_INITIAL_PROFILE(n), \ - .dummy_line = DT_INST_PROP_OR(n, dummy_line, 0), \ - .override_dummy_line = \ - DT_INST_NODE_HAS_PROP(n, dummy_line), \ - .gate_line_width = \ - DT_INST_PROP_OR(n, gate_line_width, 0), \ - .override_gate_line_width = \ - DT_INST_NODE_HAS_PROP(n, gate_line_width), \ + .profiles = { \ + [SSD16XX_PROFILE_FULL] = \ + SSD16XX_PROFILE_PTR(DT_CHILD(n, full)), \ + [SSD16XX_PROFILE_PARTIAL] = \ + SSD16XX_PROFILE_PTR(DT_CHILD(n, partial)),\ + }, \ }; \ \ - static struct ssd16xx_data ssd16xx_data_##n; \ + static struct ssd16xx_data ssd16xx_data_ ## n; \ \ - DEVICE_DT_INST_DEFINE(n, ssd16xx_init, NULL, \ - &ssd16xx_data_##n, \ - &ssd16xx_cfg_##n, \ - POST_KERNEL, \ - CONFIG_DISPLAY_INIT_PRIORITY, \ - &ssd16xx_driver_api); \ - - -DT_INST_FOREACH_STATUS_OKAY(SSD16XX_DEFINE) + DEVICE_DT_DEFINE(n, \ + ssd16xx_init, NULL, \ + &ssd16xx_data_ ## n, \ + &ssd16xx_cfg_ ## n, \ + POST_KERNEL, \ + CONFIG_DISPLAY_INIT_PRIORITY, \ + &ssd16xx_driver_api) + +DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1608, SSD16XX_DEFINE, + &quirks_solomon_ssd1608); +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_ssd1680, SSD16XX_DEFINE, + &quirks_solomon_ssd1680); +DT_FOREACH_STATUS_OKAY_VARGS(solomon_ssd1681, SSD16XX_DEFINE, + &quirks_solomon_ssd1681); diff --git a/drivers/display/ssd16xx_regs.h b/drivers/display/ssd16xx_regs.h index 900a824070570..7202109b798cf 100644 --- a/drivers/display/ssd16xx_regs.h +++ b/drivers/display/ssd16xx_regs.h @@ -69,11 +69,15 @@ #define SSD16XX_CTRL2_ENABLE_ANALOG 0x40 #define SSD16XX_CTRL2_LOAD_TEMPERATURE 0x20 #define SSD16XX_CTRL2_LOAD_LUT 0x10 -#define SSD16XX_CTRL2_TO_INITIAL 0x08 -#define SSD16XX_CTRL2_TO_PATTERN 0x04 #define SSD16XX_CTRL2_DISABLE_ANALOG 0x02 #define SSD16XX_CTRL2_DISABLE_CLK 0x01 +#define SSD16XX_GEN1_CTRL2_TO_INITIAL 0x08 +#define SSD16XX_GEN1_CTRL2_TO_PATTERN 0x04 + +#define SSD16XX_GEN2_CTRL2_MODE2 0x08 +#define SSD16XX_GEN2_CTRL2_DISPLAY 0x04 + #define SSD16XX_SLEEP_MODE_DSM 0x01 #define SSD16XX_SLEEP_MODE_PON 0x00 diff --git a/dts/bindings/display/solomon,ssd1608.yaml b/dts/bindings/display/solomon,ssd1608.yaml new file mode 100644 index 0000000000000..56ee04a8eeac8 --- /dev/null +++ b/dts/bindings/display/solomon,ssd1608.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2022 Andreas Sandberg +# SPDX-License-Identifier: Apache-2.0 + +description: Solomon Systech SSD1608 320x240 EPD display controller + +compatible: "solomon,ssd1608" + +include: solomon,ssd16xx-common.yaml diff --git a/dts/bindings/display/solomon,ssd1673.yaml b/dts/bindings/display/solomon,ssd1673.yaml new file mode 100644 index 0000000000000..9183f12bb09fd --- /dev/null +++ b/dts/bindings/display/solomon,ssd1673.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2022 Andreas Sandberg +# SPDX-License-Identifier: Apache-2.0 + +description: Solomon Systech SSD1673 250x150 EPD display controller + +compatible: "solomon,ssd1673" + +include: solomon,ssd16xx-common.yaml diff --git a/dts/bindings/display/solomon,ssd1675a.yaml b/dts/bindings/display/solomon,ssd1675a.yaml new file mode 100644 index 0000000000000..5e04c5c1e4629 --- /dev/null +++ b/dts/bindings/display/solomon,ssd1675a.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2022 Andreas Sandberg +# SPDX-License-Identifier: Apache-2.0 + +description: Solomon Systech SSD1675A 296x160 EPD display controller + +compatible: "solomon,ssd1675a" + +include: solomon,ssd16xx-common.yaml diff --git a/dts/bindings/display/solomon,ssd1680.yaml b/dts/bindings/display/solomon,ssd1680.yaml new file mode 100644 index 0000000000000..f9de5ff2b275b --- /dev/null +++ b/dts/bindings/display/solomon,ssd1680.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2022 Andreas Sandberg +# SPDX-License-Identifier: Apache-2.0 + +description: Solomon Systech SSD1680 296x176 EPD display controller + +compatible: "solomon,ssd1680" + +include: solomon,ssd16xx-common.yaml diff --git a/dts/bindings/display/solomon,ssd1681.yaml b/dts/bindings/display/solomon,ssd1681.yaml new file mode 100644 index 0000000000000..f1458d7a31ab4 --- /dev/null +++ b/dts/bindings/display/solomon,ssd1681.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2022 Andreas Sandberg +# SPDX-License-Identifier: Apache-2.0 + +description: Solomon Systech SSD1681 200x200 EPD display controller + +compatible: "solomon,ssd1681" + +include: solomon,ssd16xx-common.yaml diff --git a/dts/bindings/display/solomon,ssd16xxfb.yaml b/dts/bindings/display/solomon,ssd16xx-common.yaml similarity index 52% rename from dts/bindings/display/solomon,ssd16xxfb.yaml rename to dts/bindings/display/solomon,ssd16xx-common.yaml index 96d8130066c33..dd5b651e8feaf 100644 --- a/dts/bindings/display/solomon,ssd16xxfb.yaml +++ b/dts/bindings/display/solomon,ssd16xx-common.yaml @@ -1,39 +1,11 @@ # Copyright (c) 2018, Phytec Messtechnik GmbH # SPDX-License-Identifier: Apache-2.0 -description: SSD16XX 250x150 EPD display controller - -compatible: "solomon,ssd16xxfb" +description: SSD16XX EPD display controller include: [spi-device.yaml, display-controller.yaml] properties: - pp-height-bits: - type: int - required: true - description: Number of bits used for the height parameters - - pp-width-bits: - type: int - required: true - description: Number of bits used for the width parameters - - gdv: - type: uint8-array - description: Gate driving voltage values - - sdv: - type: uint8-array - description: Source driving voltage values - - vcom: - type: int - description: VCOM voltage - - border-waveform: - type: int - description: Border waveform - softstart: type: uint8-array description: Booster soft start values @@ -69,17 +41,6 @@ properties: If connected directly the MCU pin should be configured as active high. - lut-initial: - type: uint8-array - description: | - Initial LUT used when initializing the device and performing - clearing the screen using a full refresh operation. The - default LUT will be loaded from OTP if this property isn't - defined. - - lut-default: - type: uint8-array - tssv: type: int description: Temperature Sensor Selection Value @@ -88,10 +49,47 @@ properties: an external temperature sensor is connected to the controller. The value selects which sensor should be used. - dummy-line: - type: int - description: Dummy line period override. +child-binding: + description: | + Child nodes describe refresh profiles. Each refresh profile + specifies a set of voltages used to drive the refresh + cycle. Refresh profiles are optional and are used to override + defaults loaded from the controllers OTP memory. - gate-line-width: - type: int - description: Gate line width override. + Partial refresh will be disabled unless a partial refresh profile + has been specified. That profile may be empty to use the defaults + loaded from OTP if supported by the device. + + The driver typically looks for the following child nodes: + + - 'full' - Normal / full refresh. + - 'partial' - Partial refresh. + + properties: + lut: + type: uint8-array + description: LUT associated with this profile. + + gdv: + type: uint8-array + description: Gate driving voltage values + + sdv: + type: uint8-array + description: Source driving voltage values + + vcom: + type: int + description: VCOM voltage + + border-waveform: + type: int + description: Border waveform + + dummy-line: + type: int + description: Dummy line period override. + + gate-line-width: + type: int + description: Gate line width override. diff --git a/samples/boards/reel_board/mesh_badge/src/periphs.c b/samples/boards/reel_board/mesh_badge/src/periphs.c index 3aef5fe6a367f..d4883397c2d28 100644 --- a/samples/boards/reel_board/mesh_badge/src/periphs.c +++ b/samples/boards/reel_board/mesh_badge/src/periphs.c @@ -16,7 +16,7 @@ static const struct device *const dev_info[] = { DEVICE_DT_GET_ONE(ti_hdc1010), DEVICE_DT_GET_ONE(nxp_mma8652fc), DEVICE_DT_GET_ONE(avago_apds9960), - DEVICE_DT_GET_ONE(solomon_ssd16xxfb), + DEVICE_DT_GET(DT_CHOSEN(zephyr_display)), }; int get_hdc1010_val(struct sensor_value *val) diff --git a/samples/boards/reel_board/mesh_badge/src/reel_board.c b/samples/boards/reel_board/mesh_badge/src/reel_board.c index 1e49cd007e2f5..9829a407eea40 100644 --- a/samples/boards/reel_board/mesh_badge/src/reel_board.c +++ b/samples/boards/reel_board/mesh_badge/src/reel_board.c @@ -54,7 +54,7 @@ struct font_info { #define STAT_COUNT 128 -static const struct device *const epd_dev = DEVICE_DT_GET_ONE(solomon_ssd16xxfb); +static const struct device *const epd_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)); static bool pressed; static uint8_t screen_id = SCREEN_MAIN; static struct k_work_delayable epd_work;