深圳市航顺芯片技术研发有限公司
直播中

硬件工程师1

9年用户 1561经验值
擅长:可编程逻辑
私信 关注
[问答]

LCD的驱动调试流程是怎样的?

LCD的驱动调试流程是怎样的?

回帖(1)

张波

2021-10-9 16:25:17
1. 简介
本文主要介绍LCD的驱动调试流程,希望通过简单的描述,让LCD的调试流程梳理的更加简单清晰。


2. 调试流程步骤
2.1 准备工作
2.1.1 项目配置确认
        1. 了解项目的基本配置信息以及LCM的规格信息,包含,平台,Driver IC,接口,分辨率,色深等等。
        2. 确认该款屏或者Driver IC在其他项目上已经调试通过,如果有则联系相关人员获取代码,然后做后面的效果优化工作即可。
        3. 确认平台是否已经默认支持该款Driver IC,如果有则打开相应的宏,修改相应的配置即可。
        4. 与LCM模组供应商联系获取相应的驱动IC代码。
2.1.2 LCM驱动文件配置
        1. Alps/mediatek/custom/common/kernel/lcm/drivername   
        --------- driver ic的驱动代码
        2. Alps/mediatek/custom/common/kernel/lcm/mt65xx_lcm_list.c  
                --------- 确保要使用的驱动已经被添加到系统中
        3. Alps/mediatek/config/XXXXXXXXX/Projectconfig.mk
            CUSTOM_LK_LCM = drivername
            CUSTOM_KERNEL_LCM = drivername
            BOOT_LOGO = wvga
    ----------确保LCM参与编译


2.2 LCM驱动
Alps/mediatek/custom/common/kernel/lcm/drivername文件夹中包含了所用的驱动文件,此处以hx8379a为例。
2.2.1 lcm driver结构体
        LCM_DRIVER hx8379a_dsi_vdo_lcm_drv_tianma=
        {
            .name          = "hx8379a_dsi_vdo_tianma",
        .set_util_funcs   = lcm_set_util_funcs,
        .get_params     = lcm_get_params_tianma,
        .init            = lcm_init,
        .suspend        = lcm_suspend,
        .resume         = lcm_resume,
        .compare_id     = lcm_compare_id_tianma,
        .esd_check       = lcm_esd_check,
        .esd_recover     = lcm_esd_recover,
        #if (LCM_DSI_CMD_MODE)
            .update         = lcm_update,
        #endif
            };
        .name 为驱动名称,
        .set_util_funcs 暂时未使用
        .get_params 获取lcm的配置信息
        .init 初始化函数
        .suspend 睡眠函数,目前直接关闭LCM电源,如果对时效性要求较高可仅对相应寄存器做操作
        .resume 唤醒函数,目前采取重新初始化的方式,如果对时效性要求较高可仅对相应寄存器做操作
        .compare_id 或者lcd id,作为自动识别是使用
        .esd_check 检查lcd状态,用于处理静电问题
        .esd_recover 静电恢复函数,目前采用重新初始化得方式
        ()
        .update 区域刷屏,video mode不支持
2.2.2 LCD参数配置 get_params
        1. 分辨率, 配置LCM的分辨率
            params->width  = FRAME_WIDTH;
            params->height = FRAME_HEIGHT;
        2. 接口类型, 选择接口类型
             params->type   = LCM_TYPE_DSI;
             // enable tearing-free
             params->dbi.te_mode  = LCM_DBI_TE_MODE_DISABLED;
             params->dsi.mode   = SYNC_PULSE_VDO_MODE;  
        3. V-sync,H-sync, 调整porch
             params->dsi.vertical_sync_active = 4;// 3    2
             params->dsi.vertical_backporch = 11;// 20   1 6
             params->dsi.vertical_frontporch = 6; // 1  12
             params->dsi.vertical_active_line = FRAME_HEIGHT;
             params->dsi.horizontal_sync_active = 20;// 50  2
             params->dsi.horizontal_backporch = 30;
             params->dsi.horizontal_frontporch = 30;
             params->dsi.horizontal_active_pixel = FRAME_WIDTH;
        4. DSI timing, 一般需要FAE帮忙调试
            params->dsi.HS_TRAIL = 10;
            params->dsi.HS_ZERO = 7;
            params->dsi.HS_PRPR = 4;
            params->dsi.LPX = 3;
            params->dsi.TA_SACK = 1;
            params->dsi.TA_GET = 15;
            params->dsi.TA_SURE = 3;
            params->dsi.TA_GO = 12;
            params->dsi.CLK_TRAIL = 10;
            params->dsi.CLK_ZERO = 16;
            params->dsi.LPX_WAIT = 10;
            params->dsi.CONT_DET = 0;
            params->dsi.CLK_HS_PRPR = 6;
            params->dsi.LPX=8;
        5. CLK
            params->dsi.pll_div1=0; // div1=0,1,2,3;div1_real=1,2,4,4 ----0: 546Mbps  1:273Mbps
            params->dsi.pll_div2=2; // div2=0,1,2,3;div1_real=1,2,4,4
            params->dsi.fbk_div =26 ;    // fref=26MHz, fvco=fref*(fbk_div+1)*2/(div1_real*div2_real)
