Commit 7be865ab8634d4ec2a6bdb9459b268cd60e832af

Authored by Kim, Milo
Committed by Linus Torvalds
1 parent 050ea48bbf

backlight: new backlight driver for LP855x devices

THis driver supports TI LP8550/LP8551/LP8552/LP8553/LP8556 backlight
devices.

The brightness can be controlled by the I2C or PWM input.  The lp855x
driver provides both modes.  For the PWM control, pwm-specific functions
can be defined in the platform data.  And some information can be read
via the sysfs(lp855x device attributes).

For details, please refer to Documentation/backlight/lp855x-driver.txt.

[axel.lin@gmail.com: add missing mutex_unlock in lp855x_read_byte() error path]
[axel.lin@gmail.com: check platform data in lp855x_probe()]
[axel.lin@gmail.com: small cleanups]
[dan.carpenter@oracle.com: silence a compiler warning]
[axel.lin@gmail.com: use id->driver_data to differentiate lp855x chips]
[akpm@linux-foundation.org: simplify boolean return expression]
Signed-off-by: Milo(Woogyom) Kim <milo.kim@ti.com>
Signed-off-by: Axel Lin <axel.lin@gmail.com>
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 5 changed files with 548 additions and 0 deletions Side-by-side Diff

Documentation/backlight/lp855x-driver.txt
  1 +Kernel driver lp855x
  2 +====================
  3 +
  4 +Backlight driver for LP855x ICs
  5 +
  6 +Supported chips:
  7 + Texas Instruments LP8550, LP8551, LP8552, LP8553 and LP8556
  8 +
  9 +Author: Milo(Woogyom) Kim <milo.kim@ti.com>
  10 +
  11 +Description
  12 +-----------
  13 +
  14 +* Brightness control
  15 +
  16 +Brightness can be controlled by the pwm input or the i2c command.
  17 +The lp855x driver supports both cases.
  18 +
  19 +* Device attributes
  20 +
  21 +1) bl_ctl_mode
  22 +Backlight control mode.
  23 +Value : pwm based or register based
  24 +
  25 +2) chip_id
  26 +The lp855x chip id.
  27 +Value : lp8550/lp8551/lp8552/lp8553/lp8556
  28 +
  29 +Platform data for lp855x
  30 +------------------------
  31 +
  32 +For supporting platform specific data, the lp855x platform data can be used.
  33 +
  34 +* name : Backlight driver name. If it is not defined, default name is set.
  35 +* mode : Brightness control mode. PWM or register based.
  36 +* device_control : Value of DEVICE CONTROL register.
  37 +* initial_brightness : Initial value of backlight brightness.
  38 +* pwm_data : Platform specific pwm generation functions.
  39 + Only valid when brightness is pwm input mode.
  40 + Functions should be implemented by PWM driver.
  41 + - pwm_set_intensity() : set duty of PWM
  42 + - pwm_get_intensity() : get current duty of PWM
  43 +* load_new_rom_data :
  44 + 0 : use default configuration data
  45 + 1 : update values of eeprom or eprom registers on loading driver
  46 +* size_program : Total size of lp855x_rom_data.
  47 +* rom_data : List of new eeprom/eprom registers.
  48 +
  49 +example 1) lp8552 platform data : i2c register mode with new eeprom data
  50 +
  51 +#define EEPROM_A5_ADDR 0xA5
  52 +#define EEPROM_A5_VAL 0x4f /* EN_VSYNC=0 */
  53 +
  54 +static struct lp855x_rom_data lp8552_eeprom_arr[] = {
  55 + {EEPROM_A5_ADDR, EEPROM_A5_VAL},
  56 +};
  57 +
  58 +static struct lp855x_platform_data lp8552_pdata = {
  59 + .name = "lcd-bl",
  60 + .mode = REGISTER_BASED,
  61 + .device_control = I2C_CONFIG(LP8552),
  62 + .initial_brightness = INITIAL_BRT,
  63 + .load_new_rom_data = 1,
  64 + .size_program = ARRAY_SIZE(lp8552_eeprom_arr),
  65 + .rom_data = lp8552_eeprom_arr,
  66 +};
  67 +
  68 +example 2) lp8556 platform data : pwm input mode with default rom data
  69 +
  70 +static struct lp855x_platform_data lp8556_pdata = {
  71 + .mode = PWM_BASED,
  72 + .device_control = PWM_CONFIG(LP8556),
  73 + .initial_brightness = INITIAL_BRT,
  74 + .pwm_data = {
  75 + .pwm_set_intensity = platform_pwm_set_intensity,
  76 + .pwm_get_intensity = platform_pwm_get_intensity,
  77 + },
  78 +};
