linux/drivers/leds/leds-lm3556.c
<<
>>
Prefs
   1/*
   2 * Simple driver for Texas Instruments LM3556 LED Flash driver chip (Rev0x03)
   3 * Copyright (C) 2012 Texas Instruments
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 *
   9 * Please refer Documentation/leds/leds-lm3556.txt file.
  10 */
  11#include <linux/module.h>
  12#include <linux/delay.h>
  13#include <linux/i2c.h>
  14#include <linux/leds.h>
  15#include <linux/slab.h>
  16#include <linux/platform_device.h>
  17#include <linux/fs.h>
  18#include <linux/regmap.h>
  19#include <linux/platform_data/leds-lm3556.h>
  20
  21#define REG_FILT_TIME                   (0x0)
  22#define REG_IVFM_MODE                   (0x1)
  23#define REG_NTC                         (0x2)
  24#define REG_INDIC_TIME                  (0x3)
  25#define REG_INDIC_BLINK                 (0x4)
  26#define REG_INDIC_PERIOD                (0x5)
  27#define REG_TORCH_TIME                  (0x6)
  28#define REG_CONF                        (0x7)
  29#define REG_FLASH                       (0x8)
  30#define REG_I_CTRL                      (0x9)
  31#define REG_ENABLE                      (0xA)
  32#define REG_FLAG                        (0xB)
  33#define REG_MAX                         (0xB)
  34
  35#define IVFM_FILTER_TIME_SHIFT          (3)
  36#define UVLO_EN_SHIFT                   (7)
  37#define HYSTERSIS_SHIFT                 (5)
  38#define IVM_D_TH_SHIFT                  (2)
  39#define IVFM_ADJ_MODE_SHIFT             (0)
  40#define NTC_EVENT_LVL_SHIFT             (5)
  41#define NTC_TRIP_TH_SHIFT               (2)
  42#define NTC_BIAS_I_LVL_SHIFT            (0)
  43#define INDIC_RAMP_UP_TIME_SHIFT        (3)
  44#define INDIC_RAMP_DN_TIME_SHIFT        (0)
  45#define INDIC_N_BLANK_SHIFT             (4)
  46#define INDIC_PULSE_TIME_SHIFT          (0)
  47#define INDIC_N_PERIOD_SHIFT            (0)
  48#define TORCH_RAMP_UP_TIME_SHIFT        (3)
  49#define TORCH_RAMP_DN_TIME_SHIFT        (0)
  50#define STROBE_USUAGE_SHIFT             (7)
  51#define STROBE_PIN_POLARITY_SHIFT       (6)
  52#define TORCH_PIN_POLARITY_SHIFT        (5)
  53#define TX_PIN_POLARITY_SHIFT           (4)
  54#define TX_EVENT_LVL_SHIFT              (3)
  55#define IVFM_EN_SHIFT                   (2)
  56#define NTC_MODE_SHIFT                  (1)
  57#define INDIC_MODE_SHIFT                (0)
  58#define INDUCTOR_I_LIMIT_SHIFT          (6)
  59#define FLASH_RAMP_TIME_SHIFT           (3)
  60#define FLASH_TOUT_TIME_SHIFT           (0)
  61#define TORCH_I_SHIFT                   (4)
  62#define FLASH_I_SHIFT                   (0)
  63#define NTC_EN_SHIFT                    (7)
  64#define TX_PIN_EN_SHIFT                 (6)
  65#define STROBE_PIN_EN_SHIFT             (5)
  66#define TORCH_PIN_EN_SHIFT              (4)
  67#define PRECHG_MODE_EN_SHIFT            (3)
  68#define PASS_MODE_ONLY_EN_SHIFT         (2)
  69#define MODE_BITS_SHIFT                 (0)
  70
  71#define IVFM_FILTER_TIME_MASK           (0x3)
  72#define UVLO_EN_MASK                    (0x1)
  73#define HYSTERSIS_MASK                  (0x3)
  74#define IVM_D_TH_MASK                   (0x7)
  75#define IVFM_ADJ_MODE_MASK              (0x3)
  76#define NTC_EVENT_LVL_MASK              (0x1)
  77#define NTC_TRIP_TH_MASK                (0x7)
  78#define NTC_BIAS_I_LVL_MASK             (0x3)
  79#define INDIC_RAMP_UP_TIME_MASK         (0x7)
  80#define INDIC_RAMP_DN_TIME_MASK         (0x7)
  81#define INDIC_N_BLANK_MASK              (0x7)
  82#define INDIC_PULSE_TIME_MASK           (0x7)
  83#define INDIC_N_PERIOD_MASK             (0x7)
  84#define TORCH_RAMP_UP_TIME_MASK         (0x7)
  85#define TORCH_RAMP_DN_TIME_MASK         (0x7)
  86#define STROBE_USUAGE_MASK              (0x1)
  87#define STROBE_PIN_POLARITY_MASK        (0x1)
  88#define TORCH_PIN_POLARITY_MASK         (0x1)
  89#define TX_PIN_POLARITY_MASK            (0x1)
  90#define TX_EVENT_LVL_MASK               (0x1)
  91#define IVFM_EN_MASK                    (0x1)
  92#define NTC_MODE_MASK                   (0x1)
  93#define INDIC_MODE_MASK                 (0x1)
  94#define INDUCTOR_I_LIMIT_MASK           (0x3)
  95#define FLASH_RAMP_TIME_MASK            (0x7)
  96#define FLASH_TOUT_TIME_MASK            (0x7)
  97#define TORCH_I_MASK                    (0x7)
  98#define FLASH_I_MASK                    (0xF)
  99#define NTC_EN_MASK                     (0x1)
 100#define TX_PIN_EN_MASK                  (0x1)
 101#define STROBE_PIN_EN_MASK              (0x1)
 102#define TORCH_PIN_EN_MASK               (0x1)
 103#define PRECHG_MODE_EN_MASK             (0x1)
 104#define PASS_MODE_ONLY_EN_MASK          (0x1)
 105#define MODE_BITS_MASK                  (0x13)
 106#define EX_PIN_CONTROL_MASK             (0xF1)
 107#define EX_PIN_ENABLE_MASK              (0x70)
 108
 109enum lm3556_indic_pulse_time {
 110        PULSE_TIME_0_MS = 0,
 111        PULSE_TIME_32_MS,
 112        PULSE_TIME_64_MS,
 113        PULSE_TIME_92_MS,
 114        PULSE_TIME_128_MS,
 115        PULSE_TIME_160_MS,
 116        PULSE_TIME_196_MS,
 117        PULSE_TIME_224_MS,
 118        PULSE_TIME_256_MS,
 119        PULSE_TIME_288_MS,
 120        PULSE_TIME_320_MS,
 121        PULSE_TIME_352_MS,
 122        PULSE_TIME_384_MS,
 123        PULSE_TIME_416_MS,
 124        PULSE_TIME_448_MS,
 125        PULSE_TIME_480_MS,
 126};
 127
 128enum lm3556_indic_n_blank {
 129        INDIC_N_BLANK_0 = 0,
 130        INDIC_N_BLANK_1,
 131        INDIC_N_BLANK_2,
 132        INDIC_N_BLANK_3,
 133        INDIC_N_BLANK_4,
 134        INDIC_N_BLANK_5,
 135        INDIC_N_BLANK_6,
 136        INDIC_N_BLANK_7,
 137        INDIC_N_BLANK_8,
 138        INDIC_N_BLANK_9,
 139        INDIC_N_BLANK_10,
 140        INDIC_N_BLANK_11,
 141        INDIC_N_BLANK_12,
 142        INDIC_N_BLANK_13,
 143        INDIC_N_BLANK_14,
 144        INDIC_N_BLANK_15,
 145};
 146
 147enum lm3556_indic_period {
 148        INDIC_PERIOD_0 = 0,
 149        INDIC_PERIOD_1,
 150        INDIC_PERIOD_2,
 151        INDIC_PERIOD_3,
 152        INDIC_PERIOD_4,
 153        INDIC_PERIOD_5,
 154        INDIC_PERIOD_6,
 155        INDIC_PERIOD_7,
 156};
 157
 158enum lm3556_mode {
 159        MODES_STASNDBY = 0,
 160        MODES_INDIC,
 161        MODES_TORCH,
 162        MODES_FLASH
 163};
 164
 165#define INDIC_PATTERN_SIZE 4
 166
 167struct indicator {
 168        u8 blinking;
 169        u8 period_cnt;
 170};
 171
 172struct lm3556_chip_data {
 173        struct device *dev;
 174
 175        struct led_classdev cdev_flash;
 176        struct led_classdev cdev_torch;
 177        struct led_classdev cdev_indicator;
 178
 179        struct lm3556_platform_data *pdata;
 180        struct regmap *regmap;
 181        struct mutex lock;
 182
 183        unsigned int last_flag;
 184};
 185
 186/* indicator pattern */
 187static struct indicator indicator_pattern[INDIC_PATTERN_SIZE] = {
 188        [0] = {(INDIC_N_BLANK_1 << INDIC_N_BLANK_SHIFT)
 189               | PULSE_TIME_32_MS, INDIC_PERIOD_1},
 190        [1] = {(INDIC_N_BLANK_15 << INDIC_N_BLANK_SHIFT)
 191               | PULSE_TIME_32_MS, INDIC_PERIOD_2},
 192        [2] = {(INDIC_N_BLANK_10 << INDIC_N_BLANK_SHIFT)
 193               | PULSE_TIME_32_MS, INDIC_PERIOD_4},
 194        [3] = {(INDIC_N_BLANK_5 << INDIC_N_BLANK_SHIFT)
 195               | PULSE_TIME_32_MS, INDIC_PERIOD_7},
 196};
 197
 198/* chip initialize */
 199static int __devinit lm3556_chip_init(struct lm3556_chip_data *chip)
 200{
 201        unsigned int reg_val;
 202        int ret;
 203        struct lm3556_platform_data *pdata = chip->pdata;
 204
 205        /* set config register */
 206        ret = regmap_read(chip->regmap, REG_CONF, &reg_val);
 207        if (ret < 0) {
 208                dev_err(chip->dev, "Failed to read REG_CONF Register\n");
 209                goto out;
 210        }
 211
 212        reg_val &= (~EX_PIN_CONTROL_MASK);
 213        reg_val |= ((pdata->torch_pin_polarity & 0x01)
 214                    << TORCH_PIN_POLARITY_SHIFT);
 215        reg_val |= ((pdata->strobe_usuage & 0x01) << STROBE_USUAGE_SHIFT);
 216        reg_val |= ((pdata->strobe_pin_polarity & 0x01)
 217                    << STROBE_PIN_POLARITY_SHIFT);
 218        reg_val |= ((pdata->tx_pin_polarity & 0x01) << TX_PIN_POLARITY_SHIFT);
 219        reg_val |= ((pdata->indicator_mode & 0x01) << INDIC_MODE_SHIFT);
 220
 221        ret = regmap_write(chip->regmap, REG_CONF, reg_val);
 222        if (ret < 0) {
 223                dev_err(chip->dev, "Failed to write REG_CONF Regisgter\n");
 224                goto out;
 225        }
 226
 227        /* set enable register */
 228        ret = regmap_read(chip->regmap, REG_ENABLE, &reg_val);
 229        if (ret < 0) {
 230                dev_err(chip->dev, "Failed to read REG_ENABLE Register\n");
 231                goto out;
 232        }
 233
 234        reg_val &= (~EX_PIN_ENABLE_MASK);
 235        reg_val |= ((pdata->torch_pin_en & 0x01) << TORCH_PIN_EN_SHIFT);
 236        reg_val |= ((pdata->strobe_pin_en & 0x01) << STROBE_PIN_EN_SHIFT);
 237        reg_val |= ((pdata->tx_pin_en & 0x01) << TX_PIN_EN_SHIFT);
 238
 239        ret = regmap_write(chip->regmap, REG_ENABLE, reg_val);
 240        if (ret < 0) {
 241                dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n");
 242                goto out;
 243        }
 244
 245out:
 246        return ret;
 247}
 248
 249/* chip control */
 250static int lm3556_control(struct lm3556_chip_data *chip,
 251                          u8 brightness, enum lm3556_mode opmode)
 252{
 253        int ret;
 254        struct lm3556_platform_data *pdata = chip->pdata;
 255
 256        ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag);
 257        if (ret < 0) {
 258                dev_err(chip->dev, "Failed to read REG_FLAG Register\n");
 259                goto out;
 260        }
 261
 262        if (chip->last_flag)
 263                dev_info(chip->dev, "Last FLAG is 0x%x\n", chip->last_flag);
 264
 265        /* brightness 0 means off-state */
 266        if (!brightness)
 267                opmode = MODES_STASNDBY;
 268
 269        switch (opmode) {
 270        case MODES_TORCH:
 271                ret = regmap_update_bits(chip->regmap, REG_I_CTRL,
 272                                         TORCH_I_MASK << TORCH_I_SHIFT,
 273                                         (brightness - 1) << TORCH_I_SHIFT);
 274
 275                if (pdata->torch_pin_en)
 276                        opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT);
 277                break;
 278
 279        case MODES_FLASH:
 280                ret = regmap_update_bits(chip->regmap, REG_I_CTRL,
 281                                         FLASH_I_MASK << FLASH_I_SHIFT,
 282                                         (brightness - 1) << FLASH_I_SHIFT);
 283                break;
 284
 285        case MODES_INDIC:
 286                ret = regmap_update_bits(chip->regmap, REG_I_CTRL,
 287                                         TORCH_I_MASK << TORCH_I_SHIFT,
 288                                         (brightness - 1) << TORCH_I_SHIFT);
 289                break;
 290
 291        case MODES_STASNDBY:
 292                if (pdata->torch_pin_en)
 293                        opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT);
 294                break;
 295
 296        default:
 297                return ret;
 298        }
 299        if (ret < 0) {
 300                dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n");
 301                goto out;
 302        }
 303        ret = regmap_update_bits(chip->regmap, REG_ENABLE,
 304                                 MODE_BITS_MASK << MODE_BITS_SHIFT,
 305                                 opmode << MODE_BITS_SHIFT);
 306
 307out:
 308        return ret;
 309}
 310
 311/* torch */
 312static void lm3556_torch_brightness_set(struct led_classdev *cdev,
 313                                        enum led_brightness brightness)
 314{
 315        struct lm3556_chip_data *chip =
 316            container_of(cdev, struct lm3556_chip_data, cdev_torch);
 317
 318        mutex_lock(&chip->lock);
 319        lm3556_control(chip, brightness, MODES_TORCH);
 320        mutex_unlock(&chip->lock);
 321}
 322
 323/* flash */
 324static void lm3556_strobe_brightness_set(struct led_classdev *cdev,
 325                                         enum led_brightness brightness)
 326{
 327        struct lm3556_chip_data *chip =
 328            container_of(cdev, struct lm3556_chip_data, cdev_flash);
 329
 330        mutex_lock(&chip->lock);
 331        lm3556_control(chip, brightness, MODES_FLASH);
 332        mutex_unlock(&chip->lock);
 333}
 334
 335/* indicator */
 336static void lm3556_indicator_brightness_set(struct led_classdev *cdev,
 337                                            enum led_brightness brightness)
 338{
 339        struct lm3556_chip_data *chip =
 340            container_of(cdev, struct lm3556_chip_data, cdev_indicator);
 341
 342        mutex_lock(&chip->lock);
 343        lm3556_control(chip, brightness, MODES_INDIC);
 344        mutex_unlock(&chip->lock);
 345}
 346
 347/* indicator pattern */
 348static ssize_t lm3556_indicator_pattern_store(struct device *dev,
 349                                              struct device_attribute *devAttr,
 350                                              const char *buf, size_t size)
 351{
 352        ssize_t ret;
 353        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 354        struct lm3556_chip_data *chip =
 355            container_of(led_cdev, struct lm3556_chip_data, cdev_indicator);
 356        unsigned int state;
 357
 358        ret = kstrtouint(buf, 10, &state);
 359        if (ret)
 360                goto out;
 361        if (state > INDIC_PATTERN_SIZE - 1)
 362                state = INDIC_PATTERN_SIZE - 1;
 363
 364        ret = regmap_write(chip->regmap, REG_INDIC_BLINK,
 365                           indicator_pattern[state].blinking);
 366        if (ret < 0) {
 367                dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n");
 368                goto out;
 369        }
 370
 371        ret = regmap_write(chip->regmap, REG_INDIC_PERIOD,
 372                           indicator_pattern[state].period_cnt);
 373        if (ret < 0) {
 374                dev_err(chip->dev, "Failed to write REG_ENABLE Regisgter\n");
 375                goto out;
 376        }
 377
 378        return size;
 379out:
 380        dev_err(chip->dev, "Indicator pattern doesn't saved\n");
 381        return size;
 382}
 383
 384static DEVICE_ATTR(pattern, 0666, NULL, lm3556_indicator_pattern_store);
 385
 386static const struct regmap_config lm3556_regmap = {
 387        .reg_bits = 8,
 388        .val_bits = 8,
 389        .max_register = REG_MAX,
 390};
 391
 392/* module initialize */
 393static int __devinit lm3556_probe(struct i2c_client *client,
 394                                  const struct i2c_device_id *id)
 395{
 396        struct lm3556_platform_data *pdata = client->dev.platform_data;
 397        struct lm3556_chip_data *chip;
 398
 399        int err;
 400
 401        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
 402                dev_err(&client->dev, "i2c functionality check fail.\n");
 403                return -EOPNOTSUPP;
 404        }
 405
 406        if (pdata == NULL) {
 407                dev_err(&client->dev, "Needs Platform Data.\n");
 408                return -ENODATA;
 409        }
 410
 411        chip =
 412            devm_kzalloc(&client->dev, sizeof(struct lm3556_chip_data),
 413                         GFP_KERNEL);
 414        if (!chip)
 415                return -ENOMEM;
 416
 417        chip->dev = &client->dev;
 418        chip->pdata = pdata;
 419
 420        chip->regmap = devm_regmap_init_i2c(client, &lm3556_regmap);
 421        if (IS_ERR(chip->regmap)) {
 422                err = PTR_ERR(chip->regmap);
 423                dev_err(&client->dev, "Failed to allocate register map: %d\n",
 424                        err);
 425                return err;
 426        }
 427
 428        mutex_init(&chip->lock);
 429        i2c_set_clientdata(client, chip);
 430
 431        err = lm3556_chip_init(chip);
 432        if (err < 0)
 433                goto err_out;
 434
 435        /* flash */
 436        chip->cdev_flash.name = "flash";
 437        chip->cdev_flash.max_brightness = 16;
 438        chip->cdev_flash.brightness_set = lm3556_strobe_brightness_set;
 439        err = led_classdev_register((struct device *)
 440                                    &client->dev, &chip->cdev_flash);
 441        if (err < 0)
 442                goto err_out;
 443        /* torch */
 444        chip->cdev_torch.name = "torch";
 445        chip->cdev_torch.max_brightness = 8;
 446        chip->cdev_torch.brightness_set = lm3556_torch_brightness_set;
 447        err = led_classdev_register((struct device *)
 448                                    &client->dev, &chip->cdev_torch);
 449        if (err < 0)
 450                goto err_create_torch_file;
 451        /* indicator */
 452        chip->cdev_indicator.name = "indicator";
 453        chip->cdev_indicator.max_brightness = 8;
 454        chip->cdev_indicator.brightness_set = lm3556_indicator_brightness_set;
 455        err = led_classdev_register((struct device *)
 456                                    &client->dev, &chip->cdev_indicator);
 457        if (err < 0)
 458                goto err_create_indicator_file;
 459
 460        err = device_create_file(chip->cdev_indicator.dev, &dev_attr_pattern);
 461        if (err < 0)
 462                goto err_create_pattern_file;
 463
 464        dev_info(&client->dev, "LM3556 is initialized\n");
 465        return 0;
 466
 467err_create_pattern_file:
 468        led_classdev_unregister(&chip->cdev_indicator);
 469err_create_indicator_file:
 470        led_classdev_unregister(&chip->cdev_torch);
 471err_create_torch_file:
 472        led_classdev_unregister(&chip->cdev_flash);
 473err_out:
 474        return err;
 475}
 476
 477static int __devexit lm3556_remove(struct i2c_client *client)
 478{
 479        struct lm3556_chip_data *chip = i2c_get_clientdata(client);
 480
 481        device_remove_file(chip->cdev_indicator.dev, &dev_attr_pattern);
 482        led_classdev_unregister(&chip->cdev_indicator);
 483        led_classdev_unregister(&chip->cdev_torch);
 484        led_classdev_unregister(&chip->cdev_flash);
 485        regmap_write(chip->regmap, REG_ENABLE, 0);
 486        return 0;
 487}
 488
 489static const struct i2c_device_id lm3556_id[] = {
 490        {LM3556_NAME, 0},
 491        {}
 492};
 493
 494MODULE_DEVICE_TABLE(i2c, lm3556_id);
 495
 496static struct i2c_driver lm3556_i2c_driver = {
 497        .driver = {
 498                   .name = LM3556_NAME,
 499                   .owner = THIS_MODULE,
 500                   .pm = NULL,
 501                   },
 502        .probe = lm3556_probe,
 503        .remove = __devexit_p(lm3556_remove),
 504        .id_table = lm3556_id,
 505};
 506
 507module_i2c_driver(lm3556_i2c_driver);
 508
 509MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3556");
 510MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>");
 511MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>");
 512MODULE_LICENSE("GPL v2");
 513
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.