Tina Linux LCD调试指南

描述

Tina Linux LCD调试指南

LinuxLinuxLinuxLinuxLinux

1 概述

编写目的 本文档将介绍sunxi 平台Display Engine 模块中LCD 的调试方法。

LCD 调试方法,调试手段。

LCD 驱动编写。

lcd0 节点下各个属性的解释。

典型LCD 接口配置。

适用范围:sunxi 平台DE1.0/DE2.0 中LCD 屏幕参数设置。

 

 

2 相关术语介绍

表2-1: LCD 相关术语

术语 解释说明
SUNXI Allwinner 一系列SoC 硬件平台
LCD Liquid Crystal Display, 液晶显示器
MIPI Mobile Industry Processor Interface
DSI Display Serial Interface,显示串行接口
I8080 Intel 8080LCD 接口
RGB 这里指一种LCD 接口,该接口发送不经过任何编码的RGB 分量
LVDS Low-Voltage Differential Signaling 一种LCD 接口,低压和差分传输是其特点

3 IC 规格

LCD 接口相关规格:

支持双显,异显。也就是显示内容可以不一样,显示分辨率可以不一样,屏接口也可以不一样。

支持MIPI-DSI 接口, 数量一个。最大支持1920x1200@60 分辨率,满足宽高不要超过2048,像素时钟不超过180MHz 都支持。

支持RGB 接口,数量2 个。其中主显支持并行RGB666,副显并行支持RGB888, 并行RGB 接口最大支持1920x1200@60 分辨率,满足宽高不要超过2048,像

素时钟不超过180MHz 都支持。或者两个串行RGB 接口,串行RGB 的最高分辨率最大不超过800*480@60

支持两个dual-link LVDS 接口, 最大支持1920x1200@60 分辨率,满足宽高不要超过2048,像素时钟不超过180MHz 都支持。或者4 个single-link LVDS 接

口,分辨率最高支持1366*768@60。

两个I8080 接口。分辨率最高支持800*480@60。

LVDS 接口支持信号同显。每两个single link LVDS 接口必须连接到完全一样的LVDS 接口的屏上,将完全一样的数据发送到这两个屏上,做到信号一样。所以

理论上,T509 能做到4显,其中前2 个和后2 个分辨率可以不一样,2 个之间的分辨率必须一样,且必须连接一样的LCD 屏。

说明

在多显的场景下,以上接口可以自由搭配,除了MIPI-DSI 必须用在主显上。

技巧

一个dual link LVDS 接口共20 条线,它可以拆分成两个single link 的LVDS 接口,假设为lvds0 和lvds1,选择一个single link 的时候做显示的时候,必须选择lvds0。

 

4 模块介绍

4.1 添加屏驱动步骤

对于linux4.9 及以下版本总共需要修改三处地方(即下列前三项),对于linux5.4 则需要修改四处地方,具体可参考屏驱动源码位置。

• linux 源码仓库。

• uboot 源码仓库。在uboot 中也有显示和屏驱动,目的是显示logo。

• 板级dts 配置仓库。目的是通过board.dts 来配置一些通用的LCD 配置参数。对于linux4.9,该配置同时对内核及uboot 生效,对于linux-5.4,请参照下条。

• 对于linux5.4,还需额外配置uboot 专用板级dts 配置仓库。

确保全志显示框架的内核配置有使能,查看menuconfig 配置说明。

前期准备以下资料和信息:

• 屏手册。主要是描述屏基本信息和电气特性等,向屏厂索要。

• Driver IC 手册。主要是描述屏IC 的详细信息。这里主要是对各个命令进行详解,对我们进行初始化定制有用,向屏厂索要。

• 屏时序信息。请向屏厂索要。请看屏时序参数说明以了解更多信息。

• 屏初始化代码。,请向屏厂索要。一般情况下DSI 和I8080 屏等都需要初始化命令对屏进行初始化。

• 万用表。调屏避免不了测量相关电压。

动手添加屏驱动之前,先了解屏驱动,请看屏驱动分解。

通过第3 步的资料,定位该屏的类型,然后选择一个已有同样类型的屏驱动作为模板进行屏驱动添加或者直接在上面修改。

修改屏驱动目录下的panel.c和panel.h。在全局结构体变量panel_array中新增刚才添加strcut_lcd_panel的变量指针。panel.h中新增strcut lcd_panel的声

明。

修改Makefile。在lcd 屏驱动目录的上一级的Makefile 文件中的disp-objs中新增刚才添加屏驱动.o。

修改board.dts 中的lcd0。可以看RGB 接口,MIPI-DSI 接口,I8080 接口和LVDS 接口,里面有介绍各种接口典型配置。硬件参数说明,这一章有所有lcd0 节

点下可配置属性详细解释。

编译uboot,kernel,打包烧写。注意不同SDK,编译方式有所不同,部分SDK 默认不编译uboot。

调试。通过调试方法我们可以初步定位问题,还有FAQ,对调屏也有帮助。

4.2 屏驱动说明

4.2.1 屏驱动源码位置

linux 3.4 版本内核:

linux3-4/drivers/video/sunxi/disp2/disp/lcd/

linux 3.10 版本内核:

linux3-10/drivers/video/sunxi/disp2/disp/lcd/

linux 4.9 版本及其以上内核:

linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/

uboot-2014:

brandy/u-boot-2014.07/drivers/video/sunxi/disp2/disp/lcd

uboot-2018:

brandy/brandy-2.0/u-boot-2018/drivers/video/sunxi/disp2/disp/lcd

板级配置,其中“芯片型号” 比如R818,和“板子名称” 比如demo,请根据实际替换。

device/config/chips/芯片型号/configs/板子名称/board.dts

针对linux5.4 时使用的uboot 板级配置:

device/config/chips/芯片型号/configs/板子名称/uboot-board.dts

针对linux5.4 时使用的kernel 板级配置:

device/config/chips/芯片型号/configs/板子名称/linux-5.4/board.dts

4.2.2 menuconfig 配置说明

LCD 相关代码包含在disp 驱动模块中,进入内核根目录,执行make ARCH=arm menuconfig或者make ARCH=arm64 menuconfig(64bit 平台) 进入配置主界

面。并按以下步骤操作:

DE1.0 对应平台:R6(linux-3.10)、R16(linux-3.4)。

DE2.0 对应平台:除R6 和R16 之外的。

Linux

图4-1: DE1.0 menuconfig 配置图

Linux

图4-2: DE2.0 menuconfig 配置图

以R40 为例,具体配置目录为:Device Drivers->Graphics support->Support for frame buffer devices->Video Support for sunxi -> DISP Driver Support(sunxi-disp2)。

 

4.2.3 屏驱动分解

在屏驱动源码位置中,主要分为四类文件

panel.c和panel.h,当用户添加新屏驱动时,是需要修改这两个文件的,需要将屏结构体变量添加到全局结构体变量panel_array中。

lcd_source.c和lcd_source.h,这两个文件实现的是给屏驱动使用的函数接口,比如电源开关,gpio,dsi 读写接口等,用户不需要修改只需要用。

屏驱动。除了上面提到的源文件外,其它的一般一个c 文件和一个h 文件就代表一个屏驱动。

在屏驱动源码位置的上一级,有用户需要修改的Makefile 文件。

我们可以打开drivers/video/fbdev/sunxi/disp2/disp/lcd/default_panel.c作为屏驱动的例子,在该文件的最后

struct __lcd_panel default_panel = {

    /* panel driver name, must mach the lcd_drv_name in board.dts */

    .name = "default_lcd",

        .func = {

        .cfg_panel_info = LCD_cfg_panel_info,

        .cfg_open_flow = LCD_open_flow,

        .cfg_close_flow = LCD_close_flow,

        }

    ,

};

该全局变量default_panel的成员name与lcd_driver_name必须一致,这个关系到驱动能否找到指定的文件。

接下来是func成员的初始化,这里最主要实现三个回调函数。LCD_cfg_panel_info,LCD_open_flow和LCD_close_flow。

开关屏流程即屏上下电流程,屏手册或者driver IC 手册中里面的Power on Sequence 和Power off Sequence。

开关屏的操作流程如下图所示。

其中,LCD_open_flow 和LCD_close_flow 称为开关屏流程函数, 方框中的函数, 如LCD_power_on,称为开关屏步骤函数。

不需要进行初始化操作的LCD 屏,比如lvds 屏,RGB 屏等,LCD_panel_init 及LCD_panel_exit这函数可以为空。

Linux

图4-3: LCD 开关屏流程

函数:LCD_open_flow

功能:LCD_open_flow 函数只会系统初始化的时候调用一次,执行每个LCD_OPEN_FUNC即是把对应的开屏步骤函数进行注册,先注册先执行,但并没有立刻执

行该开屏步骤函数。

原型:

static s32 LCD_open_flow(u32 sel)

函数常用内容为:

static __s32 LCD_open_flow(__u32 sel)

{

    LCD_OPEN_FUNC(sel, LCD_power_on,10);

    LCD_OPEN_FUNC(sel, LCD_panel_init, 50);

    LCD_OPEN_FUNC(sel, sunxi_lcd_tcon_enable, 100);

    LCD_OPEN_FUNC(sel, LCD_bl_open, 0);

    return 0;

}

如上,调用四次LCD_OPEN_FUNC 注册了四个回调函数,对应了四个开屏流程, 先注册先执行。实际上注册多少个函数是用户自己的自由,只要合理即可。

LCD_power_on 即打开LCD 电源,再延迟10ms;这个步骤一般用于打开LCD 相关电源和相关管脚比如复位脚。这里一般是使用电源控制函数说明和管脚控制

函数说明进行操作。

LCD_panel_init 即初始化屏,再延迟50ms;不需要初始化的屏,可省掉此步骤,这个函数一般用于发送初始化命令给屏进行屏初始化。如果是DSI 屏看DSI 相

关函数说明,如果是I8080 屏用I8080 接口函数说明,如果是其它情况比如i2c 或者spi 可以看使用iic/spi 串行接口初始化,也可以用GPIO 来进行模拟。

sunxi_lcd_tcon_enable 打开TCON,再延迟100ms;这一步是固定的,表示开始发送图像信号。

LCD_bl_open 打开背光,再延迟0ms。前面三步搞定之后才开背光,这样不会看到闪烁。这里一般使用的函数请看背光控制函数说明。

如下图,这是屏手册中典型的上电时序图,我们编写屏驱动的时候,也要注意,该延时就得延时。

Linux

图4-4: power on

函数:LCD_OPEN_FUNC

功能:注册开屏步骤函数到开屏流程中,记住这里是注册不是执行!

原型:

void LCD_OPEN_FUNC(__u32 sel, LCD_FUNC func, __u32 delay)

参数说明:

func 是一个函数指针,其类型是:void (*LCD_FUNC) (__u32 sel),用户自己定义的函数必须也要用统一的形式。比如:

void user_defined_func(__u32 sel)

{

    //do something

}

delay 是执行该步骤后,再延迟的时间,时间单位是毫秒。

LCD_OPEN_FUNC 的第二个参数是前后两个步骤的延时长度,单位ms,注意这里的数值请按照屏手册规定去填,乱填可能导致屏初始化异常或者开关屏时间过

长,影响用户体验。

与LCD_open_flow 对应的是LCD_close_flow 是,用于注册关屏函数,使用LCD_CLOSE_FUNC进行函数注册,先注册先执行,这里只是注册回调函数不是立刻执

行。

static s32 LCD_close_flow(u32 sel)

{

    /* close lcd backlight, and delay 0ms */

    LCD_CLOSE_FUNC(sel, LCD_bl_close, 0);

    /* close lcd controller, and delay 0ms */

    LCD_CLOSE_FUNC(sel, sunxi_lcd_tcon_disable, 50);

    /* open lcd power, than delay 200ms */

    LCD_CLOSE_FUNC(sel, LCD_panel_exit, 100);

    /* close lcd power, and delay 500ms */

    LCD_CLOSE_FUNC(sel, LCD_power_off, 0);

    return 0;

}

先关闭背光,这样整个关屏过程,用户不会看到闪烁的过程。

关闭TCON,也就是停止发送数据,这是必要的。再延迟50ms。

执行关屏代码,再延迟200ms(不需要初始化的屏,可省掉此步骤)。

最后关闭电源,再延迟0ms。

 

如下图是典型关屏时序图。

Linux

图4-5: power off

函数:LCD_cfg_panel_info

功能:配置的TCON 扩展参数,比如gamma 功能和颜色映射功能。

原型:

static void LCD_cfg_panel_info(__panel_extend_para_t * info)

TCON 的扩展参数只能在屏文件中配置,参数的定义见显示效果相关参数。

需要gamma 校正,或色彩映射,在board.dts 中将相应模块的enable 参数置1,lcd_gamma_en,lcd_cmap_en,并且填充3 个系数表,lcd_gamma_tbl,

lcd_cmap_tbl,如下所示代码部分。注意的是:gamma,模板提供了18 段拐点值,然后再插值出所有的值(255 个)。如果觉得还不细,可以往相应表格里添加

子项。cmap_tbl 的大小是固定了,不能减小或增加表的大小。

最终生成的gamma 表项是由rgb 三个gamma 值组成的,各占8bit,目前提供的模板中,三个gamma 值是相同的。

static void LCD_cfg_panel_info(struct panel_extend_para *info)

{

    u32 i = 0, j = 0;

    u32 items;

    u8 lcd_gamma_tbl[][2] = {

        /* {input value, corrected value} */

        {0, 0},

        {15, 15},

        {30, 30},

        {45, 45},

        {60, 60},

        {75, 75},

        {90, 90},

        {105, 105},

        {120, 120},

        {135, 135},

        {150, 150},

        {165, 165},

        {180, 180},

        {195, 195},

        {210, 210},

        {225, 225},

        {240, 240},

        {255, 255},

    };

    u32 lcd_cmap_tbl[2][3][4] = {

    {

        {LCD_CMAP_G0, LCD_CMAP_B1, LCD_CMAP_G2, LCD_CMAP_B3},

        {LCD_CMAP_B0, LCD_CMAP_R1, LCD_CMAP_B2, LCD_CMAP_R3},

        {LCD_CMAP_R0, LCD_CMAP_G1, LCD_CMAP_R2, LCD_CMAP_G3},

        },

        {

        {LCD_CMAP_B3, LCD_CMAP_G2, LCD_CMAP_B1, LCD_CMAP_G0},

        {LCD_CMAP_R3, LCD_CMAP_B2, LCD_CMAP_R1, LCD_CMAP_B0},

        {LCD_CMAP_G3, LCD_CMAP_R2, LCD_CMAP_G1, LCD_CMAP_R0},

        },

    };

    items = sizeof(lcd_gamma_tbl) / 2;

    for (i = 0; i < items - 1; i++) {

        u32 num = lcd_gamma_tbl[i + 1][0] - lcd_gamma_tbl[i][0];

        for (j = 0; j < num; j++) {

            u32 value = 0;

            value =

            lcd_gamma_tbl[i][1] +

            ((lcd_gamma_tbl[i + 1][1] -

            lcd_gamma_tbl[i][1]) * j) / num;

            info->lcd_gamma_tbl[lcd_gamma_tbl[i][0] + j] =

            (value << 16) + (value << 8) + value;

        }

    }

    info->lcd_gamma_tbl[255] =

        (lcd_gamma_tbl[items - 1][1] << 16) +

        (lcd_gamma_tbl[items - 1][1] << 8) + lcd_gamma_tbl[items - 1][1];

    memcpy(info->lcd_cmap_tbl, lcd_cmap_tbl, sizeof(lcd_cmap_tbl));

}

