drivers: led: Add lumissil is31fl3216a driver
The IS31FL3216A is a fun light LED controller. The LED current of each channel can be set in 256 steps by adjusting the PWM duty cycle through an I2C interface. Signed-off-by: Guy Morand <guy.morand@bytesatwork.ch>
This commit is contained in:
parent
5ac063d793
commit
890363a6fb
5 changed files with 262 additions and 0 deletions
|
@ -5,6 +5,7 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/led.h)
|
|||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_HT16K33 ht16k33.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_IS31FL3216A is31fl3216a.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LED_GPIO led_gpio.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LED_PWM led_pwm.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LED_XEC led_mchp_xec.c)
|
||||
|
|
|
@ -28,6 +28,7 @@ config LED_SHELL
|
|||
|
||||
source "drivers/led/Kconfig.gpio"
|
||||
source "drivers/led/Kconfig.ht16k33"
|
||||
source "drivers/led/Kconfig.is31fl3216a"
|
||||
source "drivers/led/Kconfig.lp3943"
|
||||
source "drivers/led/Kconfig.lp503x"
|
||||
source "drivers/led/Kconfig.lp5562"
|
||||
|
|
11
drivers/led/Kconfig.is31fl3216a
Normal file
11
drivers/led/Kconfig.is31fl3216a
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Copyright (c) 2023 Endor AG
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config IS31FL3216A
|
||||
bool "IS31FL3216A LED driver"
|
||||
default y
|
||||
depends on DT_HAS_ISSI_IS31FL3216A_ENABLED
|
||||
select I2C
|
||||
help
|
||||
Enable LED driver for Lumissil Microsystems (a division of ISSI)
|
||||
IS31FL3216A. This chip supports up to 16 PWM controlled LEDs.
|
240
drivers/led/is31fl3216a.c
Normal file
240
drivers/led/is31fl3216a.c
Normal file
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Endor AG
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT issi_is31fl3216a
|
||||
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/led.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#define IS31FL3216A_REG_CONFIG 0x00
|
||||
#define IS31FL3216A_REG_CTL_1 0x01
|
||||
#define IS31FL3216A_REG_CTL_2 0x02
|
||||
#define IS31FL3216A_REG_LIGHT_EFFECT 0x03
|
||||
#define IS31FL3216A_REG_CHANNEL_CONFIG 0x04
|
||||
#define IS31FL3216A_REG_GPIO_CONFIG 0x05
|
||||
#define IS31FL3216A_REG_OUTPUT_PORT 0x06
|
||||
#define IS31FL3216A_REG_INT_CONTROL 0x07
|
||||
#define IS31FL3216A_REG_ADC_SAMPLE_RATE 0x09
|
||||
#define IS31FL3216A_REG_PWM_FIRST 0x10
|
||||
#define IS31FL3216A_REG_PWM_LAST 0x1F
|
||||
#define IS31FL3216A_REG_UPDATE 0xB0
|
||||
#define IS31FL3216A_REG_FRAME_DELAY 0xB6
|
||||
#define IS31FL3216A_REG_FRAME_START 0xB7
|
||||
|
||||
#define IS31FL3216A_MAX_LEDS 16
|
||||
|
||||
LOG_MODULE_REGISTER(is31fl3216a, CONFIG_LED_LOG_LEVEL);
|
||||
|
||||
struct is31fl3216a_cfg {
|
||||
struct i2c_dt_spec i2c;
|
||||
};
|
||||
|
||||
static int is31fl3216a_write_buffer(const struct i2c_dt_spec *i2c,
|
||||
const uint8_t *buffer, uint32_t num_bytes)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = i2c_write_dt(i2c, buffer, num_bytes);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Could not write buffer: %i", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is31fl3216a_write_reg(const struct i2c_dt_spec *i2c, uint8_t reg,
|
||||
uint8_t val)
|
||||
{
|
||||
uint8_t buffer[2] = {reg, val};
|
||||
|
||||
return is31fl3216a_write_buffer(i2c, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
static int is31fl3216a_update_pwm(const struct i2c_dt_spec *i2c)
|
||||
{
|
||||
return is31fl3216a_write_reg(i2c, IS31FL3216A_REG_UPDATE, 0);
|
||||
}
|
||||
|
||||
static uint8_t is31fl3216a_brightness_to_pwm(uint8_t brightness)
|
||||
{
|
||||
return (0xFFU * brightness) / 100;
|
||||
}
|
||||
|
||||
static int is31fl3216a_led_write_channels(const struct device *dev,
|
||||
uint32_t start_channel,
|
||||
uint32_t num_channels,
|
||||
const uint8_t *buf)
|
||||
{
|
||||
const struct is31fl3216a_cfg *config = dev->config;
|
||||
uint8_t i2c_buffer[IS31FL3216A_MAX_LEDS + 1];
|
||||
int status;
|
||||
int i;
|
||||
|
||||
if (num_channels == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (start_channel + num_channels > IS31FL3216A_MAX_LEDS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Invert channels, last register is channel 0 */
|
||||
i2c_buffer[0] = IS31FL3216A_REG_PWM_LAST - start_channel -
|
||||
(num_channels - 1);
|
||||
for (i = 0; i < num_channels; i++) {
|
||||
if (buf[num_channels - i - 1] > 100) {
|
||||
return -EINVAL;
|
||||
}
|
||||
i2c_buffer[i + 1] = is31fl3216a_brightness_to_pwm(
|
||||
buf[num_channels - i - 1]);
|
||||
}
|
||||
|
||||
status = is31fl3216a_write_buffer(&config->i2c, i2c_buffer,
|
||||
num_channels + 1);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return is31fl3216a_update_pwm(&config->i2c);
|
||||
}
|
||||
|
||||
static int is31fl3216a_led_set_brightness(const struct device *dev,
|
||||
uint32_t led, uint8_t value)
|
||||
{
|
||||
const struct is31fl3216a_cfg *config = dev->config;
|
||||
uint8_t pwm_reg = IS31FL3216A_REG_PWM_LAST - led;
|
||||
int status;
|
||||
uint8_t pwm_value;
|
||||
|
||||
if (led > IS31FL3216A_MAX_LEDS - 1 || value > 100) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pwm_value = is31fl3216a_brightness_to_pwm(value);
|
||||
status = is31fl3216a_write_reg(&config->i2c, pwm_reg, pwm_value);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return is31fl3216a_update_pwm(&config->i2c);
|
||||
}
|
||||
|
||||
static int is31fl3216a_led_on(const struct device *dev, uint32_t led)
|
||||
{
|
||||
return is31fl3216a_led_set_brightness(dev, led, 100);
|
||||
}
|
||||
|
||||
static int is31fl3216a_led_off(const struct device *dev, uint32_t led)
|
||||
{
|
||||
return is31fl3216a_led_set_brightness(dev, led, 0);
|
||||
}
|
||||
|
||||
static int is31fl3216a_init_registers(const struct i2c_dt_spec *i2c)
|
||||
{
|
||||
int i;
|
||||
int status;
|
||||
|
||||
status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_CTL_1, 0xFF);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_CTL_2, 0xFF);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_LIGHT_EFFECT, 0x00);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_CHANNEL_CONFIG,
|
||||
0x00);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_GPIO_CONFIG, 0x00);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_OUTPUT_PORT, 0x00);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_INT_CONTROL, 0x00);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_ADC_SAMPLE_RATE,
|
||||
0x00);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_FRAME_DELAY, 0x00);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_FRAME_START, 0x00);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
for (i = IS31FL3216A_REG_PWM_FIRST;
|
||||
i <= IS31FL3216A_REG_PWM_LAST;
|
||||
i++) {
|
||||
status = is31fl3216a_write_reg(i2c, i, 0);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
status = is31fl3216a_write_reg(i2c, IS31FL3216A_REG_UPDATE, 0);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return is31fl3216a_write_reg(i2c, IS31FL3216A_REG_CONFIG, 0x00);
|
||||
}
|
||||
|
||||
static int is31fl3216a_init(const struct device *dev)
|
||||
{
|
||||
const struct is31fl3216a_cfg *config = dev->config;
|
||||
|
||||
LOG_DBG("Initializing @0x%x...", config->i2c.addr);
|
||||
|
||||
if (!i2c_is_ready_dt(&config->i2c)) {
|
||||
LOG_ERR("I2C device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return is31fl3216a_init_registers(&config->i2c);
|
||||
}
|
||||
|
||||
static const struct led_driver_api is31fl3216a_led_api = {
|
||||
.set_brightness = is31fl3216a_led_set_brightness,
|
||||
.on = is31fl3216a_led_on,
|
||||
.off = is31fl3216a_led_off,
|
||||
.write_channels = is31fl3216a_led_write_channels
|
||||
};
|
||||
|
||||
#define IS31FL3216A_INIT(id) \
|
||||
static const struct is31fl3216a_cfg is31fl3216a_##id##_cfg = { \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(id), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(id, &is31fl3216a_init, NULL, NULL, \
|
||||
&is31fl3216a_##id##_cfg, POST_KERNEL, \
|
||||
CONFIG_LED_INIT_PRIORITY, &is31fl3216a_led_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(IS31FL3216A_INIT)
|
9
dts/bindings/led/issi,is31fl3216a.yaml
Normal file
9
dts/bindings/led/issi,is31fl3216a.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2023 Endor AG
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
is31fl3216a LED driver for Lumissil Microsystems (a division of ISSI)
|
||||
|
||||
compatible: "issi,is31fl3216a"
|
||||
|
||||
include: i2c-device.yaml
|
Loading…
Add table
Add a link
Reference in a new issue