上一期介绍了一下tinkerboard2 Android11下面的bootloader,这一期来介绍一下tinkerboard2在Android11下面适配DSI屏幕的方式。
硬件准备
首先介绍一下硬件。主板是华硕tinkerboard2。之前的文章已经介绍过它的硬件资源,它支持三种显示接口,包括HDMI DP DSI,可以选择其中两个接口,接两个显示屏。
电源要用12V 2A的,不然带不动屏幕,屏幕电源要由主板提供,且电源质量要比较好,不然会干扰屏幕显示。
屏幕为7寸,分辨率1024*600,如下图
屏幕出来的原始接口是不能接到主板上面的,这里要感谢深圳风火轮,他们作为华硕tinkerboard官方合作伙伴,推出了这款屏幕转接板,将屏幕的接口转换为tinkerboard2上面的DSI接口,同时引出了供电,触摸等引脚。屏幕转接板如下图。
然后需要接线,在上图中已经标明了转接板上面的接口,其中
触摸接口和屏幕接口接7寸屏的触摸排线和显示排线
Tinker board 2 mipi interface接tinker2的mipi接口,转接板附送了排线
上方排针的3.3V为屏幕逻辑板(TCON)的电源,接开发板40pin 接口的pin1
GND接pin6或者pin9都可以
BLED为背光使能,接pin17
LCD_EN为逻辑板(TCON)的使能脚,低电平有效,接pin30
如果只用到显示功能,接上面四个pin即可,另外还有触摸相关的IO,下一篇文章会介绍
Kernel部分修改
由于RK3399支持双屏显示,下面以HDMI DSI为例介绍kernel部分的修改。
目前Android全部采用的都是Linux DRM框架进行显示,在DRM框架中,其显示通路如下图所示
图中的几个组成部分
Framebuffer:显存,嵌入式系统使用的是内存的一部分
CRTC:显示控制器,在RK3399平台是SOC 内部VOP,RK3399里面包含两个VOP;
Encoder:输出转换器,指RGB、LVDS、DSI、eDP、HDMI、CVBS、VGA 等显示接口,它本质就是一个编码器,将CRTC提供过来的信号编码为对应显示接口需要的信号。
Connector:连接器,指encoder 和panel 之间交互的接口部分;
Panel:各种具体的屏幕
因此,要驱动DSI屏幕,有三个部分需要配置,包括VOP,DSI控制器,屏幕的参数。
RK3399支持两个VOP,其支持的情况如下图
一般选择VOP和具体显示接口的对应关系时,会考虑性能问题,DP HDMI这些会使用VOPB,这样可以驱动更大分辨率的屏,市面上一般的MIPI屏分辨率不会大于1080P,用VOPB没有意义,采用VOPL足够,因此配置的时候,VOPB绑定HDMI,VOPL绑定MIPI
找到设备树kernel/arch/arm64/boot/dts/rockchip/rk3399-tinker-board-2.dtsi
首先修改VOP的配置,
代码语言:javascript复制&vopb {
//打开vopb的功能
status = "okay";
//指定vopb的时钟,接HDMI的那个VOP clock-parents必须指定为PLL_VPLL
assigned-clocks = <&cru DCLK_VOP0_DIV>;
assigned-clock-parents = <&cru PLL_VPLL>;
support-multi-area;
};
//启用mmu
&vopb_mmu {
status = "okay";
};
&vopl {
//打开vopl的功能
status = "okay";
//指定vopl的时钟,不接HDMI的VOP clock-parents用PLL_CPLL
assigned-clocks = <&cru DCLK_VOP1_DIV>;
assigned-clock-parents = <&cru PLL_CPLL>;
support-multi-area;
};
//启用mmu
&vopl_mmu {
status = "okay";
};
然后修改DSI和panel的配置。
代码语言:javascript复制&dsi {
status = "okay";
//配置dsi每个lane的频率,一般出现花屏,条纹等可以调整这个值改善
//如果这个值不配置,DSI驱动会自动计算
rockchip,lane-rate = <500>;
panel@0 {
//屏幕的具体参数,使用dsi接口的屏幕其属性必须是simple-panel-dsi
compatible = "simple-panel-dsi";
reg = <0>;
//背光,即使不启用背光调节功能,这个属性也必须配置,且backlight的节点必须是有效的
//否则驱动会加载失败
backlight = <&backlight>;
//使能脚,如果enable 接到一个gpio,这个属性必须设置
enable-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
//这些根据规格书填写即可
bpc = <8>;
bus-format = <0x100a>;
width-mm = <476>;
height-mm = <267>;
dsi,flags = <3>;
dsi,format = <0>;
dsi,lanes = <4>;
//屏幕初始化序列,屏厂会提供
panel-init-sequence = [
15 00 02 80 ac
15 00 02 81 b8
15 00 02 82 09
15 00 02 83 78
15 00 02 84 7f
15 00 02 85 bb
15 00 02 86 70
];
display-timings {
native-mode = <&timing2>;
//下面timing的参数按wiki上面的
timing2: timing2 {
clock-frequency = <52000000>; //DCLK
hactive = <1024>; //hactive
vactive = <600>; //vactive
hfront-porch = <160>; //hfp
hback-porch = <160>; //hbp
hsync-len = <10>; //hsa
vfront-porch = <12>; //vfp
vsync-len = <1>; //vsa
vback-porch = <23>; //vbp
hsync-active = <0>; //hync 极性控制 置 1 反转极性
vsync-active = <0>; //vsync 极性控制 置 1 反转极性
de-active = <1>; //DEN 极性控制
pixelclk-active = <0>; //dclk 极性控制
};
};
//这个ports是panel的,这个port要和dsi对应的port绑定
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
panel_in_dsi: endpoint {
remote-endpoint = <&dsi_out_panel>;
};
};
};
};
//这个ports是dsi的,这个port要和目标panel的port绑定
ports {
#address-cells = <1>;
#size-cells = <0>;
port@1 {
reg = <1>;
dsi_out_panel: endpoint {
remote-endpoint = <&panel_in_dsi>;
};
};
};
};
然后需要设置绑定关系
代码语言:javascript复制//这个属性设置为okay,这样hdmi可以显示logo
&route_hdmi {
status = "okay";
connect = <&vopb_out_hdmi>;
};
//这个属性设置为okay,这样dsi可以显示logo
&route_dsi{
status = "okay";
connect = <&vopl_out_dsi>;
};
//dsi属性设置为okay时,不与它绑定的那个要设置为disabled
&dsi_in_vopb {
status = "disabled";
};
//hdmi属性设置为okay时,不与它绑定的那个要设置为disabled
&hdmi_in_vopl {
status = "disabled";
};
之前介绍bootloader的文章提过,其uboot会取kernel的设备树并进行一些设置,显示logo的操作是放在uboot部分,因此要配置route_dsi,这样可以让uboot将logo配置到dsi屏幕。
最后执行编译,如果之前编译过整个sdk,则编译的命令如下
代码语言:javascript复制./build.sh –K
最后会生成目标文件,将这个编译好的boot.img烧录到板上,然后注意一下串口的kernel log
这个log说明drm驱动已经能够正常运作
这个log说明dsi控制器已经工作,且已经绑定到drm框架中
这个log说明输出的lane clock,一般来说输出这个信息,说明dsi控制器和panel已经绑定
这个log说明panel已经工作,且输出这个log的时候,一般屏幕上面会显示一些图像
如果确认上述的log都有,到这里,应该开机,能显示出logo
到这里,kernel部分的修改完成
Android部分修改
Android里面修改的是HWComposer部分的内容,这个HWComposer被Android的SurfaceFlinger调用,操作GPU VOP RGA等的驱动。HWComposer由OEM厂商提供。RK的HWComposer有一系列系统属性,用于帮助客户能够根据需求配置显示。
在device目录下找到目前启用的产品型号对应的mk文件(如果编译的时候选择的板型是WW_Tinker_Board_2,则找到WW_Tinker_Board_2.mk,如果编译的时候选择的是rk官方版本的rk3399_Android11,则找到rk3399_Android11.mk),加入如下信息
代码语言:javascript复制PRODUCT_PROPERTY_OVERRIDES =
vendor.gralloc.no_afbc_for_fb_target_layer=1
vendor.gralloc.no_afbc_for_sf_client_layer=1
vendor.hwc.device.primary=HDMI-A
vendor.hwc.device.extend=DSI
前面两行的意思是关闭AFBDC。AFBDC 编码是 GPU 与 VOP 之前以降低带宽为目的的一种图像压缩格式,只有 VOP BIG 支持AFBDC 编码,VOP LITTLE 不支持该编码格式。如果有两个显示屏,VOPL的时候,如果不关闭AFBDC,则有可能将AFBDC 编码的framebuffer传递到VOPL,这时候会出现部分区域花屏,黑屏的现象。
后面两行则是设置主副显示屏,默认状态下,Android的窗口大小(wm size)和旋转方向跟主显示屏一致。因为这里使用的DSI屏和HDMI的横纵比差不多,如果没有特别的要求,HDMI和DSI哪个当主显示屏都可以。
修改此处后执行make –j8,烧录super.img到板上,即可正常显示出Android的画面,默认状态下,DSI和HDMI显示的内容是一样的,RK也有双屏异显的DEMO,后面再介绍。
到这里,这款风火轮为tinkerboard2定制的DSI屏幕的调试就完成了,且支持了HDMI DSI的双屏异显。有任何疑问可到华硕官方论坛(https://tinkerboard.cn/forum.php)发帖留言。
总结
本文以tinkerboard2对接7寸1024*600分辨率的DSI屏幕为例,介绍了在tinkerboard2在Android11下面适配DSI屏幕的方式。此方法对于其他分辨率的屏幕同样适用。
上面提到的电源适配器,屏幕,转接板,都可以从深圳风火轮处购买。深圳风火轮对tinkerboard2进行了很完善的支持,同样他们还有很多其他的配件,后面会继续与大家分享。
最后还是想提一下,相比起树莓派封闭的生态,tinkerboard2开源了其Android源码,因此可以有更多的客制化功能。就拿屏幕来说,目前市面上的树莓派屏幕,多数是HDMI接口的,因为HDMI支持EDID,可以动态读取分辨率,但是很多场合HDMI屏并不适用。而树莓派支持的DSI屏幕又很有限,价格贵,因此,如果是想将产品应用于一些商显的场合,tinkerboard2无疑更加合适。