aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
authorKim, Milo <Milo.Kim@ti.com>2013-07-09 05:11:37 -0400
committerBryan Wu <cooloney@gmail.com>2013-08-26 20:22:10 -0400
commit33b3a561f417ec3e1013999ce8bdb6c055abb1ce (patch)
treec2288f2c8fba1860f433c7fb05cc54e346238cfa /drivers/leds
parent81d22878092feab779f3efaab404036d31dc06f8 (diff)
leds: support new LP8501 device - another LP55xx common
LP8501 can drive up to 9 channels like LP5523. LEDs can be controlled directly via the I2C and programmable engines are supported. LP55xx common driver LP8501 is one of LP55xx family device, so LP55xx common code are used. Chip specific data is defined in the structure, 'lp55xx_device_config'. Differences between LP8501 and LP5523 Different register layout for LED output control and others. LP8501 specific feature for separate output power selection. LP8501 doesn't support external clock detection. Different programming engine data. LP8501 specific feature - output power selection Output channels are selected by power selection - Vout or Vdd. Separate power for VDD1-6 and VDD7-9 are available. It is configurable in the platform data. To support this feature, LP55xx DT structure and header are changed. Device tree binding is updated as well. LED pattern data Example pattern data is updated in the driver documentation. Signed-off-by: Milo Kim <milo.kim@ti.com> Signed-off-by: Bryan Wu <cooloney@gmail.com>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig18
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-lp55xx-common.c3
-rw-r--r--drivers/leds/leds-lp8501.c410
4 files changed, 429 insertions, 3 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index e43402dd1dea..77329ce672cb 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -194,11 +194,11 @@ config LEDS_LP3944
194 module will be called leds-lp3944. 194 module will be called leds-lp3944.
195 195
196config LEDS_LP55XX_COMMON 196config LEDS_LP55XX_COMMON
197 tristate "Common Driver for TI/National LP5521, LP5523/55231 and LP5562" 197 tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501"
198 depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 198 depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501
199 select FW_LOADER 199 select FW_LOADER
200 help 200 help
201 This option supports common operations for LP5521 and LP5523/55231 201 This option supports common operations for LP5521/5523/55231/5562/8501
202 devices. 202 devices.
203 203
204config LEDS_LP5521 204config LEDS_LP5521
@@ -232,6 +232,18 @@ config LEDS_LP5562
232 Driver provides direct control via LED class and interface for 232 Driver provides direct control via LED class and interface for
233 programming the engines. 233 programming the engines.
234 234
235config LEDS_LP8501
236 tristate "LED Support for TI LP8501 LED driver chip"
237 depends on LEDS_CLASS && I2C
238 select LEDS_LP55XX_COMMON
239 help
240 If you say yes here you get support for TI LP8501 LED driver.
241 It is 9 channel chip with programmable engines.
242 Driver provides direct control via LED class and interface for
243 programming the engines.
244 It is similar as LP5523, but output power selection is available.
245 And register layout and engine program schemes are different.
246
235config LEDS_LP8788 247config LEDS_LP8788
236 tristate "LED support for the TI LP8788 PMIC" 248 tristate "LED support for the TI LP8788 PMIC"
237 depends on LEDS_CLASS 249 depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index ac2897732b02..3013113e74d2 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o
27obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o 27obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
28obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o 28obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
29obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o 29obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o
30obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o
30obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o 31obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
31obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o 32obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
32obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o 33obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c
index c2fecd4d391c..351825b96f16 100644
--- a/drivers/leds/leds-lp55xx-common.c
+++ b/drivers/leds/leds-lp55xx-common.c
@@ -593,6 +593,9 @@ int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np)
593 of_property_read_string(np, "label", &pdata->label); 593 of_property_read_string(np, "label", &pdata->label);
594 of_property_read_u8(np, "clock-mode", &pdata->clock_mode); 594 of_property_read_u8(np, "clock-mode", &pdata->clock_mode);
595 595
596 /* LP8501 specific */
597 of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel);
598
596 dev->platform_data = pdata; 599 dev->platform_data = pdata;
597 600
598 return 0; 601 return 0;
diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c
new file mode 100644
index 000000000000..4573b9471aaa
--- /dev/null
+++ b/drivers/leds/leds-lp8501.c
@@ -0,0 +1,410 @@
1/*
2 * TI LP8501 9 channel LED Driver
3 *
4 * Copyright (C) 2013 Texas Instruments
5 *
6 * Author: Milo(Woogyom) Kim <milo.kim@ti.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 */
13
14#include <linux/delay.h>
15#include <linux/firmware.h>
16#include <linux/i2c.h>
17#include <linux/init.h>
18#include <linux/leds.h>
19#include <linux/module.h>
20#include <linux/mutex.h>
21#include <linux/platform_data/leds-lp55xx.h>
22#include <linux/slab.h>
23
24#include "leds-lp55xx-common.h"
25
26#define LP8501_PROGRAM_LENGTH 32
27#define LP8501_MAX_LEDS 9
28
29/* Registers */
30#define LP8501_REG_ENABLE 0x00
31#define LP8501_ENABLE BIT(6)
32#define LP8501_EXEC_M 0x3F
33#define LP8501_EXEC_ENG1_M 0x30
34#define LP8501_EXEC_ENG2_M 0x0C
35#define LP8501_EXEC_ENG3_M 0x03
36#define LP8501_RUN_ENG1 0x20
37#define LP8501_RUN_ENG2 0x08
38#define LP8501_RUN_ENG3 0x02
39
40#define LP8501_REG_OP_MODE 0x01
41#define LP8501_MODE_ENG1_M 0x30
42#define LP8501_MODE_ENG2_M 0x0C
43#define LP8501_MODE_ENG3_M 0x03
44#define LP8501_LOAD_ENG1 0x10
45#define LP8501_LOAD_ENG2 0x04
46#define LP8501_LOAD_ENG3 0x01
47
48#define LP8501_REG_PWR_CONFIG 0x05
49#define LP8501_PWR_CONFIG_M 0x03
50
51#define LP8501_REG_LED_PWM_BASE 0x16
52
53#define LP8501_REG_LED_CURRENT_BASE 0x26
54
55#define LP8501_REG_CONFIG 0x36
56#define LP8501_PWM_PSAVE BIT(7)
57#define LP8501_AUTO_INC BIT(6)
58#define LP8501_PWR_SAVE BIT(5)
59#define LP8501_CP_AUTO 0x18
60#define LP8501_INT_CLK BIT(0)
61#define LP8501_DEFAULT_CFG \
62 (LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE | LP8501_CP_AUTO)
63
64#define LP8501_REG_RESET 0x3D
65#define LP8501_RESET 0xFF
66
67#define LP8501_REG_PROG_PAGE_SEL 0x4F
68#define LP8501_PAGE_ENG1 0
69#define LP8501_PAGE_ENG2 1
70#define LP8501_PAGE_ENG3 2
71
72#define LP8501_REG_PROG_MEM 0x50
73
74#define LP8501_ENG1_IS_LOADING(mode) \
75 ((mode & LP8501_MODE_ENG1_M) == LP8501_LOAD_ENG1)
76#define LP8501_ENG2_IS_LOADING(mode) \
77 ((mode & LP8501_MODE_ENG2_M) == LP8501_LOAD_ENG2)
78#define LP8501_ENG3_IS_LOADING(mode) \
79 ((mode & LP8501_MODE_ENG3_M) == LP8501_LOAD_ENG3)
80
81static inline void lp8501_wait_opmode_done(void)
82{
83 usleep_range(1000, 2000);
84}
85
86static void lp8501_set_led_current(struct lp55xx_led *led, u8 led_current)
87{
88 led->led_current = led_current;
89 lp55xx_write(led->chip, LP8501_REG_LED_CURRENT_BASE + led->chan_nr,
90 led_current);
91}
92
93static int lp8501_post_init_device(struct lp55xx_chip *chip)
94{
95 int ret;
96 u8 val = LP8501_DEFAULT_CFG;
97
98 ret = lp55xx_write(chip, LP8501_REG_ENABLE, LP8501_ENABLE);
99 if (ret)
100 return ret;
101
102 /* Chip startup time is 500 us, 1 - 2 ms gives some margin */
103 usleep_range(1000, 2000);
104
105 if (chip->pdata->clock_mode != LP55XX_CLOCK_EXT)
106 val |= LP8501_INT_CLK;
107
108 ret = lp55xx_write(chip, LP8501_REG_CONFIG, val);
109 if (ret)
110 return ret;
111
112 /* Power selection for each output */
113 return lp55xx_update_bits(chip, LP8501_REG_PWR_CONFIG,
114 LP8501_PWR_CONFIG_M, chip->pdata->pwr_sel);
115}
116
117static void lp8501_load_engine(struct lp55xx_chip *chip)
118{
119 enum lp55xx_engine_index idx = chip->engine_idx;
120 u8 mask[] = {
121 [LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M,
122 [LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M,
123 [LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M,
124 };
125
126 u8 val[] = {
127 [LP55XX_ENGINE_1] = LP8501_LOAD_ENG1,
128 [LP55XX_ENGINE_2] = LP8501_LOAD_ENG2,
129 [LP55XX_ENGINE_3] = LP8501_LOAD_ENG3,
130 };
131
132 u8 page_sel[] = {
133 [LP55XX_ENGINE_1] = LP8501_PAGE_ENG1,
134 [LP55XX_ENGINE_2] = LP8501_PAGE_ENG2,
135 [LP55XX_ENGINE_3] = LP8501_PAGE_ENG3,
136 };
137
138 lp55xx_update_bits(chip, LP8501_REG_OP_MODE, mask[idx], val[idx]);
139
140 lp8501_wait_opmode_done();
141
142 lp55xx_write(chip, LP8501_REG_PROG_PAGE_SEL, page_sel[idx]);
143}
144
145static void lp8501_stop_engine(struct lp55xx_chip *chip)
146{
147 lp55xx_write(chip, LP8501_REG_OP_MODE, 0);
148 lp8501_wait_opmode_done();
149}
150
151static void lp8501_turn_off_channels(struct lp55xx_chip *chip)
152{
153 int i;
154
155 for (i = 0; i < LP8501_MAX_LEDS; i++)
156 lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + i, 0);
157}
158
159static void lp8501_run_engine(struct lp55xx_chip *chip, bool start)
160{
161 int ret;
162 u8 mode;
163 u8 exec;
164
165 /* stop engine */
166 if (!start) {
167 lp8501_stop_engine(chip);
168 lp8501_turn_off_channels(chip);
169 return;
170 }
171
172 /*
173 * To run the engine,
174 * operation mode and enable register should updated at the same time
175 */
176
177 ret = lp55xx_read(chip, LP8501_REG_OP_MODE, &mode);
178 if (ret)
179 return;
180
181 ret = lp55xx_read(chip, LP8501_REG_ENABLE, &exec);
182 if (ret)
183 return;
184
185 /* change operation mode to RUN only when each engine is loading */
186 if (LP8501_ENG1_IS_LOADING(mode)) {
187 mode = (mode & ~LP8501_MODE_ENG1_M) | LP8501_RUN_ENG1;
188 exec = (exec & ~LP8501_EXEC_ENG1_M) | LP8501_RUN_ENG1;
189 }
190
191 if (LP8501_ENG2_IS_LOADING(mode)) {
192 mode = (mode & ~LP8501_MODE_ENG2_M) | LP8501_RUN_ENG2;
193 exec = (exec & ~LP8501_EXEC_ENG2_M) | LP8501_RUN_ENG2;
194 }
195
196 if (LP8501_ENG3_IS_LOADING(mode)) {
197 mode = (mode & ~LP8501_MODE_ENG3_M) | LP8501_RUN_ENG3;
198 exec = (exec & ~LP8501_EXEC_ENG3_M) | LP8501_RUN_ENG3;
199 }
200
201 lp55xx_write(chip, LP8501_REG_OP_MODE, mode);
202 lp8501_wait_opmode_done();
203
204 lp55xx_update_bits(chip, LP8501_REG_ENABLE, LP8501_EXEC_M, exec);
205}
206
207static int lp8501_update_program_memory(struct lp55xx_chip *chip,
208 const u8 *data, size_t size)
209{
210 u8 pattern[LP8501_PROGRAM_LENGTH] = {0};
211 unsigned cmd;
212 char c[3];
213 int update_size;
214 int nrchars;
215 int offset = 0;
216 int ret;
217 int i;
218
219 /* clear program memory before updating */
220 for (i = 0; i < LP8501_PROGRAM_LENGTH; i++)
221 lp55xx_write(chip, LP8501_REG_PROG_MEM + i, 0);
222
223 i = 0;
224 while ((offset < size - 1) && (i < LP8501_PROGRAM_LENGTH)) {
225 /* separate sscanfs because length is working only for %s */
226 ret = sscanf(data + offset, "%2s%n ", c, &nrchars);
227 if (ret != 1)
228 goto err;
229
230 ret = sscanf(c, "%2x", &cmd);
231 if (ret != 1)
232 goto err;
233
234 pattern[i] = (u8)cmd;
235 offset += nrchars;
236 i++;
237 }
238
239 /* Each instruction is 16bit long. Check that length is even */
240 if (i % 2)
241 goto err;
242
243 update_size = i;
244 for (i = 0; i < update_size; i++)
245 lp55xx_write(chip, LP8501_REG_PROG_MEM + i, pattern[i]);
246
247 return 0;
248
249err:
250 dev_err(&chip->cl->dev, "wrong pattern format\n");
251 return -EINVAL;
252}
253
254static void lp8501_firmware_loaded(struct lp55xx_chip *chip)
255{
256 const struct firmware *fw = chip->fw;
257
258 if (fw->size > LP8501_PROGRAM_LENGTH) {
259 dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n",
260 fw->size);
261 return;
262 }
263
264 /*
265 * Program momery sequence
266 * 1) set engine mode to "LOAD"
267 * 2) write firmware data into program memory
268 */
269
270 lp8501_load_engine(chip);
271 lp8501_update_program_memory(chip, fw->data, fw->size);
272}
273
274static void lp8501_led_brightness_work(struct work_struct *work)
275{
276 struct lp55xx_led *led = container_of(work, struct lp55xx_led,
277 brightness_work);
278 struct lp55xx_chip *chip = led->chip;
279
280 mutex_lock(&chip->lock);
281 lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr,
282 led->brightness);
283 mutex_unlock(&chip->lock);
284}
285
286/* Chip specific configurations */
287static struct lp55xx_device_config lp8501_cfg = {
288 .reset = {
289 .addr = LP8501_REG_RESET,
290 .val = LP8501_RESET,
291 },
292 .enable = {
293 .addr = LP8501_REG_ENABLE,
294 .val = LP8501_ENABLE,
295 },
296 .max_channel = LP8501_MAX_LEDS,
297 .post_init_device = lp8501_post_init_device,
298 .brightness_work_fn = lp8501_led_brightness_work,
299 .set_led_current = lp8501_set_led_current,
300 .firmware_cb = lp8501_firmware_loaded,
301 .run_engine = lp8501_run_engine,
302};
303
304static int lp8501_probe(struct i2c_client *client,
305 const struct i2c_device_id *id)
306{
307 int ret;
308 struct lp55xx_chip *chip;
309 struct lp55xx_led *led;
310 struct lp55xx_platform_data *pdata;
311 struct device_node *np = client->dev.of_node;
312
313 if (!client->dev.platform_data) {
314 if (np) {
315 ret = lp55xx_of_populate_pdata(&client->dev, np);
316 if (ret < 0)
317 return ret;
318 } else {
319 dev_err(&client->dev, "no platform data\n");
320 return -EINVAL;
321 }
322 }
323 pdata = client->dev.platform_data;
324
325 chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
326 if (!chip)
327 return -ENOMEM;
328
329 led = devm_kzalloc(&client->dev,
330 sizeof(*led) * pdata->num_channels, GFP_KERNEL);
331 if (!led)
332 return -ENOMEM;
333
334 chip->cl = client;
335 chip->pdata = pdata;
336 chip->cfg = &lp8501_cfg;
337
338 mutex_init(&chip->lock);
339
340 i2c_set_clientdata(client, led);
341
342 ret = lp55xx_init_device(chip);
343 if (ret)
344 goto err_init;
345
346 dev_info(&client->dev, "%s Programmable led chip found\n", id->name);
347
348 ret = lp55xx_register_leds(led, chip);
349 if (ret)
350 goto err_register_leds;
351
352 ret = lp55xx_register_sysfs(chip);
353 if (ret) {
354 dev_err(&client->dev, "registering sysfs failed\n");
355 goto err_register_sysfs;
356 }
357
358 return 0;
359
360err_register_sysfs:
361 lp55xx_unregister_leds(led, chip);
362err_register_leds:
363 lp55xx_deinit_device(chip);
364err_init:
365 return ret;
366}
367
368static int lp8501_remove(struct i2c_client *client)
369{
370 struct lp55xx_led *led = i2c_get_clientdata(client);
371 struct lp55xx_chip *chip = led->chip;
372
373 lp8501_stop_engine(chip);
374 lp55xx_unregister_sysfs(chip);
375 lp55xx_unregister_leds(led, chip);
376 lp55xx_deinit_device(chip);
377
378 return 0;
379}
380
381static const struct i2c_device_id lp8501_id[] = {
382 { "lp8501", 0 },
383 { }
384};
385MODULE_DEVICE_TABLE(i2c, lp8501_id);
386
387#ifdef CONFIG_OF
388static const struct of_device_id of_lp8501_leds_match[] = {
389 { .compatible = "ti,lp8501", },
390 {},
391};
392
393MODULE_DEVICE_TABLE(of, of_lp8501_leds_match);
394#endif
395
396static struct i2c_driver lp8501_driver = {
397 .driver = {
398 .name = "lp8501",
399 .of_match_table = of_match_ptr(of_lp8501_leds_match),
400 },
401 .probe = lp8501_probe,
402 .remove = lp8501_remove,
403 .id_table = lp8501_id,
404};
405
406module_i2c_driver(lp8501_driver);
407
408MODULE_DESCRIPTION("Texas Instruments LP8501 LED drvier");
409MODULE_AUTHOR("Milo Kim");
410MODULE_LICENSE("GPL");