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