4.2.4 延时函数说明

函数:sunxi_lcd_delay_ms / sunxi_lcd_delay_us

功能:延时函数,分别是毫秒级别/微秒级别的延时。

原型:s32 sunxi_lcd_delay_ms(u32 ms) / s32 sunxi_lcd_delay_us(u32 us)

4.2.5 图像数据使能函数说明

函数:sunxi_lcd_tcon_enable / sunxi_lcd_tcon_disable

功能:打开LCD 控制器,开始刷新LCD 显示。关闭LCD 控制器,停止刷新数据。

原型:void sunxi_lcd_tcon_enable(u32 screen_id)

void sunxi_lcd_tcon_disable(u32 screen_id)

4.2.6 背光控制函数说明

函数:sunxi_lcd_backlight_enable / sunxi_lcd_backlight_disable

功能:打开/关闭背光,操作的是board.dts 中lcd_bl 配置的gpio。见lcd_bl_en。

原型:void sunxi_lcd_backlight_enable(u32 screen_id)

void sunxi_lcd_backlight_disable(u32 screen_id)

函数:sunxi_lcd_pwm_enable / sunxi_lcd_pwm_disable

功能:打开/关闭pwm 控制器,打开时pwm 将往外输出pwm 波形。对应的是lcd_pwm_ch所对应的那一路pwm。

原型:s32 sunxi_lcd_pwm_enable(u32 screen_id)

s32 sunxi_lcd_pwm_disable(u32 screen_id)

4.2.7 电源控制函数说明

函数:sunxi_lcd_power_enable / sunxi_lcd_power_disable

功能:打开/关闭Lcd 电源,操作的是board.dts 中的lcd_power/lcd_power1/lcd_power2(pwr_id 标识电源索引)。

原型:void sunxi_lcd_power_enable(u32 screen_id, u32 pwr_id)

void sunxi_lcd_power_disable(u32 screen_id, u32 pwr_id)

pwr_id = 0:对应于board.dts 中的lcd_power。

pwr_id = 1:对应于board.dts 中的lcd_power1。

pwr_id = 2:对应于board.dts 中的lcd_power2。

pwr_id = 3:对应于board.dts 中的lcd_power3。

函数:sunxi_lcd_pin_cfg

功能:配置lcd 的io。

原型:s32 sunxi_lcd_pin_cfg(u32 screen_id, u32 bon)

说明:配置lcd 的data/clk 等pin,对应board.dts 中的lcdd0-lcdd23/lcddclk/lcdde/lcdhsync/lcdvsync。

由于dsi 是专用pin, 所以dsi 接口屏不需要在board.dts 中配置这组pin,但同样会在此函数接口中打开与关闭对应的pin。

Bon: 1: 为开,0:为配置成disable 状态。

4.2.8 DSI 相关函数说明

MIPI DSI 屏,大部分需要初始化,使用的是DSI-D0 通道的LP 模式进行初始化。提供的接口函数说明如下:

函数:sunxi_lcd_dsi_clk_enable / sunxi_lcd_dsi_clk_disble

功能:仅限dsi 接口屏使用,使能/关闭dsi 输出的高速时钟clk 信号,必须在初始化的时候调用。

原型:s32 sunxi_lcd_dsi_clk_enable(u32 scree_id)

s32 sunxi_lcd_dsi_clk_disable(u32 scree_id)

函数:sunxi_lcd_dsi_dcs_wr

功能:对屏的dcs 写操作。

原型:s32 sunxi_lcd_dsi_dcs_wr(u32 sel,u8 cmd,u8* para_p,__u32 para_num)

参数说明:

• cmd:dcs 写命令内容。

• para_p:dcs 写命令的参数起始地址。

• para_num:dcs 写命令的参数个数,单位为byte。

函数:sunxi_lcd_dsi_dcs_wr_2para

功能:对屏的dcs 写操作,该命令带有两个参数。

原型:s32 sunxi_lcd_dsi_dcs_wr_2para(u32 sel,u8 cmd,u8 para1,__u8 para2)

参数说明:

• cmd:dcs 写命令内容。

• para1:dcs 写命令的第一个参数内容。

• para2:dcs 写命令的第二个参数内容。

sunxi_dsi_dcs_wr_0para,sunxi_dsi_dcs_wr_1para,sunxi_dsi_dcs_wr_3para,sunxi_dsi_dcs_wr_4para,

sunxi_dsi_dcs_wr_5para定义与dsi_dcs_wr_2para类似,差别就是参数数量。

函数:sunxi_lcd_dsi_dcs_read

功能:dsi 读操作。

原型:s32 sunxi_lcd_dsi_dcs_read(u32 sel, u8 cmd, u8 result, u32 num_p)

参数说明:

• sel, 显示id。

• cmd, 要读取的寄存器。

• result,用于存放读取接口的数组,用户必须自行保证其有足够空间保存读取的接口。

• num_p,指针用于存放读取字节数,用户必须保证其非空指针。

4.2.9 I8080 接口函数说明

显示驱动提供5 个接口函数可供使用。如下:

函数:sunxi_lcd_cpu_write

功能:设定CPU 屏的指定寄存器为指定的值。

原型:void sunxi_lcd_cpu_write(u32 sel, u32 index, __u32 data)

函数内容为:

Void sunxi_lcd_cpu_write(__u32 sel, __u32 index, __u32 data) {    sunxi_lcd_cpu_write_index(sel, index);    sunxi_lcd_cpu_wirte_data(sel, data); }

实现了8080 总线上的两个写操作。

sunxi_lcd_cpu_write_index 实现第一个写操作,这时PIN 脚RS(A1)为低电平,总线数据上的数据内容为参数index 的值。

Sunxi_lcd_cpu_wirte_data 实现第二个写操作,这时PIN 脚RS(A1)为高电平,总线数据上的数据内容为参数data 的值。

函数:sunxi_lcd_cpu_write_index

功能:设定CPU 屏为指定寄存器。

原型:

void sunxi_lcd_cpu_write_index(__u32 sel,__u32 index)

具体说明见sunxi_lcd_cpu_write。

函数:sunxi_lcd_cpu_write_data

功能:设定CPU 屏寄存器的值为指定的值。

原型:

void Sunxi_lcd_cpu_write_data(__u32 sel, __u32 data);

函数: tcon0_cpu_rd_24b_data

功能:读操作。

原型:

s32 tcon0_cpu_rd_24b_data(u32 sel, u32 index, u32 *data, u32 size)

参数说明:

• sel:显示id。

• index: 要读取的寄存器。

• data:用于存放读取接口的数组指针,用户必须保证其有足够空间存放数据。

• size:要读取的字节数。

4.2.10 管脚控制函数说明

函数:sunxi_lcd_gpio_set_value

功能:LCD_GPIO PIN 脚上输出高电平或低电平。

原型:s32 sunxi_lcd_gpio_set_value(u32 screen_id, u32 io_index, u32 value)

参数说明:

• io_index = 0:对应于board.dts 中的lcd_gpio_0。

• io_index = 1:对应于board.dts 中的lcd_gpio_1。

• io_index = 2:对应于board.dts 中的lcd_gpio_2。

• io_index = 3:对应于board.dts 中的lcd_gpio_3。

• value = 0:对应IO 输出低电平。

• Value = 1:对应IO 输出高电平。

只用于该GPIO 定义为输出的情形。

函数:sunxi_lcd_gpio_set_direction

功能:设置LCD_GPIO PIN 脚为输入或输出模式。

原型:

s32 sunxi_lcd_gpio_set_direction(u32 screen_id, u32 io_index, u32 direction);

参数说明:

• io_index = 0:对应于board.dts 中的lcd_gpio_0。

• io_index = 1:对应于board.dts 中的lcd_gpio_1。

• io_index = 2:对应于board.dts 中的lcd_gpio_2。

• io_index = 3:对应于board.dts 中的lcd_gpio_3。

• direction = 0:对应IO 设置为输入。

• direction = 1:对应IO 设置为输出。

一部分屏需要进行初始化操作,在开屏步骤函数中,对应于LCD_panel_init 函数,提供了几种方式对屏的初始化。

对于DSI 屏,是通过DSI-D0 通道进行初始化。对于CPU 屏,是通过8080 总线的方式,使用的是LCDIO(PD,PH)进行初始化。这种初始化方式,其总线的引脚位

置定义与CPU 屏一致。

以下这些接口在屏驱动分解中提到路径的lcd_source.c 和lcd_source.h 中定义和实现。

4.2.11 使用iic/spi 串行接口初始化

需要在屏驱动中注册iic/spi 设备对串行接口的访问。

使用硬件spi 对屏或者转接IC 进行初始化,如下代码片段。

首先调用spi_init 函数对spi 硬件进行初始化,spi_init 函数可以分为几个步骤,第一获取master;根据实际的硬件连接,选择spi(代码中选择了spi1),如果这一

步返回错误说spi 没有配置好,找spi 驱动负责人。第二步设置spi device,这里包括最大速度,spi 传输模式,以及每个字包含的比特数。最后调用spi_setup 完成

master 和device 的关联。

comm_out 是一个spi 传输的例子,核心就是spi_sync_transfer 函数。

static int spi_init(void) {    int ret = -1;    struct spi_master *master;    master = spi_busnum_to_master(1);    if (!master) {        lcd_fb_wrn("fail to get mastern");        goto OUT    }    spi_device = spi_alloc_device(master);    if (!spi_device) {        lcd_fb_wrn("fail to get spi devicen");        goto OUT;    } spi_device->bits_per_word = 8;    spi_device->max_speed_hz = 60000000; /*50MHz*/    spi_device->mode = SPI_MODE_0;    ret = spi_setup(spi_device);    if (ret) {        lcd_fb_wrn("Faile to setup spin");        goto FREE;    } lcd_fb_inf("Init spi1:bits_per_word:%d max_speed_hz:%d mode:%dn",        spi_device->bits_per_word, spi_device->max_speed_hz,        spi_device->mode); ret = 0; goto OUT; FREE:    spi_master_put(master);    kfree(spi_device);    spi_device = NULL; OUT:    return ret; } static int comm_out(unsigned int sel, unsigned char cmd) {    struct spi_transfer t;    if (!spi_device)     return -1;    DC(sel, 0);    memset(&t, 0, sizeof(struct spi_transfer));    t.tx_buf = &cmd;    t.len = 1;    t.bits_per_word = 8;    t.speed_hz = 24000000;    return spi_sync_transfer(spi_device, &t, 1); }

使用硬件i2c 对LCD& 转接IC 进行初始化,初始化i2c 硬件的核心函数是i2c_add_driver,而你要做的是初始化好其参数struct i2c_driver。

it66121_id 包含设备名字以及i2c 总线索引(i2c0,i2c1…)。

it66121_i2c_probe 能进到这个函数,你就可以开始使用i2c 了。代码段里面仅仅将后面需要的参数cilent 赋值给一个全局指针变量。

it66121_match,这是dts 的match table,由于你是给disp2 加驱动,所以这里的match table 就是disp2 的match table,这个table 关系到能否使用i2c,注意不

要填错。 tv_i2c_detect 函数,这里是非常关键的,这个函数早于probe 函数被调用,只有成功被调用后才能开始使用i2c,其中strlcpy 的调用意味着成功。

normal_i2c 是从设备地址列表,填写的LCD 或者转接IC 的从设备地址以及i2c 索引。

以probe 函数是否被调用来决定你是否可以开始使用I2C。

用i2c_smbus_write_byte_data 或者i2c_smbus_read_byte_data 来读写可以满足大部分场景。

#define IT66121_SLAVE_ADDR 0x4c #define IT66121_I2C_ID 0 static const struct i2c_device_id it66121_id[] = {    { "IT66121", IT66121_I2C_ID },    { /* END OF LIST */ } }; MODULE_DEVICE_TABLE(i2c, it66121_id); static int it66121_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) {    this_client = client;    return 0; } static const struct of_device_id it66121_match[] = {    {.compatible = "allwinner,sun8iw10p1-disp",},    {.compatible = "allwinner,sun50i-disp",},    {.compatible = "allwinner,sunxi-disp",},    {}, }; static int tv_i2c_detect(struct i2c_client *client, struct i2c_board_info *info) {    const char *type_name = "IT66121";    if (IT66121_I2C_ID == client->adapter->nr) {     strlcpy(info->type, type_name, 20);    } else pr_warn("%s:%d wrong i2c id:%d, expect id is :%dn", __func__, __LINE__,     client->adapter->nr, IT66121_I2C_ID);    return 0; } static unsigned short normal_i2c[] = {IT66121_SLAVE_ADDR, I2C_CLIENT_END}; static struct i2c_driver it66121_i2c_driver = {    .class = I2C_CLASS_HWMON,    .id_table = it66121_id,    .probe = it66121_i2c_probe,    .remove = it66121_i2c_remove,    .driver = {        .owner = THIS_MODULE,        .name = "IT66121",        .of_match_table = it66121_match,    },    .detect = tv_i2c_detect,    .address_list = normal_i2c, }; static void LCD_panel_init(u32 sel) {    int ret = -1;    ret = i2c_add_driver(&it66121_i2c_driver);    if (ret) {        pr_warn("Add it66121_i2c_driver fail!n");        return;    }    //start init chip with i2c } void it6612_twi_write_byte(it6612_reg_set* reg) {    u8 rdata = 0;    u8 tmp = 0;    rdata = i2c_smbus_read_byte_data(this_client, reg->offset);    tmp = (rdata & (~reg->mask))|(reg->mask®->value);    i2c_smbus_write_byte_data(this_client, reg->offset, tmp); }

4.2.12 U-boot 屏驱动注意事项

U-boot 编写屏驱动的步骤和内核是一样的,代码路径文件组织方式都是一样的,这里要讲的是需要注意的事项。

1.为了加快U-boot 的显示速度,开屏的几个函数之间采取异步调用的方式,原理是利用timer中断,定时调用开屏函数,所以这种情况下bootGUI 框架加载完毕并

不意味着开屏完成,而是当你见到LCD open finish的打印的时候。

 

建议:为了尽量利用异步调用的优点,请把需要的延时尽量在注册回调的时候指定,比如下面延时10ms 就是利用timer 异步来进行回调的,这10ms 时间,uboot

就可以做其它事情,以达到异步调用的目的。

LCD_OPEN_FUNC(sel, LCD_power_on,10);

 

2.sunxi_lcd_power_enable 函数和sunxi_lcd_pin_cfg 不能在LCD_power_on之外调用,否则uboot 会异常。

 

严格讲,只能在用LCD_OPEN_FUNC注册的回调第一个函数里面调用。

 

4.3 RGB 接口

4.3.1 概述

下面介绍全志平台的RGB 以及配置示例,至于lcd0 下面每个属性的详解细节请看硬件参数说明。