2.2.3 lcm初始化
        static void lcm_init(void)
        {
            MDELAY(10);
            SET_RESET_PIN(1);
            MDELAY(10);
            SET_RESET_PIN(0);
            MDELAY(50);
            SET_RESET_PIN(1);
            MDELAY(120);
        #if 1
            if(0xFFFF == lcm_type) {
                lcm_adc_id();
            }
            if(LCM_TYPE_TCL == lcm_type) {
                lcm_init_setting_TCL();
            }
            else if(LCM_TYPE_TIANMA == lcm_type) {
            lcm_init_setting_TIANMA();
            }
            else {
                lcm_init_setting_TCL();
            }
        #else
            lcm_type = LCM_TYPE_TCL;
        lcm_init_setting_TCL();
        #endif
        }
        1. 硬件复位
                按照芯片规格书调试reset pin的时序
        2. adc识别模组厂(可选)
            如果有要兼容多家模组厂的情况则需要硬件上设计adc区分模组的功能,驱动代码中加入读取adc的操作用以区分不同的模组厂:
                R1(Kohm)        R2(Kohm)        分压比        ADC
                NC        0        0       
                30        10        1/4                 1237 (+100,-100)
                20(10)        20(10)        1/2                 2480 (+100,-100)
                10        30        3/4                 3723 (+100,-100)
                0        NC        1        不能使用
        3. 初始化寄存器
        按照平台的格式填写初始化列表,目前mtk平台有data array合push table两种方式,仅仅只是格式的不同,最终都是通过dsi接口将数据送出。
        有发现有的芯片不支持data array的方式,这点要跟FAE确认好。
2.2.4  获取LCD ID
        每款芯片都会有自己的chip id, 在需要同时兼容多家driver ic的时候则需要读取此id来判断当前所使用的屏是哪款。
        如果项目只有一款屏,则此函数可以省略。


2.3 LCD效果调试
2.3.1 LCD效果
        效果调试一般交由FAE来调试,并且需要达到测试标准才算完成,一般需要注意flinker, gamma,色阶等问题。
2.3.2 LCD在线调试方法
        1. 效果调试是非常繁琐的工作,需要反复修改寄存器,常规方法是FAE修改好初始化列表里面的参数后,由驱动工程师下载验证。
        2. 目前已经实现LCD的在线调试功能,在手机开机的情况下,通过adb的方式动态修改lcd的寄存器来改变现实效果,省去了编译,下载的过程,并且FAE自己就可以完成,非常方便。
        3. 操作步骤
                在lcd_drv.h文件中开启功能宏WT_LCM_DEBUG
                在驱动文件中添加函数debug函数,并且编译代码下载到手机
                开机后手机连接到电脑并进入调试模式
                输出adb echo "debug:XXXXXXXXXXXXXXXX > /proc/lcm_debug"
                XXXXXXXXXXXXXXXX的格式可以自己定义
        4. 范例
                a 调试函数的实现,采用data array的方式,每次写入一组寄存器
                void lcm_debug_func(unsigned char *buffer, int len)
                {
                    int i = 0;
                unsigned int data_array[16];
                if(len/4 == 0 || len%4 != 0)
                {
                   printk("lcm_debug:data format errorn");
                   return;
                }
                for(i = 0; i < len/4; i++)
                {
                   data_array = buffer[4*i];
                   data_array = (data_array<<8)|buffer[4*i+1];
                   data_array = (data_array<<8)|buffer[4*i+2];
                   data_array = (data_array<<8)|buffer[4*i+3];
                   printk("lcm_debug:data_array[%d] = %xn", i, data_array);
                }
                dsi_set_cmdq((unsigned int*)data_array, i, 1);
                MDELAY(10);
                    return;
                }
                b. 想要写入
                    data_array[0]= 0x00043902;
                    data_array[1]= 0x7983FFB9;
                c. 输入 adb echo "debug;000439027983ffb9 > /proc/lcm_debug"就可以了。


2.4 LCD backlight驱动
2.4.1 相关驱动文件
        alpsmediatekcustomcommonkernelledsinccust_leds.h
        alpsmediatekkerneldriversledsleds.c
