1 /* drivers/video/backlight/rk29_backlight.c
3 * Copyright (C) 2009-2011 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/backlight.h>
24 #include <linux/clk.h>
26 #include <linux/earlysuspend.h>
28 #include <mach/rk29_iomap.h>
29 #include <mach/board.h>
31 #include "rk2818_backlight.h"
37 #define DBG(x...) printk(KERN_INFO x)
43 #define write_pwm_reg(id, addr, val) __raw_writel(val, addr+(RK29_PWM_BASE+id*0x10))
44 #define read_pwm_reg(id, addr) __raw_readl(addr+(RK29_PWM_BASE+id*0x10))
46 static struct clk *pwm_clk;
47 static struct backlight_device *rk29_bl;
48 static int suspend_flag = 0;
50 static int rk29_bl_update_status(struct backlight_device *bl)
53 struct rk29_bl_info *rk29_bl_info = bl_get_data(bl);
54 u32 id = rk29_bl_info->pwm_id;
55 u32 ref = rk29_bl_info->bl_ref;
60 if (bl->props.brightness < rk29_bl_info->min_brightness) /*avoid can't view screen when close backlight*/
61 bl->props.brightness = rk29_bl_info->min_brightness;
63 div_total = read_pwm_reg(id, PWM_REG_LRC);
65 divh = div_total*(bl->props.brightness)/BL_STEP;
67 divh = div_total*(BL_STEP-bl->props.brightness)/BL_STEP;
69 write_pwm_reg(id, PWM_REG_HRC, divh);
71 DBG(">>>%s-->%d brightness = %d, div_total = %d, divh = %d\n",__FUNCTION__,__LINE__,bl->props.brightness, div_total, divh);
75 static int rk29_bl_get_brightness(struct backlight_device *bl)
78 struct rk29_bl_info *rk29_bl_info = bl_get_data(bl);
79 u32 id = rk29_bl_info->pwm_id;
80 u32 ref = rk29_bl_info->bl_ref;
82 div_total = read_pwm_reg(id, PWM_REG_LRC);
83 divh = read_pwm_reg(id, PWM_REG_HRC);
89 return BL_STEP*divh/div_total;
91 return BL_STEP-(BL_STEP*divh/div_total);
95 static struct backlight_ops rk29_bl_ops = {
96 .update_status = rk29_bl_update_status,
97 .get_brightness = rk29_bl_get_brightness,
100 static void rk29_backlight_work_func(struct work_struct *work)
103 rk29_bl_update_status(rk29_bl);
105 static DECLARE_DELAYED_WORK(rk29_backlight_work, rk29_backlight_work_func);
107 #ifdef CONFIG_HAS_EARLYSUSPEND
108 static void rk29_bl_suspend(struct early_suspend *h)
110 struct rk29_bl_info *rk29_bl_info = bl_get_data(rk29_bl);
111 int brightness = rk29_bl->props.brightness;
113 cancel_delayed_work_sync(&rk29_backlight_work);
115 if (rk29_bl->props.brightness) {
116 rk29_bl->props.brightness = 0;
117 rk29_bl_update_status(rk29_bl);
118 rk29_bl->props.brightness = brightness;
122 clk_disable(pwm_clk);
123 if (rk29_bl_info->pwm_suspend)
124 rk29_bl_info->pwm_suspend();
130 static void rk29_bl_resume(struct early_suspend *h)
132 struct rk29_bl_info *rk29_bl_info = bl_get_data(rk29_bl);
133 DBG("%s : %s\n", __FILE__, __FUNCTION__);
135 if (rk29_bl_info->pwm_resume)
136 rk29_bl_info->pwm_resume();
140 schedule_delayed_work(&rk29_backlight_work, msecs_to_jiffies(rk29_bl_info->delay_ms));
143 static struct early_suspend bl_early_suspend = {
144 .suspend = rk29_bl_suspend,
145 .resume = rk29_bl_resume,
146 .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 1,
149 void rk29_backlight_set(bool on)
151 printk("%s: set %d\n", __func__, on);
153 rk29_bl_resume(NULL);
155 rk29_bl_suspend(NULL);
158 EXPORT_SYMBOL(rk29_backlight_set);
160 static int rk29_backlight_probe(struct platform_device *pdev)
163 struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
164 u32 id = rk29_bl_info->pwm_id;
166 unsigned long pwm_clk_rate;
169 DBG(KERN_CRIT "%s: backlight device register has existed \n",
174 if (!rk29_bl_info->delay_ms)
175 rk29_bl_info->delay_ms = 30;
177 if (rk29_bl_info->min_brightness < 0 || rk29_bl_info->min_brightness > BL_STEP)
178 rk29_bl_info->min_brightness = 52;
180 if (rk29_bl_info && rk29_bl_info->io_init) {
181 rk29_bl_info->io_init();
184 rk29_bl = backlight_device_register("rk28_bl", &pdev->dev, rk29_bl_info, &rk29_bl_ops);
186 DBG(KERN_CRIT "%s: backlight device register error\n",
191 pwm_clk = clk_get(NULL, "pwm");
192 if (IS_ERR(pwm_clk)) {
193 printk(KERN_ERR "failed to get pwm clock source\n");
196 pwm_clk_rate = clk_get_rate(pwm_clk);
197 div_total = pwm_clk_rate / PWM_APB_PRE_DIV;
199 div_total >>= (1 + (PWM_DIV >> 9));
200 div_total = (div_total) ? div_total : 1;
202 if(rk29_bl_info->bl_ref) {
209 write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_RESET);
210 write_pwm_reg(id, PWM_REG_LRC, div_total);
211 write_pwm_reg(id, PWM_REG_HRC, divh);
212 write_pwm_reg(id, PWM_REG_CNTR, 0x0);
213 write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_ENABLE|PWM_TIME_EN);
215 rk29_bl->props.power = FB_BLANK_UNBLANK;
216 rk29_bl->props.fb_blank = FB_BLANK_UNBLANK;
217 rk29_bl->props.max_brightness = BL_STEP;
218 rk29_bl->props.brightness = BL_STEP / 2;
220 schedule_delayed_work(&rk29_backlight_work, msecs_to_jiffies(rk29_bl_info->delay_ms));
222 register_early_suspend(&bl_early_suspend);
224 printk("RK29 Backlight Driver Initialized.\n");
228 static int rk29_backlight_remove(struct platform_device *pdev)
230 struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
233 backlight_device_unregister(rk29_bl);
234 unregister_early_suspend(&bl_early_suspend);
235 clk_disable(pwm_clk);
237 if (rk29_bl_info && rk29_bl_info->io_deinit) {
238 rk29_bl_info->io_deinit();
242 DBG(KERN_CRIT "%s: no backlight device has registered\n",
248 static void rk29_backlight_shutdown(struct platform_device *pdev)
250 struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
252 rk29_bl->props.brightness >>= 1;
253 rk29_bl_update_status(rk29_bl);
256 rk29_bl->props.brightness >>= 1;
257 rk29_bl_update_status(rk29_bl);
260 rk29_bl->props.brightness = 0;
261 rk29_bl_update_status(rk29_bl);
263 if (rk29_bl_info && rk29_bl_info->io_deinit)
264 rk29_bl_info->io_deinit();
267 static struct platform_driver rk29_backlight_driver = {
268 .probe = rk29_backlight_probe,
269 .remove = rk29_backlight_remove,
271 .name = "rk29_backlight",
272 .owner = THIS_MODULE,
274 .shutdown = rk29_backlight_shutdown,
277 static int __init rk29_backlight_init(void)
279 platform_driver_register(&rk29_backlight_driver);
282 fs_initcall_sync(rk29_backlight_init);