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}
 258
 259/* flash */
 260static void lm3639_flash_brightness_set(struct led_classdev *cdev,
 261                                        enum led_brightness brightness)
 262{
 263        int ret;
 264        unsigned int reg_val;
 265        struct lm3639_chip_data *pchip;
 266
 267        pchip = container_of(cdev, struct lm3639_chip_data, cdev_flash);
 268
 269        ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val);
 270        if (ret < 0)
 271                goto out;
 272        if (reg_val != 0)
 273                dev_info(pchip->dev, "last flag is 0x%x\n", reg_val);
 274
 275        /* torch off before flash control */
 276        ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00);
 277        if (ret < 0)
 278                goto out;
 279
 280        /* brightness 0 means off state */
 281        if (!brightness)
 282                return;
 283
 284        ret = regmap_update_bits(pchip->regmap,
 285                                 REG_FL_CONF_1, 0x0F, brightness - 1);
 286        if (ret < 0)
 287                goto out;
 288        ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x06);
 289        if (ret < 0)
 290                goto out;
 291
 292        return;
 293out:
 294        dev_err(pchip->dev, "i2c failed to access register\n");
 295}
 296
 297static const struct regmap_config lm3639_regmap = {
 298        .reg_bits = 8,
 299        .val_bits = 8,
 300        .max_register = REG_MAX,
 301};
 302
 303static int lm3639_probe(struct i2c_client *client,
 304                                  const struct i2c_device_id *id)
 305{
 306        int ret;
 307        struct lm3639_chip_data *pchip;
 308        struct lm3639_platform_data *pdata = dev_get_platdata(&client->dev);
 309        struct backlight_properties props;
 310
 311        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
 312                dev_err(&client->dev, "i2c functionality check fail.\n");
 313                return -EOPNOTSUPP;
 314        }
 315
 316        if (pdata == NULL) {
 317                dev_err(&client->dev, "Needs Platform Data.\n");
 318                return -ENODATA;
 319        }
 320
 321        pchip = devm_kzalloc(&client->dev,
 322                             sizeof(struct lm3639_chip_data), GFP_KERNEL);
 323        if (!pchip)
 324                return -ENOMEM;
 325
 326        pchip->pdata = pdata;
 327        pchip->dev = &client->dev;
 328
 329        pchip->regmap = devm_regmap_init_i2c(client, &lm3639_regmap);
 330        if (IS_ERR(pchip->regmap)) {
 331                ret = PTR_ERR(pchip->regmap);
 332                dev_err(&client->dev, "fail : allocate register map: %d\n",
 333                        ret);
 334                return ret;
 335        }
 336        i2c_set_clientdata(client, pchip);
 337
 338        /* chip initialize */
 339        ret = lm3639_chip_init(pchip);
 340        if (ret < 0) {
 341                dev_err(&client->dev, "fail : chip init\n");
 342                goto err_out;
 343        }
 344
 345        /* backlight */
 346        props.type = BACKLIGHT_RAW;
 347        props.brightness = pdata->init_brt_led;
 348        props.max_brightness = pdata->max_brt_led;
 349        pchip->bled =
 350            devm_backlight_device_register(pchip->dev, "lm3639_bled",
 351                                           pchip->dev, pchip, &lm3639_bled_ops,
 352                                           &props);
 353        if (IS_ERR(pchip->bled)) {
 354                dev_err(&client->dev, "fail : backlight register\n");
 355                ret = PTR_ERR(pchip->bled);
 356                goto err_out;
 357        }
 358
 359        ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode);
 360        if (ret < 0) {
 361                dev_err(&client->dev, "failed : add sysfs entries\n");
 362                goto err_out;
 363        }
 364
 365        /* flash */
 366        pchip->cdev_flash.name = "lm3639_flash";
 367        pchip->cdev_flash.max_brightness = 16;
 368        pchip->cdev_flash.brightness_set = lm3639_flash_brightness_set;
 369        ret = led_classdev_register((struct device *)
 370                                    &client->dev, &pchip->cdev_flash);
 371        if (ret < 0) {
 372                dev_err(&client->dev, "fail : flash register\n");
 373                goto err_flash;
 374        }
 375
 376        /* torch */
 377        pchip->cdev_torch.name = "lm3639_torch";
 378        pchip->cdev_torch.max_brightness = 8;
 379        pchip->cdev_torch.brightness_set = lm3639_torch_brightness_set;
 380        ret = led_classdev_register((struct device *)
 381                                    &client->dev, &pchip->cdev_torch);
 382        if (ret < 0) {
 383                dev_err(&client->dev, "fail : torch register\n");
 384                goto err_torch;
 385        }
 386
 387        return 0;
 388
 389err_torch:
 390        led_classdev_unregister(&pchip->cdev_flash);
 391err_flash:
 392        device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode);
 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        return 0;
 410}
 411
 412static const struct i2c_device_id lm3639_id[] = {
 413        {LM3639_NAME, 0},
 414        {}
 415};
 416
 417MODULE_DEVICE_TABLE(i2c, lm3639_id);
 418static struct i2c_driver lm3639_i2c_driver = {
 419        .driver = {
 420                   .name = LM3639_NAME,
 421                   },
 422        .probe = lm3639_probe,
 423        .remove = lm3639_remove,
 424        .id_table = lm3639_id,
 425};
 426
 427module_i2c_driver(lm3639_i2c_driver);
 428
 429MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639");
 430MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
 431MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
 432MODULE_LICENSE("GPL v2");
 433