backlight: new driver for ADP5520/ADP5501 MFD PMICs

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Bryan Wu <cooloney@kernel.org>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>

authored by Michael Hennerich and committed by Richard Purdie a7998cec 89dfc28c

+390
+11
drivers/video/backlight/Kconfig
··· 251 251 help 252 252 Say Y to enable the backlight driver on Avionic Design Xanthos-based 253 253 boards. 254 + 255 + config BACKLIGHT_ADP5520 256 + tristate "Backlight Driver for ADP5520/ADP5501 using WLED" 257 + depends on BACKLIGHT_CLASS_DEVICE && PMIC_ADP5520 258 + help 259 + If you have a LCD backlight connected to the BST/BL_SNK output of 260 + ADP5520 or ADP5501, say Y here to enable this driver. 261 + 262 + To compile this driver as a module, choose M here: the module will 263 + be called adp5520_bl. 264 +
+2
drivers/video/backlight/Makefile
··· 27 27 obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o 28 28 obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o 29 29 obj-$(CONFIG_BACKLIGHT_ADX) += adx_bl.o 30 + obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o 31 +
+377
drivers/video/backlight/adp5520_bl.c
··· 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 + 16 + struct adp5520_bl { 17 + struct device *master; 18 + struct adp5520_backlight_platfrom_data *pdata; 19 + struct mutex lock; 20 + unsigned long cached_daylight_max; 21 + int id; 22 + int current_brightness; 23 + }; 24 + 25 + static int adp5520_bl_set(struct backlight_device *bl, int brightness) 26 + { 27 + struct adp5520_bl *data = bl_get_data(bl); 28 + struct device *master = data->master; 29 + int ret = 0; 30 + 31 + if (data->pdata->en_ambl_sens) { 32 + if ((brightness > 0) && (brightness < ADP5020_MAX_BRIGHTNESS)) { 33 + /* Disable Ambient Light auto adjust */ 34 + ret |= adp5520_clr_bits(master, BL_CONTROL, 35 + BL_AUTO_ADJ); 36 + ret |= adp5520_write(master, DAYLIGHT_MAX, brightness); 37 + } else { 38 + /* 39 + * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust 40 + * restore daylight l3 sysfs brightness 41 + */ 42 + ret |= adp5520_write(master, DAYLIGHT_MAX, 43 + data->cached_daylight_max); 44 + ret |= adp5520_set_bits(master, BL_CONTROL, 45 + BL_AUTO_ADJ); 46 + } 47 + } else { 48 + ret |= adp5520_write(master, DAYLIGHT_MAX, brightness); 49 + } 50 + 51 + if (data->current_brightness && brightness == 0) 52 + ret |= adp5520_set_bits(master, 53 + MODE_STATUS, DIM_EN); 54 + else if (data->current_brightness == 0 && brightness) 55 + ret |= adp5520_clr_bits(master, 56 + MODE_STATUS, DIM_EN); 57 + 58 + if (!ret) 59 + data->current_brightness = brightness; 60 + 61 + return ret; 62 + } 63 + 64 + static int adp5520_bl_update_status(struct backlight_device *bl) 65 + { 66 + int brightness = bl->props.brightness; 67 + if (bl->props.power != FB_BLANK_UNBLANK) 68 + brightness = 0; 69 + 70 + if (bl->props.fb_blank != FB_BLANK_UNBLANK) 71 + brightness = 0; 72 + 73 + return adp5520_bl_set(bl, brightness); 74 + } 75 + 76 + static int adp5520_bl_get_brightness(struct backlight_device *bl) 77 + { 78 + struct adp5520_bl *data = bl_get_data(bl); 79 + int error; 80 + uint8_t reg_val; 81 + 82 + error = adp5520_read(data->master, BL_VALUE, &reg_val); 83 + 84 + return error ? data->current_brightness : reg_val; 85 + } 86 + 87 + static struct backlight_ops adp5520_bl_ops = { 88 + .update_status = adp5520_bl_update_status, 89 + .get_brightness = adp5520_bl_get_brightness, 90 + }; 91 + 92 + static int adp5520_bl_setup(struct backlight_device *bl) 93 + { 94 + struct adp5520_bl *data = bl_get_data(bl); 95 + struct device *master = data->master; 96 + struct adp5520_backlight_platfrom_data *pdata = data->pdata; 97 + int ret = 0; 98 + 99 + ret |= adp5520_write(master, DAYLIGHT_MAX, pdata->l1_daylight_max); 100 + ret |= adp5520_write(master, DAYLIGHT_DIM, pdata->l1_daylight_dim); 101 + 102 + if (pdata->en_ambl_sens) { 103 + data->cached_daylight_max = pdata->l1_daylight_max; 104 + ret |= adp5520_write(master, OFFICE_MAX, pdata->l2_office_max); 105 + ret |= adp5520_write(master, OFFICE_DIM, pdata->l2_office_dim); 106 + ret |= adp5520_write(master, DARK_MAX, pdata->l3_dark_max); 107 + ret |= adp5520_write(master, DARK_DIM, pdata->l3_dark_dim); 108 + ret |= adp5520_write(master, L2_TRIP, pdata->l2_trip); 109 + ret |= adp5520_write(master, L2_HYS, pdata->l2_hyst); 110 + ret |= adp5520_write(master, L3_TRIP, pdata->l3_trip); 111 + ret |= adp5520_write(master, L3_HYS, pdata->l3_hyst); 112 + ret |= adp5520_write(master, ALS_CMPR_CFG, 113 + ALS_CMPR_CFG_VAL(pdata->abml_filt, L3_EN)); 114 + } 115 + 116 + ret |= adp5520_write(master, BL_CONTROL, 117 + BL_CTRL_VAL(pdata->fade_led_law, pdata->en_ambl_sens)); 118 + 119 + ret |= adp5520_write(master, BL_FADE, FADE_VAL(pdata->fade_in, 120 + pdata->fade_out)); 121 + 122 + ret |= adp5520_set_bits(master, MODE_STATUS, BL_EN | DIM_EN); 123 + 124 + return ret; 125 + } 126 + 127 + static ssize_t adp5520_show(struct device *dev, char *buf, int reg) 128 + { 129 + struct adp5520_bl *data = dev_get_drvdata(dev); 130 + int error; 131 + uint8_t reg_val; 132 + 133 + mutex_lock(&data->lock); 134 + error = adp5520_read(data->master, reg, &reg_val); 135 + mutex_unlock(&data->lock); 136 + 137 + return sprintf(buf, "%u\n", reg_val); 138 + } 139 + 140 + static ssize_t adp5520_store(struct device *dev, const char *buf, 141 + size_t count, int reg) 142 + { 143 + struct adp5520_bl *data = dev_get_drvdata(dev); 144 + unsigned long val; 145 + int ret; 146 + 147 + ret = strict_strtoul(buf, 10, &val); 148 + if (ret) 149 + return ret; 150 + 151 + mutex_lock(&data->lock); 152 + adp5520_write(data->master, reg, val); 153 + mutex_unlock(&data->lock); 154 + 155 + return count; 156 + } 157 + 158 + static ssize_t adp5520_bl_dark_max_show(struct device *dev, 159 + struct device_attribute *attr, char *buf) 160 + { 161 + return adp5520_show(dev, buf, DARK_MAX); 162 + } 163 + 164 + static ssize_t adp5520_bl_dark_max_store(struct device *dev, 165 + struct device_attribute *attr, const char *buf, size_t count) 166 + { 167 + return adp5520_store(dev, buf, count, DARK_MAX); 168 + } 169 + static DEVICE_ATTR(dark_max, 0664, adp5520_bl_dark_max_show, 170 + adp5520_bl_dark_max_store); 171 + 172 + static ssize_t adp5520_bl_office_max_show(struct device *dev, 173 + struct device_attribute *attr, char *buf) 174 + { 175 + return adp5520_show(dev, buf, OFFICE_MAX); 176 + } 177 + 178 + static ssize_t adp5520_bl_office_max_store(struct device *dev, 179 + struct device_attribute *attr, const char *buf, size_t count) 180 + { 181 + return adp5520_store(dev, buf, count, OFFICE_MAX); 182 + } 183 + static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show, 184 + adp5520_bl_office_max_store); 185 + 186 + static ssize_t adp5520_bl_daylight_max_show(struct device *dev, 187 + struct device_attribute *attr, char *buf) 188 + { 189 + return adp5520_show(dev, buf, DAYLIGHT_MAX); 190 + } 191 + 192 + static ssize_t adp5520_bl_daylight_max_store(struct device *dev, 193 + struct device_attribute *attr, const char *buf, size_t count) 194 + { 195 + struct adp5520_bl *data = dev_get_drvdata(dev); 196 + 197 + strict_strtoul(buf, 10, &data->cached_daylight_max); 198 + return adp5520_store(dev, buf, count, DAYLIGHT_MAX); 199 + } 200 + static DEVICE_ATTR(daylight_max, 0664, adp5520_bl_daylight_max_show, 201 + adp5520_bl_daylight_max_store); 202 + 203 + static ssize_t adp5520_bl_dark_dim_show(struct device *dev, 204 + struct device_attribute *attr, char *buf) 205 + { 206 + return adp5520_show(dev, buf, DARK_DIM); 207 + } 208 + 209 + static ssize_t adp5520_bl_dark_dim_store(struct device *dev, 210 + struct device_attribute *attr, 211 + const char *buf, size_t count) 212 + { 213 + return adp5520_store(dev, buf, count, DARK_DIM); 214 + } 215 + static DEVICE_ATTR(dark_dim, 0664, adp5520_bl_dark_dim_show, 216 + adp5520_bl_dark_dim_store); 217 + 218 + static ssize_t adp5520_bl_office_dim_show(struct device *dev, 219 + struct device_attribute *attr, char *buf) 220 + { 221 + return adp5520_show(dev, buf, OFFICE_DIM); 222 + } 223 + 224 + static ssize_t adp5520_bl_office_dim_store(struct device *dev, 225 + struct device_attribute *attr, 226 + const char *buf, size_t count) 227 + { 228 + return adp5520_store(dev, buf, count, OFFICE_DIM); 229 + } 230 + static DEVICE_ATTR(office_dim, 0664, adp5520_bl_office_dim_show, 231 + adp5520_bl_office_dim_store); 232 + 233 + static ssize_t adp5520_bl_daylight_dim_show(struct device *dev, 234 + struct device_attribute *attr, char *buf) 235 + { 236 + return adp5520_show(dev, buf, DAYLIGHT_DIM); 237 + } 238 + 239 + static ssize_t adp5520_bl_daylight_dim_store(struct device *dev, 240 + struct device_attribute *attr, 241 + const char *buf, size_t count) 242 + { 243 + return adp5520_store(dev, buf, count, DAYLIGHT_DIM); 244 + } 245 + static DEVICE_ATTR(daylight_dim, 0664, adp5520_bl_daylight_dim_show, 246 + adp5520_bl_daylight_dim_store); 247 + 248 + static struct attribute *adp5520_bl_attributes[] = { 249 + &dev_attr_dark_max.attr, 250 + &dev_attr_dark_dim.attr, 251 + &dev_attr_office_max.attr, 252 + &dev_attr_office_dim.attr, 253 + &dev_attr_daylight_max.attr, 254 + &dev_attr_daylight_dim.attr, 255 + NULL 256 + }; 257 + 258 + static const struct attribute_group adp5520_bl_attr_group = { 259 + .attrs = adp5520_bl_attributes, 260 + }; 261 + 262 + static int __devinit adp5520_bl_probe(struct platform_device *pdev) 263 + { 264 + struct backlight_device *bl; 265 + struct adp5520_bl *data; 266 + int ret = 0; 267 + 268 + data = kzalloc(sizeof(*data), GFP_KERNEL); 269 + if (data == NULL) 270 + return -ENOMEM; 271 + 272 + data->master = pdev->dev.parent; 273 + data->pdata = pdev->dev.platform_data; 274 + 275 + if (data->pdata == NULL) { 276 + dev_err(&pdev->dev, "missing platform data\n"); 277 + kfree(data); 278 + return -ENODEV; 279 + } 280 + 281 + data->id = pdev->id; 282 + data->current_brightness = 0; 283 + 284 + mutex_init(&data->lock); 285 + 286 + bl = backlight_device_register(pdev->name, data->master, 287 + data, &adp5520_bl_ops); 288 + if (IS_ERR(bl)) { 289 + dev_err(&pdev->dev, "failed to register backlight\n"); 290 + kfree(data); 291 + return PTR_ERR(bl); 292 + } 293 + 294 + bl->props.max_brightness = 295 + bl->props.brightness = ADP5020_MAX_BRIGHTNESS; 296 + 297 + if (data->pdata->en_ambl_sens) 298 + ret = sysfs_create_group(&bl->dev.kobj, 299 + &adp5520_bl_attr_group); 300 + 301 + if (ret) { 302 + dev_err(&pdev->dev, "failed to register sysfs\n"); 303 + backlight_device_unregister(bl); 304 + kfree(data); 305 + } 306 + 307 + platform_set_drvdata(pdev, bl); 308 + ret |= adp5520_bl_setup(bl); 309 + backlight_update_status(bl); 310 + 311 + return ret; 312 + } 313 + 314 + static int __devexit adp5520_bl_remove(struct platform_device *pdev) 315 + { 316 + struct backlight_device *bl = platform_get_drvdata(pdev); 317 + struct adp5520_bl *data = bl_get_data(bl); 318 + 319 + adp5520_clr_bits(data->master, MODE_STATUS, BL_EN); 320 + 321 + if (data->pdata->en_ambl_sens) 322 + sysfs_remove_group(&bl->dev.kobj, 323 + &adp5520_bl_attr_group); 324 + 325 + backlight_device_unregister(bl); 326 + kfree(data); 327 + 328 + return 0; 329 + } 330 + 331 + #ifdef CONFIG_PM 332 + static int adp5520_bl_suspend(struct platform_device *pdev, 333 + pm_message_t state) 334 + { 335 + struct backlight_device *bl = platform_get_drvdata(pdev); 336 + return adp5520_bl_set(bl, 0); 337 + } 338 + 339 + static int adp5520_bl_resume(struct platform_device *pdev) 340 + { 341 + struct backlight_device *bl = platform_get_drvdata(pdev); 342 + 343 + backlight_update_status(bl); 344 + return 0; 345 + } 346 + #else 347 + #define adp5520_bl_suspend NULL 348 + #define adp5520_bl_resume NULL 349 + #endif 350 + 351 + static struct platform_driver adp5520_bl_driver = { 352 + .driver = { 353 + .name = "adp5520-backlight", 354 + .owner = THIS_MODULE, 355 + }, 356 + .probe = adp5520_bl_probe, 357 + .remove = __devexit_p(adp5520_bl_remove), 358 + .suspend = adp5520_bl_suspend, 359 + .resume = adp5520_bl_resume, 360 + }; 361 + 362 + static int __init adp5520_bl_init(void) 363 + { 364 + return platform_driver_register(&adp5520_bl_driver); 365 + } 366 + module_init(adp5520_bl_init); 367 + 368 + static void __exit adp5520_bl_exit(void) 369 + { 370 + platform_driver_unregister(&adp5520_bl_driver); 371 + } 372 + module_exit(adp5520_bl_exit); 373 + 374 + MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 375 + MODULE_DESCRIPTION("ADP5520(01) Backlight Driver"); 376 + MODULE_LICENSE("GPL"); 377 + MODULE_ALIAS("platform:adp5520-backlight");