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