MTK LCM驱动分析

参考:

http://blog.csdn.net/sunweizhong1024/article/details/8447915

http://blog.csdn.net/cbk861110/article/details/40931835
 

一.驱动分析5.1
Mtkfb.c (kernel-3.10\drivers\misc\mediatek\videox\mt6735)
 
1.初始化
mtkfb_init
    platform_driver_register(&mtkfb_driver)
 
 
2.重要结构体
static struct fb_ops mtkfb_ops = {
    .owner          = THIS_MODULE,
    .fb_open        = mtkfb_open,
    .fb_release     = mtkfb_release,
    .fb_setcolreg   = mtkfb_setcolreg,
    .fb_pan_display = mtkfb_pan_display_proxy,
    .fb_fillrect    = cfb_fillrect,
    .fb_copyarea    = cfb_copyarea,
    .fb_imageblit   = cfb_imageblit,
    .fb_cursor      = mtkfb_soft_cursor,
    .fb_check_var   = mtkfb_check_var,
    .fb_set_par     = mtkfb_set_par,
    .fb_ioctl       = mtkfb_ioctl,
#ifdef CONFIG_COMPAT
.fb_compat_ioctl = mtkfb_compat_ioctl,
#endif    
#ifdef CONFIG_DMA_SHARED_BUFFER
    .fb_dmabuf_export = mtkfb_dmabuf_export,
#endif
};


probe
    strstr(saved_command_line, "fps=");    //得到UBOOT传过来的参数
    fbi = framebuffer_alloc(sizeof(struct mtkfb_device), dev);  //分配fb_info结构体
       mtkfb_find_lcm_driver :hj101na02a_lcm_drv
            disp_drv_get_lcm_driver
                lcm->set_util_funcs(&lcm_utils); //设置LCD操作函数
                lcm->get_params(lcm_params); //得到LCD的一些硬件参数
                disp_drv_init_ctrl_if  //如果是DBI(mcu 屏)
                    //LCM_CTRL_SERIAL_DBI
                    LCD_Init()
                    LCD_ConfigSerialIF
                    //LCM_CTRL_PARALLEL_DBI
                    LCD_Init()
                    LCD_ConfigParallelIF
                LCD_SelectWriteIF //选择是串口还是并口
                LCD_ConfigIfFormat //配置接口的数据类型
                to_lcd_if_width //数据宽度,DBI
                disp_drv_set_driving_current //设置接口的电流
                LCD_Init_IO_pad //初始化GPIO
            disp_drv_init_context
                DISP_GetDriverDBI :DBI_DISP_DRV 操作函数
                DISP_GetDriverDPI :DPI_DISP_DRV
                DISP_GetDriverDSI :DSI_DISP_DRV
    //得到LCD的一些参数
    DISP_GetScreenWidth
     。。。。。。
    kthread_create(esd_recovery_kthread, NULL, "esd_recovery_kthread"); //esd检测     




得到指点屏幕的信息:
 mtkfb_probe
    primary_display_init(mtkfb_find_lcm_driver(), lcd_fps);
        pgc->plcm = disp_lcm_probe( lcm_name, LCM_INTERFACE_NOTDEFINED);
            lcm_drv = lcm_driver_list[0];或者lcm_drv = lcm_driver_list[i];




二.驱动分析6.0(6735)
Mtkfb.c (kernel-3.18\drivers\misc\mediatek\video\common)
 mtkfb_probe
    p = strstr(saved_command_line, "fps="); //获得命令行参数
    fbi = framebuffer_alloc(sizeof(struct mtkfb_device), dev); //分配 mtkfb_device,creates a new frame buffer info structure
    struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //得到资源,get a resource for a device,这里是IO口资源
    
disp_hal_allocate_framebuffer(res->start, res->end, 
(unsigned int *)&fbdev->fb_va_base, &fb_pa); //
(物理地址映射为虚拟地址),也就是io物理地址映射成虚拟地址
        *va = (unsigned long)ioremap_nocache(pa_start, pa_end - pa_start + 1);
    primary_display_set_frame_buffer_address((unsigned long)fbdev->fb_va_base, fb_pa); //设置frame的虚拟地址
        pgc->framebuffer_va = va;
pgc->framebuffer_mva = mva;
    primary_display_init(mtkfb_find_lcm_driver(), lcd_fps); //早期显示设备初始化,第一个参数是LCM名字 ,
mtkfb_find_lcm_driver单独分析1
        dprec_init
 
            dprec_logger_event_init(&dprec_vsync_irq_event, "VSYNC_IRQ", DPREC_LOGGER_LEVEL_SYSTRACE, NULL);
        dpmgr_init //显示设备管理初始化,
            ddp_debug_init //
