linux/drivers/video/backlight/adp8870_bl.c
<<
>>
Prefs
   1/*
   2 * Backlight driver for Analog Devices ADP8870 Backlight Devices
   3 *
   4 * Copyright 2009-2011 Analog Devices Inc.
   5 *
   6 * Licensed under the GPL-2 or later.
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/init.h>
  11#include <linux/errno.h>
  12#include <linux/pm.h>
  13#include <linux/platform_device.h>
  14#include <linux/i2c.h>
  15#include <linux/fb.h>
  16#include <linux/backlight.h>
  17#include <linux/leds.h>
  18#include <linux/workqueue.h>
  19#include <linux/slab.h>
  20
  21#include <linux/i2c/adp8870.h>
  22#define ADP8870_EXT_FEATURES
  23#define ADP8870_USE_LEDS
  24
  25
  26#define ADP8870_MFDVID  0x00  /* Manufacturer and device ID */
  27#define ADP8870_MDCR    0x01  /* Device mode and status */
  28#define ADP8870_INT_STAT 0x02  /* Interrupts status */
  29#define ADP8870_INT_EN  0x03  /* Interrupts enable */
  30#define ADP8870_CFGR    0x04  /* Configuration register */
  31#define ADP8870_BLSEL   0x05  /* Sink enable backlight or independent */
  32#define ADP8870_PWMLED  0x06  /* PWM Enable Selection Register */
  33#define ADP8870_BLOFF   0x07  /* Backlight off timeout */
  34#define ADP8870_BLDIM   0x08  /* Backlight dim timeout */
  35#define ADP8870_BLFR    0x09  /* Backlight fade in and out rates */
  36#define ADP8870_BLMX1   0x0A  /* Backlight (Brightness Level 1-daylight) maximum current */
  37#define ADP8870_BLDM1   0x0B  /* Backlight (Brightness Level 1-daylight) dim current */
  38#define ADP8870_BLMX2   0x0C  /* Backlight (Brightness Level 2-bright) maximum current */
  39#define ADP8870_BLDM2   0x0D  /* Backlight (Brightness Level 2-bright) dim current */
  40#define ADP8870_BLMX3   0x0E  /* Backlight (Brightness Level 3-office) maximum current */
  41#define ADP8870_BLDM3   0x0F  /* Backlight (Brightness Level 3-office) dim current */
  42#define ADP8870_BLMX4   0x10  /* Backlight (Brightness Level 4-indoor) maximum current */
  43#define ADP8870_BLDM4   0x11  /* Backlight (Brightness Level 4-indoor) dim current */
  44#define ADP8870_BLMX5   0x12  /* Backlight (Brightness Level 5-dark) maximum current */
  45#define ADP8870_BLDM5   0x13  /* Backlight (Brightness Level 5-dark) dim current */
  46#define ADP8870_ISCLAW  0x1A  /* Independent sink current fade law register */
  47#define ADP8870_ISCC    0x1B  /* Independent sink current control register */
  48#define ADP8870_ISCT1   0x1C  /* Independent Sink Current Timer Register LED[7:5] */
  49#define ADP8870_ISCT2   0x1D  /* Independent Sink Current Timer Register LED[4:1] */
  50#define ADP8870_ISCF    0x1E  /* Independent sink current fade register */
  51#define ADP8870_ISC1    0x1F  /* Independent Sink Current LED1 */
  52#define ADP8870_ISC2    0x20  /* Independent Sink Current LED2 */
  53#define ADP8870_ISC3    0x21  /* Independent Sink Current LED3 */
  54#define ADP8870_ISC4    0x22  /* Independent Sink Current LED4 */
  55#define ADP8870_ISC5    0x23  /* Independent Sink Current LED5 */
  56#define ADP8870_ISC6    0x24  /* Independent Sink Current LED6 */
  57#define ADP8870_ISC7    0x25  /* Independent Sink Current LED7 (Brightness Level 1-daylight) */
  58#define ADP8870_ISC7_L2 0x26  /* Independent Sink Current LED7 (Brightness Level 2-bright) */
  59#define ADP8870_ISC7_L3 0x27  /* Independent Sink Current LED7 (Brightness Level 3-office) */
  60#define ADP8870_ISC7_L4 0x28  /* Independent Sink Current LED7 (Brightness Level 4-indoor) */
  61#define ADP8870_ISC7_L5 0x29  /* Independent Sink Current LED7 (Brightness Level 5-dark) */
  62#define ADP8870_CMP_CTL 0x2D  /* ALS Comparator Control Register */
  63#define ADP8870_ALS1_EN 0x2E  /* Main ALS comparator level enable */
  64#define ADP8870_ALS2_EN 0x2F  /* Second ALS comparator level enable */
  65#define ADP8870_ALS1_STAT 0x30  /* Main ALS Comparator Status Register */
  66#define ADP8870_ALS2_STAT 0x31  /* Second ALS Comparator Status Register */
  67#define ADP8870_L2TRP   0x32  /* L2 comparator reference */
  68#define ADP8870_L2HYS   0x33  /* L2 hysteresis */
  69#define ADP8870_L3TRP   0x34  /* L3 comparator reference */
  70#define ADP8870_L3HYS   0x35  /* L3 hysteresis */
  71#define ADP8870_L4TRP   0x36  /* L4 comparator reference */
  72#define ADP8870_L4HYS   0x37  /* L4 hysteresis */
  73#define ADP8870_L5TRP   0x38  /* L5 comparator reference */
  74#define ADP8870_L5HYS   0x39  /* L5 hysteresis */
  75#define ADP8870_PH1LEVL 0x40  /* First phototransistor ambient light level-low byte register */
  76#define ADP8870_PH1LEVH 0x41  /* First phototransistor ambient light level-high byte register */
  77#define ADP8870_PH2LEVL 0x42  /* Second phototransistor ambient light level-low byte register */
  78#define ADP8870_PH2LEVH 0x43  /* Second phototransistor ambient light level-high byte register */
  79
  80#define ADP8870_MANUFID         0x3  /* Analog Devices AD8870 Manufacturer and device ID */
  81#define ADP8870_DEVID(x)        ((x) & 0xF)
  82#define ADP8870_MANID(x)        ((x) >> 4)
  83
  84/* MDCR Device mode and status */
  85#define D7ALSEN                 (1 << 7)
  86#define INT_CFG                 (1 << 6)
  87#define NSTBY                   (1 << 5)
  88#define DIM_EN                  (1 << 4)
  89#define GDWN_DIS                (1 << 3)
  90#define SIS_EN                  (1 << 2)
  91#define CMP_AUTOEN              (1 << 1)
  92#define BLEN                    (1 << 0)
  93
  94/* ADP8870_ALS1_EN Main ALS comparator level enable */
  95#define L5_EN                   (1 << 3)
  96#define L4_EN                   (1 << 2)
  97#define L3_EN                   (1 << 1)
  98#define L2_EN                   (1 << 0)
  99
 100#define CFGR_BLV_SHIFT          3
 101#define CFGR_BLV_MASK           0x7
 102#define ADP8870_FLAG_LED_MASK   0xFF
 103
 104#define FADE_VAL(in, out)       ((0xF & (in)) | ((0xF & (out)) << 4))
 105#define BL_CFGR_VAL(law, blv)   ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1))
 106#define ALS_CMPR_CFG_VAL(filt)  ((0x7 & (filt)) << 1)
 107
 108struct adp8870_bl {
 109        struct i2c_client *client;
 110        struct backlight_device *bl;
 111        struct adp8870_led *led;
 112        struct adp8870_backlight_platform_data *pdata;
 113        struct mutex lock;
 114        unsigned long cached_daylight_max;
 115        int id;
 116        int revid;
 117        int current_brightness;
 118};
 119
 120struct adp8870_led {
 121        struct led_classdev     cdev;
 122        struct work_struct      work;
 123        struct i2c_client       *client;
 124        enum led_brightness     new_brightness;
 125        int                     id;
 126        int                     flags;
 127};
 128
 129static int adp8870_read(struct i2c_client *client, int reg, uint8_t *val)
 130{
 131        int ret;
 132
 133        ret = i2c_smbus_read_byte_data(client, reg);
 134        if (ret < 0) {
 135                dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
 136                return ret;
 137        }
 138
 139        *val = ret;
 140        return 0;
 141}
 142
 143
 144static int adp8870_write(struct i2c_client *client, u8 reg, u8 val)
 145{
 146        int ret = i2c_smbus_write_byte_data(client, reg, val);
 147        if (ret)
 148                dev_err(&client->dev, "failed to write\n");
 149
 150        return ret;
 151}
 152
 153static int adp8870_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
 154{
 155        struct adp8870_bl *data = i2c_get_clientdata(client);
 156        uint8_t reg_val;
 157        int ret;
 158
 159        mutex_lock(&data->lock);
 160
 161        ret = adp8870_read(client, reg, &reg_val);
 162
 163        if (!ret && ((reg_val & bit_mask) != bit_mask)) {
 164                reg_val |= bit_mask;
 165                ret = adp8870_write(client, reg, reg_val);
 166        }
 167
 168        mutex_unlock(&data->lock);
 169        return ret;
 170}
 171
 172static int adp8870_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
 173{
 174        struct adp8870_bl *data = i2c_get_clientdata(client);
 175        uint8_t reg_val;
 176        int ret;
 177
 178        mutex_lock(&data->lock);
 179
 180        ret = adp8870_read(client, reg, &reg_val);
 181
 182        if (!ret && (reg_val & bit_mask)) {
 183                reg_val &= ~bit_mask;
 184                ret = adp8870_write(client, reg, reg_val);
 185        }
 186
 187        mutex_unlock(&data->lock);
 188        return ret;
 189}
 190
 191/*
 192 * Independent sink / LED
 193 */
 194#if defined(ADP8870_USE_LEDS)
 195static void adp8870_led_work(struct work_struct *work)
 196{
 197        struct adp8870_led *led = container_of(work, struct adp8870_led, work);
 198        adp8870_write(led->client, ADP8870_ISC1 + led->id - 1,
 199                         led->new_brightness >> 1);
 200}
 201
 202static void adp8870_led_set(struct led_classdev *led_cdev,
 203                           enum led_brightness value)
 204{
 205        struct adp8870_led *led;
 206
 207        led = container_of(led_cdev, struct adp8870_led, cdev);
 208        led->new_brightness = value;
 209        /*
 210         * Use workqueue for IO since I2C operations can sleep.
 211         */
 212        schedule_work(&led->work);
 213}
 214
 215static int adp8870_led_setup(struct adp8870_led *led)
 216{
 217        struct i2c_client *client = led->client;
 218        int ret = 0;
 219
 220        ret = adp8870_write(client, ADP8870_ISC1 + led->id - 1, 0);
 221        if (ret)
 222                return ret;
 223
 224        ret = adp8870_set_bits(client, ADP8870_ISCC, 1 << (led->id - 1));
 225        if (ret)
 226                return ret;
 227
 228        if (led->id > 4)
 229                ret = adp8870_set_bits(client, ADP8870_ISCT1,
 230                                (led->flags & 0x3) << ((led->id - 5) * 2));
 231        else
 232                ret = adp8870_set_bits(client, ADP8870_ISCT2,
 233                                (led->flags & 0x3) << ((led->id - 1) * 2));
 234
 235        return ret;
 236}
 237
 238static int __devinit adp8870_led_probe(struct i2c_client *client)
 239{
 240        struct adp8870_backlight_platform_data *pdata =
 241                client->dev.platform_data;
 242        struct adp8870_bl *data = i2c_get_clientdata(client);
 243        struct adp8870_led *led, *led_dat;
 244        struct led_info *cur_led;
 245        int ret, i;
 246
 247
 248        led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL);
 249        if (led == NULL) {
 250                dev_err(&client->dev, "failed to alloc memory\n");
 251                return -ENOMEM;
 252        }
 253
 254        ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law);
 255        if (ret)
 256                goto err_free;
 257
 258        ret = adp8870_write(client, ADP8870_ISCT1,
 259                        (pdata->led_on_time & 0x3) << 6);
 260        if (ret)
 261                goto err_free;
 262
 263        ret = adp8870_write(client, ADP8870_ISCF,
 264                        FADE_VAL(pdata->led_fade_in, pdata->led_fade_out));
 265        if (ret)
 266                goto err_free;
 267
 268        for (i = 0; i < pdata->num_leds; ++i) {
 269                cur_led = &pdata->leds[i];
 270                led_dat = &led[i];
 271
 272                led_dat->id = cur_led->flags & ADP8870_FLAG_LED_MASK;
 273
 274                if (led_dat->id > 7 || led_dat->id < 1) {
 275                        dev_err(&client->dev, "Invalid LED ID %d\n",
 276                                led_dat->id);
 277                        goto err;
 278                }
 279
 280                if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) {
 281                        dev_err(&client->dev, "LED %d used by Backlight\n",
 282                                led_dat->id);
 283                        goto err;
 284                }
 285
 286                led_dat->cdev.name = cur_led->name;
 287                led_dat->cdev.default_trigger = cur_led->default_trigger;
 288                led_dat->cdev.brightness_set = adp8870_led_set;
 289                led_dat->cdev.brightness = LED_OFF;
 290                led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT;
 291                led_dat->client = client;
 292                led_dat->new_brightness = LED_OFF;
 293                INIT_WORK(&led_dat->work, adp8870_led_work);
 294
 295                ret = led_classdev_register(&client->dev, &led_dat->cdev);
 296                if (ret) {
 297                        dev_err(&client->dev, "failed to register LED %d\n",
 298                                led_dat->id);
 299                        goto err;
 300                }
 301
 302                ret = adp8870_led_setup(led_dat);
 303                if (ret) {
 304                        dev_err(&client->dev, "failed to write\n");
 305                        i++;
 306                        goto err;
 307                }
 308        }
 309
 310        data->led = led;
 311
 312        return 0;
 313
 314 err:
 315        for (i = i - 1; i >= 0; --i) {
 316                led_classdev_unregister(&led[i].cdev);
 317                cancel_work_sync(&led[i].work);
 318        }
 319
 320 err_free:
 321        kfree(led);
 322
 323        return ret;
 324}
 325
 326static int __devexit adp8870_led_remove(struct i2c_client *client)
 327{
 328        struct adp8870_backlight_platform_data *pdata =
 329                client->dev.platform_data;
 330        struct adp8870_bl *data = i2c_get_clientdata(client);
 331        int i;
 332
 333        for (i = 0; i < pdata->num_leds; i++) {
 334                led_classdev_unregister(&data->led[i].cdev);
 335                cancel_work_sync(&data->led[i].work);
 336        }
 337
 338        kfree(data->led);
 339        return 0;
 340}
 341#else
 342static int __devinit adp8870_led_probe(struct i2c_client *client)
 343{
 344        return 0;
 345}
 346
 347static int __devexit adp8870_led_remove(struct i2c_client *client)
 348{
 349        return 0;
 350}
 351#endif
 352
 353static int adp8870_bl_set(struct backlight_device *bl, int brightness)
 354{
 355        struct adp8870_bl *data = bl_get_data(bl);
 356        struct i2c_client *client = data->client;
 357        int ret = 0;
 358
 359        if (data->pdata->en_ambl_sens) {
 360                if ((brightness > 0) && (brightness < ADP8870_MAX_BRIGHTNESS)) {
 361                        /* Disable Ambient Light auto adjust */
 362                        ret = adp8870_clr_bits(client, ADP8870_MDCR,
 363                                        CMP_AUTOEN);
 364                        if (ret)
 365                                return ret;
 366                        ret = adp8870_write(client, ADP8870_BLMX1, brightness);
 367                        if (ret)
 368                                return ret;
 369                } else {
 370                        /*
 371                         * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
 372                         * restore daylight l1 sysfs brightness
 373                         */
 374                        ret = adp8870_write(client, ADP8870_BLMX1,
 375                                         data->cached_daylight_max);
 376                        if (ret)
 377                                return ret;
 378
 379                        ret = adp8870_set_bits(client, ADP8870_MDCR,
 380                                         CMP_AUTOEN);
 381                        if (ret)
 382                                return ret;
 383                }
 384        } else {
 385                ret = adp8870_write(client, ADP8870_BLMX1, brightness);
 386                if (ret)
 387                        return ret;
 388        }
 389
 390        if (data->current_brightness && brightness == 0)
 391                ret = adp8870_set_bits(client,
 392                                ADP8870_MDCR, DIM_EN);
 393        else if (data->current_brightness == 0 && brightness)
 394                ret = adp8870_clr_bits(client,
 395                                ADP8870_MDCR, DIM_EN);
 396
 397        if (!ret)
 398                data->current_brightness = brightness;
 399
 400        return ret;
 401}
 402
 403static int adp8870_bl_update_status(struct backlight_device *bl)
 404{
 405        int brightness = bl->props.brightness;
 406        if (bl->props.power != FB_BLANK_UNBLANK)
 407                brightness = 0;
 408
 409        if (bl->props.fb_blank != FB_BLANK_UNBLANK)
 410                brightness = 0;
 411
 412        return adp8870_bl_set(bl, brightness);
 413}
 414
 415static int adp8870_bl_get_brightness(struct backlight_device *bl)
 416{
 417        struct adp8870_bl *data = bl_get_data(bl);
 418
 419        return data->current_brightness;
 420}
 421
 422static const struct backlight_ops adp8870_bl_ops = {
 423        .update_status  = adp8870_bl_update_status,
 424        .get_brightness = adp8870_bl_get_brightness,
 425};
 426
 427static int adp8870_bl_setup(struct backlight_device *bl)
 428{
 429        struct adp8870_bl *data = bl_get_data(bl);
 430        struct i2c_client *client = data->client;
 431        struct adp8870_backlight_platform_data *pdata = data->pdata;
 432        int ret = 0;
 433
 434        ret = adp8870_write(client, ADP8870_BLSEL, ~pdata->bl_led_assign);
 435        if (ret)
 436                return ret;
 437
 438        ret = adp8870_write(client, ADP8870_PWMLED, pdata->pwm_assign);
 439        if (ret)
 440                return ret;
 441
 442        ret = adp8870_write(client, ADP8870_BLMX1, pdata->l1_daylight_max);
 443        if (ret)
 444                return ret;
 445
 446        ret = adp8870_write(client, ADP8870_BLDM1, pdata->l1_daylight_dim);
 447        if (ret)
 448                return ret;
 449
 450        if (pdata->en_ambl_sens) {
 451                data->cached_daylight_max = pdata->l1_daylight_max;
 452                ret = adp8870_write(client, ADP8870_BLMX2,
 453                                                pdata->l2_bright_max);
 454                if (ret)
 455                        return ret;
 456                ret = adp8870_write(client, ADP8870_BLDM2,
 457                                                pdata->l2_bright_dim);
 458                if (ret)
 459                        return ret;
 460
 461                ret = adp8870_write(client, ADP8870_BLMX3,
 462                                                pdata->l3_office_max);
 463                if (ret)
 464                        return ret;
 465                ret = adp8870_write(client, ADP8870_BLDM3,
 466                                                pdata->l3_office_dim);
 467                if (ret)
 468                        return ret;
 469
 470                ret = adp8870_write(client, ADP8870_BLMX4,
 471                                                pdata->l4_indoor_max);
 472                if (ret)
 473                        return ret;
 474
 475                ret = adp8870_write(client, ADP8870_BLDM4,
 476                                                pdata->l4_indor_dim);
 477                if (ret)
 478                        return ret;
 479
 480                ret = adp8870_write(client, ADP8870_BLMX5,
 481                                                pdata->l5_dark_max);
 482                if (ret)
 483                        return ret;
 484
 485                ret = adp8870_write(client, ADP8870_BLDM5,
 486                                                pdata->l5_dark_dim);
 487                if (ret)
 488                        return ret;
 489
 490                ret = adp8870_write(client, ADP8870_L2TRP, pdata->l2_trip);
 491                if (ret)
 492                        return ret;
 493
 494                ret = adp8870_write(client, ADP8870_L2HYS, pdata->l2_hyst);
 495                if (ret)
 496                        return ret;
 497
 498                ret = adp8870_write(client, ADP8870_L3TRP, pdata->l3_trip);
 499                if (ret)
 500                        return ret;
 501
 502                ret = adp8870_write(client, ADP8870_L3HYS, pdata->l3_hyst);
 503                if (ret)
 504                        return ret;
 505
 506                ret = adp8870_write(client, ADP8870_L4TRP, pdata->l4_trip);
 507                if (ret)
 508                        return ret;
 509
 510                ret = adp8870_write(client, ADP8870_L4HYS, pdata->l4_hyst);
 511                if (ret)
 512                        return ret;
 513
 514                ret = adp8870_write(client, ADP8870_L5TRP, pdata->l5_trip);
 515                if (ret)
 516                        return ret;
 517
 518                ret = adp8870_write(client, ADP8870_L5HYS, pdata->l5_hyst);
 519                if (ret)
 520                        return ret;
 521
 522                ret = adp8870_write(client, ADP8870_ALS1_EN, L5_EN | L4_EN |
 523                                                L3_EN | L2_EN);
 524                if (ret)
 525                        return ret;
 526
 527                ret = adp8870_write(client, ADP8870_CMP_CTL,
 528                        ALS_CMPR_CFG_VAL(pdata->abml_filt));
 529                if (ret)
 530                        return ret;
 531        }
 532
 533        ret = adp8870_write(client, ADP8870_CFGR,
 534                        BL_CFGR_VAL(pdata->bl_fade_law, 0));
 535        if (ret)
 536                return ret;
 537
 538        ret = adp8870_write(client, ADP8870_BLFR, FADE_VAL(pdata->bl_fade_in,
 539                        pdata->bl_fade_out));
 540        if (ret)
 541                return ret;
 542        /*
 543         * ADP8870 Rev0 requires GDWN_DIS bit set
 544         */
 545
 546        ret = adp8870_set_bits(client, ADP8870_MDCR, BLEN | DIM_EN | NSTBY |
 547                        (data->revid == 0 ? GDWN_DIS : 0));
 548
 549        return ret;
 550}
 551
 552static ssize_t adp8870_show(struct device *dev, char *buf, int reg)
 553{
 554        struct adp8870_bl *data = dev_get_drvdata(dev);
 555        int error;
 556        uint8_t reg_val;
 557
 558        mutex_lock(&data->lock);
 559        error = adp8870_read(data->client, reg, &reg_val);
 560        mutex_unlock(&data->lock);
 561
 562        if (error < 0)
 563                return error;
 564
 565        return sprintf(buf, "%u\n", reg_val);
 566}
 567
 568static ssize_t adp8870_store(struct device *dev, const char *buf,
 569                         size_t count, int reg)
 570{
 571        struct adp8870_bl *data = dev_get_drvdata(dev);
 572        unsigned long val;
 573        int ret;
 574
 575        ret = strict_strtoul(buf, 10, &val);
 576        if (ret)
 577                return ret;
 578
 579        mutex_lock(&data->lock);
 580        adp8870_write(data->client, reg, val);
 581        mutex_unlock(&data->lock);
 582
 583        return count;
 584}
 585
 586static ssize_t adp8870_bl_l5_dark_max_show(struct device *dev,
 587                struct device_attribute *attr, char *buf)
 588{
 589        return adp8870_show(dev, buf, ADP8870_BLMX5);
 590}
 591
 592static ssize_t adp8870_bl_l5_dark_max_store(struct device *dev,
 593                struct device_attribute *attr, const char *buf, size_t count)
 594{
 595        return adp8870_store(dev, buf, count, ADP8870_BLMX5);
 596}
 597static DEVICE_ATTR(l5_dark_max, 0664, adp8870_bl_l5_dark_max_show,
 598                        adp8870_bl_l5_dark_max_store);
 599
 600
 601static ssize_t adp8870_bl_l4_indoor_max_show(struct device *dev,
 602                struct device_attribute *attr, char *buf)
 603{
 604        return adp8870_show(dev, buf, ADP8870_BLMX4);
 605}
 606
 607static ssize_t adp8870_bl_l4_indoor_max_store(struct device *dev,
 608                struct device_attribute *attr, const char *buf, size_t count)
 609{
 610        return adp8870_store(dev, buf, count, ADP8870_BLMX4);
 611}
 612static DEVICE_ATTR(l4_indoor_max, 0664, adp8870_bl_l4_indoor_max_show,
 613                        adp8870_bl_l4_indoor_max_store);
 614
 615
 616static ssize_t adp8870_bl_l3_office_max_show(struct device *dev,
 617                                     struct device_attribute *attr, char *buf)
 618{
 619        return adp8870_show(dev, buf, ADP8870_BLMX3);
 620}
 621
 622static ssize_t adp8870_bl_l3_office_max_store(struct device *dev,
 623                struct device_attribute *attr, const char *buf, size_t count)
 624{
 625        return adp8870_store(dev, buf, count, ADP8870_BLMX3);
 626}
 627
 628static DEVICE_ATTR(l3_office_max, 0664, adp8870_bl_l3_office_max_show,
 629                        adp8870_bl_l3_office_max_store);
 630
 631static ssize_t adp8870_bl_l2_bright_max_show(struct device *dev,
 632                struct device_attribute *attr, char *buf)
 633{
 634        return adp8870_show(dev, buf, ADP8870_BLMX2);
 635}
 636
 637static ssize_t adp8870_bl_l2_bright_max_store(struct device *dev,
 638                struct device_attribute *attr, const char *buf, size_t count)
 639{
 640        return adp8870_store(dev, buf, count, ADP8870_BLMX2);
 641}
 642static DEVICE_ATTR(l2_bright_max, 0664, adp8870_bl_l2_bright_max_show,
 643                        adp8870_bl_l2_bright_max_store);
 644
 645static ssize_t adp8870_bl_l1_daylight_max_show(struct device *dev,
 646                        struct device_attribute *attr, char *buf)
 647{
 648        return adp8870_show(dev, buf, ADP8870_BLMX1);
 649}
 650
 651static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev,
 652                struct device_attribute *attr, const char *buf, size_t count)
 653{
 654        struct adp8870_bl *data = dev_get_drvdata(dev);
 655        int ret = strict_strtoul(buf, 10, &data->cached_daylight_max);
 656        if (ret)
 657                return ret;
 658
 659        return adp8870_store(dev, buf, count, ADP8870_BLMX1);
 660}
 661static DEVICE_ATTR(l1_daylight_max, 0664, adp8870_bl_l1_daylight_max_show,
 662                        adp8870_bl_l1_daylight_max_store);
 663
 664static ssize_t adp8870_bl_l5_dark_dim_show(struct device *dev,
 665                        struct device_attribute *attr, char *buf)
 666{
 667        return adp8870_show(dev, buf, ADP8870_BLDM5);
 668}
 669
 670static ssize_t adp8870_bl_l5_dark_dim_store(struct device *dev,
 671                                     struct device_attribute *attr,
 672                                     const char *buf, size_t count)
 673{
 674        return adp8870_store(dev, buf, count, ADP8870_BLDM5);
 675}
 676static DEVICE_ATTR(l5_dark_dim, 0664, adp8870_bl_l5_dark_dim_show,
 677                        adp8870_bl_l5_dark_dim_store);
 678
 679static ssize_t adp8870_bl_l4_indoor_dim_show(struct device *dev,
 680                        struct device_attribute *attr, char *buf)
 681{
 682        return adp8870_show(dev, buf, ADP8870_BLDM4);
 683}
 684
 685static ssize_t adp8870_bl_l4_indoor_dim_store(struct device *dev,
 686                                     struct device_attribute *attr,
 687                                     const char *buf, size_t count)
 688{
 689        return adp8870_store(dev, buf, count, ADP8870_BLDM4);
 690}
 691static DEVICE_ATTR(l4_indoor_dim, 0664, adp8870_bl_l4_indoor_dim_show,
 692                        adp8870_bl_l4_indoor_dim_store);
 693
 694
 695static ssize_t adp8870_bl_l3_office_dim_show(struct device *dev,
 696                        struct device_attribute *attr, char *buf)
 697{
 698        return adp8870_show(dev, buf, ADP8870_BLDM3);
 699}
 700
 701static ssize_t adp8870_bl_l3_office_dim_store(struct device *dev,
 702                                     struct device_attribute *attr,
 703                                     const char *buf, size_t count)
 704{
 705        return adp8870_store(dev, buf, count, ADP8870_BLDM3);
 706}
 707static DEVICE_ATTR(l3_office_dim, 0664, adp8870_bl_l3_office_dim_show,
 708                        adp8870_bl_l3_office_dim_store);
 709
 710static ssize_t adp8870_bl_l2_bright_dim_show(struct device *dev,
 711                        struct device_attribute *attr, char *buf)
 712{
 713        return adp8870_show(dev, buf, ADP8870_BLDM2);
 714}
 715
 716static ssize_t adp8870_bl_l2_bright_dim_store(struct device *dev,
 717                                     struct device_attribute *attr,
 718                                     const char *buf, size_t count)
 719{
 720        return adp8870_store(dev, buf, count, ADP8870_BLDM2);
 721}
 722static DEVICE_ATTR(l2_bright_dim, 0664, adp8870_bl_l2_bright_dim_show,
 723                        adp8870_bl_l2_bright_dim_store);
 724
 725static ssize_t adp8870_bl_l1_daylight_dim_show(struct device *dev,
 726                                     struct device_attribute *attr, char *buf)
 727{
 728        return adp8870_show(dev, buf, ADP8870_BLDM1);
 729}
 730
 731static ssize_t adp8870_bl_l1_daylight_dim_store(struct device *dev,
 732                                     struct device_attribute *attr,
 733                                     const char *buf, size_t count)
 734{
 735        return adp8870_store(dev, buf, count, ADP8870_BLDM1);
 736}
 737static DEVICE_ATTR(l1_daylight_dim, 0664, adp8870_bl_l1_daylight_dim_show,
 738                        adp8870_bl_l1_daylight_dim_store);
 739
 740#ifdef ADP8870_EXT_FEATURES
 741static ssize_t adp8870_bl_ambient_light_level_show(struct device *dev,
 742                                     struct device_attribute *attr, char *buf)
 743{
 744        struct adp8870_bl *data = dev_get_drvdata(dev);
 745        int error;
 746        uint8_t reg_val;
 747        uint16_t ret_val;
 748
 749        mutex_lock(&data->lock);
 750        error = adp8870_read(data->client, ADP8870_PH1LEVL, &reg_val);
 751        if (error < 0) {
 752                mutex_unlock(&data->lock);
 753                return error;
 754        }
 755        ret_val = reg_val;
 756        error = adp8870_read(data->client, ADP8870_PH1LEVH, &reg_val);
 757        mutex_unlock(&data->lock);
 758
 759        if (error < 0)
 760                return error;
 761
 762        /* Return 13-bit conversion value for the first light sensor */
 763        ret_val += (reg_val & 0x1F) << 8;
 764
 765        return sprintf(buf, "%u\n", ret_val);
 766}
 767static DEVICE_ATTR(ambient_light_level, 0444,
 768                adp8870_bl_ambient_light_level_show, NULL);
 769
 770static ssize_t adp8870_bl_ambient_light_zone_show(struct device *dev,
 771                                     struct device_attribute *attr, char *buf)
 772{
 773        struct adp8870_bl *data = dev_get_drvdata(dev);
 774        int error;
 775        uint8_t reg_val;
 776
 777        mutex_lock(&data->lock);
 778        error = adp8870_read(data->client, ADP8870_CFGR, &reg_val);
 779        mutex_unlock(&data->lock);
 780
 781        if (error < 0)
 782                return error;
 783
 784        return sprintf(buf, "%u\n",
 785                ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1);
 786}
 787
 788static ssize_t adp8870_bl_ambient_light_zone_store(struct device *dev,
 789                                     struct device_attribute *attr,
 790                                     const char *buf, size_t count)
 791{
 792        struct adp8870_bl *data = dev_get_drvdata(dev);
 793        unsigned long val;
 794        uint8_t reg_val;
 795        int ret;
 796
 797        ret = strict_strtoul(buf, 10, &val);
 798        if (ret)
 799                return ret;
 800
 801        if (val == 0) {
 802                /* Enable automatic ambient light sensing */
 803                adp8870_set_bits(data->client, ADP8870_MDCR, CMP_AUTOEN);
 804        } else if ((val > 0) && (val < 6)) {
 805                /* Disable automatic ambient light sensing */
 806                adp8870_clr_bits(data->client, ADP8870_MDCR, CMP_AUTOEN);
 807
 808                /* Set user supplied ambient light zone */
 809                mutex_lock(&data->lock);
 810                adp8870_read(data->client, ADP8870_CFGR, &reg_val);
 811                reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
 812                reg_val |= (val - 1) << CFGR_BLV_SHIFT;
 813                adp8870_write(data->client, ADP8870_CFGR, reg_val);
 814                mutex_unlock(&data->lock);
 815        }
 816
 817        return count;
 818}
 819static DEVICE_ATTR(ambient_light_zone, 0664,
 820                adp8870_bl_ambient_light_zone_show,
 821                adp8870_bl_ambient_light_zone_store);
 822#endif
 823
 824static struct attribute *adp8870_bl_attributes[] = {
 825        &dev_attr_l5_dark_max.attr,
 826        &dev_attr_l5_dark_dim.attr,
 827        &dev_attr_l4_indoor_max.attr,
 828        &dev_attr_l4_indoor_dim.attr,
 829        &dev_attr_l3_office_max.attr,
 830        &dev_attr_l3_office_dim.attr,
 831        &dev_attr_l2_bright_max.attr,
 832        &dev_attr_l2_bright_dim.attr,
 833        &dev_attr_l1_daylight_max.attr,
 834        &dev_attr_l1_daylight_dim.attr,
 835#ifdef ADP8870_EXT_FEATURES
 836        &dev_attr_ambient_light_level.attr,
 837        &dev_attr_ambient_light_zone.attr,
 838#endif
 839        NULL
 840};
 841
 842static const struct attribute_group adp8870_bl_attr_group = {
 843        .attrs = adp8870_bl_attributes,
 844};
 845
 846static int __devinit adp8870_probe(struct i2c_client *client,
 847                                        const struct i2c_device_id *id)
 848{
 849        struct backlight_properties props;
 850        struct backlight_device *bl;
 851        struct adp8870_bl *data;
 852        struct adp8870_backlight_platform_data *pdata =
 853                client->dev.platform_data;
 854        uint8_t reg_val;
 855        int ret;
 856
 857        if (!i2c_check_functionality(client->adapter,
 858                                        I2C_FUNC_SMBUS_BYTE_DATA)) {
 859                dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
 860                return -EIO;
 861        }
 862
 863        if (!pdata) {
 864                dev_err(&client->dev, "no platform data?\n");
 865                return -EINVAL;
 866        }
 867
 868        ret = adp8870_read(client, ADP8870_MFDVID, &reg_val);
 869        if (ret < 0)
 870                return -EIO;
 871
 872        if (ADP8870_MANID(reg_val) != ADP8870_MANUFID) {
 873                dev_err(&client->dev, "failed to probe\n");
 874                return -ENODEV;
 875        }
 876
 877        data = kzalloc(sizeof(*data), GFP_KERNEL);
 878        if (data == NULL)
 879                return -ENOMEM;
 880
 881        data->revid = ADP8870_DEVID(reg_val);
 882        data->client = client;
 883        data->pdata = pdata;
 884        data->id = id->driver_data;
 885        data->current_brightness = 0;
 886        i2c_set_clientdata(client, data);
 887
 888        mutex_init(&data->lock);
 889
 890        memset(&props, 0, sizeof(props));
 891        props.type = BACKLIGHT_RAW;
 892        props.max_brightness = props.brightness = ADP8870_MAX_BRIGHTNESS;
 893        bl = backlight_device_register(dev_driver_string(&client->dev),
 894                        &client->dev, data, &adp8870_bl_ops, &props);
 895        if (IS_ERR(bl)) {
 896                dev_err(&client->dev, "failed to register backlight\n");
 897                ret = PTR_ERR(bl);
 898                goto out2;
 899        }
 900
 901        data->bl = bl;
 902
 903        if (pdata->en_ambl_sens)
 904                ret = sysfs_create_group(&bl->dev.kobj,
 905                        &adp8870_bl_attr_group);
 906
 907        if (ret) {
 908                dev_err(&client->dev, "failed to register sysfs\n");
 909                goto out1;
 910        }
 911
 912        ret = adp8870_bl_setup(bl);
 913        if (ret) {
 914                ret = -EIO;
 915                goto out;
 916        }
 917
 918        backlight_update_status(bl);
 919
 920        dev_info(&client->dev, "Rev.%d Backlight\n", data->revid);
 921
 922        if (pdata->num_leds)
 923                adp8870_led_probe(client);
 924
 925        return 0;
 926
 927out:
 928        if (data->pdata->en_ambl_sens)
 929                sysfs_remove_group(&data->bl->dev.kobj,
 930                        &adp8870_bl_attr_group);
 931out1:
 932        backlight_device_unregister(bl);
 933out2:
 934        kfree(data);
 935
 936        return ret;
 937}
 938
 939static int __devexit adp8870_remove(struct i2c_client *client)
 940{
 941        struct adp8870_bl *data = i2c_get_clientdata(client);
 942
 943        adp8870_clr_bits(client, ADP8870_MDCR, NSTBY);
 944
 945        if (data->led)
 946                adp8870_led_remove(client);
 947
 948        if (data->pdata->en_ambl_sens)
 949                sysfs_remove_group(&data->bl->dev.kobj,
 950                        &adp8870_bl_attr_group);
 951
 952        backlight_device_unregister(data->bl);
 953        kfree(data);
 954
 955        return 0;
 956}
 957
 958#ifdef CONFIG_PM
 959static int adp8870_i2c_suspend(struct i2c_client *client, pm_message_t message)
 960{
 961        adp8870_clr_bits(client, ADP8870_MDCR, NSTBY);
 962
 963        return 0;
 964}
 965
 966static int adp8870_i2c_resume(struct i2c_client *client)
 967{
 968        adp8870_set_bits(client, ADP8870_MDCR, NSTBY);
 969
 970        return 0;
 971}
 972#else
 973#define adp8870_i2c_suspend NULL
 974#define adp8870_i2c_resume NULL
 975#endif
 976
 977static const struct i2c_device_id adp8870_id[] = {
 978        { "adp8870", 0 },
 979        { }
 980};
 981MODULE_DEVICE_TABLE(i2c, adp8870_id);
 982
 983static struct i2c_driver adp8870_driver = {
 984        .driver = {
 985                .name = KBUILD_MODNAME,
 986        },
 987        .probe    = adp8870_probe,
 988        .remove   = __devexit_p(adp8870_remove),
 989        .suspend = adp8870_i2c_suspend,
 990        .resume  = adp8870_i2c_resume,
 991        .id_table = adp8870_id,
 992};
 993
 994static int __init adp8870_init(void)
 995{
 996        return i2c_add_driver(&adp8870_driver);
 997}
 998module_init(adp8870_init);
 999
1000static void __exit adp8870_exit(void)
1001{
1002        i2c_del_driver(&adp8870_driver);
1003}
1004module_exit(adp8870_exit);
1005
1006MODULE_LICENSE("GPL v2");
1007MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
1008MODULE_DESCRIPTION("ADP8870 Backlight driver");
1009MODULE_ALIAS("i2c:adp8870-backlight");
1010
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.