1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Backlight driver for Analog Devices ADP5520/ADP5501 MFD PMICs
5 * Copyright 2009 Analog Devices Inc.
8 #include <linux/kernel.h>
9 #include <linux/init.h>
10 #include <linux/platform_device.h>
12 #include <linux/backlight.h>
13 #include <linux/mfd/adp5520.h>
14 #include <linux/slab.h>
15 #include <linux/module.h>
18 struct device
*master
;
19 struct adp5520_backlight_platform_data
*pdata
;
21 unsigned long cached_daylight_max
;
23 int current_brightness
;
26 static int adp5520_bl_set(struct backlight_device
*bl
, int brightness
)
28 struct adp5520_bl
*data
= bl_get_data(bl
);
29 struct device
*master
= data
->master
;
32 if (data
->pdata
->en_ambl_sens
) {
33 if ((brightness
> 0) && (brightness
< ADP5020_MAX_BRIGHTNESS
)) {
34 /* Disable Ambient Light auto adjust */
35 ret
|= adp5520_clr_bits(master
, ADP5520_BL_CONTROL
,
37 ret
|= adp5520_write(master
, ADP5520_DAYLIGHT_MAX
,
41 * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
42 * restore daylight l3 sysfs brightness
44 ret
|= adp5520_write(master
, ADP5520_DAYLIGHT_MAX
,
45 data
->cached_daylight_max
);
46 ret
|= adp5520_set_bits(master
, ADP5520_BL_CONTROL
,
50 ret
|= adp5520_write(master
, ADP5520_DAYLIGHT_MAX
, brightness
);
53 if (data
->current_brightness
&& brightness
== 0)
54 ret
|= adp5520_set_bits(master
,
55 ADP5520_MODE_STATUS
, ADP5520_DIM_EN
);
56 else if (data
->current_brightness
== 0 && brightness
)
57 ret
|= adp5520_clr_bits(master
,
58 ADP5520_MODE_STATUS
, ADP5520_DIM_EN
);
61 data
->current_brightness
= brightness
;
66 static int adp5520_bl_update_status(struct backlight_device
*bl
)
68 return adp5520_bl_set(bl
, backlight_get_brightness(bl
));
71 static int adp5520_bl_get_brightness(struct backlight_device
*bl
)
73 struct adp5520_bl
*data
= bl_get_data(bl
);
77 error
= adp5520_read(data
->master
, ADP5520_BL_VALUE
, ®_val
);
79 return error
? data
->current_brightness
: reg_val
;
82 static const struct backlight_ops adp5520_bl_ops
= {
83 .update_status
= adp5520_bl_update_status
,
84 .get_brightness
= adp5520_bl_get_brightness
,
87 static int adp5520_bl_setup(struct backlight_device
*bl
)
89 struct adp5520_bl
*data
= bl_get_data(bl
);
90 struct device
*master
= data
->master
;
91 struct adp5520_backlight_platform_data
*pdata
= data
->pdata
;
94 ret
|= adp5520_write(master
, ADP5520_DAYLIGHT_MAX
,
95 pdata
->l1_daylight_max
);
96 ret
|= adp5520_write(master
, ADP5520_DAYLIGHT_DIM
,
97 pdata
->l1_daylight_dim
);
99 if (pdata
->en_ambl_sens
) {
100 data
->cached_daylight_max
= pdata
->l1_daylight_max
;
101 ret
|= adp5520_write(master
, ADP5520_OFFICE_MAX
,
102 pdata
->l2_office_max
);
103 ret
|= adp5520_write(master
, ADP5520_OFFICE_DIM
,
104 pdata
->l2_office_dim
);
105 ret
|= adp5520_write(master
, ADP5520_DARK_MAX
,
107 ret
|= adp5520_write(master
, ADP5520_DARK_DIM
,
109 ret
|= adp5520_write(master
, ADP5520_L2_TRIP
,
111 ret
|= adp5520_write(master
, ADP5520_L2_HYS
,
113 ret
|= adp5520_write(master
, ADP5520_L3_TRIP
,
115 ret
|= adp5520_write(master
, ADP5520_L3_HYS
,
117 ret
|= adp5520_write(master
, ADP5520_ALS_CMPR_CFG
,
118 ALS_CMPR_CFG_VAL(pdata
->abml_filt
,
122 ret
|= adp5520_write(master
, ADP5520_BL_CONTROL
,
123 BL_CTRL_VAL(pdata
->fade_led_law
,
124 pdata
->en_ambl_sens
));
126 ret
|= adp5520_write(master
, ADP5520_BL_FADE
, FADE_VAL(pdata
->fade_in
,
129 ret
|= adp5520_set_bits(master
, ADP5520_MODE_STATUS
,
130 ADP5520_BL_EN
| ADP5520_DIM_EN
);
135 static ssize_t
adp5520_show(struct device
*dev
, char *buf
, int reg
)
137 struct adp5520_bl
*data
= dev_get_drvdata(dev
);
141 mutex_lock(&data
->lock
);
142 ret
= adp5520_read(data
->master
, reg
, ®_val
);
143 mutex_unlock(&data
->lock
);
148 return sprintf(buf
, "%u\n", reg_val
);
151 static ssize_t
adp5520_store(struct device
*dev
, const char *buf
,
152 size_t count
, int reg
)
154 struct adp5520_bl
*data
= dev_get_drvdata(dev
);
158 ret
= kstrtoul(buf
, 10, &val
);
162 mutex_lock(&data
->lock
);
163 adp5520_write(data
->master
, reg
, val
);
164 mutex_unlock(&data
->lock
);
169 static ssize_t
adp5520_bl_dark_max_show(struct device
*dev
,
170 struct device_attribute
*attr
, char *buf
)
172 return adp5520_show(dev
, buf
, ADP5520_DARK_MAX
);
175 static ssize_t
adp5520_bl_dark_max_store(struct device
*dev
,
176 struct device_attribute
*attr
,
177 const char *buf
, size_t count
)
179 return adp5520_store(dev
, buf
, count
, ADP5520_DARK_MAX
);
181 static DEVICE_ATTR(dark_max
, 0664, adp5520_bl_dark_max_show
,
182 adp5520_bl_dark_max_store
);
184 static ssize_t
adp5520_bl_office_max_show(struct device
*dev
,
185 struct device_attribute
*attr
, char *buf
)
187 return adp5520_show(dev
, buf
, ADP5520_OFFICE_MAX
);
190 static ssize_t
adp5520_bl_office_max_store(struct device
*dev
,
191 struct device_attribute
*attr
,
192 const char *buf
, size_t count
)
194 return adp5520_store(dev
, buf
, count
, ADP5520_OFFICE_MAX
);
196 static DEVICE_ATTR(office_max
, 0664, adp5520_bl_office_max_show
,
197 adp5520_bl_office_max_store
);
199 static ssize_t
adp5520_bl_daylight_max_show(struct device
*dev
,
200 struct device_attribute
*attr
, char *buf
)
202 return adp5520_show(dev
, buf
, ADP5520_DAYLIGHT_MAX
);
205 static ssize_t
adp5520_bl_daylight_max_store(struct device
*dev
,
206 struct device_attribute
*attr
,
207 const char *buf
, size_t count
)
209 struct adp5520_bl
*data
= dev_get_drvdata(dev
);
212 ret
= kstrtoul(buf
, 10, &data
->cached_daylight_max
);
216 return adp5520_store(dev
, buf
, count
, ADP5520_DAYLIGHT_MAX
);
218 static DEVICE_ATTR(daylight_max
, 0664, adp5520_bl_daylight_max_show
,
219 adp5520_bl_daylight_max_store
);
221 static ssize_t
adp5520_bl_dark_dim_show(struct device
*dev
,
222 struct device_attribute
*attr
, char *buf
)
224 return adp5520_show(dev
, buf
, ADP5520_DARK_DIM
);
227 static ssize_t
adp5520_bl_dark_dim_store(struct device
*dev
,
228 struct device_attribute
*attr
,
229 const char *buf
, size_t count
)
231 return adp5520_store(dev
, buf
, count
, ADP5520_DARK_DIM
);
233 static DEVICE_ATTR(dark_dim
, 0664, adp5520_bl_dark_dim_show
,
234 adp5520_bl_dark_dim_store
);
236 static ssize_t
adp5520_bl_office_dim_show(struct device
*dev
,
237 struct device_attribute
*attr
, char *buf
)
239 return adp5520_show(dev
, buf
, ADP5520_OFFICE_DIM
);
242 static ssize_t
adp5520_bl_office_dim_store(struct device
*dev
,
243 struct device_attribute
*attr
,
244 const char *buf
, size_t count
)
246 return adp5520_store(dev
, buf
, count
, ADP5520_OFFICE_DIM
);
248 static DEVICE_ATTR(office_dim
, 0664, adp5520_bl_office_dim_show
,
249 adp5520_bl_office_dim_store
);
251 static ssize_t
adp5520_bl_daylight_dim_show(struct device
*dev
,
252 struct device_attribute
*attr
, char *buf
)
254 return adp5520_show(dev
, buf
, ADP5520_DAYLIGHT_DIM
);
257 static ssize_t
adp5520_bl_daylight_dim_store(struct device
*dev
,
258 struct device_attribute
*attr
,
259 const char *buf
, size_t count
)
261 return adp5520_store(dev
, buf
, count
, ADP5520_DAYLIGHT_DIM
);
263 static DEVICE_ATTR(daylight_dim
, 0664, adp5520_bl_daylight_dim_show
,
264 adp5520_bl_daylight_dim_store
);
266 static struct attribute
*adp5520_bl_attributes
[] = {
267 &dev_attr_dark_max
.attr
,
268 &dev_attr_dark_dim
.attr
,
269 &dev_attr_office_max
.attr
,
270 &dev_attr_office_dim
.attr
,
271 &dev_attr_daylight_max
.attr
,
272 &dev_attr_daylight_dim
.attr
,
276 static const struct attribute_group adp5520_bl_attr_group
= {
277 .attrs
= adp5520_bl_attributes
,
280 static int adp5520_bl_probe(struct platform_device
*pdev
)
282 struct backlight_properties props
;
283 struct backlight_device
*bl
;
284 struct adp5520_bl
*data
;
287 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
291 data
->master
= pdev
->dev
.parent
;
292 data
->pdata
= dev_get_platdata(&pdev
->dev
);
294 if (data
->pdata
== NULL
) {
295 dev_err(&pdev
->dev
, "missing platform data\n");
300 data
->current_brightness
= 0;
302 mutex_init(&data
->lock
);
304 memset(&props
, 0, sizeof(struct backlight_properties
));
305 props
.type
= BACKLIGHT_RAW
;
306 props
.max_brightness
= ADP5020_MAX_BRIGHTNESS
;
307 bl
= devm_backlight_device_register(&pdev
->dev
, pdev
->name
,
308 data
->master
, data
, &adp5520_bl_ops
,
311 dev_err(&pdev
->dev
, "failed to register backlight\n");
315 bl
->props
.brightness
= ADP5020_MAX_BRIGHTNESS
;
316 if (data
->pdata
->en_ambl_sens
)
317 ret
= sysfs_create_group(&bl
->dev
.kobj
,
318 &adp5520_bl_attr_group
);
321 dev_err(&pdev
->dev
, "failed to register sysfs\n");
325 platform_set_drvdata(pdev
, bl
);
326 ret
= adp5520_bl_setup(bl
);
328 dev_err(&pdev
->dev
, "failed to setup\n");
329 if (data
->pdata
->en_ambl_sens
)
330 sysfs_remove_group(&bl
->dev
.kobj
,
331 &adp5520_bl_attr_group
);
335 backlight_update_status(bl
);
340 static int adp5520_bl_remove(struct platform_device
*pdev
)
342 struct backlight_device
*bl
= platform_get_drvdata(pdev
);
343 struct adp5520_bl
*data
= bl_get_data(bl
);
345 adp5520_clr_bits(data
->master
, ADP5520_MODE_STATUS
, ADP5520_BL_EN
);
347 if (data
->pdata
->en_ambl_sens
)
348 sysfs_remove_group(&bl
->dev
.kobj
,
349 &adp5520_bl_attr_group
);
354 #ifdef CONFIG_PM_SLEEP
355 static int adp5520_bl_suspend(struct device
*dev
)
357 struct backlight_device
*bl
= dev_get_drvdata(dev
);
359 return adp5520_bl_set(bl
, 0);
362 static int adp5520_bl_resume(struct device
*dev
)
364 struct backlight_device
*bl
= dev_get_drvdata(dev
);
366 backlight_update_status(bl
);
371 static SIMPLE_DEV_PM_OPS(adp5520_bl_pm_ops
, adp5520_bl_suspend
,
374 static struct platform_driver adp5520_bl_driver
= {
376 .name
= "adp5520-backlight",
377 .pm
= &adp5520_bl_pm_ops
,
379 .probe
= adp5520_bl_probe
,
380 .remove
= adp5520_bl_remove
,
383 module_platform_driver(adp5520_bl_driver
);
385 MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
386 MODULE_DESCRIPTION("ADP5520(01) Backlight Driver");
387 MODULE_LICENSE("GPL");
388 MODULE_ALIAS("platform:adp5520-backlight");