blob: 1f6d9c7eb4a28638f5367141103c6ba9b0acf868 [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
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900171static void lp5521_load_engine(struct lp55xx_chip *chip)
Samu Onkalo500fe142010-11-11 14:05:22 -0800172{
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900173 enum lp55xx_engine_index idx = chip->engine_idx;
174 u8 mask[] = {
175 [LP55XX_ENGINE_1] = LP5521_MODE_R_M,
176 [LP55XX_ENGINE_2] = LP5521_MODE_G_M,
177 [LP55XX_ENGINE_3] = LP5521_MODE_B_M,
178 };
Samu Onkalo500fe142010-11-11 14:05:22 -0800179
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900180 u8 val[] = {
181 [LP55XX_ENGINE_1] = LP5521_LOAD_R,
182 [LP55XX_ENGINE_2] = LP5521_LOAD_G,
183 [LP55XX_ENGINE_3] = LP5521_LOAD_B,
184 };
Samu Onkalo500fe142010-11-11 14:05:22 -0800185
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900186 lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], val[idx]);
Samu Onkalo500fe142010-11-11 14:05:22 -0800187
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900188 lp5521_wait_opmode_done();
Samu Onkalo500fe142010-11-11 14:05:22 -0800189}
190
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900191static void lp5521_stop_engine(struct lp55xx_chip *chip)
Samu Onkalo500fe142010-11-11 14:05:22 -0800192{
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900193 lp55xx_write(chip, LP5521_REG_OP_MODE, 0);
194 lp5521_wait_opmode_done();
195}
196
197static void lp5521_run_engine(struct lp55xx_chip *chip, bool start)
198{
Samu Onkalo500fe142010-11-11 14:05:22 -0800199 int ret;
Samu Onkalo500fe142010-11-11 14:05:22 -0800200 u8 mode;
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900201 u8 exec;
Samu Onkalo500fe142010-11-11 14:05:22 -0800202
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900203 /* stop engine */
204 if (!start) {
205 lp5521_stop_engine(chip);
206 lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
207 lp5521_wait_opmode_done();
208 return;
209 }
210
211 /*
212 * To run the engine,
213 * operation mode and enable register should updated at the same time
214 */
215
216 ret = lp55xx_read(chip, LP5521_REG_OP_MODE, &mode);
Dan Carpenter5bc9ad72012-05-29 15:07:26 -0700217 if (ret)
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900218 return;
Dan Carpenter5bc9ad72012-05-29 15:07:26 -0700219
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900220 ret = lp55xx_read(chip, LP5521_REG_ENABLE, &exec);
Dan Carpenter5bc9ad72012-05-29 15:07:26 -0700221 if (ret)
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900222 return;
Samu Onkalo500fe142010-11-11 14:05:22 -0800223
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900224 /* change operation mode to RUN only when each engine is loading */
225 if (LP5521_R_IS_LOADING(mode)) {
226 mode = (mode & ~LP5521_MODE_R_M) | LP5521_RUN_R;
227 exec = (exec & ~LP5521_EXEC_R_M) | LP5521_RUN_R;
228 }
Samu Onkalo500fe142010-11-11 14:05:22 -0800229
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900230 if (LP5521_G_IS_LOADING(mode)) {
231 mode = (mode & ~LP5521_MODE_G_M) | LP5521_RUN_G;
232 exec = (exec & ~LP5521_EXEC_G_M) | LP5521_RUN_G;
233 }
Samu Onkalo500fe142010-11-11 14:05:22 -0800234
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900235 if (LP5521_B_IS_LOADING(mode)) {
236 mode = (mode & ~LP5521_MODE_B_M) | LP5521_RUN_B;
237 exec = (exec & ~LP5521_EXEC_B_M) | LP5521_RUN_B;
238 }
239
240 lp55xx_write(chip, LP5521_REG_OP_MODE, mode);
241 lp5521_wait_opmode_done();
242
243 lp55xx_update_bits(chip, LP5521_REG_ENABLE, LP5521_EXEC_M, exec);
244 lp5521_wait_enable_done();
245}
246
247static int lp5521_update_program_memory(struct lp55xx_chip *chip,
248 const u8 *data, size_t size)
249{
250 enum lp55xx_engine_index idx = chip->engine_idx;
251 u8 pattern[LP5521_PROGRAM_LENGTH] = {0};
252 u8 addr[] = {
253 [LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM,
254 [LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM,
255 [LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM,
256 };
257 unsigned cmd;
258 char c[3];
259 int program_size;
260 int nrchars;
261 int offset = 0;
262 int ret;
263 int i;
264
265 /* clear program memory before updating */
266 for (i = 0; i < LP5521_PROGRAM_LENGTH; i++)
267 lp55xx_write(chip, addr[idx] + i, 0);
268
269 i = 0;
270 while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) {
271 /* separate sscanfs because length is working only for %s */
272 ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
273 if (ret != 1)
274 goto err;
275
276 ret = sscanf(c, "%2x", &cmd);
277 if (ret != 1)
278 goto err;
279
280 pattern[i] = (u8)cmd;
281 offset += nrchars;
282 i++;
283 }
284
285 /* Each instruction is 16bit long. Check that length is even */
286 if (i % 2)
287 goto err;
288
289 program_size = i;
290 for (i = 0; i < program_size; i++)
291 lp55xx_write(chip, addr[idx] + i, pattern[i]);
292
293 return 0;
294
295err:
296 dev_err(&chip->cl->dev, "wrong pattern format\n");
297 return -EINVAL;
298}
299
300static void lp5521_firmware_loaded(struct lp55xx_chip *chip)
301{
302 const struct firmware *fw = chip->fw;
303
304 if (fw->size > LP5521_PROGRAM_LENGTH) {
305 dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
306 fw->size);
307 return;
308 }
309
310 /*
311 * Program momery sequence
312 * 1) set engine mode to "LOAD"
313 * 2) write firmware data into program memory
314 */
315
316 lp5521_load_engine(chip);
317 lp5521_update_program_memory(chip, fw->data, fw->size);
Samu Onkalo500fe142010-11-11 14:05:22 -0800318}
319
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900320static int lp5521_post_init_device(struct lp55xx_chip *chip)
Samu Onkalo500fe142010-11-11 14:05:22 -0800321{
Samu Onkalo500fe142010-11-11 14:05:22 -0800322 int ret;
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900323 u8 val;
Samu Onkalo500fe142010-11-11 14:05:22 -0800324
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900325 /*
326 * Make sure that the chip is reset by reading back the r channel
327 * current reg. This is dummy read is required on some platforms -
328 * otherwise further access to the R G B channels in the
329 * LP5521_REG_ENABLE register will not have any effect - strange!
330 */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900331 ret = lp55xx_read(chip, LP5521_REG_R_CURRENT, &val);
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900332 if (ret) {
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900333 dev_err(&chip->cl->dev, "error in resetting chip\n");
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900334 return ret;
335 }
336 if (val != LP5521_REG_R_CURR_DEFAULT) {
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900337 dev_err(&chip->cl->dev,
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900338 "unexpected data in register (expected 0x%x got 0x%x)\n",
339 LP5521_REG_R_CURR_DEFAULT, val);
340 ret = -EINVAL;
341 return ret;
342 }
343 usleep_range(10000, 20000);
Samu Onkalo500fe142010-11-11 14:05:22 -0800344
Samu Onkalo500fe142010-11-11 14:05:22 -0800345 /* Set all PWMs to direct control mode */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900346 ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
Samu Onkalo500fe142010-11-11 14:05:22 -0800347
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900348 val = chip->pdata->update_config ?
Kim, Milo3b49aac2012-03-23 15:02:08 -0700349 : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT);
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900350 ret = lp55xx_write(chip, LP5521_REG_CONFIG, val);
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900351 if (ret)
352 return ret;
Samu Onkalo500fe142010-11-11 14:05:22 -0800353
354 /* Initialize all channels PWM to zero -> leds off */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900355 lp55xx_write(chip, LP5521_REG_R_PWM, 0);
356 lp55xx_write(chip, LP5521_REG_G_PWM, 0);
357 lp55xx_write(chip, LP5521_REG_B_PWM, 0);
Samu Onkalo500fe142010-11-11 14:05:22 -0800358
359 /* Set engines are set to run state when OP_MODE enables engines */
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900360 ret = lp55xx_write(chip, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM);
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900361 if (ret)
362 return ret;
Samu Onkalo500fe142010-11-11 14:05:22 -0800363
Milo(Woogyom) Kim94482172013-02-05 18:03:55 +0900364 lp5521_wait_enable_done();
365
366 return 0;
Samu Onkalo500fe142010-11-11 14:05:22 -0800367}
368
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900369static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf)
Samu Onkalo500fe142010-11-11 14:05:22 -0800370{
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900371 struct lp55xx_platform_data *pdata = chip->pdata;
Samu Onkalo500fe142010-11-11 14:05:22 -0800372 int ret;
373 u8 status;
374
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900375 ret = lp55xx_read(chip, LP5521_REG_STATUS, &status);
Samu Onkalo500fe142010-11-11 14:05:22 -0800376 if (ret < 0)
377 return ret;
378
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900379 if (pdata->clock_mode != LP55XX_CLOCK_EXT)
380 return 0;
381
Samu Onkalo500fe142010-11-11 14:05:22 -0800382 /* Check that ext clock is really in use if requested */
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900383 if ((status & LP5521_EXT_CLK_USED) == 0)
384 return -EIO;
385
Samu Onkalo500fe142010-11-11 14:05:22 -0800386 return 0;
387}
388
Samu Onkalo500fe142010-11-11 14:05:22 -0800389static void lp5521_led_brightness_work(struct work_struct *work)
390{
Milo(Woogyom) Kima6e46792013-02-05 19:08:40 +0900391 struct lp55xx_led *led = container_of(work, struct lp55xx_led,
Samu Onkalo500fe142010-11-11 14:05:22 -0800392 brightness_work);
Milo(Woogyom) Kima6e46792013-02-05 19:08:40 +0900393 struct lp55xx_chip *chip = led->chip;
Samu Onkalo500fe142010-11-11 14:05:22 -0800394
395 mutex_lock(&chip->lock);
Milo(Woogyom) Kima6e46792013-02-05 19:08:40 +0900396 lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr,
Samu Onkalo500fe142010-11-11 14:05:22 -0800397 led->brightness);
398 mutex_unlock(&chip->lock);
399}
400
Samu Onkalo500fe142010-11-11 14:05:22 -0800401static ssize_t lp5521_selftest(struct device *dev,
402 struct device_attribute *attr,
403 char *buf)
404{
Milo(Woogyom) Kim9ca3bd82013-02-05 19:21:43 +0900405 struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
406 struct lp55xx_chip *chip = led->chip;
Samu Onkalo500fe142010-11-11 14:05:22 -0800407 int ret;
408
409 mutex_lock(&chip->lock);
410 ret = lp5521_run_selftest(chip, buf);
411 mutex_unlock(&chip->lock);
412 return sprintf(buf, "%s\n", ret ? "FAIL" : "OK");
413}
414
Kim, Milo011af7b2012-03-23 15:02:09 -0700415static void lp5521_clear_program_memory(struct i2c_client *cl)
416{
417 int i;
418 u8 rgb_mem[] = {
419 LP5521_REG_R_PROG_MEM,
420 LP5521_REG_G_PROG_MEM,
421 LP5521_REG_B_PROG_MEM,
422 };
423
424 for (i = 0; i < ARRAY_SIZE(rgb_mem); i++) {
425 lp5521_write(cl, rgb_mem[i], 0);
426 lp5521_write(cl, rgb_mem[i] + 1, 0);
427 }
428}
429
430static void lp5521_write_program_memory(struct i2c_client *cl,
431 u8 base, u8 *rgb, int size)
432{
433 int i;
434
435 if (!rgb || size <= 0)
436 return;
437
438 for (i = 0; i < size; i++)
439 lp5521_write(cl, base + i, *(rgb + i));
440
441 lp5521_write(cl, base + i, 0);
442 lp5521_write(cl, base + i + 1, 0);
443}
444
445static inline struct lp5521_led_pattern *lp5521_get_pattern
446 (struct lp5521_chip *chip, u8 offset)
447{
448 struct lp5521_led_pattern *ptn;
449 ptn = chip->pdata->patterns + (offset - 1);
450 return ptn;
451}
452
453static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip)
454{
455 struct lp5521_led_pattern *ptn;
456 struct i2c_client *cl = chip->client;
457 int num_patterns = chip->pdata->num_patterns;
458
459 if (mode > num_patterns || !(chip->pdata->patterns))
460 return;
461
462 if (mode == PATTERN_OFF) {
Kim, Milo32a2f742012-03-23 15:02:09 -0700463 lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT);
Kim, Milo011af7b2012-03-23 15:02:09 -0700464 usleep_range(1000, 2000);
465 lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
466 } else {
467 ptn = lp5521_get_pattern(chip, mode);
468 if (!ptn)
469 return;
470
471 lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD);
472 usleep_range(1000, 2000);
473
474 lp5521_clear_program_memory(cl);
475
476 lp5521_write_program_memory(cl, LP5521_REG_R_PROG_MEM,
477 ptn->r, ptn->size_r);
478 lp5521_write_program_memory(cl, LP5521_REG_G_PROG_MEM,
479 ptn->g, ptn->size_g);
480 lp5521_write_program_memory(cl, LP5521_REG_B_PROG_MEM,
481 ptn->b, ptn->size_b);
482
483 lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN);
484 usleep_range(1000, 2000);
Kim, Milo32a2f742012-03-23 15:02:09 -0700485 lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM);
Kim, Milo011af7b2012-03-23 15:02:09 -0700486 }
487}
488
Samu Onkalo500fe142010-11-11 14:05:22 -0800489/* device attributes */
Samu Onkalo500fe142010-11-11 14:05:22 -0800490static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL);
491
492static struct attribute *lp5521_attributes[] = {
Samu Onkalo500fe142010-11-11 14:05:22 -0800493 &dev_attr_selftest.attr,
Samu Onkalo500fe142010-11-11 14:05:22 -0800494 NULL
495};
496
497static const struct attribute_group lp5521_group = {
498 .attrs = lp5521_attributes,
499};
500
Samu Onkalo500fe142010-11-11 14:05:22 -0800501static void lp5521_unregister_sysfs(struct i2c_client *client)
502{
Samu Onkalo500fe142010-11-11 14:05:22 -0800503 struct device *dev = &client->dev;
Samu Onkalo500fe142010-11-11 14:05:22 -0800504
505 sysfs_remove_group(&dev->kobj, &lp5521_group);
Milo(Woogyom) Kim1904f832013-02-05 17:56:23 +0900506}
507
Milo(Woogyom) Kim48068d52013-02-05 18:08:49 +0900508/* Chip specific configurations */
509static struct lp55xx_device_config lp5521_cfg = {
510 .reset = {
511 .addr = LP5521_REG_RESET,
512 .val = LP5521_RESET,
513 },
Milo(Woogyom) Kime3a700d2013-02-05 18:09:56 +0900514 .enable = {
515 .addr = LP5521_REG_ENABLE,
516 .val = LP5521_ENABLE_DEFAULT,
517 },
Milo(Woogyom) Kim0e202342013-02-05 19:07:34 +0900518 .max_channel = LP5521_MAX_LEDS,
Milo(Woogyom) Kimffbdccd2013-02-05 18:57:36 +0900519 .post_init_device = lp5521_post_init_device,
Milo(Woogyom) Kima6e46792013-02-05 19:08:40 +0900520 .brightness_work_fn = lp5521_led_brightness_work,
Milo(Woogyom) Kima96bfa12013-02-05 19:09:32 +0900521 .set_led_current = lp5521_set_led_current,
Milo(Woogyom) Kim9ce7cb12013-02-05 19:18:10 +0900522 .firmware_cb = lp5521_firmware_loaded,
523 .run_engine = lp5521_run_engine,
Milo(Woogyom) Kime73c0ce2013-02-05 19:20:45 +0900524 .dev_attr_group = &lp5521_group,
Milo(Woogyom) Kim48068d52013-02-05 18:08:49 +0900525};
526
Bill Pemberton98ea1ea2012-11-19 13:23:02 -0500527static int lp5521_probe(struct i2c_client *client,
Samu Onkalo500fe142010-11-11 14:05:22 -0800528 const struct i2c_device_id *id)
529{
Milo(Woogyom) Kim1904f832013-02-05 17:56:23 +0900530 int ret;
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +0900531 struct lp55xx_chip *chip;
532 struct lp55xx_led *led;
533 struct lp55xx_platform_data *pdata = client->dev.platform_data;
Samu Onkalo500fe142010-11-11 14:05:22 -0800534
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +0900535 if (!pdata) {
Samu Onkalo500fe142010-11-11 14:05:22 -0800536 dev_err(&client->dev, "no platform data\n");
Bryan Wue430dc02012-07-04 11:16:09 +0800537 return -EINVAL;
Samu Onkalo500fe142010-11-11 14:05:22 -0800538 }
539
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +0900540 chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
541 if (!chip)
542 return -ENOMEM;
Samu Onkalo500fe142010-11-11 14:05:22 -0800543
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +0900544 led = devm_kzalloc(&client->dev,
545 sizeof(*led) * pdata->num_channels, GFP_KERNEL);
546 if (!led)
547 return -ENOMEM;
548
549 chip->cl = client;
550 chip->pdata = pdata;
Milo(Woogyom) Kim48068d52013-02-05 18:08:49 +0900551 chip->cfg = &lp5521_cfg;
Milo(Woogyom) Kim6a0c9a42013-02-05 18:03:08 +0900552
553 mutex_init(&chip->lock);
554
555 i2c_set_clientdata(client, led);
Samu Onkalo500fe142010-11-11 14:05:22 -0800556
Milo(Woogyom) Kim22ebeb42013-02-05 18:58:35 +0900557 ret = lp55xx_init_device(chip);
Milo(Woogyom) Kim944f7b12013-02-05 17:49:46 +0900558 if (ret)
Milo(Woogyom) Kimf6c64c62013-02-05 17:58:01 +0900559 goto err_init;
Samu Onkalo500fe142010-11-11 14:05:22 -0800560
561 dev_info(&client->dev, "%s programmable led chip found\n", id->name);
562
Milo(Woogyom) Kim9e9b3db2013-02-05 19:06:27 +0900563 ret = lp55xx_register_leds(led, chip);
Milo(Woogyom) Kimf6524802013-02-05 17:53:40 +0900564 if (ret)
Milo(Woogyom) Kim9e9b3db2013-02-05 19:06:27 +0900565 goto err_register_leds;
Samu Onkalo500fe142010-11-11 14:05:22 -0800566
Milo(Woogyom) Kime73c0ce2013-02-05 19:20:45 +0900567 ret = lp55xx_register_sysfs(chip);
Samu Onkalo500fe142010-11-11 14:05:22 -0800568 if (ret) {
569 dev_err(&client->dev, "registering sysfs failed\n");
Milo(Woogyom) Kime73c0ce2013-02-05 19:20:45 +0900570 goto err_register_sysfs;
Samu Onkalo500fe142010-11-11 14:05:22 -0800571 }
Milo(Woogyom) Kime73c0ce2013-02-05 19:20:45 +0900572
573 return 0;
574
575err_register_sysfs:
Milo(Woogyom) Kimc3a68eb2013-02-05 19:11:18 +0900576 lp55xx_unregister_leds(led, chip);
Milo(Woogyom) Kim9e9b3db2013-02-05 19:06:27 +0900577err_register_leds:
Milo(Woogyom) Kim6ce61762013-02-05 19:03:02 +0900578 lp55xx_deinit_device(chip);
Milo(Woogyom) Kimf6c64c62013-02-05 17:58:01 +0900579err_init:
Samu Onkalo500fe142010-11-11 14:05:22 -0800580 return ret;
581}
582
Bill Pemberton678e8a62012-11-19 13:26:00 -0500583static int lp5521_remove(struct i2c_client *client)
Samu Onkalo500fe142010-11-11 14:05:22 -0800584{
Milo(Woogyom) Kim945c7002013-02-05 18:02:26 +0900585 struct lp5521_chip *old_chip = i2c_get_clientdata(client);
Milo(Woogyom) Kim6ce61762013-02-05 19:03:02 +0900586 struct lp55xx_led *led = i2c_get_clientdata(client);
587 struct lp55xx_chip *chip = led->chip;
Samu Onkalo500fe142010-11-11 14:05:22 -0800588
Milo(Woogyom) Kim945c7002013-02-05 18:02:26 +0900589 lp5521_run_led_pattern(PATTERN_OFF, old_chip);
Samu Onkalo500fe142010-11-11 14:05:22 -0800590 lp5521_unregister_sysfs(client);
591
Milo(Woogyom) Kimc3a68eb2013-02-05 19:11:18 +0900592 lp55xx_unregister_leds(led, chip);
Milo(Woogyom) Kim6ce61762013-02-05 19:03:02 +0900593 lp55xx_deinit_device(chip);
Samu Onkalo500fe142010-11-11 14:05:22 -0800594
Samu Onkalo500fe142010-11-11 14:05:22 -0800595 return 0;
596}
597
598static const struct i2c_device_id lp5521_id[] = {
599 { "lp5521", 0 }, /* Three channel chip */
600 { }
601};
602MODULE_DEVICE_TABLE(i2c, lp5521_id);
603
604static struct i2c_driver lp5521_driver = {
605 .driver = {
606 .name = "lp5521",
607 },
608 .probe = lp5521_probe,
Bill Pembertondf07cf82012-11-19 13:20:20 -0500609 .remove = lp5521_remove,
Samu Onkalo500fe142010-11-11 14:05:22 -0800610 .id_table = lp5521_id,
611};
612
Axel Lin09a0d182012-01-10 15:09:27 -0800613module_i2c_driver(lp5521_driver);
Samu Onkalo500fe142010-11-11 14:05:22 -0800614
615MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo");
616MODULE_DESCRIPTION("LP5521 LED engine");
617MODULE_LICENSE("GPL v2");