xref: /openbmc/linux/drivers/leds/leds-lp5562.c (revision 95b2af637e283e3d549c8a6af9f182b0bd972a2e)
1ff45262aSKim, Milo /*
2ff45262aSKim, Milo  * LP5562 LED driver
3ff45262aSKim, Milo  *
4ff45262aSKim, Milo  * Copyright (C) 2013 Texas Instruments
5ff45262aSKim, Milo  *
6ff45262aSKim, Milo  * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
7ff45262aSKim, Milo  *
8ff45262aSKim, Milo  * This program is free software; you can redistribute it and/or modify
9ff45262aSKim, Milo  * it under the terms of the GNU General Public License version 2 as
10ff45262aSKim, Milo  * published by the Free Software Foundation.
11ff45262aSKim, Milo  */
12ff45262aSKim, Milo 
13ff45262aSKim, Milo #include <linux/delay.h>
14ff45262aSKim, Milo #include <linux/firmware.h>
15ff45262aSKim, Milo #include <linux/i2c.h>
16ff45262aSKim, Milo #include <linux/leds.h>
17ff45262aSKim, Milo #include <linux/module.h>
18ff45262aSKim, Milo #include <linux/mutex.h>
19c68f46ddSSachin Kamat #include <linux/of.h>
20ff45262aSKim, Milo #include <linux/platform_data/leds-lp55xx.h>
21ff45262aSKim, Milo #include <linux/slab.h>
22ff45262aSKim, Milo 
23ff45262aSKim, Milo #include "leds-lp55xx-common.h"
24ff45262aSKim, Milo 
25ff45262aSKim, Milo #define LP5562_PROGRAM_LENGTH		32
26ff45262aSKim, Milo #define LP5562_MAX_LEDS			4
27ff45262aSKim, Milo 
28ff45262aSKim, Milo /* ENABLE Register 00h */
29ff45262aSKim, Milo #define LP5562_REG_ENABLE		0x00
30ff45262aSKim, Milo #define LP5562_EXEC_ENG1_M		0x30
31ff45262aSKim, Milo #define LP5562_EXEC_ENG2_M		0x0C
32ff45262aSKim, Milo #define LP5562_EXEC_ENG3_M		0x03
33ff45262aSKim, Milo #define LP5562_EXEC_M			0x3F
34ff45262aSKim, Milo #define LP5562_MASTER_ENABLE		0x40	/* Chip master enable */
35ff45262aSKim, Milo #define LP5562_LOGARITHMIC_PWM		0x80	/* Logarithmic PWM adjustment */
36ff45262aSKim, Milo #define LP5562_EXEC_RUN			0x2A
37ff45262aSKim, Milo #define LP5562_ENABLE_DEFAULT	\
38ff45262aSKim, Milo 	(LP5562_MASTER_ENABLE | LP5562_LOGARITHMIC_PWM)
39ff45262aSKim, Milo #define LP5562_ENABLE_RUN_PROGRAM	\
40ff45262aSKim, Milo 	(LP5562_ENABLE_DEFAULT | LP5562_EXEC_RUN)
41ff45262aSKim, Milo 
42ff45262aSKim, Milo /* OPMODE Register 01h */
43ff45262aSKim, Milo #define LP5562_REG_OP_MODE		0x01
44ff45262aSKim, Milo #define LP5562_MODE_ENG1_M		0x30
45ff45262aSKim, Milo #define LP5562_MODE_ENG2_M		0x0C
46ff45262aSKim, Milo #define LP5562_MODE_ENG3_M		0x03
47ff45262aSKim, Milo #define LP5562_LOAD_ENG1		0x10
48ff45262aSKim, Milo #define LP5562_LOAD_ENG2		0x04
49ff45262aSKim, Milo #define LP5562_LOAD_ENG3		0x01
50ff45262aSKim, Milo #define LP5562_RUN_ENG1			0x20
51ff45262aSKim, Milo #define LP5562_RUN_ENG2			0x08
52ff45262aSKim, Milo #define LP5562_RUN_ENG3			0x02
53ff45262aSKim, Milo #define LP5562_ENG1_IS_LOADING(mode)	\
54ff45262aSKim, Milo 	((mode & LP5562_MODE_ENG1_M) == LP5562_LOAD_ENG1)
55ff45262aSKim, Milo #define LP5562_ENG2_IS_LOADING(mode)	\
56ff45262aSKim, Milo 	((mode & LP5562_MODE_ENG2_M) == LP5562_LOAD_ENG2)
57ff45262aSKim, Milo #define LP5562_ENG3_IS_LOADING(mode)	\
58ff45262aSKim, Milo 	((mode & LP5562_MODE_ENG3_M) == LP5562_LOAD_ENG3)
59ff45262aSKim, Milo 
60ff45262aSKim, Milo /* BRIGHTNESS Registers */
61ff45262aSKim, Milo #define LP5562_REG_R_PWM		0x04
62ff45262aSKim, Milo #define LP5562_REG_G_PWM		0x03
63ff45262aSKim, Milo #define LP5562_REG_B_PWM		0x02
64ff45262aSKim, Milo #define LP5562_REG_W_PWM		0x0E
65ff45262aSKim, Milo 
66ff45262aSKim, Milo /* CURRENT Registers */
67ff45262aSKim, Milo #define LP5562_REG_R_CURRENT		0x07
68ff45262aSKim, Milo #define LP5562_REG_G_CURRENT		0x06
69ff45262aSKim, Milo #define LP5562_REG_B_CURRENT		0x05
70ff45262aSKim, Milo #define LP5562_REG_W_CURRENT		0x0F
71ff45262aSKim, Milo 
72ff45262aSKim, Milo /* CONFIG Register 08h */
73ff45262aSKim, Milo #define LP5562_REG_CONFIG		0x08
7481f2a5b4SKim, Milo #define LP5562_PWM_HF			0x40
7581f2a5b4SKim, Milo #define LP5562_PWRSAVE_EN		0x20
7681f2a5b4SKim, Milo #define LP5562_CLK_INT			0x01	/* Internal clock */
7781f2a5b4SKim, Milo #define LP5562_DEFAULT_CFG		(LP5562_PWM_HF | LP5562_PWRSAVE_EN)
78ff45262aSKim, Milo 
79ff45262aSKim, Milo /* RESET Register 0Dh */
80ff45262aSKim, Milo #define LP5562_REG_RESET		0x0D
81ff45262aSKim, Milo #define LP5562_RESET			0xFF
82ff45262aSKim, Milo 
83ff45262aSKim, Milo /* PROGRAM ENGINE Registers */
84ff45262aSKim, Milo #define LP5562_REG_PROG_MEM_ENG1	0x10
85ff45262aSKim, Milo #define LP5562_REG_PROG_MEM_ENG2	0x30
86ff45262aSKim, Milo #define LP5562_REG_PROG_MEM_ENG3	0x50
87ff45262aSKim, Milo 
88ff45262aSKim, Milo /* LEDMAP Register 70h */
89ff45262aSKim, Milo #define LP5562_REG_ENG_SEL		0x70
90ff45262aSKim, Milo #define LP5562_ENG_SEL_PWM		0
91ff45262aSKim, Milo #define LP5562_ENG_FOR_RGB_M		0x3F
92ff45262aSKim, Milo #define LP5562_ENG_SEL_RGB		0x1B	/* R:ENG1, G:ENG2, B:ENG3 */
93ff45262aSKim, Milo #define LP5562_ENG_FOR_W_M		0xC0
94ff45262aSKim, Milo #define LP5562_ENG1_FOR_W		0x40	/* W:ENG1 */
95ff45262aSKim, Milo #define LP5562_ENG2_FOR_W		0x80	/* W:ENG2 */
96ff45262aSKim, Milo #define LP5562_ENG3_FOR_W		0xC0	/* W:ENG3 */
97ff45262aSKim, Milo 
98ff45262aSKim, Milo /* Program Commands */
99ff45262aSKim, Milo #define LP5562_CMD_DISABLE		0x00
100ff45262aSKim, Milo #define LP5562_CMD_LOAD			0x15
101ff45262aSKim, Milo #define LP5562_CMD_RUN			0x2A
102ff45262aSKim, Milo #define LP5562_CMD_DIRECT		0x3F
103ff45262aSKim, Milo #define LP5562_PATTERN_OFF		0
104ff45262aSKim, Milo 
105ff45262aSKim, Milo static inline void lp5562_wait_opmode_done(void)
106ff45262aSKim, Milo {
107ff45262aSKim, Milo 	/* operation mode change needs to be longer than 153 us */
108ff45262aSKim, Milo 	usleep_range(200, 300);
109ff45262aSKim, Milo }
110ff45262aSKim, Milo 
111ff45262aSKim, Milo static inline void lp5562_wait_enable_done(void)
112ff45262aSKim, Milo {
113ff45262aSKim, Milo 	/* it takes more 488 us to update ENABLE register */
114ff45262aSKim, Milo 	usleep_range(500, 600);
115ff45262aSKim, Milo }
116ff45262aSKim, Milo 
117ff45262aSKim, Milo static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current)
118ff45262aSKim, Milo {
119ff45262aSKim, Milo 	u8 addr[] = {
120ff45262aSKim, Milo 		LP5562_REG_R_CURRENT,
121ff45262aSKim, Milo 		LP5562_REG_G_CURRENT,
122ff45262aSKim, Milo 		LP5562_REG_B_CURRENT,
123ff45262aSKim, Milo 		LP5562_REG_W_CURRENT,
124ff45262aSKim, Milo 	};
125ff45262aSKim, Milo 
126ff45262aSKim, Milo 	led->led_current = led_current;
127ff45262aSKim, Milo 	lp55xx_write(led->chip, addr[led->chan_nr], led_current);
128ff45262aSKim, Milo }
129ff45262aSKim, Milo 
130ff45262aSKim, Milo static void lp5562_load_engine(struct lp55xx_chip *chip)
131ff45262aSKim, Milo {
132ff45262aSKim, Milo 	enum lp55xx_engine_index idx = chip->engine_idx;
133ff45262aSKim, Milo 	u8 mask[] = {
134ff45262aSKim, Milo 		[LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M,
135ff45262aSKim, Milo 		[LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M,
136ff45262aSKim, Milo 		[LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M,
137ff45262aSKim, Milo 	};
138ff45262aSKim, Milo 
139ff45262aSKim, Milo 	u8 val[] = {
140ff45262aSKim, Milo 		[LP55XX_ENGINE_1] = LP5562_LOAD_ENG1,
141ff45262aSKim, Milo 		[LP55XX_ENGINE_2] = LP5562_LOAD_ENG2,
142ff45262aSKim, Milo 		[LP55XX_ENGINE_3] = LP5562_LOAD_ENG3,
143ff45262aSKim, Milo 	};
144ff45262aSKim, Milo 
145ff45262aSKim, Milo 	lp55xx_update_bits(chip, LP5562_REG_OP_MODE, mask[idx], val[idx]);
146ff45262aSKim, Milo 
147ff45262aSKim, Milo 	lp5562_wait_opmode_done();
148ff45262aSKim, Milo }
149ff45262aSKim, Milo 
150ff45262aSKim, Milo static void lp5562_stop_engine(struct lp55xx_chip *chip)
151ff45262aSKim, Milo {
152ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DISABLE);
153ff45262aSKim, Milo 	lp5562_wait_opmode_done();
154ff45262aSKim, Milo }
155ff45262aSKim, Milo 
156ff45262aSKim, Milo static void lp5562_run_engine(struct lp55xx_chip *chip, bool start)
157ff45262aSKim, Milo {
158ff45262aSKim, Milo 	int ret;
159ff45262aSKim, Milo 	u8 mode;
160ff45262aSKim, Milo 	u8 exec;
161ff45262aSKim, Milo 
162ff45262aSKim, Milo 	/* stop engine */
163ff45262aSKim, Milo 	if (!start) {
164ff45262aSKim, Milo 		lp55xx_write(chip, LP5562_REG_ENABLE, LP5562_ENABLE_DEFAULT);
165ff45262aSKim, Milo 		lp5562_wait_enable_done();
166ff45262aSKim, Milo 		lp5562_stop_engine(chip);
167ff45262aSKim, Milo 		lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
168ff45262aSKim, Milo 		lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
169ff45262aSKim, Milo 		lp5562_wait_opmode_done();
170ff45262aSKim, Milo 		return;
171ff45262aSKim, Milo 	}
172ff45262aSKim, Milo 
173ff45262aSKim, Milo 	/*
174ff45262aSKim, Milo 	 * To run the engine,
175ff45262aSKim, Milo 	 * operation mode and enable register should updated at the same time
176ff45262aSKim, Milo 	 */
177ff45262aSKim, Milo 
178ff45262aSKim, Milo 	ret = lp55xx_read(chip, LP5562_REG_OP_MODE, &mode);
179ff45262aSKim, Milo 	if (ret)
180ff45262aSKim, Milo 		return;
181ff45262aSKim, Milo 
182ff45262aSKim, Milo 	ret = lp55xx_read(chip, LP5562_REG_ENABLE, &exec);
183ff45262aSKim, Milo 	if (ret)
184ff45262aSKim, Milo 		return;
185ff45262aSKim, Milo 
186ff45262aSKim, Milo 	/* change operation mode to RUN only when each engine is loading */
187ff45262aSKim, Milo 	if (LP5562_ENG1_IS_LOADING(mode)) {
188ff45262aSKim, Milo 		mode = (mode & ~LP5562_MODE_ENG1_M) | LP5562_RUN_ENG1;
189ff45262aSKim, Milo 		exec = (exec & ~LP5562_EXEC_ENG1_M) | LP5562_RUN_ENG1;
190ff45262aSKim, Milo 	}
191ff45262aSKim, Milo 
192ff45262aSKim, Milo 	if (LP5562_ENG2_IS_LOADING(mode)) {
193ff45262aSKim, Milo 		mode = (mode & ~LP5562_MODE_ENG2_M) | LP5562_RUN_ENG2;
194ff45262aSKim, Milo 		exec = (exec & ~LP5562_EXEC_ENG2_M) | LP5562_RUN_ENG2;
195ff45262aSKim, Milo 	}
196ff45262aSKim, Milo 
197ff45262aSKim, Milo 	if (LP5562_ENG3_IS_LOADING(mode)) {
198ff45262aSKim, Milo 		mode = (mode & ~LP5562_MODE_ENG3_M) | LP5562_RUN_ENG3;
199ff45262aSKim, Milo 		exec = (exec & ~LP5562_EXEC_ENG3_M) | LP5562_RUN_ENG3;
200ff45262aSKim, Milo 	}
201ff45262aSKim, Milo 
202ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_OP_MODE, mode);
203ff45262aSKim, Milo 	lp5562_wait_opmode_done();
204ff45262aSKim, Milo 
205ff45262aSKim, Milo 	lp55xx_update_bits(chip, LP5562_REG_ENABLE, LP5562_EXEC_M, exec);
206ff45262aSKim, Milo 	lp5562_wait_enable_done();
207ff45262aSKim, Milo }
208ff45262aSKim, Milo 
209ff45262aSKim, Milo static int lp5562_update_firmware(struct lp55xx_chip *chip,
210ff45262aSKim, Milo 					const u8 *data, size_t size)
211ff45262aSKim, Milo {
212ff45262aSKim, Milo 	enum lp55xx_engine_index idx = chip->engine_idx;
213ff45262aSKim, Milo 	u8 pattern[LP5562_PROGRAM_LENGTH] = {0};
214ff45262aSKim, Milo 	u8 addr[] = {
215ff45262aSKim, Milo 		[LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1,
216ff45262aSKim, Milo 		[LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2,
217ff45262aSKim, Milo 		[LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3,
218ff45262aSKim, Milo 	};
219ff45262aSKim, Milo 	unsigned cmd;
220ff45262aSKim, Milo 	char c[3];
221ff45262aSKim, Milo 	int program_size;
222ff45262aSKim, Milo 	int nrchars;
223ff45262aSKim, Milo 	int offset = 0;
224ff45262aSKim, Milo 	int ret;
225ff45262aSKim, Milo 	int i;
226ff45262aSKim, Milo 
227ff45262aSKim, Milo 	/* clear program memory before updating */
228ff45262aSKim, Milo 	for (i = 0; i < LP5562_PROGRAM_LENGTH; i++)
229ff45262aSKim, Milo 		lp55xx_write(chip, addr[idx] + i, 0);
230ff45262aSKim, Milo 
231ff45262aSKim, Milo 	i = 0;
232ff45262aSKim, Milo 	while ((offset < size - 1) && (i < LP5562_PROGRAM_LENGTH)) {
233ff45262aSKim, Milo 		/* separate sscanfs because length is working only for %s */
234ff45262aSKim, Milo 		ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
235ff45262aSKim, Milo 		if (ret != 1)
236ff45262aSKim, Milo 			goto err;
237ff45262aSKim, Milo 
238ff45262aSKim, Milo 		ret = sscanf(c, "%2x", &cmd);
239ff45262aSKim, Milo 		if (ret != 1)
240ff45262aSKim, Milo 			goto err;
241ff45262aSKim, Milo 
242ff45262aSKim, Milo 		pattern[i] = (u8)cmd;
243ff45262aSKim, Milo 		offset += nrchars;
244ff45262aSKim, Milo 		i++;
245ff45262aSKim, Milo 	}
246ff45262aSKim, Milo 
247ff45262aSKim, Milo 	/* Each instruction is 16bit long. Check that length is even */
248ff45262aSKim, Milo 	if (i % 2)
249ff45262aSKim, Milo 		goto err;
250ff45262aSKim, Milo 
251ff45262aSKim, Milo 	program_size = i;
252ff45262aSKim, Milo 	for (i = 0; i < program_size; i++)
253ff45262aSKim, Milo 		lp55xx_write(chip, addr[idx] + i, pattern[i]);
254ff45262aSKim, Milo 
255ff45262aSKim, Milo 	return 0;
256ff45262aSKim, Milo 
257ff45262aSKim, Milo err:
258ff45262aSKim, Milo 	dev_err(&chip->cl->dev, "wrong pattern format\n");
259ff45262aSKim, Milo 	return -EINVAL;
260ff45262aSKim, Milo }
261ff45262aSKim, Milo 
262ff45262aSKim, Milo static void lp5562_firmware_loaded(struct lp55xx_chip *chip)
263ff45262aSKim, Milo {
264ff45262aSKim, Milo 	const struct firmware *fw = chip->fw;
265ff45262aSKim, Milo 
266ff45262aSKim, Milo 	if (fw->size > LP5562_PROGRAM_LENGTH) {
267ff45262aSKim, Milo 		dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
268ff45262aSKim, Milo 			fw->size);
269ff45262aSKim, Milo 		return;
270ff45262aSKim, Milo 	}
271ff45262aSKim, Milo 
272ff45262aSKim, Milo 	/*
273ff45262aSKim, Milo 	 * Program momery sequence
274ff45262aSKim, Milo 	 *  1) set engine mode to "LOAD"
275ff45262aSKim, Milo 	 *  2) write firmware data into program memory
276ff45262aSKim, Milo 	 */
277ff45262aSKim, Milo 
278ff45262aSKim, Milo 	lp5562_load_engine(chip);
279ff45262aSKim, Milo 	lp5562_update_firmware(chip, fw->data, fw->size);
280ff45262aSKim, Milo }
281ff45262aSKim, Milo 
282ff45262aSKim, Milo static int lp5562_post_init_device(struct lp55xx_chip *chip)
283ff45262aSKim, Milo {
284ff45262aSKim, Milo 	int ret;
28581f2a5b4SKim, Milo 	u8 cfg = LP5562_DEFAULT_CFG;
286ff45262aSKim, Milo 
287ff45262aSKim, Milo 	/* Set all PWMs to direct control mode */
288ff45262aSKim, Milo 	ret = lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT);
289ff45262aSKim, Milo 	if (ret)
290ff45262aSKim, Milo 		return ret;
291ff45262aSKim, Milo 
292ff45262aSKim, Milo 	lp5562_wait_opmode_done();
293ff45262aSKim, Milo 
29481f2a5b4SKim, Milo 	/* Update configuration for the clock setting */
29581f2a5b4SKim, Milo 	if (!lp55xx_is_extclk_used(chip))
29681f2a5b4SKim, Milo 		cfg |= LP5562_CLK_INT;
29781f2a5b4SKim, Milo 
29881f2a5b4SKim, Milo 	ret = lp55xx_write(chip, LP5562_REG_CONFIG, cfg);
299ff45262aSKim, Milo 	if (ret)
300ff45262aSKim, Milo 		return ret;
301ff45262aSKim, Milo 
302ff45262aSKim, Milo 	/* Initialize all channels PWM to zero -> leds off */
303ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_R_PWM, 0);
304ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_G_PWM, 0);
305ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_B_PWM, 0);
306ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_W_PWM, 0);
307ff45262aSKim, Milo 
308ff45262aSKim, Milo 	/* Set LED map as register PWM by default */
309ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM);
310ff45262aSKim, Milo 
311ff45262aSKim, Milo 	return 0;
312ff45262aSKim, Milo }
313ff45262aSKim, Milo 
314*95b2af63SAndrew Lunn static int lp5562_led_brightness(struct lp55xx_led *led)
315ff45262aSKim, Milo {
316ff45262aSKim, Milo 	struct lp55xx_chip *chip = led->chip;
317ff45262aSKim, Milo 	u8 addr[] = {
318ff45262aSKim, Milo 		LP5562_REG_R_PWM,
319ff45262aSKim, Milo 		LP5562_REG_G_PWM,
320ff45262aSKim, Milo 		LP5562_REG_B_PWM,
321ff45262aSKim, Milo 		LP5562_REG_W_PWM,
322ff45262aSKim, Milo 	};
323*95b2af63SAndrew Lunn 	int ret;
324ff45262aSKim, Milo 
325ff45262aSKim, Milo 	mutex_lock(&chip->lock);
326*95b2af63SAndrew Lunn 	ret = lp55xx_write(chip, addr[led->chan_nr], led->brightness);
327ff45262aSKim, Milo 	mutex_unlock(&chip->lock);
328*95b2af63SAndrew Lunn 
329*95b2af63SAndrew Lunn 	return ret;
330ff45262aSKim, Milo }
331ff45262aSKim, Milo 
332ff45262aSKim, Milo static void lp5562_write_program_memory(struct lp55xx_chip *chip,
333ff45262aSKim, Milo 					u8 base, const u8 *rgb, int size)
334ff45262aSKim, Milo {
335ff45262aSKim, Milo 	int i;
336ff45262aSKim, Milo 
337ff45262aSKim, Milo 	if (!rgb || size <= 0)
338ff45262aSKim, Milo 		return;
339ff45262aSKim, Milo 
340ff45262aSKim, Milo 	for (i = 0; i < size; i++)
341ff45262aSKim, Milo 		lp55xx_write(chip, base + i, *(rgb + i));
342ff45262aSKim, Milo 
343ff45262aSKim, Milo 	lp55xx_write(chip, base + i, 0);
344ff45262aSKim, Milo 	lp55xx_write(chip, base + i + 1, 0);
345ff45262aSKim, Milo }
346ff45262aSKim, Milo 
347ff45262aSKim, Milo /* check the size of program count */
348ff45262aSKim, Milo static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn)
349ff45262aSKim, Milo {
350432eb69dSJingoo Han 	return ptn->size_r >= LP5562_PROGRAM_LENGTH ||
351ff45262aSKim, Milo 	       ptn->size_g >= LP5562_PROGRAM_LENGTH ||
352432eb69dSJingoo Han 	       ptn->size_b >= LP5562_PROGRAM_LENGTH;
353ff45262aSKim, Milo }
354ff45262aSKim, Milo 
355ff45262aSKim, Milo static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode)
356ff45262aSKim, Milo {
357ff45262aSKim, Milo 	struct lp55xx_predef_pattern *ptn;
358ff45262aSKim, Milo 	int i;
359ff45262aSKim, Milo 
360ff45262aSKim, Milo 	if (mode == LP5562_PATTERN_OFF) {
361ff45262aSKim, Milo 		lp5562_run_engine(chip, false);
362ff45262aSKim, Milo 		return 0;
363ff45262aSKim, Milo 	}
364ff45262aSKim, Milo 
365ff45262aSKim, Milo 	ptn = chip->pdata->patterns + (mode - 1);
366ff45262aSKim, Milo 	if (!ptn || _is_pc_overflow(ptn)) {
367ff45262aSKim, Milo 		dev_err(&chip->cl->dev, "invalid pattern data\n");
368ff45262aSKim, Milo 		return -EINVAL;
369ff45262aSKim, Milo 	}
370ff45262aSKim, Milo 
371ff45262aSKim, Milo 	lp5562_stop_engine(chip);
372ff45262aSKim, Milo 
373ff45262aSKim, Milo 	/* Set LED map as RGB */
374ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_RGB);
375ff45262aSKim, Milo 
376ff45262aSKim, Milo 	/* Load engines */
377ff45262aSKim, Milo 	for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) {
378ff45262aSKim, Milo 		chip->engine_idx = i;
379ff45262aSKim, Milo 		lp5562_load_engine(chip);
380ff45262aSKim, Milo 	}
381ff45262aSKim, Milo 
382ff45262aSKim, Milo 	/* Clear program registers */
383ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1, 0);
384ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1 + 1, 0);
385ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2, 0);
386ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2 + 1, 0);
387ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3, 0);
388ff45262aSKim, Milo 	lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3 + 1, 0);
389ff45262aSKim, Milo 
390ff45262aSKim, Milo 	/* Program engines */
391ff45262aSKim, Milo 	lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG1,
392ff45262aSKim, Milo 				ptn->r, ptn->size_r);
393ff45262aSKim, Milo 	lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG2,
394ff45262aSKim, Milo 				ptn->g, ptn->size_g);
395ff45262aSKim, Milo 	lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG3,
396ff45262aSKim, Milo 				ptn->b, ptn->size_b);
397ff45262aSKim, Milo 
398ff45262aSKim, Milo 	/* Run engines */
399ff45262aSKim, Milo 	lp5562_run_engine(chip, true);
400ff45262aSKim, Milo 
401ff45262aSKim, Milo 	return 0;
402ff45262aSKim, Milo }
403ff45262aSKim, Milo 
404ff45262aSKim, Milo static ssize_t lp5562_store_pattern(struct device *dev,
405ff45262aSKim, Milo 				struct device_attribute *attr,
406ff45262aSKim, Milo 				const char *buf, size_t len)
407ff45262aSKim, Milo {
408ff45262aSKim, Milo 	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
409ff45262aSKim, Milo 	struct lp55xx_chip *chip = led->chip;
410ff45262aSKim, Milo 	struct lp55xx_predef_pattern *ptn = chip->pdata->patterns;
411ff45262aSKim, Milo 	int num_patterns = chip->pdata->num_patterns;
412ff45262aSKim, Milo 	unsigned long mode;
413ff45262aSKim, Milo 	int ret;
414ff45262aSKim, Milo 
415ff45262aSKim, Milo 	ret = kstrtoul(buf, 0, &mode);
416ff45262aSKim, Milo 	if (ret)
417ff45262aSKim, Milo 		return ret;
418ff45262aSKim, Milo 
419ff45262aSKim, Milo 	if (mode > num_patterns || !ptn)
420ff45262aSKim, Milo 		return -EINVAL;
421ff45262aSKim, Milo 
422ff45262aSKim, Milo 	mutex_lock(&chip->lock);
423ff45262aSKim, Milo 	ret = lp5562_run_predef_led_pattern(chip, mode);
424ff45262aSKim, Milo 	mutex_unlock(&chip->lock);
425ff45262aSKim, Milo 
426ff45262aSKim, Milo 	if (ret)
427ff45262aSKim, Milo 		return ret;
428ff45262aSKim, Milo 
429ff45262aSKim, Milo 	return len;
430ff45262aSKim, Milo }
431ff45262aSKim, Milo 
432ff45262aSKim, Milo static ssize_t lp5562_store_engine_mux(struct device *dev,
433ff45262aSKim, Milo 				     struct device_attribute *attr,
434ff45262aSKim, Milo 				     const char *buf, size_t len)
435ff45262aSKim, Milo {
436ff45262aSKim, Milo 	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
437ff45262aSKim, Milo 	struct lp55xx_chip *chip = led->chip;
438ff45262aSKim, Milo 	u8 mask;
439ff45262aSKim, Milo 	u8 val;
440ff45262aSKim, Milo 
441ff45262aSKim, Milo 	/* LED map
442ff45262aSKim, Milo 	 * R ... Engine 1 (fixed)
443ff45262aSKim, Milo 	 * G ... Engine 2 (fixed)
444ff45262aSKim, Milo 	 * B ... Engine 3 (fixed)
445ff45262aSKim, Milo 	 * W ... Engine 1 or 2 or 3
446ff45262aSKim, Milo 	 */
447ff45262aSKim, Milo 
448ff45262aSKim, Milo 	if (sysfs_streq(buf, "RGB")) {
449ff45262aSKim, Milo 		mask = LP5562_ENG_FOR_RGB_M;
450ff45262aSKim, Milo 		val = LP5562_ENG_SEL_RGB;
451ff45262aSKim, Milo 	} else if (sysfs_streq(buf, "W")) {
452ff45262aSKim, Milo 		enum lp55xx_engine_index idx = chip->engine_idx;
453ff45262aSKim, Milo 
454ff45262aSKim, Milo 		mask = LP5562_ENG_FOR_W_M;
455ff45262aSKim, Milo 		switch (idx) {
456ff45262aSKim, Milo 		case LP55XX_ENGINE_1:
457ff45262aSKim, Milo 			val = LP5562_ENG1_FOR_W;
458ff45262aSKim, Milo 			break;
459ff45262aSKim, Milo 		case LP55XX_ENGINE_2:
460ff45262aSKim, Milo 			val = LP5562_ENG2_FOR_W;
461ff45262aSKim, Milo 			break;
462ff45262aSKim, Milo 		case LP55XX_ENGINE_3:
463ff45262aSKim, Milo 			val = LP5562_ENG3_FOR_W;
464ff45262aSKim, Milo 			break;
465ff45262aSKim, Milo 		default:
466ff45262aSKim, Milo 			return -EINVAL;
467ff45262aSKim, Milo 		}
468ff45262aSKim, Milo 
469ff45262aSKim, Milo 	} else {
470ff45262aSKim, Milo 		dev_err(dev, "choose RGB or W\n");
471ff45262aSKim, Milo 		return -EINVAL;
472ff45262aSKim, Milo 	}
473ff45262aSKim, Milo 
474ff45262aSKim, Milo 	mutex_lock(&chip->lock);
475ff45262aSKim, Milo 	lp55xx_update_bits(chip, LP5562_REG_ENG_SEL, mask, val);
476ff45262aSKim, Milo 	mutex_unlock(&chip->lock);
477ff45262aSKim, Milo 
478ff45262aSKim, Milo 	return len;
479ff45262aSKim, Milo }
480ff45262aSKim, Milo 
481daa124b1SMilo Kim static LP55XX_DEV_ATTR_WO(led_pattern, lp5562_store_pattern);
482daa124b1SMilo Kim static LP55XX_DEV_ATTR_WO(engine_mux, lp5562_store_engine_mux);
483ff45262aSKim, Milo 
484ff45262aSKim, Milo static struct attribute *lp5562_attributes[] = {
485ff45262aSKim, Milo 	&dev_attr_led_pattern.attr,
486ff45262aSKim, Milo 	&dev_attr_engine_mux.attr,
487ff45262aSKim, Milo 	NULL,
488ff45262aSKim, Milo };
489ff45262aSKim, Milo 
490ff45262aSKim, Milo static const struct attribute_group lp5562_group = {
491ff45262aSKim, Milo 	.attrs = lp5562_attributes,
492ff45262aSKim, Milo };
493ff45262aSKim, Milo 
494ff45262aSKim, Milo /* Chip specific configurations */
495ff45262aSKim, Milo static struct lp55xx_device_config lp5562_cfg = {
496ff45262aSKim, Milo 	.max_channel  = LP5562_MAX_LEDS,
497ff45262aSKim, Milo 	.reset = {
498ff45262aSKim, Milo 		.addr = LP5562_REG_RESET,
499ff45262aSKim, Milo 		.val  = LP5562_RESET,
500ff45262aSKim, Milo 	},
501ff45262aSKim, Milo 	.enable = {
502ff45262aSKim, Milo 		.addr = LP5562_REG_ENABLE,
503ff45262aSKim, Milo 		.val  = LP5562_ENABLE_DEFAULT,
504ff45262aSKim, Milo 	},
505ff45262aSKim, Milo 	.post_init_device   = lp5562_post_init_device,
506ff45262aSKim, Milo 	.set_led_current    = lp5562_set_led_current,
507*95b2af63SAndrew Lunn 	.brightness_fn      = lp5562_led_brightness,
508ff45262aSKim, Milo 	.run_engine         = lp5562_run_engine,
509ff45262aSKim, Milo 	.firmware_cb        = lp5562_firmware_loaded,
510ff45262aSKim, Milo 	.dev_attr_group     = &lp5562_group,
511ff45262aSKim, Milo };
512ff45262aSKim, Milo 
513ff45262aSKim, Milo static int lp5562_probe(struct i2c_client *client,
514ff45262aSKim, Milo 			const struct i2c_device_id *id)
515ff45262aSKim, Milo {
516ff45262aSKim, Milo 	int ret;
517ff45262aSKim, Milo 	struct lp55xx_chip *chip;
518ff45262aSKim, Milo 	struct lp55xx_led *led;
519ed133352SMilo Kim 	struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
520e015050cSKim, Milo 	struct device_node *np = client->dev.of_node;
521ff45262aSKim, Milo 
522ed133352SMilo Kim 	if (!pdata) {
523e015050cSKim, Milo 		if (np) {
524ed133352SMilo Kim 			pdata = lp55xx_of_populate_pdata(&client->dev, np);
525ed133352SMilo Kim 			if (IS_ERR(pdata))
526ed133352SMilo Kim 				return PTR_ERR(pdata);
527e015050cSKim, Milo 		} else {
528ff45262aSKim, Milo 			dev_err(&client->dev, "no platform data\n");
529ff45262aSKim, Milo 			return -EINVAL;
530ff45262aSKim, Milo 		}
531e015050cSKim, Milo 	}
532ff45262aSKim, Milo 
533ff45262aSKim, Milo 	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
534ff45262aSKim, Milo 	if (!chip)
535ff45262aSKim, Milo 		return -ENOMEM;
536ff45262aSKim, Milo 
537ff45262aSKim, Milo 	led = devm_kzalloc(&client->dev,
538ff45262aSKim, Milo 			sizeof(*led) * pdata->num_channels, GFP_KERNEL);
539ff45262aSKim, Milo 	if (!led)
540ff45262aSKim, Milo 		return -ENOMEM;
541ff45262aSKim, Milo 
542ff45262aSKim, Milo 	chip->cl = client;
543ff45262aSKim, Milo 	chip->pdata = pdata;
544ff45262aSKim, Milo 	chip->cfg = &lp5562_cfg;
545ff45262aSKim, Milo 
546ff45262aSKim, Milo 	mutex_init(&chip->lock);
547ff45262aSKim, Milo 
548ff45262aSKim, Milo 	i2c_set_clientdata(client, led);
549ff45262aSKim, Milo 
550ff45262aSKim, Milo 	ret = lp55xx_init_device(chip);
551ff45262aSKim, Milo 	if (ret)
552ff45262aSKim, Milo 		goto err_init;
553ff45262aSKim, Milo 
554ff45262aSKim, Milo 	ret = lp55xx_register_leds(led, chip);
555ff45262aSKim, Milo 	if (ret)
556ff45262aSKim, Milo 		goto err_register_leds;
557ff45262aSKim, Milo 
558ff45262aSKim, Milo 	ret = lp55xx_register_sysfs(chip);
559ff45262aSKim, Milo 	if (ret) {
560ff45262aSKim, Milo 		dev_err(&client->dev, "registering sysfs failed\n");
561ff45262aSKim, Milo 		goto err_register_sysfs;
562ff45262aSKim, Milo 	}
563ff45262aSKim, Milo 
564ff45262aSKim, Milo 	return 0;
565ff45262aSKim, Milo 
566ff45262aSKim, Milo err_register_sysfs:
567ff45262aSKim, Milo 	lp55xx_unregister_leds(led, chip);
568ff45262aSKim, Milo err_register_leds:
569ff45262aSKim, Milo 	lp55xx_deinit_device(chip);
570ff45262aSKim, Milo err_init:
571ff45262aSKim, Milo 	return ret;
572ff45262aSKim, Milo }
573ff45262aSKim, Milo 
574ff45262aSKim, Milo static int lp5562_remove(struct i2c_client *client)
575ff45262aSKim, Milo {
576ff45262aSKim, Milo 	struct lp55xx_led *led = i2c_get_clientdata(client);
577ff45262aSKim, Milo 	struct lp55xx_chip *chip = led->chip;
578ff45262aSKim, Milo 
579ff45262aSKim, Milo 	lp5562_stop_engine(chip);
580ff45262aSKim, Milo 
581ff45262aSKim, Milo 	lp55xx_unregister_sysfs(chip);
582ff45262aSKim, Milo 	lp55xx_unregister_leds(led, chip);
583ff45262aSKim, Milo 	lp55xx_deinit_device(chip);
584ff45262aSKim, Milo 
585ff45262aSKim, Milo 	return 0;
586ff45262aSKim, Milo }
587ff45262aSKim, Milo 
588ff45262aSKim, Milo static const struct i2c_device_id lp5562_id[] = {
589ff45262aSKim, Milo 	{ "lp5562", 0 },
590ff45262aSKim, Milo 	{ }
591ff45262aSKim, Milo };
592ff45262aSKim, Milo MODULE_DEVICE_TABLE(i2c, lp5562_id);
593ff45262aSKim, Milo 
59428720cffSAxel Lin #ifdef CONFIG_OF
59528720cffSAxel Lin static const struct of_device_id of_lp5562_leds_match[] = {
59628720cffSAxel Lin 	{ .compatible = "ti,lp5562", },
59728720cffSAxel Lin 	{},
59828720cffSAxel Lin };
59928720cffSAxel Lin 
60028720cffSAxel Lin MODULE_DEVICE_TABLE(of, of_lp5562_leds_match);
60128720cffSAxel Lin #endif
60228720cffSAxel Lin 
603ff45262aSKim, Milo static struct i2c_driver lp5562_driver = {
604ff45262aSKim, Milo 	.driver = {
605ff45262aSKim, Milo 		.name	= "lp5562",
60628720cffSAxel Lin 		.of_match_table = of_match_ptr(of_lp5562_leds_match),
607ff45262aSKim, Milo 	},
608ff45262aSKim, Milo 	.probe		= lp5562_probe,
609ff45262aSKim, Milo 	.remove		= lp5562_remove,
610ff45262aSKim, Milo 	.id_table	= lp5562_id,
611ff45262aSKim, Milo };
612ff45262aSKim, Milo 
613ff45262aSKim, Milo module_i2c_driver(lp5562_driver);
614ff45262aSKim, Milo 
615ff45262aSKim, Milo MODULE_DESCRIPTION("Texas Instruments LP5562 LED Driver");
616ff45262aSKim, Milo MODULE_AUTHOR("Milo Kim");
617ff45262aSKim, Milo MODULE_LICENSE("GPL");
618