MTK 平台LCM ESD客制化及代码分析和案例
参考文档:
[FAQ14251]如何配置LCM ESD Check——读寄存器方式
https://onlinesso.mediatek.com/FAQ/SW/FAQ14251
[FAQ14273]MT6735/MT6753/MT6580 ESD问题攻略——外部TE方式
https://onlinesso.mediatek.com/FAQ/SW/FAQ14273
[FAQ14880]LCM ESD Check 问题处理流程
https://onlinesso.mediatek.com/FAQ/SW/FAQ14880
[FAQ13728]MT6735通过读寄存器方式做ESD,客制化需求----多个返回值
https://onlinesso.mediatek.com/FAQ/SW/FAQ13728
一.LCM ESD客制化方法
alps/kernel-3.18/drivers/misc/mediatek/lcm/xxx/xxx.c
alps/vendor/mediatek/proprietary/bootable/bootloader/lk/dev/lcm/xxx/xxx.c
static void lcm_get_params(LCM_PARAMS *params)
{
……
/* Esd Check方法1 : Read from lcm */
params->dsi.esd_check_enable = 1;
params->dsi.customization_esd_check_enable = 1;
params->dsi.lcm_esd_check_table[0].cmd = 0x0A; //具体是哪个寄存器由FAE告知
params->dsi.lcm_esd_check_table[0].count = 1;
params->dsi.lcm_esd_check_table[0].para_list[0] = 0x1C; //读取寄存器的正确值也由FAE告知,寄存器的值要配置正确,否则会不断地esd recovery
/* Esd Check方法2 : EXT TE */
//params->dsi.esd_check_enable = 1;
//params->dsi.customization_esd_check_enable = 0;
}
二.MT6580 LCM ESD check流程分析
alps/kernel-3.18/drivers/misc/mediatek/video/mt6580/videox/primary_display.c
int primary_display_init(char *lcm_name, unsigned int lcm_fps)
{
......
primary_display_esd_check_task =
kthread_create(primary_display_esd_check_worker_kthread,
NULL, "display_esd_check");
......
//这里根据LCM中配置的params->dsi.esd_check_enable是否等于1来判断是否开启primary_display_esd_check_worker_kthread线程
if (_need_do_esd_check())
wake_up_process(primary_display_esd_check_task);
......
}
unsigned int _need_do_esd_check(void)
{
int ret = 0;
#ifdef CONFIG_OF
if ((pgc->plcm->params->dsi.esd_check_enable == 1)
&& (islcmconnected == 1))
ret = 1;
#else
if (pgc->plcm->params->dsi.esd_check_enable == 1)
ret = 1;
#endif
return ret;
}
static int primary_display_esd_check_worker_kthread(void *data)
{
......
//定义esd recovery重试的次数
int esd_try_cnt = 5;
......
//这里每2秒扫描一次
msleep(2000); /* esd check and pull clock lane every 2s */
......
//执行esd check判断是否要进行esd recovery,关于primary_display_esd_check函数的分析在下面会讲到
ret = primary_display_esd_check();
if (ret == 1) {
pr_debug("[ESD]esd check fail, will do esd recovery\n");
i = esd_try_cnt;
while (i--) {
DISPCHECK("[ESD]esd recovery try:%d\n", i);
//执行恢复动作,重新初始化显示屏参数,关于primary_display_esd_recovery函数的分析在下面有讲到
primary_display_esd_recovery();
//执行完恢复动作后,再次进行esd check,如果检测到的lcm寄存器的值正常,则不会再进行recovery的动作;否则会连续进行recovery和esd check,若在规定的5次内recovery均不成功,就会执行primary_display_esd_check_enable(0)关闭primary_display_esd_check_worker_kthread线程。
ret = primary_display_esd_check();
if (ret == 0) {
pr_debug
("[ESD]esd recovery success\n");
break;
}
pr_debug("[ESD]after esd recovery, esd check still fail\n");
if (i == 0) {
DISPERR(
"[ESD]after esd recovery %d times, esd check still fail, disable esd check\n",
esd_try_cnt);
primary_display_esd_check_enable(0);
}
}
}
......
}
/* ESD CHECK FUNCTION */
/* return 1: esd check fail */
/* return 0: esd check pass */
int primary_display_esd_check(void)
{
……
/* Esd Check : EXT TE */
//使用TE的方式进行esd check的时候,需要在lcm的驱动中定义esd_check_enable为1,customization_esd_check_enable为0,lcm的初始化参数也要匹配TE的,主控这边的TE GPIO口需要配置正确
if (pgc->plcm->params->dsi.customization_esd_check_enable == 0) {
MMProfileLogEx(ddp_mmp_get_events()->esd_extte,
MMProfileFlagStart, 0, 0);
if (primary_display_is_video_mode()) {
primary_display_switch_esd_mode(1);
/* use cmdq to pull DSI clk lane*/
if (primary_display_cmdq_enabled()) {
_primary_path_lock(__func__);
/* 0.create esd check cmdq */
cmdqRecCreate(CMDQ_SCENARIO_DISP_ESD_CHECK, &(pgc->cmdq_handle_config_esd));
_primary_path_unlock(__func__);
/* 1.reset*/
cmdqRecReset(pgc->cmdq_handle_config_esd);
/* wait stream eof first */
ret = cmdqRecWait(pgc->cmdq_handle_config_esd, CMDQ_EVENT_DISP_RDMA0_EOF);
cmdqRecWait(pgc->cmdq_handle_config_esd, CMDQ_EVENT_MUTEX0_STREAM_EOF);
_primary_path_lock(__func__);
/* 2.stop dsi vdo mode */
dpmgr_path_build_cmdq(pgc->dpmgr_handle,
pgc->cmdq_handle_config_esd, CMDQ_STOP_VDO_MODE, 0);
/* 3.pull DSI clock lane */
DSI_sw_clk_trail_cmdq(0, pgc->cmdq_handle_config_esd);
DSI_manual_enter_HS(pgc->cmdq_handle_config_esd);
/* 4.start dsi vdo mode */
dpmgr_path_build_cmdq(pgc->dpmgr_handle,
pgc->cmdq_handle_config_esd, CMDQ_START_VDO_MODE, 0);
/* 5. trigger path */
cmdqRecClearEventToken(pgc->cmdq_handle_config_esd, CMDQ_EVENT_MUTEX0_STREAM_EOF);
dpmgr_path_trigger(pgc->dpmgr_handle, pgc->cmdq_handle_config_esd, CMDQ_ENABLE);
_primary_path_unlock(__func__);
cmdqRecFlush(pgc->cmdq_handle_config_esd);
cmdqRecDestroy(pgc->cmdq_handle_config_esd);
pgc->cmdq_handle_config_esd = NULL;
}
if (_need_register_eint()) {
MMProfileLogEx(ddp_mmp_get_events()->esd_extte,
MMProfileFlagPulse, 1, 1);
if (wait_event_interruptible_timeout
(esd_ext_te_wq,
atomic_read(&esd_ext_te_event),
HZ / 2) > 0) {
ret = 0; /* esd check pass */
} else {
ret = 1; /* esd check fail */
}
atomic_set(&esd_ext_te_event, 0);
}
primary_display_switch_esd_mode(0);
} else {
MMProfileLogEx(ddp_mmp_get_events()->esd_extte,
MMProfileFlagPulse, 0, 1);
if (dpmgr_wait_event_timeout
(pgc->dpmgr_handle, DISP_PATH_EVENT_IF_VSYNC,
HZ / 2) > 0) {
ret = 0; /* esd check pass */
} else {
ret = 1; /* esd check fail */
}
}
MMProfileLogEx(ddp_mmp_get_events()->esd_extte,
MMProfileFlagEnd, 0, ret);
/* _primary_path_unlock(__func__); */
goto done;
}
/* / Esd Check : Read from lcm */
//读LCM寄存器方式进行esd check和recovery
MMProfileLogEx(ddp_mmp_get_events()->esd_rdlcm, MMProfileFlagStart, 0,
primary_display_cmdq_enabled());
if (primary_display_cmdq_enabled()) {
_primary_path_lock(__func__);
MMProfileLogEx(ddp_mmp_get_events()->esd_rdlcm,
MMProfileFlagPulse, 0, 1);
/* 0.create esd check cmdq */
cmdqRecCreate(CMDQ_SCENARIO_DISP_ESD_CHECK,
&(pgc->cmdq_handle_config_esd));
dpmgr_path_build_cmdq(pgc->dpmgr_handle,
pgc->cmdq_handle_config_esd,
CMDQ_ESD_ALLC_SLOT, 0);
MMProfileLogEx(ddp_mmp_get_events()->esd_rdlcm,
MMProfileFlagPulse, 0, 2);
DISPCHECK("[ESD]ESD config thread=%p\n", pgc->cmdq_handle_config_esd);
_primary_path_unlock(__func__);
/* 1.use cmdq to read from lcm */
//发CMDQ_ESD_CHECK_READ的命令读取lcm的寄存器数据,_esd_check_config_handle_vdo函数在下面会讲到
if (primary_display_is_video_mode())
ret = _esd_check_config_handle_vdo();
else
ret = _esd_check_config_handle_cmd();
MMProfileLogEx(ddp_mmp_get_events()->esd_rdlcm,
MMProfileFlagPulse,
primary_display_is_video_mode(), 3);
if (ret == 1) {
/* cmdq fail */
if (_need_wait_esd_eof()) {
/* Need set esd check eof synctoken to let trigger loop go. */
cmdqCoreSetEvent(CMDQ_SYNC_TOKEN_ESD_EOF);
}
/* do dsi reset */
dpmgr_path_build_cmdq(pgc->dpmgr_handle,
pgc->cmdq_handle_config_esd,
CMDQ_DSI_RESET, 0);
goto destroy_cmdq;
}
DISPCHECK("[ESD]ESD config thread done~\n");
/* 2.check data(*cpu check now) */
//发CMDQ_ESD_CHECK_CMP的命令,判断读取到的lcm寄存器数据和lcm驱动中定义的lcm_esd_check_table[i].para_list[0]是否一致
ret = dpmgr_path_build_cmdq(pgc->dpmgr_handle,
pgc->cmdq_handle_config_esd,
CMDQ_ESD_CHECK_CMP, 0);
MMProfileLogEx(ddp_mmp_get_events()->esd_rdlcm,
MMProfileFlagPulse, 0, 4);
if (ret)
ret = 1; /* esd check fail */
destroy_cmdq:
dpmgr_path_build_cmdq(pgc->dpmgr_handle,
pgc->cmdq_handle_config_esd,
CMDQ_ESD_FREE_SLOT, 0);
/* 3.destroy esd config thread */
cmdqRecDestroy(pgc->cmdq_handle_config_esd);
pgc->cmdq_handle_config_esd = NULL;
/* _primary_path_unlock(__func__); */
}
……
return ret;
}
/* For Vdo Mode Read LCM Check */
/* Config cmdq_handle_config_esd */
int _esd_check_config_handle_vdo(void)
{
int ret = 0; /* 0:success , 1:fail */
primary_display_esd_cust_bycmdq(1);
/* 1.reset */
cmdqRecReset(pgc->cmdq_handle_config_esd);
/* Lock which is used to avoid esd and suspend affect */
ret = cmdqRecWait(pgc->cmdq_handle_config_esd, CMDQ_EVENT_DISP_RDMA0_EOF);
cmdqRecWait(pgc->cmdq_handle_config_esd, CMDQ_EVENT_MUTEX0_STREAM_EOF);
_primary_path_lock(__func__);
/* 2.stop dsi vdo mode */
dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_config_esd,
CMDQ_STOP_VDO_MODE, 0);
/* 3.write instruction(read from lcm) */
//发CMDQ_ESD_CHECK_READ的命令读取lcm的寄存器数据
dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_config_esd, CMDQ_ESD_CHECK_READ, 0);
/* pull DSI clock lane */
DSI_sw_clk_trail_cmdq(0, pgc->cmdq_handle_config_esd);
DSI_manual_enter_HS(pgc->cmdq_handle_config_esd);
/* 4.start dsi vdo mode */
dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_config_esd, CMDQ_START_VDO_MODE, 0);
/* 5. trigger path */
dpmgr_path_trigger(pgc->dpmgr_handle, pgc->cmdq_handle_config_esd,
CMDQ_ENABLE);
_primary_path_unlock(__func__);
/* 6.flush instruction */
dprec_logger_start(DPREC_LOGGER_ESD_CMDQ, 0, 0);
ret = cmdqRecFlush(pgc->cmdq_handle_config_esd);
dprec_logger_done(DPREC_LOGGER_ESD_CMDQ, 0, 0);
DISPCHECK("[ESD]_esd_check_config_handle_vdo ret=%d\n", ret);
if (ret)
ret = 1;
primary_display_esd_cust_bycmdq(0);
return ret;
}
alps/kernel-3.18/drivers/misc/mediatek/video/mt6580/dispsys/ddp_dsi.c
int ddp_dsi_build_cmdq(DISP_MODULE_ENUM module, void *cmdq_trigger_handle, CMDQ_STATE state)
{
……
else if (state == CMDQ_ESD_CHECK_READ) {
// 下发CMDQ_ESD_CHECK_READ命令的数据处理
/* enable dsi interrupt: RD_RDY/CMD_DONE (need do this here?) */
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_INT_ENABLE_REG, DSI_REG[dsi_i]->DSI_INTEN,
RD_RDY, 1);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_INT_ENABLE_REG, DSI_REG[dsi_i]->DSI_INTEN,
CMD_DONE, 1);
for (i = 0; i < 3; i++) {
if (dsi_params->lcm_esd_check_table[i].cmd == 0)
break;
/* 0. send read lcm command(short packet) */
t0.CONFG = 0x04; /* /BTA */
t0.Data0 = dsi_params->lcm_esd_check_table[i].cmd;
/* / 0xB0 is used to distinguish DCS cmd or Gerneric cmd, is that Right??? */
t0.Data_ID =
(t0.Data0 <
0xB0) ? DSI_DCS_READ_PACKET_ID : DSI_GERNERIC_READ_LONG_PACKET_ID;
t0.Data1 = 0;
/* write DSI CMDQ */
DSI_OUTREG32(cmdq_trigger_handle, &DSI_CMDQ_REG[dsi_i]->data[0],
0x00013700);
DSI_OUTREG32(cmdq_trigger_handle, &DSI_CMDQ_REG[dsi_i]->data[1],
AS_UINT32(&t0));
DSI_OUTREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_CMDQ_SIZE, 2);
/* start DSI */
DSI_OUTREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_START, 0);
DSI_OUTREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_START, 1);
/* 1. wait DSI RD_RDY(must clear, in case of cpu RD_RDY interrupt handler) */
if (dsi_i == 0) {
DSI_POLLREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_INTSTA,
0x00000001, 0x1);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_INT_STATUS_REG,
DSI_REG[dsi_i]->DSI_INTSTA, RD_RDY, 0x0);
}
#if 0
else { /* DSI1 */
DSI_POLLREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_INTSTA,
0x00000001, 0x1);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_INT_STATUS_REG,
DSI_REG[dsi_i]->DSI_INTSTA, RD_RDY, 0x0);
}
#endif
/* 2. save RX data */
if (hSlot) {
DSI_BACKUPREG32(cmdq_trigger_handle,
hSlot, i * 4 + 0, &DSI_REG[0]->DSI_RX_DATA0);
DSI_BACKUPREG32(cmdq_trigger_handle,
hSlot, i * 4 + 1, &DSI_REG[0]->DSI_RX_DATA1);
DSI_BACKUPREG32(cmdq_trigger_handle,
hSlot, i * 4 + 2, &DSI_REG[0]->DSI_RX_DATA2);
DSI_BACKUPREG32(cmdq_trigger_handle,
hSlot, i * 4 + 3, &DSI_REG[0]->DSI_RX_DATA3);
}
/* 3. write RX_RACK */
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_RACK_REG, DSI_REG[dsi_i]->DSI_RACK,
DSI_RACK, 1);
/* 4. polling not busy(no need clear) */
if (dsi_i == 0) {
DSI_POLLREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_INTSTA,
0x80000000, 0);
}
#if 0
else { /* DSI1 */
DSI_POLLREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_INTSTA,
0x80000000, 0);
}
#endif
/* loop: 0~4 */
}
/* DSI_OUTREGBIT(cmdq_trigger_handle, DSI_INT_ENABLE_REG,DSI_REG[dsi_i]->DSI_INTEN,RD_RDY,0); */
} else if (state == CMDQ_ESD_CHECK_CMP) {
// 下发CMDQ_ESD_CHECK_READ命令的数据处理
DISPCHECK("[DSI]enter cmp\n");
/* cmp just once and only 1 return value */
for (i = 0; i < 3; i++) {
if (dsi_params->lcm_esd_check_table[i].cmd == 0)
break;
DISPCHECK("[DSI]enter cmp i=%d\n", i);
/* read data */
if (hSlot) {
/* read from slot */
cmdqBackupReadSlot(hSlot, i * 4 + 0, ((uint32_t *)&read_data0));
cmdqBackupReadSlot(hSlot, i * 4 + 1, ((uint32_t *)&read_data1));
cmdqBackupReadSlot(hSlot, i * 4 + 2, ((uint32_t *)&read_data2));
cmdqBackupReadSlot(hSlot, i * 4 + 3, ((uint32_t *)&read_data3));
} else {
/* read from dsi , support only one cmd read */
if (i == 0) {
DSI_OUTREG32(NULL, &read_data0,
AS_UINT32(&DSI_REG[dsi_i]->DSI_RX_DATA0));
DSI_OUTREG32(NULL, &read_data1,
AS_UINT32(&DSI_REG[dsi_i]->DSI_RX_DATA1));
DSI_OUTREG32(NULL, &read_data2,
AS_UINT32(&DSI_REG[dsi_i]->DSI_RX_DATA2));
DSI_OUTREG32(NULL, &read_data3,
AS_UINT32(&DSI_REG[dsi_i]->DSI_RX_DATA3));
}
}
MMProfileLogEx(ddp_mmp_get_events()->esd_rdlcm, MMProfileFlagPulse,
AS_UINT32(&read_data0),
AS_UINT32(&(dsi_params->lcm_esd_check_table[i])));
DISPDBG("[DSI]enter cmp read_data0 byte0=0x%x byte1=0x%x byte2=0x%x byte3=0x%x\n",
read_data0.byte0,
read_data0.byte1,
read_data0.byte2,
read_data0.byte3);
DISPDBG
("[DSI]enter cmp check_table cmd=0x%x,count=0x%x,para_list[0]=0x%x,para_list[1]=0x%x\n",
dsi_params->lcm_esd_check_table[i].cmd,
dsi_params->lcm_esd_check_table[i].count,
dsi_params->lcm_esd_check_table[i].para_list[0],
dsi_params->lcm_esd_check_table[i].para_list[1]);
DISPDBG("[DSI]enter cmp DSI+0x200=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x200));
DISPDBG("[DSI]enter cmp DSI+0x204=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x204));
DISPDBG("[DSI]enter cmp DSI+0x60=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x60));
DISPDBG("[DSI]enter cmp DSI+0x74=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x74));
DISPDBG("[DSI]enter cmp DSI+0x88=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x88));
DISPDBG("[DSI]enter cmp DSI+0x0c=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x0c));
/* 0x02: acknowledge & error report */
/* 0x11: generic short read response(1 byte return) */
/* 0x12: generic short read response(2 byte return) */
/* 0x1a: generic long read response */
/* 0x1c: dcs long read response */
/* 0x21: dcs short read response(1 byte return) */
/* 0x22: dcs short read response(2 byte return) */
//根据读取到read_data0.byte0的值,来判断lcm发过来的是什么类型的数据包,进行相应的判断处理
packet_type = read_data0.byte0;
if (packet_type == 0x1A || packet_type == 0x1C) {
recv_data_cnt = read_data0.byte1 + read_data0.byte2 * 16;
if (recv_data_cnt > 2) {
DISPCHECK
("Set receive data count from %d to 2 as ESD check supported max data count.\n",
recv_data_cnt);
recv_data_cnt = 2;
}
if (recv_data_cnt > dsi_params->lcm_esd_check_table[i].count) {
DISPCHECK
("Set receive data count from %d to %d as ESD check table specified.\n",
recv_data_cnt, dsi_params->lcm_esd_check_table[i].count);
recv_data_cnt = dsi_params->lcm_esd_check_table[i].count;
}
DISPCHECK("DSI read long packet size: %d\n", recv_data_cnt);
//比较这种lcm中配置的lcm_esd_check_table[i].para_list[0]值和read_data1.byte0的值是否一致
result = memcmp((void *)&(dsi_params->lcm_esd_check_table[i].para_list[0]),
(void *)&read_data1, recv_data_cnt);
} else if (packet_type == 0x11 ||
packet_type == 0x12 ||
packet_type == 0x21 ||
packet_type == 0x22) {
/* short read response */
if (packet_type == 0x11 || packet_type == 0x21)
recv_data_cnt = 1;
else
recv_data_cnt = 2;
if (recv_data_cnt > dsi_params->lcm_esd_check_table[i].count) {
DISPCHECK
("Set receive data count from %d to %d as ESD check table specified.\n",
recv_data_cnt, dsi_params->lcm_esd_check_table[i].count);
recv_data_cnt = dsi_params->lcm_esd_check_table[i].count;
}
DISPCHECK("DSI read short packet size: %d\n", recv_data_cnt);
//比较这种lcm中配置的lcm_esd_check_table[i].para_list[0]值和read_data0.byte1的值是否一致
result = memcmp((void *)&(dsi_params->lcm_esd_check_table[i].para_list[0]),
(void *)&read_data0.byte1, recv_data_cnt);
} else if (packet_type == 0x02) {
DISPCHECK("read return type is 0x02\n");
result = 1;
} else {
DISPCHECK("read return type is non-recognite, type = 0x%x\n", packet_type);
result = 1;
}
if (result == 0) {
/* clear rx data */
/* DSI_OUTREG32(NULL, &DSI_REG[dsi_i]->DSI_RX_DATA0,0); */
// result等于0的话,esd是ok的,不会进行recovery的动作
ret = 0; /* esd pass */
} else {
// result等于1的话,esd是fail的,会进行recovery的动作
ret = 1; /* esd fail */
break;
}
}
}
……
}
alps/kernel-3.18/drivers/misc/mediatek/video/mt6580/videox/primary_display.c
/* ESD RECOVERY */
int primary_display_esd_recovery(void)
{
......
//重新初始化显示屏参数
disp_lcm_init(pgc->plcm, 1);
......
}
alps/kernel-3.18/drivers/misc/mediatek/video/mt6580/videox/disp_lcm.c
int disp_lcm_init(disp_lcm_handle *plcm, int force)
{
LCM_DRIVER *lcm_drv = NULL;
DISPFUNC();
if (_is_lcm_inited(plcm)) {
lcm_drv = plcm->drv;
if (lcm_drv->init_power) {
if (!disp_lcm_is_inited(plcm) || force) {
DISPMSG("lcm init power()\n");
lcm_drv->init_power();
}
}
if (lcm_drv->init) {
if (!disp_lcm_is_inited(plcm) || force) {
DISPMSG("lcm init()\n");
//这里就是调用到lcm的static void lcm_init(void)函数
lcm_drv->init();
}
} else {
DISPERR("FATAL ERROR, lcm_drv->init is null\n");
return -1;
}
return 0;
}
DISPERR("plcm is null\n");
return -1;
}
三.读寄存器方式做ESD检测,目前只支持读取三个寄存器,各个寄存器只能够识别返回一个值。(只能够识别屏端返回的短包),如果需要识别返回多个值,可以做如下修改
(1)
alps/kernel-3.18/drivers/misc/mediatek/lcm/inc/lcm_drv.h
#define RT_MAX_NUM 10 //该值不可以修改,最大只支持10个返回值
#define ESD_CHECK_NUM 3 //该值表示目前最多可以读取的寄存器个数,尽量不要修改,修改成越大,esc check的负载越重,系统运行时更慢
typedef struct {
unsigned char cmd;
unsigned char count;
unsigned char para_list[RT_MAX_NUM];
} LCM_esd_check_item;
typedef struct {
……
LCM_esd_check_item lcm_esd_check_table[ESD_CHECK_NUM];
……
} LCM_DSI_PARAMS;
(2)
alps/kernel-3.18/drivers/misc/mediatek/lcm/xxx/xxx.c
alps/vendor/mediatek/proprietary/bootable/bootloader/lk/dev/lcm/xxx/xxx.c
static void lcm_get_params(LCM_PARAMS *params)
{
……
//配置三个lcm的寄存器地址,每个寄存器配置10个值,如下,寄存器的值要配置正确,否则会不断地esd recovery
params->dsi.esd_check_enable = 1;
params->dsi.customization_esd_check_enable = 1;
params->dsi.lcm_esd_check_table[0].cmd = 0xBa;
params->dsi.lcm_esd_check_table[0].count = 10;
params->dsi.lcm_esd_check_table[0].para_list[0] = 0x32;
params->dsi.lcm_esd_check_table[0].para_list[1] = 0x81;
params->dsi.lcm_esd_check_table[0].para_list[2] = 0x05;
params->dsi.lcm_esd_check_table[0].para_list[3] = 0xF9;
params->dsi.lcm_esd_check_table[0].para_list[4] = 0x0e;
params->dsi.lcm_esd_check_table[0].para_list[5] = 0x0e;
params->dsi.lcm_esd_check_table[0].para_list[6] = 0x02;
params->dsi.lcm_esd_check_table[0].para_list[7] = 0x00;
params->dsi.lcm_esd_check_table[0].para_list[8] = 0x00;
params->dsi.lcm_esd_check_table[0].para_list[9] = 0x00;
params->dsi.lcm_esd_check_table[1].cmd = 0xc1;
params->dsi.lcm_esd_check_table[1].count = 10;
params->dsi.lcm_esd_check_table[1].para_list[0] = 0x54;
params->dsi.lcm_esd_check_table[1].para_list[1] = 0x00;
params->dsi.lcm_esd_check_table[1].para_list[2] = 0x1E;
params->dsi.lcm_esd_check_table[1].para_list[3] = 0x1E;
params->dsi.lcm_esd_check_table[1].para_list[4] = 0x77;
params->dsi.lcm_esd_check_table[1].para_list[5] = 0xF1;
params->dsi.lcm_esd_check_table[1].para_list[6] = 0xFF;
params->dsi.lcm_esd_check_table[1].para_list[7] = 0xFF;
params->dsi.lcm_esd_check_table[1].para_list[8] = 0xCC;
params->dsi.lcm_esd_check_table[1].para_list[9] = 0xCC;
params->dsi.lcm_esd_check_table[2].cmd = 0xe9;
params->dsi.lcm_esd_check_table[2].count = 10;
params->dsi.lcm_esd_check_table[2].para_list[0] = 0x02;
params->dsi.lcm_esd_check_table[2].para_list[1] = 0x00;
params->dsi.lcm_esd_check_table[2].para_list[2] = 0x10;
params->dsi.lcm_esd_check_table[2].para_list[3] = 0x05;
params->dsi.lcm_esd_check_table[2].para_list[4] = 0x16;
params->dsi.lcm_esd_check_table[2].para_list[5] = 0x0A;
params->dsi.lcm_esd_check_table[2].para_list[6] = 0xA0;
params->dsi.lcm_esd_check_table[2].para_list[7] = 0x12;
params->dsi.lcm_esd_check_table[2].para_list[8] = 0x31;
params->dsi.lcm_esd_check_table[2].para_list[9] = 0x23;
}
(3)
alps/kernel-3.18/drivers/misc/mediatek/video/mt6735/ddp_dsi.c
int ddp_dsi_build_cmdq(DISP_MODULE_ENUM module, void *cmdq_trigger_handle, CMDQ_STATE state)
{
int ret = 0, result = 0;
int i = 0, j = 0;
int dsi_i = 0;
LCM_DSI_PARAMS *dsi_params = NULL;
DSI_T0_INS t0, t1;
struct DSI_RX_DATA_REG read_data0,read_data1,read_data2,read_data3;
unsigned char buffer[20];
uint32_t recv_data_cnt;
unsigned char packet_type;
unsigned int h = 0;
static cmdqBackupSlotHandle hSlot[4] = {0, 0, 0, 0};
if (DISP_MODULE_DSIDUAL == module)
dsi_i = 0;
else
dsi_i = DSI_MODULE_to_ID(module);
dsi_params = &_dsi_context[dsi_i].dsi_params;
if (cmdq_trigger_handle == NULL) {
DISPMSG("cmdq_trigger_handle is NULL\n");
return -1;
}
if (state == CMDQ_BEFORE_STREAM_SOF) {
/* need waiting te */
if (module == DISP_MODULE_DSI0) {
if (dsi0_te_enable == 0)
return 0;
#ifndef MTK_FB_CMDQ_DISABLE
ret =
cmdqRecClearEventToken(cmdq_trigger_handle, CMDQ_EVENT_DSI_TE);
ret = cmdqRecWait(cmdq_trigger_handle, CMDQ_EVENT_DSI_TE);
#endif
}
#if 0
else if (module == DISP_MODULE_DSI1) {
if (dsi1_te_enable == 0)
return 0;
ret =
cmdqRecClearEventToken(cmdq_trigger_handle,
CMDQ_EVENT_MDP_DSI1_TE_SOF);
ret = cmdqRecWait(cmdq_trigger_handle, CMDQ_EVENT_MDP_DSI1_TE_SOF);
} else if (module == DISP_MODULE_DSIDUAL) {
if (dsidual_te_enable == 0)
return 0;
/* TODO: dsi 8 lane do not use te???? */
/* ret = cmdqRecWait(cmdq_trigger_handle, CMDQ_EVENT_MDP_DSI0_TE_SOF); */
}
#endif
else {
DISPERR("wrong module: %s\n", ddp_get_module_name(module));
return -1;
}
} else if (state == CMDQ_CHECK_IDLE_AFTER_STREAM_EOF) {
/* need waiting te */
if (module == DISP_MODULE_DSI0) {
DSI_POLLREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_INTSTA,
0x80000000, 0);
}
#if 0
else if (module == DISP_MODULE_DSI1) {
DSI_POLLREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_INTSTA,
0x80000000, 0);
} else if (module == DISP_MODULE_DSIDUAL) {
DSI_POLLREG32(cmdq_trigger_handle, &DSI_REG[0]->DSI_INTSTA,
0x80000000, 0);
DSI_POLLREG32(cmdq_trigger_handle, &DSI_REG[1]->DSI_INTSTA,
0x80000000, 0);
}
#endif
else {
DISPERR("wrong module: %s\n", ddp_get_module_name(module));
return -1;
}
} else if (state == CMDQ_ESD_CHECK_READ) {
/* enable dsi interrupt: RD_RDY/CMD_DONE (need do this here?) */
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_INT_ENABLE_REG,
DSI_REG[dsi_i]->DSI_INTEN, RD_RDY, 1);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_INT_ENABLE_REG,
DSI_REG[dsi_i]->DSI_INTEN, CMD_DONE, 1);
for (i = 0; i < 3; i++) {
if (dsi_params->lcm_esd_check_table[i].cmd == 0)
break;
/* 0. send read lcm command(short packet) */
t0.CONFG = 0x04; /* BTA */
t0.Data0 = dsi_params->lcm_esd_check_table[i].cmd;
/* / 0xB0 is used to distinguish DCS cmd or Gerneric cmd, is that Right??? */
t0.Data_ID =
(t0.Data0 <
0xB0) ? DSI_DCS_READ_PACKET_ID :
DSI_GERNERIC_READ_LONG_PACKET_ID;
t0.Data1 = 0;
t1.CONFG = 0x00;
t1.Data0 = dsi_params->lcm_esd_check_table[i].count;
t1.Data1 = 0x00;
t1.Data_ID = 0x37;
/* write DSI CMDQ */
DSI_OUTREG32(cmdq_trigger_handle, &DSI_CMDQ_REG[dsi_i]->data[0],
AS_UINT32(&t1));
DSI_OUTREG32(cmdq_trigger_handle, &DSI_CMDQ_REG[dsi_i]->data[1],
AS_UINT32(&t0));
DSI_OUTREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_CMDQ_SIZE,
2);
/* start DSI */
DSI_OUTREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_START, 0);
DSI_OUTREG32(cmdq_trigger_handle, &DSI_REG[dsi_i]->DSI_START, 1);
/* 1. wait DSI RD_RDY(must clear, in case of cpu RD_RDY interrupt handler) */
if (dsi_i == 0) { /* DSI0 */
DSI_POLLREG32(cmdq_trigger_handle,
&DSI_REG[dsi_i]->DSI_INTSTA, 0x00000001, 0x1);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_INT_STATUS_REG,
DSI_REG[dsi_i]->DSI_INTSTA, RD_RDY, 0);
}
#if 0
else { /* DSI1 */
DSI_POLLREG32(cmdq_trigger_handle,
&DSI_REG[dsi_i]->DSI_INTSTA, 0x00000001, 0x1);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_INT_STATUS_REG,
DSI_REG[dsi_i]->DSI_INTSTA, RD_RDY, 0);
}
#endif
/* 2. save RX data */
if (hSlot[0] && hSlot[1] && hSlot[2] && hSlot[3]) {
DSI_BACKUPREG32(cmdq_trigger_handle, hSlot[0], i,
&DSI_REG[0]->DSI_RX_DATA0);
DSI_BACKUPREG32(cmdq_trigger_handle, hSlot[1], i,
&DSI_REG[0]->DSI_RX_DATA1);
DSI_BACKUPREG32(cmdq_trigger_handle, hSlot[2], i,
&DSI_REG[0]->DSI_RX_DATA2);
DSI_BACKUPREG32(cmdq_trigger_handle, hSlot[3], i,
&DSI_REG[0]->DSI_RX_DATA3);
}
/* 3. write RX_RACK */
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_RACK_REG,
DSI_REG[dsi_i]->DSI_RACK, DSI_RACK, 1);
/* 4. polling not busy(no need clear) */
if (dsi_i == 0) { /* DSI0 */
DSI_POLLREG32(cmdq_trigger_handle,
&DSI_REG[dsi_i]->DSI_INTSTA, 0x80000000, 0);
}
#if 0
else { /* DSI1 */
DSI_POLLREG32(cmdq_trigger_handle,
&DSI_REG[dsi_i]->DSI_INTSTA, 0x80000000, 0);
}
#endif
/* loop: 0~4 */
}
/* DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_INT_ENABLE_REG,DSI_REG[dsi_i]->DSI_INTEN,RD_RDY,0); */
} else if (state == CMDQ_ESD_CHECK_CMP) {
DISPMSG("[DSI]enter cmp\n");
/* cmp just once and only 1 return value */
for (i = 0; i < 3; i++) {
if (dsi_params->lcm_esd_check_table[i].cmd == 0)
break;
DISPMSG("[DSI]enter cmp i=%d\n", i);
/* read data */
if (hSlot[0] && hSlot[1] && hSlot[2] && hSlot[3]) {
/* read from slot */
cmdqBackupReadSlot(hSlot[0], i, ((uint32_t *)&read_data0));
cmdqBackupReadSlot(hSlot[1], i, ((uint32_t *)&read_data1));
cmdqBackupReadSlot(hSlot[2], i, ((uint32_t *)&read_data2));
cmdqBackupReadSlot(hSlot[3], i, ((uint32_t *)&read_data3));
} else {
/* read from dsi , support only one cmd read */
if (i == 0) {
DSI_OUTREG32(NULL, &read_data0,AS_UINT32(&DSI_REG[dsi_i]->DSI_RX_DATA0));
DSI_OUTREG32(NULL, &read_data1,AS_UINT32(&DSI_REG[dsi_i]->DSI_RX_DATA1));
DSI_OUTREG32(NULL, &read_data2,AS_UINT32(&DSI_REG[dsi_i]->DSI_RX_DATA2));
DSI_OUTREG32(NULL, &read_data3,AS_UINT32(&DSI_REG[dsi_i]->DSI_RX_DATA3));
}
}
MMProfileLogEx(ddp_mmp_get_events()->esd_rdlcm, MMProfileFlagPulse,
AS_UINT32(&read_data0),
AS_UINT32(&(dsi_params->lcm_esd_check_table[i])));
DISPDBG
("[DSI]enter cmp read_data0 byte0=0x%x byte1=0x%x byte2=0x%x byte3=0x%x\n",
read_data0.byte0, read_data0.byte1, read_data0.byte2,
read_data0.byte3);
DISPDBG
("[DSI]cmp check_table cmd=0x%x,count=0x%x,para_list[0]=0x%x,para_list[1]=0x%x\n",
dsi_params->lcm_esd_check_table[i].cmd,
dsi_params->lcm_esd_check_table[i].count,
dsi_params->lcm_esd_check_table[i].para_list[0],
dsi_params->lcm_esd_check_table[i].para_list[1]);
DISPDBG("[DSI]enter cmp DSI+0x200=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x200));
DISPDBG("[DSI]enter cmp DSI+0x204=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x204));
DISPDBG("[DSI]enter cmp DSI+0x60=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x60));
DISPDBG("[DSI]enter cmp DSI+0x74=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x74));
DISPDBG("[DSI]enter cmp DSI+0x88=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x88));
DISPDBG("[DSI]enter cmp DSI+0x0c=0x%x\n",
AS_UINT32(DDP_REG_BASE_DSI0 + 0x0c));
/* 0x02: acknowledge & error report */
/* 0x11: generic short read response(1 byte return) */
/* 0x12: generic short read response(2 byte return) */
/* 0x1a: generic long read response */
/* 0x1c: dcs long read response */
/* 0x21: dcs short read response(1 byte return) */
/* 0x22: dcs short read response(2 byte return) */
packet_type = read_data0.byte0;
if (packet_type == 0x1A || packet_type == 0x1C) {
recv_data_cnt = read_data0.byte1 + read_data0.byte2 * 16;
DISPDBG("packet_type=0x%x,recv_data_cnt = %d\n", packet_type, recv_data_cnt);
if(recv_data_cnt > RT_MAX_NUM)
{
DISPMSG("DSI read long packet data exceeds 10 bytes \n");
recv_data_cnt = RT_MAX_NUM;
}
if (recv_data_cnt > dsi_params->lcm_esd_check_table[i].count) {
recv_data_cnt = dsi_params->lcm_esd_check_table[i].count;
}
if (recv_data_cnt <= 4) {
memcpy((void *)buffer, (void *)&read_data1, recv_data_cnt);
} else if (recv_data_cnt <= 8) {
memcpy((void *)buffer, (void *)&read_data1, 4);
memcpy((void *)(buffer + 4), (void *)&read_data2, recv_data_cnt - 4);
} else {
memcpy((void *)buffer, (void *)&read_data1, 4);
memcpy((void *)(buffer + 4), (void *)&read_data2, 4);
memcpy((void *)(buffer + 8), (void *)&read_data3, recv_data_cnt - 8);
}
for (j = 0; j < recv_data_cnt; j++) {
DISPDBG("buffer[%d]=0x%x\n", j, buffer[j]);
if (buffer[j] != dsi_params->lcm_esd_check_table[i].para_list[j]) {
result= 1;
DISPMSG("[ESD]CMP i %d return value 0x%x,para_list[%d]=0x%x\n", i,
buffer[j], j, dsi_params->lcm_esd_check_table[i].para_list[j]);
break;
}
}
} else if (packet_type == 0x11 ||
packet_type == 0x12 ||
packet_type == 0x21 ||
packet_type == 0x22) {
/* short read response */
if (packet_type == 0x11 || packet_type == 0x21)
recv_data_cnt = 1;
else
recv_data_cnt = 2;
if (recv_data_cnt > dsi_params->lcm_esd_check_table[i].count) {
recv_data_cnt = dsi_params->lcm_esd_check_table[i].count;
}
memcpy((void *)buffer, (void *)&read_data0.byte1, recv_data_cnt);
DISPDBG("packet_type=0x%x,recv_data_cnt = %d\n", packet_type, recv_data_cnt);
for (j = 0; j < recv_data_cnt; j++) {
DISPDBG("buffer[%d]=0x%x\n", j, buffer[j]);
if (buffer[j] != dsi_params->lcm_esd_check_table[i].para_list[j]) {
result= 1;
DISPMSG("[ESD]CMP i %d return value 0x%x,para_list[%d]=0x%x\n", i,
buffer[j], j, dsi_params->lcm_esd_check_table[i].para_list[j]);
break;
}
}
} else if (packet_type == 0x02) {
DISPMSG("read return type is 0x02\n");
result = 1;
} else {
DISPMSG("read return type is non-recognite, type = 0x%x\n", packet_type);
result = 1;
}
if (result == 0) {
/* clear rx data */
/* DSI_OUTREG32(NULL, &DSI_REG[dsi_i]->DSI_RX_DATA0,0); */
ret = 0; /* esd pass */
} else {
ret = 1; /* esd fail */
break;
}
}
} else if (state == CMDQ_ESD_ALLC_SLOT) {
/* create 3*4 slot */
for(h = 0; h < 4; h++){
cmdqBackupAllocateSlot(&hSlot[h], 3);
}
} else if (state == CMDQ_ESD_FREE_SLOT) {
if (hSlot[0] && hSlot[1] && hSlot[2] && hSlot[3]) {
for(h = 0; h < 4; h++){
cmdqBackupFreeSlot(hSlot[h]);
hSlot[h] = 0;
}
}
} else if (state == CMDQ_STOP_VDO_MODE) {
/* use cmdq to stop dsi vdo mode */
/* -1. stop TE_RDY IRQ */
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_INT_ENABLE_REG,
DSI_REG[i]->DSI_INTEN, TE_RDY, 0);
/* 0. set dsi cmd mode */
DSI_SetMode(module, cmdq_trigger_handle, CMD_MODE);
/* 1. polling dsi not busy */
i = DSI_MODULE_BEGIN(module);
if (i == 0) {
/* DSI0/DUAL */
/* polling vm done */
/* polling dsi busy */
DSI_POLLREG32(cmdq_trigger_handle, &DSI_REG[i]->DSI_INTSTA,
0x80000000, 0);
#if 0
i = DSI_MODULE_END(module);
if (i == 1) { /* DUAL */
DSI_POLLREG32(cmdq_trigger_handle, &DSI_REG[i]->DSI_INTSTA,
0x80000000, 0);
}
#endif
}
#if 0
else { /* DSI1 */
DSI_POLLREG32(cmdq_trigger_handle, &DSI_REG[i]->DSI_INTSTA,
0x80000000, 0);
}
#endif
/* 2.dual dsi need do reset DSI_DUAL_EN/DSI_START */
if (module == DISP_MODULE_DSIDUAL) {
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_COM_CTRL_REG,
DSI_REG[0]->DSI_COM_CTRL, DSI_DUAL_EN, 0);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_COM_CTRL_REG,
DSI_REG[1]->DSI_COM_CTRL, DSI_DUAL_EN, 0);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_START_REG,
DSI_REG[0]->DSI_START, DSI_START, 0);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_START_REG,
DSI_REG[1]->DSI_START, DSI_START, 0);
}
/* 3.disable HS */
/* DSI_clk_HS_mode(module, cmdq_trigger_handle, false); */
} else if (state == CMDQ_START_VDO_MODE) {
/* 0. dual dsi set DSI_START/DSI_DUAL_EN */
if (module == DISP_MODULE_DSIDUAL) {
/* must set DSI_START to 0 before set dsi_dual_en, don't know why.2014.02.15 */
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_START_REG,
DSI_REG[0]->DSI_START, DSI_START, 0);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_START_REG,
DSI_REG[1]->DSI_START, DSI_START, 0);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_COM_CTRL_REG,
DSI_REG[0]->DSI_COM_CTRL, DSI_DUAL_EN, 1);
DSI_OUTREGBIT(cmdq_trigger_handle, struct DSI_COM_CTRL_REG,
DSI_REG[1]->DSI_COM_CTRL, DSI_DUAL_EN, 1);
}
/* 1. set dsi vdo mode */
DSI_SetMode(module, cmdq_trigger_handle, dsi_params->mode);
/* 2. enable HS */
/* DSI_clk_HS_mode(module, cmdq_trigger_handle, true); */
/* 3. enable mutex */
/* ddp_mutex_enable(mutex_id_for_latest_trigger,0,cmdq_trigger_handle); */
/* 4. start dsi */
/* DSI_Start(module, cmdq_trigger_handle); */
} else if (state == CMDQ_DSI_RESET) {
DISPMSG("CMDQ Timeout, Reset DSI\n");
DSI_DumpRegisters(module, 1);
DSI_Reset(module, NULL);
}
return ret;
}
四.案例分析
1.哪个案子的哪种显示屏:V166-357D jd9365
2.现象:有进行复位动作,但是屏无法恢复
3.分析:读0x0a寄存器,出现异常的情况为0x18或者0x08,且经过连续5次recovery之后异常没有消除,最终退出了recovery kthread屏也没有恢复
4.解决办法:
a.修改lcm_init和lcm_suspend时lcm rst脚的控制方法及延时的时间,保证每一次初始化过程更合理有效
b.增加初始化时下发0x11的次数确保下发成功
c. 在primary_display_esd_check_worker_kthread中增加复位延时的时间,确保有足够的时间能使0x0a恢复正常之后再进行下一次esd check
解决办法主要是c,另外中间的延时可以具体细调,不能延时过高影响用户体验,也不能延时太低再出现esd recovery fail。