Blame view

drivers/leds/leds-lp5521.c 15.6 KB
500fe1413   Samu Onkalo   leds: driver for ...
1
2
3
4
  /*
   * LP5521 LED chip driver.
   *
   * Copyright (C) 2010 Nokia Corporation
a2387cb9f   Milo(Woogyom) Kim   leds-lp5521/5523:...
5
   * Copyright (C) 2012 Texas Instruments
500fe1413   Samu Onkalo   leds: driver for ...
6
7
   *
   * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
a2387cb9f   Milo(Woogyom) Kim   leds-lp5521/5523:...
8
   *          Milo(Woogyom) Kim <milo.kim@ti.com>
500fe1413   Samu Onkalo   leds: driver for ...
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * version 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
   * General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   * 02110-1301 USA
   */
500fe1413   Samu Onkalo   leds: driver for ...
24
  #include <linux/delay.h>
79bcc10b8   Milo(Woogyom) Kim   leds-lp55xx: clea...
25
26
  #include <linux/firmware.h>
  #include <linux/i2c.h>
500fe1413   Samu Onkalo   leds: driver for ...
27
  #include <linux/leds.h>
79bcc10b8   Milo(Woogyom) Kim   leds-lp55xx: clea...
28
29
  #include <linux/module.h>
  #include <linux/mutex.h>
6a0c9a479   Milo(Woogyom) Kim   leds-lp55xx: use ...
30
  #include <linux/platform_data/leds-lp55xx.h>
79bcc10b8   Milo(Woogyom) Kim   leds-lp55xx: clea...
31
  #include <linux/slab.h>
7542a04b1   Linus Walleij   leds: lp55xx: add...
32
  #include <linux/of.h>
6a0c9a479   Milo(Woogyom) Kim   leds-lp55xx: use ...
33
34
  
  #include "leds-lp55xx-common.h"
500fe1413   Samu Onkalo   leds: driver for ...
35

12f022d27   Milo(Woogyom) Kim   leds-lp55xx: clea...
36
37
38
  #define LP5521_PROGRAM_LENGTH		32
  #define LP5521_MAX_LEDS			3
  #define LP5521_CMD_DIRECT		0x3F
500fe1413   Samu Onkalo   leds: driver for ...
39
40
41
42
43
44
45
46
47
48
49
  
  /* Registers */
  #define LP5521_REG_ENABLE		0x00
  #define LP5521_REG_OP_MODE		0x01
  #define LP5521_REG_R_PWM		0x02
  #define LP5521_REG_G_PWM		0x03
  #define LP5521_REG_B_PWM		0x04
  #define LP5521_REG_R_CURRENT		0x05
  #define LP5521_REG_G_CURRENT		0x06
  #define LP5521_REG_B_CURRENT		0x07
  #define LP5521_REG_CONFIG		0x08
500fe1413   Samu Onkalo   leds: driver for ...
50
51
  #define LP5521_REG_STATUS		0x0C
  #define LP5521_REG_RESET		0x0D
500fe1413   Samu Onkalo   leds: driver for ...
52
53
54
  #define LP5521_REG_R_PROG_MEM		0x10
  #define LP5521_REG_G_PROG_MEM		0x30
  #define LP5521_REG_B_PROG_MEM		0x50
500fe1413   Samu Onkalo   leds: driver for ...
55
56
  /* Base register to set LED current */
  #define LP5521_REG_LED_CURRENT_BASE	LP5521_REG_R_CURRENT
500fe1413   Samu Onkalo   leds: driver for ...
57
58
59
60
61
62
63
  /* Base register to set the brightness */
  #define LP5521_REG_LED_PWM_BASE		LP5521_REG_R_PWM
  
  /* Bits in ENABLE register */
  #define LP5521_MASTER_ENABLE		0x40	/* Chip master enable */
  #define LP5521_LOGARITHMIC_PWM		0x80	/* Logarithmic PWM adjustment */
  #define LP5521_EXEC_RUN			0x2A
32a2f747d   Kim, Milo   drivers/leds/leds...
64
65
66
67
  #define LP5521_ENABLE_DEFAULT	\
  	(LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM)
  #define LP5521_ENABLE_RUN_PROGRAM	\
  	(LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN)
500fe1413   Samu Onkalo   leds: driver for ...
68

