Skip to content

Commit b37ea90

Browse files
simonguinotcarlescufi
authored andcommitted
drivers: led: add driver for TI LP503x controllers
This patch adds support for the Texas Instruments LP5030 and LP5036 I2C LED controllers. They are respectively providing up to 30 and 36 channels (i.e. 10 or 12 RGB LEDs). In addition to the channel/color registers this LED controller provides a per-LED brigthness register. This driver implements both LED-based and channel-based API methods: - led_on - led_off - led_get_info - led_set_brightness - led_set_color - led_set_channel - led_write_channels Signed-off-by: Simon Guinot <simon.guinot@seagate.com>
1 parent 4f591e5 commit b37ea90

File tree

5 files changed

+336
-0
lines changed

5 files changed

+336
-0
lines changed

drivers/led/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
zephyr_sources_ifdef(CONFIG_HT16K33 ht16k33.c)
44
zephyr_sources_ifdef(CONFIG_LP3943 lp3943.c)
5+
zephyr_sources_ifdef(CONFIG_LP503X lp503x.c)
56
zephyr_sources_ifdef(CONFIG_LP5562 lp5562.c)
67
zephyr_sources_ifdef(CONFIG_PCA9633 pca9633.c)
78

drivers/led/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ config LED_INIT_PRIORITY
2222

2323
source "drivers/led/Kconfig.ht16k33"
2424
source "drivers/led/Kconfig.lp3943"
25+
source "drivers/led/Kconfig.lp503x"
2526
source "drivers/led/Kconfig.lp5562"
2627
source "drivers/led/Kconfig.pca9633"
2728

drivers/led/Kconfig.lp503x

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2020 Seagate Technology LLC
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config LP503X
5+
bool "LP503X LED controller"
6+
depends on I2C
7+
help
8+
Enable driver for the Texas Instruments LP5030 and LP5036 I2C LED
9+
controllers. They are respectively supporting up to 10 and 12 LEDs.

