Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

backlight: add new lm3639 backlight driver

This driver is a general version for LM3639 backlgiht + flash driver chip
of TI.

LM3639:
The LM3639 is a single chip LCD Display Backlight driver + white LED
Camera driver. Programming is done over an I2C compatible interface.
www.ti.com

[akpm@linux-foundation.org: code layout tweaks]
Signed-off-by: G.Shark Jeong <gshark.jeong@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Cc: Daniel Jeong <daniel.jeong@ti.com>
Cc: Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

G.Shark Jeong and committed by
Linus Torvalds
0f59858d 0c2a665a

+516
+9
drivers/video/backlight/Kconfig
··· 359 359 help 360 360 This supports TI LM3630 Backlight Driver 361 361 362 + config BACKLIGHT_LM3639 363 + tristate "Backlight Driver for LM3639" 364 + depends on BACKLIGHT_CLASS_DEVICE && I2C 365 + select REGMAP_I2C 366 + select NEW_LEDS 367 + select LEDS_CLASS 368 + help 369 + This supports TI LM3639 Backlight + 1.5A Flash LED Driver 370 + 362 371 config BACKLIGHT_LP855X 363 372 tristate "Backlight driver for TI LP855X" 364 373 depends on BACKLIGHT_CLASS_DEVICE && I2C
+1
drivers/video/backlight/Makefile
··· 24 24 obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o 25 25 obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o 26 26 obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o 27 + obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o 27 28 obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o 28 29 obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o 29 30 obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o
+437
drivers/video/backlight/lm3639_bl.c
··· 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 + 36 + struct 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 */ 51 + static int __devinit 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 | pdata->bled_pins; 81 + else 82 + reg_val = pdata->fled_pins | pdata->bled_pins | 0x01; 83 + 84 + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x79, reg_val); 85 + if (ret < 0) 86 + goto out; 87 + 88 + return ret; 89 + out: 90 + dev_err(pchip->dev, "i2c failed to access register\n"); 91 + return ret; 92 + } 93 + 94 + /* update and get brightness */ 95 + static int lm3639_bled_update_status(struct backlight_device *bl) 96 + { 97 + int ret; 98 + unsigned int reg_val; 99 + struct lm3639_chip_data *pchip = bl_get_data(bl); 100 + struct lm3639_platform_data *pdata = pchip->pdata; 101 + 102 + ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val); 103 + if (ret < 0) 104 + goto out; 105 + 106 + if (reg_val != 0) 107 + dev_info(pchip->dev, "last flag is 0x%x\n", reg_val); 108 + 109 + /* pwm control */ 110 + if (pdata->pin_pwm) { 111 + if (pdata->pwm_set_intensity) 112 + pdata->pwm_set_intensity(bl->props.brightness, 113 + pdata->max_brt_led); 114 + else 115 + dev_err(pchip->dev, 116 + "No pwm control func. in plat-data\n"); 117 + return bl->props.brightness; 118 + } 119 + 120 + /* i2c control and set brigtness */ 121 + ret = regmap_write(pchip->regmap, REG_BL_CONF_4, bl->props.brightness); 122 + if (ret < 0) 123 + goto out; 124 + ret = regmap_write(pchip->regmap, REG_BL_CONF_3, bl->props.brightness); 125 + if (ret < 0) 126 + goto out; 127 + 128 + if (!bl->props.brightness) 129 + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x00); 130 + else 131 + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x01, 0x01); 132 + if (ret < 0) 133 + goto out; 134 + 135 + return bl->props.brightness; 136 + out: 137 + dev_err(pchip->dev, "i2c failed to access registers\n"); 138 + return bl->props.brightness; 139 + } 140 + 141 + static int lm3639_bled_get_brightness(struct backlight_device *bl) 142 + { 143 + int ret; 144 + unsigned int reg_val; 145 + struct lm3639_chip_data *pchip = bl_get_data(bl); 146 + struct lm3639_platform_data *pdata = pchip->pdata; 147 + 148 + if (pdata->pin_pwm) { 149 + if (pdata->pwm_get_intensity) 150 + bl->props.brightness = pdata->pwm_get_intensity(); 151 + else 152 + dev_err(pchip->dev, 153 + "No pwm control func. in plat-data\n"); 154 + return bl->props.brightness; 155 + } 156 + 157 + ret = regmap_read(pchip->regmap, REG_BL_CONF_1, &reg_val); 158 + if (ret < 0) 159 + goto out; 160 + if (reg_val & 0x10) 161 + ret = regmap_read(pchip->regmap, REG_BL_CONF_4, &reg_val); 162 + else 163 + ret = regmap_read(pchip->regmap, REG_BL_CONF_3, &reg_val); 164 + if (ret < 0) 165 + goto out; 166 + bl->props.brightness = reg_val; 167 + 168 + return bl->props.brightness; 169 + out: 170 + dev_err(pchip->dev, "i2c failed to access register\n"); 171 + return bl->props.brightness; 172 + } 173 + 174 + static const struct backlight_ops lm3639_bled_ops = { 175 + .options = BL_CORE_SUSPENDRESUME, 176 + .update_status = lm3639_bled_update_status, 177 + .get_brightness = lm3639_bled_get_brightness, 178 + }; 179 + 180 + /* backlight mapping mode */ 181 + static ssize_t lm3639_bled_mode_store(struct device *dev, 182 + struct device_attribute *devAttr, 183 + const char *buf, size_t size) 184 + { 185 + ssize_t ret; 186 + struct lm3639_chip_data *pchip = dev_get_drvdata(dev); 187 + unsigned int state; 188 + 189 + ret = kstrtouint(buf, 10, &state); 190 + if (ret) 191 + goto out_input; 192 + 193 + if (!state) 194 + ret = 195 + regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10, 196 + 0x00); 197 + else 198 + ret = 199 + regmap_update_bits(pchip->regmap, REG_BL_CONF_1, 0x10, 200 + 0x10); 201 + 202 + if (ret < 0) 203 + goto out; 204 + 205 + return size; 206 + 207 + out: 208 + dev_err(pchip->dev, "%s:i2c access fail to register\n", __func__); 209 + return size; 210 + 211 + out_input: 212 + dev_err(pchip->dev, "%s:input conversion fail\n", __func__); 213 + return size; 214 + 215 + } 216 + 217 + static DEVICE_ATTR(bled_mode, 0666, NULL, lm3639_bled_mode_store); 218 + 219 + /* torch */ 220 + static void lm3639_torch_brightness_set(struct led_classdev *cdev, 221 + enum led_brightness brightness) 222 + { 223 + int ret; 224 + unsigned int reg_val; 225 + struct lm3639_chip_data *pchip; 226 + 227 + pchip = container_of(cdev, struct lm3639_chip_data, cdev_torch); 228 + 229 + ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val); 230 + if (ret < 0) 231 + goto out; 232 + if (reg_val != 0) 233 + dev_info(pchip->dev, "last flag is 0x%x\n", reg_val); 234 + 235 + /* brightness 0 means off state */ 236 + if (!brightness) { 237 + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00); 238 + if (ret < 0) 239 + goto out; 240 + return; 241 + } 242 + 243 + ret = regmap_update_bits(pchip->regmap, 244 + REG_FL_CONF_1, 0x70, (brightness - 1) << 4); 245 + if (ret < 0) 246 + goto out; 247 + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x02); 248 + if (ret < 0) 249 + goto out; 250 + 251 + return; 252 + out: 253 + dev_err(pchip->dev, "i2c failed to access register\n"); 254 + return; 255 + } 256 + 257 + /* flash */ 258 + static void lm3639_flash_brightness_set(struct led_classdev *cdev, 259 + enum led_brightness brightness) 260 + { 261 + int ret; 262 + unsigned int reg_val; 263 + struct lm3639_chip_data *pchip; 264 + 265 + pchip = container_of(cdev, struct lm3639_chip_data, cdev_flash); 266 + 267 + ret = regmap_read(pchip->regmap, REG_FLAG, &reg_val); 268 + if (ret < 0) 269 + goto out; 270 + if (reg_val != 0) 271 + dev_info(pchip->dev, "last flag is 0x%x\n", reg_val); 272 + 273 + /* torch off before flash control */ 274 + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x00); 275 + if (ret < 0) 276 + goto out; 277 + 278 + /* brightness 0 means off state */ 279 + if (!brightness) 280 + return; 281 + 282 + ret = regmap_update_bits(pchip->regmap, 283 + REG_FL_CONF_1, 0x0F, brightness - 1); 284 + if (ret < 0) 285 + goto out; 286 + ret = regmap_update_bits(pchip->regmap, REG_ENABLE, 0x06, 0x06); 287 + if (ret < 0) 288 + goto out; 289 + 290 + return; 291 + out: 292 + dev_err(pchip->dev, "i2c failed to access register\n"); 293 + return; 294 + } 295 + 296 + static const struct regmap_config lm3639_regmap = { 297 + .reg_bits = 8, 298 + .val_bits = 8, 299 + .max_register = REG_MAX, 300 + }; 301 + 302 + static int __devinit lm3639_probe(struct i2c_client *client, 303 + const struct i2c_device_id *id) 304 + { 305 + int ret; 306 + struct lm3639_chip_data *pchip; 307 + struct lm3639_platform_data *pdata = client->dev.platform_data; 308 + struct backlight_properties props; 309 + 310 + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 311 + dev_err(&client->dev, "i2c functionality check fail.\n"); 312 + return -EOPNOTSUPP; 313 + } 314 + 315 + if (pdata == NULL) { 316 + dev_err(&client->dev, "Needs Platform Data.\n"); 317 + return -ENODATA; 318 + } 319 + 320 + pchip = devm_kzalloc(&client->dev, 321 + sizeof(struct lm3639_chip_data), GFP_KERNEL); 322 + if (!pchip) 323 + return -ENOMEM; 324 + 325 + pchip->pdata = pdata; 326 + pchip->dev = &client->dev; 327 + 328 + pchip->regmap = devm_regmap_init_i2c(client, &lm3639_regmap); 329 + if (IS_ERR(pchip->regmap)) { 330 + ret = PTR_ERR(pchip->regmap); 331 + dev_err(&client->dev, "fail : allocate register map: %d\n", 332 + ret); 333 + return ret; 334 + } 335 + i2c_set_clientdata(client, pchip); 336 + 337 + /* chip initialize */ 338 + ret = lm3639_chip_init(pchip); 339 + if (ret < 0) { 340 + dev_err(&client->dev, "fail : chip init\n"); 341 + goto err_out; 342 + } 343 + 344 + /* backlight */ 345 + props.type = BACKLIGHT_RAW; 346 + props.brightness = pdata->init_brt_led; 347 + props.max_brightness = pdata->max_brt_led; 348 + pchip->bled = 349 + backlight_device_register("lm3639_bled", pchip->dev, pchip, 350 + &lm3639_bled_ops, &props); 351 + if (IS_ERR(pchip->bled)) { 352 + dev_err(&client->dev, "fail : backlight register\n"); 353 + ret = -EIO; 354 + goto err_out; 355 + } 356 + 357 + ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode); 358 + if (ret < 0) { 359 + dev_err(&client->dev, "failed : add sysfs entries\n"); 360 + ret = -EIO; 361 + goto err_bled_mode; 362 + } 363 + 364 + /* flash */ 365 + pchip->cdev_flash.name = "lm3639_flash"; 366 + pchip->cdev_flash.max_brightness = 16; 367 + pchip->cdev_flash.brightness_set = lm3639_flash_brightness_set; 368 + ret = led_classdev_register((struct device *) 369 + &client->dev, &pchip->cdev_flash); 370 + if (ret < 0) { 371 + dev_err(&client->dev, "fail : flash register\n"); 372 + ret = -EIO; 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 + ret = -EIO; 385 + goto err_torch; 386 + } 387 + 388 + return 0; 389 + 390 + err_torch: 391 + led_classdev_unregister(&pchip->cdev_flash); 392 + err_flash: 393 + device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode); 394 + err_bled_mode: 395 + backlight_device_unregister(pchip->bled); 396 + err_out: 397 + return ret; 398 + } 399 + 400 + static int __devexit 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 + 417 + static const struct i2c_device_id lm3639_id[] = { 418 + {LM3639_NAME, 0}, 419 + {} 420 + }; 421 + 422 + MODULE_DEVICE_TABLE(i2c, lm3639_id); 423 + static struct i2c_driver lm3639_i2c_driver = { 424 + .driver = { 425 + .name = LM3639_NAME, 426 + }, 427 + .probe = lm3639_probe, 428 + .remove = __devexit_p(lm3639_remove), 429 + .id_table = lm3639_id, 430 + }; 431 + 432 + module_i2c_driver(lm3639_i2c_driver); 433 + 434 + MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639"); 435 + MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); 436 + MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); 437 + MODULE_LICENSE("GPL v2");
+69
include/linux/platform_data/lm3639_bl.h
··· 1 + /* 2 + * Simple driver for Texas Instruments LM3630 LED Flash 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 + 11 + #ifndef __LINUX_LM3639_H 12 + #define __LINUX_LM3639_H 13 + 14 + #define LM3639_NAME "lm3639_bl" 15 + 16 + enum lm3639_pwm { 17 + LM3639_PWM_DISABLE = 0x00, 18 + LM3639_PWM_EN_ACTLOW = 0x48, 19 + LM3639_PWM_EN_ACTHIGH = 0x40, 20 + }; 21 + 22 + enum lm3639_strobe { 23 + LM3639_STROBE_DISABLE = 0x00, 24 + LM3639_STROBE_EN_ACTLOW = 0x10, 25 + LM3639_STROBE_EN_ACTHIGH = 0x30, 26 + }; 27 + 28 + enum lm3639_txpin { 29 + LM3639_TXPIN_DISABLE = 0x00, 30 + LM3639_TXPIN_EN_ACTLOW = 0x04, 31 + LM3639_TXPIN_EN_ACTHIGH = 0x0C, 32 + }; 33 + 34 + enum lm3639_fleds { 35 + LM3639_FLED_DIASBLE_ALL = 0x00, 36 + LM3639_FLED_EN_1 = 0x40, 37 + LM3639_FLED_EN_2 = 0x20, 38 + LM3639_FLED_EN_ALL = 0x60, 39 + }; 40 + 41 + enum lm3639_bleds { 42 + LM3639_BLED_DIASBLE_ALL = 0x00, 43 + LM3639_BLED_EN_1 = 0x10, 44 + LM3639_BLED_EN_2 = 0x08, 45 + LM3639_BLED_EN_ALL = 0x18, 46 + }; 47 + enum lm3639_bled_mode { 48 + LM3639_BLED_MODE_EXPONETIAL = 0x00, 49 + LM3639_BLED_MODE_LINEAR = 0x10, 50 + }; 51 + 52 + struct lm3639_platform_data { 53 + unsigned int max_brt_led; 54 + unsigned int init_brt_led; 55 + 56 + /* input pins */ 57 + enum lm3639_pwm pin_pwm; 58 + enum lm3639_strobe pin_strobe; 59 + enum lm3639_txpin pin_tx; 60 + 61 + /* output pins */ 62 + enum lm3639_fleds fled_pins; 63 + enum lm3639_bleds bled_pins; 64 + enum lm3639_bled_mode bled_mode; 65 + 66 + void (*pwm_set_intensity) (int brightness, int max_brightness); 67 + int (*pwm_get_intensity) (void); 68 + }; 69 + #endif /* __LINUX_LM3639_H */