drivers: led: added support for is31fl3733 led driver
Enabled support for is31fl3733 driver. This driver supports the full LED API, and enables the following features of the is31fl3733: - individual LED dimming - individual LED enable/disable - bulk writes of LED enabled and dimming states - global LED current limit - blanking (via custom API) Signed-off-by: Daniel DeGrasse <daniel@degrasse.com>
This commit is contained in:
parent
8fc913374f
commit
85a41ae88a
|
@ -16,6 +16,7 @@ zephyr_library_sources_ifdef(CONFIG_LP5562 lp5562.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_LP5569 lp5569.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PCA9633 pca9633.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_TLC59108 tlc59108.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_IS31FL3733 is31fl3733.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_LED_SHELL led_shell.c)
|
||||
|
||||
|
|
|
@ -38,5 +38,6 @@ source "drivers/led/Kconfig.pca9633"
|
|||
source "drivers/led/Kconfig.pwm"
|
||||
source "drivers/led/Kconfig.tlc59108"
|
||||
source "drivers/led/Kconfig.xec"
|
||||
source "drivers/led/Kconfig.is31fl3733"
|
||||
|
||||
endif # LED
|
||||
|
|
12
drivers/led/Kconfig.is31fl3733
Normal file
12
drivers/led/Kconfig.is31fl3733
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Copyright 2023 Daniel DeGrasse <daniel@degrasse.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config IS31FL3733
|
||||
bool "IS31FL3733 LED driver"
|
||||
default y
|
||||
depends on DT_HAS_ISSI_IS31FL3733_ENABLED
|
||||
select I2C
|
||||
help
|
||||
Enable LED driver for IS31FL3733.
|
||||
IS31FL3733 is a matrix LED driver, capable of a maximum of 3.29 mA
|
||||
per LED, or 42 mA total across all LEDs in the 12x16 dot matrix.
|
301
drivers/led/is31fl3733.c
Normal file
301
drivers/led/is31fl3733.c
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* Copyright 2022-2023 Daniel DeGrasse <daniel@degrasse.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#define DT_DRV_COMPAT issi_is31fl3733
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/drivers/led.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <zephyr/drivers/led/is31fl3733.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(is31fl3733, CONFIG_LED_LOG_LEVEL);
|
||||
|
||||
/* IS31FL3733 register definitions */
|
||||
#define CMD_SEL_REG 0xFD /* Command/page selection reg */
|
||||
#define CMD_SEL_LED 0x0 /* LED configuration page */
|
||||
#define CMD_SEL_PWM 0x1 /* PWM configuration page */
|
||||
#define CMD_SEL_FUNC 0x3 /* Function configuration page */
|
||||
|
||||
#define CMD_LOCK_REG 0xFE /* Command selection lock reg */
|
||||
#define CMD_LOCK_UNLOCK 0xC5 /* Command sel unlock value */
|
||||
|
||||
/* IS31FL3733 page specific register definitions */
|
||||
|
||||
/* Function configuration page */
|
||||
#define CONF_REG 0x0 /* configuration register */
|
||||
#define CONF_REG_SSD_MASK 0x1 /* Software shutdown mask */
|
||||
#define CONF_REG_SSD_SHIFT 0x0 /* Software shutdown shift */
|
||||
#define CONF_REG_SYNC_SHIFT 0x6 /* Sync mode shift */
|
||||
#define CONF_REG_SYNC_MASK 0xC /* Sync mode mask */
|
||||
|
||||
#define GLOBAL_CURRENT_CTRL_REG 0x1 /* global current control register */
|
||||
|
||||
#define RESET_REG 0x11 /* Reset all registers to POR state */
|
||||
|
||||
/* Matrix Layout definitions */
|
||||
#define IS31FL3733_ROW_COUNT 12
|
||||
#define IS31FL3733_COL_COUNT 16
|
||||
#define IS31FL3733_MAX_LED (IS31FL3733_ROW_COUNT * IS31FL3733_COL_COUNT)
|
||||
|
||||
/* Max brightness */
|
||||
#define IS31FL3733_MAX_BRIGHTNESS 100
|
||||
|
||||
struct is31fl3733_config {
|
||||
struct i2c_dt_spec bus;
|
||||
struct gpio_dt_spec sdb;
|
||||
uint8_t current_limit;
|
||||
uint8_t sync;
|
||||
};
|
||||
|
||||
struct is31fl3733_data {
|
||||
/* Active configuration page */
|
||||
uint32_t selected_page;
|
||||
/* Scratch buffer, used for bulk controller writes */
|
||||
uint8_t scratch_buf[IS31FL3733_MAX_LED + 1];
|
||||
/* LED config reg state, IS31FL3733 conf reg is write only */
|
||||
uint8_t conf_reg;
|
||||
};
|
||||
|
||||
/* Selects target register page for IS31FL3733. After setting the
|
||||
* target page, all I2C writes will use the selected page until the selected
|
||||
* page is changed.
|
||||
*/
|
||||
static int is31fl3733_select_page(const struct device *dev, uint8_t page)
|
||||
{
|
||||
const struct is31fl3733_config *config = dev->config;
|
||||
struct is31fl3733_data *data = dev->data;
|
||||
int ret = 0U;
|
||||
|
||||
if (data->selected_page == page) {
|
||||
/* No change necessary */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlock page selection register */
|
||||
ret = i2c_reg_write_byte_dt(&config->bus, CMD_LOCK_REG, CMD_LOCK_UNLOCK);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Could not unlock page selection register");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write to function select to select active page */
|
||||
ret = i2c_reg_write_byte_dt(&config->bus, CMD_SEL_REG, page);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Could not select active page");
|
||||
return ret;
|
||||
}
|
||||
data->selected_page = page;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int is31fl3733_led_set_brightness(const struct device *dev, uint32_t led, uint8_t value)
|
||||
{
|
||||
const struct is31fl3733_config *config = dev->config;
|
||||
int ret;
|
||||
uint8_t led_brightness = (uint8_t)(((uint32_t)value * 255) / 100);
|
||||
|
||||
if (led >= IS31FL3733_MAX_LED) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Configure PWM mode */
|
||||
ret = is31fl3733_select_page(dev, CMD_SEL_PWM);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return i2c_reg_write_byte_dt(&config->bus, led, led_brightness);
|
||||
}
|
||||
|
||||
static int is31fl3733_led_on(const struct device *dev, uint32_t led)
|
||||
{
|
||||
return is31fl3733_led_set_brightness(dev, led, IS31FL3733_MAX_BRIGHTNESS);
|
||||
}
|
||||
|
||||
static int is31fl3733_led_off(const struct device *dev, uint32_t led)
|
||||
{
|
||||
return is31fl3733_led_set_brightness(dev, led, 0);
|
||||
}
|
||||
|
||||
static int is31fl3733_led_write_channels(const struct device *dev, uint32_t start_channel,
|
||||
uint32_t num_channels, const uint8_t *buf)
|
||||
{
|
||||
const struct is31fl3733_config *config = dev->config;
|
||||
struct is31fl3733_data *data = dev->data;
|
||||
int ret = 0U;
|
||||
uint8_t *pwm_start;
|
||||
|
||||
if ((start_channel + num_channels) > IS31FL3733_MAX_LED) {
|
||||
return -EINVAL;
|
||||
}
|
||||
pwm_start = data->scratch_buf + start_channel;
|
||||
/* Set PWM and LED target registers as first byte of each transfer */
|
||||
*pwm_start = start_channel;
|
||||
memcpy((pwm_start + 1), buf, num_channels);
|
||||
|
||||
/* Write LED PWM states */
|
||||
ret = is31fl3733_select_page(dev, CMD_SEL_PWM);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
LOG_HEXDUMP_DBG(pwm_start, (num_channels + 1), "PWM states");
|
||||
|
||||
return i2c_write_dt(&config->bus, pwm_start, num_channels + 1);
|
||||
}
|
||||
|
||||
static int is31fl3733_init(const struct device *dev)
|
||||
{
|
||||
const struct is31fl3733_config *config = dev->config;
|
||||
struct is31fl3733_data *data = dev->data;
|
||||
int ret = 0U;
|
||||
uint8_t dummy;
|
||||
|
||||
if (!i2c_is_ready_dt(&config->bus)) {
|
||||
LOG_ERR("I2C device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (config->sdb.port != NULL) {
|
||||
if (!gpio_is_ready_dt(&config->sdb)) {
|
||||
LOG_ERR("GPIO SDB pin not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
/* Set SDB pin high to exit hardware shutdown */
|
||||
ret = gpio_pin_configure_dt(&config->sdb, GPIO_OUTPUT_ACTIVE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = is31fl3733_select_page(dev, CMD_SEL_FUNC);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* read reset reg to reset all registers to POR state,
|
||||
* in case we are booting from a warm reset.
|
||||
*/
|
||||
ret = i2c_reg_read_byte_dt(&config->bus, RESET_REG, &dummy);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Select function page after LED controller reset */
|
||||
ret = is31fl3733_select_page(dev, CMD_SEL_FUNC);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
/* Set global current control register based off devicetree value */
|
||||
ret = i2c_reg_write_byte_dt(&config->bus, GLOBAL_CURRENT_CTRL_REG,
|
||||
config->current_limit);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
/* As a final step, we exit software shutdown, disabling display
|
||||
* blanking. We also set the LED controller sync mode here.
|
||||
*/
|
||||
data->conf_reg = (config->sync << CONF_REG_SYNC_SHIFT) | CONF_REG_SSD_MASK;
|
||||
ret = i2c_reg_write_byte_dt(&config->bus, CONF_REG, data->conf_reg);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable all LEDs. We only control LED brightness in this driver. */
|
||||
data->scratch_buf[0] = 0x0;
|
||||
memset(data->scratch_buf + 1, 0xFF, (IS31FL3733_MAX_LED / 8));
|
||||
ret = is31fl3733_select_page(dev, CMD_SEL_LED);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return i2c_write_dt(&config->bus, data->scratch_buf,
|
||||
(IS31FL3733_MAX_LED / 8) + 1);
|
||||
}
|
||||
|
||||
/* Custom IS31FL3733 specific APIs */
|
||||
|
||||
/**
|
||||
* @brief Blanks IS31FL3733 LED display.
|
||||
*
|
||||
* When blank_en is set, the LED display will be disabled. This can be used for
|
||||
* flicker-free display updates, or power saving.
|
||||
*
|
||||
* @param dev: LED device structure
|
||||
* @param blank_en: should blanking be enabled
|
||||
* @return 0 on success, or negative value on error.
|
||||
*/
|
||||
int is31fl3733_blank(const struct device *dev, bool blank_en)
|
||||
{
|
||||
const struct is31fl3733_config *config = dev->config;
|
||||
struct is31fl3733_data *data = dev->data;
|
||||
int ret;
|
||||
|
||||
ret = is31fl3733_select_page(dev, CMD_SEL_FUNC);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (blank_en) {
|
||||
data->conf_reg &= ~CONF_REG_SSD_MASK;
|
||||
} else {
|
||||
data->conf_reg |= CONF_REG_SSD_MASK;
|
||||
}
|
||||
|
||||
return i2c_reg_write_byte_dt(&config->bus, CONF_REG, data->conf_reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets led current limit
|
||||
*
|
||||
* Sets the current limit for the LED driver. This is a separate value
|
||||
* from per-led brightness, and applies to all LEDs.
|
||||
* This value sets the output current limit according
|
||||
* to the following formula: (840/R_ISET) * (limit/256)
|
||||
* See table 14 of the datasheet for additional details.
|
||||
* @param dev: LED device structure
|
||||
* @param limit: current limit to apply
|
||||
* @return 0 on success, or negative value on error.
|
||||
*/
|
||||
int is31fl3733_current_limit(const struct device *dev, uint8_t limit)
|
||||
{
|
||||
const struct is31fl3733_config *config = dev->config;
|
||||
int ret;
|
||||
|
||||
ret = is31fl3733_select_page(dev, CMD_SEL_FUNC);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set global current control register */
|
||||
return i2c_reg_write_byte_dt(&config->bus, GLOBAL_CURRENT_CTRL_REG, limit);
|
||||
}
|
||||
|
||||
static const struct led_driver_api is31fl3733_api = {
|
||||
.on = is31fl3733_led_on,
|
||||
.off = is31fl3733_led_off,
|
||||
.set_brightness = is31fl3733_led_set_brightness,
|
||||
.write_channels = is31fl3733_led_write_channels,
|
||||
};
|
||||
|
||||
#define IS31FL3733_DEVICE(n) \
|
||||
static const struct is31fl3733_config is31fl3733_config_##n = { \
|
||||
.bus = I2C_DT_SPEC_INST_GET(n), \
|
||||
.sdb = GPIO_DT_SPEC_INST_GET_OR(n, sdb_gpios, {}), \
|
||||
.current_limit = DT_INST_PROP(n, current_limit), \
|
||||
.sync = DT_INST_ENUM_IDX(n, sync_mode), \
|
||||
}; \
|
||||
\
|
||||
static struct is31fl3733_data is31fl3733_data_##n = { \
|
||||
.selected_page = CMD_SEL_LED, \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, &is31fl3733_init, NULL, &is31fl3733_data_##n, \
|
||||
&is31fl3733_config_##n, POST_KERNEL, CONFIG_LED_INIT_PRIORITY, \
|
||||
&is31fl3733_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(IS31FL3733_DEVICE)
|
38
dts/bindings/led/issi,is31fl3733.yaml
Normal file
38
dts/bindings/led/issi,is31fl3733.yaml
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Copyright 2023 Daniel DeGrasse <daniel@degrasse.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
description: ISSI IS31FL3733 LED Matrix Driver
|
||||
|
||||
compatible: "issi,is31fl3733"
|
||||
|
||||
include: "i2c-device.yaml"
|
||||
|
||||
properties:
|
||||
sdb-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
Hardware shutdown pin. If routed on the board, this property must be
|
||||
present. Set to a logical 1 at boot to exit the device from hardware
|
||||
shutdown.
|
||||
|
||||
current-limit:
|
||||
type: int
|
||||
default: 0xFF
|
||||
description: |
|
||||
Global current limit. Sets the global current control register of LED
|
||||
driver (set table 14). Limits global current based on the following
|
||||
formula: (840/R_ISET) * (current-limit/256). Defaults to max value
|
||||
of 0xFF, so led output will still be enabled if property is
|
||||
not provided.
|
||||
|
||||
sync-mode:
|
||||
type: string
|
||||
default: "none"
|
||||
enum:
|
||||
- "none"
|
||||
- "master"
|
||||
- "slave"
|
||||
description: |
|
||||
This property configures the LED controller as a master or slave
|
||||
clock device. This can be used to synchronize the output of multiple
|
||||
LED controllers. See SYNC bits in led configuration register for more
|
||||
information.
|
36
include/zephyr/drivers/led/is31fl3733.h
Normal file
36
include/zephyr/drivers/led/is31fl3733.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2023 Daniel DeGrasse <daniel@degrasse.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_LED_IS31FL3733_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_LED_IS31FL3733_H_
|
||||
|
||||
/**
|
||||
* @brief Blanks IS31FL3733 LED display.
|
||||
*
|
||||
* When blank_en is set, the LED display will be disabled. This can be used for
|
||||
* flicker-free display updates, or power saving.
|
||||
*
|
||||
* @param dev: LED device structure
|
||||
* @param blank_en: should blanking be enabled
|
||||
* @return 0 on success, or negative value on error.
|
||||
*/
|
||||
int is31fl3733_blank(const struct device *dev, bool blank_en);
|
||||
|
||||
/**
|
||||
* @brief Sets led current limit
|
||||
*
|
||||
* Sets the current limit for the LED driver. This is a separate value
|
||||
* from per-led brightness, and applies to all LEDs.
|
||||
* This value sets the output current limit according
|
||||
* to the following formula: (840/R_ISET) * (limit/256)
|
||||
* See table 14 of the datasheet for additional details.
|
||||
* @param dev: LED device structure
|
||||
* @param limit: current limit to apply
|
||||
* @return 0 on success, or negative value on error.
|
||||
*/
|
||||
int is31fl3733_current_limit(const struct device *dev, uint8_t limit);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_LED_IS31FL3733_H_ */
|
Loading…
Reference in a new issue