Chinaunix首页 | 论坛 | 博客
  • 博客访问: 43364
  • 博文数量: 5
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 227
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-19 15:17
文章分类

全部博文(5)

文章存档

2013年(5)

我的朋友

分类: Android平台

2013-04-24 22:29:31

相关文件

                kernel/arch/arm/mach-rk2928/board-rk2926-sdk.c

                kernel/drivers/video/backlight/rk2818_backlight.h

                kernel/drivers/video/backlight/rk29_backlight.c


一、驱动的设备模型

和大多数我们常用的驱动一样,PWM也是做成了Platform类型的设备驱动。而与之相关的,会需要我们提供相应的Platform Device。而Platform Device是在板文件中声明和注册的。当platform总线匹配Device和Driver时候,会在Driver里获取到我们预先注册在系统里的Platform Device,进而对其进行初始化和控制。

二、Backlight设备的注册


点击(此处)折叠或打开

  1. /***********************************************************
  2. * rk30 backlight
  3. ************************************************************/
  4. #ifdef CONFIG_BACKLIGHT_RK29_BL
  5. //用来描述pwm设备的ID,通过这个宏来切换pwm的通道(2926支持3路pwm,且共享一路clock)
  6. #define PWM_ID 0
  7. #define PWM_MUX_NAME GPIO0D2_PWM_0_NAME
  8. #define PWM_MUX_MODE GPIO0D_PWM_0
  9. #define PWM_MUX_MODE_GPIO GPIO0D_GPIO0D2
  10. #define PWM_GPIO RK2928_PIN0_PD2
  11. #define PWM_EFFECT_VALUE 0

  12. #if defined(V86_VERSION_1_1)
  13. #define LCD_DISP_ON_PIN
  14. #endif

  15. #ifdef LCD_DISP_ON_PIN
  16. #if defined(V86_VERSION_1_1)
  17. #define BL_EN_PIN RK2928_PIN3_PC1
  18. #define BL_EN_VALUE GPIO_HIGH
  19. #define BL_EN_MUX_NAME GPIO3C1_OTG_DRVVBUS_NAME
  20. #define BL_EN_MUX_MODE GPIO3C_GPIO3C1
  21. #else
  22. #define BL_EN_PIN RK2928_PIN1_PB0
  23. #define BL_EN_VALUE GPIO_HIGH
  24. #endif
  25. #endif
  26. static int rk29_backlight_io_init(void)
  27. {
  28.     int ret = 0;
  29.     /* 设置GPIO口为PWM功能 */
  30.     rk30_mux_api_set(PWM_MUX_NAME, PWM_MUX_MODE);

  31. #ifdef LCD_DISP_ON_PIN
  32.     #if defined(V86_VERSION_1_1)
  33.        rk30_mux_api_set(BL_EN_MUX_NAME, BL_EN_MUX_MODE);
  34. #endif
  35.     
  36.     /* 请求GPIO口 */
  37.     ret = gpio_request(BL_EN_PIN, NULL);
  38.     if (ret != 0) {
  39.         gpio_free(BL_EN_PIN);
  40.     }

  41.     /* 设置GPIO口为输出 */
  42.     gpio_direction_output(BL_EN_PIN, 0);

  43.     /* 设置输出为高点平 */
  44.     gpio_set_value(BL_EN_PIN, BL_EN_VALUE);
  45. #endif
  46.     return ret;
  47. }

  48. static int rk29_backlight_io_deinit(void)
  49. {
  50.     int ret = 0;
  51. #ifdef LCD_DISP_ON_PIN
  52.     /* 管脚状态取反 */
  53.     gpio_set_value(BL_EN_PIN, !BL_EN_VALUE);
  54.     
  55.     /* 释放GPIO */
  56.     gpio_free(BL_EN_PIN);
  57. #endif

  58.     /* 设置管脚功能为GPIO */
  59.     rk30_mux_api_set(PWM_MUX_NAME, PWM_MUX_MODE_GPIO);

  60.     #if defined(V86_VERSION_1_0) || defined(V86_VERSION_1_1)
  61.     #if defined(CONFIG_MFD_TPS65910)
  62.     if(g_pmic_type == PMIC_TYPE_TPS65910)
  63.     {
  64.         gpio_direction_output(PWM_GPIO, GPIO_HIGH);
  65.     }
  66.     #endif
  67.     #endif
  68.     
  69.     return ret;
  70. }

  71. static int rk29_backlight_pwm_suspend(void)
  72. {
  73.     int ret = 0;
  74.     rk30_mux_api_set(PWM_MUX_NAME, PWM_MUX_MODE_GPIO);
  75.     if (gpio_request(PWM_GPIO, NULL)) {
  76.         printk("func %s, line %d: request gpio fail\n", __FUNCTION__, __LINE__);
  77.         return -1;
  78.     }
  79.     #if defined(CONFIG_MFD_TPS65910)
  80.      if (pmic_is_tps65910() )
  81.      #if defined(V86_VERSION_1_0) || defined(V86_VERSION_1_1)
  82.      gpio_direction_output(PWM_GPIO, GPIO_HIGH);
  83.      #else
  84.      gpio_direction_output(PWM_GPIO, GPIO_LOW);
  85.      #endif
  86.     #endif
  87.     #if defined(CONFIG_REGULATOR_ACT8931)
  88.      if (pmic_is_act8931() )
  89.      gpio_direction_output(PWM_GPIO, GPIO_HIGH);
  90.     #endif
  91. #ifdef LCD_DISP_ON_PIN
  92.     gpio_direction_output(BL_EN_PIN, 0);
  93.     gpio_set_value(BL_EN_PIN, !BL_EN_VALUE);
  94. #endif

  95.     return ret;
  96. }

  97. static int rk29_backlight_pwm_resume(void)
  98. {
  99.     gpio_free(PWM_GPIO);
  100.     rk30_mux_api_set(PWM_MUX_NAME, PWM_MUX_MODE);
  101. #ifdef LCD_DISP_ON_PIN
  102.     msleep(30);
  103.     gpio_direction_output(BL_EN_PIN, 1);
  104.     gpio_set_value(BL_EN_PIN, BL_EN_VALUE);
  105. #endif
  106.     return 0;
  107. }

  108. /* 设置背光信息结构体 */
  109. static struct rk29_bl_info rk29_bl_info = {
  110.     .pwm_id = PWM_ID,
  111.     .min_brightness = 40,//modefy by logan for make backlight lower
  112.     .bl_ref = PWM_EFFECT_VALUE,
  113.     .io_init = rk29_backlight_io_init,
  114.     .io_deinit = rk29_backlight_io_deinit,
  115.     .pwm_suspend = rk29_backlight_pwm_suspend,
  116.     .pwm_resume = rk29_backlight_pwm_resume,
  117. };

  118. /* 设置背光设备(Platform类型)结构体 */
  119. static struct platform_device rk29_device_backlight = {
  120.     .name = "rk29_backlight",
  121.     .id = -1,
  122.     .dev = {
  123.         .platform_data = &rk29_bl_info,
  124.     }
  125. };

  126. #if defined(V86_VERSION_1_0) || defined(V86_VERSION_1_1) //for v86 to modify flash lcd when startup
  127. static int __init set_pwm_gpio_high(void)
  128. {
  129.         printk("%s, xhc", __func__);
  130.         rk30_mux_api_set(PWM_MUX_NAME, PWM_MUX_MODE_GPIO);
  131.     if (gpio_request(PWM_GPIO, NULL)) {
  132.         printk("func %s, line %d: request gpio fail\n", __FUNCTION__, __LINE__);
  133.         return -1;
  134.     }
  135.     gpio_direction_output(PWM_GPIO, GPIO_HIGH);
  136.         gpio_free(PWM_GPIO);
  137.         return 0;
  138. }
  139. core_initcall(set_pwm_gpio_high);
  140. #endif

  141. #endif


        从上面代码可以看出,实际上,核心内容只是填充了一个Platform device的结构体。而背光信息是以Platform_data(void *类型)形式保存的。而在背光信息结构体的初始化中,也无非是设置了默认的PWM通道和初始化、反初始化、休眠和恢复等回调函数。而在其中,最主要的是初始化函数。在里面完成了PWM功能的选择、IO口的申请等内容。这是我们完成PWM功能所必须的。

