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 本身的背光控制函數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章