From 8ae4576d2e6ea66864987ba20c2061a26a0795e8 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sun, 16 Jul 2023 22:51:26 +0900 Subject: [PATCH 1/5] devicetree: rename and publish DT_COMPAT_ON_BUS_INTERNAL Rename `DT_COMPAT_ON_BUS_INTERNAL` to `DT_HAS_COMPAT_ON_BUS_STATUS_OKAY` to make it a public DT API. It is helpful for code that handles multiple DT_DRV_COMPAT in one file, such as in the following cases. ``` #if (DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(some_sensor, i2c) || \ DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(another_sensor, i2c) ... #endif #define DT_DRV_COMPAT some_sensor DT_INST_FOREACH_STATUS_OKAY(DEFINE_SOME_SENSOR) #undef DT_DRV_COMPAT #define DT_DRV_COMPAT another_sensor DT_INST_FOREACH_STATUS_OKAY(DEFINE_ANOTHER_SENSOR) ``` Signed-off-by: TOKITA Hiroshi --- include/zephyr/devicetree.h | 38 ++++++++++++++++++++++++++--- tests/lib/devicetree/api/src/main.c | 14 +++++------ 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/include/zephyr/devicetree.h b/include/zephyr/devicetree.h index ff0ecb0a33f5..9154d346739b 100644 --- a/include/zephyr/devicetree.h +++ b/include/zephyr/devicetree.h @@ -3888,6 +3888,39 @@ #define DT_INST_STRING_UNQUOTED_OR(inst, name, default_value) \ DT_STRING_UNQUOTED_OR(DT_DRV_INST(inst), name, default_value) +/* + * @brief Test if any enabled node with the given compatible is on + * the given bus type + * + * This is like DT_ANY_INST_ON_BUS_STATUS_OKAY(), except it can also + * be useful for handling multiple compatibles in single source file. + * + * Example devicetree overlay: + * + * @code{.dts} + * &i2c0 { + * temp: temperature-sensor@76 { + * compatible = "vnd,some-sensor"; + * reg = <0x76>; + * }; + * }; + * @endcode + * + * Example usage, assuming `i2c0` is an I2C bus controller node, and + * therefore `temp` is on an I2C bus: + * + * @code{.c} + * DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(vnd_some_sensor, i2c) // 1 + * @endcode + * + * @param compat lowercase-and-underscores compatible, without quotes + * @param bus a binding's bus type as a C token, lowercased and without quotes + * @return 1 if any enabled node with that compatible is on that bus type, + * 0 otherwise + */ +#define DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(compat, bus) \ + IS_ENABLED(UTIL_CAT(DT_CAT(DT_COMPAT_, compat), _BUS_##bus)) + /** * @brief Test if any `DT_DRV_COMPAT` node is on a bus of a given type * and has status okay @@ -3921,7 +3954,7 @@ * 0 otherwise */ #define DT_ANY_INST_ON_BUS_STATUS_OKAY(bus) \ - DT_COMPAT_ON_BUS_INTERNAL(DT_DRV_COMPAT, bus) + DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(DT_DRV_COMPAT, bus) /** * @brief Check if any `DT_DRV_COMPAT` node with status `okay` has a given @@ -4253,9 +4286,6 @@ /** @brief Helper for DT_NODE_HAS_STATUS */ #define DT_NODE_HAS_STATUS_INTERNAL(node_id, status) \ IS_ENABLED(DT_CAT3(node_id, _STATUS_, status)) -/** @brief Helper for test cases and DT_ANY_INST_ON_BUS_STATUS_OKAY() */ -#define DT_COMPAT_ON_BUS_INTERNAL(compat, bus) \ - IS_ENABLED(UTIL_CAT(DT_CAT(DT_COMPAT_, compat), _BUS_##bus)) /** @brief Helper macro to OR multiple has property checks in a loop macro */ #define DT_INST_NODE_HAS_PROP_AND_OR(inst, prop) \ diff --git a/tests/lib/devicetree/api/src/main.c b/tests/lib/devicetree/api/src/main.c index 906907a0ea13..aec48e6a9c9a 100644 --- a/tests/lib/devicetree/api/src/main.c +++ b/tests/lib/devicetree/api/src/main.c @@ -482,18 +482,18 @@ ZTEST(devicetree_api, test_bus) #undef DT_DRV_COMPAT /* - * Make sure the underlying DT_COMPAT_ON_BUS_INTERNAL used by + * Make sure the underlying DT_HAS_COMPAT_ON_BUS_STATUS_OKAY used by * DT_ANY_INST_ON_BUS works without DT_DRV_COMPAT defined. */ - zassert_equal(DT_COMPAT_ON_BUS_INTERNAL(vnd_spi_device, spi), 1); - zassert_equal(DT_COMPAT_ON_BUS_INTERNAL(vnd_spi_device, i2c), 0); + zassert_equal(DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(vnd_spi_device, spi), 1); + zassert_equal(DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(vnd_spi_device, i2c), 0); - zassert_equal(DT_COMPAT_ON_BUS_INTERNAL(vnd_i2c_device, i2c), 1); - zassert_equal(DT_COMPAT_ON_BUS_INTERNAL(vnd_i2c_device, spi), 0); + zassert_equal(DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(vnd_i2c_device, i2c), 1); + zassert_equal(DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(vnd_i2c_device, spi), 0); - zassert_equal(DT_COMPAT_ON_BUS_INTERNAL(vnd_gpio_expander, i2c), 1, + zassert_equal(DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(vnd_gpio_expander, i2c), 1, NULL); - zassert_equal(DT_COMPAT_ON_BUS_INTERNAL(vnd_gpio_expander, spi), 1, + zassert_equal(DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(vnd_gpio_expander, spi), 1, NULL); } From e2f4740bab632b0be5bc661e4e7d07cc7270e5db Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sun, 16 Apr 2023 15:26:54 +0900 Subject: [PATCH 2/5] drivers: display: ssd1306: add dts properties to ssd1306_config Store properties defined in dts in ssd1306_config's fields. And replace code that uses DT_INST_PROP (0, ...) by config properties. Signed-off-by: TOKITA Hiroshi --- drivers/display/ssd1306.c | 83 ++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/drivers/display/ssd1306.c b/drivers/display/ssd1306.c index 489f02c597b0..de237931984d 100644 --- a/drivers/display/ssd1306.c +++ b/drivers/display/ssd1306.c @@ -20,25 +20,6 @@ LOG_MODULE_REGISTER(ssd1306, CONFIG_DISPLAY_LOG_LEVEL); #include "ssd1306_regs.h" -#if DT_INST_PROP(0, segment_remap) == 1 -#define SSD1306_PANEL_SEGMENT_REMAP true -#else -#define SSD1306_PANEL_SEGMENT_REMAP false -#endif - -#if DT_INST_PROP(0, com_invdir) == 1 -#define SSD1306_PANEL_COM_INVDIR true -#else -#define SSD1306_PANEL_COM_INVDIR false -#endif - -#if DT_INST_PROP(0, com_sequential) == 1 -#define SSD1306_COM_PINS_HW_CONFIG SSD1306_SET_PADS_HW_SEQUENTIAL -#else -#define SSD1306_COM_PINS_HW_CONFIG SSD1306_SET_PADS_HW_ALTERNATIVE -#endif - -#define SSD1306_PANEL_NUMOF_PAGES (DT_INST_PROP(0, height) / 8) #define SSD1306_CLOCK_DIV_RATIO 0x0 #define SSD1306_CLOCK_FREQUENCY 0x8 #define SSD1306_PANEL_VCOM_DESEL_LEVEL 0x20 @@ -56,6 +37,16 @@ struct ssd1306_config { struct gpio_dt_spec data_cmd; #endif struct gpio_dt_spec reset; + uint16_t height; + uint16_t width; + uint8_t segment_offset; + uint8_t page_offset; + uint8_t display_offset; + uint8_t multiplex_ratio; + uint8_t prechargep; + bool segment_remap; + bool com_invdir; + bool com_sequential; int ready_time_ms; }; @@ -122,42 +113,40 @@ static inline int ssd1306_write_bus(const struct device *dev, static inline int ssd1306_set_panel_orientation(const struct device *dev) { - uint8_t cmd_buf[] = { - (SSD1306_PANEL_SEGMENT_REMAP ? - SSD1306_SET_SEGMENT_MAP_REMAPED : - SSD1306_SET_SEGMENT_MAP_NORMAL), - (SSD1306_PANEL_COM_INVDIR ? - SSD1306_SET_COM_OUTPUT_SCAN_FLIPPED : - SSD1306_SET_COM_OUTPUT_SCAN_NORMAL) - }; + const struct ssd1306_config *config = dev->config; + uint8_t cmd_buf[] = {(config->segment_remap ? SSD1306_SET_SEGMENT_MAP_REMAPED + : SSD1306_SET_SEGMENT_MAP_NORMAL), + (config->com_invdir ? SSD1306_SET_COM_OUTPUT_SCAN_FLIPPED + : SSD1306_SET_COM_OUTPUT_SCAN_NORMAL)}; return ssd1306_write_bus(dev, cmd_buf, sizeof(cmd_buf), true); } static inline int ssd1306_set_timing_setting(const struct device *dev) { - uint8_t cmd_buf[] = { - SSD1306_SET_CLOCK_DIV_RATIO, - (SSD1306_CLOCK_FREQUENCY << 4) | SSD1306_CLOCK_DIV_RATIO, - SSD1306_SET_CHARGE_PERIOD, - DT_INST_PROP(0, prechargep), - SSD1306_SET_VCOM_DESELECT_LEVEL, - SSD1306_PANEL_VCOM_DESEL_LEVEL - }; + const struct ssd1306_config *config = dev->config; + uint8_t cmd_buf[] = {SSD1306_SET_CLOCK_DIV_RATIO, + (SSD1306_CLOCK_FREQUENCY << 4) | SSD1306_CLOCK_DIV_RATIO, + SSD1306_SET_CHARGE_PERIOD, + config->prechargep, + SSD1306_SET_VCOM_DESELECT_LEVEL, + SSD1306_PANEL_VCOM_DESEL_LEVEL}; return ssd1306_write_bus(dev, cmd_buf, sizeof(cmd_buf), true); } static inline int ssd1306_set_hardware_config(const struct device *dev) { + const struct ssd1306_config *config = dev->config; uint8_t cmd_buf[] = { SSD1306_SET_START_LINE, SSD1306_SET_DISPLAY_OFFSET, - DT_INST_PROP(0, display_offset), + config->display_offset, SSD1306_SET_PADS_HW_CONFIG, - SSD1306_COM_PINS_HW_CONFIG, + (config->com_sequential ? SSD1306_SET_PADS_HW_SEQUENTIAL + : SSD1306_SET_PADS_HW_ALTERNATIVE), SSD1306_SET_MULTIPLEX_RATIO, - DT_INST_PROP(0, multiplex_ratio) + config->multiplex_ratio, }; return ssd1306_write_bus(dev, cmd_buf, sizeof(cmd_buf), true); @@ -248,7 +237,8 @@ static int ssd1306_write(const struct device *dev, const uint16_t x, const uint1 return ssd1306_write_bus(dev, (uint8_t *)buf, buf_len, false); #elif defined(CONFIG_SSD1306_SH1106_COMPATIBLE) - uint8_t x_offset = x + DT_INST_PROP(0, segment_offset); + const struct ssd1306_config *config = dev->config; + uint8_t x_offset = x + config->segment_offset; uint8_t cmd_buf[] = { SSD1306_SET_LOWER_COL_ADDRESS | (x_offset & SSD1306_SET_LOWER_COL_ADDRESS_MASK), @@ -317,9 +307,10 @@ static int ssd1306_set_contrast(const struct device *dev, const uint8_t contrast static void ssd1306_get_capabilities(const struct device *dev, struct display_capabilities *caps) { + const struct ssd1306_config *config = dev->config; memset(caps, 0, sizeof(struct display_capabilities)); - caps->x_resolution = DT_INST_PROP(0, width); - caps->y_resolution = DT_INST_PROP(0, height); + caps->x_resolution = config->width; + caps->y_resolution = config->height; caps->supported_pixel_formats = PIXEL_FORMAT_MONO10; caps->current_pixel_format = PIXEL_FORMAT_MONO10; caps->screen_info = SCREEN_INFO_MONO_VTILED; @@ -438,6 +429,16 @@ static const struct ssd1306_config ssd1306_config = { .data_cmd = GPIO_DT_SPEC_INST_GET(0, data_cmd_gpios), #endif .reset = GPIO_DT_SPEC_INST_GET_OR(0, reset_gpios, { 0 }), + .height = DT_INST_PROP(0, height), + .width = DT_INST_PROP(0, width), + .segment_offset = DT_INST_PROP(0, segment_offset), + .page_offset = DT_INST_PROP(0, page_offset), + .display_offset = DT_INST_PROP(0, display_offset), + .multiplex_ratio = DT_INST_PROP(0, multiplex_ratio), + .segment_remap = DT_INST_PROP(0, segment_remap), + .com_invdir = DT_INST_PROP(0, com_invdir), + .com_sequential = DT_INST_PROP(0, com_sequential), + .prechargep = DT_INST_PROP(0, prechargep), .ready_time_ms = DT_INST_PROP(0, ready_time_ms), }; From c6d030a029a8b8e3b3005cd1547027e3160dc821 Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sun, 16 Apr 2023 15:16:19 +0900 Subject: [PATCH 3/5] drivers: display: ssd1306: replace SSD1306_REVERSE_MODE by property When multiple devices are connected, the SSD1306_REVERSE_MODE setting cannot switch for each device. Add an equivalent setting to the devicetree properties to replace it. Signed-off-by: TOKITA Hiroshi --- drivers/display/Kconfig.ssd1306 | 5 ----- drivers/display/ssd1306.c | 9 ++++----- dts/bindings/display/solomon,ssd1306fb-common.yaml | 4 ++++ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/display/Kconfig.ssd1306 b/drivers/display/Kconfig.ssd1306 index 443e096ed84f..af7992b09b46 100644 --- a/drivers/display/Kconfig.ssd1306 +++ b/drivers/display/Kconfig.ssd1306 @@ -35,9 +35,4 @@ config SSD1306_SH1106_COMPATIBLE endchoice -config SSD1306_REVERSE_MODE - bool "SSD1306 reverse mode" - help - SSD1306 reverse video mode. - endif # SSD1306 diff --git a/drivers/display/ssd1306.c b/drivers/display/ssd1306.c index de237931984d..f0b9fccd5d4a 100644 --- a/drivers/display/ssd1306.c +++ b/drivers/display/ssd1306.c @@ -47,6 +47,7 @@ struct ssd1306_config { bool segment_remap; bool com_invdir; bool com_sequential; + bool color_inversion; int ready_time_ms; }; @@ -340,11 +341,8 @@ static int ssd1306_init_device(const struct device *dev) uint8_t cmd_buf[] = { SSD1306_SET_ENTIRE_DISPLAY_OFF, -#ifdef CONFIG_SSD1306_REVERSE_MODE - SSD1306_SET_REVERSE_DISPLAY, -#else - SSD1306_SET_NORMAL_DISPLAY, -#endif + (config->color_inversion ? SSD1306_SET_REVERSE_DISPLAY + : SSD1306_SET_NORMAL_DISPLAY), }; /* Reset if pin connected */ @@ -439,6 +437,7 @@ static const struct ssd1306_config ssd1306_config = { .com_invdir = DT_INST_PROP(0, com_invdir), .com_sequential = DT_INST_PROP(0, com_sequential), .prechargep = DT_INST_PROP(0, prechargep), + .color_inversion = DT_INST_PROP(0, inversion_on), .ready_time_ms = DT_INST_PROP(0, ready_time_ms), }; diff --git a/dts/bindings/display/solomon,ssd1306fb-common.yaml b/dts/bindings/display/solomon,ssd1306fb-common.yaml index bba993a58c89..c4e82d843248 100644 --- a/dts/bindings/display/solomon,ssd1306fb-common.yaml +++ b/dts/bindings/display/solomon,ssd1306fb-common.yaml @@ -49,6 +49,10 @@ properties: If connected directly the MCU pin should be configured as active low. + inversion-on: + type: boolean + description: Turn on display color inverting + ready-time-ms: type: int default: 10 From c7010abdc2e562e9c04a92efd89c8382758e58ec Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Sun, 16 Apr 2023 15:15:33 +0900 Subject: [PATCH 4/5] drivers: display: ssd1306: determin sh1106 by dts compatibility Determine sh1106 from the `compatibility` value instead of the SSD1306_CONTROLLER_TYPE setting. Change the settings in `boards/shields/ssd1306/sh1106_128x64.overlay` to follow this change. Remove the SSD1306_CONTROLLER_TYPE from its Kconfig.defconfig, and set the `compatibility` to `sinowealth,sh1106`. Signed-off-by: TOKITA Hiroshi --- boards/shields/ssd1306/Kconfig.defconfig | 4 - boards/shields/ssd1306/sh1106_128x64.overlay | 2 +- drivers/display/Kconfig.ssd1306 | 18 +--- drivers/display/ssd1306.c | 95 +++++++++++-------- .../display/sinowealth,sh1106-i2c.yaml | 8 ++ .../display/sinowealth,sh1106-spi.yaml | 14 +++ dts/bindings/vendor-prefixes.txt | 1 + 7 files changed, 82 insertions(+), 60 deletions(-) create mode 100644 dts/bindings/display/sinowealth,sh1106-i2c.yaml create mode 100644 dts/bindings/display/sinowealth,sh1106-spi.yaml diff --git a/boards/shields/ssd1306/Kconfig.defconfig b/boards/shields/ssd1306/Kconfig.defconfig index 960247d2f1d0..3fc5d5a7c051 100644 --- a/boards/shields/ssd1306/Kconfig.defconfig +++ b/boards/shields/ssd1306/Kconfig.defconfig @@ -5,10 +5,6 @@ if SHIELD_SSD1306_128X64 || SHIELD_SSD1306_128X64_SPI || SHIELD_SSD1306_128X32 | if DISPLAY -choice SSD1306_CONTROLLER_TYPE - default SSD1306_SH1106_COMPATIBLE if SHIELD_SH1106_128X64 -endchoice - if LVGL config LV_Z_VDB_SIZE diff --git a/boards/shields/ssd1306/sh1106_128x64.overlay b/boards/shields/ssd1306/sh1106_128x64.overlay index 354b818cbd8d..a9e4e0768ad6 100644 --- a/boards/shields/ssd1306/sh1106_128x64.overlay +++ b/boards/shields/ssd1306/sh1106_128x64.overlay @@ -14,7 +14,7 @@ status = "okay"; sh1106_sh1106_128x64: ssd1306@3c { - compatible = "solomon,ssd1306fb"; + compatible = "sinowealth,sh1106"; reg = <0x3c>; width = <128>; height = <64>; diff --git a/drivers/display/Kconfig.ssd1306 b/drivers/display/Kconfig.ssd1306 index af7992b09b46..32f8b0e1423c 100644 --- a/drivers/display/Kconfig.ssd1306 +++ b/drivers/display/Kconfig.ssd1306 @@ -6,9 +6,11 @@ menuconfig SSD1306 bool "SSD1306 display driver" default y - depends on DT_HAS_SOLOMON_SSD1306FB_ENABLED + depends on DT_HAS_SOLOMON_SSD1306FB_ENABLED || DT_HAS_SINOWEALTH_SH1106_ENABLED select I2C if $(dt_compat_on_bus,$(DT_COMPAT_SOLOMON_SSD1306FB),i2c) select SPI if $(dt_compat_on_bus,$(DT_COMPAT_SOLOMON_SSD1306FB),spi) + select I2C if $(dt_compat_on_bus,$(DT_COMPAT_SINOWEALTH_SH1106),i2c) + select SPI if $(dt_compat_on_bus,$(DT_COMPAT_SINOWEALTH_SH1106),spi) help Enable driver for SSD1306 display driver. @@ -21,18 +23,4 @@ config SSD1306_DEFAULT_CONTRAST help SSD1306 default contrast. -choice SSD1306_CONTROLLER_TYPE - prompt "Display controller type" - default SSD1306_DEFAULT - help - Specify the type of the controller. - -config SSD1306_DEFAULT - bool "Default SSD1306 controller" - -config SSD1306_SH1106_COMPATIBLE - bool "SH1106 compatible mode" - -endchoice - endif # SSD1306 diff --git a/drivers/display/ssd1306.c b/drivers/display/ssd1306.c index f0b9fccd5d4a..95457ff7865b 100644 --- a/drivers/display/ssd1306.c +++ b/drivers/display/ssd1306.c @@ -4,7 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +#if defined(CONFIG_DT_HAS_SOLOMON_SSD1306FB_ENABLED) #define DT_DRV_COMPAT solomon_ssd1306fb +#elif defined(CONFIG_DT_HAS_SINOWEALTH_SH1106_ENABLED) +#define DT_DRV_COMPAT sinowealth_sh1106 +#endif #include LOG_MODULE_REGISTER(ssd1306, CONFIG_DISPLAY_LOG_LEVEL); @@ -48,6 +52,7 @@ struct ssd1306_config { bool com_invdir; bool com_sequential; bool color_inversion; + bool sh1106_compatible; int ready_time_ms; }; @@ -155,15 +160,11 @@ static inline int ssd1306_set_hardware_config(const struct device *dev) static inline int ssd1306_set_charge_pump(const struct device *dev) { + const struct ssd1306_config *config = dev->config; uint8_t cmd_buf[] = { -#if defined(CONFIG_SSD1306_DEFAULT) - SSD1306_SET_CHARGE_PUMP_ON, - SSD1306_SET_CHARGE_PUMP_ON_ENABLED, -#endif -#if defined(CONFIG_SSD1306_SH1106_COMPATIBLE) - SH1106_SET_DCDC_MODE, - SH1106_SET_DCDC_ENABLED, -#endif + (config->sh1106_compatible ? SH1106_SET_DCDC_MODE : SSD1306_SET_CHARGE_PUMP_ON), + (config->sh1106_compatible ? SH1106_SET_DCDC_ENABLED + : SSD1306_SET_CHARGE_PUMP_ON_ENABLED), SSD1306_PANEL_PUMP_VOLTAGE, }; @@ -188,37 +189,10 @@ static int ssd1306_suspend(const struct device *dev) return ssd1306_write_bus(dev, cmd_buf, sizeof(cmd_buf), true); } -static int ssd1306_write(const struct device *dev, const uint16_t x, const uint16_t y, - const struct display_buffer_descriptor *desc, - const void *buf) +static int ssd1306_write_default(const struct device *dev, const uint16_t x, const uint16_t y, + const struct display_buffer_descriptor *desc, const void *buf, + const size_t buf_len) { - size_t buf_len; - - if (desc->pitch < desc->width) { - LOG_ERR("Pitch is smaller then width"); - return -1; - } - - buf_len = MIN(desc->buf_size, desc->height * desc->width / 8); - if (buf == NULL || buf_len == 0U) { - LOG_ERR("Display buffer is not available"); - return -1; - } - - if (desc->pitch > desc->width) { - LOG_ERR("Unsupported mode"); - return -1; - } - - if ((y & 0x7) != 0U) { - LOG_ERR("Unsupported origin"); - return -1; - } - - LOG_DBG("x %u, y %u, pitch %u, width %u, height %u, buf_len %u", - x, y, desc->pitch, desc->width, desc->height, buf_len); - -#if defined(CONFIG_SSD1306_DEFAULT) uint8_t cmd_buf[] = { SSD1306_SET_MEM_ADDRESSING_MODE, SSD1306_ADDRESSING_MODE, @@ -236,8 +210,12 @@ static int ssd1306_write(const struct device *dev, const uint16_t x, const uint1 } return ssd1306_write_bus(dev, (uint8_t *)buf, buf_len, false); +} -#elif defined(CONFIG_SSD1306_SH1106_COMPATIBLE) +static int ssd1306_write_sh1106(const struct device *dev, const uint16_t x, const uint16_t y, + const struct display_buffer_descriptor *desc, const void *buf, + const size_t buf_len) +{ const struct ssd1306_config *config = dev->config; uint8_t x_offset = x + config->segment_offset; uint8_t cmd_buf[] = { @@ -268,11 +246,47 @@ static int ssd1306_write(const struct device *dev, const uint16_t x, const uint1 return -1; } } -#endif return 0; } +static int ssd1306_write(const struct device *dev, const uint16_t x, const uint16_t y, + const struct display_buffer_descriptor *desc, const void *buf) +{ + const struct ssd1306_config *config = dev->config; + size_t buf_len; + + if (desc->pitch < desc->width) { + LOG_ERR("Pitch is smaller then width"); + return -1; + } + + buf_len = MIN(desc->buf_size, desc->height * desc->width / 8); + if (buf == NULL || buf_len == 0U) { + LOG_ERR("Display buffer is not available"); + return -1; + } + + if (desc->pitch > desc->width) { + LOG_ERR("Unsupported mode"); + return -1; + } + + if ((y & 0x7) != 0U) { + LOG_ERR("Unsupported origin"); + return -1; + } + + LOG_DBG("x %u, y %u, pitch %u, width %u, height %u, buf_len %u", x, y, desc->pitch, + desc->width, desc->height, buf_len); + + if (config->sh1106_compatible) { + return ssd1306_write_sh1106(dev, x, y, desc, buf, buf_len); + } + + return ssd1306_write_default(dev, x, y, desc, buf, buf_len); +} + static int ssd1306_read(const struct device *dev, const uint16_t x, const uint16_t y, const struct display_buffer_descriptor *desc, @@ -438,6 +452,7 @@ static const struct ssd1306_config ssd1306_config = { .com_sequential = DT_INST_PROP(0, com_sequential), .prechargep = DT_INST_PROP(0, prechargep), .color_inversion = DT_INST_PROP(0, inversion_on), + .sh1106_compatible = DT_NODE_HAS_COMPAT(0, sinowealth_sh1106), .ready_time_ms = DT_INST_PROP(0, ready_time_ms), }; diff --git a/dts/bindings/display/sinowealth,sh1106-i2c.yaml b/dts/bindings/display/sinowealth,sh1106-i2c.yaml new file mode 100644 index 000000000000..f476f838bbfe --- /dev/null +++ b/dts/bindings/display/sinowealth,sh1106-i2c.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2023, TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +description: SH1106 128x64 dot-matrix display controller on I2C bus + +compatible: "sinowealth,sh1106" + +include: ["solomon,ssd1306fb-common.yaml", "i2c-device.yaml"] diff --git a/dts/bindings/display/sinowealth,sh1106-spi.yaml b/dts/bindings/display/sinowealth,sh1106-spi.yaml new file mode 100644 index 000000000000..5fe3a2cf0f57 --- /dev/null +++ b/dts/bindings/display/sinowealth,sh1106-spi.yaml @@ -0,0 +1,14 @@ +# Copyright (c) 2023, TOKITA Hiroshi +# SPDX-License-Identifier: Apache-2.0 + +description: SH1106 128x64 dot-matrix display controller on SPI bus + +compatible: "sinowealth,sh1106" + +include: ["solomon,ssd1306fb-common.yaml", "spi-device.yaml"] + +properties: + data_cmd-gpios: + type: phandle-array + required: true + description: D/C# pin. diff --git a/dts/bindings/vendor-prefixes.txt b/dts/bindings/vendor-prefixes.txt index a2af2b9249ad..4b2ab91c73e1 100644 --- a/dts/bindings/vendor-prefixes.txt +++ b/dts/bindings/vendor-prefixes.txt @@ -563,6 +563,7 @@ simcom SIMCom Wireless Solutions Co., LTD simtek Cypress Semiconductor Corporation (Simtek Corporation) sinlinx Sinlinx Electronics Technology Co., LTD sinovoip SinoVoip Co., Ltd +sinowealth Sino Wealth Electronic Ltd sipeed Shenzhen Sipeed Technology Co., Ltd. sirf SiRF Technology, Inc. sis Silicon Integrated Systems Corp. From e6ece74f1424d13d5c9739d829023c8dab37eebe Mon Sep 17 00:00:00 2001 From: TOKITA Hiroshi Date: Fri, 14 Apr 2023 01:27:19 +0900 Subject: [PATCH 5/5] drivers: display: ssd1306: Support connecting SPI and I2C at the same Support connecting different display for each SPI and I2C at the same time. In a case like DTS below. ``` &spi1 { ssd1306_spi: ssd1306@0 { compatible = "solomon,ssd1306fb"; ... }; }; &i2c0 { ssd1306_i2c: ssd1306@3c { compatible = "solomon,ssd1306fb"; ... }; }; ``` Signed-off-by: TOKITA Hiroshi --- drivers/display/ssd1306.c | 152 ++++++++++++++++++++++++-------------- 1 file changed, 97 insertions(+), 55 deletions(-) diff --git a/drivers/display/ssd1306.c b/drivers/display/ssd1306.c index 95457ff7865b..0f17293a70f3 100644 --- a/drivers/display/ssd1306.c +++ b/drivers/display/ssd1306.c @@ -4,12 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#if defined(CONFIG_DT_HAS_SOLOMON_SSD1306FB_ENABLED) -#define DT_DRV_COMPAT solomon_ssd1306fb -#elif defined(CONFIG_DT_HAS_SINOWEALTH_SH1106_ENABLED) -#define DT_DRV_COMPAT sinowealth_sh1106 -#endif - #include LOG_MODULE_REGISTER(ssd1306, CONFIG_DISPLAY_LOG_LEVEL); @@ -33,14 +27,23 @@ LOG_MODULE_REGISTER(ssd1306, CONFIG_DISPLAY_LOG_LEVEL); #define SSD1306_ADDRESSING_MODE (SSD1306_SET_MEM_ADDRESSING_HORIZONTAL) #endif +union ssd1306_bus { + struct i2c_dt_spec i2c; + struct spi_dt_spec spi; +}; + +typedef bool (*ssd1306_bus_ready_fn)(const struct device *dev); +typedef int (*ssd1306_write_bus_fn)(const struct device *dev, uint8_t *buf, size_t len, + bool command); +typedef const char *(*ssd1306_bus_name_fn)(const struct device *dev); + struct ssd1306_config { -#if DT_INST_ON_BUS(0, i2c) - struct i2c_dt_spec bus; -#elif DT_INST_ON_BUS(0, spi) - struct spi_dt_spec bus; + union ssd1306_bus bus; struct gpio_dt_spec data_cmd; -#endif struct gpio_dt_spec reset; + ssd1306_bus_ready_fn bus_ready; + ssd1306_write_bus_fn write_bus; + ssd1306_bus_name_fn bus_name; uint16_t height; uint16_t width; uint8_t segment_offset; @@ -61,29 +64,36 @@ struct ssd1306_data { uint8_t scan_mode; }; -#if DT_INST_ON_BUS(0, i2c) - -static inline bool ssd1306_bus_ready(const struct device *dev) +#if (DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(solomon_ssd1306fb, i2c) || \ + DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(sinowealth_sh1106, i2c)) +static bool ssd1306_bus_ready_i2c(const struct device *dev) { const struct ssd1306_config *config = dev->config; - return device_is_ready(config->bus.bus); + return i2c_is_ready_dt(&config->bus.i2c); } -static inline int ssd1306_write_bus(const struct device *dev, - uint8_t *buf, size_t len, bool command) +static int ssd1306_write_bus_i2c(const struct device *dev, uint8_t *buf, size_t len, bool command) { const struct ssd1306_config *config = dev->config; - return i2c_burst_write_dt(&config->bus, + return i2c_burst_write_dt(&config->bus.i2c, command ? SSD1306_CONTROL_ALL_BYTES_CMD : SSD1306_CONTROL_ALL_BYTES_DATA, buf, len); } -#elif DT_INST_ON_BUS(0, spi) +static const char *ssd1306_bus_name_i2c(const struct device *dev) +{ + const struct ssd1306_config *config = dev->config; -static inline bool ssd1306_bus_ready(const struct device *dev) + return config->bus.i2c.bus->name; +} +#endif + +#if (DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(solomon_ssd1306fb, spi) || \ + DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(sinowealth_sh1106, spi)) +static bool ssd1306_bus_ready_spi(const struct device *dev) { const struct ssd1306_config *config = dev->config; @@ -91,11 +101,10 @@ static inline bool ssd1306_bus_ready(const struct device *dev) return false; } - return spi_is_ready_dt(&config->bus); + return spi_is_ready_dt(&config->bus.spi); } -static inline int ssd1306_write_bus(const struct device *dev, - uint8_t *buf, size_t len, bool command) +static int ssd1306_write_bus_spi(const struct device *dev, uint8_t *buf, size_t len, bool command) { const struct ssd1306_config *config = dev->config; int errno; @@ -111,12 +120,34 @@ static inline int ssd1306_write_bus(const struct device *dev, .count = 1 }; - errno = spi_write_dt(&config->bus, &tx_bufs); + errno = spi_write_dt(&config->bus.spi, &tx_bufs); return errno; } + +static const char *ssd1306_bus_name_spi(const struct device *dev) +{ + const struct ssd1306_config *config = dev->config; + + return config->bus.spi.bus->name; +} #endif +static inline bool ssd1306_bus_ready(const struct device *dev) +{ + const struct ssd1306_config *config = dev->config; + + return config->bus_ready(dev); +} + +static inline int ssd1306_write_bus(const struct device *dev, uint8_t *buf, size_t len, + bool command) +{ + const struct ssd1306_config *config = dev->config; + + return config->write_bus(dev, buf, len, command); +} + static inline int ssd1306_set_panel_orientation(const struct device *dev) { const struct ssd1306_config *config = dev->config; @@ -410,7 +441,7 @@ static int ssd1306_init(const struct device *dev) k_sleep(K_TIMEOUT_ABS_MS(config->ready_time_ms)); if (!ssd1306_bus_ready(dev)) { - LOG_ERR("Bus device %s not ready!", config->bus.bus->name); + LOG_ERR("Bus device %s not ready!", config->bus_name(dev)); return -EINVAL; } @@ -432,32 +463,6 @@ static int ssd1306_init(const struct device *dev) return 0; } -static const struct ssd1306_config ssd1306_config = { -#if DT_INST_ON_BUS(0, i2c) - .bus = I2C_DT_SPEC_INST_GET(0), -#elif DT_INST_ON_BUS(0, spi) - .bus = SPI_DT_SPEC_INST_GET( - 0, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 0), - .data_cmd = GPIO_DT_SPEC_INST_GET(0, data_cmd_gpios), -#endif - .reset = GPIO_DT_SPEC_INST_GET_OR(0, reset_gpios, { 0 }), - .height = DT_INST_PROP(0, height), - .width = DT_INST_PROP(0, width), - .segment_offset = DT_INST_PROP(0, segment_offset), - .page_offset = DT_INST_PROP(0, page_offset), - .display_offset = DT_INST_PROP(0, display_offset), - .multiplex_ratio = DT_INST_PROP(0, multiplex_ratio), - .segment_remap = DT_INST_PROP(0, segment_remap), - .com_invdir = DT_INST_PROP(0, com_invdir), - .com_sequential = DT_INST_PROP(0, com_sequential), - .prechargep = DT_INST_PROP(0, prechargep), - .color_inversion = DT_INST_PROP(0, inversion_on), - .sh1106_compatible = DT_NODE_HAS_COMPAT(0, sinowealth_sh1106), - .ready_time_ms = DT_INST_PROP(0, ready_time_ms), -}; - -static struct ssd1306_data ssd1306_driver; - static struct display_driver_api ssd1306_driver_api = { .blanking_on = ssd1306_suspend, .blanking_off = ssd1306_resume, @@ -471,7 +476,44 @@ static struct display_driver_api ssd1306_driver_api = { .set_orientation = ssd1306_set_orientation, }; -DEVICE_DT_INST_DEFINE(0, ssd1306_init, NULL, - &ssd1306_driver, &ssd1306_config, - POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY, - &ssd1306_driver_api); +#define SSD1306_CONFIG_SPI(node_id) \ + .bus = {.spi = SPI_DT_SPEC_GET( \ + node_id, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8), 0)}, \ + .bus_ready = ssd1306_bus_ready_spi, \ + .write_bus = ssd1306_write_bus_spi, \ + .bus_name = ssd1306_bus_name_spi, \ + .data_cmd = GPIO_DT_SPEC_GET(node_id, data_cmd_gpios), + +#define SSD1306_CONFIG_I2C(node_id) \ + .bus = {.i2c = I2C_DT_SPEC_GET(node_id)}, \ + .bus_ready = ssd1306_bus_ready_i2c, \ + .write_bus = ssd1306_write_bus_i2c, \ + .bus_name = ssd1306_bus_name_i2c, \ + .data_cmd = {0}, + +#define SSD1306_DEFINE(node_id) \ + static struct ssd1306_data data##node_id; \ + static const struct ssd1306_config config##node_id = { \ + .reset = GPIO_DT_SPEC_GET_OR(node_id, reset_gpios, {0}), \ + .height = DT_PROP(node_id, height), \ + .width = DT_PROP(node_id, width), \ + .segment_offset = DT_PROP(node_id, segment_offset), \ + .page_offset = DT_PROP(node_id, page_offset), \ + .display_offset = DT_PROP(node_id, display_offset), \ + .multiplex_ratio = DT_PROP(node_id, multiplex_ratio), \ + .segment_remap = DT_PROP(node_id, segment_remap), \ + .com_invdir = DT_PROP(node_id, com_invdir), \ + .com_sequential = DT_PROP(node_id, com_sequential), \ + .prechargep = DT_PROP(node_id, prechargep), \ + .color_inversion = DT_PROP(node_id, inversion_on), \ + .sh1106_compatible = DT_NODE_HAS_COMPAT(node_id, sinowealth_sh1106), \ + .ready_time_ms = DT_PROP(node_id, ready_time_ms), \ + COND_CODE_1(DT_ON_BUS(node_id, spi), (SSD1306_CONFIG_SPI(node_id)), \ + (SSD1306_CONFIG_I2C(node_id))) \ + }; \ + \ + DEVICE_DT_DEFINE(node_id, ssd1306_init, NULL, &data##node_id, &config##node_id, \ + POST_KERNEL, CONFIG_DISPLAY_INIT_PRIORITY, &ssd1306_driver_api); + +DT_FOREACH_STATUS_OKAY(solomon_ssd1306fb, SSD1306_DEFINE) +DT_FOREACH_STATUS_OKAY(sinowealth_sh1106, SSD1306_DEFINE)