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[LP50XX_COLORS_PER_LED + 1];
157 uint8_t i;
158
159 if (!led_info) {
160 return -ENODEV;
161 }
162
163 if (num_colors != led_info->num_colors) {
164 LOG_ERR("%s: invalid number of colors: got=%d, expected=%d",
165 dev->name,
166 num_colors,
167 led_info->num_colors);
168 return -EINVAL;
169 }
170
171 buf[0] = LP50XX_OUT0_COLOR(config->num_modules);
172 buf[0] += LP50XX_COLORS_PER_LED * led_info->index;
173
174 for (i = 0; i < led_info->num_colors; i++) {
175 buf[1 + i] = color[i];
176 }
177
178 return i2c_write_dt(&config->bus, buf, led_info->num_colors + 1);
179 }
180
lp50xx_write_channels(const struct device * dev,uint32_t start_channel,uint32_t num_channels,const uint8_t * buf)181 static int lp50xx_write_channels(const struct device *dev,
182 uint32_t start_channel,
183 uint32_t num_channels, const uint8_t *buf)
184 {
185 const struct lp50xx_config *config = dev->config;
186 struct lp50xx_data *data = dev->data;
187 uint8_t base_channel, end_channel, max_channels;
188
189 base_channel = LP50XX_BANK_BASE(config->num_modules);
190 end_channel = base_channel + start_channel + num_channels;
191 max_channels = base_channel + LP50XX_MAX_CHANNELS(config->num_modules);
192
193 if (end_channel > max_channels) {
194 return -EINVAL;
195 }
196
197 /*
198 * Unfortunately this controller doesn't support commands split into
199 * two I2C messages.
200 */
201 data->chan_buf[0] = base_channel + start_channel;
202 memcpy(data->chan_buf + 1, buf, num_channels);
203
204 return i2c_write_dt(&config->bus, data->chan_buf, num_channels + 1);
205 }
206
lp50xx_reset(const struct device * dev)207 static int lp50xx_reset(const struct device *dev)
208 {
209 const struct lp50xx_config *config = dev->config;
210 uint8_t buf[2];
211 int err;
212
213 /* Software reset */
214 buf[0] = LP50XX_RESET(config->num_modules);
215 buf[1] = RESET_SW;
216 err = i2c_write_dt(&config->bus, buf, 2);
217 if (err < 0) {
218 return err;
219 }
220
221 /* After reset, apply configuration since all registers are reset. */
222 buf[0] = LP50XX_DEVICE_CONFIG1;
223 buf[1] = CONFIG1_PWM_DITHERING_EN | CONFIG1_AUTO_INCR_EN
224 | CONFIG1_POWER_SAVE_EN;
225 if (config->max_curr_opt) {
226 buf[1] |= CONFIG1_MAX_CURRENT_OPT;
227 }
228 if (config->log_scale_en) {
229 buf[1] |= CONFIG1_LOG_SCALE_EN;
230 }
231
232 return i2c_write_dt(&config->bus, buf, 2);
233 }
234
lp50xx_hw_enable(const struct device * dev,bool enable)235 static int lp50xx_hw_enable(const struct device *dev, bool enable)
236 {
237 const struct lp50xx_config *config = dev->config;
238 int err;
239
240 if (config->gpio_enable.port == NULL) {
241 /* Nothing to do */
242 return 0;
243 }
244
245 err = gpio_pin_set_dt(&config->gpio_enable, enable);
246 if (err < 0) {
247 LOG_ERR("%s: failed to set enable gpio", dev->name);
248 return err;
249 }
250
251 k_usleep(enable ? LP50XX_ENABLE_DELAY_US : LP50XX_DISABLE_DELAY_US);
252
253 return 0;
254 }
255
lp50xx_enable(const struct device * dev,bool enable)256 static int lp50xx_enable(const struct device *dev, bool enable)
257 {
258 const struct lp50xx_config *config = dev->config;
259 uint8_t value = enable ? CONFIG0_CHIP_EN : 0;
260
261 return i2c_reg_update_byte_dt(&config->bus,
262 LP50XX_DEVICE_CONFIG0,
263 CONFIG0_CHIP_EN,
264 value);
265 }
266
lp50xx_init(const struct device * dev)267 static int lp50xx_init(const struct device *dev)
268 {
269 const struct lp50xx_config *config = dev->config;
270 uint8_t led;
271 int err;
272
273 if (!i2c_is_ready_dt(&config->bus)) {
274 LOG_ERR("%s: I2C device not ready", dev->name);
275 return -ENODEV;
276 }
277
278 /* Check LED configuration found in DT */
279 if (config->num_leds > config->max_leds) {
280 LOG_ERR("%s: invalid number of LEDs %d (max %d)",
281 dev->name,
282 config->num_leds,
283 config->max_leds);
284 return -EINVAL;
285 }
286 for (led = 0; led < config->num_leds; led++) {
287 const struct led_info *led_info =
288 lp50xx_led_to_info(config, led);
289
290 if (led_info->num_colors > LP50XX_COLORS_PER_LED) {
291 LOG_ERR("%s: LED %d: invalid number of colors (max %d)",
292 dev->name, led, LP50XX_COLORS_PER_LED);
293 return -EINVAL;
294 }
295 }
296
297 /* Configure GPIO if present */
298 if (config->gpio_enable.port != NULL) {
299 if (!gpio_is_ready_dt(&config->gpio_enable)) {
300 LOG_ERR("%s: enable gpio is not ready", dev->name);
301 return -ENODEV;
302 }
303
304 err = gpio_pin_configure_dt(&config->gpio_enable,
305 GPIO_OUTPUT_INACTIVE);
306 if (err < 0) {
307 LOG_ERR("%s: failed to initialize enable gpio",
308 dev->name);
309 return err;
310 }
311 }
312
313 /* Enable hardware */
314 err = lp50xx_hw_enable(dev, true);
315 if (err < 0) {
316 LOG_ERR("%s: failed to enable hardware", dev->name);
317 return err;
318 }
319
320 /* Reset device */
321 err = lp50xx_reset(dev);
322 if (err < 0) {
323 LOG_ERR("%s: failed to reset", dev->name);
324 return err;
325 }
326
327 /* Enable device */
328 err = lp50xx_enable(dev, true);
329 if (err < 0) {
330 LOG_ERR("%s: failed to enable", dev->name);
331 return err;
332 }
333
334 return 0;
335 }
336
337 #ifdef CONFIG_PM_DEVICE
lp50xx_pm_action(const struct device * dev,enum pm_device_action action)338 static int lp50xx_pm_action(const struct device *dev,
339 enum pm_device_action action)
340 {
341 switch (action) {
342 case PM_DEVICE_ACTION_SUSPEND:
343 return lp50xx_enable(dev, false);
344 case PM_DEVICE_ACTION_RESUME:
345 return lp50xx_enable(dev, true);
346 default:
347 return -ENOTSUP;
348 }
349
350 return 0;
351 }
352 #endif /* CONFIG_PM_DEVICE */
353
354 static DEVICE_API(led, lp50xx_led_api) = {
355 .on = lp50xx_on,
356 .off = lp50xx_off,
357 .get_info = lp50xx_get_info,
358 .set_brightness = lp50xx_set_brightness,
359 .set_color = lp50xx_set_color,
360 .write_channels = lp50xx_write_channels,
361 };
362
363 #define COLOR_MAPPING(led_node_id) \
364 const uint8_t color_mapping_##led_node_id[] = \
365 DT_PROP(led_node_id, color_mapping);
366
367 #define LED_INFO(led_node_id) \
368 { \
369 .label = DT_PROP(led_node_id, label), \
370 .index = DT_PROP(led_node_id, index), \
371 .num_colors = \
372 DT_PROP_LEN(led_node_id, color_mapping), \
373 .color_mapping = color_mapping_##led_node_id, \
374 },
375
376 #define LP50XX_DEVICE(n, id, nmodules) \
377 DT_INST_FOREACH_CHILD(n, COLOR_MAPPING) \
378 \
379 static const struct led_info lp##id##_leds_##n[] = { \
380 DT_INST_FOREACH_CHILD(n, LED_INFO) \
381 }; \
382 \
383 static const struct lp50xx_config lp##id##_config_##n = { \
384 .bus = I2C_DT_SPEC_INST_GET(n), \
385 .gpio_enable = \
386 GPIO_DT_SPEC_INST_GET_OR(n, enable_gpios, {0}), \
387 .num_modules = nmodules, \
388 .max_leds = LP##id##_MAX_LEDS, \
389 .num_leds = ARRAY_SIZE(lp##id##_leds_##n), \
390 .log_scale_en = DT_INST_PROP(n, log_scale_en), \
391 .max_curr_opt = DT_INST_PROP(n, max_curr_opt), \
392 .leds_info = lp##id##_leds_##n, \
393 }; \
394 \
395 static uint8_t lp##id##_chan_buf_##n[LP50XX_MAX_CHANNELS(nmodules) + 1];\
396 \
397 static struct lp50xx_data lp##id##_data_##n = { \
398 .chan_buf = lp##id##_chan_buf_##n, \
399 }; \
400 \
401 PM_DEVICE_DT_INST_DEFINE(n, lp50xx_pm_action); \
402 \
403 DEVICE_DT_INST_DEFINE(n, \
404 lp50xx_init, \
405 PM_DEVICE_DT_INST_GET(n), \
406 &lp##id##_data_##n, \
407 &lp##id##_config_##n, \
408 POST_KERNEL, CONFIG_LED_INIT_PRIORITY, \
409 &lp50xx_led_api);
410
411 #undef DT_DRV_COMPAT
412 #define DT_DRV_COMPAT ti_lp5009
413 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5009, LP5012_NUM_MODULES)
414
415 #undef DT_DRV_COMPAT
416 #define DT_DRV_COMPAT ti_lp5012
417 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5012, LP5012_NUM_MODULES)
418
419 #undef DT_DRV_COMPAT
420 #define DT_DRV_COMPAT ti_lp5018
421 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5018, LP5024_NUM_MODULES)
422
423 #undef DT_DRV_COMPAT
424 #define DT_DRV_COMPAT ti_lp5024
425 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5024, LP5024_NUM_MODULES)
426
427 #undef DT_DRV_COMPAT
428 #define DT_DRV_COMPAT ti_lp5030
429 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5030, LP5036_NUM_MODULES)
430
431 #undef DT_DRV_COMPAT
432 #define DT_DRV_COMPAT ti_lp5036
433 DT_INST_FOREACH_STATUS_OKAY_VARGS(LP50XX_DEVICE, 5036, LP5036_NUM_MODULES)
434