RGB 接口在全志平台又称HV 接口(Horizontal 同步和Vertical 同步)。

对于RGB 屏的初始化:

有些LCD 屏支持高级的功能比如gamma,像素格式的设置等,但是RGB 协议本身不支持图像数据之外的传输,所以无法通过RGB 管脚进行对LCD 屏进行配置,

所以拿到一款RGB 接口屏,要么不需要初始化命令,要么这个屏会提供额外的管脚给SoC 来进行配置,比如SPI 和I2C等。

4.3.2 RGB 接口管脚

Linux

图4-6: RGB 管脚

上面这些脚具体到SoC 哪根管脚以及第几个功能(管脚复用功能)请参考pin mux 表格,管脚复用功能的名字一般以“LCDX_” 开头,其中X 是数字。

其中数据脚的数量不一定是24 根。RGB 又细分几种接口,通过设置lcd_hv_if来选择。

表4-1: RGB 接口分类

位宽 时钟周期 数颜色数量和格式
24 bits 1 cycle 16.7M colors, RGB888
18 bits 1 cycle 262K colors, RGB666
16 bits 1 cycle 65K colors, RGB565
6 bits 3 cycles 262K colors, RGB666
6 bits 3 cycles 65K colors, RGB565

说明

时钟周期数的意思:是一个像素需要用多少个时钟周期发送完毕的意思。

当时钟周期为1 时,我们称这种RGB 接口为并行接口,其它的情况则是串行接口,更为普遍的原则就是只要需要多个时钟周期才能发送完一个像素的接口都是串行

接口。

如何判断是否支持24bit 的位宽,最简单的方式就是在pinmux 表格中数一数数据脚的数量,如果有24 根则支持24bit,如果只有18 根则支持18bit。

 

硬件连接

对于并行RGB 的接口,当位宽小于24 时,硬件连接应该选择连接每个分量中的高位而放弃低位,这样做的原因是损失较少的颜色数量。

对于串行RGB 接口,硬件连接可参考RGB 和I8080 管脚配置示意图中sync RGB 那几列。

RGB 接口有两种同步方式,根据经验来说尽量使用第二种方式,硬件上请保证连接好DE 脚。

Hsync+Vsync

DE(Data Enable)

4.3.3 并行RGB 接口配置示例

当我们配置并行RGB 接口时,在配置里面并不需要区分是24 位,18 位和16 位,最大位宽是哪种是参考pin mux 表格,如果LCD 屏本身支持的位宽比SoC 支持的

位宽少,当然只能选择少的一方。

因为不需要初始化,RGB 接口极少出现问题,重点关注lcd 的timing 的合理性,也就是lcd_ht,lcd_hspw,lcd_hbp,lcd_vt,lcd_vspw和lcd_vbp这个属性的合理

性。

下面是典型并行RGB 接口board.dts 配置示例,其中用空行把配置分成几个部分

第一部分,决定该配置是否使用,以及使用哪个屏驱动,lcd_driver_name 决定了用哪个屏驱动来初始化,这里是default_lcd,是针对不需要初始化设置的

RGB 屏

第二部分决定下面的配置是一个并行RGB 的配置。

第三部分决定了SoC 中的LCD 模块发送时序,请查看屏时序参数说明。

第四部分决定了背光(pwm 和lcd_bl_en)。请看背光相关参数。

第五部分是显示效果部分的配置,如果非24 位的RGB,那么一般情况下需要设置lcd_frm。

第六部分就是电源和管脚配置。是用RGB666 还是RGB888,需要根据实际pinmux 表来决定,如果该芯片只有18 根rgb 数据则只能rgb18。请看电源和管脚

参数。

&lcd0 {    /* part 1 */    lcd_used = <1>;    lcd_driver_name = "default_lcd";    /* part 2 */    lcd_if = <0>;    lcd_hv_if = <0>;    /* part 3 */    lcd_width = <150>;    lcd_height = <94>;    lcd_x = <800>;    lcd_y = <480>;    lcd_dclk_freq = <33>;    lcd_hbp = <46>;    lcd_ht = <1055>;    lcd_hspw = <0>;    lcd_vbp = <23>;    lcd_vt = <525>;    lcd_vspw = <0>;    /* part 4 */    lcd_backlight = <50>;    lcd_pwm_used = <1>;    lcd_pwm_ch = <8>;    lcd_pwm_freq = <10000>;    lcd_pwm_pol = <1>;    lcd_bl_en = <&pio PD 27 1 0 3 1>;    lcd_bright_curve_en = <0>;    /* part 5 */    lcd_frm = <0>;    lcd_io_phase = <0x0000>;    lcd_gamma_en = <0>;    lcd_cmap_en = <0>;    lcd_hv_clk_phase = <0>;    lcd_hv_sync_polarity= <0>;    /* part 6 */    lcd_power = "vcc-lcd";    lcd_pin_power = "vcc-pd";    pinctrl-0 = <&rgb24_pins_a>;    pinctrl-1 = <&rgb24_pins_b>; };

4.3.4 串行RGB 接口的典型配置

串行RGB 是相对于并行RGB 来说,而并不是说它只用一根线来发数据,只要通过多个时钟周期才能把一个像素的数据发完,那么这样的RGB 接口就是串行RGB。

同样与并行RGB 接口一样,配置中并不需要也无法体现具体是哪种串行RGB 接口,需要做的就是把硬件连接对。

下面是典型串行RGB 接口board.dts 配置示例,它只有8 根数据脚,其中用空行把配置分成几个部分

第一部分,决定该配置是否使用,以及使用哪个屏驱动,lcd_driver_name 决定了用哪个屏驱动来初始化。

第二分部决定下面的配置是一个串行RGB 的配置。

第三部分决定了SoC 中的LCD 模块发送时序,请查看屏时序参数说明。

技巧 这里需要注意的是,对于该接口,SoC 总共需要三个周期才能发完一个pixel,所以我们配置时序的时候,需要满足lcd_dclk_freq3=lcd_htlcd_vt60,或者

lcd_dclk_freq=lcd_ht3lcd_vt60要么3 倍lcd_ht要么3倍lcd_dclk_freq。

第四部分决定了背光。就是pwm 和lcd_bl_en。请看背光相关参数

第五部分是显示效果方面的设置。

第六部分管脚和电源的定义。请看电源和管脚参数。

说明

下面实例的lcd driver IC 是stv7789v,是需要初始化,初始化的接口协议是SPI,所以这多了几根spi 管脚配置,驱动里面用gpio 模拟spi 协议,所以这里都是配置

gpio 功能。

&lcd0 {    /* part 1 */    lcd_used = <1>;    lcd_driver_name = "st7789v";    /* part 2 */    lcd_if = <0>;    lcd_hv_if = <8>;    /* part 3 */    lcd_x = <240>;    lcd_y = <320>;    lcd_width = <108>;    lcd_height = <64>;    lcd_dclk_freq = <19>;    lcd_hbp = <120>;    ;10 + 20 + 10 + 240*3 = 760 real set 1000    lcd_ht = <850>;    lcd_hspw = <2>;    lcd_vbp = <13>;    lcd_vt = <373>;    lcd_vspw = <2>;    /* part 4 */    lcd_backlight = <50>;    lcd_pwm_used = <1>;    lcd_pwm_ch = <8>;    lcd_pwm_freq = <50000>;    lcd_pwm_pol = <1>;    lcd_pwm_max_limit = <255>;    lcd_bl_en = <&pio PB 1 1 0 3 1>;    lcd_bright_curve_en = <1>;    /* part 5 */    lcd_frm = <1>;    lcd_hv_clk_phase = <0>;    lcd_hv_sync_polarity= <0>;    lcd_hv_srgb_seq = <0>;    lcd_io_phase = <0x0000>;    lcd_gamma_en = <0>;    lcd_cmap_en = <0>;    lcd_rb_swap = <0>;    /* part 6 */    lcd_power = "vcc-lcd";    lcd_pin_power = "vcc-pd";    /*reset */    lcd_gpio_0 = <&pio PD 9 1 0 3 1>;    /* cs */    lcd_gpio_1 = <&pio PD 10 1 0 3 0>;    /*sda */    lcd_gpio_2 = <&pio PD 13 1 0 3 0>;    /*sck */    lcd_gpio_3 = <&pio PD 12 1 0 3 0>;    pinctrl-0 = <&rgb8_pins_a>;    pinctrl-1 = <&rgb8_pins_b>; };

4.4 MIPI-DSI 接口

4.4.1 概述

MIPI-DSI,即Mobile Industry Processor Interface Display Serial Interface,移动通信行业处理器接口显示串行接口。

对于用户来说,需要了解:

Command mode,类似MPU 接口,需要IC 内部有GRAM 来缓冲。

Video mode。类似RGB 接口,没有GRAM,需要不停往panel 刷数据。其中video mode又分为三个子mode。

• Non-burst mode with sync pulses

• Non Burst mode with sync Events

• Burst mode。简单理解就是有效数据比率更高,传输效率更高。

lane 的意思是指一对差分管脚。

4.4.2 MIPI-DSI 的管脚

MIPI-DSI 的管脚是在大部分IC 中是专用,在board.dtsi 里面不需要配置,只要硬件上连接好就行。

但是有一部分IC 的DSI 管脚不是专用的,与其它功能的脚复用,这个时候就需要配置好pinctrl-0和pinctrl-1。

mipi-dsi 的管脚是差分的,分为两种管脚,一种是时钟管脚,另外一种是数据管脚,数据管脚的数量是可变的,数量的单位是lane,每一条lane 实际包含两条线。

一般来说LCD 屏说明书里面的说的lane 的数量是指数据管脚的数量不包括时钟管脚。比如说某4 lane MIPI-DSI 屏就总共有(4+1)*2根脚。

4.4.3 MIPI-DSI 的电源

一般都有一路电源供给MIPI-DSI 这个模块,你可以理解为管脚电,也可以理解成模块电,不同IC 这路电的电压要求可能不同,一旦确定IC 型号之后,这路电的电

压就不变,如果擅自改变此路电的电压可能导致模块异常。

Linux

图4-7: pinmux

4.4.4 判断是否支持某款MIPI-DSI 屏

1.分辨率限制。有lane 的速度限制,我们可以得到最大分辨率的限制,计算公式如下,只要lane_speed 不超过上面IC 规格规定的速度,那么理论上是支持的,

请查看IC 规格。

lane_speed=lcd_vt * lcd_ht * fps * bit_per_pixel / lane_num / 1e9

• 单位:Gbps。

• fps: 期望刷新率,通过屏手册可知道,一般是60。请看lcd_dclk_freq。

• bit_per_pixel: 每个像素包含的比特数量,一般是24 或者18,通过lcd_dsi_format来设置。

• lane_num:lane 数量,通过lcd_dsi_lane来设置。

• 1e9:1000000000 的科学计数写法。

选择分辨率的同时需要考虑系统带宽,DE 能力,所以即使接口方面支持这个分辨率,对于整个系统来说不一定支持,比如说硬件为了节省成本选择了一款速

度很慢的DDR 内存然后同时又想选择高分辨率的屏幕,很明显这是不现实的。

lane 数量限制。绝大部分全志科技IC 最大支持4 lane 的MIPI-DSI,如果你看到该款屏超过4 lane 就肯定不支持了。少数IC 最大支持8 lane,应该选择该款

IC。

MIPI-DSI 标准不兼容。请查看IC 规格。

4.4.5 计算MIPI-DSI 时钟lane 频率

使用示波器测量MIPI-DSI 的时钟信号,确定其频率是否满足屏的需求。

首先,我们由给定的像素时钟和lane 数量,可以计算出理论CLK 信号的频率,如下公式:

Freq_dsi_clk = (Dclk * colordepth * 3 / lane ) / 2

Freq_dsi_clk:我们要测量的dsi 时钟脚的频率。单位MHz。

Dclk:像素时钟。由lcd_htlcd_vtfps/1e6公式算出来。

Colordepth:颜色深度,一般是8 或者6。

乘以3 表示RGB 分量3 个。

Lane:dsi 的lane 数量。

除以2:是因为dsi 时钟是双沿采样。

4.4.6 MIPI-DSI Video mode 屏配置示例

绝大多数MIPI-DSI 屏的配置都是用video mode。

下面是典型MIPI-DSI video mode 的board.dts 配置示例,其中用空行把配置分成几个部分

第一部分,决定该配置是否使用,以及使用哪个屏驱动,lcd_driver_name 决定了用哪个屏驱动来初始化。

第二部分,决定该配置是dsi 接口,而且dsi 接口使用的是video mode。

第三部分,决定了SoC 中的LCD 模块发送时序,请查看屏时序参数说明。

第四部分,背光相关的设置。请看背光相关参数。

第五部分,dsi 接口的详细设置。

第六部分,显示效果相关的设置。

第七部分,管脚和电源设置。请看电源和管脚参数。

&lcd0 {    /* part 1 */    lcd_used = <1>;    lcd_driver_name = "k101im2qa04";    /* part 2 */    lcd_if = <4>;    lcd_dsi_if = <0>;    /* part 3 */    lcd_x = <800>;    lcd_y = <1280>;    lcd_width = <135>;    lcd_height = <216>;    lcd_dclk_freq = <68>;    lcd_hbp = <36>;    lcd_ht = <854>;    lcd_hspw = <18>;    lcd_vbp = <12>;    lcd_vt = <1320>;    lcd_vspw = <4>;    /* part 4 */    lcd_backlight = <50>;    lcd_pwm_used = <1>;    lcd_pwm_ch = <0>;    lcd_pwm_freq = <50000>;    lcd_pwm_pol = <1>;    lcd_pwm_max_limit = <255>;    lcd_bl_en = <&pio PB 8 1 0 3 1>;    lcd_bright_curve_en = <0>;    /* part 5 */    lcd_dsi_lane = <4>;    lcd_dsi_format = <0>;    lcd_dsi_te = <0>;    /* part 6 */    lcd_frm = <0>;    lcd_gamma_en = <0>;    lcd_cmap_en = <0>;    /* part 7 */    lcd_pin_power = "dcdc1";    lcd_pin_power1 = "eldo3";    lcd_power = "dc1sw";    lcd_gpio_0 = <&pio PD 22 1 0 3 1>;    pinctrl-0 = <&dsi4lane_pins_a>;    pinctrl-1 = <&dsi4lane_pins_b>; };

4.4.7 MIPI-DSI 超高分辨率屏配置示例

根据分辨率的高低通常分为几种模式来配置。1080p 分辨率及其以下:只需要设置lcd_dsi_if 来控制就行。Command mode 一般是低分辨率屏,而video mode

和burst mode 则是用于高分辨率的。如果分辨率达到2k,则需要额外的设置。

分辨率达到2k 以上的屏,实际上需要多达8 条数据lane 才能正常显示,其中四条lane 发送一副图像中的奇像素,另外一副图像发送偶像素。

说明

注意只有部分IC 支持超高分辨率,具体查看芯片规格中的MIPI-DSI 部分

下面是MIPI-DSI 高分辨超高分辨率(大于2k)board.dts 配置示例,其中用空行把配置分成几个部分

第一部分,决定该配置是否使用,以及使用哪个屏驱动,lcd_driver_name 决定了用哪个屏驱动来初始化。

