1 /*
2 * Copyright (c) 2020 Seagate Technology LLC
3 * Copyright (c) 2022 Grinn
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /**
9 * @file
10 * @brief LP50xx LED controller
11 */
12 #include <errno.h>
13 #include <zephyr/device.h>
14 #include <zephyr/devicetree.h>
15 #include <zephyr/drivers/gpio.h>
16 #include <zephyr/drivers/i2c.h>
17 #include <zephyr/drivers/led.h>
18 #include <zephyr/drivers/led/lp50xx.h>
19 #include <zephyr/kernel.h>
20 #include <zephyr/pm/device.h>
21 #include <zephyr/sys/util.h>
22
23 #include <zephyr/logging/log.h>
24 LOG_MODULE_REGISTER(lp50xx, CONFIG_LED_LOG_LEVEL);
25
26 #define LP50XX_MAX_BRIGHTNESS 100U
27
28 /*
29 * Number of supported RGB led modules per chipset.
30 *
31 * For each module, there are 4 associated registers:
32 * - 1 brightness register
33 * - 3 color registers (RGB)
34 *
35 * A chipset can have more modules than leds. In this case, the
36 * associated registers will simply be inactive.
37 */
38 #define LP5012_NUM_MODULES 4
39 #define LP5024_NUM_MODULES 8
40 #define LP5036_NUM_MODULES 12
41
42 /* Maximum number of channels */
43 #define LP50XX_MAX_CHANNELS(nmodules) \
44 ((LP50XX_COLORS_PER_LED + 1) * ((nmodules) + 1))
45
46 #define LP50XX_DISABLE_DELAY_US 3
47 #define LP50XX_ENABLE_DELAY_US 500
48
49 /* Base registers */
50 #define LP50XX_DEVICE_CONFIG0 0x00
51 #define LP50XX_DEVICE_CONFIG1 0x01
52 #define LP50XX_LED_CONFIG0 0x02
53
54 #define LP50XX_BANK_BASE(nmodules) \
55 (0x03 + (((nmodules) - 1) / 8))
56
57 #define LP50XX_LED0_BRIGHTNESS(nmodules) \
58 ((LP50XX_BANK_BASE(nmodules)) + 4)
59
60 #define LP50XX_OUT0_COLOR(nmodules) \
61 (LP50XX_LED0_BRIGHTNESS(nmodules) + (nmodules))
62
63 #define LP50XX_RESET(nmodules) \
64 (LP50XX_OUT0_COLOR(nmodules) + LP50XX_COLORS_PER_LED * (nmodules))
65
66 /* Register values */
67 #define CONFIG0_CHIP_EN BIT(6)
68
69 #define CONFIG1_LED_GLOBAL_OFF BIT(0)
70 #define CONFIG1_MAX_CURRENT_OPT BIT(1)
71 #define CONFIG1_PWM_DITHERING_EN BIT(2)
72 #define CONFIG1_AUTO_INCR_EN BIT(3)
73 #define CONFIG1_POWER_SAVE_EN BIT(4)
74 #define CONFIG1_LOG_SCALE_EN BIT(5)
75
76 #define RESET_SW 0xFF
77
78 struct lp50xx_config {
79 struct i2c_dt_spec bus;
80 const struct gpio_dt_spec gpio_enable;
81 uint8_t num_modules;
82 uint8_t max_leds;
83 uint8_t num_leds;
84 bool log_scale_en;
85 bool max_curr_opt;
86 const struct led_info *leds_info;
87 };
88
89 struct lp50xx_data {
90 uint8_t *chan_buf;
91 };
92
lp50xx_led_to_info(const struct lp50xx_config * config,uint32_t led)93 static const struct led_info *lp50xx_led_to_info(
94 const struct lp50xx_config *config, uint32_t led)
95 {
96 if (led < config->num_leds) {
97 return &config->leds_info[led];
98 }
99
100 return NULL;
101 }
102
lp50xx_get_info(const struct device * dev,uint32_t led,const struct led_info ** info)103 static int lp50xx_get_info(const struct device *dev, uint32_t led,
104 const struct led_info **info)
105 {
106 const struct lp50xx_config *config = dev->config;
107 const struct led_info *led_info = lp50xx_led_to_info(config, led);
108
109 if (!led_info) {
110 return -EINVAL;
111 }
112
113 *info = led_info;
114
115 return 0;
116 }
117
lp50xx_set_brightness(const struct device * dev,uint32_t led,uint8_t value)118 static int lp50xx_set_brightness(const struct device *dev,
119 uint32_t led, uint8_t value)
120 {
121 const struct lp50xx_config *config = dev->config;
122 const struct led_info *led_info = lp50xx_led_to_info(config, led);
123 uint8_t buf[2];
124
125 if (!led_info) {
126 return -ENODEV;
127 }
128
129 if (value > LP50XX_MAX_BRIGHTNESS) {
130 LOG_ERR("%s: brightness value out of bounds: val=%d, max=%d",
131 dev->name, value, LP50XX_MAX_BRIGHTNESS);
132 return -EINVAL;
133 }
134
135 buf[0] = LP50XX_LED0_BRIGHTNESS(config->num_modules) + led_info->index;
136 buf[1] = (value * 0xff) / 100;
137
138 return i2c_write_dt(&config->bus, buf, sizeof(buf));
139 }
140
lp50xx_on(const struct device * dev,uint32_t led)141 static int lp50xx_on(const struct device *dev, uint32_t led)
142 {
143 return lp50xx_set_brightness(dev, led, 100);
144 }
145
lp50xx_off(const struct device * dev,uint32_t led)146 static int lp50xx_off(const struct device *dev, uint32_t led)
147 {
148 return lp50xx_set_brightness(dev, led, 0);
149 }
150
lp50xx_set_color(const struct device * dev,uint32_t led,uint8_t num_colors,const uint8_t * color)151 static int lp50xx_set_color(const struct device *dev, uint32_t led,
152 uint8_t num_colors, const uint8_t *color)
153 {
154 const struct lp50xx_config *config = dev->config;
155 const struct led_info *led_info = lp50xx_led_to_info(config, led);
156 uint8_t buf[4];
157
158 if (!led_info) {
159 return -ENODEV;
160 }
161
162 if (num_colors != led_info->num_colors) {
163 LOG_ERR("%s: invalid number of colors: got=%d, expected=%d",
164 dev->name,
165 num_colors,
166 led_info->num_colors);
167 return -EINVAL;
168 }
169
170 buf[0] = LP50XX_OUT0_COLOR(config->num_modules);
171 buf[0] += LP50XX_COLORS_PER_LED * led_info->index;
172
173 buf[1] = color[0];
174 buf[2] = color[1];
175 buf[3] = color[2];
176
177 return i2c_write_dt(&config->bus, buf, sizeof(buf));
178 }
179
lp50xx_write_channels(const struct device * dev,uint32_t start_channel,uint32_t num_channels,const uint8_t * buf)180 static int lp50xx_write_channels(const struct device *dev,
181 uint32_t start_channel,
182 uint32_t num_channels, const uint8_t *buf)
183 {
184 const struct lp50xx_config *config = dev->config;
185 struct lp50xx_data *data = dev->data;
186 uint8_t base_channel, end_channel, max_channels;
187
188 base_channel = LP50XX_BANK_BASE(config->num_modules);
189 end_channel = base_channel + start_channel + num_channels;
190 max_channels = base_channel + LP50XX_MAX_CHANNELS(config->num_modules);
191
192 if (end_channel > max_channels) {
193 return -EINVAL;
194 }
195
196 /*
197 * Unfortunately this controller doesn't support commands split into
198 * two I2C messages.
199 */
200 data->chan_buf[0] = base_channel + start_channel;
201 memcpy(data->chan_buf + 1, buf, num_channels);
202
203 return i2c_write_dt(&config->bus, data->chan_buf, num_channels + 1);
204 }
205
lp50xx_reset(const struct device * dev)206 static int lp50xx_reset(const struct device *dev)
207 {
208 const struct lp50xx_config *config = dev->config;
209 uint8_t buf[2];
210 int err;
211
212 /* Software reset */
213 buf[0] = LP50XX_RESET(config->num_modules);
214 buf[1] = RESET_SW;
215 err = i2c_write_dt(&config->bus, buf, 2);
216 if (err < 0) {
217 return err;
218 }
219
220 /* After reset, apply configuration since all registers are reset. */
221 buf[0] = LP50XX_DEVICE_CONFIG1;
222 buf[1] = CONFIG1_PWM_DITHERING_EN | CONFIG1_AUTO_INCR_EN
223 | CONFIG1_POWER_SAVE_EN;
224 if (config->max_curr_opt) {
225 buf[1] |= CONFIG1_MAX_CURRENT_OPT;
226 }
227 if (config->log_scale_en) {
228 buf[1] |= CONFIG1_LOG_SCALE_EN;
229 }
230
231 return i2c_write_dt(&config->bus, buf, 2);
232 }
233
lp50xx_hw_enable(const struct device * dev,bool enable)234 static int lp50xx_hw_enable(const struct device *dev, bool enable)
235 {
236 const struct lp50xx_config *config = dev->config;
237 int err;
238
239 if (config->gpio_enable.port == NULL) {
240 /* Nothing to do */
241 return 0;
242 }
243
244 err = gpio_pin_set_dt(&config->gpio_enable, enable);
245 if (err < 0) {
246 LOG_ERR("%s: failed to set enable gpio", dev->name);
247 return err;
248 }
249
250 k_usleep(enable ? LP50XX_ENABLE_DELAY_US : LP50XX_DISABLE_DELAY_US);
251
252 return 0;
253 }
254
lp50xx_enable(const struct device * dev,bool enable)255 static int lp50xx_enable(const struct device *dev, bool enable)
256 {
257 const struct lp50xx_config *config = dev->config;
258 uint8_t value = enable ? CONFIG0_CHIP_EN : 0;
259
260 return i2c_reg_update_byte_dt(&config->bus,
261 LP50XX_DEVICE_CONFIG0,
262 CONFIG0_CHIP_EN,
263 value);
264 }
265
lp50xx_init(const struct device * dev)266 static int lp50xx_init(const struct device *dev)
267 {
268 const struct lp50xx_config *config = dev->config;
269 int err;
270
271 if (!i2c_is_ready_dt(&config->bus)) {
272 LOG_ERR("%s: I2C device not ready", dev->name);
273 return -ENODEV;
274 }
275
276 if (config->num_leds > config->max_leds) {
277 LOG_ERR("%s: invalid number of LEDs %d (max %d)",
278 dev->name,
279 config->num_leds,
280 config->max_leds);
281 return -EINVAL;
282 }
283
284 /* Configure GPIO if present */
285 if (config->gpio_enable.port != NULL) {
286 if (!gpio_is_ready_dt(&config->gpio_enable)) {
287 LOG_ERR("%s: enable gpio is not ready", dev->name);
288 return -ENODEV;
289 }
290
291 err = gpio_pin_configure_dt(&config->gpio_enable,
292 GPIO_OUTPUT_INACTIVE);
293 if (err < 0) {
294 LOG_ERR("%s: failed to initialize enable gpio",
295 dev->name);
296 return err;
297 }
298 }
299
300 /* Enable hardware */
301 err = lp50xx_hw_enable(dev, true);
302 if (err < 0) {
303 LOG_ERR("%s: failed to enable hardware", dev->name);
304 return err;
305 }
306
307 /* Reset device */
308 err = lp50xx_reset(dev);
309 if (err < 0) {
310 LOG_ERR("%s: failed to reset", dev->name);
311 return err;
312 }
313
314 /* Enable device */
315 err = lp50xx_enable(dev, true);
316 if (err < 0) {
317 LOG_ERR("%s: failed to enable", dev->name);
318 return err;
319 }
320
321 return 0;
322 }
323
324 #ifdef CONFIG_PM_DEVICE
lp50xx_pm_action(const struct device * dev,enum pm_device_action action)325 static int lp50xx_pm_action(const struct device *dev,
326 enum pm_device_action action)
327 {
328 switch (action) {
329 case PM_DEVICE_ACTION_SUSPEND:
330 return lp50xx_enable(dev, false);
331 case PM_DEVICE_ACTION_RESUME:
332 return lp50xx_enable(dev, true);
333 default:
334 return -ENOTSUP;
335 }
336
337 return 0;
338 }
339 #endif /* CONFIG_PM_DEVICE */
340
341 static DEVICE_API(led, lp50xx_led_api) = {
342 .on = lp50xx_on,
343 .off = lp50xx_off,
344 .get_info = lp50xx_get_info,
345 .set_brightness = lp50xx_set_brightness,
346 .set_color = lp50xx_set_color,
347 .write_channels = lp50xx_write_channels,
348 };
349
350 #define COLOR_MAPPING(led_node_id) \
351 const uint8_t color_mapping_##led_node_id[] = \
352 DT_PROP(led_node_id, color_mapping);
353
354 #define LED_INFO(led_node_id) \
355 { \
356 .label = DT_PROP(led_node_id, label), \
357 .index = DT_PROP(led_node_id, index), \
358 .num_colors = \
359 DT_PROP_LEN(led_node_id, color_mapping), \
360 .color_mapping = color_mapping_##led_node_id, \
361 },
362
363 #define LP50XX_DEVICE(n, id, nmodules) \
364 DT_INST_FOREACH_CHILD(n, COLOR_MAPPING) \
365 \
366 static const struct led_info lp##id##_leds_##n[] = { \
367 DT_INST_FOREACH_CHILD(n, LED_INFO) \
368 }; \
369 \
370 static const struct lp50xx_config lp##id##_config_##n = { \
371 .bus = I2C_DT_SPEC_INST_GET(n), \
372 .gpio_enable = \
373 GPIO_DT_SPEC_INST_GET_OR(n, enable_gpios, {0}), \
374 .num_modules = nmodules, \
375 .max_leds = LP##id##_MAX_LEDS, \
376 .num_leds = ARRAY_SIZE(lp##id##_leds_##n), \
377 .log_scale_en = DT_INST_PROP(n, log_scale_en), \
378 .max_curr_opt = DT_INST_PROP(n, max_curr_opt), \
379 .leds_info = lp##id##_leds_##n, \
380 }; \
381 \
382 static uint8_t lp##id##_chan_buf_##n[LP50XX_MAX_CHANNELS(nmodules) + 1];\
383 \
384 static struct lp50xx_data lp##id##_data_##n = { \
385 .chan_buf = lp##id##_chan_buf_##n, \
386 }; \
387 \
388 PM_DEVICE_DT_INST_DEFINE(n, lp50xx_pm_action); \
389 \
390 DEVICE_DT_INST_DEFINE(n, \
391 lp50xx_init, \
392 PM_DEVICE_DT_INST_GET(n), \
393 &lp##id##_data_##n, \
394 &lp##id##_config_##n, \
395 POST_KERNEL, CONFIG_LED_INIT_PRIORITY, \
396 &lp50xx_led_api);
397
398 #undef DT_DRV_COMPAT
399 #define DT_DRV_COMPAT ti_lp5009
400 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5009, LP5012_NUM_MODULES)
401
402 #undef DT_DRV_COMPAT
403 #define DT_DRV_COMPAT ti_lp5012
404 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5012, LP5012_NUM_MODULES)
405
406 #undef DT_DRV_COMPAT
407 #define DT_DRV_COMPAT ti_lp5018
408 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5018, LP5024_NUM_MODULES)
409
410 #undef DT_DRV_COMPAT
411 #define DT_DRV_COMPAT ti_lp5024
412 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5024, LP5024_NUM_MODULES)
413
414 #undef DT_DRV_COMPAT
415 #define DT_DRV_COMPAT ti_lp5030
416 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5030, LP5036_NUM_MODULES)
417
418 #undef DT_DRV_COMPAT
419 #define DT_DRV_COMPAT ti_lp5036
420 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5036, LP5036_NUM_MODULES)
421