aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
authorKim Kyuwon <chammoru@gmail.com>2009-03-04 14:59:29 -0500
committerRichard Purdie <rpurdie@linux.intel.com>2009-04-06 11:06:26 -0400
commit0b56129be72c38179697b7441aacbe133d515ff9 (patch)
tree7f13b977bc999fb3d4904b8196e362236c5a5c77 /drivers/leds
parent95dc5768c9e9ce207319e17bcf7e28288c671d02 (diff)
leds: add BD2802GU LED driver
ROHM BD2802GU is a RGB LED controller attached to i2c bus and specifically engineered for decoration purposes. This RGB controller incorporates lighting patterns and illuminates. This driver is designed to minimize power consumption, so when there is no emitting LED, it enters to reset state. And because the BD2802GU has lots of features that can't be covered by the current LED framework, it provides Advanced Configuration Function(ADF) mode, so that user applications can set registers of BD2802GU directly. Here are basic usage examples : ; to turn on LED (not blink) $ echo 1 > /sys/class/leds/led1_R/brightness ; to blink LED $ echo timer > /sys/class/leds/led1_R/trigger $ echo 1 > /sys/class/leds/led1_R/delay_on $ echo 1 > /sys/class/leds/led1_R/delay_off ; to turn off LED $ echo 0 > /sys/class/leds/led1_R/brightness [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Kim Kyuwon <chammoru@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig7
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-bd2802.c765
3 files changed, 773 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index db84d8f616dc..b77baecd50ca 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -210,6 +210,13 @@ config LEDS_PWM
210 help 210 help
211 This option enables support for pwm driven LEDs 211 This option enables support for pwm driven LEDs
212 212
213config LEDS_BD2802
214 tristate "LED driver for BD2802 RGB LED"
215 depends on LEDS_CLASS && I2C
216 help
217 This option enables support for BD2802GU RGB LED driver chips
218 accessed via the I2C bus.
219
213comment "LED Triggers" 220comment "LED Triggers"
214 221
215config LEDS_TRIGGERS 222config LEDS_TRIGGERS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 291aea22bf0c..2d41c4dcf92f 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
6 6
7# LED Platform Drivers 7# LED Platform Drivers
8obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o 8obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
9obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
9obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o 10obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
10obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o 11obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
11obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o 12obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c
new file mode 100644
index 000000000000..4149ecb3a9b2
--- /dev/null
+++ b/drivers/leds/leds-bd2802.c
@@ -0,0 +1,765 @@
1/*
2 * leds-bd2802.c - RGB LED Driver
3 *
4 * Copyright (C) 2009 Samsung Electronics
5 * Kim Kyuwon <q1.kim@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
12 *
13 */
14
15#include <linux/module.h>
16#include <linux/i2c.h>
17#include <linux/gpio.h>
18#include <linux/delay.h>
19#include <linux/leds.h>
20#include <linux/leds-bd2802.h>
21
22
23#define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0))
24
25#define BD2802_LED_OFFSET 0xa
26#define BD2802_COLOR_OFFSET 0x3
27
28#define BD2802_REG_CLKSETUP 0x00
29#define BD2802_REG_CONTROL 0x01
30#define BD2802_REG_HOURSETUP 0x02
31#define BD2802_REG_CURRENT1SETUP 0x03
32#define BD2802_REG_CURRENT2SETUP 0x04
33#define BD2802_REG_WAVEPATTERN 0x05
34
35#define BD2802_CURRENT_032 0x10 /* 3.2mA */
36#define BD2802_CURRENT_000 0x00 /* 0.0mA */
37
38#define BD2802_PATTERN_FULL 0x07
39#define BD2802_PATTERN_HALF 0x03
40
41enum led_ids {
42 LED1,
43 LED2,
44 LED_NUM,
45};
46
47enum led_colors {
48 RED,
49 GREEN,
50 BLUE,
51};
52
53enum led_bits {
54 BD2802_OFF,
55 BD2802_BLINK,
56 BD2802_ON,
57};
58
59/*
60 * State '0' : 'off'
61 * State '1' : 'blink'
62 * State '2' : 'on'.
63 */
64struct led_state {
65 unsigned r:2;
66 unsigned g:2;
67 unsigned b:2;
68};
69
70struct bd2802_led {
71 struct bd2802_led_platform_data *pdata;
72 struct i2c_client *client;
73 struct rw_semaphore rwsem;
74 struct work_struct work;
75
76 struct led_state led[2];
77
78 /*
79 * Making led_classdev as array is not recommended, because array
80 * members prevent using 'container_of' macro. So repetitive works
81 * are needed.
82 */
83 struct led_classdev cdev_led1r;
84 struct led_classdev cdev_led1g;
85 struct led_classdev cdev_led1b;
86 struct led_classdev cdev_led2r;
87 struct led_classdev cdev_led2g;
88 struct led_classdev cdev_led2b;
89
90 /*
91 * Advanced Configuration Function(ADF) mode:
92 * In ADF mode, user can set registers of BD2802GU directly,
93 * therefore BD2802GU doesn't enter reset state.
94 */
95 int adf_on;
96
97 enum led_ids led_id;
98 enum led_colors color;
99 enum led_bits state;
100};
101
102
103/*--------------------------------------------------------------*/
104/* BD2802GU helper functions */
105/*--------------------------------------------------------------*/
106
107static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id,
108 enum led_colors color)
109{
110 switch (color) {
111 case RED:
112 return !led->led[id].r;
113 case GREEN:
114 return !led->led[id].g;
115 case BLUE:
116 return !led->led[id].b;
117 default:
118 dev_err(&led->client->dev, "%s: Invalid color\n", __func__);
119 return -EINVAL;
120 }
121}
122
123static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id)
124{
125 if (led->led[id].r || led->led[id].g || led->led[id].b)
126 return 0;
127
128 return 1;
129}
130
131static inline int bd2802_is_all_off(struct bd2802_led *led)
132{
133 int i;
134
135 for (i = 0; i < LED_NUM; i++)
136 if (!bd2802_is_led_off(led, i))
137 return 0;
138
139 return 1;
140}
141
142static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color)
143{
144 return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET;
145}
146
147static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color,
148 u8 reg_offset)
149{
150 return reg_offset + bd2802_get_base_offset(id, color);
151}
152
153
154/*--------------------------------------------------------------*/
155/* BD2802GU core functions */
156/*--------------------------------------------------------------*/
157
158static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val)
159{
160 int ret = i2c_smbus_write_byte_data(client, reg, val);
161 if (ret >= 0)
162 return 0;
163
164 dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
165 __func__, reg, val, ret);
166
167 return ret;
168}
169
170static void bd2802_update_state(struct bd2802_led *led, enum led_ids id,
171 enum led_colors color, enum led_bits led_bit)
172{
173 int i;
174 u8 value;
175
176 for (i = 0; i < LED_NUM; i++) {
177 if (i == id) {
178 switch (color) {
179 case RED:
180 led->led[i].r = led_bit;
181 break;
182 case GREEN:
183 led->led[i].g = led_bit;
184 break;
185 case BLUE:
186 led->led[i].b = led_bit;
187 break;
188 default:
189 dev_err(&led->client->dev,
190 "%s: Invalid color\n", __func__);
191 return;
192 }
193 }
194 }
195
196 if (led_bit == BD2802_BLINK || led_bit == BD2802_ON)
197 return;
198
199 if (!bd2802_is_led_off(led, id))
200 return;
201
202 if (bd2802_is_all_off(led) && !led->adf_on) {
203 gpio_set_value(led->pdata->reset_gpio, 0);
204 return;
205 }
206
207 /*
208 * In this case, other led is turned on, and current led is turned
209 * off. So set RGB LED Control register to stop the current RGB LED
210 */
211 value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1);
212 bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
213}
214
215static void bd2802_configure(struct bd2802_led *led)
216{
217 struct bd2802_led_platform_data *pdata = led->pdata;
218 u8 reg;
219
220 reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP);
221 bd2802_write_byte(led->client, reg, pdata->rgb_time);
222
223 reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP);
224 bd2802_write_byte(led->client, reg, pdata->rgb_time);
225}
226
227static void bd2802_reset_cancel(struct bd2802_led *led)
228{
229 gpio_set_value(led->pdata->reset_gpio, 1);
230 udelay(100);
231 bd2802_configure(led);
232}
233
234static void bd2802_enable(struct bd2802_led *led, enum led_ids id)
235{
236 enum led_ids other_led = (id == LED1) ? LED2 : LED1;
237 u8 value, other_led_on;
238
239 other_led_on = !bd2802_is_led_off(led, other_led);
240 if (id == LED1)
241 value = LED_CTL(other_led_on, 1);
242 else
243 value = LED_CTL(1 , other_led_on);
244
245 bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
246}
247
248static void bd2802_set_on(struct bd2802_led *led, enum led_ids id,
249 enum led_colors color)
250{
251 u8 reg;
252
253 if (bd2802_is_all_off(led) && !led->adf_on)
254 bd2802_reset_cancel(led);
255
256 reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
257 bd2802_write_byte(led->client, reg, BD2802_CURRENT_032);
258 reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
259 bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
260 reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
261 bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL);
262
263 bd2802_enable(led, id);
264 bd2802_update_state(led, id, color, BD2802_ON);
265}
266
267static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id,
268 enum led_colors color)
269{
270 u8 reg;
271
272 if (bd2802_is_all_off(led) && !led->adf_on)
273 bd2802_reset_cancel(led);
274
275 reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
276 bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
277 reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
278 bd2802_write_byte(led->client, reg, BD2802_CURRENT_032);
279 reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
280 bd2802_write_byte(led->client, reg, BD2802_PATTERN_HALF);
281
282 bd2802_enable(led, id);
283 bd2802_update_state(led, id, color, BD2802_BLINK);
284}
285
286static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id,
287 enum led_colors color, enum led_bits led_bit)
288{
289 if (led_bit == BD2802_OFF) {
290 dev_err(&led->client->dev,
291 "Only 'blink' and 'on' are allowed\n");
292 return;
293 }
294
295 if (led_bit == BD2802_BLINK)
296 bd2802_set_blink(led, id, color);
297 else
298 bd2802_set_on(led, id, color);
299}
300
301static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id,
302 enum led_colors color)
303{
304 u8 reg;
305
306 if (bd2802_is_rgb_off(led, id, color))
307 return;
308
309 reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
310 bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
311 reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
312 bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
313
314 bd2802_update_state(led, id, color, BD2802_OFF);
315}
316
317static void bd2802_restore_state(struct bd2802_led *led)
318{
319 int i;
320
321 for (i = 0; i < LED_NUM; i++) {
322 if (led->led[i].r)
323 bd2802_turn_on(led, i, RED, led->led[i].r);
324 if (led->led[i].g)
325 bd2802_turn_on(led, i, GREEN, led->led[i].g);
326 if (led->led[i].b)
327 bd2802_turn_on(led, i, BLUE, led->led[i].b);
328 }
329}
330
331#define BD2802_SET_REGISTER(reg_addr, reg_name) \
332static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \
333 struct device_attribute *attr, const char *buf, size_t count) \
334{ \
335 struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
336 unsigned long val; \
337 int ret; \
338 if (!count) \
339 return -EINVAL; \
340 ret = strict_strtoul(buf, 16, &val); \
341 if (ret) \
342 return ret; \
343 down_write(&led->rwsem); \
344 bd2802_write_byte(led->client, reg_addr, (u8) val); \
345 up_write(&led->rwsem); \
346 return count; \
347} \
348static struct device_attribute bd2802_reg##reg_addr##_attr = { \
349 .attr = {.name = reg_name, .mode = 0644, .owner = THIS_MODULE}, \
350 .store = bd2802_store_reg##reg_addr, \
351};
352
353BD2802_SET_REGISTER(0x00, "0x00");
354BD2802_SET_REGISTER(0x01, "0x01");
355BD2802_SET_REGISTER(0x02, "0x02");
356BD2802_SET_REGISTER(0x03, "0x03");
357BD2802_SET_REGISTER(0x04, "0x04");
358BD2802_SET_REGISTER(0x05, "0x05");
359BD2802_SET_REGISTER(0x06, "0x06");
360BD2802_SET_REGISTER(0x07, "0x07");
361BD2802_SET_REGISTER(0x08, "0x08");
362BD2802_SET_REGISTER(0x09, "0x09");
363BD2802_SET_REGISTER(0x0a, "0x0a");
364BD2802_SET_REGISTER(0x0b, "0x0b");
365BD2802_SET_REGISTER(0x0c, "0x0c");
366BD2802_SET_REGISTER(0x0d, "0x0d");
367BD2802_SET_REGISTER(0x0e, "0x0e");
368BD2802_SET_REGISTER(0x0f, "0x0f");
369BD2802_SET_REGISTER(0x10, "0x10");
370BD2802_SET_REGISTER(0x11, "0x11");
371BD2802_SET_REGISTER(0x12, "0x12");
372BD2802_SET_REGISTER(0x13, "0x13");
373BD2802_SET_REGISTER(0x14, "0x14");
374BD2802_SET_REGISTER(0x15, "0x15");
375
376static struct device_attribute *bd2802_addr_attributes[] = {
377 &bd2802_reg0x00_attr,
378 &bd2802_reg0x01_attr,
379 &bd2802_reg0x02_attr,
380 &bd2802_reg0x03_attr,
381 &bd2802_reg0x04_attr,
382 &bd2802_reg0x05_attr,
383 &bd2802_reg0x06_attr,
384 &bd2802_reg0x07_attr,
385 &bd2802_reg0x08_attr,
386 &bd2802_reg0x09_attr,
387 &bd2802_reg0x0a_attr,
388 &bd2802_reg0x0b_attr,
389 &bd2802_reg0x0c_attr,
390 &bd2802_reg0x0d_attr,
391 &bd2802_reg0x0e_attr,
392 &bd2802_reg0x0f_attr,
393 &bd2802_reg0x10_attr,
394 &bd2802_reg0x11_attr,
395 &bd2802_reg0x12_attr,
396 &bd2802_reg0x13_attr,
397 &bd2802_reg0x14_attr,
398 &bd2802_reg0x15_attr,
399};
400
401static void bd2802_enable_adv_conf(struct bd2802_led *led)
402{
403 int i, ret;
404
405 for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) {
406 ret = device_create_file(&led->client->dev,
407 bd2802_addr_attributes[i]);
408 if (ret) {
409 dev_err(&led->client->dev, "failed to sysfs file %s\n",
410 bd2802_addr_attributes[i]->attr.name);
411 goto failed_remove_files;
412 }
413 }
414
415 if (bd2802_is_all_off(led))
416 bd2802_reset_cancel(led);
417
418 led->adf_on = 1;
419
420 return;
421
422failed_remove_files:
423 for (i--; i >= 0; i--)
424 device_remove_file(&led->client->dev,
425 bd2802_addr_attributes[i]);
426}
427
428static void bd2802_disable_adv_conf(struct bd2802_led *led)
429{
430 int i;
431
432 for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++)
433 device_remove_file(&led->client->dev,
434 bd2802_addr_attributes[i]);
435
436 if (bd2802_is_all_off(led))
437 gpio_set_value(led->pdata->reset_gpio, 0);
438
439 led->adf_on = 0;
440}
441
442static ssize_t bd2802_show_adv_conf(struct device *dev,
443 struct device_attribute *attr, char *buf)
444{
445 struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
446 ssize_t ret;
447
448 down_read(&led->rwsem);
449 if (led->adf_on)
450 ret = sprintf(buf, "on\n");
451 else
452 ret = sprintf(buf, "off\n");
453 up_read(&led->rwsem);
454
455 return ret;
456}
457
458static ssize_t bd2802_store_adv_conf(struct device *dev,
459 struct device_attribute *attr, const char *buf, size_t count)
460{
461 struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
462
463 if (!count)
464 return -EINVAL;
465
466 down_write(&led->rwsem);
467 if (!led->adf_on && !strncmp(buf, "on", 2))
468 bd2802_enable_adv_conf(led);
469 else if (led->adf_on && !strncmp(buf, "off", 3))
470 bd2802_disable_adv_conf(led);
471 up_write(&led->rwsem);
472
473 return count;
474}
475
476static struct device_attribute bd2802_adv_conf_attr = {
477 .attr = {
478 .name = "advanced_configuration",
479 .mode = 0644,
480 .owner = THIS_MODULE
481 },
482 .show = bd2802_show_adv_conf,
483 .store = bd2802_store_adv_conf,
484};
485
486static void bd2802_led_work(struct work_struct *work)
487{
488 struct bd2802_led *led = container_of(work, struct bd2802_led, work);
489
490 if (led->state)
491 bd2802_turn_on(led, led->led_id, led->color, led->state);
492 else
493 bd2802_turn_off(led, led->led_id, led->color);
494}
495
496#define BD2802_CONTROL_RGBS(name, id, clr) \
497static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
498 enum led_brightness value) \
499{ \
500 struct bd2802_led *led = \
501 container_of(led_cdev, struct bd2802_led, cdev_##name); \
502 led->led_id = id; \
503 led->color = clr; \
504 if (value == LED_OFF) \
505 led->state = BD2802_OFF; \
506 else \
507 led->state = BD2802_ON; \
508 schedule_work(&led->work); \
509} \
510static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \
511 unsigned long *delay_on, unsigned long *delay_off) \
512{ \
513 struct bd2802_led *led = \
514 container_of(led_cdev, struct bd2802_led, cdev_##name); \
515 if (*delay_on == 0 || *delay_off == 0) \
516 return -EINVAL; \
517 led->led_id = id; \
518 led->color = clr; \
519 led->state = BD2802_BLINK; \
520 schedule_work(&led->work); \
521 return 0; \
522}
523
524BD2802_CONTROL_RGBS(led1r, LED1, RED);
525BD2802_CONTROL_RGBS(led1g, LED1, GREEN);
526BD2802_CONTROL_RGBS(led1b, LED1, BLUE);
527BD2802_CONTROL_RGBS(led2r, LED2, RED);
528BD2802_CONTROL_RGBS(led2g, LED2, GREEN);
529BD2802_CONTROL_RGBS(led2b, LED2, BLUE);
530
531static int bd2802_register_led_classdev(struct bd2802_led *led)
532{
533 int ret;
534
535 INIT_WORK(&led->work, bd2802_led_work);
536
537 led->cdev_led1r.name = "led1_R";
538 led->cdev_led1r.brightness = LED_OFF;
539 led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness;
540 led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
541 led->cdev_led1r.flags |= LED_CORE_SUSPENDRESUME;
542
543 ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
544 if (ret < 0) {
545 dev_err(&led->client->dev, "couldn't register LED %s\n",
546 led->cdev_led1r.name);
547 goto failed_unregister_led1_R;
548 }
549
550 led->cdev_led1g.name = "led1_G";
551 led->cdev_led1g.brightness = LED_OFF;
552 led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness;
553 led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
554 led->cdev_led1g.flags |= LED_CORE_SUSPENDRESUME;
555
556 ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
557 if (ret < 0) {
558 dev_err(&led->client->dev, "couldn't register LED %s\n",
559 led->cdev_led1g.name);
560 goto failed_unregister_led1_G;
561 }
562
563 led->cdev_led1b.name = "led1_B";
564 led->cdev_led1b.brightness = LED_OFF;
565 led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness;
566 led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
567 led->cdev_led1b.flags |= LED_CORE_SUSPENDRESUME;
568
569 ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
570 if (ret < 0) {
571 dev_err(&led->client->dev, "couldn't register LED %s\n",
572 led->cdev_led1b.name);
573 goto failed_unregister_led1_B;
574 }
575
576 led->cdev_led2r.name = "led2_R";
577 led->cdev_led2r.brightness = LED_OFF;
578 led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness;
579 led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
580 led->cdev_led2r.flags |= LED_CORE_SUSPENDRESUME;
581
582 ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
583 if (ret < 0) {
584 dev_err(&led->client->dev, "couldn't register LED %s\n",
585 led->cdev_led2r.name);
586 goto failed_unregister_led2_R;
587 }
588
589 led->cdev_led2g.name = "led2_G";
590 led->cdev_led2g.brightness = LED_OFF;
591 led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness;
592 led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
593 led->cdev_led2g.flags |= LED_CORE_SUSPENDRESUME;
594
595 ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
596 if (ret < 0) {
597 dev_err(&led->client->dev, "couldn't register LED %s\n",
598 led->cdev_led2g.name);
599 goto failed_unregister_led2_G;
600 }
601
602 led->cdev_led2b.name = "led2_B";
603 led->cdev_led2b.brightness = LED_OFF;
604 led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness;
605 led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
606 led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
607
608 ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);
609 if (ret < 0) {
610 dev_err(&led->client->dev, "couldn't register LED %s\n",
611 led->cdev_led2b.name);
612 goto failed_unregister_led2_B;
613 }
614
615 return 0;
616
617failed_unregister_led2_B:
618 led_classdev_unregister(&led->cdev_led2g);
619failed_unregister_led2_G:
620 led_classdev_unregister(&led->cdev_led2r);
621failed_unregister_led2_R:
622 led_classdev_unregister(&led->cdev_led1b);
623failed_unregister_led1_B:
624 led_classdev_unregister(&led->cdev_led1g);
625failed_unregister_led1_G:
626 led_classdev_unregister(&led->cdev_led1r);
627failed_unregister_led1_R:
628
629 return ret;
630}
631
632static void bd2802_unregister_led_classdev(struct bd2802_led *led)
633{
634 cancel_work_sync(&led->work);
635 led_classdev_unregister(&led->cdev_led1r);
636}
637
638static int __devinit bd2802_probe(struct i2c_client *client,
639 const struct i2c_device_id *id)
640{
641 struct bd2802_led *led;
642 struct bd2802_led_platform_data *pdata;
643 int ret;
644
645 led = kzalloc(sizeof(struct bd2802_led), GFP_KERNEL);
646 if (!led) {
647 dev_err(&client->dev, "failed to allocate driver data\n");
648 return -ENOMEM;
649 }
650
651 led->client = client;
652 pdata = led->pdata = client->dev.platform_data;
653 i2c_set_clientdata(client, led);
654
655 /* Configure RESET GPIO (L: RESET, H: RESET cancel) */
656 gpio_request(pdata->reset_gpio, "RGB_RESETB");
657 gpio_direction_output(pdata->reset_gpio, 1);
658
659 /* Tacss = min 0.1ms */
660 udelay(100);
661
662 /* Detect BD2802GU */
663 ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
664 if (ret < 0) {
665 dev_err(&client->dev, "failed to detect device\n");
666 goto failed_free;
667 } else
668 dev_info(&client->dev, "return 0x%02x\n", ret);
669
670 /* To save the power, reset BD2802 after detecting */
671 gpio_set_value(led->pdata->reset_gpio, 0);
672
673 init_rwsem(&led->rwsem);
674
675 ret = device_create_file(&client->dev, &bd2802_adv_conf_attr);
676 if (ret) {
677 dev_err(&client->dev, "failed to create sysfs file %s\n",
678 bd2802_adv_conf_attr.attr.name);
679 goto failed_free;
680 }
681
682 ret = bd2802_register_led_classdev(led);
683 if (ret < 0)
684 goto failed_unregister_dev_file;
685
686 return 0;
687
688failed_unregister_dev_file:
689 device_remove_file(&client->dev, &bd2802_adv_conf_attr);
690failed_free:
691 i2c_set_clientdata(client, NULL);
692 kfree(led);
693
694 return ret;
695}
696
697static int __exit bd2802_remove(struct i2c_client *client)
698{
699 struct bd2802_led *led = i2c_get_clientdata(client);
700
701 bd2802_unregister_led_classdev(led);
702 gpio_set_value(led->pdata->reset_gpio, 0);
703 if (led->adf_on)
704 bd2802_disable_adv_conf(led);
705 device_remove_file(&client->dev, &bd2802_adv_conf_attr);
706 i2c_set_clientdata(client, NULL);
707 kfree(led);
708
709 return 0;
710}
711
712static int bd2802_suspend(struct i2c_client *client, pm_message_t mesg)
713{
714 struct bd2802_led *led = i2c_get_clientdata(client);
715
716 gpio_set_value(led->pdata->reset_gpio, 0);
717
718 return 0;
719}
720
721static int bd2802_resume(struct i2c_client *client)
722{
723 struct bd2802_led *led = i2c_get_clientdata(client);
724
725 if (!bd2802_is_all_off(led) || led->adf_on) {
726 gpio_set_value(led->pdata->reset_gpio, 1);
727 udelay(100);
728 bd2802_restore_state(led);
729 }
730
731 return 0;
732}
733
734static const struct i2c_device_id bd2802_id[] = {
735 { "BD2802", 0 },
736 { }
737};
738MODULE_DEVICE_TABLE(i2c, bd2802_id);
739
740static struct i2c_driver bd2802_i2c_driver = {
741 .driver = {
742 .name = "BD2802",
743 },
744 .probe = bd2802_probe,
745 .remove = __exit_p(bd2802_remove),
746 .suspend = bd2802_suspend,
747 .resume = bd2802_resume,
748 .id_table = bd2802_id,
749};
750
751static int __init bd2802_init(void)
752{
753 return i2c_add_driver(&bd2802_i2c_driver);
754}
755module_init(bd2802_init);
756
757static void __exit bd2802_exit(void)
758{
759 i2c_del_driver(&bd2802_i2c_driver);
760}
761module_exit(bd2802_exit);
762
763MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
764MODULE_DESCRIPTION("BD2802 LED driver");
765MODULE_LICENSE("GPL");