linux/drivers/video/backlight/lm3639_bl.c
<<
>>
Prefs
   1/*
   2* Simple driver for Texas Instruments LM3639 Backlight + Flash LED driver chip
   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*/
  10#include <linux/module.h>
  11#include <linux/slab.h>
  12#include <linux/i2c.h>
  13#include <linux/leds.h>
  14#include <linux/backlight.h>
  15#include <linux/err.h>
  16#include <linux/delay.h>
  17#include <linux/uaccess.h>
  18#include <linux/interrupt.h>
  19#include <linux/regmap.h>
  20#include <linux/platform_data/lm3639_bl.h>
  21
  22#define REG_DEV_ID      0x00
  23#define REG_CHECKSUM    0x01
  24#define REG_BL_CONF_1   0x02
  25#define REG_BL_CONF_2   0x03
  26#define REG_BL_CONF_3   0x04
  27#define REG_BL_CONF_4   0x05
  28#define REG_FL_CONF_1   0x06
  29#define REG_FL_CONF_2   0x07
  30#define REG_FL_CONF_3   0x08
  31#define REG_IO_CTRL     0x09
  32#define REG_ENABLE      0x0A
  33#define REG_FLAG        0x0B
  34#define REG_MAX         REG_FLAG
  35
  36struct lm3639_chip_data {
  37        struct device *dev;
  38        struct lm3639_platform_data *pdata;
  39
  40        struct backlight_device *bled;
  41        struct led_classdev cdev_flash;
  42        struct led_classdev cdev_torch;
  43        struct regmap *regmap;
  44
  45        unsigned int bled_mode;
  46        unsigned int bled_map;
  47        unsigned int last_flag;
  48};
  49
  50/* initialize chip */
  51static int lm3639_chip_init(struct lm3639_chip_data *pchip)
  52{
  53        int ret;
  54        unsigned int reg_val;
  55        struct lm3639_platform_data *pdata = pchip->pdata;
  56
  57        /* input pins config. */
  58        ret =
  59            regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x08,
  60                               pdata->pin_pwm);
  61        if (ret < 0)
  62                goto out;
  63
  64        reg_val = (pdata->pin_pwm & 0x40) | pdata->pin_strobe | pdata->pin_tx;
  65        ret = regmap_update_bits(pchip->regmap, REG_IO_CTRL, 0x7C, reg_val);
  66        if (ret < 0)
  67                goto out;
  68
  69        /* init brightness */
  70        ret = regmap_write(pchip->regmap, REG_BL_CONF_4, pdata->init_brt_led);
  71        if (ret < 0)
  72                goto out;
  73
  74        ret = regmap_write(pchip->regmap, REG_BL_CONF_3, pdata->init_brt_led);
  75        if (ret < 0)
  76                goto out;
  77
  78        /* output pins config. */
  79        if (!pdata->init_brt_led)
  80                reg_val = pdata->fled_pins | pdata->bled_pins;
  81        else
  82                reg_val = pdata->fled_pins | pdata->bled_pins | 0x01;
  83
  84        ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x79, reg_val);
  85        if (ret < 0)
  86                goto out;
  87
  88        return ret;
  89out:
  90        dev_err(pchip->dev, "i2c failed to access register\n");
  91        return ret;
  92}
  93
  94/* update and get brightness */
  95static int lm3639_bled_update_status(struct backlight_device *bl)
  96{
  97        int ret;
  98        unsigned int reg_val;
  99        struct lm3639_chip_data *pchip = bl_get_data(bl);
 100        struct lm3639_platform_data *pdata = pchip->pdata;
 101
 102        ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
 103        if (ret < 0)
 104                goto out;
 105
 106        if (reg_val != 0)
 107                dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
 108
 109        /* pwm control */
 110        if (pdata->pin_pwm) {
 111                if (pdata->pwm_set_intensity)
 112                        pdata->pwm_set_intensity(bl->props.brightness,
 113                                                 pdata->max_brt_led);
 114                else
 115                        dev_err(pchip->dev,
 116                                "No pwm control func. in plat-data\n");
 117                return bl->props.brightness;
 118        }
 119
 120        /* i2c control and set brigtness */
 121        ret = regmap_write(pchip->regmap, REG_BL_CONF_4, bl->props.brightness);
 122        if (ret < 0)
 123                goto out;
 124        ret = regmap_write(pchip->regmap, REG_BL_CONF_3, bl->props.brightness);
 125        if (ret < 0)
 126                goto out;
 127
 128        if (!bl->props.brightness)
 129                ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x00);
 130        else
 131                ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x01);
 132        if (ret < 0)
 133                goto out;
 134
 135        return bl->props.brightness;
 136out:
 137        dev_err(pchip->dev, "i2c failed to access registers\n");
 138        return bl->props.brightness;
 139}
 140
 141static int lm3639_bled_get_brightness(struct backlight_device *bl)
 142{
 143        int ret;
 144        unsigned int reg_val;
 145        struct lm3639_chip_data *pchip = bl_get_data(bl);
 146        struct lm3639_platform_data *pdata = pchip->pdata;
 147
 148        if (pdata->pin_pwm) {
 149                if (pdata->pwm_get_intensity)
 150                        bl->props.brightness = pdata->pwm_get_intensity();
 151                else
 152                        dev_err(pchip->dev,
 153                                "No pwm control func. in plat-data\n");
 154                return bl->props.brightness;
 155        }
 156
 157        ret = regmap_read(pchip->regmap, REG_BL_CONF_1, &reg_val);
 158        if (ret < 0)
 159                goto out;
 160        if (reg_val & 0x10)
 161                ret = regmap_read(pchip->regmap, REG_BL_CONF_4, &reg_val);
 162        else
 163                ret = regmap_read(pchip->regmap, REG_BL_CONF_3, &reg_val);
 164        if (ret < 0)
 165                goto out;
 166        bl->props.brightness = reg_val;
 167
 168        return bl->props.brightness;
 169out:
 170        dev_err(pchip->dev, "i2c failed to access register\n");
 171        return bl->props.brightness;
 172}
 173
 174static const struct backlight_ops lm3639_bled_ops = {
 175        .options = BL_CORE_SUSPENDRESUME,
 176        .update_status = lm3639_bled_update_status,
 177        .get_brightness = lm3639_bled_get_brightness,
 178};
 179
 180/* backlight mapping mode */
 181static ssize_t lm3639_bled_mode_store(struct device *dev,
 182                                      struct device_attribute *devAttr,
 183                                      const char *buf, size_t size)
 184{
 185        ssize_t ret;
 186        struct lm3639_chip_data *pchip = dev_get_drvdata(dev);
 187        unsigned int state;
 188
 189        ret = kstrtouint(buf, 10, &state);
 190        if (ret)
 191                goto out_input;
 192
 193        if (!state)
 194                ret =
 195                    regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,
 196                                       0x00);
 197        else
 198                ret =
 199                    regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10,
 200                                       0x10);
 201
 202        if (ret < 0)
 203                goto out;
 204
 205        return size;
 206
 207out:
 208        dev_err(pchip->dev, "%s:i2c access fail to register\n", __func__);
 209        return ret;
 210
 211out_input:
 212        dev_err(pchip->dev, "%s:input conversion fail\n", __func__);
 213        return ret;
 214
 215}
 216
 217static DEVICE_ATTR(bled_mode, S_IWUSR, NULL, lm3639_bled_mode_store);
 218
 219/* torch */
 220static void lm3639_torch_brightness_set(struct led_classdev *cdev,
 221                                        enum led_brightness brightness)
 222{
 223        int ret;
 224        unsigned int reg_val;
 225        struct lm3639_chip_data *pchip;
 226
 227        pchip = container_of(cdev, struct lm3639_chip_data, cdev_torch);
 228
 229        ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
 230        if (ret < 0)
 231                goto out;
 232        if (reg_val != 0)
 233                dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
 234
 235        /* brightness 0 means off state */
 236        if (!brightness) {
 237                ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
 238                if (ret < 0)
 239                        goto out;
 240                return;
 241        }
 242
 243        ret = regmap_update_bits(pchip->regmap,
 244                                 REG_FL_CONF_1, 0x70, (brightness - 1) << 4);
 245        if (ret < 0)
 246                goto out;
 247        ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x02);
 248        if (ret < 0)
 249                goto out;
 250
 251        return;
 252out:
 253        dev_err(pchip->dev, "i2c failed to access register\n");
 254        return;
 255}
 256
 257/* flash */
 258static void lm3639_flash_brightness_set(struct led_classdev *cdev,
 259                                        enum led_brightness brightness)
 260{
 261        int ret;
 262        unsigned int reg_val;
 263        struct lm3639_chip_data *pchip;
 264
 265        pchip = container_of(cdev, struct lm3639_chip_data, cdev_flash);
 266
 267        ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
 268        if (ret < 0)
 269                goto out;
 270        if (reg_val != 0)
 271                dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
 272
 273        /* torch off before flash control */
 274        ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
 275        if (ret < 0)
 276                goto out;
 277
 278        /* brightness 0 means off state */
 279        if (!brightness)
 280                return;
 281
 282        ret = regmap_update_bits(pchip->regmap,
 283                                 REG_FL_CONF_1, 0x0F, brightness - 1);
 284        if (ret < 0)
 285                goto out;
 286        ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x06);
 287        if (ret < 0)
 288                goto out;
 289
 290        return;
 291out:
 292        dev_err(pchip->dev, "i2c failed to access register\n");
 293        return;
 294}
 295
 296static const struct regmap_config lm3639_regmap = {
 297        .reg_bits = 8,
 298        .val_bits = 8,
 299        .max_register = REG_MAX,
 300};
 301
 302static int lm3639_probe(struct i2c_client *client,
 303                                  const struct i2c_device_id *id)
 304{
 305        int ret;
 306        struct lm3639_chip_data *pchip;
 307        struct lm3639_platform_data *pdata = client->dev.platform_data;
 308        struct backlight_properties props;
 309
 310        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
 311                dev_err(&client->dev, "i2c functionality check fail.\n");
 312                return -EOPNOTSUPP;
 313        }
 314
 315        if (pdata == NULL) {
 316                dev_err(&client->dev, "Needs Platform Data.\n");
 317                return -ENODATA;
 318        }
 319
 320        pchip = devm_kzalloc(&client->dev,
 321                             sizeof(struct lm3639_chip_data), GFP_KERNEL);
 322        if (!pchip)
 323                return -ENOMEM;
 324
 325        pchip->pdata = pdata;
 326        pchip->dev = &client->dev;
 327
 328        pchip->regmap = devm_regmap_init_i2c(client, &lm3639_regmap);
 329        if (IS_ERR(pchip->regmap)) {
 330                ret = PTR_ERR(pchip->regmap);
 331                dev_err(&client->dev, "fail : allocate register map: %d\n",
 332                        ret);
 333                return ret;
 334        }
 335        i2c_set_clientdata(client, pchip);
 336
 337        /* chip initialize */
 338        ret = lm3639_chip_init(pchip);
 339        if (ret < 0) {
 340                dev_err(&client->dev, "fail : chip init\n");
 341                goto err_out;
 342        }
 343
 344        /* backlight */
 345        props.type = BACKLIGHT_RAW;
 346        props.brightness = pdata->init_brt_led;
 347        props.max_brightness = pdata->max_brt_led;
 348        pchip->bled =
 349            backlight_device_register("lm3639_bled", pchip->dev, pchip,
 350                                      &lm3639_bled_ops, &props);
 351        if (IS_ERR(pchip->bled)) {
 352                dev_err(&client->dev, "fail : backlight register\n");
 353                ret = PTR_ERR(pchip->bled);
 354                goto err_out;
 355        }
 356
 357        ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode);
 358        if (ret < 0) {
 359                dev_err(&client->dev, "failed : add sysfs entries\n");
 360                goto err_bled_mode;
 361        }
 362
 363        /* flash */
 364        pchip->cdev_flash.name = "lm3639_flash";
 365        pchip->cdev_flash.max_brightness = 16;
 366        pchip->cdev_flash.brightness_set = lm3639_flash_brightness_set;
 367        ret = led_classdev_register((struct device *)
 368                                    &client->dev, &pchip->cdev_flash);
 369        if (ret < 0) {
 370                dev_err(&client->dev, "fail : flash register\n");
 371                goto err_flash;
 372        }
 373
 374        /* torch */
 375        pchip->cdev_torch.name = "lm3639_torch";
 376        pchip->cdev_torch.max_brightness = 8;
 377        pchip->cdev_torch.brightness_set = lm3639_torch_brightness_set;
 378        ret = led_classdev_register((struct device *)
 379                                    &client->dev, &pchip->cdev_torch);
 380        if (ret < 0) {
 381                dev_err(&client->dev, "fail : torch register\n");
 382                goto err_torch;
 383        }
 384
 385        return 0;
 386
 387err_torch:
 388        led_classdev_unregister(&pchip->cdev_flash);
 389err_flash:
 390        device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
 391err_bled_mode:
 392        backlight_device_unregister(pchip->bled);
 393err_out:
 394        return ret;
 395}
 396
 397static int lm3639_remove(struct i2c_client *client)
 398{
 399        struct lm3639_chip_data *pchip = i2c_get_clientdata(client);
 400
 401        regmap_write(pchip->regmap, REG_ENABLE, 0x00);
 402
 403        if (&pchip->cdev_torch)
 404                led_classdev_unregister(&pchip->cdev_torch);
 405        if (&pchip->cdev_flash)
 406                led_classdev_unregister(&pchip->cdev_flash);
 407        if (pchip->bled) {
 408                device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
 409                backlight_device_unregister(pchip->bled);
 410        }
 411        return 0;
 412}
 413
 414static const struct i2c_device_id lm3639_id[] = {
 415        {LM3639_NAME, 0},
 416        {}
 417};
 418
 419MODULE_DEVICE_TABLE(i2c, lm3639_id);
 420static struct i2c_driver lm3639_i2c_driver = {
 421        .driver = {
 422                   .name = LM3639_NAME,
 423                   },
 424        .probe = lm3639_probe,
 425        .remove = lm3639_remove,
 426        .id_table = lm3639_id,
 427};
 428
 429module_i2c_driver(lm3639_i2c_driver);
 430
 431MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639");
 432MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>");
 433MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>");
 434MODULE_LICENSE("GPL v2");
 435