diff --git a/boards/arduino/giga_r1/arduino_giga_r1_stm32h747xx_m7.yaml b/boards/arduino/giga_r1/arduino_giga_r1_stm32h747xx_m7.yaml index 285330d8b011c..53bb6d3bd7049 100644 --- a/boards/arduino/giga_r1/arduino_giga_r1_stm32h747xx_m7.yaml +++ b/boards/arduino/giga_r1/arduino_giga_r1_stm32h747xx_m7.yaml @@ -12,6 +12,7 @@ supported: - gpio - arduino_spi - spi + - i2c - memc - usb_device vendor: arduino diff --git a/boards/shields/arduino_giga_display_shield/boards/arduino_giga_r1_m7.overlay b/boards/shields/arduino_giga_display_shield/boards/arduino_giga_r1_m7.overlay index 5c9ce42074458..464e37e8528cb 100644 --- a/boards/shields/arduino_giga_display_shield/boards/arduino_giga_r1_m7.overlay +++ b/boards/shields/arduino_giga_display_shield/boards/arduino_giga_r1_m7.overlay @@ -5,6 +5,7 @@ */ #include +#include / { lvgl_pointer { @@ -93,4 +94,15 @@ reset-gpios = <&gpioi 2 GPIO_ACTIVE_LOW>; irq-gpios = <&gpioi 1 GPIO_ACTIVE_HIGH>; }; + is31fl3197@50 { + compatible = "issi,is31fl3197"; + reg = <0x50>; + + led_rgb { + label = "RGB LED"; + color-mapping = , + , + ; + }; + }; }; diff --git a/doc/_scripts/redirects.py b/doc/_scripts/redirects.py index fbc7afd4131b2..5c4255fdedd6e 100644 --- a/doc/_scripts/redirects.py +++ b/doc/_scripts/redirects.py @@ -292,7 +292,7 @@ ('samples/boards/stm32/steval_stwinbx1/sensors/README', 'samples/boards/st/steval_stwinbx1/sensors/README'), ('samples/drivers/adc/README', 'samples/drivers/adc/adc_dt/README'), ('samples/drivers/led_apa102/README', 'samples/drivers/led_strip/README'), - ('samples/drivers/led_is31fl3194/README', 'samples/drivers/led/is31fl3194/README'), + ('samples/drivers/led_is31fl319x/README', 'samples/drivers/led/is31fl319x/README'), ('samples/drivers/led_is31fl3216a/README', 'samples/drivers/led/is31fl3216a/README'), ('samples/drivers/led_is31fl3733/README', 'samples/drivers/led/is31fl3733/README'), ('samples/drivers/led_lp3943/README', 'samples/drivers/led/lp3943/README'), diff --git a/drivers/led/CMakeLists.txt b/drivers/led/CMakeLists.txt index 9a53ac7c2827e..708f441580d20 100644 --- a/drivers/led/CMakeLists.txt +++ b/drivers/led/CMakeLists.txt @@ -6,7 +6,7 @@ zephyr_library() # zephyr-keep-sorted-start zephyr_library_sources_ifdef(CONFIG_HT16K33 ht16k33.c) -zephyr_library_sources_ifdef(CONFIG_IS31FL3194 is31fl3194.c) +zephyr_library_sources_ifdef(CONFIG_IS31FL319X is31fl319x.c) zephyr_library_sources_ifdef(CONFIG_IS31FL3216A is31fl3216a.c) zephyr_library_sources_ifdef(CONFIG_IS31FL3733 is31fl3733.c) zephyr_library_sources_ifdef(CONFIG_LEDS_GROUP_MULTICOLOR leds_group_multicolor.c) diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index f6809fc7c9dd7..098b3e9e32b6b 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -31,7 +31,7 @@ source "drivers/led/Kconfig.axp192" source "drivers/led/Kconfig.dac" source "drivers/led/Kconfig.gpio" source "drivers/led/Kconfig.ht16k33" -source "drivers/led/Kconfig.is31fl3194" +source "drivers/led/Kconfig.is31fl319x" source "drivers/led/Kconfig.is31fl3216a" source "drivers/led/Kconfig.is31fl3733" source "drivers/led/Kconfig.leds-group-multicolor" diff --git a/drivers/led/Kconfig.is31fl3197 b/drivers/led/Kconfig.is31fl3197 new file mode 100644 index 0000000000000..f1cb8357e4c89 --- /dev/null +++ b/drivers/led/Kconfig.is31fl3197 @@ -0,0 +1,11 @@ +# Copyright (c) 2024 Arduino SA +# SPDX-License-Identifier: Apache-2.0 + +config IS31FL3197 + bool "IS31FL3197 LED driver" + default y + depends on DT_HAS_ISSI_IS31FL3197_ENABLED + select I2C + help + Enable LED driver for Lumissil Microsystems (a division of ISSI) + IS31FL3197. This chip supports one RGB LED or 4 independent LEDs. diff --git a/drivers/led/Kconfig.is31fl3194 b/drivers/led/Kconfig.is31fl319x similarity index 58% rename from drivers/led/Kconfig.is31fl3194 rename to drivers/led/Kconfig.is31fl319x index 42166805a1d16..5c3d48bba7cb7 100644 --- a/drivers/led/Kconfig.is31fl3194 +++ b/drivers/led/Kconfig.is31fl319x @@ -1,11 +1,12 @@ # Copyright (c) 2024 Arduino SA # SPDX-License-Identifier: Apache-2.0 -config IS31FL3194 - bool "IS31FL3194 LED driver" +config IS31FL319X + bool "IS31FL319X LED driver" default y - depends on DT_HAS_ISSI_IS31FL3194_ENABLED + depends on DT_HAS_ISSI_IS31FL3194_ENABLED || DT_HAS_ISSI_IS31FL3197_ENABLED select I2C help Enable LED driver for Lumissil Microsystems (a division of ISSI) IS31FL3194. This chip supports one RGB LED or 3 independent LEDs. + IS31FL3197. This chip supports 4 LEDs. diff --git a/drivers/led/is31fl3194.c b/drivers/led/is31fl3194.c deleted file mode 100644 index 6ae0d6ab44db8..0000000000000 --- a/drivers/led/is31fl3194.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright (c) 2024 Arduino SA - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#define DT_DRV_COMPAT issi_is31fl3194 - -/** - * @file - * @brief IS31FL3194 LED driver - * - * The IS31FL3194 is a 3-channel LED driver that communicates over I2C. - */ - -#include -#include -#include -#include -#include - -#include - -LOG_MODULE_REGISTER(is31fl3194, CONFIG_LED_LOG_LEVEL); - -#define IS31FL3194_PROD_ID_REG 0x00 -#define IS31FL3194_CONF_REG 0x01 -#define IS31FL3194_CURRENT_REG 0x03 -#define IS31FL3194_OUT1_REG 0x10 -#define IS31FL3194_OUT2_REG 0x21 -#define IS31FL3194_OUT3_REG 0x32 -#define IS31FL3194_UPDATE_REG 0x40 - -#define IS31FL3194_PROD_ID_VAL 0xce -#define IS31FL3194_CONF_ENABLE 0x01 -#define IS31FL3194_UPDATE_VAL 0xc5 - -#define IS31FL3194_CHANNEL_COUNT 3 - -static const uint8_t led_channels[] = { - IS31FL3194_OUT1_REG, - IS31FL3194_OUT2_REG, - IS31FL3194_OUT3_REG -}; - -struct is31fl3194_config { - struct i2c_dt_spec bus; - uint8_t num_leds; - const struct led_info *led_infos; - const uint8_t *current_limits; -}; - -static const struct led_info *is31fl3194_led_to_info(const struct is31fl3194_config *config, - uint32_t led) -{ - if (led < config->num_leds) { - return &config->led_infos[led]; - } - - return NULL; -} - -static int is31fl3194_get_info(const struct device *dev, - uint32_t led, - const struct led_info **info_out) -{ - const struct is31fl3194_config *config = dev->config; - const struct led_info *info = is31fl3194_led_to_info(config, led); - - if (info == NULL) { - return -EINVAL; - } - - *info_out = info; - return 0; -} - -static int is31fl3194_set_color(const struct device *dev, uint32_t led, uint8_t num_colors, - const uint8_t *color) -{ - const struct is31fl3194_config *config = dev->config; - const struct led_info *info = is31fl3194_led_to_info(config, led); - int ret; - - if (info == NULL) { - return -ENODEV; - } - - if (info->num_colors != 3) { - return -ENOTSUP; - } - - if (num_colors != 3) { - return -EINVAL; - } - - for (int i = 0; i < 3; i++) { - uint8_t value; - - switch (info->color_mapping[i]) { - case LED_COLOR_ID_RED: - value = color[0]; - break; - case LED_COLOR_ID_GREEN: - value = color[1]; - break; - case LED_COLOR_ID_BLUE: - value = color[2]; - break; - default: - /* unreachable: mapping already tested in is31fl3194_check_config */ - return -EINVAL; - } - - ret = i2c_reg_write_byte_dt(&config->bus, led_channels[i], value); - if (ret != 0) { - break; - } - } - - if (ret == 0) { - ret = i2c_reg_write_byte_dt(&config->bus, - IS31FL3194_UPDATE_REG, - IS31FL3194_UPDATE_VAL); - } - - if (ret != 0) { - LOG_ERR("%s: LED write failed: %d", dev->name, ret); - } - - return ret; -} - -static int is31fl3194_set_brightness(const struct device *dev, uint32_t led, uint8_t value) -{ - const struct is31fl3194_config *config = dev->config; - const struct led_info *info = is31fl3194_led_to_info(config, led); - int ret = 0; - - if (info == NULL) { - return -ENODEV; - } - - if (info->num_colors != 1) { - return -ENOTSUP; - } - - /* Rescale 0..100 to 0..255 */ - value = value * 255 / LED_BRIGHTNESS_MAX; - - ret = i2c_reg_write_byte_dt(&config->bus, led_channels[led], value); - if (ret == 0) { - ret = i2c_reg_write_byte_dt(&config->bus, - IS31FL3194_UPDATE_REG, - IS31FL3194_UPDATE_VAL); - } - - if (ret != 0) { - LOG_ERR("%s: LED write failed", dev->name); - } - - return ret; -} - -/* - * Counts red, green, blue channels; returns true if color_id is valid - * and no more than one channel maps to the same color - */ -static bool is31fl3194_count_colors(const struct device *dev, - uint8_t color_id, uint8_t *rgb_counts) -{ - bool ret = false; - - switch (color_id) { - case LED_COLOR_ID_RED: - ret = (++rgb_counts[0] == 1); - break; - case LED_COLOR_ID_GREEN: - ret = (++rgb_counts[1] == 1); - break; - case LED_COLOR_ID_BLUE: - ret = (++rgb_counts[2] == 1); - break; - } - - if (!ret) { - LOG_ERR("%s: invalid color %d (duplicate or not RGB)", - dev->name, color_id); - } - - return ret; -} - -static int is31fl3194_check_config(const struct device *dev) -{ - const struct is31fl3194_config *config = dev->config; - const struct led_info *info; - uint8_t rgb_counts[3] = { 0 }; - uint8_t i; - - switch (config->num_leds) { - case 1: - /* check that it is a three-channel LED */ - info = &config->led_infos[0]; - - if (info->num_colors != 3) { - LOG_ERR("%s: invalid number of colors %d " - "(must be 3 for RGB LED)", - dev->name, info->num_colors); - return -EINVAL; - } - - for (i = 0; i < 3; i++) { - if (!is31fl3194_count_colors(dev, info->color_mapping[i], rgb_counts)) { - return -EINVAL; - } - - } - break; - case 3: - /* check that each LED is single-color */ - for (i = 0; i < 3; i++) { - info = &config->led_infos[i]; - - if (info->num_colors != 1) { - LOG_ERR("%s: invalid number of colors %d " - "(must be 1 when defining multiple LEDs)", - dev->name, info->num_colors); - return -EINVAL; - } - - if (!is31fl3194_count_colors(dev, info->color_mapping[0], rgb_counts)) { - return -EINVAL; - } - } - break; - default: - LOG_ERR("%s: invalid number of LEDs %d (must be 1 or 3)", - dev->name, config->num_leds); - return -EINVAL; - } - - return 0; -} - -static int is31fl3194_init(const struct device *dev) -{ - const struct is31fl3194_config *config = dev->config; - const struct led_info *info = NULL; - int i, ret; - uint8_t prod_id, band; - uint8_t current_reg = 0; - - ret = is31fl3194_check_config(dev); - if (ret != 0) { - return ret; - } - - if (!i2c_is_ready_dt(&config->bus)) { - LOG_ERR("%s: I2C device not ready", dev->name); - return -ENODEV; - } - - ret = i2c_reg_read_byte_dt(&config->bus, IS31FL3194_PROD_ID_REG, &prod_id); - if (ret != 0) { - LOG_ERR("%s: failed to read product ID", dev->name); - return ret; - } - - if (prod_id != IS31FL3194_PROD_ID_VAL) { - LOG_ERR("%s: invalid product ID 0x%02x (expected 0x%02x)", dev->name, prod_id, - IS31FL3194_PROD_ID_VAL); - return -ENODEV; - } - - /* calc current limit register value */ - info = &config->led_infos[0]; - if (info->num_colors == IS31FL3194_CHANNEL_COUNT) { - /* one RGB LED: set all channels to the same current limit */ - band = (config->current_limits[0] / 10) - 1; - for (i = 0; i < IS31FL3194_CHANNEL_COUNT; i++) { - current_reg |= band << (2 * i); - } - } else { - /* single-channel LEDs: independent limits */ - for (i = 0; i < config->num_leds; i++) { - band = (config->current_limits[i] / 10) - 1; - current_reg |= band << (2 * i); - } - } - - ret = i2c_reg_write_byte_dt(&config->bus, IS31FL3194_CURRENT_REG, current_reg); - if (ret != 0) { - LOG_ERR("%s: failed to set current limit", dev->name); - return ret; - } - - /* enable device */ - return i2c_reg_write_byte_dt(&config->bus, IS31FL3194_CONF_REG, IS31FL3194_CONF_ENABLE); -} - -static DEVICE_API(led, is31fl3194_led_api) = { - .set_brightness = is31fl3194_set_brightness, - .get_info = is31fl3194_get_info, - .set_color = is31fl3194_set_color, -}; - -#define COLOR_MAPPING(led_node_id) \ - static const uint8_t color_mapping_##led_node_id[] = \ - DT_PROP(led_node_id, color_mapping); - -#define LED_INFO(led_node_id) \ - { \ - .label = DT_PROP(led_node_id, label), \ - .num_colors = DT_PROP_LEN(led_node_id, color_mapping), \ - .color_mapping = color_mapping_##led_node_id, \ - }, - -#define LED_CURRENT(led_node_id) \ - DT_PROP(led_node_id, current_limit), - -#define IS31FL3194_DEFINE(id) \ - \ - DT_INST_FOREACH_CHILD(id, COLOR_MAPPING) \ - \ - static const struct led_info is31fl3194_leds_##id[] = \ - { DT_INST_FOREACH_CHILD(id, LED_INFO) }; \ - static const uint8_t is31fl3194_currents_##id[] = \ - { DT_INST_FOREACH_CHILD(id, LED_CURRENT) }; \ - BUILD_ASSERT(ARRAY_SIZE(is31fl3194_leds_##id) > 0, \ - "No LEDs defined for " #id); \ - \ - static const struct is31fl3194_config is31fl3194_config_##id = { \ - .bus = I2C_DT_SPEC_INST_GET(id), \ - .num_leds = ARRAY_SIZE(is31fl3194_leds_##id), \ - .led_infos = is31fl3194_leds_##id, \ - .current_limits = is31fl3194_currents_##id, \ - }; \ - DEVICE_DT_INST_DEFINE(id, &is31fl3194_init, NULL, NULL, \ - &is31fl3194_config_##id, POST_KERNEL, \ - CONFIG_LED_INIT_PRIORITY, &is31fl3194_led_api); - -DT_INST_FOREACH_STATUS_OKAY(IS31FL3194_DEFINE) diff --git a/drivers/led/is31fl319x.c b/drivers/led/is31fl319x.c new file mode 100644 index 0000000000000..02bc211ff8d25 --- /dev/null +++ b/drivers/led/is31fl319x.c @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +/** + * @file + * @brief IS31FL3194 LED driver + * + * The IS31FL3194 is a 3-channel LED driver that communicates over I2C. + */ + +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(is31fl319x, CONFIG_LED_LOG_LEVEL); + +/* define features that are specific subset of supported devices */ +#define REG_NOT_DEFINED 0xff + +#define FEATURE_ID_IS_ADDR 0x01 /* The id is the bus address */ +#define FEATURE_SET_CURRENT 0x02 /* the device supports setting current limits */ + +struct is31f1319x_model { + const uint8_t features; + const uint8_t prod_id_reg; + const uint8_t shutdown_reg; + const uint8_t conf_reg; + const uint8_t current_reg; + const uint8_t update_reg; + + const uint8_t prod_id_val; + const uint8_t shutdown_reg_val; + const uint8_t conf_enable; + const uint8_t update_val; + + const uint8_t led_channels[]; +}; + +struct is31fl319x_config { + struct i2c_dt_spec bus; + uint8_t channel_count; + uint8_t num_leds; + const struct led_info *led_infos; + const uint8_t *current_limits; + const struct is31f1319x_model *model; +}; + +#ifdef CONFIG_DT_HAS_ISSI_IS31FL3194_ENABLED +/* IS31FL3194 model registers and values */ +#define IS31FL3194_PROD_ID_REG 0x00 +#define IS31FL3194_CONF_REG 0x01 +#define IS31FL3194_CURRENT_REG 0x03 +#define IS31FL3194_OUT1_REG 0x10 +#define IS31FL3194_OUT2_REG 0x21 +#define IS31FL3194_OUT3_REG 0x32 +#define IS31FL3194_UPDATE_REG 0x40 + +#define IS31FL3194_PROD_ID_VAL 0xce +#define IS31FL3194_CONF_ENABLE 0x01 +#define IS31FL3194_UPDATE_VAL 0xc5 + +#define IS31FL3194_CHANNEL_COUNT 3 + +static const struct is31f1319x_model is31f13194_model = { + .features = FEATURE_SET_CURRENT, + + /* register indexes */ + .prod_id_reg = IS31FL3194_PROD_ID_REG, + .shutdown_reg = REG_NOT_DEFINED, + .conf_reg = IS31FL3194_CONF_REG, + .current_reg = IS31FL3194_CURRENT_REG, + .update_reg = IS31FL3194_UPDATE_REG, + + /* values for those registers */ + .prod_id_val = IS31FL3194_PROD_ID_VAL, + .shutdown_reg_val = 0, + .conf_enable = IS31FL3194_CONF_ENABLE, + .update_val = IS31FL3194_UPDATE_VAL, + + /* channel output registers */ + .led_channels = {IS31FL3194_OUT1_REG, IS31FL3194_OUT2_REG, IS31FL3194_OUT3_REG}}; +#endif + +#ifdef CONFIG_DT_HAS_ISSI_IS31FL3197_ENABLED +/* IS31FL3197 model registers and values */ +#define IS31FL3197_PROD_ID_REG 0x00 +#define IS31FL3197_SHUTDOWN_REG 0x01 +#define IS31FL3197_OPER_CONFIG_REG 0x02 +#define IS31FL3197_OUT1_REG 0x10 +#define IS31FL3197_OUT2_REG 0x11 +#define IS31FL3197_OUT3_REG 0x12 +#define IS31FL3197_OUT4_REG 0x13 +#define IS31FL3197_UPDATE_REG 0x2b + +#define IS31FL3197_SHUTDOWN_REG_VAL 0xf1 /* enable all channels */ +#define IS31FL3197_OPER_CONFIG_REG_VAL 0xff /* set all to current level */ +#define IS31FL3197_UPDATE_VAL 0xc5 + +#define IS31FL3197_CHANNEL_COUNT 4 + +static const struct is31f1319x_model is31f13197_model = { + .features = FEATURE_ID_IS_ADDR, + + /* register indexes */ + .prod_id_reg = IS31FL3197_PROD_ID_REG, + .shutdown_reg = IS31FL3197_SHUTDOWN_REG, + .conf_reg = IS31FL3197_OPER_CONFIG_REG, + .current_reg = REG_NOT_DEFINED, + .update_reg = IS31FL3197_UPDATE_REG, + + /* values for those registers */ + .prod_id_val = 0xff, + .shutdown_reg_val = IS31FL3197_SHUTDOWN_REG_VAL, + .conf_enable = IS31FL3197_OPER_CONFIG_REG_VAL, + .update_val = IS31FL3197_UPDATE_VAL, + + /* channel output registers */ + .led_channels = {IS31FL3197_OUT1_REG, IS31FL3197_OUT2_REG, IS31FL3197_OUT3_REG, + IS31FL3197_OUT4_REG} +}; +#endif + +static const struct led_info *is31fl319x_led_to_info(const struct is31fl319x_config *config, + uint32_t led) +{ + if (led < config->num_leds) { + return &config->led_infos[led]; + } + + return NULL; +} + +static int is31fl319x_get_info(const struct device *dev, + uint32_t led, + const struct led_info **info_out) +{ + const struct is31fl319x_config *config = dev->config; + const struct led_info *info = is31fl319x_led_to_info(config, led); + + if (info == NULL) { + return -EINVAL; + } + + *info_out = info; + return 0; +} + +static uint8_t is31fl319x_map_led_to_start_channel(const struct is31fl319x_config *config, + uint32_t led) +{ + /* It is assumed that led has been validated before calling this */ + const struct led_info *info = config->led_infos; + uint8_t channel = 0; + + while (led) { + channel += info->num_colors; + led--; + info++; + } + return channel; +} + +static int is31fl319x_write_channels(const struct device *dev, uint32_t start_channel, + uint32_t num_channels, const uint8_t *buf) +{ + const struct is31fl319x_config *config = dev->config; + const struct is31f1319x_model *model = config->model; + int ret = 0; + + if ((start_channel + num_channels) > config->channel_count) { + return -ENOTSUP; + } + + for (int i = 0; i < num_channels; i++) { + ret = i2c_reg_write_byte_dt(&config->bus, model->led_channels[i + start_channel], + buf[i]); + if (ret != 0) { + break; + } + } + + if (ret == 0) { + ret = i2c_reg_write_byte_dt(&config->bus, + model->update_reg, + model->update_val); + } + + if (ret != 0) { + LOG_ERR("%s: LED write failed: %d", dev->name, ret); + } + + return ret; +} + +static int is31fl319x_set_color(const struct device *dev, uint32_t led, uint8_t num_colors, + const uint8_t *color) +{ + const struct is31fl319x_config *config = dev->config; + const struct led_info *info = is31fl319x_led_to_info(config, led); + + if (info == NULL) { + return -ENODEV; + } + + uint8_t channel_start = is31fl319x_map_led_to_start_channel(config, led); + + if (info->num_colors > config->channel_count) { + return -ENOTSUP; + } + + return is31fl319x_write_channels(dev, channel_start, num_colors, color); +} + +static int is31fl319x_set_brightness(const struct device *dev, uint32_t led, uint8_t value) +{ + const struct is31fl319x_config *config = dev->config; + const struct led_info *info = is31fl319x_led_to_info(config, led); + const struct is31f1319x_model *model = config->model; + + int ret = 0; + + if (info == NULL) { + return -ENODEV; + } + + if (info->num_colors != 1) { + return -ENOTSUP; + } + + /* Rescale 0..100 to 0..255 */ + value = value * 255 / LED_BRIGHTNESS_MAX; + + uint8_t channel_start = is31fl319x_map_led_to_start_channel(config, led); + + ret = i2c_reg_write_byte_dt(&config->bus, model->led_channels[channel_start], value); + if (ret == 0) { + ret = i2c_reg_write_byte_dt(&config->bus, + model->update_reg, + model->update_val); + } + + if (ret != 0) { + LOG_ERR("%s: LED write failed", dev->name); + } + + return ret; +} + +static int is31fl319x_check_config(const struct device *dev) +{ + const struct is31fl319x_config *config = dev->config; + const struct led_info *info; + uint8_t rgb_count = 0; + + /* verify that number of leds defined is not > number of channels */ + for (int i = 0; i < config->num_leds; i++) { + info = &config->led_infos[i]; + rgb_count += info->num_colors; + } + + if (rgb_count > config->channel_count) { + return -EINVAL; + } + + return 0; +} + +static int is31fl319x_init(const struct device *dev) +{ + const struct is31fl319x_config *config = dev->config; + const struct led_info *info = NULL; + const struct is31f1319x_model *model = config->model; + int ret; + uint8_t prod_id, band, channel; + uint8_t current_reg = 0; + + ret = is31fl319x_check_config(dev); + if (ret != 0) { + return ret; + } + + if (!i2c_is_ready_dt(&config->bus)) { + LOG_ERR("%s: I2C device not ready", dev->name); + return -ENODEV; + } + + ret = i2c_reg_read_byte_dt(&config->bus, model->prod_id_reg, &prod_id); + if (ret != 0) { + LOG_ERR("%s: failed to read product ID", dev->name); + return ret; + } + + + if (model->features & FEATURE_ID_IS_ADDR) { + /* The product ID (8 bit) should be the I2C address(7 bit) */ + if (prod_id != (config->bus.addr << 1)) { + LOG_ERR("%s: invalid product ID 0x%02x (expected 0x%02x)", dev->name, + prod_id, config->bus.addr << 1); + return -ENODEV; + } + } else { + if (prod_id != model->prod_id_val) { + LOG_ERR("%s: invalid product ID 0x%02x (expected 0x%02x)", dev->name, + prod_id, model->prod_id_val); + return -ENODEV; + } + } + + /* calc current limit register value */ + if (model->features & FEATURE_SET_CURRENT) { + channel = 0; + for (int i = 0; i < config->num_leds; i++) { + info = &config->led_infos[i]; + band = (config->current_limits[i] / 10) - 1; + + for (int j = 0; j < info->num_colors; j++) { + current_reg |= band << (2 * channel); + channel++; + } + } + + ret = i2c_reg_write_byte_dt(&config->bus, model->current_reg, current_reg); + if (ret != 0) { + LOG_ERR("%s: failed to set current limit", dev->name); + return ret; + } + } + if (model->shutdown_reg != REG_NOT_DEFINED) { + ret = i2c_reg_write_byte_dt(&config->bus, model->shutdown_reg, + model->shutdown_reg_val); + if (ret != 0) { + LOG_ERR("%s: failed to set current limit", dev->name); + return ret; + } + } + + /* enable device */ + return i2c_reg_write_byte_dt(&config->bus, model->conf_reg, + model->conf_enable); +} + +static DEVICE_API(led, is31fl319x_led_api) = { + .set_brightness = is31fl319x_set_brightness, + .get_info = is31fl319x_get_info, + .set_color = is31fl319x_set_color, + .write_channels = is31fl319x_write_channels, +}; + +#define COLOR_MAPPING(led_node_id) \ + static const uint8_t color_mapping_##led_node_id[] = \ + DT_PROP(led_node_id, color_mapping); + +#define LED_INFO(led_node_id) \ + { \ + .label = DT_PROP(led_node_id, label), \ + .num_colors = DT_PROP_LEN(led_node_id, color_mapping), \ + .color_mapping = color_mapping_##led_node_id, \ + }, + +#define LED_CURRENT(led_node_id) \ + DT_PROP(led_node_id, current_limit), + +#define IS31FL319X_DEVICE(n, id, nchannels, pmodel) \ + \ + DT_INST_FOREACH_CHILD(n, COLOR_MAPPING) \ + \ + static const struct led_info is31fl319##id##_leds_##n[] = \ + { DT_INST_FOREACH_CHILD(n, LED_INFO) }; \ + \ + static const uint8_t is31fl319##id##_currents_##n[] = \ + { DT_INST_FOREACH_CHILD(n, LED_CURRENT) }; \ + BUILD_ASSERT(ARRAY_SIZE(is31fl319##id##_leds_##n) > 0, \ + "No LEDs defined for " #n); \ + \ + static const struct is31fl319x_config is31fl319##id##_config_##n = { \ + .bus = I2C_DT_SPEC_INST_GET(n), \ + .channel_count = nchannels, \ + .num_leds = ARRAY_SIZE(is31fl319##id##_leds_##n), \ + .led_infos = is31fl319##id##_leds_##n, \ + .current_limits = is31fl319##id##_currents_##n, \ + .model = pmodel, \ + }; \ + DEVICE_DT_INST_DEFINE(n, &is31fl319x_init, NULL, NULL, \ + &is31fl319##id##_config_##n, POST_KERNEL, \ + CONFIG_LED_INIT_PRIORITY, &is31fl319x_led_api); + +#undef DT_DRV_COMPAT +#ifdef CONFIG_DT_HAS_ISSI_IS31FL3194_ENABLED +#define DT_DRV_COMPAT issi_is31fl3194 +DT_INST_FOREACH_STATUS_OKAY_VARGS(IS31FL319X_DEVICE, 4, IS31FL3194_CHANNEL_COUNT, + &is31f13194_model) +#endif + +#ifdef CONFIG_DT_HAS_ISSI_IS31FL3197_ENABLED +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT issi_is31fl3197 +DT_INST_FOREACH_STATUS_OKAY_VARGS(IS31FL319X_DEVICE, 7, IS31FL3197_CHANNEL_COUNT, + &is31f13197_model) +#endif +#undef DT_DRV_COMPAT diff --git a/dts/bindings/led/issi,is31fl3197.yaml b/dts/bindings/led/issi,is31fl3197.yaml new file mode 100644 index 0000000000000..93d232e2240f3 --- /dev/null +++ b/dts/bindings/led/issi,is31fl3197.yaml @@ -0,0 +1,80 @@ +# Copyright (c) 2024 Arduino SA +# SPDX-License-Identifier: Apache-2.0 + +description: | + IS31FL3197 4-channel LED driver with programmable pattern sequencing + + This driver supports single-channel and RGB LEDs. For single channel LEDs, + the led_set_brightness() API can be used to set the brightness of each LED. + For RGB LEDs, the led_set_color() API can be used to set the red, green and + blue components; the driver takes care of routing to the outputs described + by the color-mapping property. + + The LED_SHELL application can be used for testing. + + The following shows configuration for Arduino Giga Display shield + + This driver supports single-channel and RGB and maybe RGBW LEDs. For single + channel LEDs, the led_set_brightness() API can be used to set the brightness + of each LED. For RGB LEDs, the led_set_color() API can be used to set the red, + green and blue and white components; the driver takes care of routing to the + outputs described by the color-mapping property. + + The LED_SHELL application can be used for testing. + + The following defines a single RGB LED in the is31fl3197 DT node: + + is31fl3197@50 { + compatible = "issi,is31fl3197"; + reg = <0x50>; + + led_0 { + label = "RGB LED"; + color-mapping = + , + , + ; + }; + }; + + The following example defines three single-channel LEDs in the is31fl3197 DT node: + + is31fl3197@50 { + compatible = "issi,is31fl3197"; + reg = <0x50>; + + led_0 { + label = "RED LED"; + color-mapping = ; + }; + + led_1 { + label = "GREEN LED"; + color-mapping = ; + }; + + led_2 { + label = "BLUE LED"; + color-mapping = ; + }; + }; + +compatible: "issi,is31fl3197" + +include: ["i2c-device.yaml", "led-controller.yaml"] + +child-binding: + properties: + label: + required: true + + color-mapping: + required: true + + current-limit: + type: int + default: 10 + enum: + - 10 + description: | + The current limit for the LED in mA. diff --git a/samples/drivers/led/is31fl3194/README.rst b/samples/drivers/led/is31fl3194/README.rst deleted file mode 100644 index cee837f4fe93d..0000000000000 --- a/samples/drivers/led/is31fl3194/README.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. zephyr:code-sample:: is31fl3194 - :name: IS31FL3194 RGB LED - :relevant-api: led_interface - - Cycle colors on an RGB LED connected to the IS31FL3194 using the LED API. - -Overview -******** - -This sample cycles several colors on an RGB LED forever using the LED API. - -Building and Running -******************** - -This sample can be built and executed on an Arduino Nicla Sense ME, or on -any board where the devicetree has an I2C device node with compatible -:dtcompatible:`issi,is31fl3194` enabled, along with the relevant bus -controller node also being enabled. - -.. zephyr-app-commands:: - :zephyr-app: samples/drivers/led/is31fl3194 - :board: arduino_nicla_sense_me - :goals: build flash - :compact: - -After flashing, the LED starts to switch colors and messages with the current -LED color are printed on the console. If a runtime error occurs, the sample -exits without printing to the console. diff --git a/samples/drivers/led/is31fl3194/src/main.c b/samples/drivers/led/is31fl3194/src/main.c deleted file mode 100644 index 335d152101b0f..0000000000000 --- a/samples/drivers/led/is31fl3194/src/main.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2024 Arduino SA - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include - -/* 1000 msec = 1 sec */ -#define SLEEP_TIME_MS 1000 - -/* Structure describing a color by its component values and name */ -struct color_data { - uint8_t r, g, b; - const char *name; -}; - -/* The sequence of colors the RGB LED will display */ -static const struct color_data color_sequence[] = { - { 0xFF, 0x00, 0x00, "Red" }, - { 0x00, 0xFF, 0x00, "Green" }, - { 0x00, 0x00, 0xFF, "Blue" }, - { 0xFF, 0xFF, 0xFF, "White" }, - { 0xFF, 0xFF, 0x00, "Yellow" }, - { 0xFF, 0x00, 0xFF, "Purple" }, - { 0x00, 0xFF, 0xFF, "Cyan" }, - { 0xF4, 0x79, 0x20, "Orange" }, -}; - -/* - * A build error on this line means your board is unsupported. - */ -const struct device *led = DEVICE_DT_GET_ANY(issi_is31fl3194); - -int main(void) -{ - int ret; - int i = 0; - - if (!device_is_ready(led)) { - return 0; - } - - while (1) { - ret = led_set_color(led, 0, 3, &(color_sequence[i].r)); - if (ret < 0) { - return 0; - } - - printk("LED color: %s\n", color_sequence[i].name); - k_msleep(SLEEP_TIME_MS); - i = (i + 1) % ARRAY_SIZE(color_sequence); - } - - return 0; -} diff --git a/samples/drivers/led/is31fl3194/CMakeLists.txt b/samples/drivers/led/is31fl319x/CMakeLists.txt similarity index 87% rename from samples/drivers/led/is31fl3194/CMakeLists.txt rename to samples/drivers/led/is31fl319x/CMakeLists.txt index ed52c77a3fd0e..91cfa7707b7c6 100644 --- a/samples/drivers/led/is31fl3194/CMakeLists.txt +++ b/samples/drivers/led/is31fl319x/CMakeLists.txt @@ -2,6 +2,6 @@ cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) -project(led_is31fl3194) +project(led_is31fl319x) target_sources(app PRIVATE src/main.c) diff --git a/samples/drivers/led/is31fl319x/README.rst b/samples/drivers/led/is31fl319x/README.rst new file mode 100644 index 0000000000000..7549488c23372 --- /dev/null +++ b/samples/drivers/led/is31fl319x/README.rst @@ -0,0 +1,47 @@ +.. zephyr:code-sample:: is31fl319x + :name: IS31FL319x RGB LED + :relevant-api: led_interface + + Cycle colors on an RGB LED connected to the IS31FL3194 or IS31FL3197 + using the LED API. + +Overview +******** + +This sample first looks through the color table to map which channels +map to Red, Green and Blue. + +It then shows the mapping, but setting one color on and fading it +off. First Red, then Green and finally Blue. + +It then forever cycles through several colors on an RGB LED. It uses a +helper function, that maps the RGB colors into the correct order +for the actual hardware LED, as some hardware LEDs will be defined in +RGB order whereas others may be in another order such as BGR. +Once mapped it calls off to the led library to display the color. + +Building and Running +******************** + +This sample can be built and executed on an Arduino Nicla Sense ME, or +Arduino Giga with an Arduino Giga Display shield, or on +any board where the devicetree has an I2C device node with compatible +:dtcompatible:`issi,is31fl3194` or :dtcompatible:`issi,is31fl3197` +with the relevant bus controller node also being enabled. + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/led/is31fl319x + :board: arduino_nicla_sense_me + :goals: build flash + :compact: + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/led/is31fl319x + :board: arduino_giga_r1//m7 + :shield: arduino_giga_display_shield + :goals: build flash + :compact: + +After flashing, the LED starts to switch colors and messages with the current +LED color are printed on the console. If a runtime error occurs, the sample +exits without printing to the console. diff --git a/samples/drivers/led/is31fl3194/prj.conf b/samples/drivers/led/is31fl319x/prj.conf similarity index 100% rename from samples/drivers/led/is31fl3194/prj.conf rename to samples/drivers/led/is31fl319x/prj.conf diff --git a/samples/drivers/led/is31fl3194/sample.yaml b/samples/drivers/led/is31fl319x/sample.yaml similarity index 57% rename from samples/drivers/led/is31fl3194/sample.yaml rename to samples/drivers/led/is31fl319x/sample.yaml index 0cb932b05d2ee..ca87c1ce3b3bd 100644 --- a/samples/drivers/led/is31fl3194/sample.yaml +++ b/samples/drivers/led/is31fl319x/sample.yaml @@ -1,8 +1,8 @@ sample: - description: Demonstration of the IS31FL3194 LED driver - name: is31fl3194 sample + description: Demonstration of the IS31FL319x LED driver + name: is31fl319x sample tests: - sample.drivers.led.is31fl3194: + sample.drivers.led.is31fl319x: filter: dt_compat_enabled("issi,is31fl3194") tags: LED depends_on: i2c diff --git a/samples/drivers/led/is31fl319x/src/main.c b/samples/drivers/led/is31fl319x/src/main.c new file mode 100644 index 0000000000000..c2d4d92e7470d --- /dev/null +++ b/samples/drivers/led/is31fl319x/src/main.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +/* 1000 msec = 1 sec */ +#define SLEEP_TIME_MS 1000 + +/* Structure describing a color by its component values and name */ +struct color_data { + uint8_t r, g, b; + const char *name; +}; + +/* The sequence of colors the RGB LED will display */ +static const struct color_data color_sequence[] = { + { 0xFF, 0x00, 0x00, "Red" }, + { 0x00, 0xFF, 0x00, "Green" }, + { 0x00, 0x00, 0xFF, "Blue" }, + { 0xFF, 0xFF, 0xFF, "White" }, + { 0xFF, 0xFF, 0x00, "Yellow" }, + { 0xFF, 0x00, 0xFF, "Purple" }, + { 0x00, 0xFF, 0xFF, "Cyan" }, + { 0xF4, 0x79, 0x20, "Orange" }, +}; + +/* + * A build error on these lines means your board is unsupported. + */ +const struct device *led4_dev = DEVICE_DT_GET_ANY(issi_is31fl3194); +const struct device *led7_dev = DEVICE_DT_GET_ANY(issi_is31fl3197); + +uint8_t rgb_mapping[3] = {0, 0, 0}; /* R G B */ + +/* + * forward references + */ +extern bool generate_rgb_color_mapping(const struct device *dev, uint32_t led); +extern bool output_rgb_color(const struct device *dev, uint32_t led, const uint8_t *colors); +extern void fade_one_channel(const struct device *dev, uint8_t rgb_color_index, const char *name); + +/* + * main + */ +int main(void) +{ + int i = 0; + + const struct device *led_dev = led4_dev; + + if (led_dev == NULL) { + led_dev = led7_dev; + } + + if (!device_is_ready(led_dev)) { + printk("No devices were found"); + return 0; + } + + if (!generate_rgb_color_mapping(led_dev, 0)) { + printk("Failed to generate RGB Color mapping\n"); + return 0; + } + + /* lets try to verify the color indexes */ + fade_one_channel(led_dev, 0, "Red"); + fade_one_channel(led_dev, 1, "Green"); + fade_one_channel(led_dev, 2, "Blue"); + + while (1) { + /* map the colors */ + if (!output_rgb_color(led_dev, 0, &(color_sequence[i].r))) { + printk("call to led_set_color failed\n"); + return 0; + } + + printk("LED color: %s\n", color_sequence[i].name); + k_msleep(SLEEP_TIME_MS); + i = (i + 1) % ARRAY_SIZE(color_sequence); + } + + return 0; +} + +/* + * Generate RGB color mapping + */ +bool generate_rgb_color_mapping(const struct device *dev, uint32_t led) +{ + const struct led_info *info; + + /* find the Red, Green, and blue color indexes */ + if (led_get_info(dev, led, &info) < 0) { + printk("Failed to retrieve LED info\n"); + return false; + } + /* generate color mapping table for this device */ + for (int i = 0; i < info->num_colors; i++) { + switch (info->color_mapping[i]) { + case LED_COLOR_ID_RED: + rgb_mapping[0] = i; + break; + case LED_COLOR_ID_GREEN: + rgb_mapping[1] = i; + break; + case LED_COLOR_ID_BLUE: + rgb_mapping[2] = i; + break; + default: + printk("Unknown color: %x found in index %d\n", info->color_mapping[i], i); + /* Ignore */ + break; + } + } + + printk("Color indexes: R:%u G:%u B:%u\n", rgb_mapping[0], + rgb_mapping[1], rgb_mapping[2]); + return true; +} + +/* + * Set one color (R or G or B) to all on and then fade it down to off + */ +void fade_one_channel(const struct device *dev, uint8_t rgb_color_index, const char *name) +{ + uint8_t colors[3] = {0, 0, 0}; + int color = 0xff; + + /* make sure all of the leds are off */ + if (led_set_color(dev, 0, 3, colors) < 0) { + return; + } + + printk("Test fading %s\n", name); + for (;;) { + led_set_channel(dev, rgb_mapping[rgb_color_index], color); + if (color == 0) { + break; + } + k_msleep(SLEEP_TIME_MS); + color >>= 2; + } +} + +/* + * Map the RGB color to the mapping order and output to the LED. + */ +bool output_rgb_color(const struct device *dev, uint32_t led, const uint8_t *colors) +{ + uint8_t mapped_colors[3]; + + for (uint8_t i = 0; i < 3; i++) { + mapped_colors[rgb_mapping[i]] = colors[i]; + } + + return led_set_color(dev, led, 3, mapped_colors) >= 0; +}