81f2a5b4a   Kim, Milo   leds: lp55xx: con...
69
70
71
72
73
74
75
76
77
78
79
  /* CONFIG register */
  #define LP5521_PWM_HF			0x40	/* PWM: 0 = 256Hz, 1 = 558Hz */
  #define LP5521_PWRSAVE_EN		0x20	/* 1 = Power save mode */
  #define LP5521_CP_MODE_OFF		0	/* Charge pump (CP) off */
  #define LP5521_CP_MODE_BYPASS		8	/* CP forced to bypass mode */
  #define LP5521_CP_MODE_1X5		0x10	/* CP forced to 1.5x mode */
  #define LP5521_CP_MODE_AUTO		0x18	/* Automatic mode selection */
  #define LP5521_R_TO_BATT		0x04	/* R out: 0 = CP, 1 = Vbat */
  #define LP5521_CLK_INT			0x01	/* Internal clock */
  #define LP5521_DEFAULT_CFG		\
  	(LP5521_PWM_HF | LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO)
500fe1413   Samu Onkalo   leds: driver for ...
80
81
  /* Status */
  #define LP5521_EXT_CLK_USED		0x08
b3c49c05b   Srinidhi KASAGAR   drivers/leds/leds...
82
83
  /* default R channel current register value */
  #define LP5521_REG_R_CURR_DEFAULT	0xAF
48068d5de   Milo(Woogyom) Kim   leds-lp55xx: use ...
84
85
  /* Reset register value */
  #define LP5521_RESET			0xFF
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  /* Program Memory Operations */
  #define LP5521_MODE_R_M			0x30	/* Operation Mode Register */
  #define LP5521_MODE_G_M			0x0C
  #define LP5521_MODE_B_M			0x03
  #define LP5521_LOAD_R			0x10
  #define LP5521_LOAD_G			0x04
  #define LP5521_LOAD_B			0x01
  
  #define LP5521_R_IS_LOADING(mode)	\
  	((mode & LP5521_MODE_R_M) == LP5521_LOAD_R)
  #define LP5521_G_IS_LOADING(mode)	\
  	((mode & LP5521_MODE_G_M) == LP5521_LOAD_G)
  #define LP5521_B_IS_LOADING(mode)	\
  	((mode & LP5521_MODE_B_M) == LP5521_LOAD_B)
  
  #define LP5521_EXEC_R_M			0x30	/* Enable Register */
  #define LP5521_EXEC_G_M			0x0C
  #define LP5521_EXEC_B_M			0x03
  #define LP5521_EXEC_M			0x3F
  #define LP5521_RUN_R			0x20
  #define LP5521_RUN_G			0x08
  #define LP5521_RUN_B			0x02
500fe1413   Samu Onkalo   leds: driver for ...
108

9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
109
110
111
112
113
  static inline void lp5521_wait_opmode_done(void)
  {
  	/* operation mode change needs to be longer than 153 us */
  	usleep_range(200, 300);
  }
944821740   Milo(Woogyom) Kim   leds-lp5521: clea...
114
115
116
117
118
  static inline void lp5521_wait_enable_done(void)
  {
  	/* it takes more 488 us to update ENABLE register */
  	usleep_range(500, 600);
  }
a96bfa135   Milo(Woogyom) Kim   leds-lp55xx: prov...
119
120
121
122
123
124
  static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current)
  {
  	led->led_current = led_current;
  	lp55xx_write(led->chip, LP5521_REG_LED_CURRENT_BASE + led->chan_nr,
  		led_current);
  }
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
125
  static void lp5521_load_engine(struct lp55xx_chip *chip)
500fe1413   Samu Onkalo   leds: driver for ...
126
  {
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
127
128
129
130
131
132
  	enum lp55xx_engine_index idx = chip->engine_idx;
  	u8 mask[] = {
  		[LP55XX_ENGINE_1] = LP5521_MODE_R_M,
  		[LP55XX_ENGINE_2] = LP5521_MODE_G_M,
  		[LP55XX_ENGINE_3] = LP5521_MODE_B_M,
  	};
500fe1413   Samu Onkalo   leds: driver for ...
133

9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
134
135
136
137
138
  	u8 val[] = {
  		[LP55XX_ENGINE_1] = LP5521_LOAD_R,
  		[LP55XX_ENGINE_2] = LP5521_LOAD_G,
  		[LP55XX_ENGINE_3] = LP5521_LOAD_B,
  	};
500fe1413   Samu Onkalo   leds: driver for ...
139

9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
140
  	lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], val[idx]);
500fe1413   Samu Onkalo   leds: driver for ...
141

9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
142
143
  	lp5521_wait_opmode_done();
  }
