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

backlight: add LM3533 backlight driver

Add sub-driver for the backlights on National Semiconductor / TI LM3533
lighting power chips.

The chip provides 256 brightness levels and ambient-light-sensor and pwm
input control.

[akpm@linux-foundation.org: fix warning]
[akpm@linux-foundation.org: fix the type of `mode']
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Cc: Rob Landley <rob@landley.net>
Cc: Samuel Ortiz <sameo@linux.intel.com>
Cc: Jonathan Cameron <jic23@cam.ac.uk>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Johan Hovold and committed by
Linus Torvalds
7f26c970 1615d210

+484
+48
Documentation/ABI/testing/sysfs-class-backlight-driver-lm3533
··· 1 + What: /sys/class/backlight/<backlight>/als_channel 2 + Date: May 2012 3 + KernelVersion: 3.5 4 + Contact: Johan Hovold <jhovold@gmail.com> 5 + Description: 6 + Get the ALS output channel used as input in 7 + ALS-current-control mode (0, 1), where 8 + 9 + 0 - out_current0 (backlight 0) 10 + 1 - out_current1 (backlight 1) 11 + 12 + What: /sys/class/backlight/<backlight>/als_en 13 + Date: May 2012 14 + KernelVersion: 3.5 15 + Contact: Johan Hovold <jhovold@gmail.com> 16 + Description: 17 + Enable ALS-current-control mode (0, 1). 18 + 19 + What: /sys/class/backlight/<backlight>/id 20 + Date: April 2012 21 + KernelVersion: 3.5 22 + Contact: Johan Hovold <jhovold@gmail.com> 23 + Description: 24 + Get the id of this backlight (0, 1). 25 + 26 + What: /sys/class/backlight/<backlight>/linear 27 + Date: April 2012 28 + KernelVersion: 3.5 29 + Contact: Johan Hovold <jhovold@gmail.com> 30 + Description: 31 + Set the brightness-mapping mode (0, 1), where 32 + 33 + 0 - exponential mode 34 + 1 - linear mode 35 + 36 + What: /sys/class/backlight/<backlight>/pwm 37 + Date: April 2012 38 + KernelVersion: 3.5 39 + Contact: Johan Hovold <jhovold@gmail.com> 40 + Description: 41 + Set the PWM-input control mask (5 bits), where 42 + 43 + bit 5 - PWM-input enabled in Zone 4 44 + bit 4 - PWM-input enabled in Zone 3 45 + bit 3 - PWM-input enabled in Zone 2 46 + bit 2 - PWM-input enabled in Zone 1 47 + bit 1 - PWM-input enabled in Zone 0 48 + bit 0 - PWM-input enabled
+12
drivers/video/backlight/Kconfig
··· 184 184 known as the Corgi backlight driver. If you have a Sharp Zaurus 185 185 SL-C7xx, SL-Cxx00 or SL-6000x say y. 186 186 187 + config BACKLIGHT_LM3533 188 + tristate "Backlight Driver for LM3533" 189 + depends on BACKLIGHT_CLASS_DEVICE 190 + depends on MFD_LM3533 191 + help 192 + Say Y to enable the backlight driver for National Semiconductor / TI 193 + LM3533 Lighting Power chips. 194 + 195 + The backlights can be controlled directly, through PWM input, or by 196 + the ambient-light-sensor interface. The chip supports 256 brightness 197 + levels. 198 + 187 199 config BACKLIGHT_LOCOMO 188 200 tristate "Sharp LOCOMO LCD/Backlight Driver" 189 201 depends on SHARP_LOCOMO
+1
drivers/video/backlight/Makefile
··· 21 21 obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o 22 22 obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o 23 23 obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o 24 + obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o 24 25 obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o 25 26 obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o 26 27 obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o
+423
drivers/video/backlight/lm3533_bl.c
··· 1 + /* 2 + * lm3533-bl.c -- LM3533 Backlight driver 3 + * 4 + * Copyright (C) 2011-2012 Texas Instruments 5 + * 6 + * Author: Johan Hovold <jhovold@gmail.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify it 9 + * under the terms of the GNU General Public License as published by the 10 + * Free Software Foundation; either version 2 of the License, or (at your 11 + * option) any later version. 12 + */ 13 + 14 + #include <linux/module.h> 15 + #include <linux/init.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/backlight.h> 18 + #include <linux/fb.h> 19 + #include <linux/slab.h> 20 + 21 + #include <linux/mfd/lm3533.h> 22 + 23 + 24 + #define LM3533_HVCTRLBANK_COUNT 2 25 + #define LM3533_BL_MAX_BRIGHTNESS 255 26 + 27 + #define LM3533_REG_CTRLBANK_AB_BCONF 0x1a 28 + 29 + 30 + struct lm3533_bl { 31 + struct lm3533 *lm3533; 32 + struct lm3533_ctrlbank cb; 33 + struct backlight_device *bd; 34 + int id; 35 + }; 36 + 37 + 38 + static inline int lm3533_bl_get_ctrlbank_id(struct lm3533_bl *bl) 39 + { 40 + return bl->id; 41 + } 42 + 43 + static int lm3533_bl_update_status(struct backlight_device *bd) 44 + { 45 + struct lm3533_bl *bl = bl_get_data(bd); 46 + int brightness = bd->props.brightness; 47 + 48 + if (bd->props.power != FB_BLANK_UNBLANK) 49 + brightness = 0; 50 + if (bd->props.fb_blank != FB_BLANK_UNBLANK) 51 + brightness = 0; 52 + 53 + return lm3533_ctrlbank_set_brightness(&bl->cb, (u8)brightness); 54 + } 55 + 56 + static int lm3533_bl_get_brightness(struct backlight_device *bd) 57 + { 58 + struct lm3533_bl *bl = bl_get_data(bd); 59 + u8 val; 60 + int ret; 61 + 62 + ret = lm3533_ctrlbank_get_brightness(&bl->cb, &val); 63 + if (ret) 64 + return ret; 65 + 66 + return val; 67 + } 68 + 69 + static const struct backlight_ops lm3533_bl_ops = { 70 + .get_brightness = lm3533_bl_get_brightness, 71 + .update_status = lm3533_bl_update_status, 72 + }; 73 + 74 + static ssize_t show_id(struct device *dev, 75 + struct device_attribute *attr, char *buf) 76 + { 77 + struct lm3533_bl *bl = dev_get_drvdata(dev); 78 + 79 + return scnprintf(buf, PAGE_SIZE, "%d\n", bl->id); 80 + } 81 + 82 + static ssize_t show_als_channel(struct device *dev, 83 + struct device_attribute *attr, char *buf) 84 + { 85 + struct lm3533_bl *bl = dev_get_drvdata(dev); 86 + unsigned channel = lm3533_bl_get_ctrlbank_id(bl); 87 + 88 + return scnprintf(buf, PAGE_SIZE, "%u\n", channel); 89 + } 90 + 91 + static ssize_t show_als_en(struct device *dev, 92 + struct device_attribute *attr, char *buf) 93 + { 94 + struct lm3533_bl *bl = dev_get_drvdata(dev); 95 + int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); 96 + u8 val; 97 + u8 mask; 98 + bool enable; 99 + int ret; 100 + 101 + ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val); 102 + if (ret) 103 + return ret; 104 + 105 + mask = 1 << (2 * ctrlbank); 106 + enable = val & mask; 107 + 108 + return scnprintf(buf, PAGE_SIZE, "%d\n", enable); 109 + } 110 + 111 + static ssize_t store_als_en(struct device *dev, 112 + struct device_attribute *attr, 113 + const char *buf, size_t len) 114 + { 115 + struct lm3533_bl *bl = dev_get_drvdata(dev); 116 + int ctrlbank = lm3533_bl_get_ctrlbank_id(bl); 117 + int enable; 118 + u8 val; 119 + u8 mask; 120 + int ret; 121 + 122 + if (kstrtoint(buf, 0, &enable)) 123 + return -EINVAL; 124 + 125 + mask = 1 << (2 * ctrlbank); 126 + 127 + if (enable) 128 + val = mask; 129 + else 130 + val = 0; 131 + 132 + ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val, 133 + mask); 134 + if (ret) 135 + return ret; 136 + 137 + return len; 138 + } 139 + 140 + static ssize_t show_linear(struct device *dev, 141 + struct device_attribute *attr, char *buf) 142 + { 143 + struct lm3533_bl *bl = dev_get_drvdata(dev); 144 + u8 val; 145 + u8 mask; 146 + int linear; 147 + int ret; 148 + 149 + ret = lm3533_read(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, &val); 150 + if (ret) 151 + return ret; 152 + 153 + mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1); 154 + 155 + if (val & mask) 156 + linear = 1; 157 + else 158 + linear = 0; 159 + 160 + return scnprintf(buf, PAGE_SIZE, "%x\n", linear); 161 + } 162 + 163 + static ssize_t store_linear(struct device *dev, 164 + struct device_attribute *attr, 165 + const char *buf, size_t len) 166 + { 167 + struct lm3533_bl *bl = dev_get_drvdata(dev); 168 + unsigned long linear; 169 + u8 mask; 170 + u8 val; 171 + int ret; 172 + 173 + if (kstrtoul(buf, 0, &linear)) 174 + return -EINVAL; 175 + 176 + mask = 1 << (2 * lm3533_bl_get_ctrlbank_id(bl) + 1); 177 + 178 + if (linear) 179 + val = mask; 180 + else 181 + val = 0; 182 + 183 + ret = lm3533_update(bl->lm3533, LM3533_REG_CTRLBANK_AB_BCONF, val, 184 + mask); 185 + if (ret) 186 + return ret; 187 + 188 + return len; 189 + } 190 + 191 + static ssize_t show_pwm(struct device *dev, 192 + struct device_attribute *attr, 193 + char *buf) 194 + { 195 + struct lm3533_bl *bl = dev_get_drvdata(dev); 196 + u8 val; 197 + int ret; 198 + 199 + ret = lm3533_ctrlbank_get_pwm(&bl->cb, &val); 200 + if (ret) 201 + return ret; 202 + 203 + return scnprintf(buf, PAGE_SIZE, "%u\n", val); 204 + } 205 + 206 + static ssize_t store_pwm(struct device *dev, 207 + struct device_attribute *attr, 208 + const char *buf, size_t len) 209 + { 210 + struct lm3533_bl *bl = dev_get_drvdata(dev); 211 + u8 val; 212 + int ret; 213 + 214 + if (kstrtou8(buf, 0, &val)) 215 + return -EINVAL; 216 + 217 + ret = lm3533_ctrlbank_set_pwm(&bl->cb, val); 218 + if (ret) 219 + return ret; 220 + 221 + return len; 222 + } 223 + 224 + static LM3533_ATTR_RO(als_channel); 225 + static LM3533_ATTR_RW(als_en); 226 + static LM3533_ATTR_RO(id); 227 + static LM3533_ATTR_RW(linear); 228 + static LM3533_ATTR_RW(pwm); 229 + 230 + static struct attribute *lm3533_bl_attributes[] = { 231 + &dev_attr_als_channel.attr, 232 + &dev_attr_als_en.attr, 233 + &dev_attr_id.attr, 234 + &dev_attr_linear.attr, 235 + &dev_attr_pwm.attr, 236 + NULL, 237 + }; 238 + 239 + static umode_t lm3533_bl_attr_is_visible(struct kobject *kobj, 240 + struct attribute *attr, int n) 241 + { 242 + struct device *dev = container_of(kobj, struct device, kobj); 243 + struct lm3533_bl *bl = dev_get_drvdata(dev); 244 + umode_t mode = attr->mode; 245 + 246 + if (attr == &dev_attr_als_channel.attr || 247 + attr == &dev_attr_als_en.attr) { 248 + if (!bl->lm3533->have_als) 249 + mode = 0; 250 + } 251 + 252 + return mode; 253 + }; 254 + 255 + static struct attribute_group lm3533_bl_attribute_group = { 256 + .is_visible = lm3533_bl_attr_is_visible, 257 + .attrs = lm3533_bl_attributes 258 + }; 259 + 260 + static int __devinit lm3533_bl_setup(struct lm3533_bl *bl, 261 + struct lm3533_bl_platform_data *pdata) 262 + { 263 + int ret; 264 + 265 + ret = lm3533_ctrlbank_set_max_current(&bl->cb, pdata->max_current); 266 + if (ret) 267 + return ret; 268 + 269 + return lm3533_ctrlbank_set_pwm(&bl->cb, pdata->pwm); 270 + } 271 + 272 + static int __devinit lm3533_bl_probe(struct platform_device *pdev) 273 + { 274 + struct lm3533 *lm3533; 275 + struct lm3533_bl_platform_data *pdata; 276 + struct lm3533_bl *bl; 277 + struct backlight_device *bd; 278 + struct backlight_properties props; 279 + int ret; 280 + 281 + dev_dbg(&pdev->dev, "%s\n", __func__); 282 + 283 + lm3533 = dev_get_drvdata(pdev->dev.parent); 284 + if (!lm3533) 285 + return -EINVAL; 286 + 287 + pdata = pdev->dev.platform_data; 288 + if (!pdata) { 289 + dev_err(&pdev->dev, "no platform data\n"); 290 + return -EINVAL; 291 + } 292 + 293 + if (pdev->id < 0 || pdev->id >= LM3533_HVCTRLBANK_COUNT) { 294 + dev_err(&pdev->dev, "illegal backlight id %d\n", pdev->id); 295 + return -EINVAL; 296 + } 297 + 298 + bl = kzalloc(sizeof(*bl), GFP_KERNEL); 299 + if (!bl) { 300 + dev_err(&pdev->dev, 301 + "failed to allocate memory for backlight\n"); 302 + return -ENOMEM; 303 + } 304 + 305 + bl->lm3533 = lm3533; 306 + bl->id = pdev->id; 307 + 308 + bl->cb.lm3533 = lm3533; 309 + bl->cb.id = lm3533_bl_get_ctrlbank_id(bl); 310 + bl->cb.dev = NULL; /* until registered */ 311 + 312 + memset(&props, 0, sizeof(props)); 313 + props.type = BACKLIGHT_RAW; 314 + props.max_brightness = LM3533_BL_MAX_BRIGHTNESS; 315 + props.brightness = pdata->default_brightness; 316 + bd = backlight_device_register(pdata->name, pdev->dev.parent, bl, 317 + &lm3533_bl_ops, &props); 318 + if (IS_ERR(bd)) { 319 + dev_err(&pdev->dev, "failed to register backlight device\n"); 320 + ret = PTR_ERR(bd); 321 + goto err_free; 322 + } 323 + 324 + bl->bd = bd; 325 + bl->cb.dev = &bl->bd->dev; 326 + 327 + platform_set_drvdata(pdev, bl); 328 + 329 + ret = sysfs_create_group(&bd->dev.kobj, &lm3533_bl_attribute_group); 330 + if (ret < 0) { 331 + dev_err(&pdev->dev, "failed to create sysfs attributes\n"); 332 + goto err_unregister; 333 + } 334 + 335 + backlight_update_status(bd); 336 + 337 + ret = lm3533_bl_setup(bl, pdata); 338 + if (ret) 339 + goto err_sysfs_remove; 340 + 341 + ret = lm3533_ctrlbank_enable(&bl->cb); 342 + if (ret) 343 + goto err_sysfs_remove; 344 + 345 + return 0; 346 + 347 + err_sysfs_remove: 348 + sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group); 349 + err_unregister: 350 + backlight_device_unregister(bd); 351 + err_free: 352 + kfree(bl); 353 + 354 + return ret; 355 + } 356 + 357 + static int __devexit lm3533_bl_remove(struct platform_device *pdev) 358 + { 359 + struct lm3533_bl *bl = platform_get_drvdata(pdev); 360 + struct backlight_device *bd = bl->bd; 361 + 362 + dev_dbg(&bd->dev, "%s\n", __func__); 363 + 364 + bd->props.power = FB_BLANK_POWERDOWN; 365 + bd->props.brightness = 0; 366 + 367 + lm3533_ctrlbank_disable(&bl->cb); 368 + sysfs_remove_group(&bd->dev.kobj, &lm3533_bl_attribute_group); 369 + backlight_device_unregister(bd); 370 + kfree(bl); 371 + 372 + return 0; 373 + } 374 + 375 + #ifdef CONFIG_PM 376 + static int lm3533_bl_suspend(struct platform_device *pdev, pm_message_t state) 377 + { 378 + struct lm3533_bl *bl = platform_get_drvdata(pdev); 379 + 380 + dev_dbg(&pdev->dev, "%s\n", __func__); 381 + 382 + return lm3533_ctrlbank_disable(&bl->cb); 383 + } 384 + 385 + static int lm3533_bl_resume(struct platform_device *pdev) 386 + { 387 + struct lm3533_bl *bl = platform_get_drvdata(pdev); 388 + 389 + dev_dbg(&pdev->dev, "%s\n", __func__); 390 + 391 + return lm3533_ctrlbank_enable(&bl->cb); 392 + } 393 + #else 394 + #define lm3533_bl_suspend NULL 395 + #define lm3533_bl_resume NULL 396 + #endif 397 + 398 + static void lm3533_bl_shutdown(struct platform_device *pdev) 399 + { 400 + struct lm3533_bl *bl = platform_get_drvdata(pdev); 401 + 402 + dev_dbg(&pdev->dev, "%s\n", __func__); 403 + 404 + lm3533_ctrlbank_disable(&bl->cb); 405 + } 406 + 407 + static struct platform_driver lm3533_bl_driver = { 408 + .driver = { 409 + .name = "lm3533-backlight", 410 + .owner = THIS_MODULE, 411 + }, 412 + .probe = lm3533_bl_probe, 413 + .remove = __devexit_p(lm3533_bl_remove), 414 + .shutdown = lm3533_bl_shutdown, 415 + .suspend = lm3533_bl_suspend, 416 + .resume = lm3533_bl_resume, 417 + }; 418 + module_platform_driver(lm3533_bl_driver); 419 + 420 + MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); 421 + MODULE_DESCRIPTION("LM3533 Backlight driver"); 422 + MODULE_LICENSE("GPL"); 423 + MODULE_ALIAS("platform:lm3533-backlight");