第二部分,决定该配置是dsi 接口,而且dsi 接口使用的是video mode。

第三部分,决定了SoC 中的LCD 模块发送时序,请查看屏时序参数说明。

第四部分,背光相关的设置,请看背光相关参数。

第五部分,dsi 接口的详细设置。

说明 lcd_dsi_lane 依旧设置成4 条lane 的原因,是因为这个是设置一个dsi 的lane 数量,这个屏要用两个dsi。加起来就是8 条lane。

此时lcd_tcon_mode, lcd_dsi_port_num 和lcd_tcon_en_odd_even_div 三个选项需要特别设置,点击查看具体含义,如果是1080p 及其以下分辨率的屏(只用

4lane 或者以下的),那么这三个配置默认0 即可。

第六部分,显示效果部分的设置。

第七部分,是管脚和电源的配置。请根据电路图来配置。请看电源和管脚参数。

&lcd0 {    /* part 1 */    lcd_used = <1>;    lcd_driver_name = "lq101r1sx03";    /* part 2 */    lcd_if = <4>;    lcd_dsi_if = <0>;    /* part 3 */    lcd_x = <2560>;    lcd_y = <1600>;    lcd_width = <216>;    lcd_height = <135>;    lcd_dclk_freq = <268>;    lcd_hbp = <80>;    lcd_ht = <2720>;    lcd_hspw = <32>;    lcd_vbp = <37>;    lcd_vt = <1646>;    lcd_vspw = <6>;    /* part 4 */    lcd_backlight = <50>;    lcd_pwm_used = <1>;    lcd_pwm_ch = <0>;    lcd_pwm_freq = <50000>;    lcd_pwm_pol = <1>;    lcd_pwm_max_limit = <255>;    lcd_bl_en = <&pio PH 10 1 0 3 1>;    /* part 5 */    lcd_dsi_lane = <4>;    lcd_dsi_format = <0>;    lcd_dsi_te = <0>;    lcd_dsi_port_num = <1>;    lcd_tcon_mode = <4>;    lcd_tcon_en_odd_even_div = <1>;    /* part 6 */    lcd_frm = <0>;    lcd_io_phase = <0x0000>;    lcd_gamma_en = <0>;    lcd_bright_curve_en = <0>;    lcd_cmap_en = <0>;    /* part 7 */    lcd_power = "vcc18-lcd";    lcd_power1 = "vcc33-lcd";    lcd_pin_power = "vcc-pd";    lcd_gpio_0 = <&pio PH 11 1 0 3 1>;    lcd_gpio_1 = <&pio PH 12 1 0 3 1>; };

4.4.8 MIPI-DSI Command mode 屏配置示例

Command mode 下的DSI 屏类似与I8080 接口,屏内部带RAM 用于缓冲和图像处理,这种情况一般都需要用屏的te 脚来触发vsync 中断,所以与其它类型的DSI

屏不同的是,这里需要设置lcd_vsync 脚,屏的te 脚就连到lcd_vsync 上,并且lcd_dsi_te 设置成1。

te 脚的设置非常关键,一般来说如果屏有te 脚,则必须连上,否则在显示动态画面的时候会画面会撕裂,而且软件无法解决,直接造成最终硬件无法量产的结

果。

这里只列举出与MIPI-DSI video mode 不同的关键之处,其它参考上一小节。

第一部分,决定该配置是否使用,以及使用哪个屏驱动,lcd_driver_name 决定了用哪个屏驱动来初始化。

第二部分,决定该配置是dsi 接口,而且lcd_dsi_if设置成1 表明command mode。

第三部分,决定了SoC 中的LCD 模块发送时序,请查看屏时序参数说明。

第四部分,背光相关的设置。请看背光相关参数。

第五部分,dsi 接口的详细设置。lcd_dsi_te,这里设置为1 表示使能te 触发。

第六部分,显示效果相关的设置。

第七部分,管脚和电源设置。lcd_vsync,这里是te 脚,硬件上需要将这根脚连接到屏的te脚,软件上需要将其设置为vsync 功能。请看电源和管脚参数。

 

&lcd0 {    /* part 1 */    lcd_used = <1>;    lcd_driver_name = "h245qbn02";    /* part 2 */    lcd_if = <4>;    lcd_dsi_if = <1>;    /* part 3 */    lcd_x = <240>;    lcd_y = <432>;    lcd_width = <52>;    lcd_height = <52>;    lcd_dclk_freq = <18>;    lcd_hbp = <96>;    lcd_ht = <480>;    lcd_hspw = <2>;    lcd_vbp = <21>;    lcd_vt = <514>;    lcd_vspw = <2>;    /* part 4 */    lcd_backlight = <100>;    lcd_pwm_used = <1>;    lcd_pwm_ch = <0>;    lcd_pwm_freq = <50000>;    lcd_pwm_pol = <1>;    lcd_pwm_max_limit = <255>;    lcd_bright_curve_en = <0>;    lcd_bl_en = <&pio PB 3 1 0 3 1>;    /* part 5 */    lcd_dsi_lane = <1>;    lcd_dsi_format = <0>;    lcd_dsi_te = <1>;    lcd_frm = <0>;    lcd_io_phase = <0x0000>;    lcd_gamma_en = <0>;    lcd_cmap_en = <0>;    /* part 7 */    lcd_power = "axp233_dc1sw"    lcd_power1 = "axp233_eldo1"    lcd_gpio_0 = <&pio PB 2 1 0 3 0>;    lcd_vsync = <&pio PD 21 2 0 3 0>; };

4.4.9 MIPI-DSI VR 双屏配置示例

实际场景是两个物理屏,每个屏是1080p,每个屏都是4 条lane,要求的是两个屏各自显示一帧图像的左右一半,由于宽高比和横竖屏以及DE 处理能力的因素,

一个DE+ 一个tcon+ 两个DSI 已经无法满足,必须用两个tcon 各自驱动一个dsi,但是两路显示必须要同步,这就需要用到两个tcon 的同步模式。

LCD0 标记为slave tcon,它由master tcon 来驱动(设置lcd_tcon_mode)。

LCD1 标记为master tcon,并且负责两个屏的所有电源,背光,管脚的开关。

把管脚,电源等都放到LCD1 开,LCD0 先开,对应模块寄存器都初始化,但是电源不开,然后开LCD1,LCD1 使能就会触发LCD0 一起发数据。这样做到同时

亮灭。

说明

注意:仅有极少IC 支持该模式

&lcd0 {    lcd_used = <1>;    lcd_driver_name = "lpm025m475a";    ;lcd_bl_0_percent = <0>;    ;lcd_bl_40_percent = <23>;    ;lcd_bl_100_percent = <100>;    lcd_backlight = <50>;    lcd_if = <4>;    lcd_x = <1080>;    lcd_y = <1920>;    lcd_width = <31>;    lcd_height = <56>;    lcd_dclk_freq = <141>;    lcd_pwm_used = <0>;    lcd_pwm_ch = <0>;    lcd_pwm_freq = <20000>;    lcd_pwm_pol = <0>;    lcd_pwm_max_limit = <255>;    lcd_hbp = <100>;    lcd_ht = <1212>;    lcd_hspw = <5>;    lcd_vbp = <8>;    lcd_vt = <1936>;    lcd_vspw = <2>;    lcd_dsi_if = <0>;    lcd_dsi_lane = <4>;    lcd_dsi_format = <0>;    lcd_dsi_te = <0>;    lcd_dsi_eotp = <0>;    lcd_frm = <0>;    lcd_io_phase = <0x0000>;    lcd_hv_clk_phase = <0>;    lcd_hv_sync_polarity= <0>;    lcd_gamma_en = <0>;    lcd_bright_curve_en = <0>;    lcd_cmap_en = <0>;    lcd_dsi_port_num = <0>;    lcd_tcon_mode = <3>;    lcd_slave_stop_pos = <0>;    lcd_sync_pixel_num = <0>;    lcd_sync_line_num = <0>;    }; &lcd1 {    lcd_used = <1>;    lcd_driver_name = "lpm025m475a";    ;lcd_bl_0_percent = <0>;    ;lcd_bl_40_percent = <23>;    ;lcd_bl_100_percent = <100>;    lcd_backlight = <50>;    lcd_if = <4>;    lcd_x = <1080>;    lcd_y = <1920>;    lcd_width = <31>;    lcd_height = <56>;    lcd_dclk_freq = <141>;    lcd_pwm_used = <1>;    lcd_pwm_ch = <0>;    lcd_pwm_freq = <20000>;    lcd_pwm_pol = <0>;    lcd_pwm_max_limit = <255>;    lcd_hbp = <100>;    lcd_ht = <1212>;    lcd_hspw = <5>;    lcd_vbp = <8>;    lcd_vt = <1936>;    lcd_vspw = <2>;    lcd_dsi_if = <0>;    lcd_dsi_lane = <4>;    lcd_dsi_format = <0>;    lcd_dsi_te = <0>;    lcd_dsi_eotp = <0>;    lcd_frm = <0>;    lcd_io_phase = <0x0000>;    lcd_hv_clk_phase = <0>;    lcd_hv_sync_polarity= <0>;    lcd_gamma_en = <0>;    lcd_bright_curve_en = <0>;    lcd_cmap_en = <0>;    lcd_dsi_port_num = <0>;    lcd_tcon_mode = <1>;    lcd_tcon_slave_num = <0>;    lcd_slave_stop_pos = <0>;    lcd_sync_pixel_num = <0>;    lcd_sync_line_num = <0>;    lcd_bl_en = <&pio PH 10 1 0 3 1>;    lcd_power = "vcc-dsi";    lcd_power1 = "vcc18-lcd";    lcd_power2 = "vcc33-lcd";    lcd_gpio_0 = <&pio PH 8 1 0 3 1>;    lcd_gpio_1 = <&pio PH 11 1 0 3 1>;    lcd_gpio_2 = <&pio PH 12 1 0 3 1>;    lcd_pin_power = "vcc-ph" };

4.5 I8080 接口

4.5.1 概述

Intel 8080 接口屏(又称MCU 接口) 很老的协议,一般用在分辨率很小的屏上。

信号线:

• CS 片选信号,决定该芯片是否工作。

• RS 寄存器选择信号,低表示选择index 或者status 寄存器,高表示选择控制寄存器。实际场景中一般接SoC 的LCD_DE 脚(数据使能脚)。

• /WR (低表示写数据) 数据命令区分信号,也就是写时钟信号,一般接SoC 的LCD_CLK 脚。

• /RD (低表示读数据)数据读信号,也就是读时钟信号,一般接SoC 的LCD_HSYNC 脚。

• RESET 复位LCD(用固定命令系列0 1 0 来复位)。

• Data 双向传输的数据总线。

I8080 根据的数据位宽接口有8/9/16/18,连哪些脚参考,即使位宽一样,连的管脚也不一样,还要考虑的因素是rgb 格式。

RGB565,总共有65K 这么多种颜色。

RGB666,总共有262K 那么多种颜色。

9bit 固定为262K。

 

从屏手册得知:数据位宽,颜色数量之和,参考RGB 和I8080 管脚配置示意图,进行硬件连接。

 

4.5.2 I8080 接口屏典型配置示例

下面是典型是一个RGB565 的,位宽为8 位的I8080 接口的屏的board.dts 配置示例

1.第一部分,决定该配置是否使用,以及使用哪个屏驱动,lcd_driver_name 决定了用哪个屏驱动来初始化。

2.第二部分,决定该配置是I8080 接口,而且是8bit/2cycle 格式RGB565。

技巧 为什么叫做8bit/2cycle RGB565 呢,首先它的格式是RGB565,也就是一个像素是16bit,然后它是8bit 的位宽,就需要两个时钟周期才能发完一个像素,所以才

叫2 cycle。

第三部分,决定了SoC 中的LCD 模块发送时序,请查看屏时序参数说明。这里比较特殊的是设置像素时钟要满足以下公式:lcd_dclk_freq2>=lcd_htlcd_vt*fps,或者lcd_dclk_freq=lcd_ht 2lcd_vt*60, 也就是要么双倍lcd_ht要么双倍lcd_dclk_freq。

第四部分,背光相关的设置。请看背光相关参数。

第五部分,cpu 接口的详细设置。这里使能了lcd_cpu_te和lcd_cpu_mode,意思是使用te触发和规定了触发间隔。这是非常关键的设置。

第六部分,显示效果相关的设置。这里使能了lcd_frm也是比较关键的设置,详细意思点击查看。

第七部分,管脚和电源设置。这里为了用te 触发,同样需要设置lcd_vsync,该脚功能定义已经包括在pinctrl-0 中。这里自定义了一组管脚。参考RGB 和

I8080 管脚配置示意图,通过确定I8080 的位宽,像素格式(颜色数量),在表中确定需要连接哪些管脚。请看电源和管脚参数。

&pio {    I8080_8bit_pins_a: I8080_8bit@0 {        allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "        PD19", "PD20", "PD21";        allwinner,pname = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "        PD19", "PD20", "PD21";        allwinner,function = "I8080_8bit";        allwinner,muxsel = <2>;        allwinner,drive = <3>;        allwinner,pull = <0>;    };    I8080_8bit_pins_b: I8080_8bit@1 {        allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "        PD19", "PD20", "PD21";        allwinner,pname = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "        PD19", "PD20", "PD21";        allwinner,function = "I8080_8bit_suspend";        allwinner,muxsel = <7>;        allwinner,drive = <3>;        allwinner,pull = <0>; }; }; &lcd0 {    /* part 1 */    lcd_used = <1>;    lcd_driver_name = "s2003t46g";    /* part 2 */    lcd_if = <1>;    lcd_cpu_if = <14>;    /* part 3 */    lcd_x = <240>;    lcd_y = <320>;    lcd_width = <108>;    lcd_height = <64>;    lcd_dclk_freq = <16>;    lcd_hbp = <20>;    lcd_ht = <298>;    lcd_hspw = <10>;    lcd_vbp = <8>;    lcd_vt = <336>;    lcd_vspw = <4>;    /* part 4 */    lcd_pwm_used = <1>;    lcd_pwm_ch = <8>;    lcd_pwm_freq = <50000>;    lcd_pwm_pol = <1>;    lcd_pwm_max_limit = <255>;    lcd_bright_curve_en = <1>;    /* part 5 */    lcd_cpu_mode = <1>;    lcd_cpu_te = <1>;    /* part 6 */    lcd_frm = <1>;    lcd_gamma_en = <0>;    lcd_cmap_en = <0>;    lcd_rb_swap = <0>;    /* part 7 */    lcd_power = "vcc-lcd";    lcd_pin_power = "vcc-pd";    ;reset pin    lcd_gpio_0 = <&pio PD 9 1 0 3 1>;    ;cs pin    lcd_gpio_1 = <&pio PD 10 1 0 3 0>;    pinctrl-0 = <&I8080_8bit_pins_a>;    pinctrl-1 = <&I8080_8bit_pins_a>; };

4.6 LVDS 接口

4.6.1 概述

LVDS 即Low Voltage Differential Signaling 是一种低压差分信号接口。

4.6.2 LVDS Single link 典型配置