点击(此处)折叠或打开

  1. static struct platform_device *devices[] __initdata = {
  2. #ifdef CONFIG_FB_ROCKCHIP
  3.     &device_fb,
  4. #endif
  5. #ifdef CONFIG_LCDC_RK2928
  6.     &device_lcdc,
  7. #endif
  8. #ifdef CONFIG_BACKLIGHT_RK29_BL
  9.     &rk29_device_backlight,
  10. #endif
  11. #ifdef CONFIG_ION
  12.     &device_ion,
  13. #endif
  14. #ifdef CONFIG_SND_SOC_RK2928
  15.     &device_acodec,
  16. #endif
  17. #ifdef CONFIG_BATTERY_RK30_ADC_FAC
  18.     &rk30_device_adc_battery,
  19. #endif
  20. };

点击(此处)折叠或打开

  1. static void __init rk2928_board_init(void)
  2. {
  3.         store_boot_source();
  4.     gpio_request(POWER_ON_PIN, "poweronpin");
  5.     gpio_direction_output(POWER_ON_PIN, GPIO_HIGH);
  6.  
  7.     pm_power_off = rk2928_pm_power_off;
  8.     
  9.     rk30_i2c_register_board_info();
  10.     spi_register_board_info(board_spi_devices, ARRAY_SIZE(board_spi_devices));
  11.     platform_add_devices(devices, ARRAY_SIZE(devices));

  12. #ifdef CONFIG_WIFI_CONTROL_FUNC
  13.         rk29sdk_wifi_bt_gpio_control_init();
  14. #endif
  15. }
        接着,我们可以看到,我们将我们的背光设备注册进了名为devices的Platform_device的数组里,并在rk2928_board_init函数里最终通过platform_add_devices添加进了平台设备列表里,以便在对应的驱动注册时进行匹配。而匹配的依据就是我们的设备名称.name    = "rk29_backlight"

        在系统启动后,会加载各种设备驱动,当有与我们刚刚注册的设备名称相同的驱动时,就会将两者关联,并执行驱动中的probe函数。在背光驱动中,我们可以看到驱动的类型是platform_driver,而对应的驱动名称同样为"rk29_backlight"

