欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

34. lcm ESD

程序员文章站 2022-07-15 20:56:57
...
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。