Ddp_debug.c (kernel-3.18\drivers\misc\mediatek\video\mt6735\dispsys),初始化调试接口
                debugfs = debugfs_create_file("dispsys", S_IFREG | S_IRUGO, NULL, (void *)0, &debug_fops);
 
                debugDir = debugfs_create_dir("disp", NULL);
                debugfs_dump = debugfs_create_file("dump", S_IFREG | S_IRUGO, debugDir, NULL, &debug_fops_dump);
            disp_init_irq //显示设备中断初始化
                init_waitqueue_head(&disp_irq_log_wq);
                disp_irq_log_task = kthread_create(disp_irq_log_kthread_func, NULL, "ddp_irq_log_kthread"); //单独分析2
                wake_up_process(disp_irq_log_task);
            disp_register_irq_callback(dpmgr_irq_handler); //注册显示中断处理函数,中断处理函数单独分析3
                irq_callback_table[i] = cb;  //中断函数赋值
        pgc->plcm = disp_lcm_probe(lcm_name, LCM_INTERFACE_NOTDEFINED); //lcm probe,做一些初始化,单独分析4
        lcm_param = disp_lcm_get_params(pgc->plcm); //调用具体屏驱动的lcm_get_params,得到屏的参数
            lcm_get_params 
        mtk cmdq的一些设置。。。。。。。
        dpmgr_path_set_video_mode(pgc->dpmgr_handle, primary_display_is_video_mode()); //设置LCM的模式,primary_display_is_video_mode
单独分析5
            handle = (ddp_path_handle) dp_handle;
    handle->mode = is_vdo_mode ? DDP_VIDEO_MODE : DDP_CMD_MODE;
        /* use fake timer to generate vsync signal for cmd mode w/
        if (!primary_display_is_video_mode()) { //如果不是VDO模式,需要设置同步信号
    _init_vsync_fake_monitor(lcm_fps);
        data_config = dpmgr_path_get_last_config(pgc->dpmgr_handle); //获得data_config 
        data_config->dst_w = lcm_param->width;
data_config->dst_h = lcm_param->height;
        data_config->lcm_bpp = 24; //设置BPP
        ret = disp_lcm_init(pgc->plcm, 0);//初始化LCM,调用具体屏的lcm_init
            lcm_drv->init();
                lcm_init
        primary_display_esd_check_task = kthread_create(primary_display_esd_check_worker_kthread, NULL, "display_esd_check"); //静电处理线程,单独分析6
        if (_need_register_eint()) //如果需要TE中断
            (request_irq(irq, _esd_check_ext_te_irq_handler, IRQF_TRIGGER_NONE, "DSI_TE_1-eint", NULL)) //注册中断
        if (_need_do_esd_check()) //需要使能静电保护
primary_display_esd_check_enable(1); //使能
        primary_path_aal_task = kthread_create(_disp_primary_path_check_trigger, NULL, "display_check_aal"); //aal检测线程,_disp_primary_path_check_trigger单独分析7
        present_fence_release_worker_task = kthread_create(_present_fence_release_worker_thread, NULL, "present_fence_worker");  //见Android4.4 fence机制分析章,控制每个buffer的“生命周期”
    disp_register_module_irq_callback(DISP_MODULE_RDMA0, primary_display_frame_update_irq_callback);
            if (module == DISP_MODULE_RDMA0)
                update_frm_seq_info(ddp_ovl_get_cur_addr(1, 0), 0, 1, FRM_START); //更新显存队列信息
disp_register_module_irq_callback(DISP_MODULE_OVL0, primary_display_frame_update_irq_callback);
            if ((module == DISP_MODULE_OVL0) && (_is_decouple_mode(pgc->session_mode) == 0)) {
                wake_up_interruptible(&primary_display_frame_update_wq); //唤醒队列primary_display_frame_update_kthread,后面分析           
disp_register_module_irq_callback(DISP_MODULE_WDMA0, primary_display_frame_update_irq_callback);
            if ((module == DISP_MODULE_WDMA0) && (_is_decouple_mode(pgc->session_mode) == 1)) 
                wake_up_interruptible(&decouple_fence_release_wq); //唤醒decouple_fence_release_wq队列,Android4.4 fence机制分析章,控制每个buffer的“生命周期”
primary_display_frame_update_task = kthread_create(primary_display_frame_update_kthread, NULL,  "frame_update_worker");  primary_display_frame_update_kthread单独分析8
        decouple_fence_release_task = kthread_create(decouple_fence_release_kthread, NULL,) //Android4.4 fence机制分析章,控制每个buffer的“生命周期”
 mtkfb_fbinfo_init //Initialize system fb_info object and set the default video mode.
    r = fb_alloc_cmap(&info->cmap, 32, 0);
    r = mtkfb_check_var(&var, info);
    r = mtkfb_set_par(info);
 register_framebuffer(fbi);
 
    
        
        
    
mtkfb_find_lcm_driver单独分析1,获取LCM名字
    /* mtkfb should parse lcm name from kernel boot command line */
    p = strstr(saved_command_line, "lcm="); //获得LCM的名字,从uboot传过来的参数
    strncpy((char *)mtkfb_lcm_name, (const char *)p, (int)(q - p)); //设置lcm名字
 
 
