root/drivers/leds/leds-lm3530.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lm3530_get_mode_from_str
  2. lm3530_als_configure
  3. lm3530_led_enable
  4. lm3530_led_disable
  5. lm3530_init_registers
  6. lm3530_brightness_set
  7. lm3530_mode_get
  8. lm3530_mode_set
  9. lm3530_probe
  10. lm3530_remove

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

/* [<][>][^][v][top][bottom][index][help] */