大概一个月前尝试用一块A20板子点LCD的时候,遇到了屏幕在u-boot和启动开始阶段能正常显示,但之后屏幕开始变白的问题。症状类似下图:
同时可以在log上看到这样的输出:
sun4i-drm display-engine: NO panel or bridge found... RGB output disabled
后来一番Google后找到armbian论坛上的一个贴子给出了有效的解决方法:如log所提示的信息,这是设备树中缺少panel节点造成的,需要修改设备树解决。
前些天貌似也有坛友遇到了相同问题发贴提问,回贴中坛友jimmy提到可能是linux 4.14后显示架构改了。我索性写一贴来讲述一下在主线的u-boot、Linux kernel上如何进行并行RGB液晶屏的配置。
最近编辑记录 SdtElectronics (2021-01-18 12:53:36)
离线
首先是u-boot下的配置,修改u-boot代码树的configs/目录下你的板子对应的defconfig文件,添加如下配置:
1,CONFIG_VIDEO_LCD_MODE字段,这是存储液晶屏的分辨率,扫描模式等配置的字段,具体每个键值对的含义和计算方法在Linux-sunxi wiki上的LCD页面上有。下面是一个给480x272的LCD使用的配置样例:
CONFIG_VIDEO_LCD_MODE="x:480,y:272,depth:24,pclk_khz:10000,hs:1,vs:1,le:42,ri:8,up:11,lo:4,sync:3,vmode:0"
2,CONFIG_VIDEO_LCD_BL_EN字段,这是标记驱动液晶屏的使能信号的GPIO的字段,根据你板子的实际情况填写,例如:
CONFIG_VIDEO_LCD_BL_EN="PH7"
3,CONFIG_VIDEO_LCD_BL_PWM字段,这是标记控制背光亮度pwm信号的GPIO的字段,根据你板子的实际情况填写,例如:
CONFIG_VIDEO_LCD_BL_PWM="PB2"
4,CONFIG_VIDEO_LCD_POWER字段,这是标记液晶电源使能信号的GPIO的字段,根据你板子的实际情况填写,例如:
CONFIG_VIDEO_LCD_POWER="PH8"
如果你的设备没有液晶电源使能功能,即液晶电源是常开的,此段可以略去。
5,CONFIG_VIDEO_LCD_DCLK_PHASE字段,指明LCD时钟极性,一般值为0:
CONFIG_VIDEO_LCD_DCLK_PHASE=0
所以为了驱动一块480x272的LCD,我的板子最终需要在defconfig中添加如下配置:
CONFIG_VIDEO_LCD_MODE="x:480,y:272,depth:24,pclk_khz:10000,hs:1,vs:1,le:42,ri:8,up:11,lo:4,sync:3,vmode:0"
CONFIG_VIDEO_LCD_DCLK_PHASE=0
CONFIG_VIDEO_LCD_POWER="PH8"
CONFIG_VIDEO_LCD_BL_EN="PH7"
CONFIG_VIDEO_LCD_BL_PWM="PB2"
离线
然后是修改设备树。首先检查你的SoC对应的dtsi文件,比如我的是A20的SoC的话对应的就是sun7i-a20.dtsi,其中的tcon(一个SoC可能有多个tcon,比如A20有两个,那么设备树里会有tcon0和tcon1两个节点)节点的ports的tcon0_out: port@1属性下有没有tcon0_out_lcd属性,没有的话,添加如下内容:
/ {
...
soc {
...
tcon0: lcd-controller@1c0c000 {
...
ports {
...
tcon0_out: port@1 {
...
//这是要添加的内容
tcon0_out_lcd: endpoint@0 {
reg = <0>;
remote-endpoint = <&lcd_in_tcon0>;
};
//结束
}
}
}
}
}
打省略号的部分只是为了方便大家定位到要添加代码的位置,两段注释中间的代码才是要添加的。之后贴出的代码同理。
添加一个panel节点:
/ {
...
soc {
...
//这是要添加的内容
panel: panel {
#address-cells = <1>;
#size-cells = <0>;
port {
#address-cells = <1>;
#size-cells = <0>;
lcd_in_tcon0: endpoint {
//这里要和上面加的那部分一致
remote-endpoint = <&tcon0_out_lcd>;
};
};
};
//结束
}
}
添加lcd的pins:
/ {
...
soc {
...
pio: pinctrl@1c20800 {
...
//这是要添加的内容
lcd0_rgb888_pins: lcd0-rgb888 {
pins = "PD0", "PD1", "PD2", "PD3",
"PD4", "PD5", "PD6", "PD7",
"PD8", "PD9", "PD10", "PD11",
"PD12", "PD13", "PD14", "PD15",
"PD16", "PD17", "PD18", "PD19",
"PD20", "PD21", "PD22", "PD23",
"PD24", "PD25", "PD26", "PD27";
function = "lcd0";
};
//结束
};
}
}
注意这个要根据你实际使用的SoC的lcd输出使用的GPIO修改,这个例子使用的是A20的lcd0输出。
离线
以上的修改都是对SoC对应的dtsi文件的修改,下面是需要对板子对应的dts做出的修改:
确保在dts文件开头引入了pwm.h:
#include <dt-bindings/pwm/pwm.h>
添加背光控制设备节点:
/ {
...
//这是要添加的内容
backlight: backlight {
compatible = "pwm-backlight";
pwms = <&pwm 0 50000 PWM_POLARITY_INVERTED>;
brightness-levels = <0 10 20 30 40 50 60 70 80 90 100>;
default-brightness-level = <6>;
enable-gpios = <&pio 7 7 GPIO_ACTIVE_HIGH>; /* PH7 */
};
//结束
}
这样你就能在/sys/class/backlight下对背光进行调节。为了方便阅读,这个例子只配置了11个背光级别,你可以根据需要细分更多的背光级别。
如果你的设备支持屏幕电源使能,添加电源使能节点:
/ {
...
//这是要添加的内容
panel_power: panel_power {
compatible = "regulator-fixed";
regulator-name = "panel-power";
regulator-min-microvolt = <10400000>;
regulator-max-microvolt = <10400000>;
//这里要和uboot配置里的CONFIG_VIDEO_LCD_POWER相一致
gpio = <&pio 7 8 GPIO_ACTIVE_LOW>; /* PH08 */
enable-active-low;
regulator-boot-on;
};
//结束
}
配置tcon,display engine,panel设备节点:
//这是要添加的内容
&de {
status = "okay";
};
&tcon0 {
pinctrl-names = "default";
pinctrl-0 = <&lcd0_rgb888_pins>;
status = "okay";
};
&panel {
compatible = "nec,nl4827hc19-05b";
power-supply = <&panel_power>;
backlight = <&backlight>;
};
//结束
重点讲一下panel节点的compatible属性,它标记了该屏幕是Linux kernel源码树下的/drivers/gpu/drm/panel/panel-simple.c中描述的哪个panel。比方说我这里选择了nec_nl4827hc19_05b这个panel,他在panel-simple.c里的描述是这样的:
static const struct drm_display_mode nec_nl4827hc19_05b_mode = {
.clock = 10870,
.hdisplay = 480,
.hsync_start = 480 + 2,
.hsync_end = 480 + 2 + 41,
.htotal = 480 + 2 + 41 + 2,
.vdisplay = 272,
.vsync_start = 272 + 2,
.vsync_end = 272 + 2 + 4,
.vtotal = 272 + 2 + 4 + 2,
.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
};
我们能看出来这是一个480x272的屏幕。我们就根据他的名字,给panel节点设置compatible = "nec,nl4827hc19-05b";。至于选择哪个panel是否重要,还尚待验证,因为有人指出设备树的液晶屏配置在启动时会被u-boot修改成defconfig文件描述的配置。我这里选了panel-simple.c里的一块分辨率和实际使用的屏幕相一致的panel,可以正常驱动。
你可以在我的GitHub仓库里找到我为我的A20板子和480x272的LCD修改好的设备树作为参考。
离线
完成以上修改后,重新编译u-boot然后替换原来的SPL和dtb的二进制。如果一切正常的话,屏幕应该可以正常显示了:
离线
优秀
离线
优秀
离线
我在4楼曾提到
至于选择哪个panel是否重要,还尚待验证,因为有人指出设备树的液晶屏配置在启动时会被u-boot修改成defconfig文件描述的配置。
今天在一块800x480上的屏幕上实测后,发现在设备树里选择配置和实际屏幕相符的panel型号是必须的,也就是说,新版本内核上u-boot的defconfig和设备树对于配置屏幕而言是各司其职的,二者都要被正确配置才能使boot和系统正常显示。
离线
我在4楼曾提到
至于选择哪个panel是否重要,还尚待验证,因为有人指出设备树的液晶屏配置在启动时会被u-boot修改成defconfig文件描述的配置。
今天在一块800x480上的屏幕上实测后,发现在设备树里选择配置和实际屏幕相符的panel型号是必须的,也就是说,新版本内核上u-boot的defconfig和设备树对于配置屏幕而言是各司其职的,二者都要被正确配置才能使boot和系统正常显示。
如果kernel用simplefb驱动, 那么就是用uboot初始化好的.
如果kernel用drm驱动, 那么是kernel驱动的, 和uboot没关系.
离线
@九重天
请问下 如果使用的是/driver/staging/fbtfb下面的驱动,uboot需要在初始化吗?
离线
最近一直在研究这个,不过是spi+rgb的,学习了,谢谢大佬
离线
可以选50pin的屏幕,高分辨率和低分辨率都比较好支持~
离线
按照修改还是不行呢。。。。
离线