disp_irq_log_kthread_func 
单独分析2

    Overlay是Android经常看到的名字,我们可以理解为视频叠加,Overlay也可以理解为视频输出
 
    primary_display_reset_ovl_by_cmdq(disp_irq_log_module & 0x1); //重启Overlay
    ddp_dump_reg(i); //打印Overlay寄存器的值
    
 
 
dpmgr_irq_handler 单独分析3
    irq_bits_num = ddp_get_module_max_irq_bit(module); //得到中断寄存器相应中断
    handle = (ddp_path_handle)hw_mutex_id_to_handle_map[i]; //得到相应中断处理函数
    dprec_stub_irq(irq_bit);
        dprec_stub_event(j);
        wake_up_interruptible(&(handle->wq_list[j].wq));  //唤醒处理等待队列
 
 
disp_lcm_probe 单独分析4
disp_lcm_probe
    lcm_drv = lcm_driver_list[i]; //得到LCM driver
    plcm = kzalloc(sizeof(uint8_t *) * sizeof(disp_lcm_handle), GFP_KERNEL);  //分配disp_lcm_handle
    lcm_param = kzalloc(sizeof(uint8_t *) * sizeof(LCM_PARAMS), GFP_KERNEL); //分配LCM_PARAMS
    if (plcm && lcm_param) { //设置
plcm->params = lcm_param;
plcm->drv = lcm_drv;
plcm->is_inited = isLCMInited;
plcm->index = lcmindex;
    plcm->drv->get_params(plcm->params); //这里调用具体屏驱动中的lcm_get_params
    if (plcm->params->type == LCM_TYPE_DSI && plcm->params->lcm_if == LCM_INTERFACE_NOTDEFINED)
plcm->lcm_if_id = LCM_INTERFACE_DSI0; //获得LCM传输总线类型
 
 
primary_display_is_video_mode
单独分析5
    disp_lcm_is_video_mode  //判读是否是VDO模式
 
        if (lcm_param->type == LCM_TYPE_DSI) {
    switch (lcm_param->dsi.mode)
                case SYNC_PULSE_VDO_MODE:
case SYNC_EVENT_VDO_MODE:
case BURST_VDO_MODE:
return true;
 
 
primary_display_esd_check_worker_kthread静电处理线程,单独分析6
    
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);
    ret = primary_display_esd_check();
     while (1) {
        msleep(2000);/* esd check every 2s */ 两秒就会检测一次
        ret = wait_event_interruptible(esd_check_task_wq, atomic_read(&esd_check_task_wakeup));
        两种检测模式
        1./* / Esd Check : EXT TE */ 配置dsi.customization_esd_check_enable == 0
        2./* / Esd Check : Read from lcm */ 配置dsi.customization_esd_check_enable == 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);
MMProfileLogEx(ddp_mmp_get_events()->esd_rdlcm, MMProfileFlagPulse, 0, 2);
        /* 1.use cmdq to read from lcm */
if (primary_display_is_video_mode())
    ret = _esd_check_config_handle_vdo();
         /* 2.check data(*cpu check now) */
        ret = dpmgr_path_build_cmdq(pgc->dpmgr_handle, pgc->cmdq_handle_config_esd, CMDQ_ESD_CHECK_CMP);
MMProfileLogEx(ddp_mmp_get_events()->esd_rdlcm, MMProfileFlagPulse, 0, 4);
        /* 3.destroy esd config thread */
cmdqRecDestroy(pgc->cmdq_handle_config_esd);
}
if (ret == 1) { //如果发现LCM被打死,需要重启
    primary_display_esd_recovery();
                DISPCHECK("[POWER]lcm suspend[begin]\n");
        disp_lcm_suspend(pgc->plcm); //调用具体屏的lcm_suspend函数
        DISPCHECK("[POWER]lcm suspend[end]\n");      
                disp_lcm_init(pgc->plcm, 1); //吊具体屏幕的init函数
                ...........一些CMMQ命令........
            ret = primary_display_esd_check(); //再次检测,如果失败5次就定制重启
 
   
 _disp_primary_path_check_trigger单独分析7
    while (1) {
        if (gResetOVLInAALTrigger == 1) {  //操作OVL
ovl_reset_by_cmdq(handle, DISP_MODULE_OVL0);
if (ovl_get_status() != DDP_OVL1_STATUS_SUB)
ovl_reset_by_cmdq(handle, DISP_MODULE_OVL1);
}
        _cmdq_set_config_handle_dirty_mira(handle);  //设置
        _cmdq_flush_config_handle_mira(handle, 0);
 
 
primary_display_frame_update_kthread单独分析8
    update_frm_seq_info(0, 0, 0, FRM_END); //更新
        dprec_logger_frame_seq_end(pgc->session_id, frm_update_sequence[i].seq);
MMProfileLogEx(ddp_mmp_get_events()->primary_seq_release, MMProfileFlagPulse, frm_update_sequence[i].mva, frm_update_sequence[i].seq);
 
 

三、Display

1.lcm 相关概念

1.1) MIPI接口:

    一共有三种接口:DBI(也做CPU或MCU接口)、DPI(也叫RGB接口)、DSI.

    在使用DSI接口时,目前75/77都只支持到2条data lane,加上一条clock lane.

    使用DPI接口时,根据LCM IC支持的情况,可以选择16bus、18bus传输RGB格式文件,在GPIO部分分为R、G、B分别对应 8个GPIO(GPIO20~46期间),客户采用DPI接口需要根据选择的bus方式进行配置,推荐RGB端口全部配置为对应的复选模 式,并设置为OUT输出。

    采用DBI接口,有两种模式选择,一种是选择共用DPI的bus脚 +DPI控制线,另一种是共用nand data pin+CPU 控制线。

 

1.2) DSI接口有两种sync 模式:

    video mode和command mode,其中video mode是BB端一直刷数据到LCM,cmd mode是在有数 据更新时刷数据到LCM GRAM中) 和DSI command mode相比,video mode 是需要实时传输image data到lcm端,DSI 的refresh rate决定了lcm的refresh rate。

 