点击(此处)折叠或打开

  1. static struct platform_driver rk29_backlight_driver = {
  2.     .probe    = rk29_backlight_probe,
  3.     .remove = rk29_backlight_remove,
  4.     .driver    = {
  5.         .name    = "rk29_backlight",
  6.         .owner    = THIS_MODULE,
  7.     },
  8.     .shutdown    = rk29_backlight_shutdown,
  9. };
        当名称匹配之后,会执行驱动中的probe回调函数:


点击(此处)折叠或打开

  1. static int rk29_backlight_probe(struct platform_device *pdev)
  2. {        
  3.     int ret = 0;
  4.     
  5.     /* 获取注册的背光信息 */
  6.     struct rk29_bl_info *rk29_bl_info = pdev->dev.platform_data;
  7.     u32 id = rk29_bl_info->pwm_id;
  8.     u32 divh, div_total;
  9.     unsigned long pwm_clk_rate;

  10.     背光属性
  11.     struct backlight_properties props;
  12.     int pre_div = PWM_APB_PRE_DIV;

  13.     if (rk29_bl) {
  14.         printk(KERN_CRIT "%s: backlight device register has existed \n",
  15.                 __func__);
  16.         return -EEXIST;        
  17.     }

  18.     if (!rk29_bl_info->delay_ms)
  19.         rk29_bl_info->delay_ms = 100;

  20.     if (rk29_bl_info->min_brightness < 0 || rk29_bl_info->min_brightness > BL_STEP)
  21.         rk29_bl_info->min_brightness = 52;

  22.     if (rk29_bl_info && rk29_bl_info->io_init) {
  23.         rk29_bl_info->io_init();
  24.     }

  25.     if(rk29_bl_info->pre_div > 0)
  26.         pre_div = rk29_bl_info->pre_div;

  27.     memset(&props, 0, sizeof(struct backlight_properties));
  28.     props.type = BACKLIGHT_RAW;
  29.     props.max_brightness = BL_STEP;

  30.     /* 注册背光设备 */
  31.     rk29_bl = backlight_device_register("rk28_bl", &pdev->dev, rk29_bl_info, &rk29_bl_ops, &props);
  32.     if (!rk29_bl) {
  33.         printk(KERN_CRIT "%s: backlight device register error\n",
  34.                 __func__);
  35.         return -ENODEV;        
  36.     }

  37. #if defined(CONFIG_ARCH_RK29)
  38.     pwm_clk = clk_get(NULL, "pwm");  /* 获取pwm时钟 */
  39. #elif defined(CONFIG_ARCH_RK30) || defined(CONFIG_ARCH_RK2928)
  40.     if (id == 0 || id == 1)
  41.         pwm_clk = clk_get(NULL, "pwm01");
  42.     else if (id == 2 || id == 3)
  43.         pwm_clk = clk_get(NULL, "pwm23");
  44. #endif
  45.     if (IS_ERR(pwm_clk) || !pwm_clk) {
  46.         printk(KERN_ERR "failed to get pwm clock source\n");
  47.         return -ENODEV;
  48.     }

  49.     /* 获取时钟频率(RK2926上为1485000000) */
  50.     pwm_clk_rate = clk_get_rate(pwm_clk);

  51.     /* 计算APB预分频后(RK2926上,分配系数pre_div为1000)一周期的时钟数 */
  52.     div_total = pwm_clk_rate / pre_div;

  53.     /* 根据PWM分频因子计算分频后的一周期时钟数,分频系数见下图 */
  54.     div_total >>= (1 + (PWM_DIV >> 9));

  55.     /* 分频因子过小,有可能出现为0的情况,则给默认值1 */
  56.     div_total = (div_total) ? div_total : 1;

  57.     if(rk29_bl_info->bl_ref) {
  58.         divh = 0;
  59.     } else {
  60.         divh = div_total;
  61.     }

  62.     /* 使能pwm时钟 */
  63.     clk_enable(pwm_clk);

  64.     /* 复位并设置分频值 */
  65.     write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_RESET);

  66.     /* 设置LRC寄存器为总时钟周期数 */
  67.     write_pwm_reg(id, PWM_REG_LRC, div_total);

  68.     /* 设置HRC寄存器,占空比为HRC/LRC */
  69.     write_pwm_reg(id, PWM_REG_HRC, divh);

  70.     /* 设置CNTR寄存器初值为0 */
  71.     write_pwm_reg(id, PWM_REG_CNTR, 0x0);

  72.     /* 使能PWM及TIMER使能 */
  73.     write_pwm_reg(id, PWM_REG_CTRL, PWM_DIV|PWM_ENABLE|PWM_TIME_EN);

  74.     rk29_bl->props.power = FB_BLANK_UNBLANK;
  75.     rk29_bl->props.fb_blank = FB_BLANK_UNBLANK;

  76.     /* 设置默认亮度为50% */
  77.     rk29_bl->props.brightness = BL_STEP / 2;
  78.     rk29_bl->props.state = BL_CORE_DRIVER1;        

  79.     /* 执行背光延时更新动作 */
  80.     schedule_delayed_work(&rk29_backlight_work, msecs_to_jiffies(rk29_bl_info->delay_ms));

  81.     /* 创建属性文件 */
  82.     ret = device_create_file(&pdev->dev,&dev_attr_rk29backlight);
  83.     if(ret)
  84.     {
  85.         dev_err(&pdev->dev, "failed to create sysfs file\n");
  86.     }

  87.     /* 注册android挂起和唤醒相关函数 */
  88.     register_early_suspend(&bl_early_suspend);
  89.     #ifdef CONFIG_LOGO_LOWERPOWER_WARNING
  90.     if( 1 == system_lowerpower ){

  91.            rk29_bl->props.brightness =5;
  92.            mdelay (1500);
  93.     }
  94.     #endif

  95.     printk("RK29 Backlight Driver Initialized.\n");
  96.     return ret;
  97. }
          由上面的PWM_DIV(0<<9)可知,分频系数为1/2,所以我们在div_total >>= (+ (PWM_DIV >> 9))计算后,实际效果相当于乘了1/2。其他分频系数以此类推。

        在打开pwm后,设置了默认亮度为50%,并交由延时工作队列来进行亮度的更新工作。实际更新亮度的函数为:

