Root/drivers/leds/leds-bd2802.c

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#include <linux/slab.h>
22#include <linux/pm.h>
23
24#define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0))
25
26#define BD2802_LED_OFFSET 0xa
27#define BD2802_COLOR_OFFSET 0x3
28
29#define BD2802_REG_CLKSETUP 0x00
30#define BD2802_REG_CONTROL 0x01
31#define BD2802_REG_HOURSETUP 0x02
32#define BD2802_REG_CURRENT1SETUP 0x03
33#define BD2802_REG_CURRENT2SETUP 0x04
34#define BD2802_REG_WAVEPATTERN 0x05
35
36#define BD2802_CURRENT_032 0x10 /* 3.2mA */
37#define BD2802_CURRENT_000 0x00 /* 0.0mA */
38
39#define BD2802_PATTERN_FULL 0x07
40#define BD2802_PATTERN_HALF 0x03
41
42enum led_ids {
43    LED1,
44    LED2,
45    LED_NUM,
46};
47
48enum led_colors {
49    RED,
50    GREEN,
51    BLUE,
52};
53
54enum led_bits {
55    BD2802_OFF,
56    BD2802_BLINK,
57    BD2802_ON,
58};
59
60/*
61 * State '0' : 'off'
62 * State '1' : 'blink'
63 * State '2' : 'on'.
64 */
65struct led_state {
66    unsigned r:2;
67    unsigned g:2;
68    unsigned b:2;
69};
70
71struct bd2802_led {
72    struct bd2802_led_platform_data *pdata;
73    struct i2c_client *client;
74    struct rw_semaphore rwsem;
75    struct work_struct work;
76
77    struct led_state led[2];
78
79    /*
80     * Making led_classdev as array is not recommended, because array
81     * members prevent using 'container_of' macro. So repetitive works
82     * are needed.
83     */
84    struct led_classdev cdev_led1r;
85    struct led_classdev cdev_led1g;
86    struct led_classdev cdev_led1b;
87    struct led_classdev cdev_led2r;
88    struct led_classdev cdev_led2g;
89    struct led_classdev cdev_led2b;
90
91    /*
92     * Advanced Configuration Function(ADF) mode:
93     * In ADF mode, user can set registers of BD2802GU directly,
94     * therefore BD2802GU doesn't enter reset state.
95     */
96    int adf_on;
97
98    enum led_ids led_id;
99    enum led_colors color;
100    enum led_bits state;
101
102    /* General attributes of RGB LEDs */
103    int wave_pattern;
104    int rgb_current;
105};
106
107
108/*--------------------------------------------------------------*/
109/* BD2802GU helper functions */
110/*--------------------------------------------------------------*/
111
112static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id,
113                            enum led_colors color)
114{
115    switch (color) {
116    case RED:
117        return !led->led[id].r;
118    case GREEN:
119        return !led->led[id].g;
120    case BLUE:
121        return !led->led[id].b;
122    default:
123        dev_err(&led->client->dev, "%s: Invalid color\n", __func__);
124        return -EINVAL;
125    }
126}
127
128static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id)
129{
130    if (led->led[id].r || led->led[id].g || led->led[id].b)
131        return 0;
132
133    return 1;
134}
135
136static inline int bd2802_is_all_off(struct bd2802_led *led)
137{
138    int i;
139
140    for (i = 0; i < LED_NUM; i++)
141        if (!bd2802_is_led_off(led, i))
142            return 0;
143
144    return 1;
145}
146
147static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color)
148{
149    return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET;
150}
151
152static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color,
153                                u8 reg_offset)
154{
155    return reg_offset + bd2802_get_base_offset(id, color);
156}
157
158
159/*--------------------------------------------------------------*/
160/* BD2802GU core functions */
161/*--------------------------------------------------------------*/
162
163static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val)
164{
165    int ret = i2c_smbus_write_byte_data(client, reg, val);
166    if (ret >= 0)
167        return 0;
168
169    dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
170                        __func__, reg, val, ret);
171
172    return ret;
173}
174
175static void bd2802_update_state(struct bd2802_led *led, enum led_ids id,
176                enum led_colors color, enum led_bits led_bit)
177{
178    int i;
179    u8 value;
180
181    for (i = 0; i < LED_NUM; i++) {
182        if (i == id) {
183            switch (color) {
184            case RED:
185                led->led[i].r = led_bit;
186                break;
187            case GREEN:
188                led->led[i].g = led_bit;
189                break;
190            case BLUE:
191                led->led[i].b = led_bit;
192                break;
193            default:
194                dev_err(&led->client->dev,
195                    "%s: Invalid color\n", __func__);
196                return;
197            }
198        }
199    }
200
201    if (led_bit == BD2802_BLINK || led_bit == BD2802_ON)
202        return;
203
204    if (!bd2802_is_led_off(led, id))
205        return;
206
207    if (bd2802_is_all_off(led) && !led->adf_on) {
208        gpio_set_value(led->pdata->reset_gpio, 0);
209        return;
210    }
211
212    /*
213     * In this case, other led is turned on, and current led is turned
214     * off. So set RGB LED Control register to stop the current RGB LED
215     */
216    value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1);
217    bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
218}
219
220static void bd2802_configure(struct bd2802_led *led)
221{
222    struct bd2802_led_platform_data *pdata = led->pdata;
223    u8 reg;
224
225    reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP);
226    bd2802_write_byte(led->client, reg, pdata->rgb_time);
227
228    reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP);
229    bd2802_write_byte(led->client, reg, pdata->rgb_time);
230}
231
232static void bd2802_reset_cancel(struct bd2802_led *led)
233{
234    gpio_set_value(led->pdata->reset_gpio, 1);
235    udelay(100);
236    bd2802_configure(led);
237}
238
239static void bd2802_enable(struct bd2802_led *led, enum led_ids id)
240{
241    enum led_ids other_led = (id == LED1) ? LED2 : LED1;
242    u8 value, other_led_on;
243
244    other_led_on = !bd2802_is_led_off(led, other_led);
245    if (id == LED1)
246        value = LED_CTL(other_led_on, 1);
247    else
248        value = LED_CTL(1 , other_led_on);
249
250    bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
251}
252
253static void bd2802_set_on(struct bd2802_led *led, enum led_ids id,
254                            enum led_colors color)
255{
256    u8 reg;
257
258    if (bd2802_is_all_off(led) && !led->adf_on)
259        bd2802_reset_cancel(led);
260
261    reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
262    bd2802_write_byte(led->client, reg, led->rgb_current);
263    reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
264    bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
265    reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
266    bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL);
267
268    bd2802_enable(led, id);
269    bd2802_update_state(led, id, color, BD2802_ON);
270}
271
272static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id,
273                            enum led_colors color)
274{
275    u8 reg;
276
277    if (bd2802_is_all_off(led) && !led->adf_on)
278        bd2802_reset_cancel(led);
279
280    reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
281    bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
282    reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
283    bd2802_write_byte(led->client, reg, led->rgb_current);
284    reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
285    bd2802_write_byte(led->client, reg, led->wave_pattern);
286
287    bd2802_enable(led, id);
288    bd2802_update_state(led, id, color, BD2802_BLINK);
289}
290
291static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id,
292                enum led_colors color, enum led_bits led_bit)
293{
294    if (led_bit == BD2802_OFF) {
295        dev_err(&led->client->dev,
296                    "Only 'blink' and 'on' are allowed\n");
297        return;
298    }
299
300    if (led_bit == BD2802_BLINK)
301        bd2802_set_blink(led, id, color);
302    else
303        bd2802_set_on(led, id, color);
304}
305
306static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id,
307                            enum led_colors color)
308{
309    u8 reg;
310
311    if (bd2802_is_rgb_off(led, id, color))
312        return;
313
314    reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
315    bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
316    reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
317    bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
318
319    bd2802_update_state(led, id, color, BD2802_OFF);
320}
321
322#define BD2802_SET_REGISTER(reg_addr, reg_name) \
323static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \
324    struct device_attribute *attr, const char *buf, size_t count) \
325{ \
326    struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
327    unsigned long val; \
328    int ret; \
329    if (!count) \
330        return -EINVAL; \
331    ret = kstrtoul(buf, 16, &val); \
332    if (ret) \
333        return ret; \
334    down_write(&led->rwsem); \
335    bd2802_write_byte(led->client, reg_addr, (u8) val); \
336    up_write(&led->rwsem); \
337    return count; \
338} \
339static struct device_attribute bd2802_reg##reg_addr##_attr = { \
340    .attr = {.name = reg_name, .mode = 0644}, \
341    .store = bd2802_store_reg##reg_addr, \
342};
343
344BD2802_SET_REGISTER(0x00, "0x00");
345BD2802_SET_REGISTER(0x01, "0x01");
346BD2802_SET_REGISTER(0x02, "0x02");
347BD2802_SET_REGISTER(0x03, "0x03");
348BD2802_SET_REGISTER(0x04, "0x04");
349BD2802_SET_REGISTER(0x05, "0x05");
350BD2802_SET_REGISTER(0x06, "0x06");
351BD2802_SET_REGISTER(0x07, "0x07");
352BD2802_SET_REGISTER(0x08, "0x08");
353BD2802_SET_REGISTER(0x09, "0x09");
354BD2802_SET_REGISTER(0x0a, "0x0a");
355BD2802_SET_REGISTER(0x0b, "0x0b");
356BD2802_SET_REGISTER(0x0c, "0x0c");
357BD2802_SET_REGISTER(0x0d, "0x0d");
358BD2802_SET_REGISTER(0x0e, "0x0e");
359BD2802_SET_REGISTER(0x0f, "0x0f");
360BD2802_SET_REGISTER(0x10, "0x10");
361BD2802_SET_REGISTER(0x11, "0x11");
362BD2802_SET_REGISTER(0x12, "0x12");
363BD2802_SET_REGISTER(0x13, "0x13");
364BD2802_SET_REGISTER(0x14, "0x14");
365BD2802_SET_REGISTER(0x15, "0x15");
366
367static struct device_attribute *bd2802_addr_attributes[] = {
368    &bd2802_reg0x00_attr,
369    &bd2802_reg0x01_attr,
370    &bd2802_reg0x02_attr,
371    &bd2802_reg0x03_attr,
372    &bd2802_reg0x04_attr,
373    &bd2802_reg0x05_attr,
374    &bd2802_reg0x06_attr,
375    &bd2802_reg0x07_attr,
376    &bd2802_reg0x08_attr,
377    &bd2802_reg0x09_attr,
378    &bd2802_reg0x0a_attr,
379    &bd2802_reg0x0b_attr,
380    &bd2802_reg0x0c_attr,
381    &bd2802_reg0x0d_attr,
382    &bd2802_reg0x0e_attr,
383    &bd2802_reg0x0f_attr,
384    &bd2802_reg0x10_attr,
385    &bd2802_reg0x11_attr,
386    &bd2802_reg0x12_attr,
387    &bd2802_reg0x13_attr,
388    &bd2802_reg0x14_attr,
389    &bd2802_reg0x15_attr,
390};
391
392static void bd2802_enable_adv_conf(struct bd2802_led *led)
393{
394    int i, ret;
395
396    for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) {
397        ret = device_create_file(&led->client->dev,
398                        bd2802_addr_attributes[i]);
399        if (ret) {
400            dev_err(&led->client->dev, "failed: sysfs file %s\n",
401                    bd2802_addr_attributes[i]->attr.name);
402            goto failed_remove_files;
403        }
404    }
405
406    if (bd2802_is_all_off(led))
407        bd2802_reset_cancel(led);
408
409    led->adf_on = 1;
410
411    return;
412
413failed_remove_files:
414    for (i--; i >= 0; i--)
415        device_remove_file(&led->client->dev,
416                        bd2802_addr_attributes[i]);
417}
418
419static void bd2802_disable_adv_conf(struct bd2802_led *led)
420{
421    int i;
422
423    for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++)
424        device_remove_file(&led->client->dev,
425                        bd2802_addr_attributes[i]);
426
427    if (bd2802_is_all_off(led))
428        gpio_set_value(led->pdata->reset_gpio, 0);
429
430    led->adf_on = 0;
431}
432
433static ssize_t bd2802_show_adv_conf(struct device *dev,
434    struct device_attribute *attr, char *buf)
435{
436    struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
437    ssize_t ret;
438
439    down_read(&led->rwsem);
440    if (led->adf_on)
441        ret = sprintf(buf, "on\n");
442    else
443        ret = sprintf(buf, "off\n");
444    up_read(&led->rwsem);
445
446    return ret;
447}
448
449static ssize_t bd2802_store_adv_conf(struct device *dev,
450    struct device_attribute *attr, const char *buf, size_t count)
451{
452    struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
453
454    if (!count)
455        return -EINVAL;
456
457    down_write(&led->rwsem);
458    if (!led->adf_on && !strncmp(buf, "on", 2))
459        bd2802_enable_adv_conf(led);
460    else if (led->adf_on && !strncmp(buf, "off", 3))
461        bd2802_disable_adv_conf(led);
462    up_write(&led->rwsem);
463
464    return count;
465}
466
467static struct device_attribute bd2802_adv_conf_attr = {
468    .attr = {
469        .name = "advanced_configuration",
470        .mode = 0644,
471    },
472    .show = bd2802_show_adv_conf,
473    .store = bd2802_store_adv_conf,
474};
475
476#define BD2802_CONTROL_ATTR(attr_name, name_str) \
477static ssize_t bd2802_show_##attr_name(struct device *dev, \
478    struct device_attribute *attr, char *buf) \
479{ \
480    struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
481    ssize_t ret; \
482    down_read(&led->rwsem); \
483    ret = sprintf(buf, "0x%02x\n", led->attr_name); \
484    up_read(&led->rwsem); \
485    return ret; \
486} \
487static ssize_t bd2802_store_##attr_name(struct device *dev, \
488    struct device_attribute *attr, const char *buf, size_t count) \
489{ \
490    struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
491    unsigned long val; \
492    int ret; \
493    if (!count) \
494        return -EINVAL; \
495    ret = kstrtoul(buf, 16, &val); \
496    if (ret) \
497        return ret; \
498    down_write(&led->rwsem); \
499    led->attr_name = val; \
500    up_write(&led->rwsem); \
501    return count; \
502} \
503static struct device_attribute bd2802_##attr_name##_attr = { \
504    .attr = { \
505        .name = name_str, \
506        .mode = 0644, \
507    }, \
508    .show = bd2802_show_##attr_name, \
509    .store = bd2802_store_##attr_name, \
510};
511
512BD2802_CONTROL_ATTR(wave_pattern, "wave_pattern");
513BD2802_CONTROL_ATTR(rgb_current, "rgb_current");
514
515static struct device_attribute *bd2802_attributes[] = {
516    &bd2802_adv_conf_attr,
517    &bd2802_wave_pattern_attr,
518    &bd2802_rgb_current_attr,
519};
520
521static void bd2802_led_work(struct work_struct *work)
522{
523    struct bd2802_led *led = container_of(work, struct bd2802_led, work);
524
525    if (led->state)
526        bd2802_turn_on(led, led->led_id, led->color, led->state);
527    else
528        bd2802_turn_off(led, led->led_id, led->color);
529}
530
531#define BD2802_CONTROL_RGBS(name, id, clr) \
532static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
533                    enum led_brightness value) \
534{ \
535    struct bd2802_led *led = \
536        container_of(led_cdev, struct bd2802_led, cdev_##name); \
537    led->led_id = id; \
538    led->color = clr; \
539    if (value == LED_OFF) \
540        led->state = BD2802_OFF; \
541    else \
542        led->state = BD2802_ON; \
543    schedule_work(&led->work); \
544} \
545static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \
546        unsigned long *delay_on, unsigned long *delay_off) \
547{ \
548    struct bd2802_led *led = \
549        container_of(led_cdev, struct bd2802_led, cdev_##name); \
550    if (*delay_on == 0 || *delay_off == 0) \
551        return -EINVAL; \
552    led->led_id = id; \
553    led->color = clr; \
554    led->state = BD2802_BLINK; \
555    schedule_work(&led->work); \
556    return 0; \
557}
558
559BD2802_CONTROL_RGBS(led1r, LED1, RED);
560BD2802_CONTROL_RGBS(led1g, LED1, GREEN);
561BD2802_CONTROL_RGBS(led1b, LED1, BLUE);
562BD2802_CONTROL_RGBS(led2r, LED2, RED);
563BD2802_CONTROL_RGBS(led2g, LED2, GREEN);
564BD2802_CONTROL_RGBS(led2b, LED2, BLUE);
565
566static int bd2802_register_led_classdev(struct bd2802_led *led)
567{
568    int ret;
569
570    INIT_WORK(&led->work, bd2802_led_work);
571
572    led->cdev_led1r.name = "led1_R";
573    led->cdev_led1r.brightness = LED_OFF;
574    led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness;
575    led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
576
577    ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
578    if (ret < 0) {
579        dev_err(&led->client->dev, "couldn't register LED %s\n",
580                            led->cdev_led1r.name);
581        goto failed_unregister_led1_R;
582    }
583
584    led->cdev_led1g.name = "led1_G";
585    led->cdev_led1g.brightness = LED_OFF;
586    led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness;
587    led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
588
589    ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
590    if (ret < 0) {
591        dev_err(&led->client->dev, "couldn't register LED %s\n",
592                            led->cdev_led1g.name);
593        goto failed_unregister_led1_G;
594    }
595
596    led->cdev_led1b.name = "led1_B";
597    led->cdev_led1b.brightness = LED_OFF;
598    led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness;
599    led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
600
601    ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
602    if (ret < 0) {
603        dev_err(&led->client->dev, "couldn't register LED %s\n",
604                            led->cdev_led1b.name);
605        goto failed_unregister_led1_B;
606    }
607
608    led->cdev_led2r.name = "led2_R";
609    led->cdev_led2r.brightness = LED_OFF;
610    led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness;
611    led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
612
613    ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
614    if (ret < 0) {
615        dev_err(&led->client->dev, "couldn't register LED %s\n",
616                            led->cdev_led2r.name);
617        goto failed_unregister_led2_R;
618    }
619
620    led->cdev_led2g.name = "led2_G";
621    led->cdev_led2g.brightness = LED_OFF;
622    led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness;
623    led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
624
625    ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
626    if (ret < 0) {
627        dev_err(&led->client->dev, "couldn't register LED %s\n",
628                            led->cdev_led2g.name);
629        goto failed_unregister_led2_G;
630    }
631
632    led->cdev_led2b.name = "led2_B";
633    led->cdev_led2b.brightness = LED_OFF;
634    led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness;
635    led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
636    led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
637
638    ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);
639    if (ret < 0) {
640        dev_err(&led->client->dev, "couldn't register LED %s\n",
641                            led->cdev_led2b.name);
642        goto failed_unregister_led2_B;
643    }
644
645    return 0;
646
647failed_unregister_led2_B:
648    led_classdev_unregister(&led->cdev_led2g);
649failed_unregister_led2_G:
650    led_classdev_unregister(&led->cdev_led2r);
651failed_unregister_led2_R:
652    led_classdev_unregister(&led->cdev_led1b);
653failed_unregister_led1_B:
654    led_classdev_unregister(&led->cdev_led1g);
655failed_unregister_led1_G:
656    led_classdev_unregister(&led->cdev_led1r);
657failed_unregister_led1_R:
658
659    return ret;
660}
661
662static void bd2802_unregister_led_classdev(struct bd2802_led *led)
663{
664    cancel_work_sync(&led->work);
665    led_classdev_unregister(&led->cdev_led2b);
666    led_classdev_unregister(&led->cdev_led2g);
667    led_classdev_unregister(&led->cdev_led2r);
668    led_classdev_unregister(&led->cdev_led1b);
669    led_classdev_unregister(&led->cdev_led1g);
670    led_classdev_unregister(&led->cdev_led1r);
671}
672
673static int bd2802_probe(struct i2c_client *client,
674            const struct i2c_device_id *id)
675{
676    struct bd2802_led *led;
677    struct bd2802_led_platform_data *pdata;
678    int ret, i;
679
680    led = devm_kzalloc(&client->dev, sizeof(struct bd2802_led), GFP_KERNEL);
681    if (!led) {
682        dev_err(&client->dev, "failed to allocate driver data\n");
683        return -ENOMEM;
684    }
685
686    led->client = client;
687    pdata = led->pdata = client->dev.platform_data;
688    i2c_set_clientdata(client, led);
689
690    /* Configure RESET GPIO (L: RESET, H: RESET cancel) */
691    gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH, "RGB_RESETB");
692
693    /* Tacss = min 0.1ms */
694    udelay(100);
695
696    /* Detect BD2802GU */
697    ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
698    if (ret < 0) {
699        dev_err(&client->dev, "failed to detect device\n");
700        return ret;
701    } else
702        dev_info(&client->dev, "return 0x%02x\n", ret);
703
704    /* To save the power, reset BD2802 after detecting */
705    gpio_set_value(led->pdata->reset_gpio, 0);
706
707    /* Default attributes */
708    led->wave_pattern = BD2802_PATTERN_HALF;
709    led->rgb_current = BD2802_CURRENT_032;
710
711    init_rwsem(&led->rwsem);
712
713    for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) {
714        ret = device_create_file(&led->client->dev,
715                        bd2802_attributes[i]);
716        if (ret) {
717            dev_err(&led->client->dev, "failed: sysfs file %s\n",
718                    bd2802_attributes[i]->attr.name);
719            goto failed_unregister_dev_file;
720        }
721    }
722
723    ret = bd2802_register_led_classdev(led);
724    if (ret < 0)
725        goto failed_unregister_dev_file;
726
727    return 0;
728
729failed_unregister_dev_file:
730    for (i--; i >= 0; i--)
731        device_remove_file(&led->client->dev, bd2802_attributes[i]);
732    return ret;
733}
734
735static int __exit bd2802_remove(struct i2c_client *client)
736{
737    struct bd2802_led *led = i2c_get_clientdata(client);
738    int i;
739
740    gpio_set_value(led->pdata->reset_gpio, 0);
741    bd2802_unregister_led_classdev(led);
742    if (led->adf_on)
743        bd2802_disable_adv_conf(led);
744    for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++)
745        device_remove_file(&led->client->dev, bd2802_attributes[i]);
746
747    return 0;
748}
749
750#ifdef CONFIG_PM
751
752static void bd2802_restore_state(struct bd2802_led *led)
753{
754    int i;
755
756    for (i = 0; i < LED_NUM; i++) {
757        if (led->led[i].r)
758            bd2802_turn_on(led, i, RED, led->led[i].r);
759        if (led->led[i].g)
760            bd2802_turn_on(led, i, GREEN, led->led[i].g);
761        if (led->led[i].b)
762            bd2802_turn_on(led, i, BLUE, led->led[i].b);
763    }
764}
765
766static int bd2802_suspend(struct device *dev)
767{
768    struct i2c_client *client = to_i2c_client(dev);
769    struct bd2802_led *led = i2c_get_clientdata(client);
770
771    gpio_set_value(led->pdata->reset_gpio, 0);
772
773    return 0;
774}
775
776static int bd2802_resume(struct device *dev)
777{
778    struct i2c_client *client = to_i2c_client(dev);
779    struct bd2802_led *led = i2c_get_clientdata(client);
780
781    if (!bd2802_is_all_off(led) || led->adf_on) {
782        bd2802_reset_cancel(led);
783        bd2802_restore_state(led);
784    }
785
786    return 0;
787}
788
789static SIMPLE_DEV_PM_OPS(bd2802_pm, bd2802_suspend, bd2802_resume);
790#define BD2802_PM (&bd2802_pm)
791#else /* CONFIG_PM */
792#define BD2802_PM NULL
793#endif
794
795static const struct i2c_device_id bd2802_id[] = {
796    { "BD2802", 0 },
797    { }
798};
799MODULE_DEVICE_TABLE(i2c, bd2802_id);
800
801static struct i2c_driver bd2802_i2c_driver = {
802    .driver = {
803        .name = "BD2802",
804        .pm = BD2802_PM,
805    },
806    .probe = bd2802_probe,
807    .remove = __exit_p(bd2802_remove),
808    .id_table = bd2802_id,
809};
810
811module_i2c_driver(bd2802_i2c_driver);
812
813MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
814MODULE_DESCRIPTION("BD2802 LED driver");
815MODULE_LICENSE("GPL v2");
816

Archive Download this file



interactive