2 * Backlight driver for Analog Devices ADP5520/ADP5501 MFD PMICs
4 * Copyright 2009 Analog Devices Inc.
6 * Licensed under the GPL-2 or later.
9 #include <linux/kernel.h>
10 #include <linux/init.h>
11 #include <linux/platform_device.h>
13 #include <linux/backlight.h>
14 #include <linux/mfd/adp5520.h>
15 #include <linux/slab.h>
16 #include <linux/module.h>
19 struct device
*master
;
20 struct adp5520_backlight_platform_data
*pdata
;
22 unsigned long cached_daylight_max
;
24 int current_brightness
;
27 static int adp5520_bl_set(struct backlight_device
*bl
, int brightness
)
29 struct adp5520_bl
*data
= bl_get_data(bl
);
30 struct device
*master
= data
->master
;
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
,
38 ret
|= adp5520_write(master
, ADP5520_DAYLIGHT_MAX
,
42 * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
43 * restore daylight l3 sysfs brightness
45 ret
|= adp5520_write(master
, ADP5520_DAYLIGHT_MAX
,
46 data
->cached_daylight_max
);
47 ret
|= adp5520_set_bits(master
, ADP5520_BL_CONTROL
,
51 ret
|= adp5520_write(master
, ADP5520_DAYLIGHT_MAX
, brightness
);
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
);
62 data
->current_brightness
= brightness
;
67 static int adp5520_bl_update_status(struct backlight_device
*bl
)
69 int brightness
= bl
->props
.brightness
;
71 if (bl
->props
.power
!= FB_BLANK_UNBLANK
)
74 if (bl
->props
.fb_blank
!= FB_BLANK_UNBLANK
)
77 return adp5520_bl_set(bl
, brightness
);
80 static int adp5520_bl_get_brightness(struct backlight_device
*bl
)
82 struct adp5520_bl
*data
= bl_get_data(bl
);
86 error
= adp5520_read(data
->master
, ADP5520_BL_VALUE
, ®_val
);
88 return error
? data
->current_brightness
: reg_val
;
91 static const struct backlight_ops adp5520_bl_ops
= {
92 .update_status
= adp5520_bl_update_status
,
93 .get_brightness
= adp5520_bl_get_brightness
,
96 static int adp5520_bl_setup(struct backlight_device
*bl
)
98 struct adp5520_bl
*data
= bl_get_data(bl
);
99 struct device
*master
= data
->master
;
100 struct adp5520_backlight_platform_data
*pdata
= data
->pdata
;
103 ret
|= adp5520_write(master
, ADP5520_DAYLIGHT_MAX
,
104 pdata
->l1_daylight_max
);
105 ret
|= adp5520_write(master
, ADP5520_DAYLIGHT_DIM
,
106 pdata
->l1_daylight_dim
);
108 if (pdata
->en_ambl_sens
) {
109 data
->cached_daylight_max
= pdata
->l1_daylight_max
;
110 ret
|= adp5520_write(master
, ADP5520_OFFICE_MAX
,
111 pdata
->l2_office_max
);
112 ret
|= adp5520_write(master
, ADP5520_OFFICE_DIM
,
113 pdata
->l2_office_dim
);
114 ret
|= adp5520_write(master
, ADP5520_DARK_MAX
,
116 ret
|= adp5520_write(master
, ADP5520_DARK_DIM
,
118 ret
|= adp5520_write(master
, ADP5520_L2_TRIP
,
120 ret
|= adp5520_write(master
, ADP5520_L2_HYS
,
122 ret
|= adp5520_write(master
, ADP5520_L3_TRIP
,
124 ret
|= adp5520_write(master
, ADP5520_L3_HYS
,
126 ret
|= adp5520_write(master
, ADP5520_ALS_CMPR_CFG
,
127 ALS_CMPR_CFG_VAL(pdata
->abml_filt
,
131 ret
|= adp5520_write(master
, ADP5520_BL_CONTROL
,
132 BL_CTRL_VAL(pdata
->fade_led_law
,
133 pdata
->en_ambl_sens
));
135 ret
|= adp5520_write(master
, ADP5520_BL_FADE
, FADE_VAL(pdata
->fade_in
,
138 ret
|= adp5520_set_bits(master
, ADP5520_MODE_STATUS
,
139 ADP5520_BL_EN
| ADP5520_DIM_EN
);
144 static ssize_t
adp5520_show(struct device
*dev
, char *buf
, int reg
)
146 struct adp5520_bl
*data
= dev_get_drvdata(dev
);
150 mutex_lock(&data
->lock
);
151 ret
= adp5520_read(data
->master
, reg
, ®_val
);
152 mutex_unlock(&data
->lock
);
157 return sprintf(buf
, "%u\n", reg_val
);
160 static ssize_t
adp5520_store(struct device
*dev
, const char *buf
,
161 size_t count
, int reg
)
163 struct adp5520_bl
*data
= dev_get_drvdata(dev
);
167 ret
= kstrtoul(buf
, 10, &val
);
171 mutex_lock(&data
->lock
);
172 adp5520_write(data
->master
, reg
, val
);
173 mutex_unlock(&data
->lock
);
178 static ssize_t
adp5520_bl_dark_max_show(struct device
*dev
,
179 struct device_attribute
*attr
, char *buf
)
181 return adp5520_show(dev
, buf
, ADP5520_DARK_MAX
);
184 static ssize_t
adp5520_bl_dark_max_store(struct device
*dev
,
185 struct device_attribute
*attr
,
186 const char *buf
, size_t count
)
188 return adp5520_store(dev
, buf
, count
, ADP5520_DARK_MAX
);
190 static DEVICE_ATTR(dark_max
, 0664, adp5520_bl_dark_max_show
,
191 adp5520_bl_dark_max_store
);
193 static ssize_t
adp5520_bl_office_max_show(struct device
*dev
,
194 struct device_attribute
*attr
, char *buf
)
196 return adp5520_show(dev
, buf
, ADP5520_OFFICE_MAX
);
199 static ssize_t
adp5520_bl_office_max_store(struct device
*dev
,
200 struct device_attribute
*attr
,
201 const char *buf
, size_t count
)
203 return adp5520_store(dev
, buf
, count
, ADP5520_OFFICE_MAX
);
205 static DEVICE_ATTR(office_max
, 0664, adp5520_bl_office_max_show
,
206 adp5520_bl_office_max_store
);
208 static ssize_t
adp5520_bl_daylight_max_show(struct device
*dev
,
209 struct device_attribute
*attr
, char *buf
)
211 return adp5520_show(dev
, buf
, ADP5520_DAYLIGHT_MAX
);
214 static ssize_t
adp5520_bl_daylight_max_store(struct device
*dev
,
215 struct device_attribute
*attr
,
216 const char *buf
, size_t count
)
218 struct adp5520_bl
*data
= dev_get_drvdata(dev
);
221 ret
= kstrtoul(buf
, 10, &data
->cached_daylight_max
);
225 return adp5520_store(dev
, buf
, count
, ADP5520_DAYLIGHT_MAX
);
227 static DEVICE_ATTR(daylight_max
, 0664, adp5520_bl_daylight_max_show
,
228 adp5520_bl_daylight_max_store
);
230 static ssize_t
adp5520_bl_dark_dim_show(struct device
*dev
,
231 struct device_attribute
*attr
, char *buf
)
233 return adp5520_show(dev
, buf
, ADP5520_DARK_DIM
);
236 static ssize_t
adp5520_bl_dark_dim_store(struct device
*dev
,
237 struct device_attribute
*attr
,
238 const char *buf
, size_t count
)
240 return adp5520_store(dev
, buf
, count
, ADP5520_DARK_DIM
);
242 static DEVICE_ATTR(dark_dim
, 0664, adp5520_bl_dark_dim_show
,
243 adp5520_bl_dark_dim_store
);
245 static ssize_t
adp5520_bl_office_dim_show(struct device
*dev
,
246 struct device_attribute
*attr
, char *buf
)
248 return adp5520_show(dev
, buf
, ADP5520_OFFICE_DIM
);
251 static ssize_t
adp5520_bl_office_dim_store(struct device
*dev
,
252 struct device_attribute
*attr
,
253 const char *buf
, size_t count
)
255 return adp5520_store(dev
, buf
, count
, ADP5520_OFFICE_DIM
);
257 static DEVICE_ATTR(office_dim
, 0664, adp5520_bl_office_dim_show
,
258 adp5520_bl_office_dim_store
);
260 static ssize_t
adp5520_bl_daylight_dim_show(struct device
*dev
,
261 struct device_attribute
*attr
, char *buf
)
263 return adp5520_show(dev
, buf
, ADP5520_DAYLIGHT_DIM
);
266 static ssize_t
adp5520_bl_daylight_dim_store(struct device
*dev
,
267 struct device_attribute
*attr
,
268 const char *buf
, size_t count
)
270 return adp5520_store(dev
, buf
, count
, ADP5520_DAYLIGHT_DIM
);
272 static DEVICE_ATTR(daylight_dim
, 0664, adp5520_bl_daylight_dim_show
,
273 adp5520_bl_daylight_dim_store
);
275 static struct attribute
*adp5520_bl_attributes
[] = {
276 &dev_attr_dark_max
.attr
,
277 &dev_attr_dark_dim
.attr
,
278 &dev_attr_office_max
.attr
,
279 &dev_attr_office_dim
.attr
,
280 &dev_attr_daylight_max
.attr
,
281 &dev_attr_daylight_dim
.attr
,
285 static const struct attribute_group adp5520_bl_attr_group
= {
286 .attrs
= adp5520_bl_attributes
,
289 static int adp5520_bl_probe(struct platform_device
*pdev
)
291 struct backlight_properties props
;
292 struct backlight_device
*bl
;
293 struct adp5520_bl
*data
;
296 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
300 data
->master
= pdev
->dev
.parent
;
301 data
->pdata
= dev_get_platdata(&pdev
->dev
);
303 if (data
->pdata
== NULL
) {
304 dev_err(&pdev
->dev
, "missing platform data\n");
309 data
->current_brightness
= 0;
311 mutex_init(&data
->lock
);
313 memset(&props
, 0, sizeof(struct backlight_properties
));
314 props
.type
= BACKLIGHT_RAW
;
315 props
.max_brightness
= ADP5020_MAX_BRIGHTNESS
;
316 bl
= devm_backlight_device_register(&pdev
->dev
, pdev
->name
,
317 data
->master
, data
, &adp5520_bl_ops
,
320 dev_err(&pdev
->dev
, "failed to register backlight\n");
324 bl
->props
.brightness
= ADP5020_MAX_BRIGHTNESS
;
325 if (data
->pdata
->en_ambl_sens
)
326 ret
= sysfs_create_group(&bl
->dev
.kobj
,
327 &adp5520_bl_attr_group
);
330 dev_err(&pdev
->dev
, "failed to register sysfs\n");
334 platform_set_drvdata(pdev
, bl
);
335 ret
= adp5520_bl_setup(bl
);
337 dev_err(&pdev
->dev
, "failed to setup\n");
338 if (data
->pdata
->en_ambl_sens
)
339 sysfs_remove_group(&bl
->dev
.kobj
,
340 &adp5520_bl_attr_group
);
344 backlight_update_status(bl
);
349 static int adp5520_bl_remove(struct platform_device
*pdev
)
351 struct backlight_device
*bl
= platform_get_drvdata(pdev
);
352 struct adp5520_bl
*data
= bl_get_data(bl
);
354 adp5520_clr_bits(data
->master
, ADP5520_MODE_STATUS
, ADP5520_BL_EN
);
356 if (data
->pdata
->en_ambl_sens
)
357 sysfs_remove_group(&bl
->dev
.kobj
,
358 &adp5520_bl_attr_group
);
363 #ifdef CONFIG_PM_SLEEP
364 static int adp5520_bl_suspend(struct device
*dev
)
366 struct backlight_device
*bl
= dev_get_drvdata(dev
);
368 return adp5520_bl_set(bl
, 0);
371 static int adp5520_bl_resume(struct device
*dev
)
373 struct backlight_device
*bl
= dev_get_drvdata(dev
);
375 backlight_update_status(bl
);
380 static SIMPLE_DEV_PM_OPS(adp5520_bl_pm_ops
, adp5520_bl_suspend
,
383 static struct platform_driver adp5520_bl_driver
= {
385 .name
= "adp5520-backlight",
386 .pm
= &adp5520_bl_pm_ops
,
388 .probe
= adp5520_bl_probe
,
389 .remove
= adp5520_bl_remove
,
392 module_platform_driver(adp5520_bl_driver
);
394 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
395 MODULE_DESCRIPTION("ADP5520(01) Backlight Driver");
396 MODULE_LICENSE("GPL");
397 MODULE_ALIAS("platform:adp5520-backlight");