在梳理代碼時,感覺 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 本身的背光控制函數