1 /* arch/arm/mach-rockchip/rk28_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 #ifdef CONFIG_ANDROID_POWER
29 #include <linux/android_power.h>
32 //#include <mach/typedef.h>
33 #include <mach/iomux.h>
34 #include <mach/gpio.h>
35 #include <mach/rk2818_iomap.h>
36 #include <mach/board.h>
38 #include "rk2818_backlight.h"
41 //#include <mach/rk2818_debug.h>
47 #define DBG(x...) printk(KERN_INFO x)
53 #define write_pwm_reg(id, addr, val) __raw_writel(val, addr+(RK2818_PWM_BASE+id*0x10))
54 #define read_pwm_reg(id, addr) __raw_readl(addr+(RK2818_PWM_BASE+id*0x10))
55 #define mask_pwm_reg(id, addr, msk, val) write_dma_reg(id, addr, (val)|((~(msk))&read_dma_reg(id, addr)))
58 static struct backlight_device *rk2818_bl = NULL;
59 static int suspend_flag = 0;
60 #define BACKLIGHT_SEE_MINVALUE 52
62 static s32 rk2818_bl_update_status(struct backlight_device *bl)
65 struct rk2818bl_info *rk2818_bl_info = bl->dev.parent->platform_data;
66 u32 id = rk2818_bl_info->pwm_id;
67 u32 ref = rk2818_bl_info->bl_ref;
72 div_total = read_pwm_reg(id, PWM_REG_LRC);
74 DBG(">>>%s-->%d bl->props.brightness == %d\n",__FUNCTION__,__LINE__,bl->props.brightness);
75 divh = div_total*(bl->props.brightness)/BL_STEP;
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);
83 DBG("%s::========================================\n",__func__);
87 static s32 rk2818_bl_get_brightness(struct backlight_device *bl)
90 struct rk2818bl_info *rk2818_bl_info = bl->dev.parent->platform_data;
91 u32 id = rk2818_bl_info->pwm_id;
92 u32 ref = rk2818_bl_info->bl_ref;
94 div_total = read_pwm_reg(id, PWM_REG_LRC);
95 divh = read_pwm_reg(id, PWM_REG_HRC);
97 DBG("%s::========================================\n",__func__);
100 return BL_STEP*divh/div_total;
102 return BL_STEP-(BL_STEP*divh/div_total);
106 static struct backlight_ops rk2818_bl_ops = {
107 .update_status = rk2818_bl_update_status,
108 .get_brightness = rk2818_bl_get_brightness,
112 static int rk2818_bl_change_clk(struct notifier_block *nb, unsigned long val, void *data)
114 struct rk2818bl_info *rk2818_bl_info;
122 DBG(KERN_CRIT "%s: backlight device does not exist \n",
129 case CPUFREQ_PRECHANGE:
131 case CPUFREQ_POSTCHANGE:
132 rk2818_bl_info = rk2818_bl->dev.parent->platform_data;
133 id = rk2818_bl_info->pwm_id;
135 pclk = clk_get(NULL, "arm_pclk");
136 if (!pclk || IS_ERR(pclk))
138 printk(KERN_ERR "%s, %s, failed to get lcd clock source\n",__FILE__, __FUNCTION__);
142 divl = read_pwm_reg(id, PWM_REG_LRC);
143 divh = read_pwm_reg(id, PWM_REG_HRC);
145 tmp = clk_get_rate(pclk)/PWM_APB_PRE_DIV;
146 tmp >>= (1 + (PWM_DIV >> 9));
150 div_total = (tmp) ? tmp : 1;
151 tmp = div_total*divh/divl;
153 write_pwm_reg(id, PWM_REG_LRC, div_total);
154 write_pwm_reg(id, PWM_REG_HRC, tmp);
155 write_pwm_reg(id, PWM_REG_CNTR, 0);
161 static void rk2818_delaybacklight_timer(unsigned long data)
163 struct rk2818bl_info *rk2818_bl_info = (struct rk2818bl_info *)data;
166 id = rk2818_bl_info->pwm_id;
167 brightness = rk2818_bl->props.brightness;
168 div_total = read_pwm_reg(id, PWM_REG_LRC);
169 if (rk2818_bl_info->bl_ref) {
170 divh = div_total*(brightness)/BL_STEP;
172 divh = div_total*(BL_STEP-brightness)/BL_STEP;
174 write_pwm_reg(id, PWM_REG_HRC, divh);
176 DBG("%s: ======================== \n",__func__);
179 #ifdef CONFIG_ANDROID_POWER
180 static void rk2818_bl_suspend(android_early_suspend_t *h)
182 struct rk2818bl_info *rk2818_bl_info;
186 rk2818_bl_info = rk2818_bl->dev.parent->platform_data;
188 id = rk2818_bl_info->pwm_id;
190 div_total = read_pwm_reg(id, PWM_REG_LRC);
192 if(rk2818_bl_info->bl_ref) {
198 write_pwm_reg(id, PWM_REG_HRC, divh);
202 DBG("%s: ========================= \n",__func__);
206 static void rk2818_bl_resume(android_early_suspend_t *h)
208 struct rk2818bl_info *rk2818_bl_info;
209 // u32 id, brightness;
210 //u32 div_total, divh;
211 DBG(">>>>>> %s : %s\n", __FILE__, __FUNCTION__);
212 rk2818_bl_info = rk2818_bl->dev.parent->platform_data;
214 rk2818_bl_info->timer.expires = jiffies + 30;
215 add_timer(&rk2818_bl_info->timer);
217 id = rk28_bl_info->pwm_id;
218 brightness = rk28_bl->props.brightness;
220 div_total = read_pwm_reg(id, PWM_REG_LRC);
221 if (rk28_bl_info->bl_ref) {
222 divh = div_total*(brightness)/BL_STEP;
224 divh = div_total*(BL_STEP-brightness)/BL_STEP;
227 write_pwm_reg(id, PWM_REG_HRC, divh);
231 rk28printk("%s: ======================== \n",__func__);
235 static android_early_suspend_t bl_early_suspend;
238 static char *pwm_iomux[] = {
239 GPIOF2_APWM0_SEL_NAME,
240 GPIOF3_APWM1_MMC0DETN_NAME,
241 GPIOF4_APWM2_MMC0WPT_NAME,
242 GPIOF5_APWM3_DPWM3_NAME,
245 static int rk2818_backlight_probe(struct platform_device *pdev)
248 struct rk2818bl_info *rk2818_bl_info = pdev->dev.platform_data;
249 u32 pin = (rk2818_bl_info->pw_pin >> 8) & 0xff;
250 u32 lev = rk2818_bl_info->pw_pin & 0xf;
251 u32 id = rk2818_bl_info->pwm_id;
253 struct clk* arm_pclk;
255 DBG("%s::========================================\n",__func__);
258 DBG(KERN_CRIT "%s: backlight device register has existed \n",
263 rk2818_bl = backlight_device_register("rk28_bl", &pdev->dev, NULL, &rk2818_bl_ops);
265 DBG(KERN_CRIT "%s: backlight device register error\n",
270 arm_pclk = clk_get(NULL, "arm_pclk");
271 if (!arm_pclk || IS_ERR(arm_pclk))
273 printk(KERN_ERR "failed to get lcd clock source\n");
276 div_total = clk_get_rate(arm_pclk)/PWM_APB_PRE_DIV;
279 div_total >>= (1 + (PWM_DIV >> 9));
280 div_total = (div_total) ? div_total : 1;
282 if(rk2818_bl_info->bl_ref) {
285 divh = div_total / 2;
288 /*init timer to dispose workqueue */
289 setup_timer(&rk2818_bl_info->timer, rk2818_delaybacklight_timer, (unsigned long)rk2818_bl_info);
291 rk2818_bl_info->freq_transition.notifier_call = rk2818_bl_change_clk;
292 cpufreq_register_notifier(&rk2818_bl_info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
294 write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_RESET);
295 write_pwm_reg(id, PWM_REG_LRC, div_total);
296 write_pwm_reg(id, PWM_REG_HRC, divh);
297 write_pwm_reg(id, PWM_REG_CNTR, 0x0);
298 write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_ENABLE|PWM_TIME_EN);
300 rk2818_bl->props.power = FB_BLANK_UNBLANK;
301 rk2818_bl->props.fb_blank = FB_BLANK_UNBLANK;
302 rk2818_bl->props.max_brightness = BL_STEP;
303 rk2818_bl->props.brightness = rk2818_bl_get_brightness(rk2818_bl);
305 #ifdef CONFIG_ANDROID_POWER
306 bl_early_suspend.suspend = rk2818_bl_suspend;
307 bl_early_suspend.resume = rk2818_bl_resume;
308 bl_early_suspend.level = ~0x0;
309 android_register_early_suspend(&bl_early_suspend);
312 rk2818_mux_api_set(pwm_iomux[id], 1);
314 if(rk2818_bl_info->pw_iomux)
316 rk2818_mux_api_set(rk2818_bl_info->pw_iomux, 0);
319 ret = gpio_request(pin, NULL);
323 printk(KERN_ERR ">>>>>> lcd_cs gpio_request err \n ");
326 gpio_direction_output(pin, 0);
327 gpio_set_value(pin, lev);
332 static int rk2818_backlight_remove(struct platform_device *pdev)
334 struct rk2818bl_info *rk2818_bl_info = pdev->dev.platform_data;
335 u32 pin = (rk2818_bl_info->pw_pin >> 8) & 0xff;
338 backlight_device_unregister(rk2818_bl);
339 #ifdef CONFIG_ANDROID_POWER
340 android_unregister_early_suspend(&bl_early_suspend);
342 cpufreq_unregister_notifier(&rk2818_bl_info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
346 DBG(KERN_CRIT "%s: no backlight device has registered\n",
351 static void rk2818_backlight_shutdown(struct platform_device *pdev)
355 struct rk2818bl_info *rk2818_bl_info = pdev->dev.platform_data;
356 u32 id = rk2818_bl_info->pwm_id;
357 u32 pin=rk2818_bl_info->pw_pin;
360 brightness = rk2818_bl->props.brightness;
362 div_total = read_pwm_reg(id, PWM_REG_LRC);
363 if (rk2818_bl_info->bl_ref) {
364 divh = div_total*(brightness)/BL_STEP;
366 divh = div_total*(BL_STEP-brightness)/BL_STEP;
368 write_pwm_reg(id, PWM_REG_HRC, divh);
369 //printk("divh=%d\n",divh);
372 if (rk2818_bl_info->bl_ref) {
373 divh = div_total*(brightness)/BL_STEP;
375 divh = div_total*(BL_STEP-brightness)/BL_STEP;
377 //printk("------------rk28_backlight_shutdown mdelay----------------------------\n");
378 write_pwm_reg(id, PWM_REG_HRC, divh);
380 /*set PF1=1 PF2=1 for close backlight*/
382 if(rk2818_bl_info->bl_ref) {
387 gpio_set_value(pin, lev);
391 static struct platform_driver rk2818_backlight_driver = {
392 .probe = rk2818_backlight_probe,
393 .remove = rk2818_backlight_remove,
395 .name = "rk2818_backlight",
396 .owner = THIS_MODULE,
398 .shutdown=rk2818_backlight_shutdown,
402 static int __init rk2818_backlight_init(void)
404 DBG("%s::========================================\n",__func__);
405 platform_driver_register(&rk2818_backlight_driver);
408 rootfs_initcall(rk2818_backlight_init);
410 //late_initcall(rk28_backlight_init);
411 //module_init(rk28_backlight_init);