backlight:fix hdmi switch black screen
[firefly-linux-kernel-4.4.55.git] / drivers / video / backlight / rk29_backlight.c
1 /* drivers/video/backlight/rk29_backlight.c
2  *
3  * Copyright (C) 2009-2011 Rockchip Corporation.
4  *
5  * This software is licensed under the terms of the GNU General Public
6  * License version 2, as published by the Free Software Foundation, and
7  * may be copied, distributed, and modified under those terms.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  */
15
16 #include <linux/init.h>
17 #include <linux/module.h>
18 #include <linux/kernel.h>
19 #include <linux/err.h>
20 #include <linux/delay.h>
21 #include <linux/platform_device.h>
22 #include <linux/backlight.h>
23 #include <linux/fb.h>
24 #include <linux/clk.h>
25
26 #include <linux/earlysuspend.h>
27 #include <asm/io.h>
28 #include <mach/board.h>
29 #include <plat/pwm.h>
30
31 #define PWM_DIV              PWM_DIV2
32 #define PWM_APB_PRE_DIV      1000
33 #define BL_STEP              255
34
35 /*
36  * Debug
37  */
38 #if 0
39 #define DBG(x...)       printk(KERN_INFO x)
40 #else
41 #define DBG(x...)
42 #endif
43
44 #define read_pwm_reg(addr)              __raw_readl(pwm_base + addr)
45
46 static struct clk *pwm_clk;
47 static void __iomem *pwm_base;
48 static struct backlight_device *rk29_bl;
49 static int suspend_flag = 0;
50
51
52 int convertint(const char s[])  
53 {  
54     int i;  
55     int n = 0;  
56     for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)  
57     {  
58         n = 10 * n + (s[i] - '0');  
59     }  
60     return n;  
61
62
63 static ssize_t backlight_write(struct device *dev, 
64                 struct device_attribute *attr,const char *buf, size_t count)
65 {
66    
67         struct rk29_bl_info *rk29_bl_info = bl_get_data(rk29_bl);
68         int number;
69
70         number = convertint(buf);
71         
72         rk29_bl_info->min_brightness=number;
73         return 0;
74 }
75
76
77 static ssize_t backlight_read(struct device *dev,
78                 struct device_attribute *attr, char *buf)
79 {
80         DBG("rk29_bl_info->min_brightness=%d\n",rk29_bl_info->min_brightness);
81         return 0;
82 }
83 static DEVICE_ATTR(rk29backlight, 0660, backlight_read, backlight_write);
84
85 static DEFINE_MUTEX(backlight_mutex);
86
87 static int rk29_bl_update_status(struct backlight_device *bl)
88 {
89         u32 divh,div_total;
90         struct rk29_bl_info *rk29_bl_info = bl_get_data(bl);
91         u32 id = rk29_bl_info->pwm_id;
92         u32 ref = rk29_bl_info->bl_ref;
93         int brightness = 0;
94         
95         mutex_lock(&backlight_mutex);
96 //BL_CORE_DRIVER2 is the flag if backlight is into early_suspend.
97         if (suspend_flag && (bl->props.state & BL_CORE_DRIVER2))
98             goto out;
99
100         brightness = bl->props.brightness;
101         if(rk29_bl_info->min_brightness){
102             if(brightness){
103                 brightness = brightness*(256 - rk29_bl_info->min_brightness);
104                 brightness = (brightness>>8) + rk29_bl_info->min_brightness;
105             }
106             if(brightness > 255)
107                 brightness = 255;
108         }
109
110         if (bl->props.power != FB_BLANK_UNBLANK)
111                 brightness = 0;
112
113         if (bl->props.fb_blank != FB_BLANK_UNBLANK)
114                 brightness = 0; 
115
116         if (bl->props.state & BL_CORE_DRIVER3)
117                 brightness = 0; 
118
119         if ((bl->props.state & BL_CORE_DRIVER2) && !suspend_flag ){
120                 brightness = 0;
121                 suspend_flag = 1;
122         }else if(!(bl->props.state & BL_CORE_DRIVER2) && suspend_flag ){
123                 suspend_flag = 0;
124         }
125         div_total = read_pwm_reg(PWM_REG_LRC);
126         if (ref) {
127                 divh = div_total*brightness/BL_STEP;
128         } else {
129                 divh = div_total*(BL_STEP-brightness)/BL_STEP;
130         }
131         rk_pwm_setup(id, PWM_DIV, divh, div_total);
132
133 //BL_CORE_DRIVER1 is the flag if backlight pwm is closed.
134         if ((bl->props.state & BL_CORE_DRIVER1) && brightness ==0 ){  
135                 bl->props.state &= ~BL_CORE_DRIVER1;
136                 clk_disable(pwm_clk);
137                 if (rk29_bl_info->pwm_suspend)
138                         rk29_bl_info->pwm_suspend();
139         }else if(!(bl->props.state & BL_CORE_DRIVER1) && brightness != 0){
140                 bl->props.state |= BL_CORE_DRIVER1;
141                 if (rk29_bl_info->pwm_resume)
142                         rk29_bl_info->pwm_resume();
143                 clk_enable(pwm_clk);
144                 msleep(1);
145                 div_total = read_pwm_reg(PWM_REG_LRC);
146                 if (ref) {
147                         divh = div_total*brightness/BL_STEP;
148                 } else {
149                         divh = div_total*(BL_STEP-brightness)/BL_STEP;
150                 }
151                 rk_pwm_setup(id, PWM_DIV, divh, div_total);
152         }
153
154
155
156         DBG("%s:line=%d,brightness = %d, div_total = %d, divh = %d state=%x \n",__FUNCTION__,__LINE__,brightness, div_total, divh,bl->props.state);
157 out:
158         mutex_unlock(&backlight_mutex);
159         return 0;
160 }
161
162 static int rk29_bl_get_brightness(struct backlight_device *bl)
163 {
164         u32 divh,div_total;
165         struct rk29_bl_info *rk29_bl_info = bl_get_data(bl);
166         u32 ref = rk29_bl_info->bl_ref;
167
168         div_total = read_pwm_reg(PWM_REG_LRC);
169         divh = read_pwm_reg(PWM_REG_HRC);
170
171         if (!div_total)
172                 return 0;
173
174         if (ref) {
175                 return BL_STEP*divh/div_total;
176         } else {
177                 return BL_STEP-(BL_STEP*divh/div_total);
178         }
179 }
180
181 static struct backlight_ops rk29_bl_ops = {
182         .update_status  = rk29_bl_update_status,
183         .get_brightness = rk29_bl_get_brightness,
184 };
185
186 static void rk29_backlight_work_func(struct work_struct *work)
187 {
188         rk29_bl_update_status(rk29_bl);
189 }
190 static DECLARE_DELAYED_WORK(rk29_backlight_work, rk29_backlight_work_func);
191
192 #ifdef CONFIG_HAS_EARLYSUSPEND
193 static void rk29_bl_suspend(struct early_suspend *h)
194 {
195         int brightness = rk29_bl->props.brightness;
196
197         cancel_delayed_work_sync(&rk29_backlight_work);
198
199         rk29_bl->props.state |= BL_CORE_DRIVER2;
200
201         if (rk29_bl->props.brightness) {
202                 rk29_bl->props.brightness = 0;
203                 rk29_bl_update_status(rk29_bl);
204                 rk29_bl->props.brightness = brightness;
205         }
206
207 }
208
209 static void rk29_bl_resume(struct early_suspend *h)
210 {
211         struct rk29_bl_info *rk29_bl_info = bl_get_data(rk29_bl);
212         DBG("%s : %s\n", __FILE__, __FUNCTION__);
213         rk29_bl->props.state &= ~BL_CORE_DRIVER2;
214         
215         schedule_delayed_work(&rk29_backlight_work, msecs_to_jiffies(rk29_bl_info->delay_ms));
216 }
217
218 static struct early_suspend bl_early_suspend = {
219         .suspend = rk29_bl_suspend,
220         .resume = rk29_bl_resume,
221         .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 1,
222 };
223
224 bool rk29_get_backlight_status(void)
225 {
226         return ((rk29_bl->props.state & BL_CORE_DRIVER3)==BL_CORE_DRIVER3)?true:false;
227 }
228 EXPORT_SYMBOL(rk29_get_backlight_status);
229
230 void rk29_backlight_set(bool on)
231 {
232         printk("%s: set %d\n", __func__, on);
233         if(on){
234                 rk29_bl->props.state &= ~BL_CORE_DRIVER3;
235                 rk29_bl_update_status(rk29_bl);
236         }else{
237                 rk29_bl->props.state |= BL_CORE_DRIVER3;
238                 rk29_bl_update_status(rk29_bl);
239         }
240         return;
241 }
242 EXPORT_SYMBOL(rk29_backlight_set);
243 #endif
244
245 static int rk29_backlight_probe(struct platform_device *pdev)
246 {               
247         int ret = 0;
248         struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
249         u32 id  =  rk29_bl_info->pwm_id;
250         u32 divh, div_total;
251         unsigned long pwm_clk_rate;
252         struct backlight_properties props;
253         int pre_div = PWM_APB_PRE_DIV;
254
255         if (rk29_bl) {
256                 printk(KERN_CRIT "%s: backlight device register has existed \n",
257                                 __func__);
258                 return -EEXIST;         
259         }
260
261         if (!rk29_bl_info->delay_ms)
262                 rk29_bl_info->delay_ms = 100;
263
264         if (rk29_bl_info->min_brightness < 0 || rk29_bl_info->min_brightness > BL_STEP)
265                 rk29_bl_info->min_brightness = 52;
266
267         if (rk29_bl_info && rk29_bl_info->io_init) {
268                 rk29_bl_info->io_init();
269         }
270
271         if(rk29_bl_info->pre_div > 0)
272                 pre_div = rk29_bl_info->pre_div;
273
274         memset(&props, 0, sizeof(struct backlight_properties));
275         props.type = BACKLIGHT_RAW;
276         props.max_brightness = BL_STEP;
277         rk29_bl = backlight_device_register("rk28_bl", &pdev->dev, rk29_bl_info, &rk29_bl_ops, &props);
278         if (!rk29_bl) {
279                 printk(KERN_CRIT "%s: backlight device register error\n",
280                                 __func__);
281                 return -ENODEV;         
282         }
283
284         pwm_base = rk_pwm_get_base(id);
285         pwm_clk = rk_pwm_get_clk(id);
286         if (IS_ERR(pwm_clk) || !pwm_clk) {
287                 printk(KERN_ERR "failed to get pwm clock source\n");
288                 return -ENODEV;
289         }
290         pwm_clk_rate = clk_get_rate(pwm_clk);
291         div_total = pwm_clk_rate / pre_div;
292
293         div_total >>= (1 + (PWM_DIV >> 9));
294         div_total = (div_total) ? div_total : 1;
295
296         if(rk29_bl_info->bl_ref) {
297                 divh = 0;
298         } else {
299                 divh = div_total;
300         }
301
302         clk_enable(pwm_clk);
303         rk_pwm_setup(id, PWM_DIV, divh, div_total);
304
305         rk29_bl->props.power = FB_BLANK_UNBLANK;
306         rk29_bl->props.fb_blank = FB_BLANK_UNBLANK;
307         rk29_bl->props.brightness = BL_STEP / 2;
308         rk29_bl->props.state = BL_CORE_DRIVER1;         
309
310         schedule_delayed_work(&rk29_backlight_work, msecs_to_jiffies(rk29_bl_info->delay_ms));
311         ret = device_create_file(&pdev->dev,&dev_attr_rk29backlight);
312         if(ret)
313         {
314                 dev_err(&pdev->dev, "failed to create sysfs file\n");
315         }
316
317         register_early_suspend(&bl_early_suspend);
318
319         printk("RK29 Backlight Driver Initialized.\n");
320         return ret;
321 }
322
323 static int rk29_backlight_remove(struct platform_device *pdev)
324 {               
325         struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
326
327         if (rk29_bl) {
328                 backlight_device_unregister(rk29_bl);
329                 unregister_early_suspend(&bl_early_suspend);
330                 clk_disable(pwm_clk);
331                 clk_put(pwm_clk);
332                 if (rk29_bl_info && rk29_bl_info->io_deinit) {
333                         rk29_bl_info->io_deinit();
334                 }
335                 return 0;
336         } else {
337                 DBG(KERN_CRIT "%s: no backlight device has registered\n",
338                                 __func__);
339                 return -ENODEV;
340         }
341 }
342
343 static void rk29_backlight_shutdown(struct platform_device *pdev)
344 {
345         struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
346
347         unregister_early_suspend(&bl_early_suspend);
348
349         rk29_bl->props.brightness >>= 1;
350         rk29_bl_update_status(rk29_bl);
351         mdelay(100);
352
353         rk29_bl->props.brightness >>= 1;
354         rk29_bl_update_status(rk29_bl);
355         mdelay(100);
356
357         rk29_bl->props.brightness = 0;
358         rk29_bl_update_status(rk29_bl);
359
360         if (rk29_bl_info && rk29_bl_info->io_deinit)
361                 rk29_bl_info->io_deinit();
362 }
363
364 static struct platform_driver rk29_backlight_driver = {
365         .probe  = rk29_backlight_probe,
366         .remove = rk29_backlight_remove,
367         .driver = {
368                 .name   = "rk29_backlight",
369                 .owner  = THIS_MODULE,
370         },
371         .shutdown       = rk29_backlight_shutdown,
372 };
373
374 static int __init rk29_backlight_init(void)
375 {
376         platform_driver_register(&rk29_backlight_driver);
377         return 0;
378 }
379 fs_initcall_sync(rk29_backlight_init);