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