You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
6.2 KiB
204 lines
6.2 KiB
/* |
|
* 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 <zephyr/device.h> |
|
#include <zephyr/drivers/i2c.h> |
|
#include <zephyr/drivers/led.h> |
|
#include <zephyr/kernel.h> |
|
#include <zephyr/logging/log.h> |
|
|
|
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 (led_info->num_colors != 1) { |
|
return -ENOTSUP; |
|
} |
|
|
|
/* Rescale 0..100 to 0..31 */ |
|
value = value * NCP5623_MAX_BRIGHTNESS / LED_BRIGHTNESS_MAX; |
|
|
|
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 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 DEVICE_API(led, ncp5623_led_api) = { |
|
.set_brightness = ncp5623_set_brightness, |
|
.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)
|
|
|