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
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值