Merge branch 'develop' of 192.168.1.29:/home/rockchip/kernel into develop
[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 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/debugfs.h>
23 #include <linux/backlight.h>
24 #include <linux/fb.h>
25 #include <linux/cpufreq.h>
26 #include <linux/clk.h>
27
28 #include <linux/earlysuspend.h>
29 #include <asm/io.h>
30 //#include <mach/typedef.h>
31 #include <mach/iomux.h>
32 #include <mach/gpio.h>
33 #include <mach/rk29_iomap.h>
34 #include <mach/board.h>
35
36 #include "rk2818_backlight.h"
37
38 /*
39  * Debug
40  */
41 #if 0
42 #define DBG(x...)       printk(KERN_INFO x)
43 #else
44 #define DBG(x...)
45 #endif
46
47
48 #define write_pwm_reg(id, addr, val)        __raw_writel(val, addr+(RK29_PWM_BASE+id*0x10)) 
49 #define read_pwm_reg(id, addr)              __raw_readl(addr+(RK29_PWM_BASE+id*0x10))    
50 #define mask_pwm_reg(id, addr, msk, val)    write_dma_reg(id, addr, (val)|((~(msk))&read_dma_reg(id, addr)))
51
52 static struct clk *pwm_clk;
53 static unsigned long pwm_clk_rate;
54 static struct backlight_device *rk29_bl;
55 static int suspend_flag = 0;
56 #define BACKLIGHT_SEE_MINVALUE  52
57
58 static s32 rk29_bl_update_status(struct backlight_device *bl)
59 {
60     u32 divh,div_total;
61     struct rk29_bl_info *rk29_bl_info = bl->dev.parent->platform_data;
62     u32 id = rk29_bl_info->pwm_id;
63     u32 ref = rk29_bl_info->bl_ref;
64
65     if (suspend_flag)
66         return 0;
67     
68     div_total = read_pwm_reg(id, PWM_REG_LRC);
69     if (ref) {
70
71         divh = div_total*(bl->props.brightness)/BL_STEP;
72          DBG(">>>%s-->%d   bl->props.brightness == %d, div_total == %d , divh == %d\n",__FUNCTION__,__LINE__,bl->props.brightness, div_total, divh);
73     } else {
74          DBG(">>>%s-->%d   bl->props.brightness == %d\n",__FUNCTION__,__LINE__,bl->props.brightness);
75          if(bl->props.brightness < BACKLIGHT_SEE_MINVALUE)      /*avoid can't view screen when close backlight*/
76                 bl->props.brightness = BACKLIGHT_SEE_MINVALUE;
77         divh = div_total*(BL_STEP-bl->props.brightness)/BL_STEP;
78     }
79     write_pwm_reg(id, PWM_REG_HRC, divh);
80     return 0;
81 }
82
83 static s32 rk29_bl_get_brightness(struct backlight_device *bl)
84 {
85     u32 divh,div_total;
86     struct rk29_bl_info *rk29_bl_info = bl->dev.parent->platform_data;
87     u32 id = rk29_bl_info->pwm_id;
88     u32 ref = rk29_bl_info->bl_ref;
89     
90     div_total = read_pwm_reg(id, PWM_REG_LRC);
91     divh = read_pwm_reg(id, PWM_REG_HRC);
92
93         DBG("%s:: %d div_total: %d\n",__func__,__LINE__,div_total);
94
95     if (ref) {
96         return BL_STEP*divh/div_total;
97     } else {
98         return BL_STEP-(BL_STEP*divh/div_total);
99     }
100 }
101
102 static struct backlight_ops rk29_bl_ops = {
103         .update_status = rk29_bl_update_status,
104         .get_brightness = rk29_bl_get_brightness,
105 };
106
107 #ifdef CONFIG_CPU_FREQ
108 static int rk29_bl_change_clk(struct notifier_block *nb, unsigned long val, void *data)
109 {
110     struct rk29_bl_info *rk29_bl_info;
111     u32 id;
112     u32 divl, divh, tmp;
113     u32 div_total;
114         int is_suspended = suspend_flag;
115
116         if (!rk29_bl) {
117                 DBG(KERN_CRIT "%s: backlight device does not exist\n", __func__);
118                 return -ENODEV;         
119     }
120     
121         switch (val) {
122     case CPUFREQ_PRECHANGE:        
123          break;
124     case CPUFREQ_POSTCHANGE:
125                 if (clk_get_rate(pwm_clk) == pwm_clk_rate)
126                         break;
127                 pwm_clk_rate = clk_get_rate(pwm_clk);
128          rk29_bl_info = rk29_bl->dev.parent->platform_data;
129          id = rk29_bl_info->pwm_id;
130      
131
132          
133          divl = read_pwm_reg(id, PWM_REG_LRC);
134          divh = read_pwm_reg(id, PWM_REG_HRC);
135      
136                 tmp = pwm_clk_rate / PWM_APB_PRE_DIV;
137          tmp >>= (1 + (PWM_DIV >> 9));
138
139          
140          div_total = (tmp) ? tmp : 1;
141          tmp = div_total*divh/divl;
142          
143                 if (!is_suspended)
144                         clk_disable(pwm_clk);
145          write_pwm_reg(id, PWM_REG_LRC, div_total);
146          write_pwm_reg(id, PWM_REG_HRC, tmp);    
147          write_pwm_reg(id, PWM_REG_CNTR, 0);  
148                 if (!is_suspended)
149                         clk_enable(pwm_clk);
150          break;
151     }
152    
153     return 0;
154 }   
155 #endif
156 static void rk29_delaybacklight_timer(unsigned long data)
157 {
158         struct rk29_bl_info *rk29_bl_info = (struct rk29_bl_info *)data;
159         u32 id, brightness;
160         u32 div_total, divh;
161         clk_enable(pwm_clk);
162         id = rk29_bl_info->pwm_id;
163     brightness = rk29_bl->props.brightness;
164     div_total = read_pwm_reg(id, PWM_REG_LRC);
165     if (rk29_bl_info->bl_ref) {
166         divh = div_total*(brightness)/BL_STEP;
167     } else {
168         divh = div_total*(BL_STEP-brightness)/BL_STEP;
169     }
170     write_pwm_reg(id, PWM_REG_HRC, divh);
171     suspend_flag = 0;
172 }
173
174 #ifdef CONFIG_HAS_EARLYSUSPEND
175 static void rk29_bl_suspend(struct early_suspend *h)
176 {
177     struct rk29_bl_info *rk29_bl_info;
178     u32 id;
179     u32 div_total, divh;
180     
181     rk29_bl_info = rk29_bl->dev.parent->platform_data;
182
183     id = rk29_bl_info->pwm_id;
184
185     div_total = read_pwm_reg(id, PWM_REG_LRC);
186     
187     if(rk29_bl_info->bl_ref) {
188         divh = 0;
189     } else {
190         divh = div_total;
191     }
192
193     write_pwm_reg(id, PWM_REG_HRC, divh);
194         if (!suspend_flag)
195                 clk_disable(pwm_clk);
196
197     suspend_flag = 1;
198 }
199
200
201 static void rk29_bl_resume(struct early_suspend *h)
202 {
203     struct rk29_bl_info *rk29_bl_info;
204    // u32 id, brightness;
205     //u32 div_total, divh;
206     DBG("%s : %s\n", __FILE__, __FUNCTION__);
207     rk29_bl_info = rk29_bl->dev.parent->platform_data;
208         
209         rk29_bl_info->timer.expires  = jiffies + 30;
210         add_timer(&rk29_bl_info->timer);
211         #if 0
212     id = rk28_bl_info->pwm_id;
213     brightness = rk28_bl->props.brightness;
214     
215     div_total = read_pwm_reg(id, PWM_REG_LRC);
216     if (rk28_bl_info->bl_ref) {
217         divh = div_total*(brightness)/BL_STEP;
218     } else {
219         divh = div_total*(BL_STEP-brightness)/BL_STEP;
220     }
221     //mdelay(100);
222     write_pwm_reg(id, PWM_REG_HRC, divh);
223
224     suspend_flag = 0;
225         #endif 
226 }
227
228 static struct early_suspend bl_early_suspend;
229 #endif
230
231 static int rk29_backlight_probe(struct platform_device *pdev)
232 {               
233     int ret = 0;
234     struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
235     u32 id  =  rk29_bl_info->pwm_id;
236     u32 divh, div_total;
237
238     if (rk29_bl) {
239         DBG(KERN_CRIT "%s: backlight device register has existed \n",
240                __func__); 
241                 return -EEXIST;         
242     }
243     
244         rk29_bl = backlight_device_register("rk28_bl", &pdev->dev, NULL, &rk29_bl_ops);
245         if (!rk29_bl) {
246         DBG(KERN_CRIT "%s: backlight device register error\n",
247                __func__); 
248                 return -ENODEV;         
249         }
250         
251         if (!pwm_clk)
252                 pwm_clk = clk_get(NULL, "pwm");
253         if (!pwm_clk || IS_ERR(pwm_clk)) {
254                 printk(KERN_ERR "failed to get pwm clock source\n");
255                 return -ENODEV; 
256         }
257         pwm_clk_rate = clk_get_rate(pwm_clk);
258         div_total = pwm_clk_rate / PWM_APB_PRE_DIV;
259
260         
261     
262     div_total >>= (1 + (PWM_DIV >> 9));
263     div_total = (div_total) ? div_total : 1;
264     
265    /// if(rk29_bl_info->bl_ref) {
266     ///    divh = 0;
267    /// } else {
268         divh = div_total / 2;
269    // }
270
271     /*init timer to dispose workqueue */
272     setup_timer(&rk29_bl_info->timer, rk29_delaybacklight_timer, (unsigned long)rk29_bl_info);
273
274 #ifdef CONFIG_CPU_FREQ
275     rk29_bl_info->freq_transition.notifier_call = rk29_bl_change_clk;   
276     cpufreq_register_notifier(&rk29_bl_info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
277 #endif
278         
279 //      clk_disable(pwm_clk);
280     write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_RESET);
281     write_pwm_reg(id, PWM_REG_LRC, div_total);
282     write_pwm_reg(id, PWM_REG_HRC, divh);
283     write_pwm_reg(id, PWM_REG_CNTR, 0x0);
284     write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_ENABLE|PWM_TIME_EN);
285         clk_enable(pwm_clk);
286    
287         rk29_bl->props.power = FB_BLANK_UNBLANK;
288         rk29_bl->props.fb_blank = FB_BLANK_UNBLANK;
289         rk29_bl->props.max_brightness = BL_STEP;
290         rk29_bl->props.brightness = rk29_bl_get_brightness(rk29_bl);
291
292 #ifdef CONFIG_HAS_EARLYSUSPEND
293     bl_early_suspend.suspend = rk29_bl_suspend;
294     bl_early_suspend.resume = rk29_bl_resume;
295     bl_early_suspend.level = ~0x0;
296     register_early_suspend(&bl_early_suspend);
297 #endif
298
299     if (rk29_bl_info && rk29_bl_info->io_init) {
300         rk29_bl_info->io_init();
301     }
302
303     return ret;
304 }
305
306 static int rk29_backlight_remove(struct platform_device *pdev)
307 {               
308     struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
309     
310         if (rk29_bl) {
311                 backlight_device_unregister(rk29_bl);
312 #ifdef CONFIG_HAS_EARLYSUSPEND
313         unregister_early_suspend(&bl_early_suspend);
314 #endif       
315 #ifdef CONFIG_CPU_FREQ
316         cpufreq_unregister_notifier(&rk29_bl_info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
317 #endif
318                 clk_disable(pwm_clk);
319                 clk_put(pwm_clk);
320         if (rk29_bl_info && rk29_bl_info->io_deinit) {
321             rk29_bl_info->io_deinit();
322         }
323         return 0;
324     } else {
325         DBG(KERN_CRIT "%s: no backlight device has registered\n",
326                __func__); 
327         return -ENODEV;      
328     }
329 }
330 static void rk29_backlight_shutdown(struct platform_device *pdev)
331 {
332
333    u32 divh,div_total;
334     struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
335     u32 id = rk29_bl_info->pwm_id;
336     u32  brightness;
337         brightness = rk29_bl->props.brightness; 
338         brightness/=2;
339         div_total = read_pwm_reg(id, PWM_REG_LRC);   
340         
341         if (rk29_bl_info->bl_ref) {
342                         divh = div_total*(brightness)/BL_STEP;
343         } else {
344                         divh = div_total*(BL_STEP-brightness)/BL_STEP;
345         }
346         write_pwm_reg(id, PWM_REG_HRC, divh);
347         //printk("divh=%d\n",divh);
348          mdelay(100);
349          
350         brightness/=2;
351         if (rk29_bl_info->bl_ref) {
352         divh = div_total*(brightness)/BL_STEP;
353         } else {
354         divh = div_total*(BL_STEP-brightness)/BL_STEP;
355         }
356
357         write_pwm_reg(id, PWM_REG_HRC, divh); 
358         mdelay(100);
359         /*set  PF1=1 PF2=1 for close backlight*/        
360
361     if(rk29_bl_info->bl_ref) {
362         divh = 0;
363     } else {
364         divh = div_total;
365     }
366     write_pwm_reg(id, PWM_REG_HRC, divh);
367   
368 }
369
370 static struct platform_driver rk29_backlight_driver = {
371         .probe  = rk29_backlight_probe,
372         .remove = rk29_backlight_remove,
373         .driver = {
374                 .name   = "rk29_backlight",
375                 .owner  = THIS_MODULE,
376         },
377         .shutdown=rk29_backlight_shutdown,
378 };
379
380
381 static int __init rk29_backlight_init(void)
382 {
383         platform_driver_register(&rk29_backlight_driver);
384         return 0;
385 }
386 //rootfs_initcall(rk29_backlight_init);
387
388 late_initcall(rk29_backlight_init);
389 //module_init(rk29_backlight_init);