LVDS 接口,lcd0 对应的lvds 管脚和lcd1 对应的lvds 管脚是固定而且不一样。

由于lvds 协议不具备传输数据之外的能力,一般屏端不需要任何初始化,只需要初始化SoC 端即可。所以这里的lcd_driver_name 依旧是”default_lcd”,当然你可

以为初始化的启动延时做专门的优化。

下面是典型是single link lvds 屏的board.dts 配置示例,其中用空行把配置分成几个部分

 

第一部分,决定该配置是否使用,以及使用哪个屏驱动,lcd_driver_name 决定了用哪个屏 驱动来初始化。

第二部分,决定该配置是lvds 接口,而且是single link。 技巧 如果Dual Link 的屏,那么除了要改lcd_lvds_if 为1 之外,管脚方面还要把lcd1 的管脚一起搬到下面去,也就是总共需要配置PD0 到PD9,和配置PD10 到

PD19 总共二十根脚为lvds 管脚功能(功能3)。当然屏的timing 也是要根据屏来改的。

第三部分,决定了SoC 中的LCD 模块发送时序,请查看屏时序参数说明。

第四部分,背光相关的设置。请看背光相关参数。

第五部分,lvds 接口的详细设置。

第六部分,显示效果相关的设置。

第七部分,管脚和电源设置。请看电源和管脚参数。

&lcd0 {    /* part 1 */    lcd_used = 1    lcd_driver_name = "default_lcd";    /* part 2 */    lcd_if = 3    lcd_lvds_if = 0    /* part 3 */    lcd_x = 1280    lcd_y = 800    lcd_width = 150    lcd_height = 94    lcd_dclk_freq = 70    lcd_hbp = 20    lcd_ht = 1418    lcd_hspw = 10    lcd_vbp = 10    lcd_vt = 814    lcd_vspw = 5    /* part 4 */    lcd_pwm_used = 1    lcd_pwm_ch = 0    lcd_pwm_freq = 50000    lcd_pwm_pol = 0    lcd_pwm_max_limit = 255    lcd_backlight = 50    lcd_bright_curve_en = 0    lcd_bl_en = <&pio PD 21 1 0 3 1>;    /* part 5 */    lcd_lvds_colordepth = 1    lcd_lvds_mode = 0    /* part 6 */    lcd_frm = 1    lcd_hv_clk_phase = 0    lcd_hv_sync_polarity= 0    lcd_gamma_en = 0    lcd_cmap_en = 0    /* part 7 */    lcd_power = "vcc-lcd"    pinctrl-0 = <&lvds0_pins_a>;    pinctrl-1 = <&lvds0_pins_b>; };

4.6.3 LVDS dual link 典型配置

如果Dual Link 的屏:

lcd_lvds_if设置为1(场景1)或者2(场景2)。

管脚配置方面,也从4 data lane 变成8 data lane,包括clk lane 总共20 根管脚。

场景1,物理上连接一个屏,8 data lane,SoC 向每4 条lane 传输一半的像素,奇数像素或者偶数像素。

&lcd1 {    lcd_used = <1>;    lcd_driver_name = "bp101wx1";    lcd_backlight = <50>;    lcd_if = <3>;    lcd_x = <2560>;    lcd_y = <800>;    lcd_width = <150>;    lcd_height = <94>;    lcd_dclk_freq = <138>;    lcd_pwm_used = <0>;    lcd_pwm_ch = <2>;    lcd_pwm_freq = <50000>;    lcd_pwm_pol = <1>;    lcd_pwm_max_limit = <255>;    lcd_hbp = <40>;    lcd_ht = <2836>;    lcd_hspw = <20>;    lcd_vbp = <10>;    lcd_vt = <814>;    lcd_vspw = <5>;    lcd_lvds_if = <1>;    lcd_lvds_colordepth = <0>;    lcd_lvds_mode = <0>;    lcd_frm = <0>;    lcd_hv_clk_phase = <0>;    lcd_hv_sync_polarity= <0>;    lcd_gamma_en = <0>;    lcd_bright_curve_en = <0>;    lcd_cmap_en = <0>;    lcd_fsync_en = <0>;    lcd_fsync_act_time = <1000>;    lcd_fsync_dis_time = <1000>;    lcd_fsync_pol = <0>;    deu_mode = <0>;    lcdgamma4iep = <22>;    smart_color = <90>;    lcd_bl_en = <&pio PJ 27 1 0 3 1>;    lcd_gpio_0 = <&pio PI 1 1 0 3 1>;    lcd_pin_power = "bldo5";    lcd_power = "dc1sw";    pinctrl-0 = <&lcd1_lvds2link_pins_a>;    pinctrl-1 = <&lcd1_lvds2link_pins_b>; };

场景2(部分IC 支持),物理上连接两个屏,每个屏各自4 条lane,两个屏是一样型号,分辨率和timing 一样,这时候部分IC 支持将全部像素发到每个屏上,实现

双显(信号上的双显),注意这时候lcd timing 是一个屏的timing, lcd_lvds_if 为2。

lcd1: lcd1@01c0c001 {    lcd_used = <1>;    lcd_driver_name = "bp101wx1";    lcd_backlight = <50>;    lcd_if = <3>;    lcd_x = <1280>;    lcd_y = <800>;    lcd_width = <150>;    lcd_height = <94>;    lcd_dclk_freq = <70>;    lcd_pwm_used = <0>;    lcd_pwm_ch = <2>;    lcd_pwm_freq = <50000>;    lcd_pwm_pol = <1>;    lcd_pwm_max_limit = <255>;    lcd_hbp = <20>;    lcd_ht = <1418>;    lcd_hspw = <10>;    lcd_vbp = <10>;    lcd_vt = <814>;    lcd_vspw = <5>;    lcd_lvds_if = <2>;    lcd_lvds_colordepth = <0>;    lcd_lvds_mode = <0>;    lcd_frm = <0>;    lcd_hv_clk_phase = <0>;    lcd_hv_sync_polarity= <0>;    lcd_gamma_en = <0>;    lcd_bright_curve_en = <0>;    lcd_cmap_en = <0>;    lcd_fsync_en = <0>;    lcd_fsync_act_time = <1000>;    lcd_fsync_dis_time = <1000>;    lcd_fsync_pol = <0>;    deu_mode = <0>;    lcdgamma4iep = <22>;    smart_color = <90>;    lcd_bl_en = <&pio PJ 27 1 0 3 1>;    lcd_gpio_0 = <&pio PI 1 1 0 3 1>;    lcd_pin_power = "bldo5";    lcd_power = "dc1sw";    pinctrl-0 = <&lcd1_lvds2link_pins_a>;    pinctrl-1 = <&lcd1_lvds2link_pins_a>; };

4.7 RGB 和I8080 管脚配置示意图

Linux

图4-8: pinmux

4.8 从sys_config.fex 到board.dtsi 的迁移注意事项

为了规范等原因,部分平台将配置放在board.dtsi 中实现。下面说明了修改board.dtsi 的注意事项。

4.8.1 管脚定义

在配置RGB 屏或者LVDS 屏时,现在不再需要复杂的定义,也不需要了解到底哪些管脚需要配置,也不需要lcd0_suspend节点了。其中rgb24_pins_a这个名字是

定义好的,直接用即可,一般LCD屏直接可用的配置会在注释中写明,你可以在内核目录下arch/arm/boot/dts或者arch/arm64/boot/dts下的平台-pinctrl.dtsi 文

件中找。

例子:

pinctrl-0 = <&rgb24_pins_a>; pinctrl-1 = <&rgb24_pins_b>;//休眠时候的定义,io_disable

当然,你也可以自定义一组脚,写在board.dtsi 中,只要名字不要和现有名字重复就行。

为了规范,我们将在所有平台保持一致的名字,其中后缀为a 即为管脚使能,b 的为io_disable用于设备关闭时。

目前有以下管脚定义可用:

表4-2: 显示管脚名称表

管脚名称 描述
rgb24_pins_a 和rgb24_pins_b RGB 屏接口,而且数据位宽是24,RGB888
rgb18_pins_a 和rgb18_pins_b RGB 屏接口,而且数据位宽是16,RGB666
lvds0_pins_a和lvds0_pins_b Single link LVDS 接口0 管脚定义(主显lcd0)
lvds1_pins_a 和lvds1_pins_b Single link LVDS 接口1 管脚定义(主显lcd0)
lvds2link_pins_a 和lvds2link_pins_b Dual link LVDS 接口管脚定义(主显lcd0)
lvds2_pins_a 和lvds2_pins_b Single link LVDS 接口0 管脚定义(主显lcd1)
lvds3_pins_a 和lvds3_pins_b Single link LVDS 接口1 管脚定义(主显lcd1)
lcd1_lvds2link_pins_a 和lcd1_lvds2link_pins_b Dual link LVDS 接口管脚定义(主显lcd1)
dsi4lane_pins_a 和dsi4lane_pins_b DSI 屏接口管脚定义,4lane,如果是其它lane 数量,只

4.8.2 电源定义

电源定义在旧的SDK 中并不需要注意什么,还是直接把axp 的别名字符串赋值给想lcd_power这样的属性上即可,但是新的SDK 中,如果需要使用某路电源必须先

在disp 节点中定义,然后lcd 部分使用的字符串则要和disp 中定义的一致。比如下面的例子:

disp: disp@01000000 {    disp_init_enable = <1>;    disp_mode = <0>;    /* VCC-LCD */    dc1sw-supply = <®_sw>;    /* VCC-LVDS and VCC-HDMI */    bldo1-supply = <®_bldo1>;    /* VCC-TV */    cldo4-supply = <®_cldo4>; };

其中”-supply” 是固定的,它之前的字符串则是随意的,不过建议取有意义的名字。而后面的像<®_sw> 则必须在board.dtsi 的regulator0 节点中找到。

然后lcd0 节点中,如果要使用reg_sw,则类似下面这样写就行,dc1sw 对应dc1sw-supply。

lcd_power=”dc1sw”

由于u-boot 中也有axp 驱动和display 驱动,它们和内核一样,都是读取同份配置,为了能互相兼容,取名的时候,有以下限制。

在u-boot 2018 中,axp 驱动只认类似bldo1 这样从axp 芯片中定义的名字,所以命名xxxsupply的时候最好按照这个axp 芯片的定义来命名。

4.8.3 其它注意事项

board.dtsi 里面可能只有lcd0 没有lcd1,或只有tv0 没有tv1,这时候你要添加的话,需要参考内核目录arch/arm/boot/dts 或者arch/arm64/boot/dts 下对应的平

台.dtsi 文件。其中最关键的是@ 后面那串地址必须与内核中定义一致,比如:

lcd1: lcd1@01c0c000

5 硬件参数说明

5.1 LCD 接口参数说明

5.1.1 lcd_driver_name

Lcd 屏驱动的名字(字符串),必须与屏驱动的名字对应。

5.1.2 lcd_model_name

Lcd 屏模型名字,非必须,可以用于同个屏驱动中进一步区分不同屏。

5.1.3 lcd_if

Lcd Interface

设置相应值的对应含义为:

0:HV RGB接口 1:CPU/I80接口 2:Reserved 3:LVDS接口 4:DSI接口

5.1.4 lcd_hv_if

Lcd HV panel Interface

这个参数只有在lcd_if=0 时才有效。定义RGB 同步屏下的几种接口类型。

设置相应值的对应含义为:

0:Parallel RGB 8:Serial RGB 10:Dummy RGB 11:RGB Dummy 12:Serial YUV (CCIR656)

5.1.5 lcd_hv_clk_phase

Lcd HV panel Clock Phase

这个参数只有在lcd_if=0 时才有效。定义RGB 同步屏的clock 与data 之间的相位关系。总共有4 个相位可供调节。

设置相应值的对应含义为:

0: 0 degree 1: 90 degree 2: 180 degree 3: 270 degree

5.1.6 lcd_hv_sync_polarity

Lcd HV panel Sync signals Polarity

这个参数只有在lcd_if=0 时才有效。定义RGB 同步屏的hsync 和vsync 的极性。

设置相应值的对应含义为:

0:vsync active low,hsync active low 1:vsync active high,hsync active low 2:vsync active low,hsync active high 3:vsync active high,hsync active high

5.1.7 lcd_hv_srgb_seq

Lcd HV panel Serial RGB output Sequence

这个参数只有在lcd_if=0 且lcd_hv_if=8(Serial RGB)时才有效。

定义奇数行RGB 输出的顺序:

0: Odd lines R-G-B; Even line R-G-B 1: Odd lines B-R-G; Even line R-G-B 2: Odd lines G-B-R; Even line R-G-B 4: Odd lines R-G-B; Even line B-R-G 5: Odd lines B-R-G; Even line B-R-G 6: Odd lines G-B-R; Even line B-R-G 8: Odd lines R-G-B; Even line B-R-G 9: Odd lines B-R-G; Even line G-B-R 10: Odd lines G-B-R; Even line G-B-R

5.1.8 lcd_hv_syuv_seq

Lcd HV panel Serial YUV output Sequence

这个参数只有在lcd_if=0 且lcd_hv_if=12(Serial YUV)时才有效。

定义YUV 输出格式:

0:YUYV 1:YVYU 2:UYVY 3:VYUY

5.1.9 lcd_hv_syuv_fdly

Lcd HV panel Serial YUV F line Delay

这个参数只有在lcd_if=0 且lcd_hv_if=12(Serial YUV)时才有效。

定义CCIR656 编码时F 相对有效行延迟的行数:

0:F toggle right after active video line 1:Delay 2 lines (CCIR PAL) 2:Delay 3 lines (CCIR NTSC)

5.1.10 lcd_cpu_if

Lcd CPU panel Interface

这个参数只有在lcd_if=1 时才有效, 具体时序可参照RGB 和I8080 管脚配置示意图中CPU 那几列。

设置相应值的对应含义为:

0:18bit/1cycle (RGB666) 2: 16bit/3cycle (RGB666) 4:16bit/2cycle (RGB666) 6:16bit/2cycle (RGB666) 8:16bit/1cycle (RGB565) 10:9bit/1cycle (RGB666) 12:8bit/3cycle (RGB666) 14:8bit/2cycle (RGB565)

5.1.11 lcd_cpu_te

Lcd CPU panel tear effect

设置相应值的对应含义为,设置为0 时,刷屏间隔时间为lcd_ht × lcd_vt;设置为1 或2 时,刷屏间隔时间为两个te 脉冲:

Lcd CPU panel tear effect

设置相应值的对应含义为,设置为0 时,刷屏间隔时间为lcd_ht × lcd_vt;设置为1 或2 时,刷屏间隔时间为两个te 脉冲:

0:frame trigged automatically 1:frame trigged by te rising edge 2:frame trigged by te falling edge

5.1.12 lcd_lvds_if

Lcd LVDS panel Interface

设置相应值的对应含义为:

0:Single Link( 1 clock pair+3/4 data pair) 1:Dual Link(8 data lane,每4条lane接受一半像素,奇数像素或者偶数像素) 2: Dual Link (每4条lane接受全部像素,常用于物理双屏,且两个屏一样)

lcd_lvds_if 等于2 的场景是,接两个一模一样的屏,然后两个屏显示同样的内容,此时lcd 的其它timing 只需要填写一个屏的timing 即可。

