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