MSM8909與android5.1.1的LCM屏的相容———自我感悟
剛剛接觸高通平臺,需要實現兩個LCM屏組的相容。於是就各種百度,什麼讀LCD_ID,通過AD轉換實現,什麼讀IC_ID軟體實現相容,暈。於是看著前輩的log,簡單實現了屏的相容。
相容的前提是實現屏的替換,高通的屏在Android5.1.1中分為兩部分。lk與kernel。屏的顯示是通過mipi介面實現的,而觸控實現方法需要另外在kernel中實現(IIC實現)。當系統開始執行時候,先初始化屏,接著判斷屏是否初始化完畢,根據這一點。我們可以實現屏的相容。
首先新增屏的相關資訊,新增panel_ssd2075m1_720p_video.h檔案,新增到lk/dev/gcdb/display/include目錄下。接著在lk/target/msm8909/oem_panel.c檔案中新增屏的選項,照著參考一下。有的版本的列舉型別在lk/target/msm8909/include/target/display.h檔案中照著新增即可。除此之外在oem_panel.c中的oem_panel_select()新增相容選項。
static uint32_t auto_scpan_loop = 0; //初始化一個全域性變數,代表第幾塊相容屏
int oem_panel_select(const char *panel_name, struct panel_struct *panelstruct,
struct msm_panel_info *pinfo,
struct mdss_dsi_phy_ctrl *phy_db)
{
uint32_t hw_id = board_hardware_id();
uint32_t platform_subtype = board_hardware_subtype();
int32_t panel_override_id;.........
switch (hw_id) { //選擇一個case,不知從哪裡來
case HW_PLATFORM_SURF:
case HW_PLATFORM_MTP:
case HW_PLATFORM_RCM:
panel_id = HX8394D_720P_VIDEO_PANEL;
break;
case HW_PLATFORM_QRD: //值為11,不出意外從這裡進來
switch (platform_subtype) {
case QRD_SKUA: //值為 0,不出意外從這裡進來
//panel_id = NT35521_720P_VIDEO_PANEL;
panel_id = HSX097LVDS_720P_VIDEO_PANEL;
break;
case QRD_SKUC:
panel_id = ILI9881C_720P_VIDEO_PANEL;
break;
case QRD_SKUE:
panel_id = ILI9881C_720P_VIDEO_PANEL;
break;
// default:
// dprintf(CRITICAL, "QRD Display not enabled for %d type\n",
// platform_subtype);
// return PANEL_TYPE_UNKNOWN;
}
break;
// default:
// dprintf(CRITICAL, "Display not enabled for %d HW type\n",
// hw_id);
// return PANEL_TYPE_UNKNOWN;
}dprintf(CRITICAL, "auto_scpan_loop:%d \n",auto_scpan_loop);
panel_id = NT35521z_720P_VIDEO_PANEL;
switch (auto_scpan_loop) {
case 0:
panel_id = NT35521z_720P_VIDEO_PANEL;
break;
case 1:
panel_id = SSD2075ML_720P_VIDEO_PANEL;
break;
default:
panel_id = UNKNOWN_PANEL;
dprintf(CRITICAL, "Unknown panel\n");
return PANEL_TYPE_UNKNOWN;
}
auto_scpan_loop ++;
panel_init:
phy_db->regulator_mode = DSI_PHY_REGULATOR_LDO_MODE;
return init_panel_data(panelstruct, pinfo, phy_db);
}
其實就是將panel_id的值,賦予什麼,就呼叫相關暫存器的值初始化LCD屏。
初始化的最初呼叫函式是lk/target/msm8909/target_display.c中的gcdb_display_init()函式,通過while實現反覆初始化,達到最終目的。函式中的oem_panel_max_auto_detect_panels()函式最終返回的是一個巨集,定義在lk/target/msm8909/oem_panel.c中,為:#define DISPLAY_MAX_PANEL_DETECTION 2 可以控制while迴圈的次數。
void target_display_init(const char *panel_name)
{
uint32_t panel_loop = 0;
uint32_t ret = 0;.............
do {
target_force_cont_splash_disable(false);
ret = gcdb_display_init(panel_name, MDP_REV_305, MIPI_FB_ADDR);//初始化屏的函式
if (!ret || ret == ERR_NOT_SUPPORTED) {
break;
} else {
target_force_cont_splash_disable(true);
msm_display_off();
}
} while (++panel_loop <= oem_panel_max_auto_detect_panels());//條件是一個巨集
}
初始化過程大致如下圖所示:
target_display_init()
||
\/
gcdb_display_init()
||
\/
———————————————————————————
msm_display_init() oem_panel_select()
|| ||
\/ \/
msm_display_config() init_panel_data()
||
\/
mdss_dsi_config()
||
\/
msm_dis_panel_initiliaze()
應該是搭建框架的人幫我們想好了一切,在msm_dis_panel_initiliaze()呼叫了mdss_dsi_read_panel_signature(),該函式可以幫我們實現相容。
int mdss_dsi_panel_initialize(struct mipi_dsi_panel_config *pinfo, uint32_t
broadcast)
{
int status = 0;
uint32_t ctrl_mode = 0;#if (DISPLAY_TYPE_MDSS == 1)
if (pinfo->panel_cmds) {ctrl_mode = readl(MIPI_DSI0_BASE + CTRL);
if (broadcast) {
/* Enable command mode before sending the commands. */
writel(ctrl_mode | 0x04, MIPI_DSI0_BASE + CTRL);
writel(ctrl_mode | 0x04, MIPI_DSI1_BASE + CTRL);
status = mdss_dual_dsi_cmds_tx(pinfo->panel_cmds,
pinfo->num_of_panel_cmds);
writel(ctrl_mode, MIPI_DSI0_BASE + CTRL);
writel(ctrl_mode, MIPI_DSI1_BASE + CTRL);} else {
/* Enable command mode before sending the commands. */
writel(ctrl_mode | 0x04, MIPI_DSI0_BASE + CTRL);
status = mipi_dsi_cmds_tx(pinfo->panel_cmds,
pinfo->num_of_panel_cmds);
writel(ctrl_mode, MIPI_DSI0_BASE + CTRL);
if (!status && target_panel_auto_detect_enabled()) //前面的不知道幹嘛的,反正會走到這一步
status =
mdss_dsi_read_panel_signature(pinfo->signature);
dprintf(SPEW, "Read panel signature status = 0x%x \n", status);
}
}
#endif
return status;
}
要想執行mdss_dsi_read_panel_signature();那麼if中的條件需要成立。status為0,成立,而target_panel_auto_detect_enabled()永遠返回0,不成立。所以我們需要修改該函式中的內容,如下。
uint8_t target_panel_auto_detect_enabled()
{
uint8_t ret = 0;switch(board_hardware_id()) {
case HW_PLATFORM_QRD://簡單做個判斷意思一下,反正成立
ret = 1;
break;
default:
ret = 0;
break;
}
return ret;
}
接下來程式會進入mdss_dsi_read_panel_signature();函式會讀取屏的資訊,不知是什麼。自我猜測是屏的IC_ID,有了這個就好辦了,IC_ID不變,而屏的配置資訊改變。在panel_ssd2075m1_720p_video.h中有一個巨集:#define SSD2075ML_720P_VIDEO_SIGNATURE 0xFFF7。這個值就是panel_signature,我們可以事先讀取response_value的值,通過串列埠打印出來,接著賦值給panel_signature。這樣相等就代表匹配成功,否則則代表初始化失敗。返回至函式target_display_init(),開始下一輪的初始化。
uint32_t mdss_dsi_read_panel_signature(uint32_t panel_signature)
{
uint32_t rec_buf[1];
uint32_t *lp = rec_buf, data;
int ret = response_value;/*將其初始化為0,不然只可以相容兩塊屏。原因是ret第一次被賦值,第一次容錯判斷條件直接成立*/
ret = 0;
#if (DISPLAY_TYPE_MDSS == 1)
if (ret && ret != panel_signature)
goto exit_read_signature;ret = mipi_dsi_cmds_tx(&read_ddb_start_cmd, 1);
if (ret)
goto exit_read_signature;
if (!mdss_dsi_cmds_rx(&lp, 1, 1))
goto exit_read_signature;data = ntohl(*lp);
data = data >> 8;
response_value = data; //response_value為響應值,不知道代表什麼
if (response_value == panel_signature)
{return 0;//屏的初始化配置正確,繼續往下執行
}
else{
return 1;//屏的初始化配置不正確,繼續while轉圈
}
exit_read_signature:
/* Keep the non detectable panel at the end and set panel signature 0xFFFF */
if ((panel_signature == 0) || (panel_signature == 0xFFFF) || (panel_signature == 0xFFF7) )
ret = 0;
#endif
return ret;
}
至此lk部分初始化完成,據說lk部分初始化完成後會傳參給kernel,這樣kernel只需照常配置即可,這樣屏就能亮起了。
tp部分的相容並不知道怎麼實現,不過相同型別的屏,tp都不會發生改變,只是韌體不同,即cfg資訊不同。當cfg資訊初始化進TP晶片後,就會固定不變了,除非你再次初始化。在tp驅動中,有一個巨集:#define GTP_DRIVER_SEND_CFG 1 // send config to TP while initializing (for no config built in TP's flash) 將其開啟就重新整理韌體,否者不刷。 其它的,隨緣吧。
注意:後來發現response_value 值有的屏不一樣,而有的一樣。有些醉,不過可以自己按初始化的順序新增不通用的屏相容。暈啊,求大神告知response_value 的值是幹嘛的。繼續調屏去了。。。。。。