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 adp8870_led_probe(struct i2c_client *client)
 239{
 240        struct adp8870_backlight_platform_data *pdata =
 241                dev_get_platdata(&client->dev);
 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        led = devm_kzalloc(&client->dev, pdata->num_leds * sizeof(*led),
 248                                GFP_KERNEL);
 249        if (led == NULL)
 250                return -ENOMEM;
 251
 252        ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law);
 253        if (ret)
 254                return ret;
 255
 256        ret = adp8870_write(client, ADP8870_ISCT1,
 257                        (pdata->led_on_time & 0x3) << 6);
 258        if (ret)
 259                return ret;
 260
 261        ret = adp8870_write(client, ADP8870_ISCF,
 262                        FADE_VAL(pdata->led_fade_in, pdata->led_fade_out));
 263        if (ret)
 264                return ret;
 265
 266        for (i = 0; i < pdata->num_leds; ++i) {
 267                cur_led = &pdata->leds[i];
 268                led_dat = &led[i];
 269
 270                led_dat->id = cur_led->flags & ADP8870_FLAG_LED_MASK;
 271
 272                if (led_dat->id > 7 || led_dat->id < 1) {
 273                        dev_err(&client->dev, "Invalid LED ID %d\n",
 274                                led_dat->id);
 275                        ret = -EINVAL;
 276                        goto err;
 277                }
 278
 279                if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) {
 280                        dev_err(&client->dev, "LED %d used by Backlight\n",
 281                                led_dat->id);
 282                        ret = -EBUSY;
 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        return ret;
 321}
 322
 323static int adp8870_led_remove(struct i2c_client *client)
 324{
 325        struct adp8870_backlight_platform_data *pdata =
 326                dev_get_platdata(&client->dev);
 327        struct adp8870_bl *data = i2c_get_clientdata(client);
 328        int i;
 329
 330        for (i = 0; i < pdata->num_leds; i++) {
 331                led_classdev_unregister(&data->led[i].cdev);
 332                cancel_work_sync(&data->led[i].work);
 333        }
 334
 335        return 0;
 336}
 337#else
 338static int adp8870_led_probe(struct i2c_client *client)
 339{
 340        return 0;
 341}
 342
 343static int adp8870_led_remove(struct i2c_client *client)
 344{
 345        return 0;
 346}
 347#endif
 348
 349static int adp8870_bl_set(struct backlight_device *bl, int brightness)
 350{
 351        struct adp8870_bl *data = bl_get_data(bl);
 352        struct i2c_client *client = data->client;
 353        int ret = 0;
 354
 355        if (data->pdata->en_ambl_sens) {
 356                if ((brightness > 0) && (brightness < ADP8870_MAX_BRIGHTNESS)) {
 357                        /* Disable Ambient Light auto adjust */
 358                        ret = adp8870_clr_bits(client, ADP8870_MDCR,
 359                                        CMP_AUTOEN);
 360                        if (ret)
 361                                return ret;
 362                        ret = adp8870_write(client, ADP8870_BLMX1, brightness);
 363                        if (ret)
 364                                return ret;
 365                } else {
 366                        /*
 367                         * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
 368                         * restore daylight l1 sysfs brightness
 369                         */
 370                        ret = adp8870_write(client, ADP8870_BLMX1,
 371                                         data->cached_daylight_max);
 372                        if (ret)
 373                                return ret;
 374
 375                        ret = adp8870_set_bits(client, ADP8870_MDCR,
 376                                         CMP_AUTOEN);
 377                        if (ret)
 378                                return ret;
 379                }
 380        } else {
 381                ret = adp8870_write(client, ADP8870_BLMX1, brightness);
 382                if (ret)
 383                        return ret;
 384        }
 385
 386        if (data->current_brightness && brightness == 0)
 387                ret = adp8870_set_bits(client,
 388                                ADP8870_MDCR, DIM_EN);
 389        else if (data->current_brightness == 0 && brightness)
 390                ret = adp8870_clr_bits(client,
 391                                ADP8870_MDCR, DIM_EN);
 392
 393        if (!ret)
 394                data->current_brightness = brightness;
 395
 396        return ret;
 397}
 398
 399static int adp8870_bl_update_status(struct backlight_device *bl)
 400{
 401        int brightness = bl->props.brightness;
 402        if (bl->props.power != FB_BLANK_UNBLANK)
 403                brightness = 0;
 404
 405        if (bl->props.fb_blank != FB_BLANK_UNBLANK)
 406                brightness = 0;
 407
 408        return adp8870_bl_set(bl, brightness);
 409}
 410
 411static int adp8870_bl_get_brightness(struct backlight_device *bl)
 412{
 413        struct adp8870_bl *data = bl_get_data(bl);
 414
 415        return data->current_brightness;
 416}
 417
 418static const struct backlight_ops adp8870_bl_ops = {
 419        .update_status  = adp8870_bl_update_status,
 420        .get_brightness = adp8870_bl_get_brightness,
 421};
 422
 423static int adp8870_bl_setup(struct backlight_device *bl)
 424{
 425        struct adp8870_bl *data = bl_get_data(bl);
 426        struct i2c_client *client = data->client;
 427        struct adp8870_backlight_platform_data *pdata = data->pdata;
 428        int ret = 0;
 429
 430        ret = adp8870_write(client, ADP8870_BLSEL, ~pdata->bl_led_assign);
 431        if (ret)
 432                return ret;
 433
 434        ret = adp8870_write(client, ADP8870_PWMLED, pdata->pwm_assign);
 435        if (ret)
 436                return ret;
 437
 438        ret = adp8870_write(client, ADP8870_BLMX1, pdata->l1_daylight_max);
 439        if (ret)
 440                return ret;
 441
 442        ret = adp8870_write(client, ADP8870_BLDM1, pdata->l1_daylight_dim);
 443        if (ret)
 444                return ret;
 445
 446        if (pdata->en_ambl_sens) {
 447                data->cached_daylight_max = pdata->l1_daylight_max;
 448                ret = adp8870_write(client, ADP8870_BLMX2,
 449                                                pdata->l2_bright_max);
 450                if (ret)
 451                        return ret;
 452                ret = adp8870_write(client, ADP8870_BLDM2,
 453                                                pdata->l2_bright_dim);
 454                if (ret)
 455                        return ret;
 456
 457                ret = adp8870_write(client, ADP8870_BLMX3,
 458                                                pdata->l3_office_max);
 459                if (ret)
 460                        return ret;
 461                ret = adp8870_write(client, ADP8870_BLDM3,
 462                                                pdata->l3_office_dim);
 463                if (ret)
 464                        return ret;
 465
 466                ret = adp8870_write(client, ADP8870_BLMX4,
 467                                                pdata->l4_indoor_max);
 468                if (ret)
 469                        return ret;
 470
 471                ret = adp8870_write(client, ADP8870_BLDM4,
 472                                                pdata->l4_indor_dim);
 473                if (ret)
 474                        return ret;
 475
 476                ret = adp8870_write(client, ADP8870_BLMX5,
 477                                                pdata->l5_dark_max);
 478                if (ret)
 479                        return ret;
 480
 481                ret = adp8870_write(client, ADP8870_BLDM5,
 482                                                pdata->l5_dark_dim);
 483                if (ret)
 484                        return ret;
 485
 486                ret = adp8870_write(client, ADP8870_L2TRP, pdata->l2_trip);
 487                if (ret)
 488                        return ret;
 489
 490                ret = adp8870_write(client, ADP8870_L2HYS, pdata->l2_hyst);
 491                if (ret)
 492                        return ret;
 493
 494                ret = adp8870_write(client, ADP8870_L3TRP, pdata->l3_trip);
 495                if (ret)
 496                        return ret;
 497
 498                ret = adp8870_write(client, ADP8870_L3HYS, pdata->l3_hyst);
 499                if (ret)
 500                        return ret;
 501
 502                ret = adp8870_write(client, ADP8870_L4TRP, pdata->l4_trip);
 503                if (ret)
 504                        return ret;
 505
 506                ret = adp8870_write(client, ADP8870_L4HYS, pdata->l4_hyst);
 507                if (ret)
 508                        return ret;
 509
 510                ret = adp8870_write(client, ADP8870_L5TRP, pdata->l5_trip);
 511                if (ret)
 512                        return ret;
 513
 514                ret = adp8870_write(client, ADP8870_L5HYS, pdata->l5_hyst);
 515                if (ret)
 516                        return ret;
 517
 518                ret = adp8870_write(client, ADP8870_ALS1_EN, L5_EN | L4_EN |
 519                                                L3_EN | L2_EN);
 520                if (ret)
 521                        return ret;
 522
 523                ret = adp8870_write(client, ADP8870_CMP_CTL,
 524                        ALS_CMPR_CFG_VAL(pdata->abml_filt));
 525                if (ret)
 526                        return ret;
 527        }
 528
 529        ret = adp8870_write(client, ADP8870_CFGR,
 530                        BL_CFGR_VAL(pdata->bl_fade_law, 0));
 531        if (ret)
 532                return ret;
 533
 534        ret = adp8870_write(client, ADP8870_BLFR, FADE_VAL(pdata->bl_fade_in,
 535                        pdata->bl_fade_out));
 536        if (ret)
 537                return ret;
 538        /*
 539         * ADP8870 Rev0 requires GDWN_DIS bit set
 540         */
 541
 542        ret = adp8870_set_bits(client, ADP8870_MDCR, BLEN | DIM_EN | NSTBY |
 543                        (data->revid == 0 ? GDWN_DIS : 0));
 544
 545        return ret;
 546}
 547
 548static ssize_t adp8870_show(struct device *dev, char *buf, int reg)
 549{
 550        struct adp8870_bl *data = dev_get_drvdata(dev);
 551        int error;
 552        uint8_t reg_val;
 553
 554        mutex_lock(&data->lock);
 555        error = adp8870_read(data->client, reg, &reg_val);
 556        mutex_unlock(&data->lock);
 557
 558        if (error < 0)
 559                return error;
 560
 561        return sprintf(buf, "%u\n", reg_val);
 562}
 563
 564static ssize_t adp8870_store(struct device *dev, const char *buf,
 565                         size_t count, int reg)
 566{
 567        struct adp8870_bl *data = dev_get_drvdata(dev);
 568        unsigned long val;
 569        int ret;
 570
 571        ret = kstrtoul(buf, 10, &val);
 572        if (ret)
 573                return ret;
 574
 575        mutex_lock(&data->lock);
 576        adp8870_write(data->client, reg, val);
 577        mutex_unlock(&data->lock);
 578
 579        return count;
 580}
 581
 582static ssize_t adp8870_bl_l5_dark_max_show(struct device *dev,
 583                struct device_attribute *attr, char *buf)
 584{
 585        return adp8870_show(dev, buf, ADP8870_BLMX5);
 586}
 587
 588static ssize_t adp8870_bl_l5_dark_max_store(struct device *dev,
 589                struct device_attribute *attr, const char *buf, size_t count)
 590{
 591        return adp8870_store(dev, buf, count, ADP8870_BLMX5);
 592}
 593static DEVICE_ATTR(l5_dark_max, 0664, adp8870_bl_l5_dark_max_show,
 594                        adp8870_bl_l5_dark_max_store);
 595
 596
 597static ssize_t adp8870_bl_l4_indoor_max_show(struct device *dev,
 598                struct device_attribute *attr, char *buf)
 599{
 600        return adp8870_show(dev, buf, ADP8870_BLMX4);
 601}
 602
 603static ssize_t adp8870_bl_l4_indoor_max_store(struct device *dev,
 604                struct device_attribute *attr, const char *buf, size_t count)
 605{
 606        return adp8870_store(dev, buf, count, ADP8870_BLMX4);
 607}
 608static DEVICE_ATTR(l4_indoor_max, 0664, adp8870_bl_l4_indoor_max_show,
 609                        adp8870_bl_l4_indoor_max_store);
 610
 611
 612static ssize_t adp8870_bl_l3_office_max_show(struct device *dev,
 613                                     struct device_attribute *attr, char *buf)
 614{
 615        return adp8870_show(dev, buf, ADP8870_BLMX3);
 616}
 617
 618static ssize_t adp8870_bl_l3_office_max_store(struct device *dev,
 619                struct device_attribute *attr, const char *buf, size_t count)
 620{
 621        return adp8870_store(dev, buf, count, ADP8870_BLMX3);
 622}
 623
 624static DEVICE_ATTR(l3_office_max, 0664, adp8870_bl_l3_office_max_show,
 625                        adp8870_bl_l3_office_max_store);
 626
 627static ssize_t adp8870_bl_l2_bright_max_show(struct device *dev,
 628                struct device_attribute *attr, char *buf)
 629{
 630        return adp8870_show(dev, buf, ADP8870_BLMX2);
 631}
 632
 633static ssize_t adp8870_bl_l2_bright_max_store(struct device *dev,
 634                struct device_attribute *attr, const char *buf, size_t count)
 635{
 636        return adp8870_store(dev, buf, count, ADP8870_BLMX2);
 637}
 638static DEVICE_ATTR(l2_bright_max, 0664, adp8870_bl_l2_bright_max_show,
 639                        adp8870_bl_l2_bright_max_store);
 640
 641static ssize_t adp8870_bl_l1_daylight_max_show(struct device *dev,
 642                        struct device_attribute *attr, char *buf)
 643{
 644        return adp8870_show(dev, buf, ADP8870_BLMX1);
 645}
 646
 647static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev,
 648                struct device_attribute *attr, const char *buf, size_t count)
 649{
 650        struct adp8870_bl *data = dev_get_drvdata(dev);
 651        int ret = kstrtoul(buf, 10, &data->cached_daylight_max);
 652        if (ret)
 653                return ret;
 654
 655        return adp8870_store(dev, buf, count, ADP8870_BLMX1);
 656}
 657static DEVICE_ATTR(l1_daylight_max, 0664, adp8870_bl_l1_daylight_max_show,
 658                        adp8870_bl_l1_daylight_max_store);
 659
 660static ssize_t adp8870_bl_l5_dark_dim_show(struct device *dev,
 661                        struct device_attribute *attr, char *buf)
 662{
 663        return adp8870_show(dev, buf, ADP8870_BLDM5);
 664}
 665
 666static ssize_t adp8870_bl_l5_dark_dim_store(struct device *dev,
 667                                     struct device_attribute *attr,
 668                                     const char *buf, size_t count)
 669{
 670        return adp8870_store(dev, buf, count, ADP8870_BLDM5);
 671}
 672static DEVICE_ATTR(l5_dark_dim, 0664, adp8870_bl_l5_dark_dim_show,
 673                        adp8870_bl_l5_dark_dim_store);
 674
 675static ssize_t adp8870_bl_l4_indoor_dim_show(struct device *dev,
 676                        struct device_attribute *attr, char *buf)
 677{
 678        return adp8870_show(dev, buf, ADP8870_BLDM4);
 679}
 680
 681static ssize_t adp8870_bl_l4_indoor_dim_store(struct device *dev,
 682                                     struct device_attribute *attr,
 683                                     const char *buf, size_t count)
 684{
 685        return adp8870_store(dev, buf, count, ADP8870_BLDM4);
 686}
 687static DEVICE_ATTR(l4_indoor_dim, 0664, adp8870_bl_l4_indoor_dim_show,
 688                        adp8870_bl_l4_indoor_dim_store);
 689
 690
 691static ssize_t adp8870_bl_l3_office_dim_show(struct device *dev,
 692                        struct device_attribute *attr, char *buf)
 693{
 694        return adp8870_show(dev, buf, ADP8870_BLDM3);
 695}
 696
 697static ssize_t adp8870_bl_l3_office_dim_store(struct device *dev,
 698                                     struct device_attribute *attr,
 699                                     const char *buf, size_t count)
 700{
 701        return adp8870_store(dev, buf, count, ADP8870_BLDM3);
 702}
 703static DEVICE_ATTR(l3_office_dim, 0664, adp8870_bl_l3_office_dim_show,
 704                        adp8870_bl_l3_office_dim_store);
 705
 706static ssize_t adp8870_bl_l2_bright_dim_show(struct device *dev,
 707                        struct device_attribute *attr, char *buf)
 708{
 709        return adp8870_show(dev, buf, ADP8870_BLDM2);
 710}
 711
 712static ssize_t adp8870_bl_l2_bright_dim_store(struct device *dev,
 713                                     struct device_attribute *attr,
 714                                     const char *buf, size_t count)
 715{
 716        return adp8870_store(dev, buf, count, ADP8870_BLDM2);
 717}
 718static DEVICE_ATTR(l2_bright_dim, 0664, adp8870_bl_l2_bright_dim_show,
 719                        adp8870_bl_l2_bright_dim_store);
 720
 721static ssize_t adp8870_bl_l1_daylight_dim_show(struct device *dev,
 722                                     struct device_attribute *attr, char *buf)
 723{
 724        return adp8870_show(dev, buf, ADP8870_BLDM1);
 725}
 726
 727static ssize_t adp8870_bl_l1_daylight_dim_store(struct device *dev,
 728                                     struct device_attribute *attr,
 729                                     const char *buf, size_t count)
 730{
 731        return adp8870_store(dev, buf, count, ADP8870_BLDM1);
 732}
 733static DEVICE_ATTR(l1_daylight_dim, 0664, adp8870_bl_l1_daylight_dim_show,
 734                        adp8870_bl_l1_daylight_dim_store);
 735
 736#ifdef ADP8870_EXT_FEATURES
 737static ssize_t adp8870_bl_ambient_light_level_show(struct device *dev,
 738                                     struct device_attribute *attr, char *buf)
 739{
 740        struct adp8870_bl *data = dev_get_drvdata(dev);
 741        int error;
 742        uint8_t reg_val;
 743        uint16_t ret_val;
 744
 745        mutex_lock(&data->lock);
 746        error = adp8870_read(data->client, ADP8870_PH1LEVL, &reg_val);
 747        if (error < 0) {
 748                mutex_unlock(&data->lock);
 749                return error;
 750        }
 751        ret_val = reg_val;
 752        error = adp8870_read(data->client, ADP8870_PH1LEVH, &reg_val);
 753        mutex_unlock(&data->lock);
 754
 755        if (error < 0)
 756                return error;
 757
 758        /* Return 13-bit conversion value for the first light sensor */
 759        ret_val += (reg_val & 0x1F) << 8;
 760
 761        return sprintf(buf, "%u\n", ret_val);
 762}
 763static DEVICE_ATTR(ambient_light_level, 0444,
 764                adp8870_bl_ambient_light_level_show, NULL);
 765
 766static ssize_t adp8870_bl_ambient_light_zone_show(struct device *dev,
 767                                     struct device_attribute *attr, char *buf)
 768{
 769        struct adp8870_bl *data = dev_get_drvdata(dev);
 770        int error;
 771        uint8_t reg_val;
 772
 773        mutex_lock(&data->lock);
 774        error = adp8870_read(data->client, ADP8870_CFGR, &reg_val);
 775        mutex_unlock(&data->lock);
 776
 777        if (error < 0)
 778                return error;
 779
 780        return sprintf(buf, "%u\n",
 781                ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1);
 782}
 783
 784static ssize_t adp8870_bl_ambient_light_zone_store(struct device *dev,
 785                                     struct device_attribute *attr,
 786                                     const char *buf, size_t count)
 787{
 788        struct adp8870_bl *data = dev_get_drvdata(dev);
 789        unsigned long val;
 790        uint8_t reg_val;
 791        int ret;
 792
 793        ret = kstrtoul(buf, 10, &val);
 794        if (ret)
 795                return ret;
 796
 797        if (val == 0) {
 798                /* Enable automatic ambient light sensing */
 799                adp8870_set_bits(data->client, ADP8870_MDCR, CMP_AUTOEN);
 800        } else if ((val > 0) && (val < 6)) {
 801                /* Disable automatic ambient light sensing */
 802                adp8870_clr_bits(data->client, ADP8870_MDCR, CMP_AUTOEN);
 803
 804                /* Set user supplied ambient light zone */
 805                mutex_lock(&data->lock);
 806                adp8870_read(data->client, ADP8870_CFGR, &reg_val);
 807                reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
 808                reg_val |= (val - 1) << CFGR_BLV_SHIFT;
 809                adp8870_write(data->client, ADP8870_CFGR, reg_val);
 810                mutex_unlock(&data->lock);
 811        }
 812
 813        return count;
 814}
 815static DEVICE_ATTR(ambient_light_zone, 0664,
 816                adp8870_bl_ambient_light_zone_show,
 817                adp8870_bl_ambient_light_zone_store);
 818#endif
 819
 820static struct attribute *adp8870_bl_attributes[] = {
 821        &dev_attr_l5_dark_max.attr,
 822        &dev_attr_l5_dark_dim.attr,
 823        &dev_attr_l4_indoor_max.attr,
 824        &dev_attr_l4_indoor_dim.attr,
 825        &dev_attr_l3_office_max.attr,
 826        &dev_attr_l3_office_dim.attr,
 827        &dev_attr_l2_bright_max.attr,
 828        &dev_attr_l2_bright_dim.attr,
 829        &dev_attr_l1_daylight_max.attr,
 830        &dev_attr_l1_daylight_dim.attr,
 831#ifdef ADP8870_EXT_FEATURES
 832        &dev_attr_ambient_light_level.attr,
 833        &dev_attr_ambient_light_zone.attr,
 834#endif
 835        NULL
 836};
 837
 838static const struct attribute_group adp8870_bl_attr_group = {
 839        .attrs = adp8870_bl_attributes,
 840};
 841
 842static int adp8870_probe(struct i2c_client *client,
 843                                        const struct i2c_device_id *id)
 844{
 845        struct backlight_properties props;
 846        struct backlight_device *bl;
 847        struct adp8870_bl *data;
 848        struct adp8870_backlight_platform_data *pdata =
 849                dev_get_platdata(&client->dev);
 850        uint8_t reg_val;
 851        int ret;
 852
 853        if (!i2c_check_functionality(client->adapter,
 854                                        I2C_FUNC_SMBUS_BYTE_DATA)) {
 855                dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
 856                return -EIO;
 857        }
 858
 859        if (!pdata) {
 860                dev_err(&client->dev, "no platform data?\n");
 861                return -EINVAL;
 862        }
 863
 864        ret = adp8870_read(client, ADP8870_MFDVID, &reg_val);
 865        if (ret < 0)
 866                return -EIO;
 867
 868        if (ADP8870_MANID(reg_val) != ADP8870_MANUFID) {
 869                dev_err(&client->dev, "failed to probe\n");
 870                return -ENODEV;
 871        }
 872
 873        data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
 874        if (data == NULL)
 875                return -ENOMEM;
 876
 877        data->revid = ADP8870_DEVID(reg_val);
 878        data->client = client;
 879        data->pdata = pdata;
 880        data->id = id->driver_data;
 881        data->current_brightness = 0;
 882        i2c_set_clientdata(client, data);
 883
 884        mutex_init(&data->lock);
 885
 886        memset(&props, 0, sizeof(props));
 887        props.type = BACKLIGHT_RAW;
 888        props.max_brightness = props.brightness = ADP8870_MAX_BRIGHTNESS;
 889        bl = devm_backlight_device_register(&client->dev,
 890                                dev_driver_string(&client->dev),
 891                                &client->dev, data, &adp8870_bl_ops, &props);
 892        if (IS_ERR(bl)) {
 893                dev_err(&client->dev, "failed to register backlight\n");
 894                return PTR_ERR(bl);
 895        }
 896
 897        data->bl = bl;
 898
 899        if (pdata->en_ambl_sens) {
 900                ret = sysfs_create_group(&bl->dev.kobj,
 901                        &adp8870_bl_attr_group);
 902                if (ret) {
 903                        dev_err(&client->dev, "failed to register sysfs\n");
 904                        return ret;
 905                }
 906        }
 907
 908        ret = adp8870_bl_setup(bl);
 909        if (ret) {
 910                ret = -EIO;
 911                goto out;
 912        }
 913
 914        backlight_update_status(bl);
 915
 916        dev_info(&client->dev, "Rev.%d Backlight\n", data->revid);
 917
 918        if (pdata->num_leds)
 919                adp8870_led_probe(client);
 920
 921        return 0;
 922
 923out:
 924        if (data->pdata->en_ambl_sens)
 925                sysfs_remove_group(&data->bl->dev.kobj,
 926                        &adp8870_bl_attr_group);
 927
 928        return ret;
 929}
 930
 931static int adp8870_remove(struct i2c_client *client)
 932{
 933        struct adp8870_bl *data = i2c_get_clientdata(client);
 934
 935        adp8870_clr_bits(client, ADP8870_MDCR, NSTBY);
 936
 937        if (data->led)
 938                adp8870_led_remove(client);
 939
 940        if (data->pdata->en_ambl_sens)
 941                sysfs_remove_group(&data->bl->dev.kobj,
 942                        &adp8870_bl_attr_group);
 943
 944        return 0;
 945}
 946
 947#ifdef CONFIG_PM_SLEEP
 948static int adp8870_i2c_suspend(struct device *dev)
 949{
 950        struct i2c_client *client = to_i2c_client(dev);
 951
 952        adp8870_clr_bits(client, ADP8870_MDCR, NSTBY);
 953
 954        return 0;
 955}
 956
 957static int adp8870_i2c_resume(struct device *dev)
 958{
 959        struct i2c_client *client = to_i2c_client(dev);
 960
 961        adp8870_set_bits(client, ADP8870_MDCR, NSTBY | BLEN);
 962
 963        return 0;
 964}
 965#endif
 966
 967static SIMPLE_DEV_PM_OPS(adp8870_i2c_pm_ops, adp8870_i2c_suspend,
 968                        adp8870_i2c_resume);
 969
 970static const struct i2c_device_id adp8870_id[] = {
 971        { "adp8870", 0 },
 972        { }
 973};
 974MODULE_DEVICE_TABLE(i2c, adp8870_id);
 975
 976static struct i2c_driver adp8870_driver = {
 977        .driver = {
 978                .name   = KBUILD_MODNAME,
 979                .pm     = &adp8870_i2c_pm_ops,
 980        },
 981        .probe    = adp8870_probe,
 982        .remove   = adp8870_remove,
 983        .id_table = adp8870_id,
 984};
 985
 986module_i2c_driver(adp8870_driver);
 987
 988MODULE_LICENSE("GPL v2");
 989MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
 990MODULE_DESCRIPTION("ADP8870 Backlight driver");
 991MODULE_ALIAS("i2c:adp8870-backlight");
 992