1.3)EDS机制:

    92平台LCM driver中定义了esd_check和esd_recovery的接口,但ESD线程不工作。

    目前在MT6589之前平台,video mode的ESD实现有三种模式,分别是:ext TE(外部TE信号检测)、int TE(内部TE信号检 测)、non cout clk不同ESD方式需要注意的方面

    a) int TE和ext TE的检测,都不需要实现lcm_esd_check函数,而需要实现lcm_esd_recover函数。

        non cont clk则不需要实现lcm_esd_check函数和lcm_esd_recover函数,而只需要在上面 params中配置为TRUE即可

    b) ext TE的实现,需要LCM外接TE pin到BB端,同时在inital code中配置寄存器打开TE信号的输出 (一般是写0x35寄存器,具体需要和LCM IC FAE确认)

 

1.4)HDMI/MHL:

    目前我司HDMI/MHL的相关code和driver都是有集成在codebase中的,要使用的话,只需要只需要在对应的 ProjectConfig.mk文件中开启,并且在dct中配置好对应的引脚定义即可。

以下以MHL为例:

    ProjectConfig中配置:

    MTK_HDMI_SUPPORT=yes #表明开启HDMI/MHL功能

    CUSTOM_KERNEL_HDMI=Sii8338 #表明配置为MHL的IC型号

 

1.5)TE 信号:

    大部分TE问题是由于没有正常开启TE所导致,首先检查TE是否开启。

    89平台使用内部TE,lcm driver中只需要在init过程中打开LCM TE即可,一般是写0x35寄存器,部分IC需要额外写其他 寄存器,可与FAE确认。

    检查TE是否正常开启,如果是工版,则可使用如下方式打开fps的log,查看TE信息:

        adb shell

        cd sys/kernel/debug

        echo fps:on>mtkfb

然后查看mtklog, 搜索“FPS”,若看到等待TE时间为0, 表示TE未正常开启,需要与LCM IC的FAE进一步确认开启流程 。

