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