1 /* drivers/video/backlight/rk29_backlight.c
3 * Copyright (C) 2009 Rockchip Corporation.
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.
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.
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>
25 #include <linux/cpufreq.h>
26 #include <linux/clk.h>
28 #include <linux/earlysuspend.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>
36 #include "rk2818_backlight.h"
42 #define DBG(x...) printk(KERN_INFO x)
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)))
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
58 static s32 rk29_bl_update_status(struct backlight_device *bl)
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;
68 div_total = read_pwm_reg(id, PWM_REG_LRC);
70 #if defined(CONFIG_MACH_RK29_AIGO)
71 divh = div_total*(BL_STEP - bl->props.brightness)/BL_STEP;
73 divh = div_total*(bl->props.brightness)/BL_STEP;
75 DBG(">>>%s-->%d bl->props.brightness == %d, div_total == %d , divh == %d\n",__FUNCTION__,__LINE__,bl->props.brightness, div_total, divh);
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;
82 write_pwm_reg(id, PWM_REG_HRC, divh);
86 static s32 rk29_bl_get_brightness(struct backlight_device *bl)
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;
93 div_total = read_pwm_reg(id, PWM_REG_LRC);
94 divh = read_pwm_reg(id, PWM_REG_HRC);
96 DBG("%s:: %d div_total: %d\n",__func__,__LINE__,div_total);
102 return BL_STEP*divh/div_total;
104 return BL_STEP-(BL_STEP*divh/div_total);
108 static struct backlight_ops rk29_bl_ops = {
109 .update_status = rk29_bl_update_status,
110 .get_brightness = rk29_bl_get_brightness,
113 #ifdef CONFIG_CPU_FREQ
114 static int rk29_bl_change_clk(struct notifier_block *nb, unsigned long val, void *data)
116 struct rk29_bl_info *rk29_bl_info;
120 int is_suspended = suspend_flag;
123 DBG(KERN_CRIT "%s: backlight device does not exist\n", __func__);
128 case CPUFREQ_PRECHANGE:
130 case CPUFREQ_POSTCHANGE:
131 if (clk_get_rate(pwm_clk) == pwm_clk_rate)
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;
139 divl = read_pwm_reg(id, PWM_REG_LRC);
140 divh = read_pwm_reg(id, PWM_REG_HRC);
142 tmp = pwm_clk_rate / PWM_APB_PRE_DIV;
143 tmp >>= (1 + (PWM_DIV >> 9));
146 div_total = (tmp) ? tmp : 1;
147 tmp = div_total*divh/divl;
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);
162 static void rk29_delaybacklight_timer(unsigned long data)
164 struct rk29_bl_info *rk29_bl_info = (struct rk29_bl_info *)data;
168 if (rk29_bl_info->pwm_resume)
169 rk29_bl_info->pwm_resume();
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;
178 divh = div_total*(BL_STEP-brightness)/BL_STEP;
180 write_pwm_reg(id, PWM_REG_HRC, divh);
184 #ifdef CONFIG_HAS_EARLYSUSPEND
185 static void rk29_bl_suspend(struct early_suspend *h)
187 struct rk29_bl_info *rk29_bl_info;
191 rk29_bl_info = rk29_bl->dev.parent->platform_data;
193 id = rk29_bl_info->pwm_id;
195 div_total = read_pwm_reg(id, PWM_REG_LRC);
197 if(rk29_bl_info->bl_ref) {
203 write_pwm_reg(id, PWM_REG_HRC, divh);
205 clk_disable(pwm_clk);
206 if (rk29_bl_info->pwm_suspend)
207 rk29_bl_info->pwm_suspend();
211 del_timer_sync(&rk29_bl_info->timer);
215 static void rk29_bl_resume(struct early_suspend *h)
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;
223 rk29_bl_info->timer.expires = jiffies + 30;
224 add_timer(&rk29_bl_info->timer);
226 id = rk28_bl_info->pwm_id;
227 brightness = rk28_bl->props.brightness;
229 div_total = read_pwm_reg(id, PWM_REG_LRC);
230 if (rk28_bl_info->bl_ref) {
231 divh = div_total*(brightness)/BL_STEP;
233 divh = div_total*(BL_STEP-brightness)/BL_STEP;
236 write_pwm_reg(id, PWM_REG_HRC, divh);
242 static struct early_suspend bl_early_suspend;
245 static int rk29_backlight_probe(struct platform_device *pdev)
248 struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
249 u32 id = rk29_bl_info->pwm_id;
253 DBG(KERN_CRIT "%s: backlight device register has existed \n",
258 if (rk29_bl_info && rk29_bl_info->io_init) {
259 rk29_bl_info->io_init();
262 rk29_bl = backlight_device_register("rk28_bl", &pdev->dev, NULL, &rk29_bl_ops);
264 DBG(KERN_CRIT "%s: backlight device register error\n",
269 pwm_clk = clk_get(NULL, "pwm");
270 if (IS_ERR(pwm_clk)) {
271 printk(KERN_ERR "failed to get pwm clock source\n");
274 pwm_clk_rate = clk_get_rate(pwm_clk);
275 div_total = pwm_clk_rate / PWM_APB_PRE_DIV;
279 div_total >>= (1 + (PWM_DIV >> 9));
280 div_total = (div_total) ? div_total : 1;
282 /// if(rk29_bl_info->bl_ref) {
285 divh = div_total / 2;
288 /*init timer to dispose workqueue */
289 setup_timer(&rk29_bl_info->timer, rk29_delaybacklight_timer, (unsigned long)rk29_bl_info);
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);
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);
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);
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);
315 printk("RK29 Backlight Driver Initialized.\n");
319 static int rk29_backlight_remove(struct platform_device *pdev)
321 struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
324 backlight_device_unregister(rk29_bl);
325 #ifdef CONFIG_HAS_EARLYSUSPEND
326 unregister_early_suspend(&bl_early_suspend);
328 #ifdef CONFIG_CPU_FREQ
329 cpufreq_unregister_notifier(&rk29_bl_info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
331 clk_disable(pwm_clk);
333 if (rk29_bl_info && rk29_bl_info->io_deinit) {
334 rk29_bl_info->io_deinit();
338 DBG(KERN_CRIT "%s: no backlight device has registered\n",
343 static void rk29_backlight_shutdown(struct platform_device *pdev)
347 struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
348 u32 id = rk29_bl_info->pwm_id;
350 brightness = rk29_bl->props.brightness;
352 div_total = read_pwm_reg(id, PWM_REG_LRC);
354 if (rk29_bl_info->bl_ref) {
355 divh = div_total*(brightness)/BL_STEP;
357 divh = div_total*(BL_STEP-brightness)/BL_STEP;
359 write_pwm_reg(id, PWM_REG_HRC, divh);
360 //printk("divh=%d\n",divh);
364 if (rk29_bl_info->bl_ref) {
365 divh = div_total*(brightness)/BL_STEP;
367 divh = div_total*(BL_STEP-brightness)/BL_STEP;
370 write_pwm_reg(id, PWM_REG_HRC, divh);
372 /*set PF1=1 PF2=1 for close backlight*/
374 if(rk29_bl_info->bl_ref) {
379 write_pwm_reg(id, PWM_REG_HRC, divh);
383 static struct platform_driver rk29_backlight_driver = {
384 .probe = rk29_backlight_probe,
385 .remove = rk29_backlight_remove,
387 .name = "rk29_backlight",
388 .owner = THIS_MODULE,
390 .shutdown=rk29_backlight_shutdown,
394 static int __init rk29_backlight_init(void)
396 platform_driver_register(&rk29_backlight_driver);
399 fs_initcall_sync(rk29_backlight_init);