drivers/video/backlight/Kconfig
... ... @@ -334,6 +334,13 @@
334 334 If you have a AnalogicTech AAT2870 say Y to enable the
335 335 backlight driver.
336 336  
  337 +config BACKLIGHT_LP855X
  338 + tristate "Backlight driver for TI LP855X"
  339 + depends on BACKLIGHT_CLASS_DEVICE && I2C
  340 + help
  341 + This supports TI LP8550, LP8551, LP8552, LP8553 and LP8556
  342 + backlight driver.
  343 +
337 344 endif # BACKLIGHT_CLASS_DEVICE
338 345  
339 346 endif # BACKLIGHT_LCD_SUPPORT
drivers/video/backlight/Makefile
... ... @@ -22,6 +22,7 @@
22 22 obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o
23 23 obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o
24 24 obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
  25 +obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o
25 26 obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o
26 27 obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
27 28 obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
drivers/video/backlight/lp855x_bl.c
  1 +/*
  2 + * TI LP855x Backlight Driver
  3 + *
  4 + * Copyright (C) 2011 Texas Instruments
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License version 2 as
  8 + * published by the Free Software Foundation.
  9 + *
  10 + */
  11 +
  12 +#include <linux/module.h>
  13 +#include <linux/slab.h>
  14 +#include <linux/i2c.h>
  15 +#include <linux/backlight.h>
  16 +#include <linux/err.h>
  17 +#include <linux/lp855x.h>
  18 +
  19 +/* Registers */
  20 +#define BRIGHTNESS_CTRL (0x00)
  21 +#define DEVICE_CTRL (0x01)
  22 +
  23 +#define BUF_SIZE 20
  24 +#define DEFAULT_BL_NAME "lcd-backlight"
  25 +#define MAX_BRIGHTNESS 255
  26 +
  27 +struct lp855x {
  28 + const char *chipname;
  29 + enum lp855x_chip_id chip_id;
  30 + struct i2c_client *client;
  31 + struct backlight_device *bl;
  32 + struct device *dev;
  33 + struct mutex xfer_lock;
  34 + struct lp855x_platform_data *pdata;
  35 +};
  36 +
  37 +static int lp855x_read_byte(struct lp855x *lp, u8 reg, u8 *data)
  38 +{
  39 + int ret;
  40 +
  41 + mutex_lock(&lp->xfer_lock);
  42 + ret = i2c_smbus_read_byte_data(lp->client, reg);
  43 + if (ret < 0) {
  44 + mutex_unlock(&lp->xfer_lock);
  45 + dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
  46 + return ret;
  47 + }
  48 + mutex_unlock(&lp->xfer_lock);
  49 +
  50 + *data = (u8)ret;
  51 + return 0;
  52 +}
  53 +
  54 +static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data)
  55 +{
  56 + int ret;
  57 +
  58 + mutex_lock(&lp->xfer_lock);
  59 + ret = i2c_smbus_write_byte_data(lp->client, reg, data);
  60 + mutex_unlock(&lp->xfer_lock);
  61 +
  62 + return ret;
  63 +}
  64 +
  65 +static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
  66 +{
  67 + u8 start, end;
  68 +
  69 + switch (lp->chip_id) {
  70 + case LP8550:
  71 + case LP8551:
  72 + case LP8552:
  73 + case LP8553:
  74 + start = EEPROM_START;
  75 + end = EEPROM_END;
  76 + break;
  77 + case LP8556:
  78 + start = EPROM_START;
  79 + end = EPROM_END;
  80 + break;
  81 + default:
  82 + return false;
  83 + }
  84 +
  85 + return (addr >= start && addr <= end);
  86 +}
  87 +
  88 +static int lp855x_init_registers(struct lp855x *lp)
  89 +{
  90 + u8 val, addr;
  91 + int i, ret;
  92 + struct lp855x_platform_data *pd = lp->pdata;
  93 +
  94 + val = pd->initial_brightness;
  95 + ret = lp855x_write_byte(lp, BRIGHTNESS_CTRL, val);
  96 + if (ret)
  97 + return ret;
  98 +
  99 + val = pd->device_control;
  100 + ret = lp855x_write_byte(lp, DEVICE_CTRL, val);
  101 + if (ret)
  102 + return ret;
  103 +
  104 + if (pd->load_new_rom_data && pd->size_program) {
  105 + for (i = 0; i < pd->size_program; i++) {
  106 + addr = pd->rom_data[i].addr;
  107 + val = pd->rom_data[i].val;
  108 + if (!lp855x_is_valid_rom_area(lp, addr))
  109 + continue;
  110 +
  111 + ret = lp855x_write_byte(lp, addr, val);
  112 + if (ret)
  113 + return ret;
  114 + }
  115 + }
  116 +
  117 + return ret;
  118 +}
  119 +
  120 +static int lp855x_bl_update_status(struct backlight_device *bl)
  121 +{
  122 + struct lp855x *lp = bl_get_data(bl);
  123 + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode;
  124 +
  125 + if (bl->props.state & BL_CORE_SUSPENDED)
  126 + bl->props.brightness = 0;
  127 +
  128 + if (mode == PWM_BASED) {
  129 + struct lp855x_pwm_data *pd = &lp->pdata->pwm_data;
  130 + int br = bl->props.brightness;
  131 + int max_br = bl->props.max_brightness;
  132 +
  133 + if (pd->pwm_set_intensity)
  134 + pd->pwm_set_intensity(br, max_br);
  135 +
  136 + } else if (mode == REGISTER_BASED) {
  137 + u8 val = bl->props.brightness;
  138 + lp855x_write_byte(lp, BRIGHTNESS_CTRL, val);
  139 + }
  140 +
  141 + return 0;
  142 +}
  143 +
  144 +static int lp855x_bl_get_brightness(struct backlight_device *bl)
  145 +{
  146 + struct lp855x *lp = bl_get_data(bl);
  147 + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode;
  148 +
  149 + if (mode == PWM_BASED) {
  150 + struct lp855x_pwm_data *pd = &lp->pdata->pwm_data;
  151 + int max_br = bl->props.max_brightness;
  152 +
  153 + if (pd->pwm_get_intensity)
  154 + bl->props.brightness = pd->pwm_get_intensity(max_br);
  155 +
  156 + } else if (mode == REGISTER_BASED) {
  157 + u8 val = 0;
  158 +
  159 + lp855x_read_byte(lp, BRIGHTNESS_CTRL, &val);
  160 + bl->props.brightness = val;
  161 + }
  162 +
  163 + return bl->props.brightness;
  164 +}
  165 +
  166 +static const struct backlight_ops lp855x_bl_ops = {
  167 + .options = BL_CORE_SUSPENDRESUME,
  168 + .update_status = lp855x_bl_update_status,
  169 + .get_brightness = lp855x_bl_get_brightness,
  170 +};
  171 +
  172 +static int lp855x_backlight_register(struct lp855x *lp)
  173 +{
  174 + struct backlight_device *bl;
  175 + struct backlight_properties props;
  176 + struct lp855x_platform_data *pdata = lp->pdata;
  177 + char *name = pdata->name ? : DEFAULT_BL_NAME;
  178 +
  179 + props.type = BACKLIGHT_PLATFORM;
  180 + props.max_brightness = MAX_BRIGHTNESS;
  181 +
  182 + if (pdata->initial_brightness > props.max_brightness)
  183 + pdata->initial_brightness = props.max_brightness;
  184 +
  185 + props.brightness = pdata->initial_brightness;
  186 +
  187 + bl = backlight_device_register(name, lp->dev, lp,
  188 + &lp855x_bl_ops, &props);
  189 + if (IS_ERR(bl))
  190 + return PTR_ERR(bl);
  191 +
  192 + lp->bl = bl;
  193 +
  194 + return 0;
  195 +}
  196 +
  197 +static void lp855x_backlight_unregister(struct lp855x *lp)
  198 +{
  199 + if (lp->bl)
  200 + backlight_device_unregister(lp->bl);
  201 +}
  202 +
  203 +static ssize_t lp855x_get_chip_id(struct device *dev,
  204 + struct device_attribute *attr, char *buf)
  205 +{
  206 + struct lp855x *lp = dev_get_drvdata(dev);
  207 + return scnprintf(buf, BUF_SIZE, "%s\n", lp->chipname);
  208 +}
  209 +
  210 +static ssize_t lp855x_get_bl_ctl_mode(struct device *dev,
  211 + struct device_attribute *attr, char *buf)
  212 +{
  213 + struct lp855x *lp = dev_get_drvdata(dev);
  214 + enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode;
  215 + char *strmode = NULL;
  216 +
  217 + if (mode == PWM_BASED)
  218 + strmode = "pwm based";
  219 + else if (mode == REGISTER_BASED)
  220 + strmode = "register based";
  221 +
  222 + return scnprintf(buf, BUF_SIZE, "%s\n", strmode);
  223 +}
  224 +
  225 +static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL);
  226 +static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL);
  227 +
  228 +static struct attribute *lp855x_attributes[] = {
  229 + &dev_attr_chip_id.attr,
  230 + &dev_attr_bl_ctl_mode.attr,
  231 + NULL,
  232 +};
  233 +
  234 +static const struct attribute_group lp855x_attr_group = {
  235 + .attrs = lp855x_attributes,
  236 +};
  237 +
  238 +static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
  239 +{
  240 + struct lp855x *lp;
  241 + struct lp855x_platform_data *pdata = cl->dev.platform_data;
  242 + enum lp855x_brightness_ctrl_mode mode;
  243 + int ret;
  244 +
  245 + if (!pdata) {
  246 + dev_err(&cl->dev, "no platform data supplied\n");
  247 + return -EINVAL;
  248 + }
  249 +
  250 + if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
  251 + return -EIO;
  252 +
  253 + lp = devm_kzalloc(&cl->dev, sizeof(struct lp855x), GFP_KERNEL);
  254 + if (!lp)
  255 + return -ENOMEM;
  256 +
  257 + mode = pdata->mode;
  258 + lp->client = cl;
  259 + lp->dev = &cl->dev;
  260 + lp->pdata = pdata;
  261 + lp->chipname = id->name;
  262 + lp->chip_id = id->driver_data;
  263 + i2c_set_clientdata(cl, lp);
  264 +
  265 + mutex_init(&lp->xfer_lock);
  266 +
  267 + ret = lp855x_init_registers(lp);
  268 + if (ret) {
  269 + dev_err(lp->dev, "i2c communication err: %d", ret);
  270 + if (mode == REGISTER_BASED)
  271 + goto err_dev;
  272 + }
  273 +
  274 + ret = lp855x_backlight_register(lp);
  275 + if (ret) {
  276 + dev_err(lp->dev,
  277 + "failed to register backlight. err: %d\n", ret);
  278 + goto err_dev;
  279 + }
  280 +
  281 + ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group);
  282 + if (ret) {
  283 + dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret);
  284 + goto err_sysfs;
  285 + }
  286 +
  287 + backlight_update_status(lp->bl);
  288 + return 0;
  289 +
  290 +err_sysfs:
  291 + lp855x_backlight_unregister(lp);
  292 +err_dev:
  293 + return ret;
  294 +}
  295 +
  296 +static int __devexit lp855x_remove(struct i2c_client *cl)
  297 +{
  298 + struct lp855x *lp = i2c_get_clientdata(cl);
  299 +
  300 + lp->bl->props.brightness = 0;
  301 + backlight_update_status(lp->bl);
  302 + sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group);
  303 + lp855x_backlight_unregister(lp);
  304 +
  305 + return 0;
  306 +}
  307 +
  308 +static const struct i2c_device_id lp855x_ids[] = {
  309 + {"lp8550", LP8550},
  310 + {"lp8551", LP8551},
  311 + {"lp8552", LP8552},
  312 + {"lp8553", LP8553},
  313 + {"lp8556", LP8556},
  314 + { }
  315 +};
  316 +MODULE_DEVICE_TABLE(i2c, lp855x_ids);
  317 +
  318 +static struct i2c_driver lp855x_driver = {
  319 + .driver = {
  320 + .name = "lp855x",
  321 + },
  322 + .probe = lp855x_probe,
  323 + .remove = __devexit_p(lp855x_remove),
  324 + .id_table = lp855x_ids,
  325 +};
  326 +
  327 +module_i2c_driver(lp855x_driver);
  328 +
  329 +MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver");
  330 +MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
  331 +MODULE_LICENSE("GPL");
