From 3d675c556249fa6ca71195f2a1dbc7be95b56e56 Mon Sep 17 00:00:00 2001 From: lhh Date: Wed, 17 Nov 2010 11:16:54 +0800 Subject: [PATCH] add rk29 backlight --- arch/arm/mach-rk29/board-rk29sdk.c | 66 ++++ arch/arm/mach-rk29/devices.c | 13 +- arch/arm/mach-rk29/devices.h | 2 + arch/arm/mach-rk29/include/mach/board.h | 11 + arch/arm/mach-rk29/include/mach/rk29_iomap.h | 1 + arch/arm/mach-rk29/io.c | 1 + drivers/video/backlight/Kconfig | 9 +- drivers/video/backlight/Makefile | 2 +- drivers/video/backlight/rk2818_backlight.h | 2 +- drivers/video/backlight/rk29_backlight.c | 389 +++++++++++++++++++ 10 files changed, 492 insertions(+), 4 deletions(-) create mode 100644 drivers/video/backlight/rk29_backlight.c diff --git a/arch/arm/mach-rk29/board-rk29sdk.c b/arch/arm/mach-rk29/board-rk29sdk.c index cb6a04796045..ee2c8b23408e 100755 --- a/arch/arm/mach-rk29/board-rk29sdk.c +++ b/arch/arm/mach-rk29/board-rk29sdk.c @@ -430,6 +430,69 @@ static struct i2c_board_info __initdata board_i2c3_devices[] = { #endif +/***************************************************************************************** + * backlight devices + * author: nzy@rock-chips.com + *****************************************************************************************/ +#ifdef CONFIG_BACKLIGHT_RK29_BL + /* + GPIO1B5_PWM0_NAME, GPIO1L_PWM0 + GPIO5D2_PWM1_UART1SIRIN_NAME, GPIO5H_PWM1 + GPIO2A3_SDMMC0WRITEPRT_PWM2_NAME, GPIO2L_PWM2 + GPIO1A5_EMMCPWREN_PWM3_NAME, GPIO1L_PWM3 + */ + +#define PWM_ID 0 +#define PWM_MUX_NAME GPIO1B5_PWM0_NAME +#define PWM_MUX_MODE GPIO1L_PWM0 +#define PWM_MUX_MODE_GPIO GPIO1L_GPIO1B5 +#define PWM_EFFECT_VALUE 0 + +//#define LCD_DISP_ON_PIN + +#ifdef LCD_DISP_ON_PIN +#define BL_EN_MUX_NAME GPIOF34_UART3_SEL_NAME +#define BL_EN_MUX_MODE IOMUXB_GPIO1_B34 + +#define BL_EN_PIN GPIO0L_GPIO0A5 +#define BL_EN_VALUE GPIO_HIGH +#endif +static int rk29_backlight_io_init(void) +{ + int ret = 0; + + rk29_mux_api_set(PWM_MUX_NAME, PWM_MUX_MODE); + #ifdef LCD_DISP_ON_PIN + rk29_mux_api_set(BL_EN_MUX_NAME, BL_EN_MUX_MODE); + + ret = gpio_request(BL_EN_PIN, NULL); + if(ret != 0) + { + gpio_free(BL_EN_PIN); + } + + gpio_direction_output(BL_EN_PIN, 0); + gpio_set_value(BL_EN_PIN, BL_EN_VALUE); + #endif + return ret; +} + +static int rk29_backlight_io_deinit(void) +{ + int ret = 0; + #ifdef LCD_DISP_ON_PIN + gpio_free(BL_EN_PIN); + #endif + rk29_mux_api_set(PWM_MUX_NAME, PWM_MUX_MODE_GPIO); + return ret; +} +struct rk29_bl_info rk29_bl_info = { + .pwm_id = PWM_ID, + .bl_ref = PWM_EFFECT_VALUE, + .io_init = rk29_backlight_io_init, + .io_deinit = rk29_backlight_io_deinit, +}; +#endif /***************************************************************************************** * SDMMC devices *****************************************************************************************/ @@ -590,6 +653,9 @@ static struct platform_device *devices[] __initdata = { #ifdef CONFIG_FB_RK29 &rk29_device_fb, #endif +#ifdef CONFIG_BACKLIGHT_RK29_BL + &rk29_device_backlight, +#endif #ifdef CONFIG_VIVANTE &rk29_device_gpu, #endif diff --git a/arch/arm/mach-rk29/devices.c b/arch/arm/mach-rk29/devices.c index c505683e082d..333735f66229 100755 --- a/arch/arm/mach-rk29/devices.c +++ b/arch/arm/mach-rk29/devices.c @@ -132,7 +132,18 @@ struct platform_device rk29_device_i2c3 = { }; #endif - +/*********************************************************** +* backlight +***************************************************************/ +#ifdef CONFIG_BACKLIGHT_RK29_BL +struct platform_device rk29_device_backlight = { + .name = "rk29_backlight", + .id = -1, + .dev = { + .platform_data = &rk29_bl_info, + } +}; +#endif #ifdef CONFIG_SDMMC0_RK29 #ifndef CONFIG_EMMC_RK29 static struct resource resources_sdmmc0[] = { diff --git a/arch/arm/mach-rk29/devices.h b/arch/arm/mach-rk29/devices.h index 8e476673f714..d202ce311b50 100755 --- a/arch/arm/mach-rk29/devices.h +++ b/arch/arm/mach-rk29/devices.h @@ -40,5 +40,7 @@ extern struct rk29_sdmmc_platform_data default_sdmmc1_data; extern struct platform_device rk29_device_sdmmc0; extern struct platform_device rk29_device_sdmmc1; extern struct platform_device rk29_device_adc; +extern struct rk29_bl_info rk29_bl_info; +extern struct platform_device rk29_device_backlight; #endif diff --git a/arch/arm/mach-rk29/include/mach/board.h b/arch/arm/mach-rk29/include/mach/board.h index b7b1160afbdd..17dbf18b01ab 100755 --- a/arch/arm/mach-rk29/include/mach/board.h +++ b/arch/arm/mach-rk29/include/mach/board.h @@ -16,6 +16,8 @@ #define __ASM_ARCH_RK29_BOARD_H #include +#include +#include /*spi*/ struct spi_cs_gpio { @@ -66,6 +68,15 @@ struct rk29fb_info{ int (*io_deinit)(void); }; +struct rk29_bl_info{ + u32 pwm_id; + u32 bl_ref; + int (*io_init)(void); + int (*io_deinit)(void); + struct timer_list timer; + struct notifier_block freq_transition; +}; + struct rk29_sdmmc_platform_data { unsigned int num_slots; unsigned int host_caps; diff --git a/arch/arm/mach-rk29/include/mach/rk29_iomap.h b/arch/arm/mach-rk29/include/mach/rk29_iomap.h index bea219587a5d..0f6193e6cb13 100644 --- a/arch/arm/mach-rk29/include/mach/rk29_iomap.h +++ b/arch/arm/mach-rk29/include/mach/rk29_iomap.h @@ -148,6 +148,7 @@ #define RK29_TIMER3_SIZE SZ_16K #define RK29_WDT_PHYS 0x2004C000 #define RK29_WDT_SIZE SZ_16K +#define RK29_PWM_BASE (RK29_ADDR_BASE1+0x50000) #define RK29_PWM_PHYS 0x20050000 #define RK29_PWM_SIZE SZ_16K #define RK29_I2C1_PHYS 0x20054000 diff --git a/arch/arm/mach-rk29/io.c b/arch/arm/mach-rk29/io.c index 9f681ccb3b94..a56d8894267a 100644 --- a/arch/arm/mach-rk29/io.c +++ b/arch/arm/mach-rk29/io.c @@ -36,6 +36,7 @@ static struct map_desc rk29_io_desc[] __initdata = { RK29_DEVICE(TIMER1), RK29_DEVICE(DDRC), RK29_DEVICE(UART1), + RK29_DEVICE(PWM), RK29_DEVICE(GRF), RK29_DEVICE(CRU), RK29_DEVICE(GPIO0), diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 9061330841aa..bb85a9067202 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -264,7 +264,14 @@ config BACKLIGHT_ADP5520 config BACKLIGHT_RK2818_BL bool "rk2818 backlight driver" - depends on BACKLIGHT_CLASS_DEVICE + depends on BACKLIGHT_CLASS_DEVICE && ARCH_RK2818 default y help rk2818 backlight support. + +config BACKLIGHT_RK29_BL + bool "rk29 backlight driver" + depends on BACKLIGHT_CLASS_DEVICE && ARCH_RK29 + default y + help + rk29 backlight support. diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 1739ab2b389a..096d9731d907 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -29,4 +29,4 @@ obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o obj-$(CONFIG_BACKLIGHT_ADX) += adx_bl.o obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o obj-$(CONFIG_BACKLIGHT_RK2818_BL) += rk2818_backlight.o - +obj-$(CONFIG_BACKLIGHT_RK29_BL) += rk29_backlight.o diff --git a/drivers/video/backlight/rk2818_backlight.h b/drivers/video/backlight/rk2818_backlight.h index 1b68a039bdd5..0ce4039e9a9d 100644 --- a/drivers/video/backlight/rk2818_backlight.h +++ b/drivers/video/backlight/rk2818_backlight.h @@ -1,5 +1,5 @@ /* - * linux/include/asm-arm/arch-rockchip/pwm.h + * drivers/video/rk29_backlight.h * */ diff --git a/drivers/video/backlight/rk29_backlight.c b/drivers/video/backlight/rk29_backlight.c new file mode 100644 index 000000000000..a54f7d925abb --- /dev/null +++ b/drivers/video/backlight/rk29_backlight.c @@ -0,0 +1,389 @@ +/* drivers/video/backlight/rk29_backlight.c + * + * Copyright (C) 2009 Rockchip Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +//#include +#include +#include +#include +#include + +#include "rk2818_backlight.h" + +/* + * Debug + */ +#if 0 +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) +#endif + + +#define write_pwm_reg(id, addr, val) __raw_writel(val, addr+(RK29_PWM_BASE+id*0x10)) +#define read_pwm_reg(id, addr) __raw_readl(addr+(RK29_PWM_BASE+id*0x10)) +#define mask_pwm_reg(id, addr, msk, val) write_dma_reg(id, addr, (val)|((~(msk))&read_dma_reg(id, addr))) + +static struct clk *pwm_clk; +static unsigned long pwm_clk_rate; +static struct backlight_device *rk29_bl; +static int suspend_flag = 0; +#define BACKLIGHT_SEE_MINVALUE 52 + +static s32 rk29_bl_update_status(struct backlight_device *bl) +{ + u32 divh,div_total; + struct rk29_bl_info *rk29_bl_info = bl->dev.parent->platform_data; + u32 id = rk29_bl_info->pwm_id; + u32 ref = rk29_bl_info->bl_ref; + + if (suspend_flag) + return 0; + + div_total = read_pwm_reg(id, PWM_REG_LRC); + if (ref) { + + divh = div_total*(bl->props.brightness)/BL_STEP; + DBG(">>>%s-->%d bl->props.brightness == %d, div_total == %d , divh == %d\n",__FUNCTION__,__LINE__,bl->props.brightness, div_total, divh); + } else { + DBG(">>>%s-->%d bl->props.brightness == %d\n",__FUNCTION__,__LINE__,bl->props.brightness); + if(bl->props.brightness < BACKLIGHT_SEE_MINVALUE) /*avoid can't view screen when close backlight*/ + bl->props.brightness = BACKLIGHT_SEE_MINVALUE; + divh = div_total*(BL_STEP-bl->props.brightness)/BL_STEP; + } + write_pwm_reg(id, PWM_REG_HRC, divh); + return 0; +} + +static s32 rk29_bl_get_brightness(struct backlight_device *bl) +{ + u32 divh,div_total; + struct rk29_bl_info *rk29_bl_info = bl->dev.parent->platform_data; + u32 id = rk29_bl_info->pwm_id; + u32 ref = rk29_bl_info->bl_ref; + + div_total = read_pwm_reg(id, PWM_REG_LRC); + divh = read_pwm_reg(id, PWM_REG_HRC); + + DBG("%s:: %d div_total: %d\n",__func__,__LINE__,div_total); + + if (ref) { + return BL_STEP*divh/div_total; + } else { + return BL_STEP-(BL_STEP*divh/div_total); + } +} + +static struct backlight_ops rk29_bl_ops = { + .update_status = rk29_bl_update_status, + .get_brightness = rk29_bl_get_brightness, +}; + +#ifdef CONFIG_CPU_FREQ +static int rk29_bl_change_clk(struct notifier_block *nb, unsigned long val, void *data) +{ + struct rk29_bl_info *rk29_bl_info; + u32 id; + u32 divl, divh, tmp; + u32 div_total; + int is_suspended = suspend_flag; + + if (!rk29_bl) { + DBG(KERN_CRIT "%s: backlight device does not exist\n", __func__); + return -ENODEV; + } + + switch (val) { + case CPUFREQ_PRECHANGE: + break; + case CPUFREQ_POSTCHANGE: + if (clk_get_rate(pwm_clk) == pwm_clk_rate) + break; + pwm_clk_rate = clk_get_rate(pwm_clk); + rk29_bl_info = rk29_bl->dev.parent->platform_data; + id = rk29_bl_info->pwm_id; + + + + divl = read_pwm_reg(id, PWM_REG_LRC); + divh = read_pwm_reg(id, PWM_REG_HRC); + + tmp = pwm_clk_rate / PWM_APB_PRE_DIV; + tmp >>= (1 + (PWM_DIV >> 9)); + + + div_total = (tmp) ? tmp : 1; + tmp = div_total*divh/divl; + + if (!is_suspended) + clk_disable(pwm_clk); + write_pwm_reg(id, PWM_REG_LRC, div_total); + write_pwm_reg(id, PWM_REG_HRC, tmp); + write_pwm_reg(id, PWM_REG_CNTR, 0); + if (!is_suspended) + clk_enable(pwm_clk); + break; + } + + return 0; +} +#endif +static void rk29_delaybacklight_timer(unsigned long data) +{ + struct rk29_bl_info *rk29_bl_info = (struct rk29_bl_info *)data; + u32 id, brightness; + u32 div_total, divh; + clk_enable(pwm_clk); + id = rk29_bl_info->pwm_id; + brightness = rk29_bl->props.brightness; + div_total = read_pwm_reg(id, PWM_REG_LRC); + if (rk29_bl_info->bl_ref) { + divh = div_total*(brightness)/BL_STEP; + } else { + divh = div_total*(BL_STEP-brightness)/BL_STEP; + } + write_pwm_reg(id, PWM_REG_HRC, divh); + suspend_flag = 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void rk29_bl_suspend(struct early_suspend *h) +{ + struct rk29_bl_info *rk29_bl_info; + u32 id; + u32 div_total, divh; + + rk29_bl_info = rk29_bl->dev.parent->platform_data; + + id = rk29_bl_info->pwm_id; + + div_total = read_pwm_reg(id, PWM_REG_LRC); + + if(rk29_bl_info->bl_ref) { + divh = 0; + } else { + divh = div_total; + } + + write_pwm_reg(id, PWM_REG_HRC, divh); + if (!suspend_flag) + clk_disable(pwm_clk); + + suspend_flag = 1; +} + + +static void rk29_bl_resume(struct early_suspend *h) +{ + struct rk29_bl_info *rk29_bl_info; + // u32 id, brightness; + //u32 div_total, divh; + DBG("%s : %s\n", __FILE__, __FUNCTION__); + rk29_bl_info = rk29_bl->dev.parent->platform_data; + + rk29_bl_info->timer.expires = jiffies + 30; + add_timer(&rk29_bl_info->timer); + #if 0 + id = rk28_bl_info->pwm_id; + brightness = rk28_bl->props.brightness; + + div_total = read_pwm_reg(id, PWM_REG_LRC); + if (rk28_bl_info->bl_ref) { + divh = div_total*(brightness)/BL_STEP; + } else { + divh = div_total*(BL_STEP-brightness)/BL_STEP; + } + //mdelay(100); + write_pwm_reg(id, PWM_REG_HRC, divh); + + suspend_flag = 0; + #endif +} + +static struct early_suspend bl_early_suspend; +#endif + +static int rk29_backlight_probe(struct platform_device *pdev) +{ + int ret = 0; + struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data; + u32 id = rk29_bl_info->pwm_id; + u32 divh, div_total; + + if (rk29_bl) { + DBG(KERN_CRIT "%s: backlight device register has existed \n", + __func__); + return -EEXIST; + } + + rk29_bl = backlight_device_register("rk28_bl", &pdev->dev, NULL, &rk29_bl_ops); + if (!rk29_bl) { + DBG(KERN_CRIT "%s: backlight device register error\n", + __func__); + return -ENODEV; + } + + if (!pwm_clk) + pwm_clk = clk_get(NULL, "pwm"); + if (!pwm_clk || IS_ERR(pwm_clk)) { + printk(KERN_ERR "failed to get pwm clock source\n"); + return -ENODEV; + } + pwm_clk_rate = clk_get_rate(pwm_clk); + div_total = pwm_clk_rate / PWM_APB_PRE_DIV; + + + + div_total >>= (1 + (PWM_DIV >> 9)); + div_total = (div_total) ? div_total : 1; + + /// if(rk29_bl_info->bl_ref) { + /// divh = 0; + /// } else { + divh = div_total / 2; + // } + + /*init timer to dispose workqueue */ + setup_timer(&rk29_bl_info->timer, rk29_delaybacklight_timer, (unsigned long)rk29_bl_info); + +#ifdef CONFIG_CPU_FREQ + rk29_bl_info->freq_transition.notifier_call = rk29_bl_change_clk; + cpufreq_register_notifier(&rk29_bl_info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); +#endif + +// clk_disable(pwm_clk); + write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_RESET); + write_pwm_reg(id, PWM_REG_LRC, div_total); + write_pwm_reg(id, PWM_REG_HRC, divh); + write_pwm_reg(id, PWM_REG_CNTR, 0x0); + write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_ENABLE|PWM_TIME_EN); + clk_enable(pwm_clk); + + rk29_bl->props.power = FB_BLANK_UNBLANK; + rk29_bl->props.fb_blank = FB_BLANK_UNBLANK; + rk29_bl->props.max_brightness = BL_STEP; + rk29_bl->props.brightness = rk29_bl_get_brightness(rk29_bl); + +#ifdef CONFIG_HAS_EARLYSUSPEND + bl_early_suspend.suspend = rk29_bl_suspend; + bl_early_suspend.resume = rk29_bl_resume; + bl_early_suspend.level = ~0x0; + register_early_suspend(&bl_early_suspend); +#endif + + if (rk29_bl_info && rk29_bl_info->io_init) { + rk29_bl_info->io_init(); + } + + return ret; +} + +static int rk29_backlight_remove(struct platform_device *pdev) +{ + struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data; + + if (rk29_bl) { + backlight_device_unregister(rk29_bl); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&bl_early_suspend); +#endif +#ifdef CONFIG_CPU_FREQ + cpufreq_unregister_notifier(&rk29_bl_info->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); +#endif + clk_disable(pwm_clk); + clk_put(pwm_clk); + if (rk29_bl_info && rk29_bl_info->io_deinit) { + rk29_bl_info->io_deinit(); + } + return 0; + } else { + DBG(KERN_CRIT "%s: no backlight device has registered\n", + __func__); + return -ENODEV; + } +} +static void rk29_backlight_shutdown(struct platform_device *pdev) +{ + + u32 divh,div_total; + struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data; + u32 id = rk29_bl_info->pwm_id; + u32 brightness; + brightness = rk29_bl->props.brightness; + brightness/=2; + div_total = read_pwm_reg(id, PWM_REG_LRC); + + if (rk29_bl_info->bl_ref) { + divh = div_total*(brightness)/BL_STEP; + } else { + divh = div_total*(BL_STEP-brightness)/BL_STEP; + } + write_pwm_reg(id, PWM_REG_HRC, divh); + //printk("divh=%d\n",divh); + mdelay(100); + + brightness/=2; + if (rk29_bl_info->bl_ref) { + divh = div_total*(brightness)/BL_STEP; + } else { + divh = div_total*(BL_STEP-brightness)/BL_STEP; + } + + write_pwm_reg(id, PWM_REG_HRC, divh); + mdelay(100); + /*set PF1=1 PF2=1 for close backlight*/ + + if(rk29_bl_info->bl_ref) { + divh = 0; + } else { + divh = div_total; + } + write_pwm_reg(id, PWM_REG_HRC, divh); + +} + +static struct platform_driver rk29_backlight_driver = { + .probe = rk29_backlight_probe, + .remove = rk29_backlight_remove, + .driver = { + .name = "rk29_backlight", + .owner = THIS_MODULE, + }, + .shutdown=rk29_backlight_shutdown, +}; + + +static int __init rk29_backlight_init(void) +{ + platform_driver_register(&rk29_backlight_driver); + return 0; +} +//rootfs_initcall(rk29_backlight_init); + +late_initcall(rk29_backlight_init); +//module_init(rk29_backlight_init); -- 2.34.1