drivers/led/lp503x.c

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
/*
2+
* Copyright (c) 2020 Seagate Technology LLC
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT ti_lp503x
8+
9+
/**
10+
* @file
11+
* @brief LP503x LED controller
12+
*/
13+
14+
#include <drivers/i2c.h>
15+
#include <drivers/led.h>
16+
#include <drivers/led/lp503x.h>
17+
#include <device.h>
18+
#include <zephyr.h>
19+
20+
#define LOG_LEVEL CONFIG_LED_LOG_LEVEL
21+
#include <logging/log.h>
22+
LOG_MODULE_REGISTER(lp503x);
23+
24+
#define LP503X_DEVICE_CONFIG0 0
25+
#define CONFIG0_CHIP_EN BIT(6)
26+
27+
#define LP503X_DEVICE_CONFIG1 0x1
28+
#define CONFIG1_LED_GLOBAL_OFF BIT(0)
29+
#define CONFIG1_MAX_CURRENT_OPT BIT(1)
30+
#define CONFIG1_PWM_DITHERING_EN BIT(2)
31+
#define CONFIG1_AUTO_INCR_EN BIT(3)
32+
#define CONFIG1_POWER_SAVE_EN BIT(4)
33+
#define CONFIG1_LOG_SCALE_EN BIT(5)
34+
35+
#define LP503X_LED_CONFIG0 0x2
36+
#define CONFIG0_LED0_BANK_EN BIT(0)
37+
#define CONFIG0_LED1_BANK_EN BIT(1)
38+
#define CONFIG0_LED2_BANK_EN BIT(2)
39+
#define CONFIG0_LED3_BANK_EN BIT(3)
40+
#define CONFIG0_LED4_BANK_EN BIT(4)
41+
#define CONFIG0_LED5_BANK_EN BIT(5)
42+
#define CONFIG0_LED6_BANK_EN BIT(6)
43+
#define CONFIG0_LED7_BANK_EN BIT(7)
44+
45+
#define LP503X_LED_CONFIG1 0x3
46+
#define CONFIG1_LED8_BANK_EN BIT(0)
47+
#define CONFIG1_LED9_BANK_EN BIT(1)
48+
#define CONFIG1_LED10_BANK_EN BIT(2)
49+
#define CONFIG1_LED11_BANK_EN BIT(3)
50+
51+
#define LP503X_BANK_BRIGHTNESS 0x4
52+
#define LP503X_BANK_A_COLOR 0x5
53+
#define LP503X_BANK_B_COLOR 0x6
54+
#define LP503X_BANK_C_COLOR 0x7
55+
56+
#define LP503X_LED_BRIGHTNESS_BASE 0x8
57+
#define LP503X_OUT_COLOR_BASE 0x14
58+
59+
/* Expose channels starting from the bank registers. */
60+
#define LP503X_CHANNEL_BASE LP503X_BANK_BRIGHTNESS
61+
62+
#define DEV_CFG(dev) ((const struct lp503x_config *) ((dev)->config))
63+
#define DEV_DATA(dev) ((struct lp503x_data *) ((dev)->data))
64+
65+
struct lp503x_config {
66+
char *i2c_bus_label;
67+
uint8_t i2c_addr;
68+
uint8_t num_leds;
69+
bool log_scale_en;
70+
bool max_curr_opt;
71+
const struct led_info *leds_info;
72+
};
73+
74+
struct lp503x_data {
75+
struct device *i2c;
76+
uint8_t *chan_buf;
77+
};
78+
79+
static const struct led_info *
80+
lp503x_led_to_info(const struct lp503x_config *config, uint32_t led)
81+
{
82+
int i;
83+
84+
for (i = 0; i < config->num_leds; i++) {
85+
if (config->leds_info[i].index == led) {
86+
return &config->leds_info[i];
87+
}
88+
}
89+
return NULL;
90+
}
91+
92+
static int lp503x_get_info(struct device *dev, uint32_t led,
93+
const struct led_info **info)
94+
{
95+
const struct lp503x_config *config = DEV_CFG(dev);
96+
const struct led_info *led_info = lp503x_led_to_info(config, led);
97+
98+
if (!led_info) {
99+
return -EINVAL;
100+
}
101+
102+
*info = led_info;
103+
104+
return 0;
105+
}
106+
107+
static int lp503x_set_brightness(struct device *dev,
108+
uint32_t led, uint8_t value)
109+
{
110+
const struct lp503x_config *config = DEV_CFG(dev);
111+
struct lp503x_data *data = DEV_DATA(dev);
112+
const struct led_info *led_info = lp503x_led_to_info(config, led);
113+
uint8_t buf[2];
114+
115+
if (!led_info || value > 100) {
116+
return -EINVAL;
117+
}
118+
119+
buf[0] = LP503X_LED_BRIGHTNESS_BASE + led_info->index;
120+
buf[1] = (value * 0xff) / 100;
121+
122+
return i2c_write(data->i2c, buf, sizeof(buf), config->i2c_addr);
123+
}
124+
125+
static int lp503x_on(struct device *dev, uint32_t led)
126+
{
127+
return lp503x_set_brightness(dev, led, 100);
128+
}
129+
130+
static int lp503x_off(struct device *dev, uint32_t led)
131+
{
132+
return lp503x_set_brightness(dev, led, 0);
133+
}
134+
135+
static int lp503x_set_color(struct device *dev, uint32_t led,
136+
uint8_t num_colors, const uint8_t *color)
137+
{
138+
const struct lp503x_config *config = DEV_CFG(dev);
139+
struct lp503x_data *data = DEV_DATA(dev);
140+
const struct led_info *led_info = lp503x_led_to_info(config, led);
141+
uint8_t buf[4];
142+
143+
if (!led_info || num_colors != led_info->num_colors) {
144+
return -EINVAL;
145+
}
146+
147+
buf[0] = LP503X_OUT_COLOR_BASE + 3 * led_info->index;
148+
buf[1] = color[0];
149+
buf[2] = color[1];
150+
buf[3] = color[2];
151+
152+
return i2c_write(data->i2c, buf, sizeof(buf), config->i2c_addr);
153+
}
154+
155+
static int lp503x_write_channels(struct device *dev, uint32_t start_channel,
156+
uint32_t num_channels, const uint8_t *buf)
157+
{
158+
const struct lp503x_config *config = DEV_CFG(dev);
159+
struct lp503x_data *data = DEV_DATA(dev);
160+
161+
if (start_channel >= LP503X_NUM_CHANNELS ||
162+
start_channel + num_channels > LP503X_NUM_CHANNELS) {
163+
return -EINVAL;
164+
}
165+
166+
/*
167+
* Unfortunately this controller don't support commands split into
168+
* two I2C messages.
169+
*/
170+
data->chan_buf[0] = LP503X_CHANNEL_BASE + start_channel;
171+
memcpy(data->chan_buf + 1, buf, num_channels);
172+
173+
return i2c_write(data->i2c, data->chan_buf,
174+
num_channels + 1, config->i2c_addr);
175+
}
176+
177+
static int lp503x_init(struct device *dev)
178+
{
179+
const struct lp503x_config *config = DEV_CFG(dev);
180+
struct lp503x_data *data = DEV_DATA(dev);
181+
uint8_t buf[3];
182+
int err;
183+
184+
data->i2c = device_get_binding(config->i2c_bus_label);
185+
if (data->i2c == NULL) {
186+
LOG_ERR("%s: device %s not found",
187+
dev->name, config->i2c_bus_label);
188+
return -ENODEV;
189+
}
190+
if (config->num_leds > LP503X_MAX_LEDS) {
191+
LOG_ERR("%s: invalid number of LEDs %d (max %d)",
192+
dev->name, config->num_leds, LP503X_MAX_LEDS);
193+
return -EINVAL;
194+
}
195+
196+
/*
197+
* Since the status of the LP503x controller is unknown when entering
198+
* this function, and since there is no way to reset it, then the whole
199+
* configuration must be applied.
200+
*/
201+
202+
/* Disable bank control for all LEDs. */
203+
buf[0] = LP503X_LED_CONFIG0;
204+
buf[1] = 0;
205+
buf[2] = 0;
206+
err = i2c_write(data->i2c, buf, 3, config->i2c_addr);
207+
if (err < 0) {
208+
return err;
209+
}
210+
211+
/* Enable LED controller. */
212+
buf[0] = LP503X_DEVICE_CONFIG0;
213+
buf[1] = CONFIG0_CHIP_EN;
214+
err = i2c_write(data->i2c, buf, 2, config->i2c_addr);
215+
if (err < 0) {
216+
return err;
217+
}
218+
219+
/* Apply configuration. */
220+
buf[0] = LP503X_DEVICE_CONFIG1;
221+
buf[1] = CONFIG1_PWM_DITHERING_EN | CONFIG1_AUTO_INCR_EN
222+
| CONFIG1_POWER_SAVE_EN;
223+
if (config->max_curr_opt) {
224+
buf[1] |= CONFIG1_MAX_CURRENT_OPT;
225+
}
226+
if (config->log_scale_en) {
227+
buf[1] |= CONFIG1_LOG_SCALE_EN;
228+
}
229+
230+
return i2c_write(data->i2c, buf, 2, config->i2c_addr);
231+
}
232+
233+
static const struct led_driver_api lp503x_led_api = {
234+
.on = lp503x_on,
235+
.off = lp503x_off,
236+
.get_info = lp503x_get_info,
237+
.set_brightness = lp503x_set_brightness,
238+
.set_color = lp503x_set_color,
239+
.write_channels = lp503x_write_channels,
240+
};
241+
242+
#define COLOR_MAPPING(led_node_id) \
243+
const uint8_t color_mapping_##led_node_id[] = \
244+
DT_PROP(led_node_id, color_mapping);
245+
246+
#define LED_INFO(led_node_id) \
247+
{ \
248+
.label = DT_LABEL(led_node_id), \
249+
.index = DT_PROP(led_node_id, index), \
250+
.num_colors = \
251+
DT_PROP_LEN(led_node_id, color_mapping), \
252+
.color_mapping = color_mapping_##led_node_id, \
253+
},
254+
255+
#define LP503x_DEVICE(id) \
256+
\
257+
DT_INST_FOREACH_CHILD(id, COLOR_MAPPING) \
258+
\
259+
const struct led_info lp503x_leds_##id[] = { \
260+
DT_INST_FOREACH_CHILD(id, LED_INFO) \
261+
}; \
262+
\
263+
static uint8_t lp503x_chan_buf_##id[LP503X_NUM_CHANNELS + 1]; \
264+
\
265+
static struct lp503x_config lp503x_config_##id = { \
266+
.i2c_bus_label = DT_INST_BUS_LABEL(id), \
267+
.i2c_addr = DT_INST_REG_ADDR(id), \
268+
.num_leds = ARRAY_SIZE(lp503x_leds_##id), \
269+
.max_curr_opt = DT_INST_PROP(id, max_curr_opt), \
270+
.log_scale_en = DT_INST_PROP(id, log_scale_en), \
271+
.leds_info = lp503x_leds_##id, \
272+
}; \
273+
\
274+
static struct lp503x_data lp503x_data_##id = { \
275+
.chan_buf = lp503x_chan_buf_##id, \
276+
}; \
277+
\
278+
DEVICE_AND_API_INIT(lp503x_led_##id, DT_INST_LABEL(id), \
279+
&lp503x_init, \
280+
&lp503x_data_##id, \
281+
&lp503x_config_##id, \
282+
POST_KERNEL, CONFIG_LED_INIT_PRIORITY, \
283+
&lp503x_led_api);
284+
285+
DT_INST_FOREACH_STATUS_OKAY(LP503x_DEVICE)

