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
 148        if (ret)
 149                dev_err(&client->dev, "failed to write\n");
 150
 151        return ret;
 152}
 153
 154static int adp8870_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
 155{
 156        struct adp8870_bl *data = i2c_get_clientdata(client);
 157        uint8_t reg_val;
 158        int ret;
 159
 160        mutex_lock(&data->lock);
 161
 162        ret = adp8870_read(client, reg, &reg_val);
 163
 164        if (!ret && ((reg_val & bit_mask) != bit_mask)) {
 165                reg_val |= bit_mask;
 166                ret = adp8870_write(client, reg, reg_val);
 167        }
 168
 169        mutex_unlock(&data->lock);
 170        return ret;
 171}
 172
 173static int adp8870_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
 174{
 175        struct adp8870_bl *data = i2c_get_clientdata(client);
 176        uint8_t reg_val;
 177        int ret;
 178
 179        mutex_lock(&data->lock);
 180
 181        ret = adp8870_read(client, reg, &reg_val);
 182
 183        if (!ret && (reg_val & bit_mask)) {
 184                reg_val &= ~bit_mask;
 185                ret = adp8870_write(client, reg, reg_val);
 186        }
 187
 188        mutex_unlock(&data->lock);
 189        return ret;
 190}
 191
 192/*
 193 * Independent sink / LED
 194 */
 195#if defined(ADP8870_USE_LEDS)
 196static void adp8870_led_work(struct work_struct *work)
 197{
 198        struct adp8870_led *led = container_of(work, struct adp8870_led, work);
 199
 200        adp8870_write(led->client, ADP8870_ISC1 + led->id - 1,
 201                         led->new_brightness >> 1);
 202}
 203
 204static void adp8870_led_set(struct led_classdev *led_cdev,
 205                           enum led_brightness value)
 206{
 207        struct adp8870_led *led;
 208
 209        led = container_of(led_cdev, struct adp8870_led, cdev);
 210        led->new_brightness = value;
 211        /*
 212         * Use workqueue for IO since I2C operations can sleep.
 213         */
 214        schedule_work(&led->work);
 215}
 216
 217static int adp8870_led_setup(struct adp8870_led *led)
 218{
 219        struct i2c_client *client = led->client;
 220        int ret = 0;
 221
 222        ret = adp8870_write(client, ADP8870_ISC1 + led->id - 1, 0);
 223        if (ret)
 224                return ret;
 225
 226        ret = adp8870_set_bits(client, ADP8870_ISCC, 1 << (led->id - 1));
 227        if (ret)
 228                return ret;
 229
 230        if (led->id > 4)
 231                ret = adp8870_set_bits(client, ADP8870_ISCT1,
 232                                (led->flags & 0x3) << ((led->id - 5) * 2));
 233        else
 234                ret = adp8870_set_bits(client, ADP8870_ISCT2,
 235                                (led->flags & 0x3) << ((led->id - 1) * 2));
 236
 237        return ret;
 238}
 239
 240static int adp8870_led_probe(struct i2c_client *client)
 241{
 242        struct adp8870_backlight_platform_data *pdata =
 243                dev_get_platdata(&client->dev);
 244        struct adp8870_bl *data = i2c_get_clientdata(client);
 245        struct adp8870_led *led, *led_dat;
 246        struct led_info *cur_led;
 247        int ret, i;
 248
 249        led = devm_kzalloc(&client->dev, pdata->num_leds * sizeof(*led),
 250                                GFP_KERNEL);
 251        if (led == NULL)
 252                return -ENOMEM;
 253
 254        ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law);
 255        if (ret)
 256                return ret;
 257
 258        ret = adp8870_write(client, ADP8870_ISCT1,
 259                        (pdata->led_on_time & 0x3) << 6);
 260        if (ret)
 261                return ret;
 262
 263        ret = adp8870_write(client, ADP8870_ISCF,
 264                        FADE_VAL(pdata->led_fade_in, pdata->led_fade_out));
 265        if (ret)
 266                return ret;
 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                        ret = -EINVAL;
 278                        goto err;
 279                }
 280
 281                if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) {
 282                        dev_err(&client->dev, "LED %d used by Backlight\n",
 283                                led_dat->id);
 284                        ret = -EBUSY;
 285                        goto err;
 286                }
 287
 288                led_dat->cdev.name = cur_led->name;
 289                led_dat->cdev.default_trigger = cur_led->default_trigger;
 290                led_dat->cdev.brightness_set = adp8870_led_set;
 291                led_dat->cdev.brightness = LED_OFF;
 292                led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT;
 293                led_dat->client = client;
 294                led_dat->new_brightness = LED_OFF;
 295                INIT_WORK(&led_dat->work, adp8870_led_work);
 296
 297                ret = led_classdev_register(&client->dev, &led_dat->cdev);
 298                if (ret) {
 299                        dev_err(&client->dev, "failed to register LED %d\n",
 300                                led_dat->id);
 301                        goto err;
 302                }
 303
 304                ret = adp8870_led_setup(led_dat);
 305                if (ret) {
 306                        dev_err(&client->dev, "failed to write\n");
 307                        i++;
 308                        goto err;
 309                }
 310        }
 311
 312        data->led = led;
 313
 314        return 0;
 315
 316 err:
 317        for (i = i - 1; i >= 0; --i) {
 318                led_classdev_unregister(&led[i].cdev);
 319                cancel_work_sync(&led[i].work);
 320        }
 321
 322        return ret;
 323}
 324
 325static int adp8870_led_remove(struct i2c_client *client)
 326{
 327        struct adp8870_backlight_platform_data *pdata =
 328                dev_get_platdata(&client->dev);
 329        struct adp8870_bl *data = i2c_get_clientdata(client);
 330        int i;
 331
 332        for (i = 0; i < pdata->num_leds; i++) {
 333                led_classdev_unregister(&data->led[i].cdev);
 334                cancel_work_sync(&data->led[i].work);
 335        }
 336
 337        return 0;
 338}
 339#else
 340static int adp8870_led_probe(struct i2c_client *client)
 341{
 342        return 0;
 343}
 344
 345static int adp8870_led_remove(struct i2c_client *client)
 346{
 347        return 0;
 348}
 349#endif
 350
 351static int adp8870_bl_set(struct backlight_device *bl, int brightness)
 352{
 353        struct adp8870_bl *data = bl_get_data(bl);
 354        struct i2c_client *client = data->client;
 355        int ret = 0;
 356
 357        if (data->pdata->en_ambl_sens) {
 358                if ((brightness > 0) && (brightness < ADP8870_MAX_BRIGHTNESS)) {
 359                        /* Disable Ambient Light auto adjust */
 360                        ret = adp8870_clr_bits(client, ADP8870_MDCR,
 361                                        CMP_AUTOEN);
 362                        if (ret)
 363                                return ret;
 364                        ret = adp8870_write(client, ADP8870_BLMX1, brightness);
 365                        if (ret)
 366                                return ret;
 367                } else {
 368                        /*
 369                         * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
 370                         * restore daylight l1 sysfs brightness
 371                         */
 372                        ret = adp8870_write(client, ADP8870_BLMX1,
 373                                         data->cached_daylight_max);
 374                        if (ret)
 375                                return ret;
 376
 377                        ret = adp8870_set_bits(client, ADP8870_MDCR,
 378                                         CMP_AUTOEN);
 379                        if (ret)
 380                                return ret;
 381                }
 382        } else {
 383                ret = adp8870_write(client, ADP8870_BLMX1, brightness);
 384                if (ret)
 385                        return ret;
 386        }
 387
 388        if (data->current_brightness && brightness == 0)
 389                ret = adp8870_set_bits(client,
 390                                ADP8870_MDCR, DIM_EN);
 391        else if (data->current_brightness == 0 && brightness)
 392                ret = adp8870_clr_bits(client,
 393                                ADP8870_MDCR, DIM_EN);
 394
 395        if (!ret)
 396                data->current_brightness = brightness;
 397
 398        return ret;
 399}
 400
 401static int adp8870_bl_update_status(struct backlight_device *bl)
 402{
 403        int brightness = bl->props.brightness;
 404
 405        if (bl->props.power != FB_BLANK_UNBLANK)
 406                brightness = 0;
 407
 408        if (bl->props.fb_blank != FB_BLANK_UNBLANK)
 409                brightness = 0;
 410
 411        return adp8870_bl_set(bl, brightness);
 412}
 413
 414static int adp8870_bl_get_brightness(struct backlight_device *bl)
 415{
 416        struct adp8870_bl *data = bl_get_data(bl);
 417
 418        return data->current_brightness;
 419}
 420
 421static const struct backlight_ops adp8870_bl_ops = {
 422        .update_status  = adp8870_bl_update_status,
 423        .get_brightness = adp8870_bl_get_brightness,
 424};
 425
 426static int adp8870_bl_setup(struct backlight_device *bl)
 427{
 428        struct adp8870_bl *data = bl_get_data(bl);
 429        struct i2c_client *client = data->client;
 430        struct adp8870_backlight_platform_data *pdata = data->pdata;
 431        int ret = 0;
 432
 433        ret = adp8870_write(client, ADP8870_BLSEL, ~pdata->bl_led_assign);
 434        if (ret)
 435                return ret;
 436
 437        ret = adp8870_write(client, ADP8870_PWMLED, pdata->pwm_assign);
 438        if (ret)
 439                return ret;
 440
 441        ret = adp8870_write(client, ADP8870_BLMX1, pdata->l1_daylight_max);
 442        if (ret)
 443                return ret;
 444
 445        ret = adp8870_write(client, ADP8870_BLDM1, pdata->l1_daylight_dim);
 446        if (ret)
 447                return ret;
 448
 449        if (pdata->en_ambl_sens) {
 450                data->cached_daylight_max = pdata->l1_daylight_max;
 451                ret = adp8870_write(client, ADP8870_BLMX2,
 452                                                pdata->l2_bright_max);
 453                if (ret)
 454                        return ret;
 455                ret = adp8870_write(client, ADP8870_BLDM2,
 456                                                pdata->l2_bright_dim);
 457                if (ret)
 458                        return ret;
 459
 460                ret = adp8870_write(client, ADP8870_BLMX3,
 461                                                pdata->l3_office_max);
 462                if (ret)
 463                        return ret;
 464                ret = adp8870_write(client, ADP8870_BLDM3,
 465                                                pdata->l3_office_dim);
 466                if (ret)
 467                        return ret;
 468
 469                ret = adp8870_write(client, ADP8870_BLMX4,
 470                                                pdata->l4_indoor_max);
 471                if (ret)
 472                        return ret;
 473
 474                ret = adp8870_write(client, ADP8870_BLDM4,
 475                                                pdata->l4_indor_dim);
 476                if (ret)
 477                        return ret;
 478
 479                ret = adp8870_write(client, ADP8870_BLMX5,
 480                                                pdata->l5_dark_max);
 481                if (ret)
 482                        return ret;
 483
 484                ret = adp8870_write(client, ADP8870_BLDM5,
 485                                                pdata->l5_dark_dim);
 486                if (ret)
 487                        return ret;
 488
 489                ret = adp8870_write(client, ADP8870_L2TRP, pdata->l2_trip);
 490                if (ret)
 491                        return ret;
 492
 493                ret = adp8870_write(client, ADP8870_L2HYS, pdata->l2_hyst);
 494                if (ret)
 495                        return ret;
 496
 497                ret = adp8870_write(client, ADP8870_L3TRP, pdata->l3_trip);
 498                if (ret)
 499                        return ret;
 500
 501                ret = adp8870_write(client, ADP8870_L3HYS, pdata->l3_hyst);
 502                if (ret)
 503                        return ret;
 504
 505                ret = adp8870_write(client, ADP8870_L4TRP, pdata->l4_trip);
 506                if (ret)
 507                        return ret;
 508
 509                ret = adp8870_write(client, ADP8870_L4HYS, pdata->l4_hyst);
 510                if (ret)
 511                        return ret;
 512
 513                ret = adp8870_write(client, ADP8870_L5TRP, pdata->l5_trip);
 514                if (ret)
 515                        return ret;
 516
 517                ret = adp8870_write(client, ADP8870_L5HYS, pdata->l5_hyst);
 518                if (ret)
 519                        return ret;
 520
 521                ret = adp8870_write(client, ADP8870_ALS1_EN, L5_EN | L4_EN |
 522                                                L3_EN | L2_EN);
 523                if (ret)
 524                        return ret;
 525
 526                ret = adp8870_write(client, ADP8870_CMP_CTL,
 527                        ALS_CMPR_CFG_VAL(pdata->abml_filt));
 528                if (ret)
 529                        return ret;
 530        }
 531
 532        ret = adp8870_write(client, ADP8870_CFGR,
 533                        BL_CFGR_VAL(pdata->bl_fade_law, 0));
 534        if (ret)
 535                return ret;
 536
 537        ret = adp8870_write(client, ADP8870_BLFR, FADE_VAL(pdata->bl_fade_in,
 538                        pdata->bl_fade_out));
 539        if (ret)
 540                return ret;
 541        /*
 542         * ADP8870 Rev0 requires GDWN_DIS bit set
 543         */
 544
 545        ret = adp8870_set_bits(client, ADP8870_MDCR, BLEN | DIM_EN | NSTBY |
 546                        (data->revid == 0 ? GDWN_DIS : 0));
 547
 548        return ret;
 549}
 550
 551static ssize_t adp8870_show(struct device *dev, char *buf, int reg)
 552{
 553        struct adp8870_bl *data = dev_get_drvdata(dev);
 554        int error;
 555        uint8_t reg_val;
 556
 557        mutex_lock(&data->lock);
 558        error = adp8870_read(data->client, reg, &reg_val);
 559        mutex_unlock(&data->lock);
 560
 561        if (error < 0)
 562                return error;
 563
 564        return sprintf(buf, "%u\n", reg_val);
 565}
 566
 567static ssize_t adp8870_store(struct device *dev, const char *buf,
 568                         size_t count, int reg)
 569{
 570        struct adp8870_bl *data = dev_get_drvdata(dev);
 571        unsigned long val;
 572        int ret;
 573
 574        ret = kstrtoul(buf, 10, &val);
 575        if (ret)
 576                return ret;
 577
 578        mutex_lock(&data->lock);
 579        adp8870_write(data->client, reg, val);
 580        mutex_unlock(&data->lock);
 581
 582        return count;
 583}
 584
 585static ssize_t adp8870_bl_l5_dark_max_show(struct device *dev,
 586                struct device_attribute *attr, char *buf)
 587{
 588        return adp8870_show(dev, buf, ADP8870_BLMX5);
 589}
 590
 591static ssize_t adp8870_bl_l5_dark_max_store(struct device *dev,
 592                struct device_attribute *attr, const char *buf, size_t count)
 593{
 594        return adp8870_store(dev, buf, count, ADP8870_BLMX5);
 595}
 596static DEVICE_ATTR(l5_dark_max, 0664, adp8870_bl_l5_dark_max_show,
 597                        adp8870_bl_l5_dark_max_store);
 598
 599
 600static ssize_t adp8870_bl_l4_indoor_max_show(struct device *dev,
 601                struct device_attribute *attr, char *buf)
 602{
 603        return adp8870_show(dev, buf, ADP8870_BLMX4);
 604}
 605
 606static ssize_t adp8870_bl_l4_indoor_max_store(struct device *dev,
 607                struct device_attribute *attr, const char *buf, size_t count)
 608{
 609        return adp8870_store(dev, buf, count, ADP8870_BLMX4);
 610}
 611static DEVICE_ATTR(l4_indoor_max, 0664, adp8870_bl_l4_indoor_max_show,
 612                        adp8870_bl_l4_indoor_max_store);
 613
 614
 615static ssize_t adp8870_bl_l3_office_max_show(struct device *dev,
 616                                     struct device_attribute *attr, char *buf)
 617{
 618        return adp8870_show(dev, buf, ADP8870_BLMX3);
 619}
 620
 621static ssize_t adp8870_bl_l3_office_max_store(struct device *dev,
 622                struct device_attribute *attr, const char *buf, size_t count)
 623{
 624        return adp8870_store(dev, buf, count, ADP8870_BLMX3);
 625}
 626
 627static DEVICE_ATTR(l3_office_max, 0664, adp8870_bl_l3_office_max_show,
 628                        adp8870_bl_l3_office_max_store);
 629
 630static ssize_t adp8870_bl_l2_bright_max_show(struct device *dev,
 631                struct device_attribute *attr, char *buf)
 632{
 633        return adp8870_show(dev, buf, ADP8870_BLMX2);
 634}
 635
 636static ssize_t adp8870_bl_l2_bright_max_store(struct device *dev,
 637                struct device_attribute *attr, const char *buf, size_t count)
 638{
 639        return adp8870_store(dev, buf, count, ADP8870_BLMX2);
 640}
 641static DEVICE_ATTR(l2_bright_max, 0664, adp8870_bl_l2_bright_max_show,
 642                        adp8870_bl_l2_bright_max_store);
 643
 644static ssize_t adp8870_bl_l1_daylight_max_show(struct device *dev,
 645                        struct device_attribute *attr, char *buf)
 646{
 647        return adp8870_show(dev, buf, ADP8870_BLMX1);
 648}
 649
 650static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev,
 651                struct device_attribute *attr, const char *buf, size_t count)
 652{
 653        struct adp8870_bl *data = dev_get_drvdata(dev);
 654        int ret = kstrtoul(buf, 10, &data->cached_daylight_max);
 655
 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 = kstrtoul(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                ret = adp8870_read(data->client, ADP8870_CFGR, &reg_val);
 811                if (!ret) {
 812                        reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
 813                        reg_val |= (val - 1) << CFGR_BLV_SHIFT;
 814                        adp8870_write(data->client, ADP8870_CFGR, reg_val);
 815                }
 816                mutex_unlock(&data->lock);
 817        }
 818
 819        return count;
 820}
 821static DEVICE_ATTR(ambient_light_zone, 0664,
 822                adp8870_bl_ambient_light_zone_show,
 823                adp8870_bl_ambient_light_zone_store);
 824#endif
 825
 826static struct attribute *adp8870_bl_attributes[] = {
 827        &dev_attr_l5_dark_max.attr,
 828        &dev_attr_l5_dark_dim.attr,
 829        &dev_attr_l4_indoor_max.attr,
 830        &dev_attr_l4_indoor_dim.attr,
 831        &dev_attr_l3_office_max.attr,
 832        &dev_attr_l3_office_dim.attr,
 833        &dev_attr_l2_bright_max.attr,
 834        &dev_attr_l2_bright_dim.attr,
 835        &dev_attr_l1_daylight_max.attr,
 836        &dev_attr_l1_daylight_dim.attr,
 837#ifdef ADP8870_EXT_FEATURES
 838        &dev_attr_ambient_light_level.attr,
 839        &dev_attr_ambient_light_zone.attr,
 840#endif
 841        NULL
 842};
 843
 844static const struct attribute_group adp8870_bl_attr_group = {
 845        .attrs = adp8870_bl_attributes,
 846};
 847
 848static int adp8870_probe(struct i2c_client *client,
 849                                        const struct i2c_device_id *id)
 850{
 851        struct backlight_properties props;
 852        struct backlight_device *bl;
 853        struct adp8870_bl *data;
 854        struct adp8870_backlight_platform_data *pdata =
 855                dev_get_platdata(&client->dev);
 856        uint8_t reg_val;
 857        int ret;
 858
 859        if (!i2c_check_functionality(client->adapter,
 860                                        I2C_FUNC_SMBUS_BYTE_DATA)) {
 861                dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
 862                return -EIO;
 863        }
 864
 865        if (!pdata) {
 866                dev_err(&client->dev, "no platform data?\n");
 867                return -EINVAL;
 868        }
 869
 870        ret = adp8870_read(client, ADP8870_MFDVID, &reg_val);
 871        if (ret < 0)
 872                return -EIO;
 873
 874        if (ADP8870_MANID(reg_val) != ADP8870_MANUFID) {
 875                dev_err(&client->dev, "failed to probe\n");
 876                return -ENODEV;
 877        }
 878
 879        data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
 880        if (data == NULL)
 881                return -ENOMEM;
 882
 883        data->revid = ADP8870_DEVID(reg_val);
 884        data->client = client;
 885        data->pdata = pdata;
 886        data->id = id->driver_data;
 887        data->current_brightness = 0;
 888        i2c_set_clientdata(client, data);
 889
 890        mutex_init(&data->lock);
 891
 892        memset(&props, 0, sizeof(props));
 893        props.type = BACKLIGHT_RAW;
 894        props.max_brightness = props.brightness = ADP8870_MAX_BRIGHTNESS;
 895        bl = devm_backlight_device_register(&client->dev,
 896                                dev_driver_string(&client->dev),
 897                                &client->dev, data, &adp8870_bl_ops, &props);
 898        if (IS_ERR(bl)) {
 899                dev_err(&client->dev, "failed to register backlight\n");
 900                return PTR_ERR(bl);
 901        }
 902
 903        data->bl = bl;
 904
 905        if (pdata->en_ambl_sens) {
 906                ret = sysfs_create_group(&bl->dev.kobj,
 907                        &adp8870_bl_attr_group);
 908                if (ret) {
 909                        dev_err(&client->dev, "failed to register sysfs\n");
 910                        return ret;
 911                }
 912        }
 913
 914        ret = adp8870_bl_setup(bl);
 915        if (ret) {
 916                ret = -EIO;
 917                goto out;
 918        }
 919
 920        backlight_update_status(bl);
 921
 922        dev_info(&client->dev, "Rev.%d Backlight\n", data->revid);
 923
 924        if (pdata->num_leds)
 925                adp8870_led_probe(client);
 926
 927        return 0;
 928
 929out:
 930        if (data->pdata->en_ambl_sens)
 931                sysfs_remove_group(&data->bl->dev.kobj,
 932                        &adp8870_bl_attr_group);
 933
 934        return ret;
 935}
 936
 937static int adp8870_remove(struct i2c_client *client)
 938{
 939        struct adp8870_bl *data = i2c_get_clientdata(client);
 940
 941        adp8870_clr_bits(client, ADP8870_MDCR, NSTBY);
 942
 943        if (data->led)
 944                adp8870_led_remove(client);
 945
 946        if (data->pdata->en_ambl_sens)
 947                sysfs_remove_group(&data->bl->dev.kobj,
 948                        &adp8870_bl_attr_group);
 949
 950        return 0;
 951}
 952
 953#ifdef CONFIG_PM_SLEEP
 954static int adp8870_i2c_suspend(struct device *dev)
 955{
 956        struct i2c_client *client = to_i2c_client(dev);
 957
 958        adp8870_clr_bits(client, ADP8870_MDCR, NSTBY);
 959
 960        return 0;
 961}
 962
 963static int adp8870_i2c_resume(struct device *dev)
 964{
 965        struct i2c_client *client = to_i2c_client(dev);
 966
 967        adp8870_set_bits(client, ADP8870_MDCR, NSTBY | BLEN);
 968
 969        return 0;
 970}
 971#endif
 972
 973static SIMPLE_DEV_PM_OPS(adp8870_i2c_pm_ops, adp8870_i2c_suspend,
 974                        adp8870_i2c_resume);
 975
 976static const struct i2c_device_id adp8870_id[] = {
 977        { "adp8870", 0 },
 978        { }
 979};
 980MODULE_DEVICE_TABLE(i2c, adp8870_id);
 981
 982static struct i2c_driver adp8870_driver = {
 983        .driver = {
 984                .name   = KBUILD_MODNAME,
 985                .pm     = &adp8870_i2c_pm_ops,
 986        },
 987        .probe    = adp8870_probe,
 988        .remove   = adp8870_remove,
 989        .id_table = adp8870_id,
 990};
 991
 992module_i2c_driver(adp8870_driver);
 993
 994MODULE_LICENSE("GPL v2");
 995MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
 996MODULE_DESCRIPTION("ADP8870 Backlight driver");
 997