若TE已经成功开启,依然有Teering现象,可从如下方面思考分析。

    1)是否使用了竖屏横用,导致对GRAM的读写方向不一致,一般会出现斜线切屏现象。

    2)是否clock速度过低,FPS低于LCM自刷新率的1/2?

    3)是否clock速率过快,超过LCM的自刷新率,导致写GRAM时可能从后面赶上读,导致Teering发生。

 

    4)fmark周期与CS周期

          出现TE现象的根本原因是两边速度不一致,具体是LCM的刷新速度要快于主控送数据的速度,两者的速度要符合一定的范围才行。只要保证CS的周期在两个TE周期之间即可,也就是CS的写频率不能低于TE读频率的二分之一,Tearing出现的根本条件是读写有交叉。通常都是写Gram速度(WR)慢于lcd刷屏速度(TE[x2] 只要刷屏的位置不超过写Gram位置就不会有切屏现象[x3] 

          举个实例:比如CS差不多就比两个TE周期小一点,要刷两桢数据,首先第一桢刷屏开始刷屏了,表示读GRAM开始,它的速度比较快,它读的是老旧数据;紧接着主控开始写GRAM,大概写到GRAM的快一半时,这时候已经刷完一桢,然后开始刷第二桢,即又从GRAM的最上方开始读并刷屏,此时读出来的才是刚写入的新数据,在写完GRAM之前,读的步骤永远跟不上写的步骤,就不会出现tearing。

     如果CS比两个TE周期大,假设相当于三个TE周期,那么只有在第三个TE读周期时,显示的数据才是写好的GRAM的数据;第一个TE读的是老旧的数据,第二个TE周期由于GRAM还没有写完,但读步骤赶上写GRAM步骤了,导致显式一部分是旧的一部分是新的,所以出现TE。此即本质。
    5) TE类型

        TE显示使能时,必须保证CPU的LCD TE使能和LCM驱动的TE功能都打开。LCM的TM使能有两


 

 

1.6)HS/LP:

HS:high speed , clock切为HS模式,高速模式。

LP:low power,低电平

    有些LCM在开机的时候,如果使用LP下发init code,可能会不准确或者导致花屏等问题,这时候需要使用HS mode发送init code,比如三星的某款OLED(D53D6EA8061V-Amoled)。

continuous clock/no-continuous clock模式

 

1.6)dithering:

抖动显示技术:

    MT6572 如所用lcm不支持RGB888 color format, 显示效果差需要开启dithering的。

 

1.7)其他概念:

    AAL:BB端CABC(即AAL),为1种方式控制背光

        continuous clock/Non-continuous clock : Switch clock lane from HS to LP

 

 

2.LCM时钟配置

MT6582 LCM Driver中配置:params->dsi.PLL_CLOCK = 234;

计算方法:

    展频开关:

    如果MIPI Clock对RF/WCN产生干扰,并且在尝试寻找相应的频点依然无法解除 EMI,可以尝试做Frequency Hopping;

82平台默认打开展频开关,不同于72/89平台,将展频的开关以及展频幅度的选择 ,都开放到LCM Driver中,以如下为例:

    params->dsi.ssc_range =4;

    params->dsi.ssc_disable = 0;

    代表:展频打开,ssc_range = 4%

 

 

3.AAL与CABC背光选择(两种方式控制背光):

参考[FAQ05966]

【BB端CABC(即AAL)】

    - 打开功能,向MTK申请patch,并在ProjectConfig.mk中打开MTK_AAL_SUPPORT = yes

    cust_leds.c(包括lk与kernel中的两支文件)设置如下
    
 
    【LCM端CABC】
    - 对于Video Mode,ALPS.JB2.MP.V1.3(包括1.3)之前的版本,请向MTK申请patch
    - lcm driver中实现set_backlight接口
    - cust_leds.c(包括lk与kernel中的两支文件)设置如下
    

 

 

4. 调整Display 消耗的BW(bandwidth带宽)方法:

•LCM driver建议如下:

    –MIPI的clock 尽量低,建议60fps

    –For DSI Video mode,建议不要使用burst mode(比较能吃BW)

        params->dsi.mode = BURST_VDO_MODE;

    –Video mode的时序,blank 区间(如VBP/VFP/HBP/HFP)尽量少(当然也需要满足LCM module的spec)

 

 

5.DSI video mode相关参数配置方法:

对应配置文件:\alps\mediatek\custom\common\kernel\lcm\xxxx.c中lcm_get_params()函数

1,data lane每帧回LP11(Low Power state,dp,dn都为高电平),clk一直HS( High Speed),对应配置:

    params->dsi.cont_clock=1;

    params->dsi.clk_lp_per_line_enable=0;

 

2,data lane每一行回一次LP11,clk lane每一帧回一次LP,对应配置:

    params->dsi.cont_clock=0;

    params->dsi.clk_lp_per_line_enable=0;

 

