刚刚接触高通平台,需要实现两个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 的值是干嘛的。继续调屏去了。。。。。。