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/board.h>
31 #define PWM_DIV PWM_DIV2
32 #define PWM_APB_PRE_DIV 1000
39 #define DBG(x...) printk(KERN_INFO x)
44 #define read_pwm_reg(addr) __raw_readl(pwm_base + addr)
46 static struct clk *pwm_clk;
47 static void __iomem *pwm_base;
48 static struct backlight_device *rk29_bl;
49 static int suspend_flag = 0;
52 int convertint(const char s[])
56 for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
58 n = 10 * n + (s[i] - '0');
63 static ssize_t backlight_write(struct device *dev,
64 struct device_attribute *attr,const char *buf, size_t count)
67 struct rk29_bl_info *rk29_bl_info = bl_get_data(rk29_bl);
70 number = convertint(buf);
72 rk29_bl_info->min_brightness=number;
77 static ssize_t backlight_read(struct device *dev,
78 struct device_attribute *attr, char *buf)
80 DBG("rk29_bl_info->min_brightness=%d\n",rk29_bl_info->min_brightness);
83 static DEVICE_ATTR(rk29backlight, 0660, backlight_read, backlight_write);
85 static DEFINE_MUTEX(backlight_mutex);
87 static int rk29_bl_update_status(struct backlight_device *bl)
90 struct rk29_bl_info *rk29_bl_info = bl_get_data(bl);
91 u32 id = rk29_bl_info->pwm_id;
92 u32 ref = rk29_bl_info->bl_ref;
95 mutex_lock(&backlight_mutex);
96 //BL_CORE_DRIVER2 is the flag if backlight is into early_suspend.
97 if (suspend_flag && (bl->props.state & BL_CORE_DRIVER2))
100 brightness = bl->props.brightness;
101 if(rk29_bl_info->min_brightness){
103 brightness = brightness*(256 - rk29_bl_info->min_brightness);
104 brightness = (brightness>>8) + rk29_bl_info->min_brightness;
110 if (bl->props.power != FB_BLANK_UNBLANK)
113 if (bl->props.fb_blank != FB_BLANK_UNBLANK)
116 if (bl->props.state & BL_CORE_DRIVER3)
119 if ((bl->props.state & BL_CORE_DRIVER2) && !suspend_flag ){
122 }else if(!(bl->props.state & BL_CORE_DRIVER2) && suspend_flag ){
125 div_total = read_pwm_reg(PWM_REG_LRC);
127 divh = div_total*brightness/BL_STEP;
129 divh = div_total*(BL_STEP-brightness)/BL_STEP;
131 rk_pwm_setup(id, PWM_DIV, divh, div_total);
133 //BL_CORE_DRIVER1 is the flag if backlight pwm is closed.
134 if ((bl->props.state & BL_CORE_DRIVER1) && brightness ==0 ){
135 bl->props.state &= ~BL_CORE_DRIVER1;
136 clk_disable(pwm_clk);
137 if (rk29_bl_info->pwm_suspend)
138 rk29_bl_info->pwm_suspend();
139 }else if(!(bl->props.state & BL_CORE_DRIVER1) && brightness != 0){
140 bl->props.state |= BL_CORE_DRIVER1;
141 if (rk29_bl_info->pwm_resume)
142 rk29_bl_info->pwm_resume();
145 div_total = read_pwm_reg(PWM_REG_LRC);
147 divh = div_total*brightness/BL_STEP;
149 divh = div_total*(BL_STEP-brightness)/BL_STEP;
151 rk_pwm_setup(id, PWM_DIV, divh, div_total);
156 DBG("%s:line=%d,brightness = %d, div_total = %d, divh = %d state=%x \n",__FUNCTION__,__LINE__,brightness, div_total, divh,bl->props.state);
158 mutex_unlock(&backlight_mutex);
162 static int rk29_bl_get_brightness(struct backlight_device *bl)
165 struct rk29_bl_info *rk29_bl_info = bl_get_data(bl);
166 u32 ref = rk29_bl_info->bl_ref;
168 div_total = read_pwm_reg(PWM_REG_LRC);
169 divh = read_pwm_reg(PWM_REG_HRC);
175 return BL_STEP*divh/div_total;
177 return BL_STEP-(BL_STEP*divh/div_total);
181 static struct backlight_ops rk29_bl_ops = {
182 .update_status = rk29_bl_update_status,
183 .get_brightness = rk29_bl_get_brightness,
186 static void rk29_backlight_work_func(struct work_struct *work)
188 rk29_bl_update_status(rk29_bl);
190 static DECLARE_DELAYED_WORK(rk29_backlight_work, rk29_backlight_work_func);
192 #ifdef CONFIG_HAS_EARLYSUSPEND
193 static void rk29_bl_suspend(struct early_suspend *h)
195 int brightness = rk29_bl->props.brightness;
197 cancel_delayed_work_sync(&rk29_backlight_work);
199 rk29_bl->props.state |= BL_CORE_DRIVER2;
201 if (rk29_bl->props.brightness) {
202 rk29_bl->props.brightness = 0;
203 rk29_bl_update_status(rk29_bl);
204 rk29_bl->props.brightness = brightness;
209 static void rk29_bl_resume(struct early_suspend *h)
211 struct rk29_bl_info *rk29_bl_info = bl_get_data(rk29_bl);
212 DBG("%s : %s\n", __FILE__, __FUNCTION__);
213 rk29_bl->props.state &= ~BL_CORE_DRIVER2;
215 schedule_delayed_work(&rk29_backlight_work, msecs_to_jiffies(rk29_bl_info->delay_ms));
218 static struct early_suspend bl_early_suspend = {
219 .suspend = rk29_bl_suspend,
220 .resume = rk29_bl_resume,
221 .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 1,
224 bool rk29_get_backlight_status(void)
226 return ((rk29_bl->props.state & BL_CORE_DRIVER3)==BL_CORE_DRIVER3)?true:false;
228 EXPORT_SYMBOL(rk29_get_backlight_status);
230 void rk29_backlight_set(bool on)
232 printk("%s: set %d\n", __func__, on);
234 rk29_bl->props.state &= ~BL_CORE_DRIVER3;
235 rk29_bl_update_status(rk29_bl);
237 rk29_bl->props.state |= BL_CORE_DRIVER3;
238 rk29_bl_update_status(rk29_bl);
242 EXPORT_SYMBOL(rk29_backlight_set);
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;
251 unsigned long pwm_clk_rate;
252 struct backlight_properties props;
253 int pre_div = PWM_APB_PRE_DIV;
256 printk(KERN_CRIT "%s: backlight device register has existed \n",
261 if (!rk29_bl_info->delay_ms)
262 rk29_bl_info->delay_ms = 100;
264 if (rk29_bl_info->min_brightness < 0 || rk29_bl_info->min_brightness > BL_STEP)
265 rk29_bl_info->min_brightness = 52;
267 if (rk29_bl_info && rk29_bl_info->io_init) {
268 rk29_bl_info->io_init();
271 if(rk29_bl_info->pre_div > 0)
272 pre_div = rk29_bl_info->pre_div;
274 memset(&props, 0, sizeof(struct backlight_properties));
275 props.type = BACKLIGHT_RAW;
276 props.max_brightness = BL_STEP;
277 rk29_bl = backlight_device_register("rk28_bl", &pdev->dev, rk29_bl_info, &rk29_bl_ops, &props);
279 printk(KERN_CRIT "%s: backlight device register error\n",
284 pwm_base = rk_pwm_get_base(id);
285 pwm_clk = rk_pwm_get_clk(id);
286 if (IS_ERR(pwm_clk) || !pwm_clk) {
287 printk(KERN_ERR "failed to get pwm clock source\n");
290 pwm_clk_rate = clk_get_rate(pwm_clk);
291 div_total = pwm_clk_rate / pre_div;
293 div_total >>= (1 + (PWM_DIV >> 9));
294 div_total = (div_total) ? div_total : 1;
296 if(rk29_bl_info->bl_ref) {
303 rk_pwm_setup(id, PWM_DIV, divh, div_total);
305 rk29_bl->props.power = FB_BLANK_UNBLANK;
306 rk29_bl->props.fb_blank = FB_BLANK_UNBLANK;
307 rk29_bl->props.brightness = BL_STEP / 2;
308 rk29_bl->props.state = BL_CORE_DRIVER1;
310 schedule_delayed_work(&rk29_backlight_work, msecs_to_jiffies(rk29_bl_info->delay_ms));
311 ret = device_create_file(&pdev->dev,&dev_attr_rk29backlight);
314 dev_err(&pdev->dev, "failed to create sysfs file\n");
317 register_early_suspend(&bl_early_suspend);
319 printk("RK29 Backlight Driver Initialized.\n");
323 static int rk29_backlight_remove(struct platform_device *pdev)
325 struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
328 backlight_device_unregister(rk29_bl);
329 unregister_early_suspend(&bl_early_suspend);
330 clk_disable(pwm_clk);
332 if (rk29_bl_info && rk29_bl_info->io_deinit) {
333 rk29_bl_info->io_deinit();
337 DBG(KERN_CRIT "%s: no backlight device has registered\n",
343 static void rk29_backlight_shutdown(struct platform_device *pdev)
345 struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
347 unregister_early_suspend(&bl_early_suspend);
349 rk29_bl->props.brightness >>= 1;
350 rk29_bl_update_status(rk29_bl);
353 rk29_bl->props.brightness >>= 1;
354 rk29_bl_update_status(rk29_bl);
357 rk29_bl->props.brightness = 0;
358 rk29_bl_update_status(rk29_bl);
360 if (rk29_bl_info && rk29_bl_info->io_deinit)
361 rk29_bl_info->io_deinit();
364 static struct platform_driver rk29_backlight_driver = {
365 .probe = rk29_backlight_probe,
366 .remove = rk29_backlight_remove,
368 .name = "rk29_backlight",
369 .owner = THIS_MODULE,
371 .shutdown = rk29_backlight_shutdown,
374 static int __init rk29_backlight_init(void)
376 platform_driver_register(&rk29_backlight_driver);
379 fs_initcall_sync(rk29_backlight_init);