5.1.13 lcd_lvds_colordepth

Lcd LVDS panel color depth

设置相应值对应含义为:

0:8bit per color(4 data pair) 1:6bit per color(3 data pair)

5.1.14 lcd_lvds_mode

Lcd LVDS Mode

这个参数只有在lcd_lvds_bitwidth=0 时才有效。

设置相应值对应含义为(见下图):

0:NS mode 1:JEIDA mode

Linux

图5-1: lvds mode

5.1.15 lcd_dsi_if

Lcd MIPI DSI panel Interface

这个参数只有在lcd_if=4 时才有效。定义MIPI DSI 屏的两种类型。

设置相应值的对应含义为:

0:Video mode 1:Command mode 2:video burst mode

注:Video mode 的LCD 屏,是实时刷屏的,有ht,hbp 等时序参数的定义;Command mode 的屏,屏上带有显示Buffer,一般会有一个TE 引脚。

5.1.16 lcd_dsi_lane

Lcd MIPI DSI panel Data Lane number

这个参数只有在lcd_if=4 时才有效。

设置相应值的对应含义为:

1:1 data lane 2:2 data lane 3:3 data lane 4:4 data lane

5.1.17 lcd_dsi_format

Lcd MIPI DSI panel Data Pixel Format

这个参数只有在lcd_if=4 时才有效。

设置相应值的对应含义为:

0:Package Pixel Stream, 24bit RGB 1:Loosely Package Pixel Stream, 18bit RGB 2:Package Pixel Stream, 18bit RGB 3:Package Pixel Stream, 16bit RGB

5.1.18 lcd_dsi_te

Lcd MIPI DSI panel Tear Effect

这个参数只有在lcd_if=4 时才有效。

设置相应值的对应含义为:

0:frame trigged automatically 1:frame trigged by te rising edge 2:frame trigged by te falling edge

注:设置为0 时,刷屏间隔时间为lcd_ht × lcd_vt;设置为1 或2 时,刷屏间隔时间为两个te脉冲。

这个的作用就是屏一端发给SoC 端的信号,用于同步信号,如果使能这个变量,那么SoC 内部的显示中断将由这个外部脚来触发。

5.1.19 lcd_dsi_port_num

DSI 屏port 数量

这个参数只有在lcd_if=4 时才有效。

设置相应值的对应含义为:

0:一个port 1:两个port

这个选项的一个作用是,单LCD 屏为8 条lane 的时候,如果只需要初始化其中一个driver IC,则这个设置1,如果两个driver IC 都要初始化,这里设置成0。并使用lcd_source.c 定义的函数来进行初始化。

5.1.20 lcd_tcon_mode

Tcon 模式

这个参数只有在lcd_if=4 时才有效。

设置相应值的对应含义为:

0:normal mode 1:tcon master mode(在第一次发送数据同步) 2::tcon master mode(每一帧都同步) 3:tcon slave mode(依靠master mode来启动) 4:one tcon driver two dsi(8条lane) 5.1.21 lcd_slave_tcon_num

5.1.21 lcd_slave_tcon_num

Slave Tcon 的序号

这个参数只有在lcd_if=4 时而且lcd_tcon_mode 等于1 或者2 才有效。用于告诉master 模式下的tcon,从tcon 的序号是多少。

设置相应值的对应含义为:

0:tcon_lcd0 1:tcon_lcd1

5.1.22 lcd_tcon_en_odd_even_div

这个参数只有在lcd_if=4 而且lcd_tcon_mode=4 时才有效。

设置相应值的对应含义为:

0:tcon将一帧图像分左右两半来发送给两个DSI模块 1:tcon将一帧图像分奇偶像素来发给两个DSI模块

5.1.23 lcd_sync_pixel_num

这个参数只有在lcd_if=4 而且lcd_tcon_mode 等于2 或者3 时才有效。

设置同步从tcon 的起始pixel。

整数:不超过lcd_ht

5.1.24 lcd_sync_line_num

这个参数只有在lcd_if=4 而且lcd_tcon_mode 等于2 或者3 时才有效。

设置同步从tcon 的起始行。

整数:不超过lcd_vt

5.1.25 lcd_cpu_mode

Lcd CPU 模式,控制。

设置相应值的对应含义为,设置为0 时,刷屏间隔时间为lcd_ht × lcd_vt;设置为1 或2 时,刷屏间隔时间为两个te 脉冲:

0:中断自动根据时序,由场消隐信号内部触发。 1:中断根据数据Block的counter触发或者由外部te触发。

5.1.26 lcd_fsync_en

LCD 使能fsync 功能,用于触发sensor 出图, 目的是同步,部分IC 支持。

0:disable 1:enable

 

5.1.27 lcd_fsync_act_time

LCD 的fsync 功能,其中的有效电平时间长度,单位:像素时钟的个数。

0~lcd_ht-1

5.1.28 lcd_fsync_dis_time

LCD 的fsync 功能,其中的无效电平时间长度,单位:像素时钟的个数。

0~lcd_ht-1

5.1.29 lcd_fsync_pol

LCD 的fsync 功能的有效电平的极性。

0:有效电平为低 1:有效电平为高

5.2 屏时序参数说明

下面几个参数对于调屏非常关键,决定了发送端(SoC)发送数据时序。由于涉及到发送端和接收端的调试,除了分辨率和尺寸之外,其它几个数值都不是绝对不

变的,两款一样分辨率,同种接口的屏,它们的数值也有可能不一样。

获取途径如下:

询问LCD 屏厂。

从屏手册或者Driver IC 手册中查找(向屏厂索要这些文档),如下图所示。

Linux

图5-2: lcd_info1

Linux

图5-3: lcd_info2

在前面两步都搞不定的情况下,可以根据vesa 标准来设置,主要是DMT 和CVT 标准。

其中DMT,指的是《VESA and Industry Standards and Guidelines for Computer Display Monitor Timing(DMT)》,下载该标准,里面就有各种常用分辨率的

timing。其中的CVT,指的是《VESA Coordinated Video Timings(CVT) Standard》,该标准提供一种通用公式用于计算出指定分辨率,刷新率等参数的timing。

可以下载这个excel 表来计算VESA Coordinated Video Timing Generator。

由下面两条公式得知,我们不需要设置lcd_hfp和lcd_vfp参数,因为驱动会自动根据其它几个已知参数中算出lcd_hfp和lcd_vfp。

lcd_ht = lcd_x + lcd_hspw + lcd_hbp + lcd_hfp lcd_vt = lcd_y + lcd_vspw + lcd_vbp + lcd_vfp

5.2.1 lcd_x

显示屏的水平像素数量,也就是屏分辨率中的宽。

5.2.2 lcd_y

显示屏的垂直行数,也就是屏分辨率中的高。

5.2.3 lcd_ht

Horizontal Total time

指一行总的dclk 的cycle 个数。见下图:

Linux

图5-4: lcdht

5.2.4 lcd_hbp

Horizontal Back Porch

指有效行间,行同步信号(hsync)开始,到有效数据开始之间的dclk 的cycle 个数,包括同步信号区。见上图,注意的是包含了hspw 段。

说明 是包含了hspw 段,也就是lcd_hbp= 实际的hbp+ 实际的hspw

5.2.5 lcd_hspw

Horizontal Sync Pulse Width

指行同步信号的宽度。单位为1 个dclk 的时间(即是1 个data cycle 的时间)。见上图。

5.2.6 lcd_vt

Vertical Total time

指一场的总行数。见下图:

Linux

图5-5: lcdvt

5.2.7 lcd_vbp

Vertical Back Porch

指场同步信号(vsync)开始,到有效数据行开始之间的行数,包括场同步信号区。

说明

是包含了vspw 段,也就是lcd_vbp= 实际的vbp+ 实际的vspw

5.2.8 lcd_vspw

Vertical Sync Pulse Width

指场同步信号的宽度。单位为行。见上图。

5.2.9 lcd_dclk_freq

Dot Clock Frequency

传输像素传送频率。单位为MHz。

fps = (lcd_dclk_freq×1000×1000) / (ht×vt)。

这个值根据以下公式计算:

lcd_dclk_freq=lcd_htlcd_vtfps

注意:

后面的三个参数都是从屏手册中获得,fps 一般是60。

如果是串行接口,发完一个像素需要2 到3 个周期的,那么。

lcd_dclk_freq * cycles = lcd_ht*lcd_vt*fps

或者

lcd_dclk_freq = lcd_ht*cycles*lcd_vt*fps

5.2.10 lcd_width

Width of lcd panel in mm

此参数描述lcd 屏幕的物理宽度,单位是mm。用于计算dpi。

5.2.11 lcd_height

height of lcd panel in mm

此参数描述lcd 屏幕的物理高度,单位是mm。用于计算dpi。

5.3 背光相关参数

目前用得比较广泛的就是pwm 背光调节,原理是利用pwm 脉冲开关产生的高频率闪烁效应,通过调节占空比,达到欺骗人眼,调节亮暗的目的。

5.3.1 lcd_pwm_used

是否使用pwm。

此参数标识是否使用pwm 用以背光亮度的控制。

5.3.2 lcd_pwm_ch

Pwm channel used

此参数标识使用的Pwm 通道,这里是指使用SoC 哪个pwm 通道,通过查看原理图连接可知。

5.3.3 lcd_pwm_freq

Lcd backlight PWM Frequency

这个参数配置PWM 信号的频率,单位为Hz。

说明 频率不宜过低否则很容易就会看到闪烁,频率不宜过快否则背光调节效果差。部分屏手册会标明所允许的pwm 频率范围,请遵循屏手册固定范围进行设置。 在低亮度的时候容易看到闪烁,是正常现象,目前已知用上pwm 的背光都是如此。

5.3.4 lcd_pwm_pol

Lcd backlight PWM Polarity

这个参数配置PWM 信号的占空比的极性。设置相应值对应含义为:

0:active high 1:active low

5.3.5 lcd_pwm_max_limit

Lcd backlight PWM 最高限制,以亮度值表示

比如150,则表示背光最高只能调到150,0-255 范围内的亮度值将会被线性映射到0-150 范围内。用于控制最高背光亮度,节省功耗。

5.3.6 lcd_bl_en

背光使能脚,非必须,看原理图是否有,用于使能或者禁止背光电路的电压。

示例:lcd_bl_en = port:PD24<1><2><1>

含义:PD24 输出高电平时打开LCD 背光;下拉,默认高电平

• 第一个尖括号:功能分配;1 为输出;

• 第二个尖括号:内置电阻;使用0 的话,标示内部电阻高阻态,如果是1 则是内部电阻上拉,2 就代表内部电阻下拉。使用default 的话代表默认状态,即电阻上

拉。其它数据无效。

• 第三个尖括号:驱动能力;default 表驱动能力是等级1

• 第四个尖括号:电平;0 为低电平,1 为高电平。

需要在屏驱动调用相应的接口进行开、关的控制。

说明

一般来说,高电平是使能,在这个前提下,建议将内阻电阻设置成下拉,防止硬件原因造成的上拉,导致背光提前亮。默认电平请填写高电平,这是uboot 显示过

度到内核显示,平滑无闪烁的需要。

5.3.7 lcd_bl_n_percent

背光映射值,n 为(0-100)

此功能是针对亮度非线性的LCD 屏的,按照配置的亮度曲线方式来调整亮度变化,以使亮度变化更线性。

比如lcd_bl_50_percent = 60,表明将50% 的亮度值调整成60%,即亮度比原来提高10%。

说明

修改此属性不当可能导致背光调节效果差。

5.3.8 lcd_backlight

背光默认值,0-255。

此属性决定在uboot 显示logo 阶段的亮度,进入都内核时则是读取保存的配置来决定亮度。

说明

显示logo 阶段,一般来说需要比较亮的亮度,业内做法都是如此。

5.4 显示效果相关参数

5.4.1 lcd_frm

Lcd Frame Rate Modulator

FRM 是解决由于PIN 减少导致的色深问题。

这个参数设置相应值对应含义为:

0:RGB888 -- RGB888 direct 1:RGB888 -- RGB666 dither 2:RGB888 -- RGB565 dither

有些LCD 屏的像素格式是18bit 色深(RGB666)或16bit 色深(RGB565),建议打开FRM功能,通过dither 的方式弥补色深,使显示达到24bit 色深(RGB888)

的效果。如下图所示,上图是色深为RGB66 的LCD 屏显示,下图是打开dither 后的显示,打开dither 后色彩渐变的地方过度平滑。

Linux

图5-6: lcd_frm 打开

Linux

图5-7: lcd_frm 关闭

5.4.2 lcd_gamma_en

Lcd Gamma Correction Enable

设置相应值的对应含义为:

0:Lcd的Gamma校正功能关闭 1:Lcd的Gamma校正功能开启

设置为1 时,需要在屏驱动中对lcd_gamma_tbl[256] 进行赋值。

5.4.3 lcd_cmap_en

Lcd Color Map Enable

设置相应值的对应含义为:

0:Lcd的色彩映射功能关闭 1:Lcd的色彩映射功能开启

设置为1 时,需要对lcd_cmap_tbl 2[4] 进行赋值Lcd Color Map Table。

每个像素有R、G、B 三个单元,每四个像素组成一个选择项,总共有12 个可选。数组第一维表示奇偶行,第二维表示像素的RGB,第三维表示第几个像素,数组

的内容即表示该位置映射到的内容。

LCD CMAP 是对像素的映射输出功能,只有像素有特殊排布的LCD 屏才需要配置。

LCD CMAP 定义每行的4 个像素为一个总单元,每个像素分R、G、B 3 个小单元,总共有12个小单元。通过lcd_cmap_tbl 定义映射关系,输出的每个小单元可随

意映射到12 个小单元之一。

__u32 lcd_cmap_tbl[2][3][4] = {    {        {LCD_CMAP_G0,LCD_CMAP_B1,LCD_CMAP_G2,LCD_CMAP_B3},        {LCD_CMAP_B0,LCD_CMAP_R1,LCD_CMAP_B2,LCD_CMAP_R3},        {LCD_CMAP_R0,LCD_CMAP_G1,LCD_CMAP_R2,LCD_CMAP_G3},    },    {        {LCD_CMAP_B3,LCD_CMAP_G2,LCD_CMAP_B1,LCD_CMAP_G0},        {LCD_CMAP_R3,LCD_CMAP_B2,LCD_CMAP_R1,LCD_CMAP_B0},        {LCD_CMAP_G3,LCD_CMAP_R2,LCD_CMAP_G1,LCD_CMAP_R0},    }, };

如上,上三行代表奇数行的像素排布,下三行代表偶数行的像素排布。

每四个像素为一个单元,第一列代表每四个像素的第一个像素映射,第二列代表每四个像素的第二个像素映射,以此类推。

如上的定义,像素的输出格式如下图所示。

Linux

图5-8: cmap

5.4.4 lcd_rb_swap

调换tcon 模块RGB 中的R 分量和B 分量。

0:不变 1:调换R分量和B分量

5.5 电源和管脚参数

5.5.1 概述

如果需要使用某路电源必须现在[disp]节点中定义,然后[lcd]部分使用的字符串则要和disp 中定义的一致。比如下面的例子:

