OSDN Git Service

backlight: new driver for ADP5520/ADP5501 MFD PMICs
[uclinux-h8/linux.git] / 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");