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