linux/drivers/video/backlight/lp855x_bl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * TI LP855x Backlight Driver
   4 *
   5 *                      Copyright (C) 2011 Texas Instruments
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/slab.h>
  10#include <linux/i2c.h>
  11#include <linux/backlight.h>
  12#include <linux/delay.h>
  13#include <linux/err.h>
  14#include <linux/of.h>
  15#include <linux/platform_data/lp855x.h>
  16#include <linux/pwm.h>
  17#include <linux/regulator/consumer.h>
  18
  19/* LP8550/1/2/3/6 Registers */
  20#define LP855X_BRIGHTNESS_CTRL          0x00
  21#define LP855X_DEVICE_CTRL              0x01
  22#define LP855X_EEPROM_START             0xA0
  23#define LP855X_EEPROM_END               0xA7
  24#define LP8556_EPROM_START              0xA0
  25#define LP8556_EPROM_END                0xAF
  26
  27/* LP8555/7 Registers */
  28#define LP8557_BL_CMD                   0x00
  29#define LP8557_BL_MASK                  0x01
  30#define LP8557_BL_ON                    0x01
  31#define LP8557_BL_OFF                   0x00
  32#define LP8557_BRIGHTNESS_CTRL          0x04
  33#define LP8557_CONFIG                   0x10
  34#define LP8555_EPROM_START              0x10
  35#define LP8555_EPROM_END                0x7A
  36#define LP8557_EPROM_START              0x10
  37#define LP8557_EPROM_END                0x1E
  38
  39#define DEFAULT_BL_NAME         "lcd-backlight"
  40#define MAX_BRIGHTNESS          255
  41
  42enum lp855x_brightness_ctrl_mode {
  43        PWM_BASED = 1,
  44        REGISTER_BASED,
  45};
  46
  47struct lp855x;
  48
  49/*
  50 * struct lp855x_device_config
  51 * @pre_init_device: init device function call before updating the brightness
  52 * @reg_brightness: register address for brigthenss control
  53 * @reg_devicectrl: register address for device control
  54 * @post_init_device: late init device function call
  55 */
  56struct lp855x_device_config {
  57        int (*pre_init_device)(struct lp855x *);
  58        u8 reg_brightness;
  59        u8 reg_devicectrl;
  60        int (*post_init_device)(struct lp855x *);
  61};
  62
  63struct lp855x {
  64        const char *chipname;
  65        enum lp855x_chip_id chip_id;
  66        enum lp855x_brightness_ctrl_mode mode;
  67        struct lp855x_device_config *cfg;
  68        struct i2c_client *client;
  69        struct backlight_device *bl;
  70        struct device *dev;
  71        struct lp855x_platform_data *pdata;
  72        struct pwm_device *pwm;
  73        struct regulator *supply;       /* regulator for VDD input */
  74        struct regulator *enable;       /* regulator for EN/VDDIO input */
  75};
  76
  77static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data)
  78{
  79        return i2c_smbus_write_byte_data(lp->client, reg, data);
  80}
  81
  82static int lp855x_update_bit(struct lp855x *lp, u8 reg, u8 mask, u8 data)
  83{
  84        int ret;
  85        u8 tmp;
  86
  87        ret = i2c_smbus_read_byte_data(lp->client, reg);
  88        if (ret < 0) {
  89                dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
  90                return ret;
  91        }
  92
  93        tmp = (u8)ret;
  94        tmp &= ~mask;
  95        tmp |= data & mask;
  96
  97        return lp855x_write_byte(lp, reg, tmp);
  98}
  99
 100static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
 101{
 102        u8 start, end;
 103
 104        switch (lp->chip_id) {
 105        case LP8550:
 106        case LP8551:
 107        case LP8552:
 108        case LP8553:
 109                start = LP855X_EEPROM_START;
 110                end = LP855X_EEPROM_END;
 111                break;
 112        case LP8556:
 113                start = LP8556_EPROM_START;
 114                end = LP8556_EPROM_END;
 115                break;
 116        case LP8555:
 117                start = LP8555_EPROM_START;
 118                end = LP8555_EPROM_END;
 119                break;
 120        case LP8557:
 121                start = LP8557_EPROM_START;
 122                end = LP8557_EPROM_END;
 123                break;
 124        default:
 125                return false;
 126        }
 127
 128        return addr >= start && addr <= end;
 129}
 130
 131static int lp8557_bl_off(struct lp855x *lp)
 132{
 133        /* BL_ON = 0 before updating EPROM settings */
 134        return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
 135                                LP8557_BL_OFF);
 136}
 137
 138static int lp8557_bl_on(struct lp855x *lp)
 139{
 140        /* BL_ON = 1 after updating EPROM settings */
 141        return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
 142                                LP8557_BL_ON);
 143}
 144
 145static struct lp855x_device_config lp855x_dev_cfg = {
 146        .reg_brightness = LP855X_BRIGHTNESS_CTRL,
 147        .reg_devicectrl = LP855X_DEVICE_CTRL,
 148};
 149
 150static struct lp855x_device_config lp8557_dev_cfg = {
 151        .reg_brightness = LP8557_BRIGHTNESS_CTRL,
 152        .reg_devicectrl = LP8557_CONFIG,
 153        .pre_init_device = lp8557_bl_off,
 154        .post_init_device = lp8557_bl_on,
 155};
 156
 157/*
 158 * Device specific configuration flow
 159 *
 160 *    a) pre_init_device(optional)
 161 *    b) update the brightness register
 162 *    c) update device control register
 163 *    d) update ROM area(optional)
 164 *    e) post_init_device(optional)
 165 *
 166 */
 167static int lp855x_configure(struct lp855x *lp)
 168{
 169        u8 val, addr;
 170        int i, ret;
 171        struct lp855x_platform_data *pd = lp->pdata;
 172
 173        switch (lp->chip_id) {
 174        case LP8550:
 175        case LP8551:
 176        case LP8552:
 177        case LP8553:
 178        case LP8556:
 179                lp->cfg = &lp855x_dev_cfg;
 180                break;
 181        case LP8555:
 182        case LP8557:
 183                lp->cfg = &lp8557_dev_cfg;
 184                break;
 185        default:
 186                return -EINVAL;
 187        }
 188
 189        if (lp->cfg->pre_init_device) {
 190                ret = lp->cfg->pre_init_device(lp);
 191                if (ret) {
 192                        dev_err(lp->dev, "pre init device err: %d\n", ret);
 193                        goto err;
 194                }
 195        }
 196
 197        val = pd->initial_brightness;
 198        ret = lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
 199        if (ret)
 200                goto err;
 201
 202        val = pd->device_control;
 203        ret = lp855x_write_byte(lp, lp->cfg->reg_devicectrl, val);
 204        if (ret)
 205                goto err;
 206
 207        if (pd->size_program > 0) {
 208                for (i = 0; i < pd->size_program; i++) {
 209                        addr = pd->rom_data[i].addr;
 210                        val = pd->rom_data[i].val;
 211                        if (!lp855x_is_valid_rom_area(lp, addr))
 212                                continue;
 213
 214                        ret = lp855x_write_byte(lp, addr, val);
 215                        if (ret)
 216                                goto err;
 217                }
 218        }
 219
 220        if (lp->cfg->post_init_device) {
 221                ret = lp->cfg->post_init_device(lp);
 222                if (ret) {
 223                        dev_err(lp->dev, "post init device err: %d\n", ret);
 224                        goto err;
 225                }
 226        }
 227
 228        return 0;
 229
 230err:
 231        return ret;
 232}
 233
 234static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
 235{
 236        unsigned int period = lp->pdata->period_ns;
 237        unsigned int duty = br * period / max_br;
 238        struct pwm_device *pwm;
 239
 240        /* request pwm device with the consumer name */
 241        if (!lp->pwm) {
 242                pwm = devm_pwm_get(lp->dev, lp->chipname);
 243                if (IS_ERR(pwm))
 244                        return;
 245
 246                lp->pwm = pwm;
 247
 248                /*
 249                 * FIXME: pwm_apply_args() should be removed when switching to
 250                 * the atomic PWM API.
 251                 */
 252                pwm_apply_args(pwm);
 253        }
 254
 255        pwm_config(lp->pwm, duty, period);
 256        if (duty)
 257                pwm_enable(lp->pwm);
 258        else
 259                pwm_disable(lp->pwm);
 260}
 261
 262static int lp855x_bl_update_status(struct backlight_device *bl)
 263{
 264        struct lp855x *lp = bl_get_data(bl);
 265        int brightness = bl->props.brightness;
 266
 267        if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
 268                brightness = 0;
 269
 270        if (lp->mode == PWM_BASED)
 271                lp855x_pwm_ctrl(lp, brightness, bl->props.max_brightness);
 272        else if (lp->mode == REGISTER_BASED)
 273                lp855x_write_byte(lp, lp->cfg->reg_brightness, (u8)brightness);
 274
 275        return 0;
 276}
 277
 278static const struct backlight_ops lp855x_bl_ops = {
 279        .options = BL_CORE_SUSPENDRESUME,
 280        .update_status = lp855x_bl_update_status,
 281};
 282
 283static int lp855x_backlight_register(struct lp855x *lp)
 284{
 285        struct backlight_device *bl;
 286        struct backlight_properties props;
 287        struct lp855x_platform_data *pdata = lp->pdata;
 288        const char *name = pdata->name ? : DEFAULT_BL_NAME;
 289
 290        memset(&props, 0, sizeof(props));
 291        props.type = BACKLIGHT_PLATFORM;
 292        props.max_brightness = MAX_BRIGHTNESS;
 293
 294        if (pdata->initial_brightness > props.max_brightness)
 295                pdata->initial_brightness = props.max_brightness;
 296
 297        props.brightness = pdata->initial_brightness;
 298
 299        bl = devm_backlight_device_register(lp->dev, name, lp->dev, lp,
 300                                       &lp855x_bl_ops, &props);
 301        if (IS_ERR(bl))
 302                return PTR_ERR(bl);
 303
 304        lp->bl = bl;
 305
 306        return 0;
 307}
 308
 309static ssize_t lp855x_get_chip_id(struct device *dev,
 310                                struct device_attribute *attr, char *buf)
 311{
 312        struct lp855x *lp = dev_get_drvdata(dev);
 313
 314        return scnprintf(buf, PAGE_SIZE, "%s\n", lp->chipname);
 315}
 316
 317static ssize_t lp855x_get_bl_ctl_mode(struct device *dev,
 318                                     struct device_attribute *attr, char *buf)
 319{
 320        struct lp855x *lp = dev_get_drvdata(dev);
 321        char *strmode = NULL;
 322
 323        if (lp->mode == PWM_BASED)
 324                strmode = "pwm based";
 325        else if (lp->mode == REGISTER_BASED)
 326                strmode = "register based";
 327
 328        return scnprintf(buf, PAGE_SIZE, "%s\n", strmode);
 329}
 330
 331static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL);
 332static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL);
 333
 334static struct attribute *lp855x_attributes[] = {
 335        &dev_attr_chip_id.attr,
 336        &dev_attr_bl_ctl_mode.attr,
 337        NULL,
 338};
 339
 340static const struct attribute_group lp855x_attr_group = {
 341        .attrs = lp855x_attributes,
 342};
 343
 344#ifdef CONFIG_OF
 345static int lp855x_parse_dt(struct lp855x *lp)
 346{
 347        struct device *dev = lp->dev;
 348        struct device_node *node = dev->of_node;
 349        struct lp855x_platform_data *pdata;
 350        int rom_length;
 351
 352        if (!node) {
 353                dev_err(dev, "no platform data\n");
 354                return -EINVAL;
 355        }
 356
 357        pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 358        if (!pdata)
 359                return -ENOMEM;
 360
 361        of_property_read_string(node, "bl-name", &pdata->name);
 362        of_property_read_u8(node, "dev-ctrl", &pdata->device_control);
 363        of_property_read_u8(node, "init-brt", &pdata->initial_brightness);
 364        of_property_read_u32(node, "pwm-period", &pdata->period_ns);
 365
 366        /* Fill ROM platform data if defined */
 367        rom_length = of_get_child_count(node);
 368        if (rom_length > 0) {
 369                struct lp855x_rom_data *rom;
 370                struct device_node *child;
 371                int i = 0;
 372
 373                rom = devm_kcalloc(dev, rom_length, sizeof(*rom), GFP_KERNEL);
 374                if (!rom)
 375                        return -ENOMEM;
 376
 377                for_each_child_of_node(node, child) {
 378                        of_property_read_u8(child, "rom-addr", &rom[i].addr);
 379                        of_property_read_u8(child, "rom-val", &rom[i].val);
 380                        i++;
 381                }
 382
 383                pdata->size_program = rom_length;
 384                pdata->rom_data = &rom[0];
 385        }
 386
 387        lp->pdata = pdata;
 388
 389        return 0;
 390}
 391#else
 392static int lp855x_parse_dt(struct lp855x *lp)
 393{
 394        return -EINVAL;
 395}
 396#endif
 397
 398static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
 399{
 400        struct lp855x *lp;
 401        int ret;
 402
 403        if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
 404                return -EIO;
 405
 406        lp = devm_kzalloc(&cl->dev, sizeof(struct lp855x), GFP_KERNEL);
 407        if (!lp)
 408                return -ENOMEM;
 409
 410        lp->client = cl;
 411        lp->dev = &cl->dev;
 412        lp->chipname = id->name;
 413        lp->chip_id = id->driver_data;
 414        lp->pdata = dev_get_platdata(&cl->dev);
 415
 416        if (!lp->pdata) {
 417                ret = lp855x_parse_dt(lp);
 418                if (ret < 0)
 419                        return ret;
 420        }
 421
 422        if (lp->pdata->period_ns > 0)
 423                lp->mode = PWM_BASED;
 424        else
 425                lp->mode = REGISTER_BASED;
 426
 427        lp->supply = devm_regulator_get(lp->dev, "power");
 428        if (IS_ERR(lp->supply)) {
 429                if (PTR_ERR(lp->supply) == -EPROBE_DEFER)
 430                        return -EPROBE_DEFER;
 431                lp->supply = NULL;
 432        }
 433
 434        lp->enable = devm_regulator_get_optional(lp->dev, "enable");
 435        if (IS_ERR(lp->enable)) {
 436                ret = PTR_ERR(lp->enable);
 437                if (ret == -ENODEV) {
 438                        lp->enable = NULL;
 439                } else {
 440                        if (ret != -EPROBE_DEFER)
 441                                dev_err(lp->dev, "error getting enable regulator: %d\n",
 442                                        ret);
 443                        return ret;
 444                }
 445        }
 446
 447        if (lp->supply) {
 448                ret = regulator_enable(lp->supply);
 449                if (ret < 0) {
 450                        dev_err(&cl->dev, "failed to enable supply: %d\n", ret);
 451                        return ret;
 452                }
 453        }
 454
 455        if (lp->enable) {
 456                ret = regulator_enable(lp->enable);
 457                if (ret < 0) {
 458                        dev_err(lp->dev, "failed to enable vddio: %d\n", ret);
 459                        goto disable_supply;
 460                }
 461
 462                /*
 463                 * LP8555 datasheet says t_RESPONSE (time between VDDIO and
 464                 * I2C) is 1ms.
 465                 */
 466                usleep_range(1000, 2000);
 467        }
 468
 469        i2c_set_clientdata(cl, lp);
 470
 471        ret = lp855x_configure(lp);
 472        if (ret) {
 473                dev_err(lp->dev, "device config err: %d", ret);
 474                goto disable_vddio;
 475        }
 476
 477        ret = lp855x_backlight_register(lp);
 478        if (ret) {
 479                dev_err(lp->dev,
 480                        "failed to register backlight. err: %d\n", ret);
 481                goto disable_vddio;
 482        }
 483
 484        ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group);
 485        if (ret) {
 486                dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret);
 487                goto disable_vddio;
 488        }
 489
 490        backlight_update_status(lp->bl);
 491
 492        return 0;
 493
 494disable_vddio:
 495        if (lp->enable)
 496                regulator_disable(lp->enable);
 497disable_supply:
 498        if (lp->supply)
 499                regulator_disable(lp->supply);
 500
 501        return ret;
 502}
 503
 504static int lp855x_remove(struct i2c_client *cl)
 505{
 506        struct lp855x *lp = i2c_get_clientdata(cl);
 507
 508        lp->bl->props.brightness = 0;
 509        backlight_update_status(lp->bl);
 510        if (lp->enable)
 511                regulator_disable(lp->enable);
 512        if (lp->supply)
 513                regulator_disable(lp->supply);
 514        sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group);
 515
 516        return 0;
 517}
 518
 519static const struct of_device_id lp855x_dt_ids[] = {
 520        { .compatible = "ti,lp8550", },
 521        { .compatible = "ti,lp8551", },
 522        { .compatible = "ti,lp8552", },
 523        { .compatible = "ti,lp8553", },
 524        { .compatible = "ti,lp8555", },
 525        { .compatible = "ti,lp8556", },
 526        { .compatible = "ti,lp8557", },
 527        { }
 528};
 529MODULE_DEVICE_TABLE(of, lp855x_dt_ids);
 530
 531static const struct i2c_device_id lp855x_ids[] = {
 532        {"lp8550", LP8550},
 533        {"lp8551", LP8551},
 534        {"lp8552", LP8552},
 535        {"lp8553", LP8553},
 536        {"lp8555", LP8555},
 537        {"lp8556", LP8556},
 538        {"lp8557", LP8557},
 539        { }
 540};
 541MODULE_DEVICE_TABLE(i2c, lp855x_ids);
 542
 543static struct i2c_driver lp855x_driver = {
 544        .driver = {
 545                   .name = "lp855x",
 546                   .of_match_table = of_match_ptr(lp855x_dt_ids),
 547                   },
 548        .probe = lp855x_probe,
 549        .remove = lp855x_remove,
 550        .id_table = lp855x_ids,
 551};
 552
 553module_i2c_driver(lp855x_driver);
 554
 555MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver");
 556MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
 557MODULE_LICENSE("GPL");
 558