/* * Copyright (c) 2023 NXP Semiconductors * Copyright (c) 2023 Cognipilot Foundation * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT onnn_ncp5623 /** * @file * @brief NCP5623 LED driver * * The NCP5623 is a 3-channel LED driver that communicates over I2C. */ #include #include #include #include #include LOG_MODULE_REGISTER(ncp5623, CONFIG_LED_LOG_LEVEL); #define NCP5623_LED_CURRENT 0x20 #define NCP5623_LED_PWM0 0x40 #define NCP5623_LED_PWM1 0x60 #define NCP5623_LED_PWM2 0x80 #define NCP5623_CHANNEL_COUNT 3 /* Brightness limits */ #define NCP5623_MIN_BRIGHTNESS 0 #define NCP5623_MAX_BRIGHTNESS 0x1f static const uint8_t led_channels[] = {NCP5623_LED_PWM0, NCP5623_LED_PWM1, NCP5623_LED_PWM2}; struct ncp5623_config { struct i2c_dt_spec bus; uint8_t num_leds; const struct led_info *leds_info; }; static const struct led_info *ncp5623_led_to_info(const struct ncp5623_config *config, uint32_t led) { if (led < config->num_leds) { return &config->leds_info[led]; } return NULL; } static int ncp5623_get_info(const struct device *dev, uint32_t led, const struct led_info **info) { const struct ncp5623_config *config = dev->config; const struct led_info *led_info = ncp5623_led_to_info(config, led); if (!led_info) { return -EINVAL; } *info = led_info; return 0; } static int ncp5623_set_color(const struct device *dev, uint32_t led, uint8_t num_colors, const uint8_t *color) { const struct ncp5623_config *config = dev->config; const struct led_info *led_info = ncp5623_led_to_info(config, led); uint8_t buf[6] = {0x70, NCP5623_LED_PWM0, 0x70, NCP5623_LED_PWM1, 0x70, NCP5623_LED_PWM2}; if (!led_info) { return -ENODEV; } if (led_info->num_colors != 3) { return -ENOTSUP; } if (num_colors != 3) { return -EINVAL; } buf[1] = buf[1] | color[0] / 8; buf[3] = buf[3] | color[1] / 8; buf[5] = buf[5] | color[2] / 8; return i2c_burst_write_dt(&config->bus, NCP5623_LED_CURRENT | NCP5623_MAX_BRIGHTNESS, buf, sizeof(buf)); } static int ncp5623_set_brightness(const struct device *dev, uint32_t led, uint8_t value) { const struct ncp5623_config *config = dev->config; const struct led_info *led_info = ncp5623_led_to_info(config, led); int ret = 0; if (!led_info) { return -ENODEV; } if (value > 100) { return -EINVAL; } if (led_info->num_colors != 1) { return -ENOTSUP; } /* Rescale 0..100 to 0..31 */ value = value * NCP5623_MAX_BRIGHTNESS / 100; ret = i2c_reg_write_byte_dt(&config->bus, led_channels[led] | value, 0x70); if (ret < 0) { LOG_ERR("%s: LED write failed", dev->name); } return ret; } static inline int ncp5623_led_on(const struct device *dev, uint32_t led) { return ncp5623_set_brightness(dev, led, 100); } static inline int ncp5623_led_off(const struct device *dev, uint32_t led) { return ncp5623_set_brightness(dev, led, 0); } static int ncp5623_led_init(const struct device *dev) { const struct ncp5623_config *config = dev->config; const struct led_info *led_info = NULL; int i; uint8_t buf[6] = {0x70, NCP5623_LED_PWM0, 0x70, NCP5623_LED_PWM1, 0x70, NCP5623_LED_PWM2}; if (!i2c_is_ready_dt(&config->bus)) { LOG_ERR("%s: I2C device not ready", dev->name); return -ENODEV; } if (config->num_leds == 1) { /* one three-channel (RGB) LED */ led_info = ncp5623_led_to_info(config, 0); if (!led_info) { return -ENODEV; } if (led_info->num_colors != NCP5623_CHANNEL_COUNT) { LOG_ERR("%s: invalid number of colors %d (must be %d with a single LED)", dev->name, led_info->num_colors, NCP5623_CHANNEL_COUNT); return -EINVAL; } } else if (config->num_leds <= 3) { /* three single-channel LEDs */ for (i = 0; i < config->num_leds; i++) { led_info = ncp5623_led_to_info(config, i); if (!led_info) { return -ENODEV; } if (led_info->num_colors > 1) { LOG_ERR("%s: invalid number of colors %d (must be 1 when defining " "multiple leds)", dev->name, led_info->num_colors); return -EINVAL; } } } else { LOG_ERR("%s: invalid number of leds %d (max %d)", dev->name, config->num_leds, NCP5623_CHANNEL_COUNT); return -EINVAL; } if (i2c_burst_write_dt(&config->bus, NCP5623_LED_CURRENT | NCP5623_MAX_BRIGHTNESS, buf, 6)) { LOG_ERR("%s: LED write failed", dev->name); return -EIO; } return 0; } static const struct led_driver_api ncp5623_led_api = { .set_brightness = ncp5623_set_brightness, .on = ncp5623_led_on, .off = ncp5623_led_off, .get_info = ncp5623_get_info, .set_color = ncp5623_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), \ .index = DT_PROP(led_node_id, index), \ .num_colors = DT_PROP_LEN(led_node_id, color_mapping), \ .color_mapping = color_mapping_##led_node_id, \ }, #define NCP5623_DEFINE(id) \ \ DT_INST_FOREACH_CHILD(id, COLOR_MAPPING) \ \ static const struct led_info ncp5623_leds_##id[] = {DT_INST_FOREACH_CHILD(id, LED_INFO)}; \ \ static const struct ncp5623_config ncp5623_config_##id = { \ .bus = I2C_DT_SPEC_INST_GET(id), \ .num_leds = ARRAY_SIZE(ncp5623_leds_##id), \ .leds_info = ncp5623_leds_##id, \ }; \ DEVICE_DT_INST_DEFINE(id, &ncp5623_led_init, NULL, NULL, &ncp5623_config_##id, \ POST_KERNEL, CONFIG_LED_INIT_PRIORITY, &ncp5623_led_api); DT_INST_FOREACH_STATUS_OKAY(NCP5623_DEFINE)