1.947驱动是根据parade-ps8622.c改编的,电源由于pmic的ldo无法输出1.8v电源,改飞线。
vendor\nxp-opensource\kernel_imx\drivers\gpu\drm\imx\nwl_dsi-imx.c
kernel去掉CONFIG_DRM_IMX_NWL_DSI=y能正常启动,但是没有bind bridge,没有以下动作:
&ldb1 {
status = "okay";
lvds-channel@0 {
fsl,data-mapping = "jeida";
fsl,data-width = <24>;
status = "okay";
port@1 {
reg = <1>;
lvds0_out: endpoint {
remote-endpoint = <&ds90ub947_0_in>;
};
};
};
};
&i2c1_lvds0 {
#address-cells = <1>;
#size-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lvds0_lpi2c1>;
clock-frequency = <400000>;
status = "okay";
lvds-pannel@1a {
compatible = "ti,ds90ub947";
reg = <0x1a>;
#address-cells = <0x1>;
#size-cells = <0x0>;
#reset-cells = <0x1>;
dual-pixel-mode;
port {
ds90ub947_0_in: endpoint {
clock-lanes = <3>;
data-lanes = <0 1 2 4>;
remote-endpoint = <&lvds0_out>;
};
};
display-timings {
1920X1080P@60 {
clock-frequency = <72800000>;
hactive = <1920>;
vactive = <1080>;
hfront-porch = <0>;
hback-porch = <120>;
hsync-len = <0>;
vback-porch = <44>;
vfront-porch = <0>;
vsync-len = <0>;
hsync-active = <1>;
vsync-active = <0>;
};
};
};
imx-drm display-subsystem: bound ldb@562410e0 (ops imx_ldb_ops)
但是通过对比发现frame buffer device生成。
kernel_imx/drivers/gpu/drm/drm_fb_helper.c 下手动添加显示参数信息:
drm_fb_helper_single_fb_probe ->
+ crtc_count =1;
+ sizes.surface_width=1900;
+ sizes.surface_height=1060;
+ sizes.fb_width=1920;
+ sizes.fb_height=1080;
[ 2.250241] Console: switching to colour frame buffer device 240x67
[ 2.279556] imx-drm display-subsystem: fb0: frame buffer device
还是没有输出,发现是0x4F寄存器配置的是双路的lvds屏驱动,顺便把12.3寸屏给调了。
lvds0_panel {
compatible = "lvds0_1080p";
# dual-pixel-mode; //LVDS信号pixel-clock为双倍,但是该属性在kernel中找不到了,而且在947中也已经配置好了,实验证明可以去掉。
port@0 {
ds90ub947_0_in: endpoint {
remote-endpoint = <&lvds0_out>;
};
};
};
lvds1_panel {
compatible = "lvds1_720p";
# dual-pixel-mode;
port@0 {
ds90ub947_1_in: endpoint {
remote-endpoint = <&lvds1_out>;
};
};
};
&ldb1_phy {
status = "okay";
};
&ldb1 {
status = "okay";
fsl,dual-channel;
lvds-channel@0 {
fsl,data-mapping = "spwg"; //和947配置相对应格式,不然会出现花屏现象
fsl,data-width = <24>;
status = "okay";
port@1 {
reg = <1>;
lvds0_out: endpoint {
remote-endpoint = <&ds90ub947_0_in>;
};
};
};
};
&ldb2_phy {
status = "okay";
};
&ldb2 {
status = "okay"
fsl,dual-channel;
lvds-channel@0 {
fsl,data-mapping = "spwg";//这描述了如何在序列化的LVDS信号中布局颜色位。
fsl,data-width = <24>;
status = "okay";
port@1 {
reg = <1>;
lvds1_out: endpoint {
remote-endpoint = <&ds90ub947_1_in>;
};
};
};
};
there are two LVDS channels(LVDS0 and LVDS1) which can transfer video * datas, there two channels can be used as split/dual/single/separate mode. * * split mode means display data from DI0 or DI1 will send to both channels * LVDS0+LVDS1. * dual mode means display data from DI0 or DI1 will be duplicated on LVDS0 * and LVDS1, it said, LVDS0 and LVDS1 has the same content. * single mode means only work for DI0/DI1->LVDS0 or DI0/DI1->LVDS1. * separate mode means you can make DI0/DI1->LVDS0 and DI0/DI1->LVDS1 work * at the same time.
根据如上说明,uboot参数中可以配置ldb的这四种模式,并且在设备树中,对于单通道和双通道也有以下配置:
- split-mode: Provide this bool property if your board uses LDB split
mode to drive a high resolution display, say 1080P@60. In this
mode, two LVDS channels will drive one display
(如果您的电路板使用LDB split模式来驱动高分辨率显示器,例如1080P@60,则提供此bool属性
。在这种模式下,两个LVDS通道将驱动一个显示). -->fsl,dual-channel;
- dual-mode: Provide this bool property if your board uses LDB dual
mode to drive two displays. In this mode, one display engine will
drive two displays which have the same timings and display content.
如果板使用LDB dual-mode驱动两个显示器,则提供此bool属性。在这种模式下,一个显示引擎将驱动两个具有相同clock和显示内容的显示。
并且clock-frequency为datasheet中DCLK×2
根据上图,我们lvds采用CH0和CH1双通道,需要配置947的0x4f寄存器为Dual-pixel-mode,则 the
pixel frequency is 2x the OLDI frequency,也就是说pixel clock需要配置成双倍来配合lvds的split mode。
fsl,dual-channel;对应imx-ldb.c如下:
fsl,data-mapping = "spwg"对应VESA格式如下:
HSD156JUW1-A00 Preliminary Specification 2.0 for Customer.pdf:
对于pannel来说,需要一些时序型号与RGB信号配合显示,时序告知pannel,这些颜色显示在pannel的什么物理位子。这些时序叫做TCON. 根据pannel自带的drive ic。所需要的TCON信号不同。
DE 模式一般需要:DE和clock信号来确定点。比如一个800x480分辨率的pannel。理论上,在DE有效信号的时候(高或底),就有一个800个clock,来确认行中800个点。每个clock有效的时候,读取一次RGB信号。因为存在(回扫信号)所以DE是个方波,当视频在会扫的时候,DE就拉底。DE一个周期,pannel就扫描一行。扫描480行后,又从第一行扫描开始。(这个规律由pannel的驱动IC所决定的)。
HV模式,需要行同步,和场同步。来表示扫描的行于列。提供多种接口,有利于视频信号的兼容。
名称 | 在数据手册中的简称 | 中文名 | 意义 | 备注 |
name | No | 名字 | 液晶屏名字(可选) | No |
refresh | No | 刷新频率 | 刷新频率(内核中很多例子都赋值为60) | No |
xres | No | 行宽 | 每行的像素个数 | No |
yres | No | 屏幕高度 | 屏幕的行数 | No |
pixclock | No | 像素时钟 | 每个像素时钟周期的长度,单位是皮秒(10的负12次方分之1秒) | No |
left_margin | HBP (Horizontal Back Porch) | 水平后沿 | 在每行或每列的象素数据开始输出时要插入的象 素时钟周期数 | No |
right_margin | HFP (Horizontal Front Porch ) | 水平前沿 | 在每行或每列的象素结束到LCD 行时钟输出脉冲 之间的象素时钟数 | No |
upper_margin | VBP (Vertical Back Porch) | 垂直后沿 | 在垂直同步周期之后帧开头时的无效行数 | No |
lower_margin | VFP (Vertical Front Porch) | 垂直前沿 | 本帧数据输出结束到下一帧垂直同步周期开始之 前的无效行数 | No |
hsync_len | HPW (HSYNC plus width) | 行同步脉宽 | 单位:像素时钟周期 | 也有手册简称为HWH(HSYNC width) |
vsync_len | VPW (VSYNC width) | 垂直同步脉宽 | 单位:显示一行的时间th | 也有手册简称为VWH(VSYNC width) |
sync | No | 同步极性设置 | 可以根据需要设置FB_SYNC_HOR_HIGH_ACT(水平同步高电平有效)和FB_SYNC_VERT_HIGH_ACT(垂直同步高电平有效) | No |
vmode | No | No | 在内核中的大多数示例都直接置为FB_VMODE_NONINTERLACED。interlaced的意思是交错[隔行]扫描,电视中使用2:1的交错率, 即每帧分两场,垂直扫描两次,一场扫描奇数行,另一场扫描偶数行。很显然LCD目前不是这种模式。 | No |
flag | No | No | 目前没有看到用法 | No |
VSYNC: 帧同步信号,表示扫描1帧的开始,一帧也就是LCD显示的一个画面。
VFPD: 帧同步信号的前肩,单位为1行(Line)的时间。
VBPD: 帧同步信号的后肩,单位为1行(Line)的时间。
LINEVAL :帧显示尺寸-1,即屏行宽-1,对于800*480分配率的LCD屏,那么LINEVAL=480-1=479,请记住,是屏行宽,也就是LCD屏显示一帧数据所需要的行的数目。
HOZVAL:行显示尺寸-1,即屏列宽-1,对于800*480分配率的LCD屏,那么HOZVAL=800-1=799,请记住,是屏列宽,也就是LCD屏显示一行数据所需要的像素(pixel)的数目。
上图是一帧图像的显示时序图。的上图显示,up_margin = 13-1=12,, yres= 240,
整个场周期为263,所以lower_margin= 263-13-240 = 10
可以看到,left_margin = 69, xres = 320, right_margin = 408 -320 - 70 = 18
具体参考:https://blog.csdn.net/a617996505/article/details/82386952#
- 水平总周期t4 = t5 + t6
- 垂直总周期 t1 = t2 + t3
- t7为时钟周期,因为是大屏需要双通道驱动,配置947的0x4f寄存器为Dual-pixel-mode,所以pixelclock * 2
- DE为高电平有效。(DISPLAY_FLAGS_DE_HIGH)
- 该DE,模式没有行同步和场同步,所以都设置为1。
- 一行扫描两个像素点,所以hactive=t5 * 2
static const struct display_timing lvds0_adan_timing = {
.pixelclock = { 132000000, 145600000, 161000000 },
.hactive = { 1920, 1920, 1920 },
.hfront_porch = { 90, 120, 150 },
.hback_porch = { 1, 1, 1 },
.hsync_len = { 1, 1, 1 },
.vactive = { 1080, 1080, 1080 },
.vfront_porch = { 5, 44, 88 },
.vback_porch = { 1, 1, 1 },
.vsync_len = { 1, 1, 1 },
.flags = DISPLAY_FLAGS_DE_HIGH,
};
static const struct panel_desc lvds0_adan = {
.timings = &lvds0_adan_timing,
.num_timings = 1,
.bpc = 8,
.size = {
.width = 158,
.height = 211,
},
.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
};
{
.compatible = "lvds0_1080p",
.data = &lvds0_adan ,
},{
所以一个cycle是4行,每行有7个数据,一共28个bit,其中RGB888占用24个,DE VS HS --占用4个,所以bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG。
1.大屏一般都是lvds双通道驱动,扫描模式为DE模式,947需要设置为dual-pixel-mode,时钟频率为双倍驱动。
调试屏幕首先要把背光点亮,没有点亮背光屏幕什么都显示不了,根据规格书将背光点亮,可以寻求硬件工程师帮忙
在调试的时候,我们会发现屏幕上有莫名其妙的星星点点,一般这种情况下,我们首先要看看VGH和VGL电压是否处于datasheet所描述的范围之 内。如果属于标准范围之内,但星星点点依旧,很有可能就是时序问题。这时候不妨在代码中变更采样的时序(比如上升沿采样改为下降沿采样)。如果无法在代码 中更改,也可以在clk信号线加个100R电阻,也可能解决该问题。
在确认VPW,VBP,VFP,HPW,HBP,HFP的设置已经符合LCD规格要求后,如果屏幕的显示还在抖动的话,不妨将输出的时钟信号频率降低,有可能解决该问题。
还存在一种现象,LVDS的信号线电压高出规格书一点也会出现。曾经有过LVDS信号电压为3.3V的接到5V导致屏幕显示出现细小电波抖动。
出现花屏现象,理论上是RGB没有调好导致的。如颜色位数(24),格式标准(VESA和JEIDA)。
检查一下规格书的颜色格式部分, 是否将RGB888格式设置成了666,或者顺序是RBG,BGR之类的都有可能。
3.中断引脚配置(0xc6固定配置为0x21 0xc7清除中断)
GPIO[3:0]都是可以作为透传功能的GPIO,且可以配置方向,如947 GPIO0输入,948 GPIO0输出,则配置为0x0D[947]=0x03,0x1D[948]=0x05。
以上GPIO_REG[8:5]是仅用于寄存器配置的GPIOs,可以输出进行编程,也可以仅通过本地寄存器位作为输入进行读取。在适用的情况下,如果启用
GPIO_REG模式,这些位与I2S引脚共享,并将覆盖I2S输入。GPIO的启用和配置见表。
注意:本地GPIO值可以通过本地寄存器访问进行配置和读取,也可以通过双向控制通道进行远程寄存器访问。这些引脚的配置和状态不会像GPIO[3:0]那样从串行器传输到解串器。
0xc6固定配置成0x21,如果选用947端INTB脚作为中断输出,则需要低电平有效时对0xc7寄存器读一次进行清除中断动作,否则将被一直拉低。
上图可知,选用REM_INTB引脚作为中断,则不需要进行清除中断标志动作。
主要由947端配置寄存器0x03 [DA]、0x17 [DE]、0x4f [00],如下:
0X03主要是i2c的透传读写功能打开[bit3],0x17主要配置i2c远端地址读取功能[bit7],0x4f主要配置格式为OpenLDI Mapping。
Open LVDS Display Interface(OpenLDI)
LVDS接口电路中,将像素的并行数据转换为串行数据的格式主要有两种标准:VESA和JEIDA,此模式的意思是lvds格式根据arm输出的格式不做改变,直通到948端进行输出,
而SPWG mapping则表示会经过一次转换,如arm端输入VESA格式,则948端是JEIDA格式,会倒转一下。
基本也就这几个需要配置的,目前9478的驱动是不需要bind drm_bridge的,就是普通的I2C驱动放在bridge下面就好了。
err = ds90ub947_write_checkout(ds90ub947, 0x03, 0xDA);
err = ds90ub947_write_checkout(ds90ub947, 0x17, 0xDE);
err = ds90ub947_write_checkout(ds90ub947, 0x4F, 0x00); // OpenLDI mapping Dual-pixel mode.
err = ds90ub947_write_checkout(ds90ub947, 0xC6, 0x21); //INI
err = ds90ub947_write_checkout(ds90ub947, 0x11, 0x03);//GPIO7(TP_RST) 011: GPIO mode, input.
err = ds90ub947_write_checkout(ds90ub947, 0x10, 0x30); //GPIO6(BL_ENABLE) 011: GPIO mode, input.
err = ds90ub947_write_checkout(ds90ub947, 0x0E, 0x03); //GPIO1(PWM) 011: GPIO mode, input.
err = ds90ub947_write_checkout(ds90ub947, 0x0D, 0x03); //GPIO0 (TP_RST) 011: GPIO mode, input.
err = ds90ub948_write_checkout(ds90ub948, 0x20, 0x90); //BL_ENABLE GPIO6
err = ds90ub948_write_checkout(ds90ub948, 0x1E, 0x95);
err = ds90ub948_write_checkout(ds90ub948, 0x60, 0x40); //GPIO output=3.3v
err = ds90ub948_write_checkout(ds90ub948, 0x1D, 0x05); //TP_RST(GPIO0) 101: Remote GPIO Control, GPIO mode; output.
err = ds90ub948_write_checkout(ds90ub948, 0x1D, 0x05); //TP_RST(GPIO0) 101: Remote GPIO Control, GPIO mode; output.
err = ds90ub948_write_checkout(ds90ub948, 0x1D, 0x05); //TP_RST(GPIO0) 101: Remote GPIO Control, GPIO mode; output.
err = ds90ub948_write_checkout(ds90ub948, 0x1F, 0x09); //LCD0_STBY(GPIO3) default:high
msleep(380);
err = ds90ub948_write_checkout(ds90ub948, 0x1E, 0x95); //PWM(GPIO1) TP_EN(GPIO2)
err = ds90ub948_write_checkout(ds90ub948, 0x1E, 0x95); //PWM(GPIO1) TP_EN(GPIO2)
err = ds90ub948_write_checkout(ds90ub948, 0x1E, 0x95); //PWM(GPIO1) TP_EN(GPIO2)
* panel_simple_init(void)
* err = platform_driver_register(&panel_simple_platform_driver);
* __platform_driver_register(drv, THIS_MODULE)
* drv->driver.bus = &platform_bus_type;
* struct bus_type platform_bus_type
* .match = platform_match
* if (of_driver_match_device(dev, drv))
* return of_match_device(drv->of_match_table, dev) != NULL;
* return of_match_node(matches, dev->of_node);
* match = __of_match_node(matches, node);
* score = __of_device_is_compatible(node, matches->compatible, matches->type, matches->name);
* if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
* /drivers/gpu/drm/imx/imx-drm-core.c
* module_platform_driver(imx_drm_pdrv);
* .probe = imx_drm_platform_probe,
* static int imx_drm_platform_probe(struct platform_device *pdev)
* ret = drm_of_component_probe_with_match(&pdev->dev, match, compare_of, &imx_drm_ops);
* .bind = imx_drm_bind,
* ret = component_bind_all(dev, drm);
* ret = component_bind(c, master, data);
* imx_ldb_bind
* drm_of_find_panel_or_bridge
* imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth, MAX_CRTC);
* return drm_fbdev_cma_init_with_funcs(dev, preferred_bpp, max_conn_count, &drm_fb_cma_funcs);
* ret = drm_fb_helper_initial_config(helper, preferred_bpp);
* ret = __drm_fb_helper_initial_config_and_unlock(fb_helper, bpp_sel);
* ret = register_framebuffer(info);