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 443e096ed84f..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,23 +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 - -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 489f02c597b0..0f17293a70f3 100644 --- a/drivers/display/ssd1306.c +++ b/drivers/display/ssd1306.c @@ -4,8 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#define DT_DRV_COMPAT solomon_ssd1306fb - #include LOG_MODULE_REGISTER(ssd1306, CONFIG_DISPLAY_LOG_LEVEL); @@ -20,25 +18,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 @@ -48,14 +27,35 @@ 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; + uint8_t page_offset; + uint8_t display_offset; + uint8_t multiplex_ratio; + uint8_t prechargep; + bool segment_remap; + bool com_invdir; + bool com_sequential; + bool color_inversion; + bool sh1106_compatible; int ready_time_ms; }; @@ -64,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; + + return config->bus.i2c.bus->name; +} +#endif -static inline bool ssd1306_bus_ready(const struct device *dev) +#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; @@ -94,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; @@ -114,50 +120,70 @@ 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) { - 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); @@ -165,15 +191,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, }; @@ -198,37 +220,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, @@ -246,9 +241,14 @@ 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); +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[] = { SSD1306_SET_LOWER_COL_ADDRESS | (x_offset & SSD1306_SET_LOWER_COL_ADDRESS_MASK), @@ -277,11 +277,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, @@ -317,9 +353,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; @@ -349,11 +386,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 */ @@ -407,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; } @@ -429,20 +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 }), - .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, @@ -456,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) 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/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 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. 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); }