点击(此处)折叠或打开

  1. static int rk29_bl_update_status(struct backlight_device *bl)
  2. {
  3.     u32 divh,div_total;
  4.     struct rk29_bl_info *rk29_bl_info = bl_get_data(bl);
  5.     u32 id = rk29_bl_info->pwm_id;
  6.     u32 ref = rk29_bl_info->bl_ref;
  7.     int brightness = bl->props.brightness;
  8.     
  9.     if (suspend_flag)
  10.         return 0;    

  11.     if (bl->props.brightness < rk29_bl_info->min_brightness && bl->props.brightness != 0)    /*avoid can't view screen when close backlight*/
  12.         brightness = rk29_bl_info->min_brightness;

  13.     if (bl->props.power != FB_BLANK_UNBLANK)
  14.         brightness = 0;

  15.     if (bl->props.fb_blank != FB_BLANK_UNBLANK)
  16.         brightness = 0;    

  17.     if (bl->props.state & BL_CORE_DRIVER3)
  18.         brightness = 0;    

  19.     /* 读取一个pwm周期中的时钟周期数 */
  20.     div_total = read_pwm_reg(id, PWM_REG_LRC);

  21.     /* 通过亮度值计算设置高点平时钟周期数 */
  22.     if (ref) {
  23.         divh = div_total*brightness/BL_STEP;
  24.     } else {
  25.         divh = div_total*(BL_STEP-brightness)/BL_STEP;
  26.     }

  27.     /* 更新HRC,以更新新占空比的pwm输出 */
  28.     write_pwm_reg(id, PWM_REG_HRC, divh);

  29.     if ((bl->props.state & BL_CORE_DRIVER1) && brightness ==0 ){ //BL_CORE_DRIVER1 is the flag if backlight is closed.
  30.         bl->props.state &= ~BL_CORE_DRIVER1;
  31.         clk_disable(pwm_clk);
  32.         if (rk29_bl_info->pwm_suspend)
  33.             rk29_bl_info->pwm_suspend();
  34.     }else if(!(bl->props.state & BL_CORE_DRIVER1) && brightness != 0){
  35.         bl->props.state |= BL_CORE_DRIVER1;
  36.         if (rk29_bl_info->pwm_resume)
  37.             rk29_bl_info->pwm_resume();
  38.         clk_enable(pwm_clk);
  39.     }

  40.     DBG("%s:line=%d,brightness = %d, div_total = %d, divh = %d state=%x \n",__FUNCTION__,__LINE__,brightness, div_total, divh,bl->props.state);
  41.     return 0;
  42. }

        在实际情况下,我们的屏幕是在pwm为低电平输出时为亮状态,也就是说我们的占空比是灭屏状态所占的比例。所以我们需要将亮状态的输出调整为 1-占空比,而我们每一个pwm周期为LRC个时钟周期,高电平周期为HRC,所以占空比为HRC/LRC,对应到实际情况下,我们的有效亮度占空比应为1-HRC/LRC = (LRC-HRC)/LRC。而映射到BL_STEP上,就是(BL_STEP-brightness)/BL_STEP。所以,我们如果想调整一个pwm周期的时间,那么我们可以调整LRC的数值,而如果想要设置占空比,那么我们就调整HRC的值。在我们实际情况下,当HRC为0时,占空比为1,而有效亮度占空比为100%,当HRC为LRC时,占空比为100%,实际有效亮度占空比为0。上面的ref变量就是用来标识我们当前状态下的占空比是否与真正的有效亮度对应,并根据它来进行分类计算。
        事实上,瑞芯微文档和手册并没有对pwm相关内容做过详细的说明。上面大部分内容为对照代码的分析。如有错误,希望高手予以指正。





阅读(5246) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~