MTK的lcm背光流程和客制化

在梳理代码时,感觉 MTK 的代码中公共部分和客制化部分分的还是很清楚的。 
首先说明一下文档的结构,我们先介绍我们客制化的地方,因为这个才是我们实际调试及解决 bug 时真正要关心的,而平台端不需要客制化的代码只需要梳理清楚就行。

背光流程中,客制化与否的分界文件是 
cust_leds.c (vendor\vendor\mediatek\proprietary\bootable\bootloader\lk\target$(project)) 
其中

staticstruct cust_mt65xx_led cust_led_list[MT65XX_LED_TYPE_TOTAL] = {
         {"red",  MT65XX_LED_MODE_PMIC,     MT65XX_LED_PMIC_NLED_ISINK0,{0,0,0,0,0}},
         {"green",  MT65XX_LED_MODE_PMIC,   MT65XX_LED_PMIC_NLED_ISINK1,{0,0,0,0,0}},
         {"blue",   MT65XX_LED_MODE_NONE,   -1,                         {0,0,0,0,0}},
         {"jogball-backlight",MT65XX_LED_MODE_NONE, -1,{0,0,0,0,0}},
         {"keyboard-backlight",MT65XX_LED_MODE_NONE,-1,{0,0,0,0,0}},
         {"button-backlight",  MT65XX_LED_MODE_NONE, -1,{0,0,0,0,0}},
         {"lcd-backlight",   MT65XX_LED_MODE_CUST_LCM,(int)primary_display_setbacklight,{0}}, };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这个结构体就是客制化led子系统(包括呼吸灯、键盘灯、按键灯、背光等)中的模块具体调用方式的,比如

{"lcd-backlight",   MT65XX_LED_MODE_CUST_LCM,(int)primary_display_setbacklight,{0}},
  • 1
  • 1

第一个成员就是定义操作的模块, lcd-backlight 就代表背光, 
第二个成员表示对该模块的操作方式,mode, 
第三个成员就是代表控制该模块的具体函数, 
第四个成员就是代表一些配置, config; 
这些成员的含义可以通过 cust_mt65xx_led 的声明来得知,在 cust_leds.h 中。

我们需要根据项目需要客制化模块背光控制函数,可以有很多选择,具体分析下函数怎么执行就行。 
在背光控制中,有很多方式比如 disp_bls_set_backlight 、primary_display_setbacklight 等等,网上可以搜到的比较多的是 disp_bls_set_backlight ,客制化为这个函数的朋友可以搜一下,由于我没有看过primary_display_setbacklight 的相关介绍,正好项目中用到了,那我就写一下这种控制背光方式的原理。 
primary_display_setbacklight 主要是针对设置了 cabc (根据画面内容实时调节背光亮度)的项目执行的背光控制函数。 
我们来看 
primary_display_setbacklight 函数 (kernel-3.18\kernel-3.18\drivers\misc\mediatek\video\mt6735\primary_display.c)

