aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Murphy <dmurphy@ti.com>2014-11-19 07:48:35 -0500
committerBryan Wu <cooloney@gmail.com>2014-11-19 13:33:06 -0500
commit7a8685accb95801bb29ab85d5b370999d3fb8e32 (patch)
tree6abb9b310e172699bee34640626a89ea50f86be8
parent4d71a4a12b130ad033219e6f58c74a64059415eb (diff)
leds: lp8860: Introduce TI lp8860 4 channel LED driver
Introduce the Texas Instruments lp8860 4 channel LED driver. This driver configures the device in display cluster mode as this seems to be the most used configuration at the time of the driver configuration. For more product information please see the link below: http://www.ti.com/product/lp8860-q1 Signed-off-by: Dan Murphy <dmurphy@ti.com> Signed-off-by: Bryan Wu <cooloney@gmail.com>
-rw-r--r--Documentation/devicetree/bindings/leds/leds-lp8860.txt29
-rw-r--r--drivers/leds/Kconfig10
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-lp8860.c491
4 files changed, 531 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/leds/leds-lp8860.txt b/Documentation/devicetree/bindings/leds/leds-lp8860.txt
new file mode 100644
index 000000000000..aad38dd94d4b
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-lp8860.txt
@@ -0,0 +1,29 @@
1* Texas Instruments - lp8860 4-Channel LED Driver
2
3The LP8860-Q1 is an high-efficiency LED
4driver with boost controller. It has 4 high-precision
5current sinks that can be controlled by a PWM input
6signal, a SPI/I2C master, or both.
7
8Required properties:
9 - compatible:
10 "ti,lp8860"
11 - reg - I2C slave address
12 - label - Used for naming LEDs
13
14Optional properties:
15 - enable-gpio - gpio pin to enable/disable the device.
16 - supply - "vled" - LED supply
17
18Example:
19
20leds: leds@6 {
21 compatible = "ti,lp8860";
22 reg = <0x2d>;
23 label = "display_cluster";
24 enable-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
25 vled-supply = <&vbatt>;
26}
27
28For more product information please see the link below:
29http://www.ti.com/product/lp8860-q1
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index a210338cfeb1..b0dce59f23e8 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -250,6 +250,16 @@ config LEDS_LP8788
250 help 250 help
251 This option enables support for the Keyboard LEDs on the LP8788 PMIC. 251 This option enables support for the Keyboard LEDs on the LP8788 PMIC.
252 252
253config LEDS_LP8860
254 tristate "LED support for the TI LP8860 4 channel LED driver"
255 depends on LEDS_CLASS && REGMAP_I2C
256 help
257 If you say yes here you get support for the TI LP8860 4 channel
258 LED driver.
259 This option enables support for the display cluster LEDs
260 on the LP8860 4 channel LED driver using the I2C communication
261 bus.
262
253config LEDS_CLEVO_MAIL 263config LEDS_CLEVO_MAIL
254 tristate "Mail LED on Clevo notebook" 264 tristate "Mail LED on Clevo notebook"
255 depends on LEDS_CLASS 265 depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index a2b164741465..1c65a191d907 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
28obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o 28obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o
29obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o 29obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o
30obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o 30obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
31obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.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
33obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o 34obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o
diff --git a/drivers/leds/leds-lp8860.c b/drivers/leds/leds-lp8860.c
new file mode 100644
index 000000000000..840e93f3ab3e
--- /dev/null
+++ b/drivers/leds/leds-lp8860.c
@@ -0,0 +1,491 @@
1/*
2 * TI LP8860 4-Channel LED Driver
3 *
4 * Copyright (C) 2014 Texas Instruments
5 *
6 * Author: Dan Murphy <dmurphy@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/i2c.h>
15#include <linux/init.h>
16#include <linux/leds.h>
17#include <linux/regmap.h>
18#include <linux/regulator/consumer.h>
19#include <linux/module.h>
20#include <linux/mutex.h>
21#include <linux/of.h>
22#include <linux/of_gpio.h>
23#include <linux/gpio/consumer.h>
24#include <linux/slab.h>
25
26#define LP8860_DISP_CL1_BRT_MSB 0x00
27#define LP8860_DISP_CL1_BRT_LSB 0x01
28#define LP8860_DISP_CL1_CURR_MSB 0x02
29#define LP8860_DISP_CL1_CURR_LSB 0x03
30#define LP8860_CL2_BRT_MSB 0x04
31#define LP8860_CL2_BRT_LSB 0x05
32#define LP8860_CL2_CURRENT 0x06
33#define LP8860_CL3_BRT_MSB 0x07
34#define LP8860_CL3_BRT_LSB 0x08
35#define LP8860_CL3_CURRENT 0x09
36#define LP8860_CL4_BRT_MSB 0x0a
37#define LP8860_CL4_BRT_LSB 0x0b
38#define LP8860_CL4_CURRENT 0x0c
39#define LP8860_CONFIG 0x0d
40#define LP8860_STATUS 0x0e
41#define LP8860_FAULT 0x0f
42#define LP8860_LED_FAULT 0x10
43#define LP8860_FAULT_CLEAR 0x11
44#define LP8860_ID 0x12
45#define LP8860_TEMP_MSB 0x13
46#define LP8860_TEMP_LSB 0x14
47#define LP8860_DISP_LED_CURR_MSB 0x15
48#define LP8860_DISP_LED_CURR_LSB 0x16
49#define LP8860_DISP_LED_PWM_MSB 0x17
50#define LP8860_DISP_LED_PWM_LSB 0x18
51#define LP8860_EEPROM_CNTRL 0x19
52#define LP8860_EEPROM_UNLOCK 0x1a
53
54#define LP8860_EEPROM_REG_0 0x60
55#define LP8860_EEPROM_REG_1 0x61
56#define LP8860_EEPROM_REG_2 0x62
57#define LP8860_EEPROM_REG_3 0x63
58#define LP8860_EEPROM_REG_4 0x64
59#define LP8860_EEPROM_REG_5 0x65
60#define LP8860_EEPROM_REG_6 0x66
61#define LP8860_EEPROM_REG_7 0x67
62#define LP8860_EEPROM_REG_8 0x68
63#define LP8860_EEPROM_REG_9 0x69
64#define LP8860_EEPROM_REG_10 0x6a
65#define LP8860_EEPROM_REG_11 0x6b
66#define LP8860_EEPROM_REG_12 0x6c
67#define LP8860_EEPROM_REG_13 0x6d
68#define LP8860_EEPROM_REG_14 0x6e
69#define LP8860_EEPROM_REG_15 0x6f
70#define LP8860_EEPROM_REG_16 0x70
71#define LP8860_EEPROM_REG_17 0x71
72#define LP8860_EEPROM_REG_18 0x72
73#define LP8860_EEPROM_REG_19 0x73
74#define LP8860_EEPROM_REG_20 0x74
75#define LP8860_EEPROM_REG_21 0x75
76#define LP8860_EEPROM_REG_22 0x76
77#define LP8860_EEPROM_REG_23 0x77
78#define LP8860_EEPROM_REG_24 0x78
79
80#define LP8860_LOCK_EEPROM 0x00
81#define LP8860_UNLOCK_EEPROM 0x01
82#define LP8860_PROGRAM_EEPROM 0x02
83#define LP8860_EEPROM_CODE_1 0x08
84#define LP8860_EEPROM_CODE_2 0xba
85#define LP8860_EEPROM_CODE_3 0xef
86
87#define LP8860_CLEAR_FAULTS 0x01
88
89#define LP8860_DISP_LED_NAME "display_cluster"
90
91/**
92 * struct lp8860_led -
93 * @lock - Lock for reading/writing the device
94 * @work - Work item used to off load the brightness register writes
95 * @client - Pointer to the I2C client
96 * @led_dev - led class device pointer
97 * @regmap - Devices register map
98 * @eeprom_regmap - EEPROM register map
99 * @enable_gpio - VDDIO/EN gpio to enable communication interface
100 * @regulator - LED supply regulator pointer
101 * @brightness - Current brightness value requested
102 * @label - LED label
103**/
104struct lp8860_led {
105 struct mutex lock;
106 struct work_struct work;
107 struct i2c_client *client;
108 struct led_classdev led_dev;
109 struct regmap *regmap;
110 struct regmap *eeprom_regmap;
111 struct gpio_desc *enable_gpio;
112 struct regulator *regulator;
113 enum led_brightness brightness;
114 const char *label;
115};
116
117struct lp8860_eeprom_reg {
118 uint8_t reg;
119 uint8_t value;
120};
121
122static struct lp8860_eeprom_reg lp8860_eeprom_disp_regs[] = {
123 { LP8860_EEPROM_REG_0, 0xed },
124 { LP8860_EEPROM_REG_1, 0xdf },
125 { LP8860_EEPROM_REG_2, 0xdc },
126 { LP8860_EEPROM_REG_3, 0xf0 },
127 { LP8860_EEPROM_REG_4, 0xdf },
128 { LP8860_EEPROM_REG_5, 0xe5 },
129 { LP8860_EEPROM_REG_6, 0xf2 },
130 { LP8860_EEPROM_REG_7, 0x77 },
131 { LP8860_EEPROM_REG_8, 0x77 },
132 { LP8860_EEPROM_REG_9, 0x71 },
133 { LP8860_EEPROM_REG_10, 0x3f },
134 { LP8860_EEPROM_REG_11, 0xb7 },
135 { LP8860_EEPROM_REG_12, 0x17 },
136 { LP8860_EEPROM_REG_13, 0xef },
137 { LP8860_EEPROM_REG_14, 0xb0 },
138 { LP8860_EEPROM_REG_15, 0x87 },
139 { LP8860_EEPROM_REG_16, 0xce },
140 { LP8860_EEPROM_REG_17, 0x72 },
141 { LP8860_EEPROM_REG_18, 0xe5 },
142 { LP8860_EEPROM_REG_19, 0xdf },
143 { LP8860_EEPROM_REG_20, 0x35 },
144 { LP8860_EEPROM_REG_21, 0x06 },
145 { LP8860_EEPROM_REG_22, 0xdc },
146 { LP8860_EEPROM_REG_23, 0x88 },
147 { LP8860_EEPROM_REG_24, 0x3E },
148};
149
150static int lp8860_unlock_eeprom(struct lp8860_led *led, int lock)
151{
152 int ret;
153
154 mutex_lock(&led->lock);
155
156 if (lock == LP8860_UNLOCK_EEPROM) {
157 ret = regmap_write(led->regmap,
158 LP8860_EEPROM_UNLOCK,
159 LP8860_EEPROM_CODE_1);
160 if (ret) {
161 dev_err(&led->client->dev, "EEPROM Unlock failed\n");
162 goto out;
163 }
164
165 ret = regmap_write(led->regmap,
166 LP8860_EEPROM_UNLOCK,
167 LP8860_EEPROM_CODE_2);
168 if (ret) {
169 dev_err(&led->client->dev, "EEPROM Unlock failed\n");
170 goto out;
171 }
172 ret = regmap_write(led->regmap,
173 LP8860_EEPROM_UNLOCK,
174 LP8860_EEPROM_CODE_3);
175 if (ret) {
176 dev_err(&led->client->dev, "EEPROM Unlock failed\n");
177 goto out;
178 }
179 } else {
180 ret = regmap_write(led->regmap,
181 LP8860_EEPROM_UNLOCK,
182 LP8860_LOCK_EEPROM);
183 }
184
185out:
186 mutex_unlock(&led->lock);
187 return ret;
188}
189
190static int lp8860_fault_check(struct lp8860_led *led)
191{
192 int ret, fault;
193 unsigned int read_buf;
194
195 ret = regmap_read(led->regmap, LP8860_LED_FAULT, &read_buf);
196 if (ret)
197 goto out;
198
199 fault = read_buf;
200
201 ret = regmap_read(led->regmap, LP8860_FAULT, &read_buf);
202 if (ret)
203 goto out;
204
205 fault |= read_buf;
206
207 /* Attempt to clear any faults */
208 if (fault)
209 ret = regmap_write(led->regmap, LP8860_FAULT_CLEAR,
210 LP8860_CLEAR_FAULTS);
211out:
212 return ret;
213}
214
215static void lp8860_led_brightness_work(struct work_struct *work)
216{
217 struct lp8860_led *led = container_of(work, struct lp8860_led, work);
218 int ret;
219 int disp_brightness = led->brightness * 255;
220
221 mutex_lock(&led->lock);
222
223 ret = lp8860_fault_check(led);
224 if (ret) {
225 dev_err(&led->client->dev, "Cannot read/clear faults\n");
226 goto out;
227 }
228
229 ret = regmap_write(led->regmap, LP8860_DISP_CL1_BRT_MSB,
230 (disp_brightness & 0xff00) >> 8);
231 if (ret) {
232 dev_err(&led->client->dev, "Cannot write CL1 MSB\n");
233 goto out;
234 }
235
236 ret = regmap_write(led->regmap, LP8860_DISP_CL1_BRT_LSB,
237 disp_brightness & 0xff);
238 if (ret) {
239 dev_err(&led->client->dev, "Cannot write CL1 LSB\n");
240 goto out;
241 }
242out:
243 mutex_unlock(&led->lock);
244}
245
246static void lp8860_brightness_set(struct led_classdev *led_cdev,
247 enum led_brightness brt_val)
248{
249 struct lp8860_led *led =
250 container_of(led_cdev, struct lp8860_led, led_dev);
251
252 led->brightness = brt_val;
253 schedule_work(&led->work);
254}
255
256static int lp8860_init(struct lp8860_led *led)
257{
258 unsigned int read_buf;
259 int ret, i, reg_count;
260
261 if (led->enable_gpio)
262 gpiod_direction_output(led->enable_gpio, 1);
263
264 ret = lp8860_fault_check(led);
265 if (ret)
266 goto out;
267
268 ret = regmap_read(led->regmap, LP8860_STATUS, &read_buf);
269 if (ret)
270 goto out;
271
272 ret = lp8860_unlock_eeprom(led, LP8860_UNLOCK_EEPROM);
273 if (ret) {
274 dev_err(&led->client->dev, "Failed unlocking EEPROM\n");
275 goto out;
276 }
277
278 reg_count = ARRAY_SIZE(lp8860_eeprom_disp_regs) / sizeof(lp8860_eeprom_disp_regs[0]);
279 for (i = 0; i < reg_count; i++) {
280 ret = regmap_write(led->eeprom_regmap,
281 lp8860_eeprom_disp_regs[i].reg,
282 lp8860_eeprom_disp_regs[i].value);
283 if (ret) {
284 dev_err(&led->client->dev, "Failed writing EEPROM\n");
285 goto out;
286 }
287 }
288
289 ret = lp8860_unlock_eeprom(led, LP8860_LOCK_EEPROM);
290 if (ret)
291 goto out;
292
293 ret = regmap_write(led->regmap,
294 LP8860_EEPROM_CNTRL,
295 LP8860_PROGRAM_EEPROM);
296 if (ret)
297 dev_err(&led->client->dev, "Failed programming EEPROM\n");
298out:
299 if (ret)
300 if (led->enable_gpio)
301 gpiod_direction_output(led->enable_gpio, 0);
302 return ret;
303}
304
305static struct reg_default lp8860_reg_defs[] = {
306 { LP8860_DISP_CL1_BRT_MSB, 0x00},
307 { LP8860_DISP_CL1_BRT_LSB, 0x00},
308 { LP8860_DISP_CL1_CURR_MSB, 0x00},
309 { LP8860_DISP_CL1_CURR_LSB, 0x00},
310 { LP8860_CL2_BRT_MSB, 0x00},
311 { LP8860_CL2_BRT_LSB, 0x00},
312 { LP8860_CL2_CURRENT, 0x00},
313 { LP8860_CL3_BRT_MSB, 0x00},
314 { LP8860_CL3_BRT_LSB, 0x00},
315 { LP8860_CL3_CURRENT, 0x00},
316 { LP8860_CL4_BRT_MSB, 0x00},
317 { LP8860_CL4_BRT_LSB, 0x00},
318 { LP8860_CL4_CURRENT, 0x00},
319 { LP8860_CONFIG, 0x00},
320 { LP8860_FAULT_CLEAR, 0x00},
321 { LP8860_EEPROM_CNTRL, 0x80},
322 { LP8860_EEPROM_UNLOCK, 0x00},
323};
324
325static const struct regmap_config lp8860_regmap_config = {
326 .reg_bits = 8,
327 .val_bits = 8,
328
329 .max_register = LP8860_EEPROM_UNLOCK,
330 .reg_defaults = lp8860_reg_defs,
331 .num_reg_defaults = ARRAY_SIZE(lp8860_reg_defs),
332 .cache_type = REGCACHE_NONE,
333};
334
335static struct reg_default lp8860_eeprom_defs[] = {
336 { LP8860_EEPROM_REG_0, 0x00 },
337 { LP8860_EEPROM_REG_1, 0x00 },
338 { LP8860_EEPROM_REG_2, 0x00 },
339 { LP8860_EEPROM_REG_3, 0x00 },
340 { LP8860_EEPROM_REG_4, 0x00 },
341 { LP8860_EEPROM_REG_5, 0x00 },
342 { LP8860_EEPROM_REG_6, 0x00 },
343 { LP8860_EEPROM_REG_7, 0x00 },
344 { LP8860_EEPROM_REG_8, 0x00 },
345 { LP8860_EEPROM_REG_9, 0x00 },
346 { LP8860_EEPROM_REG_10, 0x00 },
347 { LP8860_EEPROM_REG_11, 0x00 },
348 { LP8860_EEPROM_REG_12, 0x00 },
349 { LP8860_EEPROM_REG_13, 0x00 },
350 { LP8860_EEPROM_REG_14, 0x00 },
351 { LP8860_EEPROM_REG_15, 0x00 },
352 { LP8860_EEPROM_REG_16, 0x00 },
353 { LP8860_EEPROM_REG_17, 0x00 },
354 { LP8860_EEPROM_REG_18, 0x00 },
355 { LP8860_EEPROM_REG_19, 0x00 },
356 { LP8860_EEPROM_REG_20, 0x00 },
357 { LP8860_EEPROM_REG_21, 0x00 },
358 { LP8860_EEPROM_REG_22, 0x00 },
359 { LP8860_EEPROM_REG_23, 0x00 },
360 { LP8860_EEPROM_REG_24, 0x00 },
361};
362
363static const struct regmap_config lp8860_eeprom_regmap_config = {
364 .reg_bits = 8,
365 .val_bits = 8,
366
367 .max_register = LP8860_EEPROM_REG_24,
368 .reg_defaults = lp8860_eeprom_defs,
369 .num_reg_defaults = ARRAY_SIZE(lp8860_eeprom_defs),
370 .cache_type = REGCACHE_NONE,
371};
372
373static int lp8860_probe(struct i2c_client *client,
374 const struct i2c_device_id *id)
375{
376 int ret;
377 struct lp8860_led *led;
378 struct device_node *np = client->dev.of_node;
379
380 led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL);
381 if (!led)
382 return -ENOMEM;
383
384 led->label = LP8860_DISP_LED_NAME;
385
386 if (client->dev.of_node) {
387 ret = of_property_read_string(np, "label", &led->label);
388 if (ret) {
389 dev_err(&client->dev, "Missing label in dt\n");
390 return -EINVAL;
391 }
392 }
393
394 led->enable_gpio = devm_gpiod_get(&client->dev, "enable");
395 if (IS_ERR(led->enable_gpio))
396 led->enable_gpio = NULL;
397 else
398 gpiod_direction_output(led->enable_gpio, 0);
399
400 led->regulator = devm_regulator_get(&client->dev, "vled");
401 if (IS_ERR(led->regulator))
402 led->regulator = NULL;
403
404 led->client = client;
405 led->led_dev.name = led->label;
406 led->led_dev.max_brightness = LED_FULL;
407 led->led_dev.brightness_set = lp8860_brightness_set;
408
409 mutex_init(&led->lock);
410 INIT_WORK(&led->work, lp8860_led_brightness_work);
411
412 i2c_set_clientdata(client, led);
413
414 led->regmap = devm_regmap_init_i2c(client, &lp8860_regmap_config);
415 if (IS_ERR(led->regmap)) {
416 ret = PTR_ERR(led->regmap);
417 dev_err(&client->dev, "Failed to allocate register map: %d\n",
418 ret);
419 return ret;
420 }
421
422 led->eeprom_regmap = devm_regmap_init_i2c(client, &lp8860_eeprom_regmap_config);
423 if (IS_ERR(led->eeprom_regmap)) {
424 ret = PTR_ERR(led->eeprom_regmap);
425 dev_err(&client->dev, "Failed to allocate register map: %d\n",
426 ret);
427 return ret;
428 }
429
430 ret = lp8860_init(led);
431 if (ret)
432 return ret;
433
434 ret = led_classdev_register(&client->dev, &led->led_dev);
435 if (ret) {
436 dev_err(&client->dev, "led register err: %d\n", ret);
437 return ret;
438 }
439
440 return 0;
441}
442
443static int lp8860_remove(struct i2c_client *client)
444{
445 struct lp8860_led *led = i2c_get_clientdata(client);
446 int ret;
447
448 led_classdev_unregister(&led->led_dev);
449 cancel_work_sync(&led->work);
450
451 if (led->enable_gpio)
452 gpiod_direction_output(led->enable_gpio, 0);
453
454 if (led->regulator) {
455 ret = regulator_disable(led->regulator);
456 if (ret)
457 dev_err(&led->client->dev,
458 "Failed to disable regulator\n");
459 }
460
461 return 0;
462}
463
464static const struct i2c_device_id lp8860_id[] = {
465 { "lp8860", 0 },
466 { }
467};
468MODULE_DEVICE_TABLE(i2c, lp8860_id);
469
470#ifdef CONFIG_OF
471static const struct of_device_id of_lp8860_leds_match[] = {
472 { .compatible = "ti,lp8860", },
473 {},
474};
475MODULE_DEVICE_TABLE(of, of_lp8860_leds_match);
476#endif
477
478static struct i2c_driver lp8860_driver = {
479 .driver = {
480 .name = "lp8860",
481 .of_match_table = of_match_ptr(of_lp8860_leds_match),
482 },
483 .probe = lp8860_probe,
484 .remove = lp8860_remove,
485 .id_table = lp8860_id,
486};
487module_i2c_driver(lp8860_driver);
488
489MODULE_DESCRIPTION("Texas Instruments LP8860 LED drvier");
490MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
491MODULE_LICENSE("GPL");