MTK平台关于lcm esd check机制的介绍

原创文章,转载请注明出处

一、esd check的作用

esd意思是静电释放,在手机领域中,都是有静电标准的。静电的危害也是很明显的,在手机使用过程中可能会因为静电原因造成手机屏幕显示异常,触摸屏失灵,严重点还可能造成死机等现象。因此消除静电是一项很重要的工作,有硬件和软件两种措施,这里我们只介绍软件上的处理,即esd check。其实这种方法无法避免esd,只是一种遇到esd时软件上做的一些处理而已。

二、esd check的两种方式

mtk平台对于lcm的esd check有两种处理方式,分别是读寄存器方式和外部TE方式

1、读寄存器方式

通过读寄存器方式做ESD,只需要在lcm driver里面的lcm_get_params函数中按照如下的方式进行客制化配置就能够正常的run了

PS:MT6737在ESD读取寄存器时候,只能够识别屏端返回的短包,并且只能够判断一个返回值。如果有LCM IC返回的是长包,或者需要判断多个返回值。

2、外部TE方式

该方式是通过CPU irq中断的方式实现的,具体按如下配置既可

1)lcm driver中的lcm_get_params函数

2)lcm driver中的lcm_init函数

关于LCM IC中0x35的说明:

H-Blanking:在将光信号转换为电信号的扫描过程中,扫描总是从图像的左上角开始,水平向前行进,同时扫描点也以较慢的速率向下移动。当扫描点到达图像右侧边缘时,扫描点快速返回左侧,重新开始在第1行的起点下面进行第2行扫描,行与行之间的返回过程称为H-Blanking。

V-Blanking:一幅完整的图像扫描信号,由H-Blanking间隔分开的行信号序列构成,称为一帧。扫描点扫描完一帧后,要从图像的右下角返回到图像的左上角,开始新一帧的扫描,这一时间间隔,叫做V-Blanking

3)项目工程中dct的配置

三、软件流程

kernel-3.18\drivers\misc\mediatek\video\mt6735\primary_display.c

unsigned int _need_do_esd_check(void)	//esd总开关	返回1有效
{
	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;
}
unsigned int _need_register_eint(void)		//TE模式开关
{
	int ret = 1;

	/* 1.need do esd check */
	/* 2.dsi vdo mode */
	/* 3.customization_esd_check_enable = 0 */
	if (_need_do_esd_check() == 0)
		ret = 0;
	else if (primary_display_is_video_mode() == 0)
		ret = 0;
	else if (pgc->plcm->params->dsi.customization_esd_check_enable == 1)
		ret = 0;

	return ret;
}
int primary_display_init(char *lcm_name, unsigned int lcm_fps, int is_lcm_inited)
{
……
#ifdef MTK_FB_ESD_ENABLE
	primary_display_esd_check_task = kthread_create(primary_display_esd_check_worker_kthread,
							NULL, "display_esd_check");    //esd check线程
	init_waitqueue_head(&esd_ext_te_wq);	//等待队列 用于TE模式
	init_waitqueue_head(&esd_check_task_wq);	//用于整个esd流程
	if (_need_do_esd_check())
		wake_up_process(primary_display_esd_check_task);

	if (_need_register_eint()) {
		struct device_node *node = NULL;
		int irq;
		u32 ints[2] = { 0, 0 };
	 	node = of_find_compatible_node(NULL, NULL, "mediatek, DSI_TE_1-eint");//获取TE中断信息
		if (node) {
			of_property_read_u32_array(node, "debounce", ints, ARRAY_SIZE(ints));
			/* FIXME: find definatition of  mt_gpio_set_debounce */
			/* mt_gpio_set_debounce(ints[0], ints[1]); */
			mt_eint_set_hw_debounce(ints[0], ints[1]);
			irq = irq_of_parse_and_map(node, 0);
			if (request_irq(irq, _esd_check_ext_te_irq_handler, IRQF_TRIGGER_RISING, 
"DSI_TE_1-eint", NULL))    //注册TE中断函数
				DISPMSG("[ESD]EINT IRQ LINE NOT AVAILABLE!!\n");
			else
				eint_flag++;

		} else {
			DISPMSG("[ESD][%s] can't find DSI_TE_1 eint compatible node\n", __func__);
		}
	}


	if (_need_do_esd_check())
		primary_display_esd_check_enable(1);    //使能esd check

#endif
……
}
void primary_display_esd_check_enable(int enable)
{
	/* check if enable ESD check mechanism first */
	if (_is_enable_esd_check() != true) {
		DISPMSG("[ESD]Checking if ESD enable but ESD check mechanism is not enabled.\n");
		return;
	}

	if (_need_do_esd_check()) {
		if (_need_register_eint() && eint_flag != 2) {
			DISPMSG("[ESD]Please check DCT setting about GPIO107/EINT107\n");
			return;
		}

		if (enable) {
			pr_warn("[DISP][ESD]esd check thread wakeup\n");
			atomic_set(&esd_check_task_wakeup, 1);    
			wake_up_interruptible(&esd_check_task_wq);    //唤醒esd check的等待队列
		} else {
			DISPMSG("[ESD]esd check thread stop\n");
			atomic_set(&esd_check_task_wakeup, 0);
		}
	}
}
static int primary_display_esd_check_worker_kthread(void *data)
{
	int ret = 0;
	int i = 0;
	int esd_try_cnt = 5;	/* 20; */
	int count = 0;
	struct sched_param param = {.sched_priority = 87 }; /* RTPM_PRIO_FB_THREAD */

	sched_setscheduler(current, SCHED_RR, &param);
	dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_DONE);
	dpmgr_enable_event(pgc->dpmgr_handle, DISP_PATH_EVENT_FRAME_START);

	while (1) {
		if (count == 0) {
			count++;
			msleep(3000);
		}
		msleep(2000);	/* esd check every 2s */
		ret = wait_event_interruptible(esd_check_task_wq, atomic_read(&esd_check_task_wakeup));
		if (ret < 0) {
			DISPMSG("[ESD]esd check thread waked up accidently\n");
			continue;
		}
#ifdef DISP_SWITCH_DST_MODE
		_primary_path_switch_dst_lock();
#endif
		_primary_path_cmd_lock();
		ret = primary_display_esd_check();//esd check核心函数,成功返回0,失败返回1
		if (ret == 1) {
			DISPMSG("[ESD]esd check fail, will do esd recovery %d\n", ret);
			i = esd_try_cnt;
			while (i--) {
				DISPMSG("[ESD]esd recovery try:%d\n", i);
				primary_display_esd_recovery();//esd check失败后会这行recovery操作
				ret = primary_display_esd_check();//recovery之后再次做esd check
				if (ret == 0) {//返回0说明recovery成功,退出循环
					DISPMSG("[ESD]esd recovery success\n");
					break;
				}
				DISPMSG("[ESD]after esd recovery, esd check still fail\n");
				if (i == 0) {//如果五次recovery都失败则会disable esd check操作,关闭对应线程
					DISPMSG("[ESD]after esd recovery %d times, esd check still fail,\n",
						  esd_try_cnt);
					DISPMSG("disable esd check\n");
					primary_display_esd_check_enable(0);//
					primary_display_esd_recovery();
				}
			}
		}
		_primary_path_cmd_unlock();
#ifdef DISP_SWITCH_DST_MODE
		_primary_path_switch_dst_unlock();
#endif
		if (kthread_should_stop())
			break;
	}
	return 0;
}
int primary_display_esd_check(void)
{
……
	/* Esd Check : EXT TE */
		if (pgc->plcm->params->dsi.customization_esd_check_enable == 0) {//外部TE方式
		MMProfileLogEx(ddp_mmp_get_events()->esd_extte, MMProfileFlagStart, 0, 0);
		if (primary_display_is_video_mode()) {
			……
				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) {//检查TE中断脚是否有触发中断,如果HZ/2之内没有触发中断,则说明发生了esd
					ret = 0; /* esd check pass */
				} else {
					ret = 1; /* esd check fail */
					DISPMSG("esd check fail release fence fake\n");
					primary_display_release_fence_fake();
				}
				atomic_set(&esd_ext_te_event, 0);
			}
			primary_display_switch_esd_mode(0);
		} else {
			……
		}
		MMProfileLogEx(ddp_mmp_get_events()->esd_extte, MMProfileFlagEnd, 0, ret);
		goto done;
	}
	/* Esd Check : Read from lcm */
	……
		ret = dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_config_esd, 
