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
lp5562_wait_opmode_done(void)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
lp5562_wait_enable_done(void)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
lp5562_set_led_current(struct lp55xx_led * led,u8 led_current)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
lp5562_load_engine(struct lp55xx_chip * chip)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
lp5562_stop_engine(struct lp55xx_chip * chip)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
lp5562_run_engine(struct lp55xx_chip * chip,bool start)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
lp5562_update_firmware(struct lp55xx_chip * chip,const u8 * data,size_t size)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
lp5562_firmware_loaded(struct lp55xx_chip * chip)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
lp5562_post_init_device(struct lp55xx_chip * chip)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
lp5562_led_brightness(struct lp55xx_led * led)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
lp5562_write_program_memory(struct lp55xx_chip * chip,u8 base,const u8 * rgb,int size)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 */
_is_pc_overflow(struct lp55xx_predef_pattern * ptn)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
lp5562_run_predef_led_pattern(struct lp55xx_chip * chip,int mode)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
lp5562_store_pattern(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)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
lp5562_store_engine_mux(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)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
lp5562_probe(struct i2c_client * client)5146231f926SUwe Kleine-König static int lp5562_probe(struct i2c_client *client)
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);
5208853c95eSMarek Behún struct device_node *np = dev_of_node(&client->dev);
521ff45262aSKim, Milo
52292a81562SDan Murphy chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
52392a81562SDan Murphy if (!chip)
52492a81562SDan Murphy return -ENOMEM;
52592a81562SDan Murphy
52692a81562SDan Murphy chip->cfg = &lp5562_cfg;
52792a81562SDan Murphy
528ed133352SMilo Kim if (!pdata) {
529e015050cSKim, Milo if (np) {
53092a81562SDan Murphy pdata = lp55xx_of_populate_pdata(&client->dev, np,
53192a81562SDan Murphy chip);
532ed133352SMilo Kim if (IS_ERR(pdata))
533ed133352SMilo Kim return PTR_ERR(pdata);
534e015050cSKim, Milo } else {
535ff45262aSKim, Milo dev_err(&client->dev, "no platform data\n");
536ff45262aSKim, Milo return -EINVAL;
537ff45262aSKim, Milo }
538e015050cSKim, Milo }
539ff45262aSKim, Milo
540ff45262aSKim, Milo
541a86854d0SKees Cook led = devm_kcalloc(&client->dev,
542a86854d0SKees Cook pdata->num_channels, sizeof(*led), GFP_KERNEL);
543ff45262aSKim, Milo if (!led)
544ff45262aSKim, Milo return -ENOMEM;
545ff45262aSKim, Milo
546ff45262aSKim, Milo chip->cl = client;
547ff45262aSKim, Milo chip->pdata = pdata;
548ff45262aSKim, Milo
549ff45262aSKim, Milo mutex_init(&chip->lock);
550ff45262aSKim, Milo
551ff45262aSKim, Milo i2c_set_clientdata(client, led);
552ff45262aSKim, Milo
553ff45262aSKim, Milo ret = lp55xx_init_device(chip);
554ff45262aSKim, Milo if (ret)
555ff45262aSKim, Milo goto err_init;
556ff45262aSKim, Milo
557ff45262aSKim, Milo ret = lp55xx_register_leds(led, chip);
558ff45262aSKim, Milo if (ret)
559c732eaf0SDan Murphy goto err_out;
560ff45262aSKim, Milo
561ff45262aSKim, Milo ret = lp55xx_register_sysfs(chip);
562ff45262aSKim, Milo if (ret) {
563ff45262aSKim, Milo dev_err(&client->dev, "registering sysfs failed\n");
564c732eaf0SDan Murphy goto err_out;
565ff45262aSKim, Milo }
566ff45262aSKim, Milo
567ff45262aSKim, Milo return 0;
568ff45262aSKim, Milo
569c732eaf0SDan Murphy err_out:
570ff45262aSKim, Milo lp55xx_deinit_device(chip);
571ff45262aSKim, Milo err_init:
572ff45262aSKim, Milo return ret;
573ff45262aSKim, Milo }
574ff45262aSKim, Milo
lp5562_remove(struct i2c_client * client)575ed5c2f5fSUwe Kleine-König static void lp5562_remove(struct i2c_client *client)
576ff45262aSKim, Milo {
577ff45262aSKim, Milo struct lp55xx_led *led = i2c_get_clientdata(client);
578ff45262aSKim, Milo struct lp55xx_chip *chip = led->chip;
579ff45262aSKim, Milo
580ff45262aSKim, Milo lp5562_stop_engine(chip);
581ff45262aSKim, Milo
582ff45262aSKim, Milo lp55xx_unregister_sysfs(chip);
583ff45262aSKim, Milo lp55xx_deinit_device(chip);
584ff45262aSKim, Milo }
585ff45262aSKim, Milo
586ff45262aSKim, Milo static const struct i2c_device_id lp5562_id[] = {
587ff45262aSKim, Milo { "lp5562", 0 },
588ff45262aSKim, Milo { }
589ff45262aSKim, Milo };
590ff45262aSKim, Milo MODULE_DEVICE_TABLE(i2c, lp5562_id);
591ff45262aSKim, Milo
59228720cffSAxel Lin static const struct of_device_id of_lp5562_leds_match[] = {
59328720cffSAxel Lin { .compatible = "ti,lp5562", },
59428720cffSAxel Lin {},
59528720cffSAxel Lin };
59628720cffSAxel Lin
59728720cffSAxel Lin MODULE_DEVICE_TABLE(of, of_lp5562_leds_match);
59828720cffSAxel Lin
599ff45262aSKim, Milo static struct i2c_driver lp5562_driver = {
600ff45262aSKim, Milo .driver = {
601ff45262aSKim, Milo .name = "lp5562",
602*3d590af8SZhu Wang .of_match_table = of_lp5562_leds_match,
603ff45262aSKim, Milo },
604d9ff8a8eSUwe Kleine-König .probe = lp5562_probe,
605ff45262aSKim, Milo .remove = lp5562_remove,
606ff45262aSKim, Milo .id_table = lp5562_id,
607ff45262aSKim, Milo };
608ff45262aSKim, Milo
609ff45262aSKim, Milo module_i2c_driver(lp5562_driver);
610ff45262aSKim, Milo
611ff45262aSKim, Milo MODULE_DESCRIPTION("Texas Instruments LP5562 LED Driver");
612ff45262aSKim, Milo MODULE_AUTHOR("Milo Kim");
613ff45262aSKim, Milo MODULE_LICENSE("GPL");
614