3,data lane和clk lane都是每行回一次LP11,对应配置:

    params->dsi.cont_clock=0;

    params->dsi.clk_lp_per_line_enable=1;

 

 

6.LCM CABC 配置

参考[FAQ12413]

    

 

 

7.MHL 卡顿问题

 

 

8.ESD机制各个平台的差异:

    对于89/72/82等新平台,Display架构做了调整,ESD的实现方式与之前的75/77等平台稍有差异。对于之前75/77平台,可以参考FAQ03210及FAQ05163.

    新旧架构下,主要是DSI Video Mode下ESD方式不一样。

之前的架构下Video Mode的屏采用检测外部TE或者内部TE来做ESD Check,因此需要在lcm driver中配置相应的参数。

 

新架构下不支持外部TE或内部TE来做ESD check, lcm_get_params中关于esd的参数不用再配置。

如lcm_int_te_monitor、lcm_int_te_period、lcm_ext_te_monitor等无需配置。

[SOLUTION]

    新的Display架构下,DSI Video Mode及DSI Command Mode都采用读寄存器的方式来进行esd check. 因此都只需要在lcm driver中实现esd_check和esd_recover函数即可。

对于具体读取哪些寄存器来进行esd check,需要与屏厂确认。

ESD实现后如果出现每两秒闪屏的问题,可以按如下流程处理:

    1. 首先检查esd check中是否添了过多的log信息或者有delay操作,建议先去掉所有log测试。

    2. 如果依然出现每两秒闪屏,可参考FAQ05680和FAQ05681进行处理。

 

 

9.DBI/DPI接口的GPIO的配置情况

    DBI:DBI接口分为串行和并行两种。由lcm_params->ctrl这个参数控制。

    LCM_CTRL_SERIAL_DBI/LCM_CTRL_PARALLEL_DBI

    1.)如果是serial类型的,是通过MT6572 datasheet里面的0x14012028 DBI_SCNF (DBI Serial Interface Configuration Register)这个寄存器来config串行接口。比如使用LSDI还是LSDA,LSCK上升沿还是下降沿发送数据 ,LSCK在没有数据的时候是LOW/HIGH.

    配置几个GPIO pin:LSCE0B(相当于数据使能信号,低电平有效), LSCK, LSDA/LSDI(传送command时用),DBI[XX:0](传 送data时用)

CSS,CSH:chip select setup time/chip select hold time

(这两个时间之内是不会传数据的,Invalid data)

 

    2.)如果是Parallel类型的,

        配置几个GPIO pin:LPCE0B(相当于CS信号,低电平有效), LPA0(RS信号,MTK平台上面和CS信号是同步的), LCD CLK,LPWRB/LPRDB(类似数据使能信号)和DBI[XX:0](复用DPI的data pin,传送data和command时用)

写的时候用LPWRB,读的时候用LPRDB

有C2WS和C2WH两个变量:chip selection to write setup time和chip selection to write hold time

同理C2RS和C2RH.

硬件连接:

DBI Parallel类型:BB端需要打开LRDB、LWRB、LPA0 pin脚复用功能,并连接到LCM的RD、WR、RS

DBI serial类型: BB端需要打开LSCE0B、LSCK、LSDA/LSDI pin脚复用功能, 并连接到LCM的CSX, SCL, SDA/DOUT/DIN

lcm Driver里面变量write wait state time,是处于wait状态的时间。比如大于等于C2WS,参考MT6572 datasheet PAGE1659原理图

note:89和72的DBI的clock都是不可调整的,都是130Mhz.

但是如果需要调整DBI的FPS的话,可以调整C2WS/C2WH/WST,分别对应write_setup/write_hold/write_wait

DPI:

使用DPI接口时,根据LCM IC支持的情况,可以选择16bus、18bus传输RGB格式文件,在GPIO部分分为R、G、B分别对应 8个GPIO(GPIO20~46期间),客户采用DPI接口需要根据选择的bus方式进行配置,推荐RGB端口全部配置为对应的复选模式,并设置为 OUT输出。

同时DPI的接口需要BB端打开DPIHSYNC、DPIVSYNC、DPIDE、DPICK复用功能,并分别连接到对应的LCM控制端

另注:

其实DPI和DBI一样,都是可以通过lcm_params->ctrl这个变量来控制是使用Parallel还是serial还是GPIO的类型来下 command。

但是一般DPI都会选择使用LCM_CTRL_SERIAL_DBI这个类型,因为DPI的屏,DB[17..0]只是会用来做数据传输,控制线是 会通过LSDA/LSDI传输。

 

 

