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

Configure Feed

Select the types of activity you want to include in your feed.

at v3.2-rc3 402 lines 11 kB view raw
1/* 2 * Backlight driver for Analog Devices ADP5520/ADP5501 MFD PMICs 3 * 4 * Copyright 2009 Analog Devices Inc. 5 * 6 * Licensed under the GPL-2 or later. 7 */ 8 9#include <linux/kernel.h> 10#include <linux/init.h> 11#include <linux/platform_device.h> 12#include <linux/fb.h> 13#include <linux/backlight.h> 14#include <linux/mfd/adp5520.h> 15#include <linux/slab.h> 16#include <linux/module.h> 17 18struct adp5520_bl { 19 struct device *master; 20 struct adp5520_backlight_platform_data *pdata; 21 struct mutex lock; 22 unsigned long cached_daylight_max; 23 int id; 24 int current_brightness; 25}; 26 27static int adp5520_bl_set(struct backlight_device *bl, int brightness) 28{ 29 struct adp5520_bl *data = bl_get_data(bl); 30 struct device *master = data->master; 31 int ret = 0; 32 33 if (data->pdata->en_ambl_sens) { 34 if ((brightness > 0) && (brightness < ADP5020_MAX_BRIGHTNESS)) { 35 /* Disable Ambient Light auto adjust */ 36 ret |= adp5520_clr_bits(master, ADP5520_BL_CONTROL, 37 ADP5520_BL_AUTO_ADJ); 38 ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX, 39 brightness); 40 } else { 41 /* 42 * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust 43 * restore daylight l3 sysfs brightness 44 */ 45 ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX, 46 data->cached_daylight_max); 47 ret |= adp5520_set_bits(master, ADP5520_BL_CONTROL, 48 ADP5520_BL_AUTO_ADJ); 49 } 50 } else { 51 ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX, brightness); 52 } 53 54 if (data->current_brightness && brightness == 0) 55 ret |= adp5520_set_bits(master, 56 ADP5520_MODE_STATUS, ADP5520_DIM_EN); 57 else if (data->current_brightness == 0 && brightness) 58 ret |= adp5520_clr_bits(master, 59 ADP5520_MODE_STATUS, ADP5520_DIM_EN); 60 61 if (!ret) 62 data->current_brightness = brightness; 63 64 return ret; 65} 66 67static int adp5520_bl_update_status(struct backlight_device *bl) 68{ 69 int brightness = bl->props.brightness; 70 if (bl->props.power != FB_BLANK_UNBLANK) 71 brightness = 0; 72 73 if (bl->props.fb_blank != FB_BLANK_UNBLANK) 74 brightness = 0; 75 76 return adp5520_bl_set(bl, brightness); 77} 78 79static int adp5520_bl_get_brightness(struct backlight_device *bl) 80{ 81 struct adp5520_bl *data = bl_get_data(bl); 82 int error; 83 uint8_t reg_val; 84 85 error = adp5520_read(data->master, ADP5520_BL_VALUE, &reg_val); 86 87 return error ? data->current_brightness : reg_val; 88} 89 90static const struct backlight_ops adp5520_bl_ops = { 91 .update_status = adp5520_bl_update_status, 92 .get_brightness = adp5520_bl_get_brightness, 93}; 94 95static int adp5520_bl_setup(struct backlight_device *bl) 96{ 97 struct adp5520_bl *data = bl_get_data(bl); 98 struct device *master = data->master; 99 struct adp5520_backlight_platform_data *pdata = data->pdata; 100 int ret = 0; 101 102 ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX, 103 pdata->l1_daylight_max); 104 ret |= adp5520_write(master, ADP5520_DAYLIGHT_DIM, 105 pdata->l1_daylight_dim); 106 107 if (pdata->en_ambl_sens) { 108 data->cached_daylight_max = pdata->l1_daylight_max; 109 ret |= adp5520_write(master, ADP5520_OFFICE_MAX, 110 pdata->l2_office_max); 111 ret |= adp5520_write(master, ADP5520_OFFICE_DIM, 112 pdata->l2_office_dim); 113 ret |= adp5520_write(master, ADP5520_DARK_MAX, 114 pdata->l3_dark_max); 115 ret |= adp5520_write(master, ADP5520_DARK_DIM, 116 pdata->l3_dark_dim); 117 ret |= adp5520_write(master, ADP5520_L2_TRIP, 118 pdata->l2_trip); 119 ret |= adp5520_write(master, ADP5520_L2_HYS, 120 pdata->l2_hyst); 121 ret |= adp5520_write(master, ADP5520_L3_TRIP, 122 pdata->l3_trip); 123 ret |= adp5520_write(master, ADP5520_L3_HYS, 124 pdata->l3_hyst); 125 ret |= adp5520_write(master, ADP5520_ALS_CMPR_CFG, 126 ALS_CMPR_CFG_VAL(pdata->abml_filt, 127 ADP5520_L3_EN)); 128 } 129 130 ret |= adp5520_write(master, ADP5520_BL_CONTROL, 131 BL_CTRL_VAL(pdata->fade_led_law, 132 pdata->en_ambl_sens)); 133 134 ret |= adp5520_write(master, ADP5520_BL_FADE, FADE_VAL(pdata->fade_in, 135 pdata->fade_out)); 136 137 ret |= adp5520_set_bits(master, ADP5520_MODE_STATUS, 138 ADP5520_BL_EN | ADP5520_DIM_EN); 139 140 return ret; 141} 142 143static ssize_t adp5520_show(struct device *dev, char *buf, int reg) 144{ 145 struct adp5520_bl *data = dev_get_drvdata(dev); 146 int error; 147 uint8_t reg_val; 148 149 mutex_lock(&data->lock); 150 error = adp5520_read(data->master, reg, &reg_val); 151 mutex_unlock(&data->lock); 152 153 return sprintf(buf, "%u\n", reg_val); 154} 155 156static ssize_t adp5520_store(struct device *dev, const char *buf, 157 size_t count, int reg) 158{ 159 struct adp5520_bl *data = dev_get_drvdata(dev); 160 unsigned long val; 161 int ret; 162 163 ret = strict_strtoul(buf, 10, &val); 164 if (ret) 165 return ret; 166 167 mutex_lock(&data->lock); 168 adp5520_write(data->master, reg, val); 169 mutex_unlock(&data->lock); 170 171 return count; 172} 173 174static ssize_t adp5520_bl_dark_max_show(struct device *dev, 175 struct device_attribute *attr, char *buf) 176{ 177 return adp5520_show(dev, buf, ADP5520_DARK_MAX); 178} 179 180static ssize_t adp5520_bl_dark_max_store(struct device *dev, 181 struct device_attribute *attr, 182 const char *buf, size_t count) 183{ 184 return adp5520_store(dev, buf, count, ADP5520_DARK_MAX); 185} 186static DEVICE_ATTR(dark_max, 0664, adp5520_bl_dark_max_show, 187 adp5520_bl_dark_max_store); 188 189static ssize_t adp5520_bl_office_max_show(struct device *dev, 190 struct device_attribute *attr, char *buf) 191{ 192 return adp5520_show(dev, buf, ADP5520_OFFICE_MAX); 193} 194 195static ssize_t adp5520_bl_office_max_store(struct device *dev, 196 struct device_attribute *attr, 197 const char *buf, size_t count) 198{ 199 return adp5520_store(dev, buf, count, ADP5520_OFFICE_MAX); 200} 201static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show, 202 adp5520_bl_office_max_store); 203 204static ssize_t adp5520_bl_daylight_max_show(struct device *dev, 205 struct device_attribute *attr, char *buf) 206{ 207 return adp5520_show(dev, buf, ADP5520_DAYLIGHT_MAX); 208} 209 210static ssize_t adp5520_bl_daylight_max_store(struct device *dev, 211 struct device_attribute *attr, 212 const char *buf, size_t count) 213{ 214 struct adp5520_bl *data = dev_get_drvdata(dev); 215 int ret; 216 217 ret = strict_strtoul(buf, 10, &data->cached_daylight_max); 218 if (ret < 0) 219 return ret; 220 221 return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_MAX); 222} 223static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show, 224 adp5520_bl_daylight_max_store); 225 226static ssize_t adp5520_bl_dark_dim_show(struct device *dev, 227 struct device_attribute *attr, char *buf) 228{ 229 return adp5520_show(dev, buf, ADP5520_DARK_DIM); 230} 231 232static ssize_t adp5520_bl_dark_dim_store(struct device *dev, 233 struct device_attribute *attr, 234 const char *buf, size_t count) 235{ 236 return adp5520_store(dev, buf, count, ADP5520_DARK_DIM); 237} 238static DEVICE_ATTR(dark_dim, 0664, adp5520_bl_dark_dim_show, 239 adp5520_bl_dark_dim_store); 240 241static ssize_t adp5520_bl_office_dim_show(struct device *dev, 242 struct device_attribute *attr, char *buf) 243{ 244 return adp5520_show(dev, buf, ADP5520_OFFICE_DIM); 245} 246 247static ssize_t adp5520_bl_office_dim_store(struct device *dev, 248 struct device_attribute *attr, 249 const char *buf, size_t count) 250{ 251 return adp5520_store(dev, buf, count, ADP5520_OFFICE_DIM); 252} 253static DEVICE_ATTR(office_dim, 0664, adp5520_bl_office_dim_show, 254 adp5520_bl_office_dim_store); 255 256static ssize_t adp5520_bl_daylight_dim_show(struct device *dev, 257 struct device_attribute *attr, char *buf) 258{ 259 return adp5520_show(dev, buf, ADP5520_DAYLIGHT_DIM); 260} 261 262static ssize_t adp5520_bl_daylight_dim_store(struct device *dev, 263 struct device_attribute *attr, 264 const char *buf, size_t count) 265{ 266 return adp5520_store(dev, buf, count, ADP5520_DAYLIGHT_DIM); 267} 268static DEVICE_ATTR(daylight_dim, 0664, adp5520_bl_daylight_dim_show, 269 adp5520_bl_daylight_dim_store); 270 271static struct attribute *adp5520_bl_attributes[] = { 272 &dev_attr_dark_max.attr, 273 &dev_attr_dark_dim.attr, 274 &dev_attr_office_max.attr, 275 &dev_attr_office_dim.attr, 276 &dev_attr_daylight_max.attr, 277 &dev_attr_daylight_dim.attr, 278 NULL 279}; 280 281static const struct attribute_group adp5520_bl_attr_group = { 282 .attrs = adp5520_bl_attributes, 283}; 284 285static int __devinit adp5520_bl_probe(struct platform_device *pdev) 286{ 287 struct backlight_properties props; 288 struct backlight_device *bl; 289 struct adp5520_bl *data; 290 int ret = 0; 291 292 data = kzalloc(sizeof(*data), GFP_KERNEL); 293 if (data == NULL) 294 return -ENOMEM; 295 296 data->master = pdev->dev.parent; 297 data->pdata = pdev->dev.platform_data; 298 299 if (data->pdata == NULL) { 300 dev_err(&pdev->dev, "missing platform data\n"); 301 kfree(data); 302 return -ENODEV; 303 } 304 305 data->id = pdev->id; 306 data->current_brightness = 0; 307 308 mutex_init(&data->lock); 309 310 memset(&props, 0, sizeof(struct backlight_properties)); 311 props.type = BACKLIGHT_RAW; 312 props.max_brightness = ADP5020_MAX_BRIGHTNESS; 313 bl = backlight_device_register(pdev->name, data->master, data, 314 &adp5520_bl_ops, &props); 315 if (IS_ERR(bl)) { 316 dev_err(&pdev->dev, "failed to register backlight\n"); 317 kfree(data); 318 return PTR_ERR(bl); 319 } 320 321 bl->props.brightness = ADP5020_MAX_BRIGHTNESS; 322 if (data->pdata->en_ambl_sens) 323 ret = sysfs_create_group(&bl->dev.kobj, 324 &adp5520_bl_attr_group); 325 326 if (ret) { 327 dev_err(&pdev->dev, "failed to register sysfs\n"); 328 backlight_device_unregister(bl); 329 kfree(data); 330 } 331 332 platform_set_drvdata(pdev, bl); 333 ret |= adp5520_bl_setup(bl); 334 backlight_update_status(bl); 335 336 return ret; 337} 338 339static int __devexit adp5520_bl_remove(struct platform_device *pdev) 340{ 341 struct backlight_device *bl = platform_get_drvdata(pdev); 342 struct adp5520_bl *data = bl_get_data(bl); 343 344 adp5520_clr_bits(data->master, ADP5520_MODE_STATUS, ADP5520_BL_EN); 345 346 if (data->pdata->en_ambl_sens) 347 sysfs_remove_group(&bl->dev.kobj, 348 &adp5520_bl_attr_group); 349 350 backlight_device_unregister(bl); 351 kfree(data); 352 353 return 0; 354} 355 356#ifdef CONFIG_PM 357static int adp5520_bl_suspend(struct platform_device *pdev, 358 pm_message_t state) 359{ 360 struct backlight_device *bl = platform_get_drvdata(pdev); 361 return adp5520_bl_set(bl, 0); 362} 363 364static int adp5520_bl_resume(struct platform_device *pdev) 365{ 366 struct backlight_device *bl = platform_get_drvdata(pdev); 367 368 backlight_update_status(bl); 369 return 0; 370} 371#else 372#define adp5520_bl_suspend NULL 373#define adp5520_bl_resume NULL 374#endif 375 376static struct platform_driver adp5520_bl_driver = { 377 .driver = { 378 .name = "adp5520-backlight", 379 .owner = THIS_MODULE, 380 }, 381 .probe = adp5520_bl_probe, 382 .remove = __devexit_p(adp5520_bl_remove), 383 .suspend = adp5520_bl_suspend, 384 .resume = adp5520_bl_resume, 385}; 386 387static int __init adp5520_bl_init(void) 388{ 389 return platform_driver_register(&adp5520_bl_driver); 390} 391module_init(adp5520_bl_init); 392 393static void __exit adp5520_bl_exit(void) 394{ 395 platform_driver_unregister(&adp5520_bl_driver); 396} 397module_exit(adp5520_bl_exit); 398 399MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 400MODULE_DESCRIPTION("ADP5520(01) Backlight Driver"); 401MODULE_LICENSE("GPL"); 402MODULE_ALIAS("platform:adp5520-backlight");