2.4.2 硬件控制方式
        背光的硬件控制方式很重要,选择不对会直接导致背光无法亮起,对照原理图确认好硬件连接,在DWS文件中修改相应的pin。
2.4.3 led相关的结构体
        LCD背光调试前需要先确认好背光控制方式,PMIC,GPIO,PWM  BLS等等,下面的结构体体现了PWM方式背光的配置,最后的comfigdata {1,4,4,4,0}修改pwm的频率,以解决闪屏的问题。
           static struct cust_mt65xx_led cust_led_list[MT65XX_LED_TYPE_TOTAL] = {
               {"red",               MT65XX_LED_MODE_NONE, -1, {0}},
               {"green",             MT65XX_LED_MODE_NONE, -1, {0}},
               {"blue",              MT65XX_LED_MODE_NONE, -1, {0}},
               {"jogball-backlight", MT65XX_LED_MODE_NONE, -1, {0}},
               {"keyboard-backlight",MT65XX_LED_MODE_NONE, -1, {0}},
               #if 0
               {"button-backlight",  MT65XX_LED_MODE_NONE, -1, {0}},
               #else
               {"button-backlight",  MT65XX_LED_MODE_PMIC, MT65XX_LED_PMIC_NLED_ISINK3, {0}},
               #endif
               #if 0
               {"lcd-backlight",     MT65XX_LED_MODE_PMIC, MT65XX_LED_PMIC_LCD_ISINK, {0}},
               #else
               {"lcd-backlight",     MT65XX_LED_MODE_PWM, PWM2, {1,4,4,4,0}},
               #endif
        };
        PMIC : 即电流井,使用PMIC的ISINK功能,控制输出电流。实现背光控制。
        GPIO : 后端一般配合驱动IC,通过GPIO的脉冲控制IC输出不同的亮度等级电流。
        PWM:脉宽调制,控制PWM的DUTY,从而控制后端PWM IC的输出电流,实现背光控制。
        BLS:同PWM ,只是使用了平台的DISP_PWM管脚功能,跟AAL功能搭配,可以实现LCM 亮度跟随环境变化的控制模式。
2.4.4 backlight mapping
        主要用于客制化背光等级,由上层256级向下层转化的算法,可由客户定制。
        以PWM2方式为例,pwm仅支持64级等级变化,因此需要256向64级的转化,最简单的设计如下;
        unsigned int brightness_mapping(unsigned int level)
        {
        return level>>2;
        }
        注意要点:
            需要根据具体的PWIC来处理;有些PWIC 在duty为10% 以下会出现无法点亮背光的问题;所以若需要通过这里调低最低亮度;需要关注PWMIC 的硬件工作条件。
    同时,一般上层最低给过来是30,转化后是7~8, 一般可以接受,若更低了,部分IC 会有问题。


3 异常分析
3.1 黑屏
黑屏一般是背光的不亮造成,需要检查lcd背光
1. 焊接
2. 信号输出
3. 背光驱动


3.2 白屏
开机白屏一般是连接,驱动不匹配,需要检查lcd
1. 焊接
2. 屏的型号是否正确
3. reset信号,供电是否正常
4. 相应的驱动是否添加正确


3.3 花屏
花屏说明总线上有数据传到了lcd上面,硬件连接基本正常,需要检查lcd
1. 确认lcd id读取正确,没有发生驱动匹配错误的问题
2. 竖条状闪动花屏,并无图像,说明时序有问题,初始化失败,需要调整dsi的时序
3. 有图像轮廓存在的花瓶,需要确认RGB配色是否匹配
4. 有些花屏跟reset时序,mipi clk的配置有关,需要调试修改


3.4 闪屏
闪屏说明初始化完成了,只是效果不理想,需要调试lcd
1. reset时序
2. mipi clk
3. H-sync V-syc porch
4. V-com电压


3.5 退出睡眠时候的白屏
1. 闪一下白屏
        a) 背光亮起太早,延时背光亮起的时间
        b) 退出睡眠的过程耗时太长,优化此流程
        c) Frame buffer数据异常,需要检查异常原因
2. 退出睡眠一直白屏
        a) 退出睡眠失败,检查此原因,reset pin,寄存器操作是否有问题
        b) Frame buffer数据异常,需要检查异常原因