CMDQ_ESD_CHECK_CMP);//读寄存器方式
		if (ret) {
			ret = 1;	/* esd check fail */
			DISPMSG("esd check fail release fence fake\n");
			primary_display_release_fence_fake();
		} 
	……
}
static irqreturn_t _esd_check_ext_te_irq_handler(int irq, void *data)//TE中断函数
{
	MMProfileLogEx(ddp_mmp_get_events()->esd_vdo_eint, MMProfileFlagPulse, 0, 0);
	atomic_set(&esd_ext_te_event, 1);
	wake_up_interruptible(&esd_ext_te_wq);
	return IRQ_HANDLED;
}
int dpmgr_path_build_cmdq(disp_path_handle dp_handle, void *trigger_loop_handle, CMDQ_STATE state)
{
……
	for ( i=0; i< module_num; i++) {
		module_name = modules[i];
		if (ddp_modules_driver[module_name] != 0) {
			if (ddp_modules_driver[module_name]->build_cmdq!= 0) {
				DISP_LOG_D("%s build cmdq, state=%d\n",ddp_get_module_name(module_name), state);
				ddp_modules_driver[module_name]->build_cmdq(module_name, trigger_loop_handle, state); //读寄存器方式,最终会调用build_cmdq成员函数
			}
		}
	}
	return 0;
}

H	int ddp_dsi_build_cmdq(DISP_MODULE_ENUM module, void *cmdq_trigger_handle, CMDQ_STATE state)
{
……
	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;
			/* read data */
			if (hSlot) {
				/* read from slot */
				cmdqBackupReadSlot(hSlot, i, ((uint32_t *) &read_data0));
			} 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));
				}
			}
				if (read_data0.byte1 ==
			    dsi_params->lcm_esd_check_table[i].para_list[0]) {//如果读取原先设置好的寄存器的值与预想的不一样,则判定发生esd
				/* clear rx data */
				/* DSI_OUTREG32(NULL, &DSI_REG[dsi_i]->DSI_RX_DATA0,0); */
				ret = 0;	/* esd pass */
			} else {
				ret = 1;	/* esd fail */
				break;
			}
		}
……
}
int primary_display_esd_recovery(void)//recovery操作实际是对lcm进行init操作
{
……
disp_lcm_suspend(pgc->plcm);
disp_lcm_init(pgc->plcm, 1);
……
}

以上就是整个esd check的流程

四、相关案例分析

1、开启esd之后不生效,故意改错返回值也不生效

lcm driver中的lcm_get_params函数中没有将customization_esd_check_enable设置为1

2、设置多个返回值,除了第一个,其余无效

MT6737在ESD读取寄存器时候,只能够识别屏端返回的短包,并且只能够判断一个返回值

3、开启esd之后打静电出现黑屏恢复不了

lcm的特殊性,需在primary_display_esd_check_worker_kthread线程中primary_display_esd_recovery与primary_display_esd_check之间添加延时,让lcm有充足时间recovery成功

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值