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