d4dd7aba1d8a072b964cde2397235f469244a727
[firefly-linux-kernel-4.4.55.git] / drivers / video / backlight / rk2818_backlight.c
1 /* arch/arm/mach-rockchip/rk28_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 #ifdef CONFIG_ANDROID_POWER
29 #include <linux/android_power.h>
30 #endif
31 #include <asm/io.h>
32 //#include <mach/typedef.h>
33 #include <mach/iomux.h>
34 #include <mach/gpio.h>
35 #include <mach/rk2818_iomap.h>
36 #include <mach/board.h>
37
38 #include "rk2818_backlight.h"
39
40 //#define RK28_PRINT 
41 //#include <mach/rk2818_debug.h>
42
43 /*
44  * Debug
45  */
46 #if 0
47 #define DBG(x...)       printk(KERN_INFO x)
48 #else
49 #define DBG(x...)
50 #endif
51
52
53 #define write_pwm_reg(id, addr, val)        __raw_writel(val, addr+(RK2818_PWM_BASE+id*0x10)) 
54 #define read_pwm_reg(id, addr)              __raw_readl(addr+(RK2818_PWM_BASE+id*0x10))    
55 #define mask_pwm_reg(id, addr, msk, val)    write_dma_reg(id, addr, (val)|((~(msk))&read_dma_reg(id, addr)))
56
57
58 static struct backlight_device *rk2818_bl = NULL;
59 static int suspend_flag = 0;
60 #define BACKLIGHT_SEE_MINVALUE  52
61
62 static s32 rk2818_bl_update_status(struct backlight_device *bl)
63 {
64     u32 divh,div_total;
65     struct rk2818bl_info *rk2818_bl_info = bl->dev.parent->platform_data;
66     u32 id = rk2818_bl_info->pwm_id;
67     u32 ref = rk2818_bl_info->bl_ref;
68
69     if (suspend_flag)
70         return 0;
71     
72     div_total = read_pwm_reg(id, PWM_REG_LRC);
73     if (ref) {
74          DBG(">>>%s-->%d   bl->props.brightness == %d\n",__FUNCTION__,__LINE__,bl->props.brightness);
75         divh = div_total*(bl->props.brightness)/BL_STEP;
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     DBG("%s::========================================\n",__func__);
84     return 0;
85 }
86
87 static s32 rk2818_bl_get_brightness(struct backlight_device *bl)
88 {
89     u32 divh,div_total;
90     struct rk2818bl_info *rk2818_bl_info = bl->dev.parent->platform_data;
91     u32 id = rk2818_bl_info->pwm_id;
92     u32 ref = rk2818_bl_info->bl_ref;
93     
94     div_total = read_pwm_reg(id, PWM_REG_LRC);
95     divh = read_pwm_reg(id, PWM_REG_HRC);
96
97         DBG("%s::========================================\n",__func__);
98
99     if (ref) {
100         return BL_STEP*divh/div_total;
101     } else {
102         return BL_STEP-(BL_STEP*divh/div_total);
103     }
104 }
105
106 static struct backlight_ops rk2818_bl_ops = {
107         .update_status = rk2818_bl_update_status,
108         .get_brightness = rk2818_bl_get_brightness,
109 };
110
111
112 static int rk2818_bl_change_clk(struct notifier_block *nb, unsigned long val, void *data)
113 {
114     struct rk2818bl_info *rk2818_bl_info;
115     u32 id;
116     u32 divl, divh, tmp;
117     u32 div_total;
118     struct clk* pclk; 
119
120     if (!rk2818_bl)
121     {
122         DBG(KERN_CRIT "%s: backlight device does not exist \n",
123                __func__); 
124                 return -ENODEV;         
125     }
126     
127     switch (val) 
128     {
129     case CPUFREQ_PRECHANGE:        
130          break;
131     case CPUFREQ_POSTCHANGE:
132          rk2818_bl_info = rk2818_bl->dev.parent->platform_data;
133          id = rk2818_bl_info->pwm_id;
134      
135          pclk = clk_get(NULL, "arm_pclk");
136          if (!pclk || IS_ERR(pclk)) 
137          {
138                 printk(KERN_ERR "%s, %s, failed to get lcd clock source\n",__FILE__, __FUNCTION__);
139                 return -ENODEV; 
140          }
141          
142          divl = read_pwm_reg(id, PWM_REG_LRC);
143          divh = read_pwm_reg(id, PWM_REG_HRC);
144      
145          tmp = clk_get_rate(pclk)/PWM_APB_PRE_DIV;
146          tmp >>= (1 + (PWM_DIV >> 9));
147
148          clk_put(pclk);
149          
150          div_total = (tmp) ? tmp : 1;
151          tmp = div_total*divh/divl;
152          
153          write_pwm_reg(id, PWM_REG_LRC, div_total);
154          write_pwm_reg(id, PWM_REG_HRC, tmp);    
155          write_pwm_reg(id, PWM_REG_CNTR, 0);  
156          break;
157     }
158    
159     return 0;
160 }   
161 static void rk2818_delaybacklight_timer(unsigned long data)
162 {
163         struct rk2818bl_info *rk2818_bl_info = (struct rk2818bl_info *)data;
164         u32 id, brightness;
165         u32 div_total, divh;
166         id = rk2818_bl_info->pwm_id;
167     brightness = rk2818_bl->props.brightness;
168     div_total = read_pwm_reg(id, PWM_REG_LRC);
169     if (rk2818_bl_info->bl_ref) {
170         divh = div_total*(brightness)/BL_STEP;
171     } else {
172         divh = div_total*(BL_STEP-brightness)/BL_STEP;
173     }
174     write_pwm_reg(id, PWM_REG_HRC, divh);
175     suspend_flag = 0;
176     DBG("%s: ======================== \n",__func__); 
177 }
178
179 #ifdef CONFIG_ANDROID_POWER
180 static void rk2818_bl_suspend(android_early_suspend_t *h)
181 {
182     struct rk2818bl_info *rk2818_bl_info;
183     u32 id;
184     u32 div_total, divh;
185     
186     rk2818_bl_info = rk2818_bl->dev.parent->platform_data;
187
188     id = rk2818_bl_info->pwm_id;
189
190     div_total = read_pwm_reg(id, PWM_REG_LRC);
191     
192     if(rk2818_bl_info->bl_ref) {
193         divh = 0;
194     } else {
195         divh = div_total;
196     }
197
198     write_pwm_reg(id, PWM_REG_HRC, divh);
199
200     suspend_flag = 1;
201     
202     DBG("%s: ========================= \n",__func__); 
203 }
204
205
206 static void rk2818_bl_resume(android_early_suspend_t *h)
207 {
208     struct rk2818bl_info *rk2818_bl_info;
209    // u32 id, brightness;
210     //u32 div_total, divh;
211     DBG(">>>>>> %s : %s\n", __FILE__, __FUNCTION__);
212     rk2818_bl_info = rk2818_bl->dev.parent->platform_data;
213         
214         rk2818_bl_info->timer.expires  = jiffies + 30;
215         add_timer(&rk2818_bl_info->timer);
216         #if 0
217     id = rk28_bl_info->pwm_id;
218     brightness = rk28_bl->props.brightness;
219     
220     div_total = read_pwm_reg(id, PWM_REG_LRC);
221     if (rk28_bl_info->bl_ref) {
222         divh = div_total*(brightness)/BL_STEP;
223     } else {
224         divh = div_total*(BL_STEP-brightness)/BL_STEP;
225     }
226     //mdelay(100);
227     write_pwm_reg(id, PWM_REG_HRC, divh);
228
229     suspend_flag = 0;
230
231     rk28printk("%s: ======================== \n",__func__); 
232         #endif 
233 }
234
235 static android_early_suspend_t bl_early_suspend;
236 #endif
237
238 static char *pwm_iomux[] = {
239      GPIOF2_APWM0_SEL_NAME,
240      GPIOF3_APWM1_MMC0DETN_NAME,
241      GPIOF4_APWM2_MMC0WPT_NAME,
242      GPIOF5_APWM3_DPWM3_NAME,
243 };
244
245 static int rk2818_backlight_probe(struct platform_device *pdev)
246 {               
247     int ret = 0;
248     struct rk2818bl_info *rk2818_bl_info = pdev->dev.platform_data;
249     u32 pin =  (rk2818_bl_info->pw_pin >> 8) & 0xff;
250     u32 lev =  rk2818_bl_info->pw_pin & 0xf;
251     u32 id  =  rk2818_bl_info->pwm_id;
252     u32 divh, div_total;
253     struct clk* arm_pclk; 
254
255     DBG("%s::========================================\n",__func__);
256
257         if (rk2818_bl) {
258         DBG(KERN_CRIT "%s: backlight device register has existed \n",
259                __func__); 
260                 return -EEXIST;         
261     }
262     
263         rk2818_bl = backlight_device_register("rk28_bl", &pdev->dev, NULL, &rk2818_bl_ops);
264         if (!rk2818_bl) {
265         DBG(KERN_CRIT "%s: backlight device register error\n",
266                __func__); 
267                 return -ENODEV;         
268         }
269
270     arm_pclk = clk_get(NULL, "arm_pclk");
271     if (!arm_pclk || IS_ERR(arm_pclk)) 
272     {
273                 printk(KERN_ERR "failed to get lcd clock source\n");
274                 return -ENODEV; 
275         }
276     div_total = clk_get_rate(arm_pclk)/PWM_APB_PRE_DIV;
277     clk_put(arm_pclk);
278     
279     div_total >>= (1 + (PWM_DIV >> 9));
280     div_total = (div_total) ? div_total : 1;
281     
282     if(rk2818_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(&rk2818_bl_info->timer, rk2818_delaybacklight_timer, (unsigned long)rk2818_bl_info);
290
291     rk2818_bl_info->freq_transition.notifier_call = rk2818_bl_change_clk;   
292     cpufreq_register_notifier(&rk2818_bl_info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
293         
294     write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_RESET);
295     write_pwm_reg(id, PWM_REG_LRC, div_total);
296     write_pwm_reg(id, PWM_REG_HRC, divh);
297     write_pwm_reg(id, PWM_REG_CNTR, 0x0);
298     write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_ENABLE|PWM_TIME_EN);
299    
300         rk2818_bl->props.power = FB_BLANK_UNBLANK;
301         rk2818_bl->props.fb_blank = FB_BLANK_UNBLANK;
302         rk2818_bl->props.max_brightness = BL_STEP;
303         rk2818_bl->props.brightness = rk2818_bl_get_brightness(rk2818_bl);
304
305 #ifdef CONFIG_ANDROID_POWER
306     bl_early_suspend.suspend = rk2818_bl_suspend;
307     bl_early_suspend.resume = rk2818_bl_resume;
308     bl_early_suspend.level = ~0x0;
309     android_register_early_suspend(&bl_early_suspend);
310 #endif
311
312     rk2818_mux_api_set(pwm_iomux[id], 1);
313
314     if(rk2818_bl_info->pw_iomux)
315     {
316         rk2818_mux_api_set(rk2818_bl_info->pw_iomux, 0); 
317     }
318
319     ret = gpio_request(pin, NULL); 
320     if(ret != 0)
321     {
322         gpio_free(pin);
323         printk(KERN_ERR ">>>>>> lcd_cs gpio_request err \n ");        
324     }
325     
326     gpio_direction_output(pin, 0);
327     gpio_set_value(pin, lev);
328
329     return 0;
330 }
331
332 static int rk2818_backlight_remove(struct platform_device *pdev)
333 {               
334     struct rk2818bl_info *rk2818_bl_info = pdev->dev.platform_data;
335     u32 pin =  (rk2818_bl_info->pw_pin >> 8) & 0xff;
336     
337         if (rk2818_bl) {
338                 backlight_device_unregister(rk2818_bl);
339 #ifdef CONFIG_ANDROID_POWER
340         android_unregister_early_suspend(&bl_early_suspend);
341 #endif       
342         cpufreq_unregister_notifier(&rk2818_bl_info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
343         gpio_free(pin); 
344         return 0;
345     } else {
346         DBG(KERN_CRIT "%s: no backlight device has registered\n",
347                __func__); 
348         return -ENODEV;      
349     }
350 }
351 static void rk2818_backlight_shutdown(struct platform_device *pdev)
352 {
353
354    u32 divh,div_total;
355     struct rk2818bl_info *rk2818_bl_info = pdev->dev.platform_data;
356     u32 id = rk2818_bl_info->pwm_id;
357     u32 pin=rk2818_bl_info->pw_pin;
358     u32  brightness;
359     u8 lev;
360         brightness = rk2818_bl->props.brightness; 
361         brightness/=2;
362         div_total = read_pwm_reg(id, PWM_REG_LRC);   
363         if (rk2818_bl_info->bl_ref) {
364                         divh = div_total*(brightness)/BL_STEP;
365         } else {
366                         divh = div_total*(BL_STEP-brightness)/BL_STEP;
367         }
368         write_pwm_reg(id, PWM_REG_HRC, divh);
369         //printk("divh=%d\n",divh);
370          mdelay(100);
371         brightness/=2;
372         if (rk2818_bl_info->bl_ref) {
373         divh = div_total*(brightness)/BL_STEP;
374         } else {
375         divh = div_total*(BL_STEP-brightness)/BL_STEP;
376         }
377         //printk("------------rk28_backlight_shutdown  mdelay----------------------------\n");
378         write_pwm_reg(id, PWM_REG_HRC, divh); 
379     mdelay(100);
380         /*set  PF1=1 PF2=1 for close backlight*/        
381
382     if(rk2818_bl_info->bl_ref) {
383         lev = GPIO_LOW;
384     } else {
385         lev = GPIO_HIGH;
386     }
387     gpio_set_value(pin, lev);
388   
389 }
390
391 static struct platform_driver rk2818_backlight_driver = {
392         .probe  = rk2818_backlight_probe,
393         .remove = rk2818_backlight_remove,
394         .driver = {
395                 .name   = "rk2818_backlight",
396                 .owner  = THIS_MODULE,
397         },
398         .shutdown=rk2818_backlight_shutdown,
399 };
400
401
402 static int __init rk2818_backlight_init(void)
403 {
404         DBG("%s::========================================\n",__func__);
405         platform_driver_register(&rk2818_backlight_driver);
406         return 0;
407 }
408 rootfs_initcall(rk2818_backlight_init);
409
410 //late_initcall(rk28_backlight_init);
411 //module_init(rk28_backlight_init);