10.LCM Porting时如何配置Clock

clock配置方法:

    lcm driver中配置clock有不同的方式,曾经使用过的配置方法有如下几种:

    Type1: 配置倍频与分频参数:dsi.pll_div1(倍频), dsi.pll_div2(分频)--- (适用于75/77等之前的平台)

    Type2: 配置倍频与分频参数:dsi.pll_fbk_div(倍频), dsi.pll_div1 & dsi.pll_div2 (分频)---(适用于89/72等前 期版本)

    Type3: 直接配置clock lane频率:dsi.PLL_CLOCK(前期配置成枚举值,后期将直接配置成对应的频率常数值)----(适 用于89/72/82...)

 

11.如何使用PMIC的LDO方式供给LCM端 1.8/2.8v的电压

参考[FAQ10038]

    1. 如何在开机阶段使用PMIC的LDO方式供给LCM端1.8/2.8v的电压?

    2. 如何在suspend/resume的时候,断掉/供给LCM端1.8/2.8v的电压?

 

1. )在开机的时候,建议在preloader或者LK阶段就通过PMIC的LDO方式来给LCM端上电,

    比如可以在alps\mediatek\platform\mt6589\preloader\src\drivers\mtk_pmic_6320.c文件里面的 pmic6320_init函数中做上电1.8/2.8v的操作。

    AOSP版本mtk_pmic_6320.c的路径 :alps/bootable/bootloader/preloader/platform/mt6572/src/drivers/mtk_pmic_6320.c

下面是在LK阶段的上电/掉电方法:

    使用upmu_common.c文件里面API来分别控制每一个LDO_VGPX.

比如:

    upmu_set_rg_vgp6_vosel用来控制上电的电压值;

    upmu_set_rg_vgp6_en用来控制enable VGP6这个pin

 

2. )因为在suspend/resume的时候,kernel都是跑起来的,所以上电/掉电 1.8/2.8v的操作都应该放在kernel里面。

    下面是在kernel里面的上电/掉电方法,在kernel里面有统一的上电/掉电的接口函数: 上电接口函数:hwPowerOn 掉电接口函数hwPowerDown

以下以PMIC6320的VGP6为例。

请在您要上电的文件#include <mach/mt_pm_ldo.h> 上电请调用 hwPowerOn,掉电请调用hwPowerDown

    hwPowerOn(MT65XX_POWER_LDO_VGP6, VOL_2800, "ldo_test");

    bool hwPowerDown(MT65XX_POWER_LDO_VGP6, "ldo_test");

 

 

12.如何拉低并保持LCM RESET PIN脚为低 电平

平台默认RESET PIN脚输出为高平的,如果一定需要拉低,可以配置RESET PIN脚为GPIO模式,再通过GPIO方式拉低。

使用mediatek/dct目录下的DCT工具,使用其打开custom/XXX/kernel/dct/dct目录下的DWS文件,将GPIO131配置成 0:GPIO131. 

对GPIO PIN脚的控制有如下一些方法:

    lcm_util.set_gpio_mode(GPIO131, GPIO_MODE_00);

    lcm_util.set_gpio_dir(GPIO131,GPIO_DIR_OUT);

    lcm_util.set_gpio_out(GPIO131,0);

 

 

13.如何调节MIPI接口驱动能力

    在使用DBI、DPI的MIPI接口时,可以在lcm_get_params函数中设置参数 io_driving_current的值来配置IO的驱动电流(6589上面不支持) 

DSI的MIPI接口,不支持IO驱动电路的调节 ,其可选值的大小可以在lcm_drv.h看到定义。

其可选值的大小可以在lcm_drv.h看到定义:

    typedef enum{

    }LCM_DRIVING_CURRENT;

该值的会在lcd_drv.c文件中写到寄存器中:

    LCD_STATUS LCD_Set_DrivingCurrent();

 

 

14.如何通过检测外部TE实现esd check的 功能

    在72/82/92的JB/KK版本,我们都是通过读取esd寄存器的方式实现esd check,但是由于esd  check的时候会切换到cmd mode去读,所以屏的玻璃存在最大1帧时间的等待,如果这个vdo 

mode屏的玻璃延时等待时间较小(小于切换的时间),就会出现闪屏,所以不能使用读esd寄存器 的方式做esd check。 

    如果这个屏有 external TE管脚的话,可以通过检测ext.TE的方式来做esd check,具体原理 为:把原来的esd流程全部关闭,启动一个新的线程,循环检测外部TE中断,如果检测失败,就

recovery。