4 经验总结
1. 关于LCD ID获取的处理
在做多LCD兼容时需要分为两步,兼容多个IC的时候读取IC的chip id来识别,同一IC兼容多家屏厂的时候同步lcd id pin上面的adc值来区分。
MTK平台默认已经实现通过chip id来识别LCD的方法,通常情况下一个项目如果只是用一款LCD是不需要读取chip id的,当兼容两个或者更多的芯片时才需要读取chip id。
ADC的方案有公司自行实现的,原理是硬件上将屏的lcd id pin连到基带芯片的一路adc通道,在做lcd识别的时候当该ic的chip id获取成功后去读取adc来判断当前的屏是哪一款,具体adc的范围由实际测量几块屏获取,容差范围为正负200.
异常处理:
一般情况都能工程获取到chip id和正确的adc,当屏或者主板出现异常的时候会有存在获取失败的情况,MTK的默认做法是当chip id获取不成功的时候使用lcm列表中的最后一款屏的配置参数去尝试初始化,但我们并不建议这样处理,当遇到异常就上报异常给系统,把异常情况表现出来,方便研发来修正。那么同理,当获取的adc不在所有的屏的覆盖范围之内,也应该当做异常来处理。


2. 效果确认,刷屏帧率
效果调试一般会交给FAE来完成,现在mtk平台有专门的pc工具来调试效果,今后就可以直接将手机和工具给FAE,让FAE独自完成。
效果的确认一般会由硬件测试同事完成,包括硬件客观指标,主管指标,功耗测试等等。驱动工程师自己也可以关注一下刷屏帧率的问题,一般android手机的刷屏帧率要求是在60Hz以上。
(测试工具见BBS附件)


3. pwm参数
目前MTK平台支持的lcd背光的种类还是比较多的,PMIC,PWM, BLS, CUST等等。我们的项目目前使用比较多的是PWM的方式。
具体配置如下:
{"lcd-backlight",     MT65XX_LED_MODE_PWM, PWM2, {1,4,4,4,0}},
前期调试过程中有遇到调整背光亮度过程中闪屏的问题如此配置有关,平台默认的最后的参数为{0},为解决闪屏问题才改为现在的 {1,4,4,4,0}。
五个成员的含义如下:
clock_source:所使用的时钟源;0={代表使用32K时钟},1={52M(89平台) /66M(72平台)}
Div: 分频 , 根据所用时钟源以及期望PWM频率决定。
low_duration :duty 低电平时间
high_duration :duty 高电平时间
pmic_pad :是否使用PMIC上的PWM管脚
比如:72平台上配置:
       {"lcd-backlight",     MT65XX_LED_MODE_PWM, PWM1,{1,1,32,32,0}},
           {1,1,32,32,0}表示src_clk=66M时钟,div=1,LDuration=32,HDuration=32; pmic_pad=0
           最终PWM输出频率是:66M/(1+1)/64/32=16.111KHz
主要修改为clk的修改两种模式一种为30Hz,一种为66MHz(52M),该问题的解决思路是提高pwm的频率来解决闪屏问题。


4. porch计算
        对于video模式的屏,需要通过hsynv, vsync,TE等信号来进行刷屏同步。
        Clk的算法:
            params->dsi.pll_div1=0; // div1=0,1,2,3;div1_real=1,2,4,4 ----0: 546Mbps  1:273Mbps
            params->dsi.pll_div2=2; // div2=0,1,2,3;div1_real=1,2,4,4
            params->dsi.fbk_div =26 ;    // fref=26MHz, fvco=fref*(fbk_div+1)*2/(div1_real*div2_real)
          
        帧率的算法:
        Clk*Lean_num/(vsa+vbp+vfp+val)*(hsa+hbp+hfp+hal)


5. 参数下载格式
在参数下载的时候有两种方式,data array和push table,两种方式在参数的格式要求上有所差异,push table的方式最终调用函数:dsi_set_cmdq_v2, 而data array的方式调用函数dsi_set_cmdq, 本质上区别不大,但也存在一些屏只能使用push table的方式的问题。
dsi_set_cmdq_v2和dsi_set_cmdq的区别,具体根据实际的LCM IC来使用。
仅仅只是dsi_set_cmdq_v2的每次移动地址,再放入一个数据。
for(i=0; i {
   goto_addr = (UINT32)(&DSI_VM_CMD_REG->data[0].byte1) + i;
   mask_para = (0xFF<<((goto_addr&0x3)*8));
   set_para = (para_list<<((goto_addr&0x3)*8));
   MASKREG32(goto_addr&(~0x3), mask_para, set_para);
}
而dsi_set_cmdq是每次都写入一个数据
OUTREG32(&DSI_REG->DSI_VM_CMD_CON, AS_UINT32(&vm_cmdq));
for(i=0;i OUTREG32(&DSI_VM_CMD_REG->data, AS_UINT32((pdata+i+1)));
举报

更多回帖

发帖
×
20
完善资料,
赚取积分