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