原创文章,转载请注明出处
一、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, ¶m);
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成功