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