Merge branch 'develop-3.0' of ssh://10.10.10.29/rk/kernel into develop-3.0
[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
30 #include "rk2818_backlight.h"
31
32 /*
33  * Debug
34  */
35 #if 0
36 #define DBG(x...)       printk(KERN_INFO x)
37 #else
38 #define DBG(x...)
39 #endif
40
41 #if defined(CONFIG_ARCH_RK30)
42 #define write_pwm_reg(id, addr, val)        __raw_writel(val, addr+(RK30_PWM01_BASE+(id>>1)*0x20000)+id*0x10)
43 #define read_pwm_reg(id, addr)              __raw_readl(addr+(RK30_PWM01_BASE+(id>>1)*0x20000+id*0x10))
44 #elif defined(CONFIG_ARCH_RK29)
45 #define write_pwm_reg(id, addr, val)        __raw_writel(val, addr+(RK29_PWM_BASE+id*0x10))
46 #define read_pwm_reg(id, addr)              __raw_readl(addr+(RK29_PWM_BASE+id*0x10))    
47 #endif
48
49 static struct clk *pwm_clk;
50 static struct backlight_device *rk29_bl;
51 static int suspend_flag = 0;
52
53 static int rk29_bl_update_status(struct backlight_device *bl)
54 {
55         u32 divh,div_total;
56         struct rk29_bl_info *rk29_bl_info = bl_get_data(bl);
57         u32 id = rk29_bl_info->pwm_id;
58         u32 ref = rk29_bl_info->bl_ref;
59
60         if (suspend_flag)
61                 return 0;
62
63         if (bl->props.brightness < rk29_bl_info->min_brightness)        /*avoid can't view screen when close backlight*/
64                 bl->props.brightness = rk29_bl_info->min_brightness;
65
66         div_total = read_pwm_reg(id, PWM_REG_LRC);
67         if (ref) {
68                 divh = div_total*(bl->props.brightness)/BL_STEP;
69         } else {
70                 divh = div_total*(BL_STEP-bl->props.brightness)/BL_STEP;
71         }
72         write_pwm_reg(id, PWM_REG_HRC, divh);
73
74         DBG(">>>%s-->%d brightness = %d, div_total = %d, divh = %d\n",__FUNCTION__,__LINE__,bl->props.brightness, div_total, divh);
75         return 0;
76 }
77
78 static int rk29_bl_get_brightness(struct backlight_device *bl)
79 {
80         u32 divh,div_total;
81         struct rk29_bl_info *rk29_bl_info = bl_get_data(bl);
82         u32 id = rk29_bl_info->pwm_id;
83         u32 ref = rk29_bl_info->bl_ref;
84
85         div_total = read_pwm_reg(id, PWM_REG_LRC);
86         divh = read_pwm_reg(id, PWM_REG_HRC);
87
88         if (!div_total)
89                 return 0;
90
91         if (ref) {
92                 return BL_STEP*divh/div_total;
93         } else {
94                 return BL_STEP-(BL_STEP*divh/div_total);
95         }
96 }
97
98 static struct backlight_ops rk29_bl_ops = {
99         .update_status  = rk29_bl_update_status,
100         .get_brightness = rk29_bl_get_brightness,
101 };
102
103 static void rk29_backlight_work_func(struct work_struct *work)
104 {
105         suspend_flag = 0;
106         rk29_bl_update_status(rk29_bl);
107 }
108 static DECLARE_DELAYED_WORK(rk29_backlight_work, rk29_backlight_work_func);
109
110 #ifdef CONFIG_HAS_EARLYSUSPEND
111 static void rk29_bl_suspend(struct early_suspend *h)
112 {
113         struct rk29_bl_info *rk29_bl_info = bl_get_data(rk29_bl);
114         int brightness = rk29_bl->props.brightness;
115
116         cancel_delayed_work_sync(&rk29_backlight_work);
117
118         if (rk29_bl->props.brightness) {
119                 rk29_bl->props.brightness = 0;
120                 rk29_bl_update_status(rk29_bl);
121                 rk29_bl->props.brightness = brightness;
122         }
123
124         if (!suspend_flag) {
125                 clk_disable(pwm_clk);
126                 if (rk29_bl_info->pwm_suspend)
127                         rk29_bl_info->pwm_suspend();
128         }
129
130         suspend_flag = 1;
131 }
132
133 static void rk29_bl_resume(struct early_suspend *h)
134 {
135         struct rk29_bl_info *rk29_bl_info = bl_get_data(rk29_bl);
136         DBG("%s : %s\n", __FILE__, __FUNCTION__);
137
138         if (rk29_bl_info->pwm_resume)
139                 rk29_bl_info->pwm_resume();
140
141         clk_enable(pwm_clk);
142
143         schedule_delayed_work(&rk29_backlight_work, msecs_to_jiffies(rk29_bl_info->delay_ms));
144 }
145
146 static struct early_suspend bl_early_suspend = {
147         .suspend = rk29_bl_suspend,
148         .resume = rk29_bl_resume,
149         .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 1,
150 };
151
152 void rk29_backlight_set(bool on)
153 {
154         printk("%s: set %d\n", __func__, on);
155         if(on)
156                 rk29_bl_resume(NULL);
157         else
158                 rk29_bl_suspend(NULL);
159         return;
160 }
161 EXPORT_SYMBOL(rk29_backlight_set);
162 #endif
163
164 static int rk29_backlight_probe(struct platform_device *pdev)
165 {               
166         int ret = 0;
167         struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
168         u32 id  =  rk29_bl_info->pwm_id;
169         u32 divh, div_total;
170         unsigned long pwm_clk_rate;
171         struct backlight_properties props;
172
173         if (rk29_bl) {
174                 printk(KERN_CRIT "%s: backlight device register has existed \n",
175                                 __func__);
176                 return -EEXIST;         
177         }
178
179         if (!rk29_bl_info->delay_ms)
180                 rk29_bl_info->delay_ms = 100;
181
182         if (rk29_bl_info->min_brightness < 0 || rk29_bl_info->min_brightness > BL_STEP)
183                 rk29_bl_info->min_brightness = 52;
184
185         if (rk29_bl_info && rk29_bl_info->io_init) {
186                 rk29_bl_info->io_init();
187         }
188
189         memset(&props, 0, sizeof(struct backlight_properties));
190         props.type = BACKLIGHT_RAW;
191         props.max_brightness = BL_STEP;
192         rk29_bl = backlight_device_register("rk28_bl", &pdev->dev, rk29_bl_info, &rk29_bl_ops, &props);
193         if (!rk29_bl) {
194                 printk(KERN_CRIT "%s: backlight device register error\n",
195                                 __func__);
196                 return -ENODEV;         
197         }
198
199 #if defined(CONFIG_ARCH_RK29)
200         pwm_clk = clk_get(NULL, "pwm");
201 #elif defined(CONFIG_ARCH_RK30)
202         if (id == 0 || id == 1)
203                 pwm_clk = clk_get(NULL, "pwm01");
204         else if (id == 2 || id == 3)
205                 pwm_clk = clk_get(NULL, "pwm23");
206 #endif
207         if (IS_ERR(pwm_clk)) {
208                 printk(KERN_ERR "failed to get pwm clock source\n");
209                 return -ENODEV;
210         }
211         pwm_clk_rate = clk_get_rate(pwm_clk);
212         div_total = pwm_clk_rate / PWM_APB_PRE_DIV;
213
214         div_total >>= (1 + (PWM_DIV >> 9));
215         div_total = (div_total) ? div_total : 1;
216
217         if(rk29_bl_info->bl_ref) {
218                 divh = 0;
219         } else {
220                 divh = div_total;
221         }
222
223         clk_enable(pwm_clk);
224         write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_RESET);
225         write_pwm_reg(id, PWM_REG_LRC, div_total);
226         write_pwm_reg(id, PWM_REG_HRC, divh);
227         write_pwm_reg(id, PWM_REG_CNTR, 0x0);
228         write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_ENABLE|PWM_TIME_EN);
229
230         rk29_bl->props.power = FB_BLANK_UNBLANK;
231         rk29_bl->props.fb_blank = FB_BLANK_UNBLANK;
232         rk29_bl->props.brightness = BL_STEP / 2;
233
234         schedule_delayed_work(&rk29_backlight_work, msecs_to_jiffies(rk29_bl_info->delay_ms));
235
236         register_early_suspend(&bl_early_suspend);
237
238         printk("RK29 Backlight Driver Initialized.\n");
239         return ret;
240 }
241
242 static int rk29_backlight_remove(struct platform_device *pdev)
243 {               
244         struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
245
246         if (rk29_bl) {
247                 backlight_device_unregister(rk29_bl);
248                 unregister_early_suspend(&bl_early_suspend);
249                 clk_disable(pwm_clk);
250                 clk_put(pwm_clk);
251                 if (rk29_bl_info && rk29_bl_info->io_deinit) {
252                         rk29_bl_info->io_deinit();
253                 }
254                 return 0;
255         } else {
256                 DBG(KERN_CRIT "%s: no backlight device has registered\n",
257                                 __func__);
258                 return -ENODEV;
259         }
260 }
261
262 static void rk29_backlight_shutdown(struct platform_device *pdev)
263 {
264         struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
265
266         unregister_early_suspend(&bl_early_suspend);
267
268         rk29_bl->props.brightness >>= 1;
269         rk29_bl_update_status(rk29_bl);
270         mdelay(100);
271
272         rk29_bl->props.brightness >>= 1;
273         rk29_bl_update_status(rk29_bl);
274         mdelay(100);
275
276         rk29_bl->props.brightness = 0;
277         rk29_bl_update_status(rk29_bl);
278
279         if (rk29_bl_info && rk29_bl_info->io_deinit)
280                 rk29_bl_info->io_deinit();
281 }
282
283 static struct platform_driver rk29_backlight_driver = {
284         .probe  = rk29_backlight_probe,
285         .remove = rk29_backlight_remove,
286         .driver = {
287                 .name   = "rk29_backlight",
288                 .owner  = THIS_MODULE,
289         },
290         .shutdown       = rk29_backlight_shutdown,
291 };
292
293 static int __init rk29_backlight_init(void)
294 {
295         platform_driver_register(&rk29_backlight_driver);
296         return 0;
297 }
298 fs_initcall_sync(rk29_backlight_init);