linux/drivers/leds/leds-lm3530.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2011 ST-Ericsson SA.
   3 * Copyright (C) 2009 Motorola, Inc.
   4 *
   5 * License Terms: GNU General Public License v2
   6 *
   7 * Simple driver for National Semiconductor LM3530 Backlight driver chip
   8 *
   9 * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
  10 * based on leds-lm3530.c by Dan Murphy <D.Murphy@motorola.com>
  11 */
  12
  13#include <linux/i2c.h>
  14#include <linux/leds.h>
  15#include <linux/slab.h>
  16#include <linux/platform_device.h>
  17#include <linux/input.h>
  18#include <linux/led-lm3530.h>
  19#include <linux/types.h>
  20#include <linux/regulator/consumer.h>
  21#include <linux/module.h>
  22
  23#define LM3530_LED_DEV "lcd-backlight"
  24#define LM3530_NAME "lm3530-led"
  25
  26#define LM3530_GEN_CONFIG               0x10
  27#define LM3530_ALS_CONFIG               0x20
  28#define LM3530_BRT_RAMP_RATE            0x30
  29#define LM3530_ALS_IMP_SELECT           0x41
  30#define LM3530_BRT_CTRL_REG             0xA0
  31#define LM3530_ALS_ZB0_REG              0x60
  32#define LM3530_ALS_ZB1_REG              0x61
  33#define LM3530_ALS_ZB2_REG              0x62
  34#define LM3530_ALS_ZB3_REG              0x63
  35#define LM3530_ALS_Z0T_REG              0x70
  36#define LM3530_ALS_Z1T_REG              0x71
  37#define LM3530_ALS_Z2T_REG              0x72
  38#define LM3530_ALS_Z3T_REG              0x73
  39#define LM3530_ALS_Z4T_REG              0x74
  40#define LM3530_REG_MAX                  14
  41
  42/* General Control Register */
  43#define LM3530_EN_I2C_SHIFT             (0)
  44#define LM3530_RAMP_LAW_SHIFT           (1)
  45#define LM3530_MAX_CURR_SHIFT           (2)
  46#define LM3530_EN_PWM_SHIFT             (5)
  47#define LM3530_PWM_POL_SHIFT            (6)
  48#define LM3530_EN_PWM_SIMPLE_SHIFT      (7)
  49
  50#define LM3530_ENABLE_I2C               (1 << LM3530_EN_I2C_SHIFT)
  51#define LM3530_ENABLE_PWM               (1 << LM3530_EN_PWM_SHIFT)
  52#define LM3530_POL_LOW                  (1 << LM3530_PWM_POL_SHIFT)
  53#define LM3530_ENABLE_PWM_SIMPLE        (1 << LM3530_EN_PWM_SIMPLE_SHIFT)
  54
  55/* ALS Config Register Options */
  56#define LM3530_ALS_AVG_TIME_SHIFT       (0)
  57#define LM3530_EN_ALS_SHIFT             (3)
  58#define LM3530_ALS_SEL_SHIFT            (5)
  59
  60#define LM3530_ENABLE_ALS               (3 << LM3530_EN_ALS_SHIFT)
  61
  62/* Brightness Ramp Rate Register */
  63#define LM3530_BRT_RAMP_FALL_SHIFT      (0)
  64#define LM3530_BRT_RAMP_RISE_SHIFT      (3)
  65
  66/* ALS Resistor Select */
  67#define LM3530_ALS1_IMP_SHIFT           (0)
  68#define LM3530_ALS2_IMP_SHIFT           (4)
  69
  70/* Zone Boundary Register defaults */
  71#define LM3530_ALS_ZB_MAX               (4)
  72#define LM3530_ALS_WINDOW_mV            (1000)
  73#define LM3530_ALS_OFFSET_mV            (4)
  74
  75/* Zone Target Register defaults */
  76#define LM3530_DEF_ZT_0                 (0x7F)
  77#define LM3530_DEF_ZT_1                 (0x66)
  78#define LM3530_DEF_ZT_2                 (0x4C)
  79#define LM3530_DEF_ZT_3                 (0x33)
  80#define LM3530_DEF_ZT_4                 (0x19)
  81
  82/* 7 bits are used for the brightness : LM3530_BRT_CTRL_REG */
  83#define MAX_BRIGHTNESS                  (127)
  84
  85struct lm3530_mode_map {
  86        const char *mode;
  87        enum lm3530_mode mode_val;
  88};
  89
  90static struct lm3530_mode_map mode_map[] = {
  91        { "man", LM3530_BL_MODE_MANUAL },
  92        { "als", LM3530_BL_MODE_ALS },
  93        { "pwm", LM3530_BL_MODE_PWM },
  94};
  95
  96/**
  97 * struct lm3530_data
  98 * @led_dev: led class device
  99 * @client: i2c client
 100 * @pdata: LM3530 platform data
 101 * @mode: mode of operation - manual, ALS, PWM
 102 * @regulator: regulator
 103 * @brighness: previous brightness value
 104 * @enable: regulator is enabled
 105 */
 106struct lm3530_data {
 107        struct led_classdev led_dev;
 108        struct i2c_client *client;
 109        struct lm3530_platform_data *pdata;
 110        enum lm3530_mode mode;
 111        struct regulator *regulator;
 112        enum led_brightness brightness;
 113        bool enable;
 114};
 115
 116/*
 117 * struct lm3530_als_data
 118 * @config  : value of ALS configuration register
 119 * @imp_sel : value of ALS resistor select register
 120 * @zone    : values of ALS ZB(Zone Boundary) registers
 121 */
 122struct lm3530_als_data {
 123        u8 config;
 124        u8 imp_sel;
 125        u8 zones[LM3530_ALS_ZB_MAX];
 126};
 127
 128static const u8 lm3530_reg[LM3530_REG_MAX] = {
 129        LM3530_GEN_CONFIG,
 130        LM3530_ALS_CONFIG,
 131        LM3530_BRT_RAMP_RATE,
 132        LM3530_ALS_IMP_SELECT,
 133        LM3530_BRT_CTRL_REG,
 134        LM3530_ALS_ZB0_REG,
 135        LM3530_ALS_ZB1_REG,
 136        LM3530_ALS_ZB2_REG,
 137        LM3530_ALS_ZB3_REG,
 138        LM3530_ALS_Z0T_REG,
 139        LM3530_ALS_Z1T_REG,
 140        LM3530_ALS_Z2T_REG,
 141        LM3530_ALS_Z3T_REG,
 142        LM3530_ALS_Z4T_REG,
 143};
 144
 145static int lm3530_get_mode_from_str(const char *str)
 146{
 147        int i;
 148
 149        for (i = 0; i < ARRAY_SIZE(mode_map); i++)
 150                if (sysfs_streq(str, mode_map[i].mode))
 151                        return mode_map[i].mode_val;
 152
 153        return -EINVAL;
 154}
 155
 156static void lm3530_als_configure(struct lm3530_platform_data *pdata,
 157                                struct lm3530_als_data *als)
 158{
 159        int i;
 160        u32 als_vmin, als_vmax, als_vstep;
 161
 162        if (pdata->als_vmax == 0) {
 163                pdata->als_vmin = 0;
 164                pdata->als_vmax = LM3530_ALS_WINDOW_mV;
 165        }
 166
 167        als_vmin = pdata->als_vmin;
 168        als_vmax = pdata->als_vmax;
 169
 170        if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV)
 171                pdata->als_vmax = als_vmax = als_vmin + LM3530_ALS_WINDOW_mV;
 172
 173        /* n zone boundary makes n+1 zones */
 174        als_vstep = (als_vmax - als_vmin) / (LM3530_ALS_ZB_MAX + 1);
 175
 176        for (i = 0; i < LM3530_ALS_ZB_MAX; i++)
 177                als->zones[i] = (((als_vmin + LM3530_ALS_OFFSET_mV) +
 178                        als_vstep + (i * als_vstep)) * LED_FULL) / 1000;
 179
 180        als->config =
 181                (pdata->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
 182                (LM3530_ENABLE_ALS) |
 183                (pdata->als_input_mode << LM3530_ALS_SEL_SHIFT);
 184
 185        als->imp_sel =
 186                (pdata->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
 187                (pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
 188}
 189
 190static int lm3530_init_registers(struct lm3530_data *drvdata)
 191{
 192        int ret = 0;
 193        int i;
 194        u8 gen_config;
 195        u8 brt_ramp;
 196        u8 brightness;
 197        u8 reg_val[LM3530_REG_MAX];
 198        struct lm3530_platform_data *pdata = drvdata->pdata;
 199        struct i2c_client *client = drvdata->client;
 200        struct lm3530_pwm_data *pwm = &pdata->pwm_data;
 201        struct lm3530_als_data als;
 202
 203        memset(&als, 0, sizeof(struct lm3530_als_data));
 204
 205        gen_config = (pdata->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
 206                        ((pdata->max_current & 7) << LM3530_MAX_CURR_SHIFT);
 207
 208        switch (drvdata->mode) {
 209        case LM3530_BL_MODE_MANUAL:
 210                gen_config |= LM3530_ENABLE_I2C;
 211                break;
 212        case LM3530_BL_MODE_ALS:
 213                gen_config |= LM3530_ENABLE_I2C;
 214                lm3530_als_configure(pdata, &als);
 215                break;
 216        case LM3530_BL_MODE_PWM:
 217                gen_config |= LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE |
 218                              (pdata->pwm_pol_hi << LM3530_PWM_POL_SHIFT);
 219                break;
 220        }
 221
 222        brt_ramp = (pdata->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
 223                        (pdata->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
 224
 225        if (drvdata->brightness)
 226                brightness = drvdata->brightness;
 227        else
 228                brightness = drvdata->brightness = pdata->brt_val;
 229
 230        if (brightness > drvdata->led_dev.max_brightness)
 231                brightness = drvdata->led_dev.max_brightness;
 232
 233        reg_val[0] = gen_config;        /* LM3530_GEN_CONFIG */
 234        reg_val[1] = als.config;        /* LM3530_ALS_CONFIG */
 235        reg_val[2] = brt_ramp;          /* LM3530_BRT_RAMP_RATE */
 236        reg_val[3] = als.imp_sel;       /* LM3530_ALS_IMP_SELECT */
 237        reg_val[4] = brightness;        /* LM3530_BRT_CTRL_REG */
 238        reg_val[5] = als.zones[0];      /* LM3530_ALS_ZB0_REG */
 239        reg_val[6] = als.zones[1];      /* LM3530_ALS_ZB1_REG */
 240        reg_val[7] = als.zones[2];      /* LM3530_ALS_ZB2_REG */
 241        reg_val[8] = als.zones[3];      /* LM3530_ALS_ZB3_REG */
 242        reg_val[9] = LM3530_DEF_ZT_0;   /* LM3530_ALS_Z0T_REG */
 243        reg_val[10] = LM3530_DEF_ZT_1;  /* LM3530_ALS_Z1T_REG */
 244        reg_val[11] = LM3530_DEF_ZT_2;  /* LM3530_ALS_Z2T_REG */
 245        reg_val[12] = LM3530_DEF_ZT_3;  /* LM3530_ALS_Z3T_REG */
 246        reg_val[13] = LM3530_DEF_ZT_4;  /* LM3530_ALS_Z4T_REG */
 247
 248        if (!drvdata->enable) {
 249                ret = regulator_enable(drvdata->regulator);
 250                if (ret) {
 251                        dev_err(&drvdata->client->dev,
 252                                        "Enable regulator failed\n");
 253                        return ret;
 254                }
 255                drvdata->enable = true;
 256        }
 257
 258        for (i = 0; i < LM3530_REG_MAX; i++) {
 259                /* do not update brightness register when pwm mode */
 260                if (lm3530_reg[i] == LM3530_BRT_CTRL_REG &&
 261                    drvdata->mode == LM3530_BL_MODE_PWM) {
 262                        if (pwm->pwm_set_intensity)
 263                                pwm->pwm_set_intensity(reg_val[i],
 264                                        drvdata->led_dev.max_brightness);
 265                        continue;
 266                }
 267
 268                ret = i2c_smbus_write_byte_data(client,
 269                                lm3530_reg[i], reg_val[i]);
 270                if (ret)
 271                        break;
 272        }
 273
 274        return ret;
 275}
 276
 277static void lm3530_brightness_set(struct led_classdev *led_cdev,
 278                                     enum led_brightness brt_val)
 279{
 280        int err;
 281        struct lm3530_data *drvdata =
 282            container_of(led_cdev, struct lm3530_data, led_dev);
 283        struct lm3530_platform_data *pdata = drvdata->pdata;
 284        struct lm3530_pwm_data *pwm = &pdata->pwm_data;
 285        u8 max_brightness = led_cdev->max_brightness;
 286
 287        switch (drvdata->mode) {
 288        case LM3530_BL_MODE_MANUAL:
 289
 290                if (!drvdata->enable) {
 291                        err = lm3530_init_registers(drvdata);
 292                        if (err) {
 293                                dev_err(&drvdata->client->dev,
 294                                        "Register Init failed: %d\n", err);
 295                                break;
 296                        }
 297                }
 298
 299                /* set the brightness in brightness control register*/
 300                err = i2c_smbus_write_byte_data(drvdata->client,
 301                                LM3530_BRT_CTRL_REG, brt_val);
 302                if (err)
 303                        dev_err(&drvdata->client->dev,
 304                                "Unable to set brightness: %d\n", err);
 305                else
 306                        drvdata->brightness = brt_val;
 307
 308                if (brt_val == 0) {
 309                        err = regulator_disable(drvdata->regulator);
 310                        if (err)
 311                                dev_err(&drvdata->client->dev,
 312                                        "Disable regulator failed\n");
 313                        drvdata->enable = false;
 314                }
 315                break;
 316        case LM3530_BL_MODE_ALS:
 317                break;
 318        case LM3530_BL_MODE_PWM:
 319                if (pwm->pwm_set_intensity)
 320                        pwm->pwm_set_intensity(brt_val, max_brightness);
 321                break;
 322        default:
 323                break;
 324        }
 325}
 326
 327static ssize_t lm3530_mode_get(struct device *dev,
 328                struct device_attribute *attr, char *buf)
 329{
 330        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 331        struct lm3530_data *drvdata;
 332        int i, len = 0;
 333
 334        drvdata = container_of(led_cdev, struct lm3530_data, led_dev);
 335        for (i = 0; i < ARRAY_SIZE(mode_map); i++)
 336                if (drvdata->mode == mode_map[i].mode_val)
 337                        len += sprintf(buf + len, "[%s] ", mode_map[i].mode);
 338                else
 339                        len += sprintf(buf + len, "%s ", mode_map[i].mode);
 340
 341        len += sprintf(buf + len, "\n");
 342
 343        return len;
 344}
 345
 346static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
 347                                   *attr, const char *buf, size_t size)
 348{
 349        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 350        struct lm3530_data *drvdata;
 351        struct lm3530_pwm_data *pwm;
 352        u8 max_brightness;
 353        int mode, err;
 354
 355        drvdata = container_of(led_cdev, struct lm3530_data, led_dev);
 356        pwm = &drvdata->pdata->pwm_data;
 357        max_brightness = led_cdev->max_brightness;
 358        mode = lm3530_get_mode_from_str(buf);
 359        if (mode < 0) {
 360                dev_err(dev, "Invalid mode\n");
 361                return mode;
 362        }
 363
 364        drvdata->mode = mode;
 365
 366        /* set pwm to low if unnecessary */
 367        if (mode != LM3530_BL_MODE_PWM && pwm->pwm_set_intensity)
 368                pwm->pwm_set_intensity(0, max_brightness);
 369
 370        err = lm3530_init_registers(drvdata);
 371        if (err) {
 372                dev_err(dev, "Setting %s Mode failed :%d\n", buf, err);
 373                return err;
 374        }
 375
 376        return sizeof(drvdata->mode);
 377}
 378static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set);
 379
 380static int __devinit lm3530_probe(struct i2c_client *client,
 381                           const struct i2c_device_id *id)
 382{
 383        struct lm3530_platform_data *pdata = client->dev.platform_data;
 384        struct lm3530_data *drvdata;
 385        int err = 0;
 386
 387        if (pdata == NULL) {
 388                dev_err(&client->dev, "platform data required\n");
 389                return -ENODEV;
 390        }
 391
 392        /* BL mode */
 393        if (pdata->mode > LM3530_BL_MODE_PWM) {
 394                dev_err(&client->dev, "Illegal Mode request\n");
 395                return -EINVAL;
 396        }
 397
 398        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
 399                dev_err(&client->dev, "I2C_FUNC_I2C not supported\n");
 400                return -EIO;
 401        }
 402
 403        drvdata = devm_kzalloc(&client->dev, sizeof(struct lm3530_data),
 404                                GFP_KERNEL);
 405        if (drvdata == NULL)
 406                return -ENOMEM;
 407
 408        drvdata->mode = pdata->mode;
 409        drvdata->client = client;
 410        drvdata->pdata = pdata;
 411        drvdata->brightness = LED_OFF;
 412        drvdata->enable = false;
 413        drvdata->led_dev.name = LM3530_LED_DEV;
 414        drvdata->led_dev.brightness_set = lm3530_brightness_set;
 415        drvdata->led_dev.max_brightness = MAX_BRIGHTNESS;
 416
 417        i2c_set_clientdata(client, drvdata);
 418
 419        drvdata->regulator = devm_regulator_get(&client->dev, "vin");
 420        if (IS_ERR(drvdata->regulator)) {
 421                dev_err(&client->dev, "regulator get failed\n");
 422                err = PTR_ERR(drvdata->regulator);
 423                drvdata->regulator = NULL;
 424                return err;
 425        }
 426
 427        if (drvdata->pdata->brt_val) {
 428                err = lm3530_init_registers(drvdata);
 429                if (err < 0) {
 430                        dev_err(&client->dev,
 431                                "Register Init failed: %d\n", err);
 432                        return err;
 433                }
 434        }
 435        err = led_classdev_register(&client->dev, &drvdata->led_dev);
 436        if (err < 0) {
 437                dev_err(&client->dev, "Register led class failed: %d\n", err);
 438                return err;
 439        }
 440
 441        err = device_create_file(drvdata->led_dev.dev, &dev_attr_mode);
 442        if (err < 0) {
 443                dev_err(&client->dev, "File device creation failed: %d\n", err);
 444                err = -ENODEV;
 445                goto err_create_file;
 446        }
 447
 448        return 0;
 449
 450err_create_file:
 451        led_classdev_unregister(&drvdata->led_dev);
 452        return err;
 453}
 454
 455static int __devexit lm3530_remove(struct i2c_client *client)
 456{
 457        struct lm3530_data *drvdata = i2c_get_clientdata(client);
 458
 459        device_remove_file(drvdata->led_dev.dev, &dev_attr_mode);
 460
 461        if (drvdata->enable)
 462                regulator_disable(drvdata->regulator);
 463        led_classdev_unregister(&drvdata->led_dev);
 464        return 0;
 465}
 466
 467static const struct i2c_device_id lm3530_id[] = {
 468        {LM3530_NAME, 0},
 469        {}
 470};
 471MODULE_DEVICE_TABLE(i2c, lm3530_id);
 472
 473static struct i2c_driver lm3530_i2c_driver = {
 474        .probe = lm3530_probe,
 475        .remove = __devexit_p(lm3530_remove),
 476        .id_table = lm3530_id,
 477        .driver = {
 478                .name = LM3530_NAME,
 479                .owner = THIS_MODULE,
 480        },
 481};
 482
 483module_i2c_driver(lm3530_i2c_driver);
 484
 485MODULE_DESCRIPTION("Back Light driver for LM3530");
 486MODULE_LICENSE("GPL v2");
 487MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>");
 488
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.