modify early_suspend of wm831x backlight
[firefly-linux-kernel-4.4.55.git] / drivers / video / backlight / wm831x_bl.c
1 /*
2  * Backlight driver for Wolfson Microelectronics WM831x PMICs
3  *
4  * Copyright 2009 Wolfson Microelectonics plc
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/platform_device.h>
14 #include <linux/fb.h>
15 #include <linux/backlight.h>
16 #include <linux/slab.h>
17
18 #include <linux/mfd/wm831x/core.h>
19 #include <linux/mfd/wm831x/pdata.h>
20 #include <linux/mfd/wm831x/regulator.h>
21 #ifdef CONFIG_HAS_EARLYSUSPEND
22 #include <linux/earlysuspend.h>
23 #endif
24 #include <linux/delay.h>
25 #include <linux/ktime.h>
26 #define BL_SET   255
27 struct wm831x_backlight_data {
28         struct wm831x *wm831x;
29         int isink_reg;
30         int current_brightness;
31 #ifdef CONFIG_HAS_EARLYSUSPEND
32         struct  early_suspend early_suspend;
33         struct delayed_work work;
34         int suspend_flag;
35 #endif
36 };
37 #define TS_POLL_DELAY (10000*1000*1000)
38 int wm831x_bright = 0;
39 int max_tp = 0;
40 #ifdef CONFIG_HAS_EARLYSUSPEND
41 static struct backlight_device *gwm831x_bl;
42 static struct wm831x_backlight_data *gwm831x_data;
43 #endif
44 static int wm831x_backlight_set(struct backlight_device *bl, int brightness)
45 {
46         struct wm831x_backlight_data *data = bl_get_data(bl);
47         struct wm831x *wm831x = data->wm831x;
48 //      int power_up = !data->current_brightness && brightness;
49 //      int power_down = data->current_brightness && !brightness;
50         int power_up;
51         int power_down;
52         int ret;
53         int bright_tp;
54
55         bright_tp =( max_tp*brightness)/BL_SET;
56         power_up =!data->current_brightness && bright_tp;
57         power_down = data->current_brightness && !bright_tp;
58         if (power_up) {
59                 /* Enable the ISINK */
60                 ret = wm831x_set_bits(wm831x, data->isink_reg,
61                                       WM831X_CS1_ENA, WM831X_CS1_ENA);
62                 if (ret < 0)
63                         goto err;
64
65                 /* Enable the DC-DC */
66                 ret = wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE,
67                                       WM831X_DC4_ENA, WM831X_DC4_ENA);
68                 if (ret < 0)
69                         goto err;
70         }
71
72         if (power_down) {
73                 /* DCDC first */
74                 ret = wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE,
75                                       WM831X_DC4_ENA, 0);
76                 if (ret < 0)
77                         goto err;
78
79                 /* ISINK */
80                 ret = wm831x_set_bits(wm831x, data->isink_reg,
81                                       WM831X_CS1_DRIVE | WM831X_CS1_ENA, 0);
82                 if (ret < 0)
83                         goto err;
84         }
85
86         /* Set the new brightness */
87         ret = wm831x_set_bits(wm831x, data->isink_reg,
88                               WM831X_CS1_ISEL_MASK, bright_tp);
89         if (ret < 0)
90                 goto err;
91
92         if (power_up) {
93                 /* Drive current through the ISINK */
94                 ret = wm831x_set_bits(wm831x, data->isink_reg,
95                                       WM831X_CS1_DRIVE, WM831X_CS1_DRIVE);
96                 if (ret < 0)
97                         return ret;
98         }
99
100         data->current_brightness = brightness;
101
102         return 0;
103
104 err:
105         /* If we were in the middle of a power transition always shut down
106          * for safety.
107          */
108         if (power_up || power_down) {
109                 wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0);
110                 wm831x_set_bits(wm831x, data->isink_reg, WM831X_CS1_ENA, 0);
111         }
112
113         return ret;
114 }
115
116 static int wm831x_backlight_update_status(struct backlight_device *bl)
117 {
118         int brightness = bl->props.brightness;
119
120         if(gwm831x_data->suspend_flag == 1)
121                 brightness = 0;
122                 
123         if (bl->props.power != FB_BLANK_UNBLANK)
124                 brightness = 0;
125
126         if (bl->props.fb_blank != FB_BLANK_UNBLANK)
127                 brightness = 0;
128
129         if (bl->props.state & BL_CORE_SUSPENDED)
130                 brightness = 0;
131
132         printk("backlight brightness=%d\n", brightness);
133
134         return wm831x_backlight_set(bl, brightness);
135 }
136
137 static int wm831x_backlight_get_brightness(struct backlight_device *bl)
138 {
139         struct wm831x_backlight_data *data = bl_get_data(bl);
140         return data->current_brightness;
141 }
142
143 static struct backlight_ops wm831x_backlight_ops = {
144         .options = BL_CORE_SUSPENDRESUME,
145         .update_status  = wm831x_backlight_update_status,
146         .get_brightness = wm831x_backlight_get_brightness,
147 };
148 #ifdef CONFIG_HAS_EARLYSUSPEND
149 static void wm831x_bl_work(struct work_struct *work)
150 {
151         //struct wm831x_backlight_data *wm831x_data = container_of(work, struct wm831x_backlight_data,
152                                                    //work.work);
153         backlight_update_status(gwm831x_bl);
154 }
155
156 static void wm831x_bl_suspend(struct early_suspend *h)
157 {
158         struct wm831x_backlight_data *wm831x_data;
159         wm831x_data = container_of(h, struct wm831x_backlight_data, early_suspend);
160         wm831x_data->suspend_flag = 1;
161
162         schedule_delayed_work(&wm831x_data->work, msecs_to_jiffies(100));               
163 }
164
165
166 static void wm831x_bl_resume(struct early_suspend *h)
167 {
168         struct wm831x_backlight_data *wm831x_data;
169         wm831x_data = container_of(h, struct wm831x_backlight_data, early_suspend);
170         wm831x_data->suspend_flag = 0;
171         
172         schedule_delayed_work(&wm831x_data->work, msecs_to_jiffies(100));
173 }
174
175 #endif
176 static int wm831x_backlight_probe(struct platform_device *pdev)
177 {
178         struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
179         struct wm831x_pdata *wm831x_pdata;
180         struct wm831x_backlight_pdata *pdata;
181         struct wm831x_backlight_data *data;
182         struct backlight_device *bl;
183         struct backlight_properties props;
184         int ret, i, max_isel, isink_reg, dcdc_cfg;
185
186         /* We need platform data */
187         if (pdev->dev.parent->platform_data) {
188                 wm831x_pdata = pdev->dev.parent->platform_data;
189                 pdata = wm831x_pdata->backlight;
190         } else {
191                 pdata = NULL;
192         }
193
194         if (!pdata) {
195                 dev_err(&pdev->dev, "No platform data supplied\n");
196                 return -EINVAL;
197         }
198
199         /* Figure out the maximum current we can use */
200         for (i = 0; i < WM831X_ISINK_MAX_ISEL; i++) {
201                 if (wm831x_isinkv_values[i] > pdata->max_uA)
202                         break;
203         }
204
205         if (i == 0) {
206                 dev_err(&pdev->dev, "Invalid max_uA: %duA\n", pdata->max_uA);
207                 return -EINVAL;
208         }
209         max_isel = i - 1;
210         max_tp = max_isel;
211         if (pdata->max_uA != wm831x_isinkv_values[max_isel])
212                 dev_warn(&pdev->dev,
213                          "Maximum current is %duA not %duA as requested\n",
214                          wm831x_isinkv_values[max_isel], pdata->max_uA);
215
216         switch (pdata->isink) {
217         case 1:
218                 isink_reg = WM831X_CURRENT_SINK_1;
219                 dcdc_cfg = 0;
220                 break;
221         case 2:
222                 isink_reg = WM831X_CURRENT_SINK_2;
223                 dcdc_cfg = WM831X_DC4_FBSRC;
224                 break;
225         default:
226                 dev_err(&pdev->dev, "Invalid ISINK %d\n", pdata->isink);
227                 return -EINVAL;
228         }
229
230         /* Configure the ISINK to use for feedback */
231         ret = wm831x_reg_unlock(wm831x);
232         if (ret < 0)
233                 return ret;
234
235         ret = wm831x_set_bits(wm831x, WM831X_DC4_CONTROL, WM831X_DC4_FBSRC,
236                               dcdc_cfg);
237
238         wm831x_reg_lock(wm831x);
239         if (ret < 0)
240                 return ret;
241
242         data = kzalloc(sizeof(*data), GFP_KERNEL);
243         if (data == NULL)
244                 return -ENOMEM;
245
246         data->wm831x = wm831x;
247         data->current_brightness = 0;
248         data->isink_reg = isink_reg;
249
250         props.max_brightness = max_isel;
251         bl = backlight_device_register("wm831x", &pdev->dev, data,
252                                        &wm831x_backlight_ops);
253         if (IS_ERR(bl)) {
254                 dev_err(&pdev->dev, "failed to register backlight\n");
255                 kfree(data);
256                 return PTR_ERR(bl);
257         }
258
259         bl->props.brightness = BL_SET;
260         bl->props.max_brightness= BL_SET;
261
262         platform_set_drvdata(pdev, bl);
263
264 #ifdef CONFIG_HAS_EARLYSUSPEND  
265         data->early_suspend.level = ~0x0;
266         data->early_suspend.suspend = wm831x_bl_suspend;
267         data->early_suspend.resume = wm831x_bl_resume;
268         register_early_suspend(&data->early_suspend);
269         INIT_DELAYED_WORK(&data->work, wm831x_bl_work);
270         gwm831x_bl = bl;
271         gwm831x_data = data;
272 #endif
273
274
275         /* Disable the DCDC if it was started so we can bootstrap */
276         wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0);
277
278         //backlight_update_status(bl);
279         schedule_delayed_work(&data->work, msecs_to_jiffies(100));
280
281         return 0;
282 }
283
284 static int wm831x_backlight_remove(struct platform_device *pdev)
285 {
286         struct backlight_device *bl = platform_get_drvdata(pdev);
287         struct wm831x_backlight_data *data = bl_get_data(bl);
288
289         backlight_device_unregister(bl);
290 #ifdef CONFIG_HAS_EARLYSUSPEND
291         unregister_early_suspend(&data->early_suspend);
292 #endif 
293         kfree(data);
294         return 0;
295 }
296
297 static struct platform_driver wm831x_backlight_driver = {
298         .driver         = {
299                 .name   = "wm831x-backlight",
300                 .owner  = THIS_MODULE,
301         },
302         .probe          = wm831x_backlight_probe,
303         .remove         = wm831x_backlight_remove,
304 };
305
306 static int __init wm831x_backlight_init(void)
307 {
308         return platform_driver_register(&wm831x_backlight_driver);
309 }
310 module_init(wm831x_backlight_init);
311
312 static void __exit wm831x_backlight_exit(void)
313 {
314         platform_driver_unregister(&wm831x_backlight_driver);
315 }
316 module_exit(wm831x_backlight_exit);
317
318 MODULE_DESCRIPTION("Backlight Driver for WM831x PMICs");
319 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com");
320 MODULE_LICENSE("GPL");
321 MODULE_ALIAS("platform:wm831x-backlight");