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