1 /*
2  * Copyright (c) 2018 Workaround GmbH
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ti_lp5562
8 
9 /**
10  * @file
11  * @brief LP5562 LED driver
12  *
13  * The LP5562 is a 4-channel LED driver that communicates over I2C. The four
14  * channels are expected to be connected to a red, green, blue and white LED.
15  * Each LED can be driven by two different sources.
16  *
17  * 1. The brightness of each LED can be configured directly by setting a
18  * register that drives the PWM of the connected LED.
19  *
20  * 2. A program can be transferred to the driver and run by one of the three
21  * available execution engines. Up to 16 commands can be defined in each
22  * program. Possible commands are:
23  *   - Set the brightness.
24  *   - Fade the brightness over time.
25  *   - Loop parts of the program or the whole program.
26  *   - Add delays.
27  *   - Synchronize between the engines.
28  *
29  * After the program has been transferred, it can run infinitely without
30  * communication between the host MCU and the driver.
31  */
32 
33 #include <zephyr/drivers/i2c.h>
34 #include <zephyr/drivers/led.h>
35 #include <zephyr/drivers/gpio.h>
36 #include <zephyr/device.h>
37 #include <zephyr/kernel.h>
38 #include <zephyr/pm/device.h>
39 
40 #define LOG_LEVEL CONFIG_LED_LOG_LEVEL
41 #include <zephyr/logging/log.h>
42 LOG_MODULE_REGISTER(lp5562);
43 
44 #include "led_context.h"
45 
46 /* Registers */
47 #define LP5562_ENABLE             0x00
48 #define LP5562_OP_MODE            0x01
49 #define LP5562_B_PWM              0x02
50 #define LP5562_G_PWM              0x03
51 #define LP5562_R_PWM              0x04
52 #define LP5562_B_CURRENT          0x05
53 #define LP5562_G_CURRENT          0x06
54 #define LP5562_R_CURRENT          0x07
55 #define LP5562_CONFIG             0x08
56 #define LP5562_ENG1_PC            0x09
57 #define LP5562_ENG2_PC            0x0A
58 #define LP5562_ENG3_PC            0x0B
59 #define LP5562_STATUS             0x0C
60 #define LP5562_RESET              0x0D
61 #define LP5562_W_PWM              0x0E
62 #define LP5562_W_CURRENT          0x0F
63 #define LP5562_PROG_MEM_ENG1_BASE 0x10
64 #define LP5562_PROG_MEM_ENG2_BASE 0x30
65 #define LP5562_PROG_MEM_ENG3_BASE 0x50
66 #define LP5562_LED_MAP            0x70
67 
68 /*
69  * The wait command has six bits for the number of steps (max 63) with up to
70  * 15.6ms per step if the prescaler is set to 1. We round the step length
71  * however to 16ms for easier handling, so the maximum blinking period is
72  * therefore (16 * 63) = 1008ms. We round it down to 1000ms to be on the safe
73  * side.
74  */
75 #define LP5562_MAX_BLINK_PERIOD 1000
76 /*
77  * The minimum waiting period is 0.49ms with the prescaler set to 0 and one
78  * step. We round up to a full millisecond.
79  */
80 #define LP5562_MIN_BLINK_PERIOD 1
81 
82 /* Brightness limits in percent */
83 #define LP5562_MIN_BRIGHTNESS 0
84 #define LP5562_MAX_BRIGHTNESS 100
85 
86 /* Output current limits in 0.1 mA */
87 #define LP5562_MIN_CURRENT_SETTING 0
88 #define LP5562_MAX_CURRENT_SETTING 255
89 
90 /* Values for ENABLE register. */
91 #define LP5562_ENABLE_CHIP_EN_MASK (1 << 6)
92 #define LP5562_ENABLE_CHIP_EN_SET  (1 << 6)
93 #define LP5562_ENABLE_CHIP_EN_CLR  (0 << 6)
94 #define LP5562_ENABLE_LOG_EN       (1 << 7)
95 
96 /* Values for CONFIG register. */
97 #define LP5562_CONFIG_EXTERNAL_CLOCK         0x00
98 #define LP5562_CONFIG_INTERNAL_CLOCK         0x01
99 #define LP5562_CONFIG_CLOCK_AUTOMATIC_SELECT 0x02
100 #define LP5562_CONFIG_PWRSAVE_EN             (1 << 5)
101 /* Enable 558 Hz frequency for PWM. Default is 256. */
102 #define LP5562_CONFIG_PWM_HW_FREQ_558        (1 << 6)
103 
104 /* Values for execution engine programs. */
105 #define LP5562_PROG_COMMAND_SET_PWM (1 << 6)
106 #define LP5562_PROG_COMMAND_RAMP_TIME(prescale, step_time) \
107 	(((prescale) << 6) | (step_time))
108 #define LP5562_PROG_COMMAND_STEP_COUNT(fade_direction, count) \
109 	(((fade_direction) << 7) | (count))
110 
111 /* Helper definitions. */
112 #define LP5562_PROG_MAX_COMMANDS 16
113 #define LP5562_MASK              0x03
114 #define LP5562_CHANNEL_MASK(channel) ((LP5562_MASK) << (channel << 1))
115 
116 /*
117  * Available channels. There are four LED channels usable with the LP5562. While
118  * they can be mapped to LEDs of any color, the driver's typical application is
119  * with a red, a green, a blue and a white LED. Since the data sheet's
120  * nomenclature uses RGBW, we keep it that way.
121  */
122 enum lp5562_led_channels {
123 	LP5562_CHANNEL_B,
124 	LP5562_CHANNEL_G,
125 	LP5562_CHANNEL_R,
126 	LP5562_CHANNEL_W,
127 
128 	LP5562_CHANNEL_COUNT,
129 };
130 
131 /*
132  * Each channel can be driven by directly assigning a value between 0 and 255 to
133  * it to drive the PWM or by one of the three execution engines that can be
134  * programmed for custom lighting patterns in order to reduce the I2C traffic
135  * for repetitive patterns.
136  */
137 enum lp5562_led_sources {
138 	LP5562_SOURCE_PWM,
139 	LP5562_SOURCE_ENGINE_1,
140 	LP5562_SOURCE_ENGINE_2,
141 	LP5562_SOURCE_ENGINE_3,
142 
143 	LP5562_SOURCE_COUNT,
144 };
145 
146 /* Operational modes of the execution engines. */
147 enum lp5562_engine_op_modes {
148 	LP5562_OP_MODE_DISABLED = 0x00,
149 	LP5562_OP_MODE_LOAD = 0x01,
150 	LP5562_OP_MODE_RUN = 0x02,
151 	LP5562_OP_MODE_DIRECT_CTRL = 0x03,
152 };
153 
154 /* Execution state of the engines. */
155 enum lp5562_engine_exec_states {
156 	LP5562_ENGINE_MODE_HOLD = 0x00,
157 	LP5562_ENGINE_MODE_STEP = 0x01,
158 	LP5562_ENGINE_MODE_RUN = 0x02,
159 	LP5562_ENGINE_MODE_EXEC = 0x03,
160 };
161 
162 /* Fading directions for programs executed by the engines. */
163 enum lp5562_engine_fade_dirs {
164 	LP5562_FADE_UP = 0x00,
165 	LP5562_FADE_DOWN = 0x01,
166 };
167 
168 struct lp5562_config {
169 	struct i2c_dt_spec bus;
170 	uint8_t r_current;
171 	uint8_t g_current;
172 	uint8_t b_current;
173 	uint8_t w_current;
174 	struct gpio_dt_spec enable_gpio;
175 };
176 
177 struct lp5562_data {
178 	struct led_data dev_data;
179 };
180 
181 /*
182  * @brief Get the register for the given LED channel used to directly write a
183  *	brightness value instead of using the execution engines.
184  *
185  * @param channel LED channel.
186  * @param reg     Pointer to the register address.
187  *
188  * @retval 0       On success.
189  * @retval -EINVAL If an invalid channel is given.
190  */
lp5562_get_pwm_reg(enum lp5562_led_channels channel,uint8_t * reg)191 static int lp5562_get_pwm_reg(enum lp5562_led_channels channel, uint8_t *reg)
192 {
193 	switch (channel) {
194 	case LP5562_CHANNEL_W:
195 		*reg = LP5562_W_PWM;
196 		break;
197 	case LP5562_CHANNEL_R:
198 		*reg = LP5562_R_PWM;
199 		break;
200 	case LP5562_CHANNEL_G:
201 		*reg = LP5562_G_PWM;
202 		break;
203 	case LP5562_CHANNEL_B:
204 		*reg = LP5562_B_PWM;
205 		break;
206 	default:
207 		LOG_ERR("Invalid channel given.");
208 		return -EINVAL;
209 	}
210 
211 	return 0;
212 }
213 
214 /*
215  * @brief Get the base address for programs of the given execution engine.
216  *
217  * @param engine    Engine the base address is requested for.
218  * @param base_addr Pointer to the base address.
219  *
220  * @retval 0       On success.
221  * @retval -EINVAL If a source is given that is not a valid engine.
222  */
lp5562_get_engine_ram_base_addr(enum lp5562_led_sources engine,uint8_t * base_addr)223 static int lp5562_get_engine_ram_base_addr(enum lp5562_led_sources engine,
224 					   uint8_t *base_addr)
225 {
226 	switch (engine) {
227 	case LP5562_SOURCE_ENGINE_1:
228 		*base_addr = LP5562_PROG_MEM_ENG1_BASE;
229 		break;
230 	case LP5562_SOURCE_ENGINE_2:
231 		*base_addr = LP5562_PROG_MEM_ENG2_BASE;
232 		break;
233 	case LP5562_SOURCE_ENGINE_3:
234 		*base_addr = LP5562_PROG_MEM_ENG3_BASE;
235 		break;
236 	default:
237 		return -EINVAL;
238 	}
239 
240 	return 0;
241 }
242 
243 /*
244  * @brief Helper to get the register bit shift for the execution engines.
245  *
246  * The engine with the highest index is placed on the lowest two bits in the
247  * OP_MODE and ENABLE registers.
248  *
249  * @param engine Engine the shift is requested for.
250  * @param shift  Pointer to the shift value.
251  *
252  * @retval 0       On success.
253  * @retval -EINVAL If a source is given that is not a valid engine.
254  */
lp5562_get_engine_reg_shift(enum lp5562_led_sources engine,uint8_t * shift)255 static int lp5562_get_engine_reg_shift(enum lp5562_led_sources engine,
256 				       uint8_t *shift)
257 {
258 	switch (engine) {
259 	case LP5562_SOURCE_ENGINE_1:
260 		*shift = 4U;
261 		break;
262 	case LP5562_SOURCE_ENGINE_2:
263 		*shift = 2U;
264 		break;
265 	case LP5562_SOURCE_ENGINE_3:
266 		*shift = 0U;
267 		break;
268 	default:
269 		return -EINVAL;
270 	}
271 
272 	return 0;
273 }
274 
275 /*
276  * @brief Convert a time in milliseconds to a combination of prescale and
277  *	step_time for the execution engine programs.
278  *
279  * This function expects the given time in milliseconds to be in the allowed
280  * range the device can handle (0ms to 1000ms).
281  *
282  * @param data      Capabilities of the driver.
283  * @param ms        Time to be converted in milliseconds [0..1000].
284  * @param prescale  Pointer to the prescale value.
285  * @param step_time Pointer to the step_time value.
286  */
lp5562_ms_to_prescale_and_step(struct led_data * data,uint32_t ms,uint8_t * prescale,uint8_t * step_time)287 static void lp5562_ms_to_prescale_and_step(struct led_data *data, uint32_t ms,
288 					   uint8_t *prescale, uint8_t *step_time)
289 {
290 	/*
291 	 * One step with the prescaler set to 0 takes 0.49ms. The max value for
292 	 * step_time is 63, so we just double the millisecond value. That way
293 	 * the step_time value never goes above the allowed 63.
294 	 */
295 	if (ms < 31) {
296 		*prescale = 0U;
297 		*step_time = ms << 1;
298 
299 		return;
300 	}
301 
302 	/*
303 	 * With a prescaler value set to 1 one step takes 15.6ms. So by dividing
304 	 * through 16 we get a decent enough result with low effort.
305 	 */
306 	*prescale = 1U;
307 	*step_time = ms >> 4;
308 
309 	return;
310 }
311 
312 /*
313  * @brief Assign a source to the given LED channel.
314  *
315  * @param dev     LP5562 device.
316  * @param channel LED channel the source is assigned to.
317  * @param source  Source for the channel.
318  *
319  * @retval 0    On success.
320  * @retval -EIO If the underlying I2C call fails.
321  */
lp5562_set_led_source(const struct device * dev,enum lp5562_led_channels channel,enum lp5562_led_sources source)322 static int lp5562_set_led_source(const struct device *dev,
323 				 enum lp5562_led_channels channel,
324 				 enum lp5562_led_sources source)
325 {
326 	const struct lp5562_config *config = dev->config;
327 
328 	if (i2c_reg_update_byte_dt(&config->bus, LP5562_LED_MAP,
329 				   LP5562_CHANNEL_MASK(channel),
330 				   source << (channel << 1))) {
331 		LOG_ERR("LED reg update failed.");
332 		return -EIO;
333 	}
334 
335 	return 0;
336 }
337 
338 /*
339  * @brief Get the assigned source of the given LED channel.
340  *
341  * @param dev     LP5562 device.
342  * @param channel Requested LED channel.
343  * @param source  Pointer to the source of the channel.
344  *
345  * @retval 0    On success.
346  * @retval -EIO If the underlying I2C call fails.
347  */
lp5562_get_led_source(const struct device * dev,enum lp5562_led_channels channel,enum lp5562_led_sources * source)348 static int lp5562_get_led_source(const struct device *dev,
349 				 enum lp5562_led_channels channel,
350 				 enum lp5562_led_sources *source)
351 {
352 	const struct lp5562_config *config = dev->config;
353 	uint8_t led_map;
354 
355 	if (i2c_reg_read_byte_dt(&config->bus, LP5562_LED_MAP, &led_map)) {
356 		return -EIO;
357 	}
358 
359 	*source = (led_map >> (channel << 1)) & LP5562_MASK;
360 
361 	return 0;
362 }
363 
364 /*
365  * @brief Request whether an engine is currently running.
366  *
367  * @param dev    LP5562 device.
368  * @param engine Engine to check.
369  *
370  * @return Indication of the engine execution state.
371  *
372  * @retval true  If the engine is currently running.
373  * @retval false If the engine is not running or an error occurred.
374  */
lp5562_is_engine_executing(const struct device * dev,enum lp5562_led_sources engine)375 static bool lp5562_is_engine_executing(const struct device *dev,
376 				       enum lp5562_led_sources engine)
377 {
378 	const struct lp5562_config *config = dev->config;
379 	uint8_t enabled, shift;
380 	int ret;
381 
382 	ret = lp5562_get_engine_reg_shift(engine, &shift);
383 	if (ret) {
384 		return false;
385 	}
386 
387 	if (i2c_reg_read_byte_dt(&config->bus, LP5562_ENABLE, &enabled)) {
388 		LOG_ERR("Failed to read ENABLE register.");
389 		return false;
390 	}
391 
392 	enabled = (enabled >> shift) & LP5562_MASK;
393 
394 	if (enabled == LP5562_ENGINE_MODE_RUN) {
395 		return true;
396 	}
397 
398 	return false;
399 }
400 
401 /*
402  * @brief Get an available execution engine that is currently unused.
403  *
404  * @param dev    LP5562 device.
405  * @param engine Pointer to the engine ID.
406  *
407  * @retval 0       On success.
408  * @retval -ENODEV If all engines are busy.
409  */
lp5562_get_available_engine(const struct device * dev,enum lp5562_led_sources * engine)410 static int lp5562_get_available_engine(const struct device *dev,
411 				       enum lp5562_led_sources *engine)
412 {
413 	enum lp5562_led_sources src;
414 
415 	for (src = LP5562_SOURCE_ENGINE_1; src < LP5562_SOURCE_COUNT; src++) {
416 		if (!lp5562_is_engine_executing(dev, src)) {
417 			LOG_DBG("Available engine: %d", src);
418 			*engine = src;
419 			return 0;
420 		}
421 	}
422 
423 	LOG_ERR("No unused engine available");
424 
425 	return -ENODEV;
426 }
427 
428 /*
429  * @brief Set an register shifted for the given execution engine.
430  *
431  * @param dev    LP5562 device.
432  * @param engine Engine the value is shifted for.
433  * @param reg    Register address to set.
434  * @param val    Value to set.
435  *
436  * @retval 0    On success.
437  * @retval -EIO If the underlying I2C call fails.
438  */
lp5562_set_engine_reg(const struct device * dev,enum lp5562_led_sources engine,uint8_t reg,uint8_t val)439 static int lp5562_set_engine_reg(const struct device *dev,
440 				 enum lp5562_led_sources engine,
441 				 uint8_t reg, uint8_t val)
442 {
443 	const struct lp5562_config *config = dev->config;
444 	uint8_t shift;
445 	int ret;
446 
447 	ret = lp5562_get_engine_reg_shift(engine, &shift);
448 	if (ret) {
449 		return ret;
450 	}
451 
452 	if (i2c_reg_update_byte_dt(&config->bus, reg, LP5562_MASK << shift,
453 				   val << shift)) {
454 		return -EIO;
455 	}
456 
457 	return 0;
458 }
459 
460 /*
461  * @brief Set the operational mode of the given engine.
462  *
463  * @param dev    LP5562 device.
464  * @param engine Engine the operational mode is changed for.
465  * @param mode   Mode to set.
466  *
467  * @retval 0    On success.
468  * @retval -EIO If the underlying I2C call fails.
469  */
lp5562_set_engine_op_mode(const struct device * dev,enum lp5562_led_sources engine,enum lp5562_engine_op_modes mode)470 static inline int lp5562_set_engine_op_mode(const struct device *dev,
471 					    enum lp5562_led_sources engine,
472 					    enum lp5562_engine_op_modes mode)
473 {
474 	return lp5562_set_engine_reg(dev, engine, LP5562_OP_MODE, mode);
475 }
476 
477 /*
478  * @brief Set the execution state of the given engine.
479  *
480  * @param dev    LP5562 device.
481  * @param engine Engine the execution state is changed for.
482  * @param state  State to set.
483  *
484  * @retval 0    On success.
485  * @retval -EIO If the underlying I2C call fails.
486  */
lp5562_set_engine_exec_state(const struct device * dev,enum lp5562_led_sources engine,enum lp5562_engine_exec_states state)487 static inline int lp5562_set_engine_exec_state(const struct device *dev,
488 					       enum lp5562_led_sources engine,
489 					       enum lp5562_engine_exec_states state)
490 {
491 	int ret;
492 
493 	ret = lp5562_set_engine_reg(dev, engine, LP5562_ENABLE, state);
494 
495 	/*
496 	 * Delay between consecutive I2C writes to
497 	 * ENABLE register (00h) need to be longer than 488μs (typ.).
498 	 */
499 	k_sleep(K_MSEC(1));
500 
501 	return ret;
502 }
503 
504 /*
505  * @brief Start the execution of the program of the given engine.
506  *
507  * @param dev    LP5562 device.
508  * @param engine Engine that is started.
509  *
510  * @retval 0    On success.
511  * @retval -EIO If the underlying I2C call fails.
512  */
lp5562_start_program_exec(const struct device * dev,enum lp5562_led_sources engine)513 static inline int lp5562_start_program_exec(const struct device *dev,
514 					    enum lp5562_led_sources engine)
515 {
516 	if (lp5562_set_engine_op_mode(dev, engine, LP5562_OP_MODE_RUN)) {
517 		return -EIO;
518 	}
519 
520 	return lp5562_set_engine_exec_state(dev, engine,
521 					    LP5562_ENGINE_MODE_RUN);
522 }
523 
524 /*
525  * @brief Stop the execution of the program of the given engine.
526  *
527  * @param dev    LP5562 device.
528  * @param engine Engine that is stopped.
529  *
530  * @retval 0    On success.
531  * @retval -EIO If the underlying I2C call fails.
532  */
lp5562_stop_program_exec(const struct device * dev,enum lp5562_led_sources engine)533 static inline int lp5562_stop_program_exec(const struct device *dev,
534 					   enum lp5562_led_sources engine)
535 {
536 	if (lp5562_set_engine_op_mode(dev, engine, LP5562_OP_MODE_DISABLED)) {
537 		return -EIO;
538 	}
539 
540 	return lp5562_set_engine_exec_state(dev, engine,
541 					    LP5562_ENGINE_MODE_HOLD);
542 }
543 
544 /*
545  * @brief Program a command to the memory of the given execution engine.
546  *
547  * @param dev           LP5562 device.
548  * @param engine        Engine that is programmed.
549  * @param command_index Index of the command that is programmed.
550  * @param command_msb   Most significant byte of the command.
551  * @param command_lsb   Least significant byte of the command.
552  *
553  * @retval 0       On success.
554  * @retval -EINVAL If the given command index is out of range or an invalid
555  *		   engine is passed.
556  * @retval -EIO    If the underlying I2C call fails.
557  */
lp5562_program_command(const struct device * dev,enum lp5562_led_sources engine,uint8_t command_index,uint8_t command_msb,uint8_t command_lsb)558 static int lp5562_program_command(const struct device *dev,
559 				  enum lp5562_led_sources engine,
560 				  uint8_t command_index,
561 				  uint8_t command_msb,
562 				  uint8_t command_lsb)
563 {
564 	const struct lp5562_config *config = dev->config;
565 	uint8_t prog_base_addr;
566 	int ret;
567 
568 	if (command_index >= LP5562_PROG_MAX_COMMANDS) {
569 		return -EINVAL;
570 	}
571 
572 	ret = lp5562_get_engine_ram_base_addr(engine, &prog_base_addr);
573 	if (ret) {
574 		LOG_ERR("Failed to get base RAM address.");
575 		return ret;
576 	}
577 
578 	if (i2c_reg_write_byte_dt(&config->bus,
579 				  prog_base_addr + (command_index << 1),
580 				  command_msb)) {
581 		LOG_ERR("Failed to update LED.");
582 		return -EIO;
583 	}
584 
585 	if (i2c_reg_write_byte_dt(&config->bus,
586 				  prog_base_addr + (command_index << 1) + 1,
587 				  command_lsb)) {
588 		LOG_ERR("Failed to update LED.");
589 		return -EIO;
590 	}
591 
592 	return 0;
593 }
594 
595 /*
596  * @brief Program a command to set a fixed brightness to the given engine.
597  *
598  * @param dev           LP5562 device.
599  * @param engine        Engine to be programmed.
600  * @param command_index Index of the command in the program sequence.
601  * @param brightness    Brightness to be set for the LED in percent.
602  *
603  * @retval 0       On success.
604  * @retval -EINVAL If the passed arguments are invalid or out of range.
605  * @retval -EIO    If the underlying I2C call fails.
606  */
lp5562_program_set_brightness(const struct device * dev,enum lp5562_led_sources engine,uint8_t command_index,uint8_t brightness)607 static int lp5562_program_set_brightness(const struct device *dev,
608 					 enum lp5562_led_sources engine,
609 					 uint8_t command_index,
610 					 uint8_t brightness)
611 {
612 	struct lp5562_data *data = dev->data;
613 	struct led_data *dev_data = &data->dev_data;
614 	uint8_t val;
615 
616 	if ((brightness < dev_data->min_brightness) ||
617 			(brightness > dev_data->max_brightness)) {
618 		return -EINVAL;
619 	}
620 
621 	val = (brightness * 0xFF) / dev_data->max_brightness;
622 
623 	return lp5562_program_command(dev, engine, command_index,
624 			LP5562_PROG_COMMAND_SET_PWM, val);
625 }
626 
627 /*
628  * @brief Program a command to ramp the brightness over time.
629  *
630  * In each step the PWM value is increased or decreased by 1/255th until the
631  * maximum or minimum value is reached or step_count steps have been done.
632  *
633  * @param dev           LP5562 device.
634  * @param engine        Engine to be programmed.
635  * @param command_index Index of the command in the program sequence.
636  * @param time_per_step Time each step takes in milliseconds.
637  * @param step_count    Number of steps to perform.
638  * @param fade_dir      Direction of the ramp (in-/decrease brightness).
639  *
640  * @retval 0       On success.
641  * @retval -EINVAL If the passed arguments are invalid or out of range.
642  * @retval -EIO    If the underlying I2C call fails.
643  */
lp5562_program_ramp(const struct device * dev,enum lp5562_led_sources engine,uint8_t command_index,uint32_t time_per_step,uint8_t step_count,enum lp5562_engine_fade_dirs fade_dir)644 static int lp5562_program_ramp(const struct device *dev,
645 			       enum lp5562_led_sources engine,
646 			       uint8_t command_index,
647 			       uint32_t time_per_step,
648 			       uint8_t step_count,
649 			       enum lp5562_engine_fade_dirs fade_dir)
650 {
651 	struct lp5562_data *data = dev->data;
652 	struct led_data *dev_data = &data->dev_data;
653 	uint8_t prescale, step_time;
654 
655 	if ((time_per_step < dev_data->min_period) ||
656 			(time_per_step > dev_data->max_period)) {
657 		return -EINVAL;
658 	}
659 
660 	lp5562_ms_to_prescale_and_step(dev_data, time_per_step,
661 			&prescale, &step_time);
662 
663 	return lp5562_program_command(dev, engine, command_index,
664 			LP5562_PROG_COMMAND_RAMP_TIME(prescale, step_time),
665 			LP5562_PROG_COMMAND_STEP_COUNT(fade_dir, step_count));
666 }
667 
668 /*
669  * @brief Program a command to do nothing for the given time.
670  *
671  * @param dev           LP5562 device.
672  * @param engine        Engine to be programmed.
673  * @param command_index Index of the command in the program sequence.
674  * @param time          Time to do nothing in milliseconds.
675  *
676  * @retval 0       On success.
677  * @retval -EINVAL If the passed arguments are invalid or out of range.
678  * @retval -EIO    If the underlying I2C call fails.
679  */
lp5562_program_wait(const struct device * dev,enum lp5562_led_sources engine,uint8_t command_index,uint32_t time)680 static inline int lp5562_program_wait(const struct device *dev,
681 				      enum lp5562_led_sources engine,
682 				      uint8_t command_index,
683 				      uint32_t time)
684 {
685 	/*
686 	 * A wait command is a ramp with the step_count set to 0. The fading
687 	 * direction does not matter in this case.
688 	 */
689 	return lp5562_program_ramp(dev, engine, command_index,
690 			time, 0, LP5562_FADE_UP);
691 }
692 
693 /*
694  * @brief Program a command to go back to the beginning of the program.
695  *
696  * Can be used at the end of a program to loop it infinitely.
697  *
698  * @param dev           LP5562 device.
699  * @param engine        Engine to be programmed.
700  * @param command_index Index of the command in the program sequence.
701  *
702  * @retval 0       On success.
703  * @retval -EINVAL If the given command index is out of range or an invalid
704  *		   engine is passed.
705  * @retval -EIO    If the underlying I2C call fails.
706  */
lp5562_program_go_to_start(const struct device * dev,enum lp5562_led_sources engine,uint8_t command_index)707 static inline int lp5562_program_go_to_start(const struct device *dev,
708 					     enum lp5562_led_sources engine,
709 					     uint8_t command_index)
710 {
711 	return lp5562_program_command(dev, engine, command_index, 0x00, 0x00);
712 }
713 
714 /*
715  * @brief Change the brightness of a running blink program.
716  *
717  * We know that the current program executes a blinking pattern
718  * consisting of following commands:
719  *
720  * - set_brightness high
721  * - wait on_delay
722  * - set_brightness low
723  * - wait off_delay
724  * - return to start
725  *
726  * In order to change the brightness during blinking, we overwrite only
727  * the first command and start execution again.
728  *
729  * @param dev           LP5562 device.
730  * @param engine        Engine running the blinking program.
731  * @param brightness_on New brightness value.
732  *
733  * @retval 0       On Success.
734  * @retval -EINVAL If the engine ID or brightness is out of range.
735  * @retval -EIO    If the underlying I2C call fails.
736  */
lp5562_update_blinking_brightness(const struct device * dev,enum lp5562_led_sources engine,uint8_t brightness_on)737 static int lp5562_update_blinking_brightness(const struct device *dev,
738 					     enum lp5562_led_sources engine,
739 					     uint8_t brightness_on)
740 {
741 	int ret;
742 
743 	ret = lp5562_stop_program_exec(dev, engine);
744 	if (ret) {
745 		return ret;
746 	}
747 
748 	ret = lp5562_set_engine_op_mode(dev, engine, LP5562_OP_MODE_LOAD);
749 	if (ret) {
750 		return ret;
751 	}
752 
753 
754 	ret = lp5562_program_set_brightness(dev, engine, 0, brightness_on);
755 	if (ret) {
756 		return ret;
757 	}
758 
759 	ret = lp5562_start_program_exec(dev, engine);
760 	if (ret) {
761 		LOG_ERR("Failed to execute program.");
762 		return ret;
763 	}
764 
765 	return 0;
766 }
767 
lp5562_led_blink(const struct device * dev,uint32_t led,uint32_t delay_on,uint32_t delay_off)768 static int lp5562_led_blink(const struct device *dev, uint32_t led,
769 			    uint32_t delay_on, uint32_t delay_off)
770 {
771 	struct lp5562_data *data = dev->data;
772 	struct led_data *dev_data = &data->dev_data;
773 	int ret;
774 	enum lp5562_led_sources engine;
775 	uint8_t command_index = 0U;
776 
777 	/*
778 	 * Read current "led" source setting. This is to check
779 	 * whether the "led" is in PWM mode or using an Engine.
780 	 */
781 	ret = lp5562_get_led_source(dev, led, &engine);
782 	if (ret) {
783 		return ret;
784 	}
785 
786 	/* Find and assign new engine only if the "led" is not using any. */
787 	if (engine == LP5562_SOURCE_PWM) {
788 		ret = lp5562_get_available_engine(dev, &engine);
789 		if (ret) {
790 			return ret;
791 		}
792 
793 		ret = lp5562_set_led_source(dev, led, engine);
794 		if (ret) {
795 			LOG_ERR("Failed to set LED source.");
796 			return ret;
797 		}
798 	}
799 
800 	ret = lp5562_set_engine_op_mode(dev, engine, LP5562_OP_MODE_LOAD);
801 	if (ret) {
802 		return ret;
803 	}
804 
805 	ret = lp5562_program_set_brightness(dev, engine, command_index,
806 			dev_data->max_brightness);
807 	if (ret) {
808 		return ret;
809 	}
810 
811 	ret = lp5562_program_wait(dev, engine, ++command_index, delay_on);
812 	if (ret) {
813 		return ret;
814 	}
815 
816 	ret = lp5562_program_set_brightness(dev, engine, ++command_index,
817 			dev_data->min_brightness);
818 	if (ret) {
819 		return ret;
820 	}
821 
822 	ret = lp5562_program_wait(dev, engine, ++command_index, delay_off);
823 	if (ret) {
824 		return ret;
825 	}
826 
827 	ret = lp5562_program_go_to_start(dev, engine, ++command_index);
828 	if (ret) {
829 		return ret;
830 	}
831 
832 	ret = lp5562_start_program_exec(dev, engine);
833 	if (ret) {
834 		LOG_ERR("Failed to execute program.");
835 		return ret;
836 	}
837 
838 	return 0;
839 }
840 
lp5562_led_set_brightness(const struct device * dev,uint32_t led,uint8_t value)841 static int lp5562_led_set_brightness(const struct device *dev, uint32_t led,
842 				     uint8_t value)
843 {
844 	const struct lp5562_config *config = dev->config;
845 	struct lp5562_data *data = dev->data;
846 	struct led_data *dev_data = &data->dev_data;
847 	int ret;
848 	uint8_t val, reg;
849 	enum lp5562_led_sources current_source;
850 
851 	if ((value < dev_data->min_brightness) ||
852 			(value > dev_data->max_brightness)) {
853 		return -EINVAL;
854 	}
855 
856 	ret = lp5562_get_led_source(dev, led, &current_source);
857 	if (ret) {
858 		return ret;
859 	}
860 
861 	if (current_source != LP5562_SOURCE_PWM) {
862 		if (lp5562_is_engine_executing(dev, current_source)) {
863 			/*
864 			 * LED is blinking currently. Restart the blinking with
865 			 * the passed brightness.
866 			 */
867 			return lp5562_update_blinking_brightness(dev,
868 					current_source, value);
869 		}
870 
871 		ret = lp5562_set_led_source(dev, led, LP5562_SOURCE_PWM);
872 		if (ret) {
873 			return ret;
874 		}
875 	}
876 
877 	val = (value * 0xFF) / dev_data->max_brightness;
878 
879 	ret = lp5562_get_pwm_reg(led, &reg);
880 	if (ret) {
881 		return ret;
882 	}
883 
884 	if (i2c_reg_write_byte_dt(&config->bus, reg, val)) {
885 		LOG_ERR("LED write failed");
886 		return -EIO;
887 	}
888 
889 	return 0;
890 }
891 
lp5562_led_on(const struct device * dev,uint32_t led)892 static inline int lp5562_led_on(const struct device *dev, uint32_t led)
893 {
894 	struct lp5562_data *data = dev->data;
895 	struct led_data *dev_data = &data->dev_data;
896 
897 	return lp5562_led_set_brightness(dev, led, dev_data->max_brightness);
898 }
899 
lp5562_led_off(const struct device * dev,uint32_t led)900 static inline int lp5562_led_off(const struct device *dev, uint32_t led)
901 {
902 	struct lp5562_data *data = dev->data;
903 	struct led_data *dev_data = &data->dev_data;
904 
905 	int ret;
906 	enum lp5562_led_sources current_source;
907 
908 	ret = lp5562_get_led_source(dev, led, &current_source);
909 	if (ret) {
910 		return ret;
911 	}
912 
913 	if (current_source != LP5562_SOURCE_PWM) {
914 		ret = lp5562_stop_program_exec(dev, current_source);
915 		if (ret) {
916 			return ret;
917 		}
918 	}
919 
920 	return lp5562_led_set_brightness(dev, led, dev_data->min_brightness);
921 }
922 
lp5562_led_update_current(const struct device * dev)923 static int lp5562_led_update_current(const struct device *dev)
924 {
925 	const struct lp5562_config *config = dev->config;
926 	int ret;
927 	uint8_t tx_buf[4] = {
928 		LP5562_B_CURRENT,
929 		config->b_current,
930 		config->g_current,
931 		config->r_current };
932 
933 	ret = i2c_write_dt(&config->bus, tx_buf, sizeof(tx_buf));
934 	if (ret == 0) {
935 		ret = i2c_reg_write_byte_dt(&config->bus, LP5562_W_CURRENT, config->w_current);
936 	}
937 
938 	return ret;
939 }
940 
lp5562_enable(const struct device * dev,bool soft_reset)941 static int lp5562_enable(const struct device *dev, bool soft_reset)
942 {
943 	const struct lp5562_config *config = dev->config;
944 	const struct gpio_dt_spec *enable_gpio = &config->enable_gpio;
945 	int err = 0;
946 
947 	/* If ENABLE_GPIO control is enabled, we need to assert ENABLE_GPIO first. */
948 	if (enable_gpio->port != NULL) {
949 		err = gpio_pin_set_dt(enable_gpio, 1);
950 		if (err) {
951 			LOG_ERR("%s: failed to set enable GPIO 1", dev->name);
952 			return err;
953 		}
954 		/*
955 		 * The I2C host should allow at least 1ms before sending data to
956 		 * the LP5562 after the rising edge of the enable line.
957 		 * So let's wait for 1 ms.
958 		 */
959 		k_sleep(K_MSEC(1));
960 	}
961 
962 	if (soft_reset) {
963 		/* Reset all internal registers to have a deterministic state. */
964 		err = i2c_reg_write_byte_dt(&config->bus, LP5562_RESET, 0xFF);
965 		if (err) {
966 			LOG_ERR("%s: failed to soft-reset device", dev->name);
967 			return err;
968 		}
969 	}
970 
971 	/* Set en bit in LP5562_ENABLE register. */
972 	err = i2c_reg_update_byte_dt(&config->bus, LP5562_ENABLE, LP5562_ENABLE_CHIP_EN_MASK,
973 				     LP5562_ENABLE_CHIP_EN_SET);
974 	if (err) {
975 		LOG_ERR("%s: failed to set EN Bit in ENABLE register", dev->name);
976 		return err;
977 	}
978 	/* Allow 500 µs delay after setting chip_en bit to '1'. */
979 	k_sleep(K_USEC(500));
980 	return 0;
981 }
982 
983 #ifdef CONFIG_PM_DEVICE
lp5562_disable(const struct device * dev)984 static int lp5562_disable(const struct device *dev)
985 {
986 	const struct lp5562_config *config = dev->config;
987 	const struct gpio_dt_spec *enable_gpio = &config->enable_gpio;
988 	int err = 0;
989 
990 	/* clear en bit in register configurations */
991 	err = i2c_reg_update_byte_dt(&config->bus, LP5562_ENABLE, LP5562_ENABLE_CHIP_EN_MASK,
992 				     LP5562_ENABLE_CHIP_EN_CLR);
993 	if (err) {
994 		LOG_ERR("%s: failed to clear EN Bit in ENABLE register", dev->name);
995 		return err;
996 	}
997 
998 	/* if gpio control is enabled, we can de-assert EN_GPIO now */
999 	if (enable_gpio->port != NULL) {
1000 		err = gpio_pin_set_dt(enable_gpio, 0);
1001 		if (err) {
1002 			LOG_ERR("%s: failed to set enable GPIO to 0", dev->name);
1003 			return err;
1004 		}
1005 	}
1006 	return 0;
1007 }
1008 #endif
1009 
lp5562_led_init(const struct device * dev)1010 static int lp5562_led_init(const struct device *dev)
1011 {
1012 	const struct lp5562_config *config = dev->config;
1013 	struct lp5562_data *data = dev->data;
1014 	struct led_data *dev_data = &data->dev_data;
1015 	const struct gpio_dt_spec *enable_gpio = &config->enable_gpio;
1016 	int ret;
1017 
1018 	if (enable_gpio->port != NULL) {
1019 		if (!gpio_is_ready_dt(enable_gpio)) {
1020 			return -ENODEV;
1021 		}
1022 		ret = gpio_pin_configure_dt(enable_gpio, GPIO_OUTPUT);
1023 		if (ret) {
1024 			LOG_ERR("LP5562 Enable GPIO Config failed");
1025 			return ret;
1026 		}
1027 	}
1028 
1029 	if (!device_is_ready(config->bus.bus)) {
1030 		LOG_ERR("I2C device not ready");
1031 		return -ENODEV;
1032 	}
1033 
1034 	ret = lp5562_enable(dev, true);
1035 	if (ret) {
1036 		return ret;
1037 	}
1038 
1039 	/* Hardware specific limits */
1040 	dev_data->min_period = LP5562_MIN_BLINK_PERIOD;
1041 	dev_data->max_period = LP5562_MAX_BLINK_PERIOD;
1042 	dev_data->min_brightness = LP5562_MIN_BRIGHTNESS;
1043 	dev_data->max_brightness = LP5562_MAX_BRIGHTNESS;
1044 
1045 	ret = lp5562_led_update_current(dev);
1046 	if (ret) {
1047 		LOG_ERR("Setting current setting LP5562 LED chip failed.");
1048 		return ret;
1049 	}
1050 
1051 	if (i2c_reg_write_byte_dt(&config->bus, LP5562_CONFIG,
1052 				  (LP5562_CONFIG_INTERNAL_CLOCK |
1053 				   LP5562_CONFIG_PWRSAVE_EN))) {
1054 		LOG_ERR("Configuring LP5562 LED chip failed.");
1055 		return -EIO;
1056 	}
1057 
1058 	if (i2c_reg_write_byte_dt(&config->bus, LP5562_OP_MODE, 0x00)) {
1059 		LOG_ERR("Disabling all engines failed.");
1060 		return -EIO;
1061 	}
1062 
1063 	if (i2c_reg_write_byte_dt(&config->bus, LP5562_LED_MAP, 0x00)) {
1064 		LOG_ERR("Setting all LEDs to manual control failed.");
1065 		return -EIO;
1066 	}
1067 
1068 	return 0;
1069 }
1070 
1071 static DEVICE_API(led, lp5562_led_api) = {
1072 	.blink = lp5562_led_blink,
1073 	.set_brightness = lp5562_led_set_brightness,
1074 	.on = lp5562_led_on,
1075 	.off = lp5562_led_off,
1076 };
1077 
1078 #ifdef CONFIG_PM_DEVICE
lp5562_pm_action(const struct device * dev,enum pm_device_action action)1079 static int lp5562_pm_action(const struct device *dev, enum pm_device_action action)
1080 {
1081 	switch (action) {
1082 	case PM_DEVICE_ACTION_SUSPEND:
1083 		return lp5562_disable(dev);
1084 	case PM_DEVICE_ACTION_RESUME:
1085 		return lp5562_enable(dev, false);
1086 	default:
1087 		return -ENOTSUP;
1088 	}
1089 }
1090 #endif /* CONFIG_PM_DEVICE */
1091 
1092 #define LP5562_DEFINE(id)						\
1093 	BUILD_ASSERT(DT_INST_PROP(id, red_output_current) <= LP5562_MAX_CURRENT_SETTING,\
1094 		"Red channel current must be between 0 and 25.5 mA.");	\
1095 	BUILD_ASSERT(DT_INST_PROP(id, green_output_current) <= LP5562_MAX_CURRENT_SETTING,\
1096 		"Green channel current must be between 0 and 25.5 mA.");	\
1097 	BUILD_ASSERT(DT_INST_PROP(id, blue_output_current) <= LP5562_MAX_CURRENT_SETTING,\
1098 		"Blue channel current must be between 0 and 25.5 mA.");	\
1099 	BUILD_ASSERT(DT_INST_PROP(id, white_output_current) <= LP5562_MAX_CURRENT_SETTING,\
1100 		"White channel current must be between 0 and 25.5 mA.");	\
1101 	static const struct lp5562_config lp5562_config_##id = {	\
1102 		.bus = I2C_DT_SPEC_INST_GET(id),			\
1103 		.r_current = DT_INST_PROP(id, red_output_current),	\
1104 		.g_current = DT_INST_PROP(id, green_output_current),	\
1105 		.b_current = DT_INST_PROP(id, blue_output_current),	\
1106 		.w_current = DT_INST_PROP(id, white_output_current),	\
1107 		.enable_gpio = GPIO_DT_SPEC_INST_GET_OR(id, enable_gpios, {0}),	\
1108 	};								\
1109 									\
1110 	PM_DEVICE_DT_INST_DEFINE(id, lp5562_pm_action);			\
1111 									\
1112 	struct lp5562_data lp5562_data_##id;				\
1113 	DEVICE_DT_INST_DEFINE(id, &lp5562_led_init, PM_DEVICE_DT_INST_GET(id),	\
1114 			&lp5562_data_##id,				\
1115 			&lp5562_config_##id, POST_KERNEL,		\
1116 			CONFIG_LED_INIT_PRIORITY,			\
1117 			&lp5562_led_api);				\
1118 
1119 DT_INST_FOREACH_STATUS_OKAY(LP5562_DEFINE)
1120