include/linux/lp855x.h
  1 +/*
  2 + * LP855x Backlight Driver
  3 + *
  4 + * Copyright (C) 2011 Texas Instruments
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License version 2 as
  8 + * published by the Free Software Foundation.
  9 + *
  10 + */
  11 +
  12 +#ifndef _LP855X_H
  13 +#define _LP855X_H
  14 +
  15 +#define BL_CTL_SHFT (0)
  16 +#define BRT_MODE_SHFT (1)
  17 +#define BRT_MODE_MASK (0x06)
  18 +
  19 +/* Enable backlight. Only valid when BRT_MODE=10(I2C only) */
  20 +#define ENABLE_BL (1)
  21 +#define DISABLE_BL (0)
  22 +
  23 +#define I2C_CONFIG(id) id ## _I2C_CONFIG
  24 +#define PWM_CONFIG(id) id ## _PWM_CONFIG
  25 +
  26 +/* DEVICE CONTROL register - LP8550 */
  27 +#define LP8550_PWM_CONFIG (LP8550_PWM_ONLY << BRT_MODE_SHFT)
  28 +#define LP8550_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \
  29 + (LP8550_I2C_ONLY << BRT_MODE_SHFT))
  30 +
  31 +/* DEVICE CONTROL register - LP8551 */
  32 +#define LP8551_PWM_CONFIG LP8550_PWM_CONFIG
  33 +#define LP8551_I2C_CONFIG LP8550_I2C_CONFIG
  34 +
  35 +/* DEVICE CONTROL register - LP8552 */
  36 +#define LP8552_PWM_CONFIG LP8550_PWM_CONFIG
  37 +#define LP8552_I2C_CONFIG LP8550_I2C_CONFIG
  38 +
  39 +/* DEVICE CONTROL register - LP8553 */
  40 +#define LP8553_PWM_CONFIG LP8550_PWM_CONFIG
  41 +#define LP8553_I2C_CONFIG LP8550_I2C_CONFIG
  42 +
  43 +/* DEVICE CONTROL register - LP8556 */
  44 +#define LP8556_PWM_CONFIG (LP8556_PWM_ONLY << BRT_MODE_SHFT)
  45 +#define LP8556_COMB1_CONFIG (LP8556_COMBINED1 << BRT_MODE_SHFT)
  46 +#define LP8556_I2C_CONFIG ((ENABLE_BL << BL_CTL_SHFT) | \
  47 + (LP8556_I2C_ONLY << BRT_MODE_SHFT))
  48 +#define LP8556_COMB2_CONFIG (LP8556_COMBINED2 << BRT_MODE_SHFT)
  49 +
  50 +/* ROM area boundary */
  51 +#define EEPROM_START (0xA0)
  52 +#define EEPROM_END (0xA7)
  53 +#define EPROM_START (0xA0)
  54 +#define EPROM_END (0xAF)
  55 +
  56 +enum lp855x_chip_id {
  57 + LP8550,
  58 + LP8551,
  59 + LP8552,
  60 + LP8553,
  61 + LP8556,
  62 +};
  63 +
  64 +enum lp855x_brightness_ctrl_mode {
  65 + PWM_BASED = 1,
  66 + REGISTER_BASED,
  67 +};
  68 +
  69 +enum lp8550_brighntess_source {
  70 + LP8550_PWM_ONLY,
  71 + LP8550_I2C_ONLY = 2,
  72 +};
  73 +
  74 +enum lp8551_brighntess_source {
  75 + LP8551_PWM_ONLY = LP8550_PWM_ONLY,
  76 + LP8551_I2C_ONLY = LP8550_I2C_ONLY,
  77 +};
  78 +
  79 +enum lp8552_brighntess_source {
  80 + LP8552_PWM_ONLY = LP8550_PWM_ONLY,
  81 + LP8552_I2C_ONLY = LP8550_I2C_ONLY,
  82 +};
  83 +
  84 +enum lp8553_brighntess_source {
  85 + LP8553_PWM_ONLY = LP8550_PWM_ONLY,
  86 + LP8553_I2C_ONLY = LP8550_I2C_ONLY,
  87 +};
  88 +
  89 +enum lp8556_brightness_source {
  90 + LP8556_PWM_ONLY,
  91 + LP8556_COMBINED1, /* pwm + i2c before the shaper block */
  92 + LP8556_I2C_ONLY,
  93 + LP8556_COMBINED2, /* pwm + i2c after the shaper block */
  94 +};
  95 +
  96 +struct lp855x_pwm_data {
  97 + void (*pwm_set_intensity) (int brightness, int max_brightness);
  98 + int (*pwm_get_intensity) (int max_brightness);
  99 +};
  100 +
  101 +struct lp855x_rom_data {
  102 + u8 addr;
  103 + u8 val;
  104 +};
  105 +
  106 +/**
  107 + * struct lp855x_platform_data
  108 + * @name : Backlight driver name. If it is not defined, default name is set.
  109 + * @mode : brightness control by pwm or lp855x register
  110 + * @device_control : value of DEVICE CONTROL register
  111 + * @initial_brightness : initial value of backlight brightness
  112 + * @pwm_data : platform specific pwm generation functions.
  113 + Only valid when mode is PWM_BASED.
  114 + * @load_new_rom_data :
  115 + 0 : use default configuration data
  116 + 1 : update values of eeprom or eprom registers on loading driver
  117 + * @size_program : total size of lp855x_rom_data
  118 + * @rom_data : list of new eeprom/eprom registers
  119 + */
  120 +struct lp855x_platform_data {
  121 + char *name;
  122 + enum lp855x_brightness_ctrl_mode mode;
  123 + u8 device_control;
  124 + int initial_brightness;
  125 + struct lp855x_pwm_data pwm_data;
  126 + u8 load_new_rom_data;
  127 + int size_program;
  128 + struct lp855x_rom_data *rom_data;
  129 +};
  130 +
  131 +#endif