add rk29 backlight

This commit is contained in:
lhh
2010-11-17 11:16:54 +08:00
parent ce9b786556
commit 3d675c5562
10 changed files with 492 additions and 4 deletions

View File

@@ -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

View File

@@ -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[] = {

View File

@@ -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

View File

@@ -16,6 +16,8 @@
#define __ASM_ARCH_RK29_BOARD_H
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/notifier.h>
/*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;

View File

@@ -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

View File

@@ -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),

View File

@@ -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.

View File

@@ -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

View File

@@ -1,5 +1,5 @@
/*
* linux/include/asm-arm/arch-rockchip/pwm.h
* drivers/video/rk29_backlight.h
*
*/

View File

@@ -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 <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/debugfs.h>
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/cpufreq.h>
#include <linux/clk.h>
#include <linux/earlysuspend.h>
#include <asm/io.h>
//#include <mach/typedef.h>
#include <mach/iomux.h>
#include <mach/gpio.h>
#include <mach/rk29_iomap.h>
#include <mach/board.h>
#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);