
1. 项目概述RK3568多路显示移植到底在解决什么问题之前在团队里接手了一块基于瑞芯微RK3568的开发板要求把开源鸿蒙OpenHarmony系统跑上去并且要同时驱动多个显示设备一块HDMI屏幕做主屏一块MIPI DSI接口的LCD面板做副屏另一块接班的项目甚至要加一路eDP。当时网上的资料零散得很OpenHarmony在RK3568上的多路显示移植能查到的样例也不够系统很多细节都要靠看内核源码和设备树自己摸索。这篇文章把我踩过的坑和最终整理出的完整流程记录下来希望能给正在做类似“RK3568 OpenHarmony多屏显示”的朋友一条相对顺畅的路。这个内容解决的核心问题就是当你手里的RK3568板卡已经能单路显示正常点亮之后怎么把多路显示“拧”到一起让它们在OpenHarmony系统里并存、切换、甚至组合成扩展桌面或镜像桌面。适合的人群主要包括三类一是做开源鸿蒙系统适配的驱动工程师二是做IoT/工业HMI设备选型RK3568的产品开发三是对OpenHarmony图形显示子系统感兴趣、想自己动手移植玩一玩的学习者。就算你手里的板卡不是RK3568比如是RK3588、RK3399这类同样带VOP显示控制器的芯片这篇文章里的排查方法论大体也是通用的。先说明一点我后面讲的所有操作默认你的OpenHarmony版本是当前常见的3.2/4.0分支内核基线基于Linux 5.10或4.19而且开发板已经能够正常启动系统进入桌面。如果还在Uboot阶段就把显示搞挂了那要先回到基础启动流程排查这篇文章重点讲的是进入OpenHarmony之后的显示框架适配不是最底层的启动点亮。2. RK3568显示硬件架构与OpenHarmony软件栈的关键匹配点2.1 先看懂RK3568的显示控制器VOP2RK3568内部的显示控制器叫VOP2它和更早的RK3288、RK3399上的VOP1有很大区别。VOP2不是单一的视频输出口而是一个支持多视频端口VideoPort简称VP的显示核心。在RK3568这颗芯片上VOP2通常提供三个可以独立配置的视频端口分别叫做VP0、VP1、VP2。这三个视频端口并不是完全对等的它们各自能连接的显示接口有限制。早期有些资料说“RK3568支持三屏异显”这句话不严谨准确说法是“RK3568的VOP2提供三个视频端口可以同时驱动多个显示接口但具体组合要看引脚复用和SoC内部连线”。实践中最常见的分配方式是VP0接HDMI或MIPI DSI0VP1接MIPI DSI0/DSI1或eDPVP2接RGB/LVDS等等。开发展板的设计手册里会明确写明每一个显示接口和VP的对应关系这一步一定不能想当然。为什么先讲VOP2因为多路显示移植的本质就是在设备树里把“哪个显示接口挂到哪个VP上面”这个关系配置正确。内核里的Rockchip DRM驱动会读取设备树中的route_xxx节点和connect属性完成VP和显示接口的绑定。这就解释了为什么很多人的DTS里已经有hdmi、dsi这些节点并且status okay但显示依然不工作——因为VP没有对应上或者route节点把错误的VP绑给了显示接口。2.2 OpenHarmony显示软件栈的脉络梳理OpenHarmony的显示链路可以简单理解为三个层次最底层是Linux内核的DRM/KMS显示驱动中间是HDFHardware Driver Foundation框架中的显示设备驱动模块最上层是系统的图形渲染栈和窗口管理服务。实际移植工作中绝大多数问题出在内核DRM驱动和设备树配置上倒不一定需要大动HDF层的代码。内核侧RK3568的显示驱动目录一般在kernel/linux/build/.../drivers/gpu/drm/rockchip/对应的文件包括rockchip_drm_drv.c、rockchip_vop2_reg.c、dw_hdmi-rockchip.c、dw-mipi-dsi-rockchip.c等。这些文件在Rockchip官方Linux SDK基础上OpenHarmony版本也基本沿用只是会做一些小的适配改动。所以如果你之前接触过Rockchip Linux BSP的多屏配置那在OpenHarmony里做多路显示移植是有很大一部分经验可以直接迁移的。HDF层主要做的是让OpenHarmony的“显示图形栈”能够识别内核里注册出来的DRM设备。它会把DRM的CRTC、Encoder、Connector、Plane抽象成OpenHarmony显示模型里的设备对象。如果你想显示多路输出但同时连接器Connector没有正确枚举出来那HDF层无论怎么配置都不可能看到第二块屏幕。2.3 多路显示为什么难难在哪些地方多路显示移植的难点主要集中在三个层面。第一是时序问题多路显示不是简单地把设备树节点打开要让不同显示接口同时工作必须保证它们的时钟源、像素时钟、VOP时钟都能正确分配RK3568的CRUClock and Reset Unit里有复杂的时钟树配置不当会出现“打开HDMI之后MIPI DSI黑屏”这类奇怪现象。第二是资源竞争问题VOP2的三个视频端口共享显存带宽和一部分后端处理资源如果两个屏幕都请求超高分辨率的4K输出而内存带宽不够就可能导致画面闪烁或者花屏。通常需要合理分配分辨率和刷新率比如主屏4K60副屏只跑1080P60。第三是系统层的显示策略问题OpenHarmony系统默认可能只把第一个显示设备作为“主屏幕”来使用副屏需要配置合适的显示策略才能实现扩展显示或镜像显示的一致性体验。这一步已经不是纯驱动的问题还需要在系统参数和用户态服务层面调整。3. 设备树配置多路显示移植的地基3.1 RK3568常见显示接口与设备树节点对照做移植之前先把常见显示接口对应的设备树节点记住。RK3568的DTS里显示子系统的核心节点是vop围绕VOP的还会有vp0、vp1、vp2这些子节点以及route_hdmi、route_dsi0、route_dsi1、route_edp、route_rgb等等。这里最关键的字段就是每个route节点下面的connect它用来指定这个显示接口绑定到哪个VP节点上面。展示一段典型的节点结构display_subsystem { status okay; route_hdmi { status okay; connect vp0; /* HDMI绑定到VP0 */ }; route_dsi0 { status okay; connect vp1; /* DSI0绑定到VP1 */ }; }; hdmi { status okay; pinctrl-names default, sleep; pinctrl-0 hdmim0_tx0_cec hdmim0_tx0_sda hdmim0_tx0_scl hdmim0_tx0_clk; pinctrl-1 hdmim0_tx0_cec_sleep hdmim0_tx0_sda_sleep hdmim0_tx0_scl_sleep hdmim0_tx0_clk_sleep; }; mipi_dsi0 { status okay; panel0 { compatible xxxx,xxxx-panel; reg 0; backlight backlight; reset-gpios gpio0 RK_PB6 GPIO_ACTIVE_LOW; ... }; };注意connect vp0这种写法不同SDK版本里可能略有差异有的版本用connect vp0有的版本直接通过VOP节点的内部路由自动匹配。总体上如果你发现显示接口的设备树配置都正确但内核日志里提示“no available vop”或者“failed to find route”十有八九就是connect指向出了问题。3.2 DSI加HDMI双显示的一个可落地配置实例我实际操作时最稳妥的做法是先做一张表格把板卡上想要启用的显示接口、分辨率、使用的VP、对应的route节点列出来。比如我这里最终要的是HDMI做主屏显示系统桌面MIPI DSI0做副屏显示应用的自定义内容那我的规划就是显示接口分辨率VProute节点用途HDMI1920x108060VP0route_hdmi主屏MIPI DSI01024x60060VP1route_dsi0副屏依据这个规划DTS里设置route_hdmi的connect vp0route_dsi0的connect vp1。同时在mipi_dsi0下挂好具体的LCD panel信息比如初始化序列、背光控制引脚、复位时序等。MIPI DSI屏和HDMI不同必须要配置panel节点的时序参数否则D-PHY不会输出正确的同步信号。这里有一个必须强调的细节很多人只改了status okay却漏掉了pinctrl配置。RK3568的HDMI、DSI引脚是可以复用成不同位置的设备树里选用了哪组引脚pinctrl节点就必须相应使能。比如HDMI的hdmim0_tx0_cec和hdmim1_tx0_cec分别对应不同引脚组如果你实际布线用的是m1组而DTS里写的是m0组那显示控制器就算工作信号也无法到达HDMI座子屏幕自然是黑的。3.3 显示路由与VP绑定的避坑心得关于route_xxx节点网上很多教程都没有讲透。它不只是“打开显示输出”的开关它还承担着告诉内核“该把哪个connector注册成主显示”的角色。如果你希望HDMI上的主屏幕在OpenHarmony开机阶段就能显示系统UI那route_hdmi的优先级要提前且VDOP的绑定要保证HDMI是系统真正枚举出来的第一个显示设备。内核DRM框架枚举设备时会按照一种默认顺序遍历通常和VOP内部硬件编码有关。如果HDMI和DSI的VP绑定顺序反了可能你会看到系统界面跑到MIPI DSI屏幕上去而HDMI只是一个有信号却很奇怪的副屏。遇到这种情况除了改DTS里VP绑定之外还可以在OpenHarmony用户态设置主屏参数但这样做非常绕不如驱动层直接保证HDMI作为第一个connector。另外关于显示子系统复合设备节点display_subsystem { status okay; memory-region drm_logo; };有部分OpenHarmony版本里的display_subsystem节点还需要关联drm_logo或者drm_fb等内存区域用来在系统启动早期显示logo或者预留显存。如果你把内存区域配置得不合理比如地址范围与其他模块冲突启动时会报fdt_resource相关的错误这个问题在双屏场景尤其容易暴露出来因为显存需求变大了。4. 驱动适配与Kernel侧的实际调试方法4.1 先验证VOP2和连接器是否正常工作在设备树配置完之后不能直接进OpenHarmony桌面看效果因为如果出了问题你分不清到底是DRM驱动没起来还是系统上层服务把显示搞挂了。我更推荐先在内核启动阶段就拿到足够的证据。RK3568的DRM驱动启动后可以通过内核串口日志看到类似drm: vop2 0000、display-subsystem ... fd000000.vop、connector: HDMI-A-1、connector: DSI-1这样的打印信息。如果日志里能看到两个connector说明KMS层已经识别出两路显示输出这一步就成功了一大半。如果没有第二路连接器要回头检查MIPI DSI的panel节点是否成功匹配panel的初始化序列是否执行成功对应VP的时钟是否打开一个非常实用的方法是在串口下使用drm_info或者查看debugfs文件。OpenHarmony的Kernel里如果开启了CONFIG_DEBUG_FS可以执行cat /sys/kernel/debug/dri/0/summary这个文件会打印出当前DRM设备的状态包括每个plane、crtc、connector的状态。里面能看出当前哪一路显示是开启的、连接器有没有检测到外接屏幕、分辨率是多少。我调试HDMI和MIPI DSI共存时最喜欢用的就是这个节点因为它把状态汇总得很清楚。4.2 几类常见的内核报错和排查方向我把实际调试中遇到的比较有代表性的报错信息整理一下方便你对照排查。第一种是rockchip-drm display-subsystem: failed to bind ............ (ops xxx): -517这个-517是EPROBE_DEFER的意思代表驱动初始化时依赖的某个资源还没准备好。比如VOP2驱动等待pixel clock、等待pwm backlight等通常不是致命错误内核会延迟重试。但如果持续出现并最终失败就要看它到底在等什么资源多半是某个GPIO、regulator或者clock配置缺失。第二种是dw-mipi-dsi0: failed to get panel: -19这说明DSI主机控制器找不到对应的panel设备。大概率是panel节点没挂在正确的位置或者compatible字符串匹配不上。排查方法是检查panel节点的compatible是否和你内核驱动里的of_device_id匹配。第三种是rockchip-hdmi: failed to get edidHDMI线没有正确读取到显示器EDID信息可能的原因有HDMI线的物理连接、I2C通信异常、引脚复用不对。调试时可以先用支持EDID读取的显示器测试不要用电视盒子之类兼容性差的目标设备。4.3 OpenHarmony HDF层的显示适配要点内核DRM层把两路显示输出都注册好之后OpenHarmony的HDF层需要能正确枚举到这些设备。HDF显示驱动模块一般是通过/dev/dri/card0或类似的设备节点来和内核通信的。如果你发现内核日志里DRM已经识别到两个connector但系统屏幕设置里只有一块屏那问题很可能出在HDF层对显示设备的枚举逻辑上。一个常见的做法是先确认OpenHarmony系统里的显示设备注册情况。可以借助hidumper查看系统服务或者用hilog查看准备阶段的相关日志。如果HDF层只是把第一个connector识别为PrimaryDev而副屏的connector没有被加到设备列表中就需要检查HDF显示驱动的配置文件通常是类似display_device_config或者设备服务发布时的配置文件确保允许注册多个显示设备。需要注意的是OpenHarmony版本差异比较大不同分支对显示设备的抽象不同。有的版本把一路显示输出定义为一个DisplayDevice有的版本则是通过DrmConnector的通用方式上报。我目前建议优先保证内核侧稳定HDF层的问题一般能通过日志快速定位极少需要改动HDF源码。5. 多屏协同调试与OpenHarmony上层效果验证5.1 确认主副屏关系先让系统亮起来再说内核已经枚举出两路显示输出之后进入系统桌面前必须先确认哪一路是主屏。OpenHarmony默认会把系统中第一个枚举出的显示设备当作主显示设备。这里的“第一个”并不完全等于设备树里的route_hdmi优先级还会受到内核DRM中connector注册顺序的影响。我第一次调双屏时让HDMI绑定到了VP0DSI绑定到了VP1但开机后系统UI仍然跑到了HDMI上而DSI那一路只是重复显示了桌面。这是因为OpenHarmony当时默认采用的策略是主屏输出系统桌面后面的显示设备默认走镜像模式。如果要实现“副屏显示独立应用界面”需要在应用层调用多屏管理接口把特定窗口移到第二个显示设备上。工程上这个行为根据OpenHarmony版本不同而不同有些版本的窗口管理服务已经支持把窗口按displayId指定到目标屏幕。如果副屏完全黑屏可以先在串口下看一下DSI的panel是否被正确关闭了电源。有时候不是驱动问题而是背光控制方向反了。部分LCD模组的背光引脚是高电平点亮有的是低电平点亮搞反了就是屏幕明明有信号但看起来黑乎乎一团用强光手电贴上去看会发现其实有画面。5.2 分辨率、刷新率、旋转的常用设置在OpenHarmony上通过hidumper可以看到当前显示设备列表和设备能力。常见的显示参数有分辨率、刷新率、旋转角等。命令行看屏幕参数可以用类似这样的方式hidumper -s WindowManagerService -a -a输出的内容会带有显示设备的信息比如显示id、支持的分辨率列表、当前活动模式。如果你发现副屏的分辨率没有从EDID或panel时序里正确读到可以回到DTS的panel节点去检查display-timings子节点。MIPI DSI屏幕一般不能像HDMI那样自动协商分辨率必须由DTS中的时序参数确定。刷新率方面OpenHarmony的图形栈一般会沿用DRM驱动里提供的默认mode。如果你需要把副屏固定到某个刷新率可以在DTS的route_xxx节点或者panel节点里指定video_mode为宜也可以在内核驱动中强制preferred_mode。但要注意刷新率过高会增加VP和内存带宽占用RK3568同时跑4K60和1080P60已经很紧张了再往上加容易触发带宽瓶颈。关于旋转MIPI竖屏或者工业面板很多时候是竖屏安装的需要设置旋转。OpenHarmony屏幕旋转可以在驱动层把rotation参数配置好也可以在应用层通过窗口属性旋转。我的经验是能靠panel的原生横竖屏方向就尽量靠硬件解决不要依赖软件旋转因为软件旋转会额外占用GPU和带宽双屏环境下更容易卡顿。5.3 怎么判断是屏幕问题还是驱动问题双屏调试最怕的就是“一个亮一个不亮”而且你不知道谁出了问题。我自己有一个固定的排查顺序先确认两路的硬件信号是否正常再查内核状态最后才看OpenHarmony上层。第一HDMI用一台确认能自动显示的显示器DSI用一块确认在单屏模式下能点亮的屏。如果单屏都点不亮就不要去排查双屏问题。第二内核启动时串口日志要看dri相关输出确保两路都注册成功。第三用/sys/kernel/debug/dri/0/state去查看每个connector的current state它会告诉你当前哪一路处于连接状态、哪一路已经输出信号。如果这里都正常那问题大概率在OpenHarmony上层的显示策略。补充一个技巧在调试期间可以临时把其中一个显示接口从DTS里改为status disabled只留HDMI或者只留DSI这样可以快速缩小问题范围。等单路都稳定了再同时使能两路把问题留给“双屏互扰”这个维度。6. 实际移植中遇到的高频问题和我的排查心得6.1 高频问题速查表我把调试中遇到的高频问题整理成一张表方便直接查阅。现象可能原因快速排查方向开机HDMI有logo进系统桌面后黑屏主屏切换逻辑异常或HDF层没有把HDMI设为主显示查看系统日志中DisplayManager相关报错副屏一直黑屏但内核日志有connector背光GPIO配置错误或panel init sequence失败检查backlight节点和panel初始化时序两屏都亮但画面一样OpenHarmony当前显示策略是镜像模式通过窗口管理服务设置扩展模式或指定displayId两屏其中一个出现花屏显存带宽不足或VP绑定导致时钟冲突降低分辨率/刷新率重新分配VP绑定启动过程卡在开机动画动画渲染到了不存在的display上检查开机动画配置的默认display切换副屏时整个系统卡顿显示渲染线程同步问题或带宽不足关闭GPU硬件合成合成改用手动控制策略有些问题看起来是显示问题实际是电源域和引脚配置问题。比如RK3568的eDP接口和某个MIPI接口可能在引脚上有复用冲突如果同时打开就会导致引脚控制权归属混乱表现就是屏幕时而亮时而灭。这时候要在DTS的iomux节点里检查引脚配置通常是使用pinctrl来控制用错了引脚组怎么都调不对。6.2 我经历的一次MPI DSI时序反复失败的完整排查有一次做双屏HDMI本来很稳定加上DSI副屏之后DSI一直报failed to display但panel节点怎么看都没问题。最后发现是panel的上电时序问题。RK3568的MIPI DSI panel通常要求先上电源再拉复位脚等待若干毫秒然后才能发送初始化命令。而这个时序配置除了DTS里panel节点指定的reset-gpios之外还与驱动代码中的prepare、unprepare、enable回调密切相关。我检查驱动后意识到panel驱动里如果prepare阶段被设置成休眠太久或者reset时序只在enable阶段执行而和OpenHarmony的显示开启流程没有对上就会导致上电顺序不满足屏厂要求。后来我按照屏幕规格书把reset时序放在prepare中执行再通过延时函数保证电源稳定屏幕才正常亮起来。这类问题非常依赖屏幕型号没法给一个万能的DTS配置。但是方法论是通用的首先要找到屏幕规格书里的“上电时序图”然后在panel驱动里严格对照时序图实现prepare和enable阶段的GPIO操作。有条件的可以用逻辑分析仪抓一下引脚电平变化和规格书的时序图表比一比很快就能定位问题。6.3 多路显示带宽和资源的预估经验多路显示移植做到后期还要面对带宽规划问题。RK3568的DDR带宽是共享的除了显示之外还要给CPU、GPU、VPU、编解码等模块分配。两路1080P60同时刷新大约需要的内存带宽是两路之和约等于2 x 1920 x 1080 x 60 x 4RGBA按32bit算大概就是接近1.6GB/s的纯显示带宽。再加上系统UI的组合渲染RK3568在带宽上还不至于崩溃但如果在双屏基础上再做4K视频播放就很容易出现画面卡顿。实际项目中要关注OpenHarmony图形栈的合成方式。常用的合成方式是GPU合成或HWC硬件合成如果合成负担过大可以适当降低副屏分辨率或者把副屏的应用场景限制为静态界面或低帧率内容。这一条经验在工业HMI设备上特别常用副屏不一定需要和主屏同样的刷新速率。关于显存分配在DTS的display_subsystem里预留的memory-region要足够大特别是有多屏logo或者单屏显示需求时要额外注意。如果系统启动后画面闪烁、撕裂就要考虑显存是否被其他模块占用过多或者调整内核的CMA配置来预留更多连续内存。7. 一块板同时跑起HDMI和MIPI DSI之后的一些体会多路显示移植做到最后很多问题回头看并不是高深的技术难题而是对硬件细节的掌握程度。RK3568的显示能力很强但强不强取决于设备树有没有配好、时钟有没有理顺、VP有没有分对。OpenHarmony这边则要留意显示设备枚举、主屏策略这些偏应用的设置。拿我自己的经验说最让我耗费时间的一次问题其实只出在一个pinctrl引脚组选错上。当时HDMI有信号但乱码后来对比原理图才发现DTS里给HDMI配的引脚复用和实际PCB布线选用的不是同一组。从这个角度看做驱动移植手里一定要有最新的原理图、芯片技术参考手册和屏幕规格书这三个文档缺一个都要走不少弯路。调试到双屏全部亮起来并且OpenHarmony桌面能正常显示的时候那种感觉确实很舒服。后续如果你要继续深入可以尝试在副屏上跑一些自定义应用用多窗口参数把应用指定到不同的displayId上实现真正的异显。也可以进一步研究VP的硬件图层叠加把系统UI、视频、相机预览分别放到不同的plane上提升整体的视觉流畅度。