add rk29 backlight
authorlhh <lhh@rock-chips.com>
Wed, 17 Nov 2010 03:16:54 +0000 (11:16 +0800)
committerlhh <lhh@rock-chips.com>
Wed, 17 Nov 2010 03:16:54 +0000 (11:16 +0800)
arch/arm/mach-rk29/board-rk29sdk.c
arch/arm/mach-rk29/devices.c
arch/arm/mach-rk29/devices.h
arch/arm/mach-rk29/include/mach/board.h
arch/arm/mach-rk29/include/mach/rk29_iomap.h
arch/arm/mach-rk29/io.c
drivers/video/backlight/Kconfig
drivers/video/backlight/Makefile
drivers/video/backlight/rk2818_backlight.h
drivers/video/backlight/rk29_backlight.c [new file with mode: 0644]

index cb6a04796045b934ef427186462f99251d85d0eb..ee2c8b23408e1bcca88c625c35833535b4cf2d1d 100755 (executable)
@@ -430,6 +430,69 @@ static struct i2c_board_info __initdata board_i2c3_devices[] = {
 #endif\r
 \r
 \r
+/*****************************************************************************************\r
+ * backlight  devices\r
+ * author: nzy@rock-chips.com\r
+ *****************************************************************************************/\r
+#ifdef CONFIG_BACKLIGHT_RK29_BL\r
+ /*\r
+ GPIO1B5_PWM0_NAME,       GPIO1L_PWM0\r
+ GPIO5D2_PWM1_UART1SIRIN_NAME,  GPIO5H_PWM1\r
+ GPIO2A3_SDMMC0WRITEPRT_PWM2_NAME,   GPIO2L_PWM2\r
+ GPIO1A5_EMMCPWREN_PWM3_NAME,     GPIO1L_PWM3\r
+ */\r
\r
+#define PWM_ID            0  \r
+#define PWM_MUX_NAME      GPIO1B5_PWM0_NAME\r
+#define PWM_MUX_MODE      GPIO1L_PWM0\r
+#define PWM_MUX_MODE_GPIO GPIO1L_GPIO1B5\r
+#define PWM_EFFECT_VALUE  0\r
+\r
+//#define LCD_DISP_ON_PIN\r
+\r
+#ifdef  LCD_DISP_ON_PIN\r
+#define BL_EN_MUX_NAME    GPIOF34_UART3_SEL_NAME\r
+#define BL_EN_MUX_MODE    IOMUXB_GPIO1_B34\r
+\r
+#define BL_EN_PIN         GPIO0L_GPIO0A5\r
+#define BL_EN_VALUE       GPIO_HIGH\r
+#endif\r
+static int rk29_backlight_io_init(void)\r
+{\r
+    int ret = 0;\r
+    \r
+    rk29_mux_api_set(PWM_MUX_NAME, PWM_MUX_MODE);\r
+       #ifdef  LCD_DISP_ON_PIN\r
+    rk29_mux_api_set(BL_EN_MUX_NAME, BL_EN_MUX_MODE); \r
+       \r
+    ret = gpio_request(BL_EN_PIN, NULL); \r
+    if(ret != 0)\r
+    {\r
+        gpio_free(BL_EN_PIN);   \r
+    }\r
+    \r
+    gpio_direction_output(BL_EN_PIN, 0);\r
+    gpio_set_value(BL_EN_PIN, BL_EN_VALUE);\r
+       #endif\r
+    return ret;\r
+}\r
+\r
+static int rk29_backlight_io_deinit(void)\r
+{\r
+    int ret = 0;\r
+    #ifdef  LCD_DISP_ON_PIN\r
+    gpio_free(BL_EN_PIN);\r
+    #endif\r
+    rk29_mux_api_set(PWM_MUX_NAME, PWM_MUX_MODE_GPIO);\r
+    return ret;\r
+}\r
+struct rk29_bl_info rk29_bl_info = {\r
+    .pwm_id   = PWM_ID,\r
+    .bl_ref   = PWM_EFFECT_VALUE,\r
+    .io_init   = rk29_backlight_io_init,\r
+    .io_deinit = rk29_backlight_io_deinit, \r
+};\r
+#endif\r
 /*****************************************************************************************\r
  * SDMMC devices\r
 *****************************************************************************************/\r
@@ -590,6 +653,9 @@ static struct platform_device *devices[] __initdata = {
 #ifdef CONFIG_FB_RK29\r
        &rk29_device_fb,\r
 #endif\r
+#ifdef CONFIG_BACKLIGHT_RK29_BL\r
+       &rk29_device_backlight,\r
+#endif\r
 #ifdef CONFIG_VIVANTE\r
        &rk29_device_gpu,\r
 #endif\r
index c505683e082d41bb7cd8dc16c9390a78626f7518..333735f662295a7b38e6abd72b61dc64b3129a52 100755 (executable)
@@ -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[] = {
index 8e476673f71429e7e0223757c295430f1f5696de..d202ce311b5007b912865ffcb20d3eb8d92892e8 100755 (executable)
@@ -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
index b7b1160afbdd4e8b004f78aa4b9208ba5ff18888..17dbf18b01ab1789b09197f9dfc2eec34332cedc 100755 (executable)
@@ -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;
index bea219587a5d90840ffd0fb558df62c112f7baad..0f6193e6cb13ffb95a7bb8e2e6f7512d24ad4d44 100644 (file)
 #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
index 9f681ccb3b9455fd801b4d3c12585e44f0c76926..a56d8894267a8ad42f0183f7f8d9eb9b94568f5d 100644 (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),
index 9061330841aaef6f5ed0dd271802f15ffc753717..bb85a9067202a7af79a56102816df73a0c26d9d3 100644 (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.         
index 1739ab2b389a317207a31e1b0398c53681c789df..096d9731d90759446fc709fd6377944d1ac7e33a 100644 (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
index 1b68a039bdd5c3a46c13af5fbbb49e11da360bad..0ce4039e9a9d63a74c14b9b6d3de22f10f8e67e7 100644 (file)
@@ -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 (file)
index 0000000..a54f7d9
--- /dev/null
@@ -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);