500fe1413   Samu Onkalo   leds: driver for ...
144

28c9266b3   Milo Kim   leds: lp5521/5523...
145
  static void lp5521_stop_all_engines(struct lp55xx_chip *chip)
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
146
147
148
  {
  	lp55xx_write(chip, LP5521_REG_OP_MODE, 0);
  	lp5521_wait_opmode_done();
500fe1413   Samu Onkalo   leds: driver for ...
149
  }
28c9266b3   Milo Kim   leds: lp5521/5523...
150
151
152
153
154
155
156
157
158
159
160
161
162
  static void lp5521_stop_engine(struct lp55xx_chip *chip)
  {
  	enum lp55xx_engine_index idx = chip->engine_idx;
  	u8 mask[] = {
  		[LP55XX_ENGINE_1] = LP5521_MODE_R_M,
  		[LP55XX_ENGINE_2] = LP5521_MODE_G_M,
  		[LP55XX_ENGINE_3] = LP5521_MODE_B_M,
  	};
  
  	lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], 0);
  
  	lp5521_wait_opmode_done();
  }
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
163
  static void lp5521_run_engine(struct lp55xx_chip *chip, bool start)
500fe1413   Samu Onkalo   leds: driver for ...
164
  {
500fe1413   Samu Onkalo   leds: driver for ...
165
  	int ret;
500fe1413   Samu Onkalo   leds: driver for ...
166
  	u8 mode;
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
167
  	u8 exec;
500fe1413   Samu Onkalo   leds: driver for ...
168

9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
169
170
171
172
173
174
175
176
177
178
179
180
181
182
  	/* stop engine */
  	if (!start) {
  		lp5521_stop_engine(chip);
  		lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
  		lp5521_wait_opmode_done();
  		return;
  	}
  
  	/*
  	 * To run the engine,
  	 * operation mode and enable register should updated at the same time
  	 */
  
  	ret = lp55xx_read(chip, LP5521_REG_OP_MODE, &mode);
5bc9ad774   Dan Carpenter   drivers/leds/leds...
183
  	if (ret)
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
184
  		return;
5bc9ad774   Dan Carpenter   drivers/leds/leds...
185

9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
186
  	ret = lp55xx_read(chip, LP5521_REG_ENABLE, &exec);
5bc9ad774   Dan Carpenter   drivers/leds/leds...
187
  	if (ret)
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  		return;
  
  	/* change operation mode to RUN only when each engine is loading */
  	if (LP5521_R_IS_LOADING(mode)) {
  		mode = (mode & ~LP5521_MODE_R_M) | LP5521_RUN_R;
  		exec = (exec & ~LP5521_EXEC_R_M) | LP5521_RUN_R;
  	}
  
  	if (LP5521_G_IS_LOADING(mode)) {
  		mode = (mode & ~LP5521_MODE_G_M) | LP5521_RUN_G;
  		exec = (exec & ~LP5521_EXEC_G_M) | LP5521_RUN_G;
  	}
  
  	if (LP5521_B_IS_LOADING(mode)) {
  		mode = (mode & ~LP5521_MODE_B_M) | LP5521_RUN_B;
  		exec = (exec & ~LP5521_EXEC_B_M) | LP5521_RUN_B;
  	}
  
  	lp55xx_write(chip, LP5521_REG_OP_MODE, mode);
  	lp5521_wait_opmode_done();
  
  	lp55xx_update_bits(chip, LP5521_REG_ENABLE, LP5521_EXEC_M, exec);
  	lp5521_wait_enable_done();
  }
  
  static int lp5521_update_program_memory(struct lp55xx_chip *chip,
  					const u8 *data, size_t size)
  {
  	enum lp55xx_engine_index idx = chip->engine_idx;
  	u8 pattern[LP5521_PROGRAM_LENGTH] = {0};
  	u8 addr[] = {
  		[LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM,
  		[LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM,
  		[LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM,
  	};
  	unsigned cmd;
  	char c[3];
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
225
  	int nrchars;
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
226
  	int ret;
1eca0b3ab   Milo Kim   leds: lp5521: rem...
227
228
  	int offset = 0;
  	int i = 0;
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
229

9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  	while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) {
  		/* separate sscanfs because length is working only for %s */
  		ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
  		if (ret != 1)
  			goto err;
  
  		ret = sscanf(c, "%2x", &cmd);
  		if (ret != 1)
  			goto err;
  
  		pattern[i] = (u8)cmd;
  		offset += nrchars;
  		i++;
  	}
  
  	/* Each instruction is 16bit long. Check that length is even */
  	if (i % 2)
  		goto err;
1eca0b3ab   Milo Kim   leds: lp5521: rem...
248
  	for (i = 0; i < LP5521_PROGRAM_LENGTH; i++) {
c0e5e9b54   Milo Kim   leds: lp5521: res...
249
  		ret = lp55xx_write(chip, addr[idx] + i, pattern[i]);
e70988d1a   Milo Kim   leds: lp5521/5523...
250
  		if (ret)
c0e5e9b54   Milo Kim   leds: lp5521: res...
251
  			return -EINVAL;
c0e5e9b54   Milo Kim   leds: lp5521: res...
252
  	}
c0e5e9b54   Milo Kim   leds: lp5521: res...
253
  	return size;
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
  
  err:
  	dev_err(&chip->cl->dev, "wrong pattern format
  ");
  	return -EINVAL;
  }
  
  static void lp5521_firmware_loaded(struct lp55xx_chip *chip)
  {
  	const struct firmware *fw = chip->fw;
  
  	if (fw->size > LP5521_PROGRAM_LENGTH) {
  		dev_err(&chip->cl->dev, "firmware data size overflow: %zu
  ",
  			fw->size);
  		return;
  	}
500fe1413   Samu Onkalo   leds: driver for ...
271

9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
272
273
274
275
276
277
278
279
  	/*
  	 * Program momery sequence
  	 *  1) set engine mode to "LOAD"
  	 *  2) write firmware data into program memory
  	 */
  
  	lp5521_load_engine(chip);
  	lp5521_update_program_memory(chip, fw->data, fw->size);
500fe1413   Samu Onkalo   leds: driver for ...
280
  }
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
281
  static int lp5521_post_init_device(struct lp55xx_chip *chip)
500fe1413   Samu Onkalo   leds: driver for ...
282
  {
500fe1413   Samu Onkalo   leds: driver for ...
283
  	int ret;
944821740   Milo(Woogyom) Kim   leds-lp5521: clea...
284
  	u8 val;
500fe1413   Samu Onkalo   leds: driver for ...
285

944821740   Milo(Woogyom) Kim   leds-lp5521: clea...
286
287
288
289
290
291
  	/*
  	 * Make sure that the chip is reset by reading back the r channel
  	 * current reg. This is dummy read is required on some platforms -
  	 * otherwise further access to the R G B channels in the
  	 * LP5521_REG_ENABLE register will not have any effect - strange!
  	 */
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
292
  	ret = lp55xx_read(chip, LP5521_REG_R_CURRENT, &val);
944821740   Milo(Woogyom) Kim   leds-lp5521: clea...
293
  	if (ret) {
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
294
295
  		dev_err(&chip->cl->dev, "error in resetting chip
  ");
944821740   Milo(Woogyom) Kim   leds-lp5521: clea...
296
297
298
  		return ret;
  	}
  	if (val != LP5521_REG_R_CURR_DEFAULT) {
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
299
  		dev_err(&chip->cl->dev,
944821740   Milo(Woogyom) Kim   leds-lp5521: clea...
300
301
302
303
304
305
306
  			"unexpected data in register (expected 0x%x got 0x%x)
  ",
  			LP5521_REG_R_CURR_DEFAULT, val);
  		ret = -EINVAL;
  		return ret;
  	}
  	usleep_range(10000, 20000);
500fe1413   Samu Onkalo   leds: driver for ...
307

500fe1413   Samu Onkalo   leds: driver for ...
308
  	/* Set all PWMs to direct control mode */
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
309
  	ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
500fe1413   Samu Onkalo   leds: driver for ...
310

81f2a5b4a   Kim, Milo   leds: lp55xx: con...
311
312
313
314
  	/* Update configuration for the clock setting */
  	val = LP5521_DEFAULT_CFG;
  	if (!lp55xx_is_extclk_used(chip))
  		val |= LP5521_CLK_INT;
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
315
  	ret = lp55xx_write(chip, LP5521_REG_CONFIG, val);
944821740   Milo(Woogyom) Kim   leds-lp5521: clea...
316
317
  	if (ret)
  		return ret;
500fe1413   Samu Onkalo   leds: driver for ...
318
319
  
  	/* Initialize all channels PWM to zero -> leds off */
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
320
321
322
  	lp55xx_write(chip, LP5521_REG_R_PWM, 0);
  	lp55xx_write(chip, LP5521_REG_G_PWM, 0);
  	lp55xx_write(chip, LP5521_REG_B_PWM, 0);
500fe1413   Samu Onkalo   leds: driver for ...
323
324
  
  	/* Set engines are set to run state when OP_MODE enables engines */
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
325
  	ret = lp55xx_write(chip, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM);
944821740   Milo(Woogyom) Kim   leds-lp5521: clea...
326
327
  	if (ret)
  		return ret;
500fe1413   Samu Onkalo   leds: driver for ...
328

944821740   Milo(Woogyom) Kim   leds-lp5521: clea...
329
330
331
  	lp5521_wait_enable_done();
  
  	return 0;
500fe1413   Samu Onkalo   leds: driver for ...
332
  }
9ca3bd802   Milo(Woogyom) Kim   leds-lp55xx: code...
333
  static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf)
500fe1413   Samu Onkalo   leds: driver for ...
334
  {
9ca3bd802   Milo(Woogyom) Kim   leds-lp55xx: code...
335
  	struct lp55xx_platform_data *pdata = chip->pdata;
500fe1413   Samu Onkalo   leds: driver for ...
336
337
  	int ret;
  	u8 status;
9ca3bd802   Milo(Woogyom) Kim   leds-lp55xx: code...
338
  	ret = lp55xx_read(chip, LP5521_REG_STATUS, &status);
500fe1413   Samu Onkalo   leds: driver for ...
339
340
  	if (ret < 0)
  		return ret;
9ca3bd802   Milo(Woogyom) Kim   leds-lp55xx: code...
341
342
  	if (pdata->clock_mode != LP55XX_CLOCK_EXT)
  		return 0;
500fe1413   Samu Onkalo   leds: driver for ...
343
  	/* Check that ext clock is really in use if requested */
9ca3bd802   Milo(Woogyom) Kim   leds-lp55xx: code...
344
345
  	if  ((status & LP5521_EXT_CLK_USED) == 0)
  		return -EIO;
500fe1413   Samu Onkalo   leds: driver for ...
346
347
  	return 0;
  }
95b2af637   Andrew Lunn   leds: lp55xx: Rem...
348
  static int lp5521_led_brightness(struct lp55xx_led *led)
500fe1413   Samu Onkalo   leds: driver for ...
349
  {
a6e4679a0   Milo(Woogyom) Kim   leds-lp55xx: use ...
350
  	struct lp55xx_chip *chip = led->chip;
95b2af637   Andrew Lunn   leds: lp55xx: Rem...
351
  	int ret;
500fe1413   Samu Onkalo   leds: driver for ...
352
353
  
  	mutex_lock(&chip->lock);
95b2af637   Andrew Lunn   leds: lp55xx: Rem...
354
  	ret = lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr,
500fe1413   Samu Onkalo   leds: driver for ...
355
356
  		led->brightness);
  	mutex_unlock(&chip->lock);
95b2af637   Andrew Lunn   leds: lp55xx: Rem...
357
358
  
  	return ret;
500fe1413   Samu Onkalo   leds: driver for ...
359
  }
c0e5e9b54   Milo Kim   leds: lp5521: res...
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
  static ssize_t show_engine_mode(struct device *dev,
  				struct device_attribute *attr,
  				char *buf, int nr)
  {
  	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
  	struct lp55xx_chip *chip = led->chip;
  	enum lp55xx_engine_mode mode = chip->engines[nr - 1].mode;
  
  	switch (mode) {
  	case LP55XX_ENGINE_RUN:
  		return sprintf(buf, "run
  ");
  	case LP55XX_ENGINE_LOAD:
  		return sprintf(buf, "load
  ");
  	case LP55XX_ENGINE_DISABLED:
  	default:
  		return sprintf(buf, "disabled
  ");
  	}
  }
  show_mode(1)
  show_mode(2)
  show_mode(3)
  
  static ssize_t store_engine_mode(struct device *dev,
  				 struct device_attribute *attr,
  				 const char *buf, size_t len, int nr)
  {
  	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
  	struct lp55xx_chip *chip = led->chip;
  	struct lp55xx_engine *engine = &chip->engines[nr - 1];
  
  	mutex_lock(&chip->lock);
  
  	chip->engine_idx = nr;
  
  	if (!strncmp(buf, "run", 3)) {
  		lp5521_run_engine(chip, true);
  		engine->mode = LP55XX_ENGINE_RUN;
  	} else if (!strncmp(buf, "load", 4)) {
  		lp5521_stop_engine(chip);
  		lp5521_load_engine(chip);
  		engine->mode = LP55XX_ENGINE_LOAD;
  	} else if (!strncmp(buf, "disabled", 8)) {
  		lp5521_stop_engine(chip);
  		engine->mode = LP55XX_ENGINE_DISABLED;
  	}
  
  	mutex_unlock(&chip->lock);
  
  	return len;
  }
  store_mode(1)
  store_mode(2)
  store_mode(3)
  
  static ssize_t store_engine_load(struct device *dev,
  			     struct device_attribute *attr,
  			     const char *buf, size_t len, int nr)
  {
  	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
  	struct lp55xx_chip *chip = led->chip;
e70988d1a   Milo Kim   leds: lp5521/5523...
423
  	int ret;
c0e5e9b54   Milo Kim   leds: lp5521: res...
424
425
426
427
428
  
  	mutex_lock(&chip->lock);
  
  	chip->engine_idx = nr;
  	lp5521_load_engine(chip);
e70988d1a   Milo Kim   leds: lp5521/5523...
429
  	ret = lp5521_update_program_memory(chip, buf, len);
c0e5e9b54   Milo Kim   leds: lp5521: res...
430
431
  
  	mutex_unlock(&chip->lock);
e70988d1a   Milo Kim   leds: lp5521/5523...
432
  	return ret;
c0e5e9b54   Milo Kim   leds: lp5521: res...
433
434
435
436
  }
  store_load(1)
  store_load(2)
  store_load(3)
500fe1413   Samu Onkalo   leds: driver for ...
437
438
439
440
  static ssize_t lp5521_selftest(struct device *dev,
  			       struct device_attribute *attr,
  			       char *buf)
  {
9ca3bd802   Milo(Woogyom) Kim   leds-lp55xx: code...
441
442
  	struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
  	struct lp55xx_chip *chip = led->chip;
500fe1413   Samu Onkalo   leds: driver for ...
443
444
445
446
447
  	int ret;
  
  	mutex_lock(&chip->lock);
  	ret = lp5521_run_selftest(chip, buf);
  	mutex_unlock(&chip->lock);
24d321284   Kim, Milo   leds: lp55xx: fix...
448
449
450
  
  	return scnprintf(buf, PAGE_SIZE, "%s
  ", ret ? "FAIL" : "OK");
500fe1413   Samu Onkalo   leds: driver for ...
451
  }
500fe1413   Samu Onkalo   leds: driver for ...
452
  /* device attributes */
c0e5e9b54   Milo Kim   leds: lp5521: res...
453
454
455
456
457
458
459
  static LP55XX_DEV_ATTR_RW(engine1_mode, show_engine1_mode, store_engine1_mode);
  static LP55XX_DEV_ATTR_RW(engine2_mode, show_engine2_mode, store_engine2_mode);
  static LP55XX_DEV_ATTR_RW(engine3_mode, show_engine3_mode, store_engine3_mode);
  static LP55XX_DEV_ATTR_WO(engine1_load, store_engine1_load);
  static LP55XX_DEV_ATTR_WO(engine2_load, store_engine2_load);
  static LP55XX_DEV_ATTR_WO(engine3_load, store_engine3_load);
  static LP55XX_DEV_ATTR_RO(selftest, lp5521_selftest);
500fe1413   Samu Onkalo   leds: driver for ...
460
461
  
  static struct attribute *lp5521_attributes[] = {
c0e5e9b54   Milo Kim   leds: lp5521: res...
462
463
464
465
466
467
  	&dev_attr_engine1_mode.attr,
  	&dev_attr_engine2_mode.attr,
  	&dev_attr_engine3_mode.attr,
  	&dev_attr_engine1_load.attr,
  	&dev_attr_engine2_load.attr,
  	&dev_attr_engine3_load.attr,
500fe1413   Samu Onkalo   leds: driver for ...
468
  	&dev_attr_selftest.attr,
500fe1413   Samu Onkalo   leds: driver for ...
469
470
471
472
473
474
  	NULL
  };
  
  static const struct attribute_group lp5521_group = {
  	.attrs = lp5521_attributes,
  };
48068d5de   Milo(Woogyom) Kim   leds-lp55xx: use ...
475
476
477
478
479
480
  /* Chip specific configurations */
  static struct lp55xx_device_config lp5521_cfg = {
  	.reset = {
  		.addr = LP5521_REG_RESET,
  		.val  = LP5521_RESET,
  	},
e3a700d8a   Milo(Woogyom) Kim   leds-lp55xx: use ...
481
482
483
484
  	.enable = {
  		.addr = LP5521_REG_ENABLE,
  		.val  = LP5521_ENABLE_DEFAULT,
  	},
0e2023463   Milo(Woogyom) Kim   leds-lp55xx: use ...
485
  	.max_channel  = LP5521_MAX_LEDS,
ffbdccdbb   Milo(Woogyom) Kim   leds-lp55xx: use ...
486
  	.post_init_device   = lp5521_post_init_device,
95b2af637   Andrew Lunn   leds: lp55xx: Rem...
487
  	.brightness_fn      = lp5521_led_brightness,
a96bfa135   Milo(Woogyom) Kim   leds-lp55xx: prov...
488
  	.set_led_current    = lp5521_set_led_current,
9ce7cb170   Milo(Woogyom) Kim   leds-lp5521: use ...
489
490
  	.firmware_cb        = lp5521_firmware_loaded,
  	.run_engine         = lp5521_run_engine,
e73c0ce6b   Milo(Woogyom) Kim   leds-lp55xx: use ...
491
  	.dev_attr_group     = &lp5521_group,
48068d5de   Milo(Woogyom) Kim   leds-lp55xx: use ...
492
  };
98ea1ea20   Bill Pemberton   leds: remove use ...
493
  static int lp5521_probe(struct i2c_client *client,
500fe1413   Samu Onkalo   leds: driver for ...
494
495
  			const struct i2c_device_id *id)
  {
1904f83d5   Milo(Woogyom) Kim   leds-lp55xx: clea...
496
  	int ret;
6a0c9a479   Milo(Woogyom) Kim   leds-lp55xx: use ...
497
498
  	struct lp55xx_chip *chip;
  	struct lp55xx_led *led;
ed1333520   Milo Kim   leds:lp55xx: use ...
499
  	struct lp55xx_platform_data *pdata = dev_get_platdata(&client->dev);
7542a04b1   Linus Walleij   leds: lp55xx: add...
500
  	struct device_node *np = client->dev.of_node;
ed1333520   Milo Kim   leds:lp55xx: use ...
501
  	if (!pdata) {
7542a04b1   Linus Walleij   leds: lp55xx: add...
502
  		if (np) {
ed1333520   Milo Kim   leds:lp55xx: use ...
503
504
505
  			pdata = lp55xx_of_populate_pdata(&client->dev, np);
  			if (IS_ERR(pdata))
  				return PTR_ERR(pdata);
7542a04b1   Linus Walleij   leds: lp55xx: add...
506
507
508
509
510
  		} else {
  			dev_err(&client->dev, "no platform data
  ");
  			return -EINVAL;
  		}
500fe1413   Samu Onkalo   leds: driver for ...
511
  	}
6a0c9a479   Milo(Woogyom) Kim   leds-lp55xx: use ...
512
513
514
515
516
517
518
519
520
521
522
  	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
  	if (!chip)
  		return -ENOMEM;
  
  	led = devm_kzalloc(&client->dev,
  			sizeof(*led) * pdata->num_channels, GFP_KERNEL);
  	if (!led)
  		return -ENOMEM;
  
  	chip->cl = client;
  	chip->pdata = pdata;
48068d5de   Milo(Woogyom) Kim   leds-lp55xx: use ...
523
  	chip->cfg = &lp5521_cfg;
6a0c9a479   Milo(Woogyom) Kim   leds-lp55xx: use ...
524
525
  
  	mutex_init(&chip->lock);
500fe1413   Samu Onkalo   leds: driver for ...
526

6a0c9a479   Milo(Woogyom) Kim   leds-lp55xx: use ...
527
  	i2c_set_clientdata(client, led);
500fe1413   Samu Onkalo   leds: driver for ...
528

22ebeb488   Milo(Woogyom) Kim   leds-lp55xx: clea...
529
  	ret = lp55xx_init_device(chip);
944f7b1de   Milo(Woogyom) Kim   leds-lp55xx: clea...
530
  	if (ret)
f6c64c6fc   Milo(Woogyom) Kim   leds-lp55xx: do c...
531
  		goto err_init;
500fe1413   Samu Onkalo   leds: driver for ...
532
533
534
  
  	dev_info(&client->dev, "%s programmable led chip found
  ", id->name);
9e9b3db1b   Milo(Woogyom) Kim   leds-lp55xx: use ...
535
  	ret = lp55xx_register_leds(led, chip);
f65248080   Milo(Woogyom) Kim   leds-lp55xx: clea...
536
  	if (ret)
9e9b3db1b   Milo(Woogyom) Kim   leds-lp55xx: use ...
537
  		goto err_register_leds;
500fe1413   Samu Onkalo   leds: driver for ...
538

e73c0ce6b   Milo(Woogyom) Kim   leds-lp55xx: use ...
539
  	ret = lp55xx_register_sysfs(chip);
500fe1413   Samu Onkalo   leds: driver for ...
540
541
542
  	if (ret) {
  		dev_err(&client->dev, "registering sysfs failed
  ");
e73c0ce6b   Milo(Woogyom) Kim   leds-lp55xx: use ...
543
  		goto err_register_sysfs;
500fe1413   Samu Onkalo   leds: driver for ...
544
  	}
e73c0ce6b   Milo(Woogyom) Kim   leds-lp55xx: use ...
545
546
547
548
  
  	return 0;
  
  err_register_sysfs:
c3a68ebfc   Milo(Woogyom) Kim   leds-lp55xx: use ...
549
  	lp55xx_unregister_leds(led, chip);
9e9b3db1b   Milo(Woogyom) Kim   leds-lp55xx: use ...
550
  err_register_leds:
6ce617626   Milo(Woogyom) Kim   leds-lp55xx: use ...
551
  	lp55xx_deinit_device(chip);
f6c64c6fc   Milo(Woogyom) Kim   leds-lp55xx: do c...
552
  err_init:
500fe1413   Samu Onkalo   leds: driver for ...
553
554
  	return ret;
  }
678e8a6be   Bill Pemberton   leds: remove use ...
555
  static int lp5521_remove(struct i2c_client *client)
500fe1413   Samu Onkalo   leds: driver for ...
556
  {
6ce617626   Milo(Woogyom) Kim   leds-lp55xx: use ...
557
558
  	struct lp55xx_led *led = i2c_get_clientdata(client);
  	struct lp55xx_chip *chip = led->chip;
500fe1413   Samu Onkalo   leds: driver for ...
559

28c9266b3   Milo Kim   leds: lp5521/5523...
560
  	lp5521_stop_all_engines(chip);
87cc4bde2   Milo(Woogyom) Kim   leds-lp55xx: clea...
561
  	lp55xx_unregister_sysfs(chip);
c3a68ebfc   Milo(Woogyom) Kim   leds-lp55xx: use ...
562
  	lp55xx_unregister_leds(led, chip);
6ce617626   Milo(Woogyom) Kim   leds-lp55xx: use ...
563
  	lp55xx_deinit_device(chip);
500fe1413   Samu Onkalo   leds: driver for ...
564

500fe1413   Samu Onkalo   leds: driver for ...
565
566
567
568
569
570
571
572
  	return 0;
  }
  
  static const struct i2c_device_id lp5521_id[] = {
  	{ "lp5521", 0 }, /* Three channel chip */
  	{ }
  };
  MODULE_DEVICE_TABLE(i2c, lp5521_id);
b548a34ba   Axel Lin   leds: lp5521: Pro...
573
574
575
576
577
578
579
580
  #ifdef CONFIG_OF
  static const struct of_device_id of_lp5521_leds_match[] = {
  	{ .compatible = "national,lp5521", },
  	{},
  };
  
  MODULE_DEVICE_TABLE(of, of_lp5521_leds_match);
  #endif
500fe1413   Samu Onkalo   leds: driver for ...
581
582
583
  static struct i2c_driver lp5521_driver = {
  	.driver = {
  		.name	= "lp5521",
b548a34ba   Axel Lin   leds: lp5521: Pro...
584
  		.of_match_table = of_match_ptr(of_lp5521_leds_match),
500fe1413   Samu Onkalo   leds: driver for ...
585
586
  	},
  	.probe		= lp5521_probe,
df07cf812   Bill Pemberton   leds: remove use ...
587
  	.remove		= lp5521_remove,
500fe1413   Samu Onkalo   leds: driver for ...
588
589
  	.id_table	= lp5521_id,
  };
09a0d183e   Axel Lin   leds: convert led...
590
  module_i2c_driver(lp5521_driver);
500fe1413   Samu Onkalo   leds: driver for ...
591
592
  
  MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo");
a2387cb9f   Milo(Woogyom) Kim   leds-lp5521/5523:...
593
  MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
500fe1413   Samu Onkalo   leds: driver for ...
594
595
  MODULE_DESCRIPTION("LP5521 LED engine");
  MODULE_LICENSE("GPL v2");