disp: disp@01000000 {    disp_init_enable = <1>;    disp_mode = <0>;    /* VCC-LCD */    dc1sw-supply = <®_sw>;    /* VCC-LVDS and VCC-HDMI */    bldo1-supply = <®_bldo1>;    /* VCC-TV */    cldo4-supply = <®_cldo4>; };

其中-supply是固定的,在-supply之前的字符串则是随意的,不过建议取有意义的名字。在=后面的像<®_sw>则必须在board.dtsi 的regulator0节点中找到。

然后lcd0 节点中,如果要使用reg_sw,则像下面这样写就行,dc1sw 对应dc1sw-supply。

lcd_power=”dc1sw”

由于u-boot 中也有axp 驱动和display 驱动,和内核,它们都是读取同份配置,为了能互相兼容,取名的时候,有以下限制:

在u-boot 2018 中,axp 驱动只认类似bldo1 这样从axp 芯片中定义的名字,所以命名xxxsupply的时候最好按照这个axp 芯片的定义来命名。

5.5.2 lcd_power

见上面概述的注意事项。

示例:lcd_power = “vcc-lcd”

配置regulator 的名字。配置好之后,需要在屏驱动调用相应的接口进行开、关的控制。

注意:如果有多个电源需要打开,则定义lcd_power1,lcd_power2 等。

5.5.3 lcd_pin_power

用法lcd_power一致,区别是用户设置之后,不需要在屏驱动中去操作,而是驱动框架自行在屏驱动之前使能,在屏驱动之后禁止。

示例:lcd_pin_power = “vcc-pd”

注意:如果需要多组,则添加lcd_pin_power1,lcd_pin_power2 等。除了lcddx 之外,这里的电源还有可能是pwm 所对应管脚的电源。

5.5.4 lcd_gpio_0

示例:lcd_gpio_0 = port:PD25<0><0><0>

含义:lcd_gpio_0 引脚为PD25。

• 第一个尖括号:功能分配;0 为输入,1 为输出。

• 第二个尖括号:内置电阻;使用0 的话,标示内部电阻高阻态,如果是1 则是内部电阻上拉,2 就代表内部电阻下拉。使用default 的话代表默认状态,即电阻上

拉。其它数据无效。

• 第三个尖括号:驱动能力;default 表驱动能力是等级1。

• 第四个尖括号:表示默认值;即是当设置为输出时,该引脚输出的电平,0 为低电平,1 为高电平。

需要在屏驱动调用相应的接口进行拉高,拉低的控制。请看管脚控制函数说明

注意:如果有多个gpio 脚需要控制,则定义lcd_gpio_0,lcd_gpio_1 等。

5.5.5 lcddx

示例:lcdd0 = port:PD00<3><0>

含义:lcdd0 这个引脚,即是PD0,配置为LVDS 输出。

• 第一个尖括号:功能分配;0 为输入,1 为输出,2 为LCD 输出,3 为LVDS 接口输出,7 为disable。

• 第二个尖括号:内置电阻;使用0 的话,标示内部电阻高阻态,如果是1 则是内部电阻上拉,2 就代表内部电阻下拉。使用default 的话代表默认状态,即电阻上

拉。其它数据无效。

• 第三个尖括号:驱动能力;default 表驱动能力是等级1。

• 第四个尖括号:表示默认值;即是当设置为输出时,该引脚输出的电平,0 为低电平,1 为高电平。

LCD PIN 的配置如下:

LCD 为HV RGB 屏,CPU/I80 屏时,必须定义相应的IO 口为LCD 输出(如果是0 路输出,第一个尖括号为2;如果是1 路输出,第一个尖括号为3)。

具体的IO 对应关系可参考user manual 手册进行配置。

LCD PIN 的所有IO,均可通过注释方式去掉其定义,显示驱动对注释IO 不进行初始化操作。

需要在屏驱动调用相应的接口进行开、关的控制。

注意:不是一定要叫做lcdd0 这个名字,你改成其它名字对驱动不会造成任何影响,这里只是为了方便记忆。

5.5.6 pinctrl-0 和pinctrl-1

在配置lcd0节点时,当碰到需要配置管脚复用时,你只要把pinctrl-0和pinctrl-1赋值好就行,可以用提前定义好的,也可以用自己定义的,提前定义的管脚一般可

以在内核目录下arch/arm/boot/dts或者arch/arm64/boot/dts下找:平台-pinctrl.dtsi 中找到定义。

例子:

pinctrl-0 = <&rgb24_pins_a>; pinctrl-1 = <&rgb24_pins_b>;//休眠时候的定义,io_disable

表5-1: 提前定义好的管脚名称

管脚名称 描述
rgb24_pins_a 和rgb24_pins_b RGB 屏接口,而且数据位宽是24,RGB888
rgb18_pins_a 和rgb18_pins_b RGB 屏接口,而且数据位宽是16,RGB666
lvds0_pins_a 和lvds0_pins_b Single link LVDS 接口0 管脚定义(主显lcd0)
lvds1_pins_a 和lvds1_pins_b Single link LVDS 接口1 管脚定义(主显lcd0)
lvds2link_pins_a 和lvds2link_pins_b Dual link LVDS 接口管脚定义(主显lcd0)
lvds2_pins_a 和lvds2_pins_b Single link LVDS 接口0 管脚定义(主显lcd1)
lvds3_pins_a 和lvds3_pins_b Single link LVDS 接口1 管脚定义(主显lcd1)
lcd1_lvds2link_pins_a 和lcd1_lvds2link_pins_b Dual link LVDS 接口管脚定义(主显lcd1)
dsi4lane_pins_a 和dsi4lane_pins_b 4 lane DSI 屏接口管脚定义

自定义一组脚

写在board.dtsi 中,只要名字不要和现有名字重复就行,首先判断自己需要用的管脚,属于大cpu 域还是小cpu 域,以此判断需要将管脚定义放在pio(大cpu

域)下面还是r_pio(小cpu域)下面。

例子:

&pio {    I8080_8bit_pins_a: I8080_8bit@0 {        allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "         PD19", "PD20", "PD21";        allwinner,pname = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "            PD19", "PD20", "PD21";            allwinner,function = "I8080_8bit";            allwinner,muxsel = <2>;            allwinner,drive = <3>;            allwinner,pull = <0>;        };        I8080_8bit_pins_b: I8080_8bit@1 {        allwinner,pins = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "         PD19", "PD20", "PD21";        allwinner,pname = "PD1", "PD2", "PD3", "PD4", "PD5", "PD6", "PD7", "PD8", "PD18", "         PD19", "PD20", "PD21";            allwinner,function = "I8080_8bit_suspend";            allwinner,muxsel = <7>;            allwinner,drive = <3>;            allwinner,pull = <0>;    }; };

• pins,具体管脚。

• pname,管脚名称,随便取。

• function,管脚功能名称,随便取。

• muxsel,管脚功能选择。根据port spec 来选择对应功能。

• drive,驱动能力,数值越大驱动能力越大。

• pull,上下拉,使用0 的话,标示内部电阻高阻态,如果是1 则是内部电阻上拉,2 就代表内部电阻下拉。使用default 的话代表默认状态,即电阻上拉。其它数

据无效。

为了规范,我们将在所有平台保持一致的名字,其中后缀为a 为管脚使能,b 的为io_disable 用于设备关闭时。

有时候,你需要用两组不同功能的管脚,可以像下面这样定义即可。

pinctrl-0 = <&rgb24_pins_a>, <&xxx_pins_a>; pinctrl-1 = <&rgb24_pins_b>, <&xxx_pins_b>;//休眠时候的定义,io_disable

5.6 ESD 静电检测自动恢复功能

这个功能在linux4.9 以及linux 3.10 sunxi-product 分支上实现了,如果需要这个功能,需要完成以下步骤。

首先打开如下内核配置:

Linux

图5-9: ESD 内核配置

修改屏驱动,实现三个回调函数:

如下示例,在屏he0801a068 上添加esd 相关的回调函数。 (linux-4.9/drivers/video/fbdev/sunxi/disp2/disp/lcd/he0801a068.c)。

Linux

图5-10: ESD 屏驱动添加函数

esd_check 函数原型:

S32 esd_check(u32 sel)

作用:是给上层反馈当前屏的状态。

返回值:如果屏正常的话就返回0,不正常的话就返回非0。

sel:显示索引。

由于屏的类型接口众多,不同屏检测屏的状态各异,一般来说是通过驱动接口读取屏的内部信息(id 或者其它寄存器),如果获取正常则认为屏是正常的,获取失

败则认为屏是异常的。比如下面dsi 屏的做法:

Linux

图5-11: ESD 屏驱动函数实现

此外,一般情况下,也会通过dsi 接口读取0x0A 命令(获取power 模式)来判断屏是否正常。

sunxi_lcd_dsi_dcs_read(sel, 0x0A, result, &num)

Linux

图5-12: ESD MIPI 状态寄存器

reset_panel 函数原型:

s32 reset_panel(u32 sel)

作用:当屏幕异常的时候所需要的复位操作。

返回值:复位成功就是0,复位失败非0。

sel:显示索引。

每个屏的初始化都不同,顺序步骤都不一样,总的来说就是执行部分或者完整的屏驱动里面的close_flow 和open_flow 所定义的回调函数。根据实际情况灵活编写

这个函数。

值得注意的是:某些dsi 屏中,需要至少执行过一次sunxi_lcd_dsi_clk_disable(dsi 高速时钟禁止)和sunxi_lcd_dsi_clk_enable(高速时钟使能),否则可能导致

dsi 的读函数异常。

下图是复位函数示例:

Linux

图5-13: ESD 复位函数1

set_esd_info 函数原型:

s32 set_esd_info(struct disp_lcd_esd_info *p_info)

作用:控制esd 检测的具体行为。比如间隔多长时间检测一次,复位的级别,以及检测函数被调用的位置。

返回值:成功设置返回0,否则非0。

p_info:需要设置的esd 行为结构体。

示例:下面图所示,每隔60 次显示中断检测一次(调用esd_check 函数,如果显示帧率是60fps 的话,那么就是1 秒一次),然后将在显示中断处理函数里面执行

检测函数,由esd_check_func_pos 成员决定调用esd_check 函数的位置,如果是0 则在中断之外执行检测函数,之所以有这个选项是因为显示中断资源(中断处

理时间)是非常珍贵的资源,关系到显示帧率的问题。下图中的level 为1 表示复位全志SoC 的LCD 相关模块以及reset_panel 里面的操作,level 为0 的时候表示

仅仅执行reset_panel 里面的操作。

Linux

图5-14: ESD 设置信息函数

可以通过cat /sys/class/disp/disp/attr/sys 获取当前的esd info。

screen 0: de_rate 594000000 hz, ref_fps:60 mgr0: 2560x1600 fmt[rgb] cs[0x204] range[full] eotf[0x4] bits[8bits] unblank err[0] force_sync[0] dmabuf: cache[0] cache max[0] umap skip[0] overflow[0] capture: dis req[0] runing[0] done[0,0] lcd output(enable) backlight( 50) fps:60.9 esd level(1) freq(300) pos(1) reset(244) 2560x1600 err:0 skip:0 skip T.O:50 irq:73424 vsync:0 vsync_skip:0 BUF en ch[1] lyr[0] z[0] prem[N] fbd[N] a[globl 255] fmt[ 0] fb [2560,1600;2560,1600;2560,1600] crop[ 0, 0,2560,1600] frame[ 0, 0,2560,1600] addr[98100000,00000000,00000000] right[00000000,00000000,00000000] flags[0x00] trd[0,0] depth[ 0] acquire: 0, 25.5 fps release: 0, 25.5 fps display: 0, 25.5 fps

esd level(1) freq(300) pos(1) reset(244)

esd levele 和freq 和pos 的意思请看上面set_esd_info 函数原型的解释。

Reset 后面的数字表示屏复位的次数(也就是esd 导致屏挂掉之后,并且成功检测到并复位的次数)。

此功能可能遇到的问题。

打静电挂了,但是读出来的值仍然是正确的,此问题无解。

Dsi 读操作卡住了,卡在中断里面了。此问题可能和DSI 的lp 模式下的速率有关系,而lp 的速率又和dclk 的频率有关系。此时可以尝试修改de_dsi_28.c() 文件

中的dsi_basic_cfg 函数,如下图所示红框所示的寄存器值,这个寄存器是Lp 模式时钟分频值,一般来说这个值越小,lp 速率越快,尝试改小看是否还会卡

住。

Linux

图5-15: Lp 模式时钟分频值

6 调试方法

系统起来之后可以读取sysfs 一些信息,来协助调试。

6.1 加快调试速度的方法

很明显,如果你在安卓上调试LCD 屏会比较不方便,安卓编译时间和安卓固件都太过巨大,每次修改内核后,可能都要经过10 几分钟都才能验证,这样效率就太

低下了,可用采用如下方法:

使用linux 固件而不是安卓固件。SDK 是支持仅仅编译linux 固件,一般是配置lichee 或者longan 的时候选择linux,打包的时候,用lichee 或者longan 根目录

下的build.sh 来打包就行。因为linux 内核小得多,编译更快,更方便调试。

使用内核来调试LCD 屏。我们知道Uboot 和内核都需要添加LCD 驱动,这样才能快速显示logo,但是uboot 并不方便调试,所以有时候我们需要把uboot 的

显示驱动关掉,专心调试内核的LCD 驱动,调好之后才移植到uboot,另外这样做的一个优点是,我可以非常方便的修改lcd timing 而不需要重烧固件。就是

利用uboot 命令的fdt 命令修改device tree。 比如说:

  fdt set lcd0 lcd_hbp <40>

更多命令见fdt help。

如何关闭uboot 显示呢,一般是在uboot 源码路径下inlcude/configs/平台.h 中,注释掉CONFIG_SUNXI_MODULE_DISPLAY 即可,如果是uboot 2018 则是注释

掉configs/平台_defconfig 中CONFIG_DISP2_SUNXI。

6.2 查看显示信息

以下信息是所有信息中最重要的。

cat /sys/class/disp/disp/attr/sys screen 0: de_rate 297000000 hz, ref_fps:60 mgr0: 1280x800 fmt[rgb] cs[0x204] range[full] eotf[0x4] bits[8bits] err[0] force_sync[0] unblank direct_show[false] lcd output backlight( 50) fps:60.9 1280x 800 err:0 skip:31 irq:1942 vsync:0 vsync_skip:0 BUF enable ch[1] lyr[0] z[0] prem[N] a[globl 255] fmt[ 8] fb[1280, 800;1280, 800;1280, 800] crop[ 0, 0,1280, 800] frame[ 0, 0,1280, 800] addr[ 0, 0, 0] flags[0x 0] trd[0,0]

lcd output

表示当前显示接口是LCD 输出。

1280x800

表示当前LCD 的分辨率,与board.dts 中lcd0 的设置一样。

ref_fps:60

是根据你在board.dts的lcd0填的时序算出来的理论值。

fps:60.9

后面的数值是实时统计的,正常来说应该是在60(期望的fps) 附近,如果差太多则不正常,重新检查屏时序,和在屏驱动的初始化序列是否有被调用到。