int primary_display_setbacklight(unsigned int level) /*可以看到传入的参数就是亮度等级level*/
{
         ……/*前面一堆设置,我们也没必要搞懂*/
                   if(primary_display_cmdq_enabled()) {/*判断cmdq是否开启*/
                            if(primary_display_is_video_mode()) {/*判断lcm的配置是否为video_mode*/
                                     disp_lcm_set_backlight(pgc->plcm,level);/*这里就直接调用到lcm驱动文件中的lcm_setbacklight的函数,也是传递level值*/
                            } else {
…………/*后面一堆其他的不重要的操作*/
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

到这里就直接调到底层的操作了,客制化部分就结束了。

然后我们看一下平台端走的客制化控制函数之前怎么走的。 
Led 子系统的驱动文件是 leds_drv.c, 
模块注册等等都是字符设备注册方式: 
驱动结构体 platform_driver mt65xx_leds_driver 和设备结构体 platform_device mt65xx_leds_device 的name 相同时就会触发探测函数 mt65xx_leds_probe 
我们来看这个函数的内容: 
第一个重要的函数就是 struct cust_mt65xx_led * cust_led_list = mt_get_cust_led_list(); 
调用 mt_get_cust_led_list(),这个函数调用到 leds.c 中的 
struct cust_mt65xx_led *mt_get_cust_led_list(void),再调用到 leds.c 中的 
struct cust_mt65xx_led *get_cust_led_dtsi(void) 
这个函数就厉害了,我们可以看到这个函数的注释:get the leds info from device tree,即从 devicetree 读取 leds 的节点信息。

pled_dtsi = kmalloc(MT65XX_LED_TYPE_TOTAL * sizeof(struct cust_mt65xx_led), GFP_KERNEL);
  • 1
  • 1

首先我们看到申请了一个结构体,内存大小是前面我们需要客制化的结构体 cust_mt65xx_led 的大小。 
之后开始遍历 led 子系统中的各个模块,每个模块都会执行下列步骤: 
pled_dtsi[i].name= leds_name[i]; 得到模块名称

led_node = of_find_compatible_node(NULL,NULL, strncat(node_name, leds_name[i], (sizeof(node_name)-strlen(node_name)-1)));
  • 1
  • 1

读取节点信息, of_find_compatible_node 函数式 device tree 读取节点信息的函数。

ret =of_property_read_u32(led_node,"led_mode",&mode);
  • 1
  • 1

读取模块工作模式,赋值给 mode;

ret =of_property_read_u32(led_node, "data", &data);
  • 1
  • 1

读取模块模块控制函数,赋值给 data;

ret =of_property_read_u32_array(led_node, "pwm_config", pwm_config, ARRAY_SIZE(pwm_config));
  • 1
  • 1

读取模块配置参数,赋值给 config; 
switch(pled_dtsi[i].mode) 
然后根据工作模式,

case  MT65XX_LED_MODE_CUST_LCM:
pled_dtsi[i].data =(long)mtkfb_set_backlight_level;
                                               break;
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

选取并指定模块控制方式函数。 
这里我们客制化的是 MT65XX_LED_MODE_CUST_LCM,所以背光控制函数指定为mtkfb_set_backlight_level。 
到这里第一个重要的函数结束,继续回到 leds_drv.c 走 probe 函数, 
i2c_add_driver(&led_i2c_driver) ;注册 i2c 驱动 
get_div_array(); 获取分频信息,这个具体的功能可能跟 pwm 配置有关; 
然后又进行一次 leds 子系统各模块的遍历,将上面读取的 device tree 中的信息赋值给 g_leds_data 数组,只不过这个数组是 mt65xx_led_data 类型,会有更多的操作函数

g_leds_data[i]->cust.mode= cust_led_list[i].mode;
g_leds_data[i]->cust.data= cust_led_list[i].data;
g_leds_data[i]->cust.name= cust_led_list[i].name;
g_leds_data[i]->cdev.name= cust_led_list[i].name;
g_leds_data[i]->cust.config_data= cust_led_list[i].config_data;
g_leds_data[i]->cdev.brightness_set= mt65xx_led_set;
/*这里可以看到亮度等级控制函数指定为mt65xx_led_set;*/
g_leds_data[i]->cdev.blink_set= mt65xx_blink_set;
/*这里是指定闪烁控制函数,主要是对呼吸灯的定制*/
INIT_WORK(&g_leds_data[i]->work,mt_mt65xx_led_work);
/*增加一个工作队列mt_mt65xx_led_work。*/
ret =led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev);(注册设备)
/*……后面是一些其他的操作*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这样的话控制背光亮度就可以确定为 mt65xx_led_set;

我们来看这个函数: 
static void mt65xx_led_set(struct led_classdev *led_cdev, enum led_brightness level)(leds_drv.c) 
传入参数是设备和亮度等级,开始! 
node =of_find_compatible_node(NULL, NULL, “mediatek,lcd-backlight”);读取device tree中lcd-backlight 的信息,即背光; 
前面是对对 level 的一些普通的判断操作,包括限制大小,然后

if (level ==0) {
……
                            gpio_direction_output(I2C_SET_FOR_BACKLIGHT,0);
                   }
if(!last_level1 && level) {
……
                            gpio_direction_output(I2C_SET_FOR_BACKLIGHT,1);
……
                   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

根据亮度等级置高置低引脚,这里可能是为了省电,在亮度为 0 时直接关闭那个 pin 脚,厉害了。 
然后结束就是 mt_mt65xx_led_set(led_cdev,level); 
这个就是在对传进的 level 参数处理完成之后再次传入一个背光控制函数, 
我们看 void mt_mt65xx_led_set(struct led_classdev *led_cdev, enum led_brightness level) 
#ifdef CONFIG_MTK_AAL_SUPPORT 
使用 AAl 这个宏来控制代码,项目中没有打开这个宏,就走 else 里的代码 
然后就是一番逻辑判断之后 
mt_mt65xx_led_set_cust(&led_data->cust,level); 
在看 int mt_mt65xx_led_set_cust(struct cust_mt65xx_led *cust, int level) 这个函数: 
switch(cust->mode) 判断 device tree 中的背光控制模式,

case MT65XX_LED_MODE_CUST_LCM:
                   if (strcmp(cust->name,"lcd-backlight") == 0)
                            bl_brightness_hal =level;
                   /* warning for this APIrevork */
                   return ((cust_brightness_set)(cust->data)) (level, bl_div_hal);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

这里最后一句话 ((cust_brightness_set)(cust->data)) 是使用 typedef 进行代码简化,想深究的话就搜一下 typedef 的用法 
((cust_brightness_set)(cust->data)) (level, bl_div_hal); 这句话的意思就是执行 
客制化的模块控制函数 primary_display_setbacklight(); 
到这里就和前面的客制化内容相互对接了。 
其实这个 switch(cust->mode) 就是为了根据客制化的 mode 来判断是走平台的背光控制还是走 lcd 本身的背光控制函数

你可能感兴趣的:(MTK平台)