blob: 89371e065c138cb564acd5b089198216ac4ffd47 [file] [log] [blame]
Samu Onkalo500fe142010-11-11 14:05:22 -08001/*
2 * LP5521 LED chip driver.
3 *
4 * Copyright (C) 2010 Nokia Corporation
5 *
6 * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 */
22
23#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/i2c.h>
26#include <linux/mutex.h>
27#include <linux/gpio.h>
28#include <linux/interrupt.h>
29#include <linux/delay.h>
30#include <linux/ctype.h>
31#include <linux/spinlock.h>
32#include <linux/wait.h>
33#include <linux/leds.h>
34#include <linux/leds-lp5521.h>
35#include <linux/workqueue.h>
36#include <linux/slab.h>
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +090037#include <linux/platform_data/leds-lp55xx.h>
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +090038#include <linux/firmware.h>
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +090039
40#include "leds-lp55xx-common.h"
Samu Onkalo500fe142010-11-11 14:05:22 -080041
42#define LP5521_PROGRAM_LENGTH 32 /* in bytes */
43
44#define LP5521_MAX_LEDS 3 /* Maximum number of LEDs */
45#define LP5521_MAX_ENGINES 3 /* Maximum number of engines */
46
47#define LP5521_ENG_MASK_BASE 0x30 /* 00110000 */
48#define LP5521_ENG_STATUS_MASK 0x07 /* 00000111 */
49
50#define LP5521_CMD_LOAD 0x15 /* 00010101 */
51#define LP5521_CMD_RUN 0x2a /* 00101010 */
52#define LP5521_CMD_DIRECT 0x3f /* 00111111 */
53#define LP5521_CMD_DISABLED 0x00 /* 00000000 */
54
55/* Registers */
56#define LP5521_REG_ENABLE 0x00
57#define LP5521_REG_OP_MODE 0x01
58#define LP5521_REG_R_PWM 0x02
59#define LP5521_REG_G_PWM 0x03
60#define LP5521_REG_B_PWM 0x04
61#define LP5521_REG_R_CURRENT 0x05
62#define LP5521_REG_G_CURRENT 0x06
63#define LP5521_REG_B_CURRENT 0x07
64#define LP5521_REG_CONFIG 0x08
65#define LP5521_REG_R_CHANNEL_PC 0x09
66#define LP5521_REG_G_CHANNEL_PC 0x0A
67#define LP5521_REG_B_CHANNEL_PC 0x0B
68#define LP5521_REG_STATUS 0x0C
69#define LP5521_REG_RESET 0x0D
70#define LP5521_REG_GPO 0x0E
71#define LP5521_REG_R_PROG_MEM 0x10
72#define LP5521_REG_G_PROG_MEM 0x30
73#define LP5521_REG_B_PROG_MEM 0x50
74
75#define LP5521_PROG_MEM_BASE LP5521_REG_R_PROG_MEM
76#define LP5521_PROG_MEM_SIZE 0x20
77
78/* Base register to set LED current */
79#define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT
80
81/* Base register to set the brightness */
82#define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM
83
84/* Bits in ENABLE register */
85#define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */
86#define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */
87#define LP5521_EXEC_RUN 0x2A
Kim, Milo32a2f742012-03-23 15:02:09 -070088#define LP5521_ENABLE_DEFAULT \
89 (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM)
90#define LP5521_ENABLE_RUN_PROGRAM \
91 (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN)
Samu Onkalo500fe142010-11-11 14:05:22 -080092
Samu Onkalo500fe142010-11-11 14:05:22 -080093/* Status */
94#define LP5521_EXT_CLK_USED 0x08
95
Srinidhi KASAGARb3c49c02011-10-31 17:12:24 -070096/* default R channel current register value */
97#define LP5521_REG_R_CURR_DEFAULT 0xAF
98
Kim, Milo011af7b2012-03-23 15:02:09 -070099/* Pattern Mode */
100#define PATTERN_OFF 0
101
Milo(Woogyom) Kim48068d52013-02-05 18:08:49 +0900102/* Reset register value */
103#define LP5521_RESET 0xFF
104
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900105/* Program Memory Operations */
106#define LP5521_MODE_R_M 0x30 /* Operation Mode Register */
107#define LP5521_MODE_G_M 0x0C
108#define LP5521_MODE_B_M 0x03
109#define LP5521_LOAD_R 0x10
110#define LP5521_LOAD_G 0x04
111#define LP5521_LOAD_B 0x01
112
113#define LP5521_R_IS_LOADING(mode) \
114 ((mode & LP5521_MODE_R_M) == LP5521_LOAD_R)
115#define LP5521_G_IS_LOADING(mode) \
116 ((mode & LP5521_MODE_G_M) == LP5521_LOAD_G)
117#define LP5521_B_IS_LOADING(mode) \
118 ((mode & LP5521_MODE_B_M) == LP5521_LOAD_B)
119
120#define LP5521_EXEC_R_M 0x30 /* Enable Register */
121#define LP5521_EXEC_G_M 0x0C
122#define LP5521_EXEC_B_M 0x03
123#define LP5521_EXEC_M 0x3F
124#define LP5521_RUN_R 0x20
125#define LP5521_RUN_G 0x08
126#define LP5521_RUN_B 0x02
Samu Onkalo500fe142010-11-11 14:05:22 -0800127
128struct lp5521_led {
129 int id;
130 u8 chan_nr;
131 u8 led_current;
132 u8 max_current;
133 struct led_classdev cdev;
134 struct work_struct brightness_work;
135 u8 brightness;
136};
137
138struct lp5521_chip {
139 struct lp5521_platform_data *pdata;
140 struct mutex lock; /* Serialize control */
141 struct i2c_client *client;
Samu Onkalo500fe142010-11-11 14:05:22 -0800142 struct lp5521_led leds[LP5521_MAX_LEDS];
143 u8 num_channels;
144 u8 num_leds;
145};
146
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900147static inline void lp5521_wait_opmode_done(void)
148{
149 /* operation mode change needs to be longer than 153 us */
150 usleep_range(200, 300);
151}
152
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900153static inline void lp5521_wait_enable_done(void)
154{
155 /* it takes more 488 us to update ENABLE register */
156 usleep_range(500, 600);
157}
158
Milo(Woogyom) Kima96bfa12013-02-05 19:09:32 +0900159static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current)
160{
161 led->led_current = led_current;
162 lp55xx_write(led->chip, LP5521_REG_LED_CURRENT_BASE + led->chan_nr,
163 led_current);
164}
165
Samu Onkalo500fe142010-11-11 14:05:22 -0800166static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value)
167{
168 return i2c_smbus_write_byte_data(client, reg, value);
169}
170
171static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf)
172{
173 s32 ret;
174
175 ret = i2c_smbus_read_byte_data(client, reg);
176 if (ret < 0)
Sachin Kamat313e8b52012-11-20 01:59:00 -0800177 return ret;
Samu Onkalo500fe142010-11-11 14:05:22 -0800178
179 *buf = ret;
180 return 0;
181}
182
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900183static void lp5521_load_engine(struct lp55xx_chip *chip)
Samu Onkalo500fe142010-11-11 14:05:22 -0800184{
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900185 enum lp55xx_engine_index idx = chip->engine_idx;
186 u8 mask[] = {
187 [LP55XX_ENGINE_1] = LP5521_MODE_R_M,
188 [LP55XX_ENGINE_2] = LP5521_MODE_G_M,
189 [LP55XX_ENGINE_3] = LP5521_MODE_B_M,
190 };
Samu Onkalo500fe142010-11-11 14:05:22 -0800191
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900192 u8 val[] = {
193 [LP55XX_ENGINE_1] = LP5521_LOAD_R,
194 [LP55XX_ENGINE_2] = LP5521_LOAD_G,
195 [LP55XX_ENGINE_3] = LP5521_LOAD_B,
196 };
Samu Onkalo500fe142010-11-11 14:05:22 -0800197
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900198 lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], val[idx]);
Samu Onkalo500fe142010-11-11 14:05:22 -0800199
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900200 lp5521_wait_opmode_done();
Samu Onkalo500fe142010-11-11 14:05:22 -0800201}
202
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900203static void lp5521_stop_engine(struct lp55xx_chip *chip)
Samu Onkalo500fe142010-11-11 14:05:22 -0800204{
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900205 lp55xx_write(chip, LP5521_REG_OP_MODE, 0);
206 lp5521_wait_opmode_done();
207}
208
209static void lp5521_run_engine(struct lp55xx_chip *chip, bool start)
210{
Samu Onkalo500fe142010-11-11 14:05:22 -0800211 int ret;
Samu Onkalo500fe142010-11-11 14:05:22 -0800212 u8 mode;
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900213 u8 exec;
Samu Onkalo500fe142010-11-11 14:05:22 -0800214
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900215 /* stop engine */
216 if (!start) {
217 lp5521_stop_engine(chip);
218 lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
219 lp5521_wait_opmode_done();
220 return;
221 }
222
223 /*
224 * To run the engine,
225 * operation mode and enable register should updated at the same time
226 */
227
228 ret = lp55xx_read(chip, LP5521_REG_OP_MODE, &mode);
Dan Carpenter5bc9ad72012-05-29 15:07:26 -0700229 if (ret)
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900230 return;
Dan Carpenter5bc9ad72012-05-29 15:07:26 -0700231
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900232 ret = lp55xx_read(chip, LP5521_REG_ENABLE, &exec);
Dan Carpenter5bc9ad72012-05-29 15:07:26 -0700233 if (ret)
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900234 return;
Samu Onkalo500fe142010-11-11 14:05:22 -0800235
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900236 /* change operation mode to RUN only when each engine is loading */
237 if (LP5521_R_IS_LOADING(mode)) {
238 mode = (mode & ~LP5521_MODE_R_M) | LP5521_RUN_R;
239 exec = (exec & ~LP5521_EXEC_R_M) | LP5521_RUN_R;
240 }
Samu Onkalo500fe142010-11-11 14:05:22 -0800241
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900242 if (LP5521_G_IS_LOADING(mode)) {
243 mode = (mode & ~LP5521_MODE_G_M) | LP5521_RUN_G;
244 exec = (exec & ~LP5521_EXEC_G_M) | LP5521_RUN_G;
245 }
Samu Onkalo500fe142010-11-11 14:05:22 -0800246
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900247 if (LP5521_B_IS_LOADING(mode)) {
248 mode = (mode & ~LP5521_MODE_B_M) | LP5521_RUN_B;
249 exec = (exec & ~LP5521_EXEC_B_M) | LP5521_RUN_B;
250 }
251
252 lp55xx_write(chip, LP5521_REG_OP_MODE, mode);
253 lp5521_wait_opmode_done();
254
255 lp55xx_update_bits(chip, LP5521_REG_ENABLE, LP5521_EXEC_M, exec);
256 lp5521_wait_enable_done();
257}
258
259static int lp5521_update_program_memory(struct lp55xx_chip *chip,
260 const u8 *data, size_t size)
261{
262 enum lp55xx_engine_index idx = chip->engine_idx;
263 u8 pattern[LP5521_PROGRAM_LENGTH] = {0};
264 u8 addr[] = {
265 [LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM,
266 [LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM,
267 [LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM,
268 };
269 unsigned cmd;
270 char c[3];
271 int program_size;
272 int nrchars;
273 int offset = 0;
274 int ret;
275 int i;
276
277 /* clear program memory before updating */
278 for (i = 0; i < LP5521_PROGRAM_LENGTH; i++)
279 lp55xx_write(chip, addr[idx] + i, 0);
280
281 i = 0;
282 while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) {
283 /* separate sscanfs because length is working only for %s */
284 ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
285 if (ret != 1)
286 goto err;
287
288 ret = sscanf(c, "%2x", &cmd);
289 if (ret != 1)
290 goto err;
291
292 pattern[i] = (u8)cmd;
293 offset += nrchars;
294 i++;
295 }
296
297 /* Each instruction is 16bit long. Check that length is even */
298 if (i % 2)
299 goto err;
300
301 program_size = i;
302 for (i = 0; i < program_size; i++)
303 lp55xx_write(chip, addr[idx] + i, pattern[i]);
304
305 return 0;
306
307err:
308 dev_err(&chip->cl->dev, "wrong pattern format\n");
309 return -EINVAL;
310}
311
312static void lp5521_firmware_loaded(struct lp55xx_chip *chip)
313{
314 const struct firmware *fw = chip->fw;
315
316 if (fw->size > LP5521_PROGRAM_LENGTH) {
317 dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
318 fw->size);
319 return;
320 }
321
322 /*
323 * Program momery sequence
324 * 1) set engine mode to "LOAD"
325 * 2) write firmware data into program memory
326 */
327
328 lp5521_load_engine(chip);
329 lp5521_update_program_memory(chip, fw->data, fw->size);
Samu Onkalo500fe142010-11-11 14:05:22 -0800330}
331
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900332static int lp5521_post_init_device(struct lp55xx_chip *chip)
Samu Onkalo500fe142010-11-11 14:05:22 -0800333{
Samu Onkalo500fe142010-11-11 14:05:22 -0800334 int ret;
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900335 u8 val;
Samu Onkalo500fe142010-11-11 14:05:22 -0800336
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900337 /*
338 * Make sure that the chip is reset by reading back the r channel
339 * current reg. This is dummy read is required on some platforms -
340 * otherwise further access to the R G B channels in the
341 * LP5521_REG_ENABLE register will not have any effect - strange!
342 */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900343 ret = lp55xx_read(chip, LP5521_REG_R_CURRENT, &val);
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900344 if (ret) {
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900345 dev_err(&chip->cl->dev, "error in resetting chip\n");
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900346 return ret;
347 }
348 if (val != LP5521_REG_R_CURR_DEFAULT) {
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900349 dev_err(&chip->cl->dev,
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900350 "unexpected data in register (expected 0x%x got 0x%x)\n",
351 LP5521_REG_R_CURR_DEFAULT, val);
352 ret = -EINVAL;
353 return ret;
354 }
355 usleep_range(10000, 20000);
Samu Onkalo500fe142010-11-11 14:05:22 -0800356
Samu Onkalo500fe142010-11-11 14:05:22 -0800357 /* Set all PWMs to direct control mode */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900358 ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
Samu Onkalo500fe142010-11-11 14:05:22 -0800359
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900360 val = chip->pdata->update_config ?
Kim, Milo3b49aac2012-03-23 15:02:08 -0700361 : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT);
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900362 ret = lp55xx_write(chip, LP5521_REG_CONFIG, val);
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900363 if (ret)
364 return ret;
Samu Onkalo500fe142010-11-11 14:05:22 -0800365
366 /* Initialize all channels PWM to zero -> leds off */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900367 lp55xx_write(chip, LP5521_REG_R_PWM, 0);
368 lp55xx_write(chip, LP5521_REG_G_PWM, 0);
369 lp55xx_write(chip, LP5521_REG_B_PWM, 0);
Samu Onkalo500fe142010-11-11 14:05:22 -0800370
371 /* Set engines are set to run state when OP_MODE enables engines */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900372 ret = lp55xx_write(chip, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM);
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900373 if (ret)
374 return ret;
Samu Onkalo500fe142010-11-11 14:05:22 -0800375
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900376 lp5521_wait_enable_done();
377
378 return 0;
Samu Onkalo500fe142010-11-11 14:05:22 -0800379}
380
381static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf)
382{
383 int ret;
384 u8 status;
385
386 ret = lp5521_read(chip->client, LP5521_REG_STATUS, &status);
387 if (ret < 0)
388 return ret;
389
390 /* Check that ext clock is really in use if requested */
391 if (chip->pdata && chip->pdata->clock_mode == LP5521_CLOCK_EXT)
392 if ((status & LP5521_EXT_CLK_USED) == 0)
393 return -EIO;
394 return 0;
395}
396
Samu Onkalo500fe142010-11-11 14:05:22 -0800397static void lp5521_led_brightness_work(struct work_struct *work)
398{
Milo(Woogyom) Kima6e46792013-02-05 19:08:40 +0900399 struct lp55xx_led *led = container_of(work, struct lp55xx_led,
Samu Onkalo500fe142010-11-11 14:05:22 -0800400 brightness_work);
Milo(Woogyom) Kima6e46792013-02-05 19:08:40 +0900401 struct lp55xx_chip *chip = led->chip;
Samu Onkalo500fe142010-11-11 14:05:22 -0800402
403 mutex_lock(&chip->lock);
Milo(Woogyom) Kima6e46792013-02-05 19:08:40 +0900404 lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr,
Samu Onkalo500fe142010-11-11 14:05:22 -0800405 led->brightness);
406 mutex_unlock(&chip->lock);
407}
408
Samu Onkalo500fe142010-11-11 14:05:22 -0800409static ssize_t lp5521_selftest(struct device *dev,
410 struct device_attribute *attr,
411 char *buf)
412{
413 struct i2c_client *client = to_i2c_client(dev);
414 struct lp5521_chip *chip = i2c_get_clientdata(client);
415 int ret;
416
417 mutex_lock(&chip->lock);
418 ret = lp5521_run_selftest(chip, buf);
419 mutex_unlock(&chip->lock);
420 return sprintf(buf, "%s\n", ret ? "FAIL" : "OK");
421}
422
Kim, Milo011af7b2012-03-23 15:02:09 -0700423static void lp5521_clear_program_memory(struct i2c_client *cl)
424{
425 int i;
426 u8 rgb_mem[] = {
427 LP5521_REG_R_PROG_MEM,
428 LP5521_REG_G_PROG_MEM,
429 LP5521_REG_B_PROG_MEM,
430 };
431
432 for (i = 0; i < ARRAY_SIZE(rgb_mem); i++) {
433 lp5521_write(cl, rgb_mem[i], 0);
434 lp5521_write(cl, rgb_mem[i] + 1, 0);
435 }
436}
437
438static void lp5521_write_program_memory(struct i2c_client *cl,
439 u8 base, u8 *rgb, int size)
440{
441 int i;
442
443 if (!rgb || size <= 0)
444 return;
445
446 for (i = 0; i < size; i++)
447 lp5521_write(cl, base + i, *(rgb + i));
448
449 lp5521_write(cl, base + i, 0);
450 lp5521_write(cl, base + i + 1, 0);
451}
452
453static inline struct lp5521_led_pattern *lp5521_get_pattern
454 (struct lp5521_chip *chip, u8 offset)
455{
456 struct lp5521_led_pattern *ptn;
457 ptn = chip->pdata->patterns + (offset - 1);
458 return ptn;
459}
460
461static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip)
462{
463 struct lp5521_led_pattern *ptn;
464 struct i2c_client *cl = chip->client;
465 int num_patterns = chip->pdata->num_patterns;
466
467 if (mode > num_patterns || !(chip->pdata->patterns))
468 return;
469
470 if (mode == PATTERN_OFF) {
Kim, Milo32a2f742012-03-23 15:02:09 -0700471 lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT);
Kim, Milo011af7b2012-03-23 15:02:09 -0700472 usleep_range(1000, 2000);
473 lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
474 } else {
475 ptn = lp5521_get_pattern(chip, mode);
476 if (!ptn)
477 return;
478
479 lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD);
480 usleep_range(1000, 2000);
481
482 lp5521_clear_program_memory(cl);
483
484 lp5521_write_program_memory(cl, LP5521_REG_R_PROG_MEM,
485 ptn->r, ptn->size_r);
486 lp5521_write_program_memory(cl, LP5521_REG_G_PROG_MEM,
487 ptn->g, ptn->size_g);
488 lp5521_write_program_memory(cl, LP5521_REG_B_PROG_MEM,
489 ptn->b, ptn->size_b);
490
491 lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN);
492 usleep_range(1000, 2000);
Kim, Milo32a2f742012-03-23 15:02:09 -0700493 lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM);
Kim, Milo011af7b2012-03-23 15:02:09 -0700494 }
495}
496
Samu Onkalo500fe142010-11-11 14:05:22 -0800497/* device attributes */
Samu Onkalo500fe142010-11-11 14:05:22 -0800498static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL);
499
500static struct attribute *lp5521_attributes[] = {
Samu Onkalo500fe142010-11-11 14:05:22 -0800501 &dev_attr_selftest.attr,
Samu Onkalo500fe142010-11-11 14:05:22 -0800502 NULL
503};
504
505static const struct attribute_group lp5521_group = {
506 .attrs = lp5521_attributes,
507};
508
Samu Onkalo500fe142010-11-11 14:05:22 -0800509static int lp5521_register_sysfs(struct i2c_client *client)
510{
511 struct device *dev = &client->dev;
512 return sysfs_create_group(&dev->kobj, &lp5521_group);
513}
514
515static void lp5521_unregister_sysfs(struct i2c_client *client)
516{
Samu Onkalo500fe142010-11-11 14:05:22 -0800517 struct device *dev = &client->dev;
Samu Onkalo500fe142010-11-11 14:05:22 -0800518
519 sysfs_remove_group(&dev->kobj, &lp5521_group);
Milo(Woogyom) Kim1904f832013-02-05 17:56:23 +0900520}
521
Milo(Woogyom) Kim48068d52013-02-05 18:08:49 +0900522/* Chip specific configurations */
523static struct lp55xx_device_config lp5521_cfg = {
524 .reset = {
525 .addr = LP5521_REG_RESET,
526 .val = LP5521_RESET,
527 },
Milo(Woogyom) Kime3a700d2013-02-05 18:09:56 +0900528 .enable = {
529 .addr = LP5521_REG_ENABLE,
530 .val = LP5521_ENABLE_DEFAULT,
531 },
Milo(Woogyom) Kim0e202342013-02-05 19:07:34 +0900532 .max_channel = LP5521_MAX_LEDS,
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900533 .post_init_device = lp5521_post_init_device,
Milo(Woogyom) Kima6e46792013-02-05 19:08:40 +0900534 .brightness_work_fn = lp5521_led_brightness_work,
Milo(Woogyom) Kima96bfa12013-02-05 19:09:32 +0900535 .set_led_current = lp5521_set_led_current,
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900536 .firmware_cb = lp5521_firmware_loaded,
537 .run_engine = lp5521_run_engine,
Milo(Woogyom) Kim48068d52013-02-05 18:08:49 +0900538};
539
Bill Pemberton98ea1ea2012-11-19 13:23:02 -0500540static int lp5521_probe(struct i2c_client *client,
Samu Onkalo500fe142010-11-11 14:05:22 -0800541 const struct i2c_device_id *id)
542{
Milo(Woogyom) Kim1904f832013-02-05 17:56:23 +0900543 int ret;
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +0900544 struct lp55xx_chip *chip;
545 struct lp55xx_led *led;
546 struct lp55xx_platform_data *pdata = client->dev.platform_data;
Samu Onkalo500fe142010-11-11 14:05:22 -0800547
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +0900548 if (!pdata) {
Samu Onkalo500fe142010-11-11 14:05:22 -0800549 dev_err(&client->dev, "no platform data\n");
Bryan Wue430dc02012-07-04 11:16:09 +0800550 return -EINVAL;
Samu Onkalo500fe142010-11-11 14:05:22 -0800551 }
552
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +0900553 chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
554 if (!chip)
555 return -ENOMEM;
Samu Onkalo500fe142010-11-11 14:05:22 -0800556
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +0900557 led = devm_kzalloc(&client->dev,
558 sizeof(*led) * pdata->num_channels, GFP_KERNEL);
559 if (!led)
560 return -ENOMEM;
561
562 chip->cl = client;
563 chip->pdata = pdata;
Milo(Woogyom) Kim48068d52013-02-05 18:08:49 +0900564 chip->cfg = &lp5521_cfg;
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +0900565
566 mutex_init(&chip->lock);
567
568 i2c_set_clientdata(client, led);
Samu Onkalo500fe142010-11-11 14:05:22 -0800569
Milo(Woogyom) Kim22ebeb42013-02-05 18:58:35 +0900570 ret = lp55xx_init_device(chip);
Milo(Woogyom) Kim944f7b12013-02-05 17:49:46 +0900571 if (ret)
Milo(Woogyom) Kimf6c64c62013-02-05 17:58:01 +0900572 goto err_init;
Samu Onkalo500fe142010-11-11 14:05:22 -0800573
574 dev_info(&client->dev, "%s programmable led chip found\n", id->name);
575
Milo(Woogyom) Kim9e9b3db2013-02-05 19:06:27 +0900576 ret = lp55xx_register_leds(led, chip);
Milo(Woogyom) Kimf6524802013-02-05 17:53:40 +0900577 if (ret)
Milo(Woogyom) Kim9e9b3db2013-02-05 19:06:27 +0900578 goto err_register_leds;
Samu Onkalo500fe142010-11-11 14:05:22 -0800579
580 ret = lp5521_register_sysfs(client);
581 if (ret) {
582 dev_err(&client->dev, "registering sysfs failed\n");
Bryan Wue430dc02012-07-04 11:16:09 +0800583 goto fail2;
Samu Onkalo500fe142010-11-11 14:05:22 -0800584 }
585 return ret;
Bryan Wue430dc02012-07-04 11:16:09 +0800586fail2:
Milo(Woogyom) Kimc3a68eb2013-02-05 19:11:18 +0900587 lp55xx_unregister_leds(led, chip);
Milo(Woogyom) Kim9e9b3db2013-02-05 19:06:27 +0900588err_register_leds:
Milo(Woogyom) Kim6ce61762013-02-05 19:03:02 +0900589 lp55xx_deinit_device(chip);
Milo(Woogyom) Kimf6c64c62013-02-05 17:58:01 +0900590err_init:
Samu Onkalo500fe142010-11-11 14:05:22 -0800591 return ret;
592}
593
Bill Pemberton678e8a62012-11-19 13:26:00 -0500594static int lp5521_remove(struct i2c_client *client)
Samu Onkalo500fe142010-11-11 14:05:22 -0800595{
Milo(Woogyom) Kim945c7002013-02-05 18:02:26 +0900596 struct lp5521_chip *old_chip = i2c_get_clientdata(client);
Milo(Woogyom) Kim6ce61762013-02-05 19:03:02 +0900597 struct lp55xx_led *led = i2c_get_clientdata(client);
598 struct lp55xx_chip *chip = led->chip;
Samu Onkalo500fe142010-11-11 14:05:22 -0800599
Milo(Woogyom) Kim945c7002013-02-05 18:02:26 +0900600 lp5521_run_led_pattern(PATTERN_OFF, old_chip);
Samu Onkalo500fe142010-11-11 14:05:22 -0800601 lp5521_unregister_sysfs(client);
602
Milo(Woogyom) Kimc3a68eb2013-02-05 19:11:18 +0900603 lp55xx_unregister_leds(led, chip);
Milo(Woogyom) Kim6ce61762013-02-05 19:03:02 +0900604 lp55xx_deinit_device(chip);
Samu Onkalo500fe142010-11-11 14:05:22 -0800605
Samu Onkalo500fe142010-11-11 14:05:22 -0800606 return 0;
607}
608
609static const struct i2c_device_id lp5521_id[] = {
610 { "lp5521", 0 }, /* Three channel chip */
611 { }
612};
613MODULE_DEVICE_TABLE(i2c, lp5521_id);
614
615static struct i2c_driver lp5521_driver = {
616 .driver = {
617 .name = "lp5521",
618 },
619 .probe = lp5521_probe,
Bill Pembertondf07cf82012-11-19 13:20:20 -0500620 .remove = lp5521_remove,
Samu Onkalo500fe142010-11-11 14:05:22 -0800621 .id_table = lp5521_id,
622};
623
Axel Lin09a0d182012-01-10 15:09:27 -0800624module_i2c_driver(lp5521_driver);
Samu Onkalo500fe142010-11-11 14:05:22 -0800625
626MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo");
627MODULE_DESCRIPTION("LP5521 LED engine");
628MODULE_LICENSE("GPL v2");