irq:1942

这是vsync 中断的次数,每加1 都代表刷新了一帧,正常来说是一秒60(期望的fps)次,重复cat sys,如果无变化,则异常。

BUF

开头的表示图层信息,一行BUF 表示一个图层,如果一个BUF 都没有出现,那么将是黑屏,不过和屏驱动本身关系就不大了,应该查看应用层& 框架层。

err:0

这个表示缺数,如果数字很大且一直变化,屏幕会花甚至全黑,全红等。

skip:31

这个表示跳帧的数量,如果这个数值很大且一直变化,有可能卡顿,如果数字与irq 后面的数字一样,说明每一帧都跳,会黑屏(有背光)。

6.3 查看电源信息

查看axp 某一路电源是否有enable 可以通过下面命令查看。当然这个只是软件的,实际还是用万用表量为准。

cat /sys/class/regulator/dump pmu1736_ldoio2 : disabled 0 700000 supply_name: pmu1736_ldoio1 : disabled 0 700000 supply_name: pmu1736_dc1sw : enabled 1 3300000 supply_name: vcc-lcd pmu1736_cpus : enabled 0 900000 supply_name: pmu1736_cldo4 : disabled 0 700000 supply_name: pmu1736_cldo3 : disabled 0 700000 supply_name: pmu1736_cldo2 : enabled 1 3300000 supply_name: vcc-pf pmu1736_cldo1 : disabled 0 700000 supply_name: pmu1736_bldo5 : enabled 2 1800000 supply_name: vcc-cpvin vcc-pc pmu1736_bldo4 : disabled 0 700000 supply_name: pmu1736_bldo3 : disabled 0 700000 supply_name: pmu1736_bldo2 : disabled 0 700000 supply_name: pmu1736_bldo1 : disabled 0 700000 supply_name: pmu1736_aldo5 : enabled 0 2500000 supply_name: pmu1736_aldo4 : enabled 0 3300000 supply_name: pmu1736_aldo3 : enabled 1 1800000 supply_name: avcc pmu1736_aldo2 : enabled 0 1800000 supply_name: pmu1736_aldo1 : disabled 0 700000 supply_name: pmu1736_rtc : enabled 0 1800000 supply_name: pmu1736_dcdc6 : disabled 0 500000 supply_name: pmu1736_dcdc5 : enabled 0 1480000 supply_name: pmu1736_dcdc4 : enabled 1 900000 supply_name: vdd-sys pmu1736_dcdc3 : enabled 0 900000 supply_name: pmu1736_dcdc2 : enabled 0 1160000 supply_name: pmu1736_dcdc1 : enabled 4 3300000 supply_name: vcc-emmc vcc-io vcc-io vcc-io

6.4 查看pwm 信息

pwm 的用处这里是提供背光电源。

cat /sys/kernel/debug/pwm platform/7020c00.s_pwm, 1 PWM device pwm-0 ((null) ): period: 0 ns duty: 0 ns polarity: normal platform/300a000.pwm, 2 PWM devices pwm-0 (lcd ): requested enabled period: 20000 ns duty: 3984 ns polarity: normal pwm-1 ((null) ): period: 0 ns duty: 0 ns polarity: normal

上面的“requested enabled” 表示请求并且使能了,括号里面的lcd 表示是由lcd 申请的。

6.5 查看管脚信息

cat /sys/kernel/debug/pinctrl/pio/pinmux-pins pin 227 (PH3): twi1 (GPIO UNCLAIMED) function io_disabled group PH3 pin 228 (PH4): (MUX UNCLAIMED) (GPIO UNCLAIMED) pin 229 (PH5): (MUX UNCLAIMED) pio:229 pin 230 (PH6): (MUX UNCLAIMED) pio:230 pin 231 (PH7): (MUX UNCLAIMED) pio:231

上面的信息我们知道PH5,PH6 这些IO 被申请为普通GPIO 功能,而PH3 被申请为twi1。

6.6 查看时钟信息

cat /sys/kernel/debug/clk/clk_summary

这个命令可以看哪个时钟是否使能,然后频率是多少。

与显示相关的是tcon,pll_video,mipi 等等。

cat /sys/kernel/debug/clk/clk_summary | grep tcon cat /sys/kernel/debug/clk/clk_summary | grep pll_video cat /sys/kernel/debug/clk/clk_summary | grep mipi

6.7 查看接口自带colorbar

显示是一整条链路,中间任何一个环节出错,最终的表现都是显示异常,图像显示异常几个可能

原因:

图像本身异常。

图像经过DE(Display Engine)后异常。

图像经过接口模块后异常。这是我们关注的点。

有一个简单的方法可以初步判断,接口模块(tcon 和dsi 等)可以自己输出内置的一些patten(比如说彩条、灰阶图、棋盘图等),当接口输出这些内置patten

的时候,如果这时候显示就异常,这说明了:

LCD 的驱动或者配置有问题。

LCD 屏由于外部环境导致显示异常。

显示自带patten 的方式:

在linux-4.9 及其以上版本的内核,disp 的sysfs 中有一个attr 可以直接操作显示:

echo X > /sys/class/disp/disp/attr/colorbar

上面的操作是显示colorbar,其中的X 可以是0 到8,对应的含义如下图所示:

Linux

图6-1: colorbar

如果有多个显示设备,想让第二个显示设备显示colorbar 的话,那么先:

echo 1 > /sys/class/disp/disp/attr/disp

然后再执行上面操作。

如果没有这个attr 的话,可以直接操作寄存器,也就是操作tcon 寄存器的040 偏移的最低3位。

在linux 下,cd /sys/class/sunxi_dump 然后:

echo 0x06511040 > dump;cat dump

这样会打印当前tcon 的040 偏移寄存器的值,然后在上面值的基础上修改最低3 位为上图的值即可,修改方式示例:

echo 0x06511040 0x800001f1 > write

注意tcon 的基地址不一定是0x06511000,不同平台不一样,请参考SoC 文档获取tcon 的基地址。

6.8 DE 截屏

显示出现异常的时候,有可能是下面三个原因:

SoC 端屏接口模块+LCD 屏出现了问题。

图像经过SoC 端图像合成模块(DE)处理后出现了问题。

图像源本身就有问题。 本节介绍,确认图像源本身没问题的前提下,如何进一步确认是否经过DE 处理之后图像是否有问题。

语法:

echo 屏幕索引> /sys/class/disp/disp/attr/disp echo 路径/bmp文件名> /sys/class/disp/disp/attr/capture_dump

第一个echo 的作用是确定捕捉哪个显示的,“屏幕索引” 可选取值是0 或者1。

第二个echo 作用是生成截屏bmp 文件,确保“路径” 是可写的,有剩余空间的。

比如:

echo 0 > /sys/class/disp/disp/attr/disp echo /data/xx.bmp > /sys/class/disp/disp/attr/capture_dump

这样就会在/data 目录下生成screen 0 的bmp 截图,文件名是xx.bmp。

除了bmp 之外,还支持保存raw 数据,包括RGB 和YUV 颜色空间,它们以后缀来区分,用法

如下:

# 截取yuv420p颜色空间的raw数据。 echo /data/xx.yuv420_p > /sys/class/disp/disp/attr/capture_dump # 截取yuv420_sp_uvuv颜色空间的raw数据。 echo /data/xx.yuv420_sp_uvuv > /sys/class/disp/disp/attr/capture_dump # 截取yuv420_sp_vuvu颜色空间的raw数据。 echo /data/xx.yuv420_sp_vuvu > /sys/class/disp/disp/attr/capture_dump # 截取yuv420_sp_vuvu颜色空间的raw数据。 echo /data/xx.yuv420_sp_vuvu > /sys/class/disp/disp/attr/capture_dump # 截取argb8888颜色空间的raw数据。 echo /data/xx.argb8888 > /sys/class/disp/disp/attr/capture_dump # 截取abgr8888颜色空间的raw数据。 echo /data/xx.abgr8888 > /sys/class/disp/disp/attr/capture_dump # 截取rgb888颜色空间的raw数据。 echo /data/xx.rgb888 > /sys/class/disp/disp/attr/capture_dump # 截取bgr888颜色空间的raw数据。 echo /data/xx.bgr888 > /sys/class/disp/disp/attr/capture_dump # 截取rgba8888颜色空间的raw数据。 echo /data/xx.rgba8888 > /sys/class/disp/disp/attr/capture_dump # 截取bgra8888颜色空间的raw数据。 echo /data/xx.bgra8888 > /sys/class/disp/disp/attr/capture_dump

注意:这个功能只有linux-4.9 以及后续的内核才支持这个功能。

7 FAQ

7.1 屏显示异常

总结过往经验,绝大部分屏显异常都是由于上下电时序和timing 不合理导致。

请看屏时序参数说明和屏驱动分解。

7.2 黑屏-无背光

问题表现:完全黑屏,背光也没有。

有两种可能:

屏驱动添加失败。驱动没有加载屏驱动,导致背光电源相关函数没有运行到。这个你可以通过调试方法定位下。

pwm 配置和背光电路的问题,pwm 的信息可以看pwm 信息和背光相关参数,另外就是直接测量下硬件测量下相关管脚和电压。

7.3 黑屏-有背光

黑屏但是有背光,可能有多种原因导致,请依次按以下步骤检查:

没送图层。如果应用没有送任何图层那么表现的现象就是黑屏,通过查看显示信息一小节可以确定有没有送图层。如果确定没有图层,可以通过查看接口自带

colorbar,确认屏能否正常显示。

SoC 端的显示接口模块没有供电。SoC 端模块没有供电自然无法传输视频信号到屏上。一般SoC 端模块供电的axp 名字叫做vcc-lcd,vcc-dsi,vcc33-lcd,

vcc18-dsi 等。

复位脚没有复位。如果有复位脚,请确保硬件连接正确,确保复位脚的复位操作有放到屏驱动中。

board.dts 中lcd0 有严重错误。第一个是lcd 的timing 太离谱,请严格按照屏手册中的提示来写!参考屏时序参数说明。第二个就是,接口类型搞错,比如接

的DSI 屏,配置却写成LVDS 的。

屏的初始化命令不对。包括各个步骤先后顺序,延时等,这个时候请找屏厂确认初始化命令。

7.4 闪屏

分为几种:

屏的整体在闪。

这个最大可能是背光电路的电压不稳定,检查电压。

屏部分在闪,而且是概率性。

board.dts 中的时序填写不合理。

屏上由一个矩形区域在闪。

屏极化导致,需要关机放一边再开机则不会。

7.5 条形波纹

有些LCD 屏的像素格式是18bit 色深(RGB666)或16bit 色深(RGB565),建议打开FRM功能,通过dither 的方式弥补色深,使显示达到24bit 色深(RGB888)

的效果。如下图所示,上图是色深为RGB66 的LCD 屏显示,下图是打开dither 后的显示,打开dither 后色彩渐变的地方过度平滑。

设置[lcd0] 的lcd_frm 属性可以改善这种现象。请看lcd_frm解释。

7.6 背光太亮或者太暗

请看背光相关参数。

7.7 重启断电测试屏异常

花屏的第一个原因是fps 过高,超过屏的限制:

FPS 异常是一件非常严重的事情,关系到整个操作系统的稳定,如果fps 过高会造成系统带宽增加,送显流程异常,fps 过高还会造成LCD 屏花屏不稳定,容易造

成LCD 屏损坏,FPS 过低则造成用户体验过差。

通过查看查看显示信息一节,可以得知现在的实时统计的fps。

如果fps 离正常值差很多,首先检查board.dts 中[lcd0] 节点,所填信息必须满足下面公式。

lcd_dclk_freq*num_of_pixel_clk=lcd_ht*lcd_vt*fps /1e9

其中,num_of_pixel_clk 通常为1,表示发送一个像素所需要的时钟周期为1 一个,低分辨率的MCU 和串行接口通常需要2 到3 个时钟周期才能发送完一个像素。

如果上面填写没有错,通过查看查看时钟信息一节可以确认下几个主要时钟的频率信息,把这些信息和board.dts 发给维护者进一步分析。

7.8 RGB 接口或者I8080 接口显示抖动有花纹

改大时钟管脚的管脚驱动能力 参考lcd_gpio_0一小节和pinctrl-0 和pinctrl-1,修改驱动能力,改大。

还有另外一种写法,比如原来是:

  lcdclk = port:PD18<2><0><2>

可以改成:

  lcdclk = port:PD18<2><0><3>

修改时钟相位,也就是修改lcd_hv_clk_phase。由于发送端和接收端时钟相位的不同导致接收端解错若干位。

7.9 LCD 屏出现极化和残影

何谓液晶极化现象:实际上就是液晶电介质极化。就是在外界电场作用下,电介质内部沿电场方向产生感应偶极矩,在电解质表明出现极化电荷的现象叫做电介质

的极化。

通俗的讲就是在液晶面板施加一定电压后,会聚集大量电荷,当电压消失的时候,这些聚集的电荷也要释放,但由于介电效应,这些聚集的电荷不会立刻释放消

失,这些不会马上消失的惰性电荷造成了液晶的DC 残留从而形成了极化现象。

几种常见的液晶极化现象

液晶长期静止某个画面的时候,切换到灰阶画面的时候出现屏闪,屏闪一段时间后消失。这种现象属于残留电荷放电的过程。

液晶长期静止某个画面的时候,出现四周发黑中间发白的现象,业内称为黑白电视框异常。

非法关机的时候,重新上电会出现屏闪,屏闪一定时间后消失。与第一种原因相同。

残影现象:当液晶静止在一个画面比较久的情况下,切换其他画面出现的镜像残留。残影的本质来说是液晶DC 残留电荷导致,某种意义来说也属于液晶极化

现象。

针对液晶屏出现极化和残影现象,有如下对策。

调整vcom 电压大小。 VCOM 是液晶分子偏转的参考电压,要求要稳定,对液晶显示有直接影响,具体的屏不同的话也是不同的。电压的具体值是根据输入的数据以及Vcom 电压大

小来确定的,用来显示各种不同灰阶,也就是实现彩色显示GAMMA。Gamma 电压是用来控制显示器的灰阶的,一般情况下分为G0~G14,不同的Gamma

电压与Vcom 电压之间的压差造成液晶旋转角度不同从而形成亮度的差异,Vcom 电压最好的状况是位于G0 和G14 的中间值,这样液晶屏的闪烁状况会最

好。

调节vcom 电压的方式,如果屏管脚有vcom 管脚,直接调整相关电路,如果屏driver IC 提供寄存器接口,可以通过寄存器接口来调整大小。

严格按照屏规定的上下电时序来对屏进行开关屏。许多极化残影现象并非长时间显示静止显示某个画面导致的,而是由于关机或者关屏时没有严格按照下电时

序导致的,比如该关的电没关,或者延时不够。

8 总结

调试LCD 显示屏实际上就是调试发送端芯片(全志SoC)和接收端芯片(LCD 屏上的driver IC)的一个过程:

添加屏驱动请看添加屏驱动步骤和屏驱动说明。

仔细阅读屏手册以及driver IC 手册。

仔细阅读硬件参数说明。

确保LCD 所需要的各路电源管脚正常。
 

  审核编辑:汤梓红
 
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分