include/drivers/led/lp503x.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2020 Seagate Technology LLC
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
8+
#ifndef ZEPHYR_INCLUDE_DRIVERS_LED_LP503X_H_
9+
#define ZEPHYR_INCLUDE_DRIVERS_LED_LP503X_H_
10+
11+
#define LP503X_MAX_LEDS 12
12+
#define LP503X_COLORS_PER_LED 3
13+
14+
/*
15+
* LED channels mapping.
16+
*/
17+
18+
#define LP503X_NUM_CHANNELS 52
19+
20+
/* Bank channels. */
21+
#define LP503X_BANK_CHAN_BASE 0
22+
#define LP503X_BANK_BRIGHT_CHAN LP503X_BANK_CHAN_BASE
23+
#define LP503X_BANK_COL1_CHAN(led) (LP503X_BANK_CHAN_BASE + 1)
24+
#define LP503X_BANK_COL2_CHAN(led) (LP503X_BANK_CHAN_BASE + 2)
25+
#define LP503X_BANK_COL3_CHAN(led) (LP503X_BANK_CHAN_BASE + 3)
26+
27+
/* LED brightness channels. */
28+
#define LP503X_LED_BRIGHT_CHAN_BASE 4
29+
#define LP503X_LED_BRIGHT_CHAN(led) (LP503X_LED_BRIGHT_CHAN_BASE + led)
30+
31+
/* LED color channels. */
32+
#define LP503X_LED_COL_CHAN_BASE 16
33+
#define LP503X_LED_COL1_CHAN(led) (LP503X_LED_COL_CHAN_BASE + \
34+
led * LP503X_COLORS_PER_LED)
35+
#define LP503X_LED_COL2_CHAN(led) (LP503X_LED_COL_CHAN_BASE + \
36+
led * LP503X_COLORS_PER_LED + 1)
37+
#define LP503X_LED_COL3_CHAN(led) (LP503X_LED_COL_CHAN_BASE + \
38+
led * LP503X_COLORS_PER_LED + 2)
39+
40+
#endif /* ZEPHYR_INCLUDE_DRIVERS_LED_LP503X_H_ */

0 commit comments

Comments
 (0)