]> bbs.cooldavid.org Git - net-next-2.6.git/blob - drivers/video/backlight/adp5520_bl.c
5183f0e4d3145986d0d68998aa752c240bf3fd5e
[net-next-2.6.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_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         int brightness = bl->props.brightness;
68         if (bl->props.power != FB_BLANK_UNBLANK)
69                 brightness = 0;
70
71         if (bl->props.fb_blank != FB_BLANK_UNBLANK)
72                 brightness = 0;
73
74         return adp5520_bl_set(bl, brightness);
75 }
76
77 static int adp5520_bl_get_brightness(struct backlight_device *bl)
78 {
79         struct adp5520_bl *data = bl_get_data(bl);
80         int error;
81         uint8_t reg_val;
82
83         error = adp5520_read(data->master, ADP5520_BL_VALUE, &reg_val);
84
85         return error ? data->current_brightness : reg_val;
86 }
87
88 static const struct backlight_ops adp5520_bl_ops = {
89         .update_status  = adp5520_bl_update_status,
90         .get_brightness = adp5520_bl_get_brightness,
91 };
92
93 static int adp5520_bl_setup(struct backlight_device *bl)
94 {
95         struct adp5520_bl *data = bl_get_data(bl);
96         struct device *master = data->master;
97         struct adp5520_backlight_platform_data *pdata = data->pdata;
98         int ret = 0;
99
100         ret |= adp5520_write(master, ADP5520_DAYLIGHT_MAX,
101                                 pdata->l1_daylight_max);
102         ret |= adp5520_write(master, ADP5520_DAYLIGHT_DIM,
103                                 pdata->l1_daylight_dim);
104
105         if (pdata->en_ambl_sens) {
106                 data->cached_daylight_max = pdata->l1_daylight_max;
107                 ret |= adp5520_write(master, ADP5520_OFFICE_MAX,
108                                 pdata->l2_office_max);
109                 ret |= adp5520_write(master, ADP5520_OFFICE_DIM,
110                                 pdata->l2_office_dim);
111                 ret |= adp5520_write(master, ADP5520_DARK_MAX,
112                                 pdata->l3_dark_max);
113                 ret |= adp5520_write(master, ADP5520_DARK_DIM,
114                                 pdata->l3_dark_dim);
115                 ret |= adp5520_write(master, ADP5520_L2_TRIP,
116                                 pdata->l2_trip);
117                 ret |= adp5520_write(master, ADP5520_L2_HYS,
118                                 pdata->l2_hyst);
119                 ret |= adp5520_write(master, ADP5520_L3_TRIP,
120                                  pdata->l3_trip);
121                 ret |= adp5520_write(master, ADP5520_L3_HYS,
122                                 pdata->l3_hyst);
123                 ret |= adp5520_write(master, ADP5520_ALS_CMPR_CFG,
124                                 ALS_CMPR_CFG_VAL(pdata->abml_filt,
125                                 ADP5520_L3_EN));
126         }
127
128         ret |= adp5520_write(master, ADP5520_BL_CONTROL,
129                         BL_CTRL_VAL(pdata->fade_led_law,
130                                         pdata->en_ambl_sens));
131
132         ret |= adp5520_write(master, ADP5520_BL_FADE, FADE_VAL(pdata->fade_in,
133                         pdata->fade_out));
134
135         ret |= adp5520_set_bits(master, ADP5520_MODE_STATUS,
136                         ADP5520_BL_EN | ADP5520_DIM_EN);
137
138         return ret;
139 }
140
141 static ssize_t adp5520_show(struct device *dev, char *buf, int reg)
142 {
143         struct adp5520_bl *data = dev_get_drvdata(dev);
144         int error;
145         uint8_t reg_val;
146
147         mutex_lock(&data->lock);
148         error = adp5520_read(data->master, reg, &reg_val);
149         mutex_unlock(&data->lock);
150
151         return sprintf(buf, "%u\n", reg_val);
152 }
153
154 static ssize_t adp5520_store(struct device *dev, const char *buf,
155                          size_t count, int reg)
156 {
157         struct adp5520_bl *data = dev_get_drvdata(dev);
158         unsigned long val;
159         int ret;
160
161         ret = strict_strtoul(buf, 10, &val);
162         if (ret)
163                 return ret;
164
165         mutex_lock(&data->lock);
166         adp5520_write(data->master, reg, val);
167         mutex_unlock(&data->lock);
168
169         return count;
170 }
171
172 static ssize_t adp5520_bl_dark_max_show(struct device *dev,
173                         struct device_attribute *attr, char *buf)
174 {
175         return adp5520_show(dev, buf, ADP5520_DARK_MAX);
176 }
177
178 static ssize_t adp5520_bl_dark_max_store(struct device *dev,
179                         struct device_attribute *attr,
180                         const char *buf, size_t count)
181 {
182         return adp5520_store(dev, buf, count, ADP5520_DARK_MAX);
183 }
184 static DEVICE_ATTR(dark_max, 0664, adp5520_bl_dark_max_show,
185                         adp5520_bl_dark_max_store);
186
187 static ssize_t adp5520_bl_office_max_show(struct device *dev,
188                         struct device_attribute *attr, char *buf)
189 {
190         return adp5520_show(dev, buf, ADP5520_OFFICE_MAX);
191 }
192
193 static ssize_t adp5520_bl_office_max_store(struct device *dev,
194                         struct device_attribute *attr,
195                         const char *buf, size_t count)
196 {
197         return adp5520_store(dev, buf, count, ADP5520_OFFICE_MAX);
198 }
199 static DEVICE_ATTR(office_max, 0664, adp5520_bl_office_max_show,
200                         adp5520_bl_office_max_store);
201
202 static ssize_t adp5520_bl_daylight_max_show(struct device *dev,
203                         struct device_attribute *attr, char *buf)
204 {
205         return adp5520_show(dev, buf, ADP5520_DAYLIGHT_MAX);
206 }
207
208 static ssize_t adp5520_bl_daylight_max_store(struct device *dev,
209                         struct device_attribute *attr,
210                         const char *buf, size_t count)
211 {
212         struct adp5520_bl *data = dev_get_drvdata(dev);
213
214         strict_strtoul(buf, 10, &data->cached_daylight_max);
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 __devinit 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 = kzalloc(sizeof(*data), GFP_KERNEL);
287         if (data == NULL)
288                 return -ENOMEM;
289
290         data->master = pdev->dev.parent;
291         data->pdata = pdev->dev.platform_data;
292
293         if (data->pdata  == NULL) {
294                 dev_err(&pdev->dev, "missing platform data\n");
295                 kfree(data);
296                 return -ENODEV;
297         }
298
299         data->id = pdev->id;
300         data->current_brightness = 0;
301
302         mutex_init(&data->lock);
303
304         memset(&props, 0, sizeof(struct backlight_properties));
305         props.max_brightness = ADP5020_MAX_BRIGHTNESS;
306         bl = backlight_device_register(pdev->name, data->master, data,
307                                        &adp5520_bl_ops, &props);
308         if (IS_ERR(bl)) {
309                 dev_err(&pdev->dev, "failed to register backlight\n");
310                 kfree(data);
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                 backlight_device_unregister(bl);
322                 kfree(data);
323         }
324
325         platform_set_drvdata(pdev, bl);
326         ret |= adp5520_bl_setup(bl);
327         backlight_update_status(bl);
328
329         return ret;
330 }
331
332 static int __devexit adp5520_bl_remove(struct platform_device *pdev)
333 {
334         struct backlight_device *bl = platform_get_drvdata(pdev);
335         struct adp5520_bl *data = bl_get_data(bl);
336
337         adp5520_clr_bits(data->master, ADP5520_MODE_STATUS, ADP5520_BL_EN);
338
339         if (data->pdata->en_ambl_sens)
340                 sysfs_remove_group(&bl->dev.kobj,
341                                 &adp5520_bl_attr_group);
342
343         backlight_device_unregister(bl);
344         kfree(data);
345
346         return 0;
347 }
348
349 #ifdef CONFIG_PM
350 static int adp5520_bl_suspend(struct platform_device *pdev,
351                                  pm_message_t state)
352 {
353         struct backlight_device *bl = platform_get_drvdata(pdev);
354         return adp5520_bl_set(bl, 0);
355 }
356
357 static int adp5520_bl_resume(struct platform_device *pdev)
358 {
359         struct backlight_device *bl = platform_get_drvdata(pdev);
360
361         backlight_update_status(bl);
362         return 0;
363 }
364 #else
365 #define adp5520_bl_suspend      NULL
366 #define adp5520_bl_resume       NULL
367 #endif
368
369 static struct platform_driver adp5520_bl_driver = {
370         .driver         = {
371                 .name   = "adp5520-backlight",
372                 .owner  = THIS_MODULE,
373         },
374         .probe          = adp5520_bl_probe,
375         .remove         = __devexit_p(adp5520_bl_remove),
376         .suspend        = adp5520_bl_suspend,
377         .resume         = adp5520_bl_resume,
378 };
379
380 static int __init adp5520_bl_init(void)
381 {
382         return platform_driver_register(&adp5520_bl_driver);
383 }
384 module_init(adp5520_bl_init);
385
386 static void __exit adp5520_bl_exit(void)
387 {
388         platform_driver_unregister(&adp5520_bl_driver);
389 }
390 module_exit(adp5520_bl_exit);
391
392 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
393 MODULE_DESCRIPTION("ADP5520(01) Backlight Driver");
394 MODULE_LICENSE("GPL");
395 MODULE_ALIAS("platform:adp5520-backlight");