新的esd流程里面需要做三件事:

    1)         物理连接一根外部TE pin,然后在dws文件里面配置一个GPIO口为DSI_TE模式。

    2)         在DSI初始化的时候,注册该ext.TE的irq处理函数,收到中断则设置irq_flag为TRUE。

    3)         启动线程定时去wait flag==1,如果超时,则做esd recovery。

 

 

15.如何配置DSI时钟频率

    1、)DSI vdo mode下的数据速率data_rate的大致计算公式为: Data rate= (Height+VSA+VBP+VFP)*(Width+HSA+HBP+HFP)* total_bit_per_pixel*frame_per_second/total_lane_num 

 

    2、)DSI cmd mode下的数据速率data_rate的大致计算公式为: Data rate= width*height*1.2* total_bit_per_pixel*frame_per_second/total_lane_num 

参数注释:

    data_rate : 表示的是数据速率

    width,height  :屏幕分辨率

    VSA VBP VFP :DSI vdo mode的vertical porch配置参数

    HSA HBP HFP :DSI vdo mode的horizontal porch配置参数

    total_bit_per_pixel :表示的是一个pixel需要用几个bit来表示,比如RGB565的话 就是16个bit 

    frame_per_second :就是我们通常看到的fps,叫做帧率,表示每秒发送多少个帧 ,一般是60帧每秒 

    total_lane_num :表示的是data lane的对数。

 

3、)DSI采用的是双边采样,则clk等于数据速率的一半,因此: clk=data_rate/2

有两种配置clk的方式,第一种方式配置四个参数得到,第二种配置方式直接配置频 率,建议采用第二种。 

第一种方式,通过div分频倍频实现,各个平台略有差异,但是原理基本一致,请参 考porting guide,如下举例89平台: 

    params->dsi.pll_div1   = ;  //配置范围为0,1,2,3的时候,对应的 div1_real等于1,2,4,4 

    params->dsi.pll_div2   = ;  //配置范围为0,1,2,3的时候,对应的 div2_real等于1,2,4,4 

    params->dsi.fbk_div   = ;  //范围 0..63

    params->dsi.fbk_sel   = ;  //配置范围为0,1,2,3的时候,对应的 fbk_sel_real等于1,2,4,4 

    输出频率 =26MHz*(fbk_div+1)*(2*fbk_sel_real)/(div1_real*div2_real) 

第二种方式,直接配置clk大小:

    params->dsi.PLL_CLOCK = LCM_DSI_6589_PLL_CLOCK_234;//这里举例89平台,使用 一个宏,表示配置的clk等于234MHz。但是在89之后的平台,使用直接配置一个频率 

数字的方式,比如params->dsi.PLL_CLOCK = 234,表示234MHZ)

 

 

4、在lcm porting过程中,这些参数都定义在lcm_drv.h文件中的LCM_DSI_PARAMS结 构体中,随着平台的发展,或许有所不同,但是基本原理都是一致的,如何配置clk的大小,请先根据自己的帧率、像素格式、porch值、屏的分辨率、 data lane对数等计算出data_rate,然后计算出clk。

 

 

16.背光模式设置成 T65XX_LED_MODE_CUST_BLS_PWM,如何修改PWM的工作频率

前提:cust_leds.c文件里面使用的背光模式是MT65XX_LED_MODE_CUST_BLS_PWM

MT6582版本工作频率计算公式如下:

    PWM工作频率计算公式:26MHz (clock freq.) / (PWM_CLKDIV+1) / 1024 (period) 26 KHz 

    所以需要修改PWM的工作频率,可以通过修改PWM_CLKDIV,clock freq,或者period的值来达到修 改PWM的工作频率的效果。 

以下的三种方法可以任选1~2种来达到想要的PWM工作频率:

1.  修改分频参数方法,修改config_data里面的第二个参数:

    {"lcd-backlight",     MT65XX_LED_MODE_CUST_BLS_PWM, (int)disp_bls_set_backlight,{0, 1, 0, 0, 0}} //设置div=1 

2. 修改clock freq:

在alps\mediatek\platform\mt6582\kernel\drivers\dispsys\ddp_bls.c文件的 disp_bls_init和disp_bls_config函数中设置CLK_CFG_1这个寄存器的值,来选择合适的时钟源,加入如下代码: 

    mt65xx_reg_sync_writel(DRV_Reg32(CLK_CFG_1) | (0x00000003), CLK_CFG_1); //设置156 MHz的时钟源 

 



posted @ 2020-11-25 15:11  luoyuna  阅读(4466)  评论(0编辑  收藏  举报