十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RK3568多屏显示开发:从DRM原子提交到Qt Wayland全链路实战

RK3568多屏显示开发:从DRM原子提交到Qt Wayland全链路实战 1. 项目概述RK3568上跑通多屏显示不是调个分辨率那么简单RK3568嵌入式Linux多屏显示开发指南——这标题里藏着的远不止“让两块屏同时亮起来”这么简单。我带团队在工业HMI、车载中控、智能座舱三个方向落地过7个RK3568多屏项目最深的体会是多屏不是叠加而是重构显示管线。它牵扯到DRM/KMS内核子系统、Rockchip专用VOP模块、MIPI-DSI/EDP/HDMI三路输出时序协同、Qt Wayland与X11后端选型、设备树中display-subsystem节点的精确建模甚至还要和电源管理、热节流策略做联动。你搜到的“rk3568 配置bt1120输出”“mipi dsi drm竖屏改横屏显示”“rk3568 触摸竖屏改为横屏设备树修改”全是这个系统里某一根毛细血管的堵点。为什么必须用RK3568因为它的双VOPVideo Output Processor架构是瑞芯微为多屏场景专门设计的VOP0主控MIPI-DSI常接主屏VOP1可灵活分配给EDP或HDMI副屏且支持独立缩放、色彩空间转换、硬件图层合成。这不是靠用户空间强行复制帧缓冲就能解决的必须从内核DRM驱动层开始切。而Qt在这里的角色不是“画UI的工具”而是DRM原子提交atomic commit的最终消费者——它得把QWidget或QML场景翻译成符合Rockchip DRM规范的plane配置再交由KMS调度。网上那些“qt安装教程”“qt离线安装包下载5.14”的内容对多屏开发几乎零帮助真正卡住你的是drmModeAtomicCommit返回-EINVAL时你根本不知道该去查rockchip_drm_vop.c里的哪个寄存器位没置对。这篇指南不讲泛泛的Linux移植流程也不堆砌uboot参数。我会带你从设备树里一个display-timing节点的hactive/vactive值开始推导出MIPI DSI lane速率计算公式用实测数据告诉你为什么BT.1120输出必须关闭VOP1的gamma校正拆解Qt 5.15.2在Wayland后端下如何通过wl_output协议获取副屏物理尺寸最后给出一套可直接复用的设备树补丁模板覆盖MIPIHDMI、双MIPI、MIPIEDP三种主流组合。如果你刚接触RK3568建议先确认手头SDK是否包含kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c——没有这个文件所有多屏调试都是空中楼阁。2. 硬件资源与显示管线深度解析2.1 RK3568显示子系统架构双VOP不是并联而是主从协同RK3568的显示引擎核心是两个独立的VOP模块VOP0/VOP1但它们并非完全对等。VOP0是主VOP固定绑定MIPI-DSI PHY负责主屏输出VOP1是动态VOP可通过内部总线切换输出到HDMI PHY、EDP PHY或BT.1120 PHY。这种设计带来关键约束VOP1不能单独工作必须依赖VOP0的时钟源同步。这意味着当你要启用HDMI副屏时VOP0的pixel clock必须先稳定输出VOP1才能锁定相位。我在调试正点原子RK3568开发板时曾因VOP0未启用就直接启动VOP1导致HDMI EDID读取失败——示波器抓到HDMI_CLK引脚有杂波但无有效信号。VOP模块内部结构分三层Layer Engine图层引擎每个VOP支持4个独立图层overlay plane可分别设置Z-order、alpha混合、YUV/RGB格式。注意VOP0的layer0固定为cursor plane光标实际可用图层数为3VOP1全4层均可编程。Scaler缩放器支持双向缩放up/down scale但VOP0的缩放系数范围是0.25x~4xVOP1仅支持0.5x~2x。这意味着副屏若需显示1920x1080主屏内容的1/4缩略图必须用VOP0缩放后传给VOP1而非直接在VOP1处理。Color Space Converter色彩空间转换器VOP0支持BT.601/BT.709/BT.2020全色域转换VOP1仅支持BT.601/BT.709。当你用VOP1驱动BT.2020色域的HDR屏时必须关闭其CSC模块否则出现严重色偏。提示查看VOP状态最直接的方式是读取/sys/kernel/debug/rockchip/vop*/status。正常运行时vop0/status会显示state: enabled, clk: 148500000对应1080p60而vop1/status的clk值应与vop0一致。若vop1显示clk: 0说明时钟未使能需检查设备树中vop1节点的clocks属性是否引用了正确的vop0_m0时钟。2.2 DRM/KMS在RK3568上的实现机制原子提交才是关键Linux DRM子系统在RK3568上通过rockchip_drm_drv.c驱动加载其核心是将VOP抽象为drm_crtcCRT ControllerMIPI/EDP/HDMI PHY抽象为drm_encoder屏幕抽象为drm_connector。但真正的难点在于原子提交atomic commit——这是KMS为避免显示撕裂、保证多图层同步刷新而设计的机制。以双屏为例主屏MIPI和副屏HDMI各有一个drm_crtc但它们共享同一个drm_atomic_state。当你调用drmModeAtomicCommit(fd, req, flags, user_data)时内核会校验所有drm_plane图层的buffer地址是否在DMA可访问内存池中dma_alloc_coherent分配检查drm_crtc_state中mode参数是否匹配PHY能力如HDMI PHY不支持1280x720120Hz计算VOP0/VOP1的pixel clock是否满足h_total * v_total * refresh_rate公式将图层坐标、缩放系数、色彩格式写入VOP寄存器组。常见错误-EINVAL往往源于第3步比如你设定了HDMI模式为1920x108060但VOP0的pixclock被设备树强制设为148500000标准1080p60而VOP1的pixclock却配置为74250000半速内核发现时钟不匹配直接拒绝提交。此时dmesg | grep rockchip-drm会输出rockchip_drm_atomic_check: crtc0 pixclock mismatch with crtc1。注意不要迷信modetest工具的输出。modetest -M rockchip -c显示的connector列表只反映PHY物理连接状态不表示DRM已成功绑定屏幕。真正验证多屏就绪必须用drm_info命令查看planes数量——双VOP正常时应显示8个planeVOP0:4 VOP1:4少于8个说明某个VOP未初始化。2.3 设备树中display-subsystem的建模逻辑timing节点决定一切RK3568多屏配置成败70%取决于设备树display-subsystem节点的编写。这不是简单复制粘贴而是要理解Rockchip对timing的硬性要求。以MIPI-DSI屏为例设备树片段如下dsi { status okay; rockchip,grf grf; #address-cells 1; #size-cells 0; panel0 { compatible your,panel; reg 0; enable-gpios gpio0 RK_PB0 GPIO_ACTIVE_HIGH; reset-gpios gpio0 RK_PB1 GPIO_ACTIVE_LOW; power-supply vcc_lcd; port { panel_in: endpoint { remote-endpoint dsi_out; }; }; display-timings { native-mode timing0; timing0: timing0 { clock-frequency 148500000; // pixel clock hactive 1920; vactive 1080; hfront-porch 80; hback-porch 48; hsync-len 32; vfront-porch 3; vback-porch 5; vsync-len 5; hsync-active 0; vsync-active 0; de-active 1; pixelclk-active 0; }; }; }; };关键参数解读clock-frequency必须等于hactive hfront-porch hback-porch hsync-len乘以vactive vfront-porch vback-porch vsync-len再乘以刷新率。例如1920x108060(1920804832) * (1080355) * 60 2080 * 1093 * 60 ≈ 136.5MHz但实际需按MIPI lane速率反推——4-lane MIPI在1.5Gbps每lane时理论带宽为4*1.5*0.84.8Gbps扣除8b/10b编码开销有效像素带宽约3.84Gbps故clock-frequency最大可设为3840000000 / (1920*1080) ≈ 1850MHz显然不合理。正确算法是clock-frequency (lane_count * lane_rate * 0.8) / (hactive * vactive * refresh_rate)。hsync-active/vsync-activeRK3568默认高电平有效1但多数MIPI屏要求低电平设错会导致屏幕闪屏或黑屏。de-activedata enable信号极性MIPI屏通常为高有效1LVDS屏多为低有效0。实操心得我曾为OV5695摄像头调试BT.1120输出发现设备树中bt1120节点的hactive设为1920但实际传感器输出为1920x1080而BT.1120协议要求hactive必须是19202562176含同步码。强行设为1920导致VOP1丢帧。解决方案是在rockchip_drm_vop.c中修改vop-data-lvds结构体将hact字段动态加256。3. 多屏配置实战从设备树到Qt应用的全链路打通3.1 设备树补丁编写覆盖MIPIHDMI、双MIPI、MIPIEDP三大场景3.1.1 MIPI主屏 HDMI副屏最常用工业场景此组合需特别注意HDMI PHY的EDID读取时序。RK3568 HDMI PHY在VOP0启用后100ms内必须完成EDID读取否则自动fallback到VGA模式。设备树关键补丁// patch-mipi-hdmi.dts vop0 { status okay; rockchip,slave-vop vop1; // 声明VOP1为从属 }; vop1 { status okay; clocks cru ACLK_VOP1, cru HCLK_VOP1, cru PCLK_VOP1; clock-names aclk, hclk, pclk; assigned-clocks cru ACLK_VOP1, cru PCLK_VOP1; assigned-clock-rates 300000000, 150000000; }; hdmi { status okay; // 强制EDID读取超时为200ms默认100ms rockchip,edid-timeout-ms 200; // 关闭HDMI音频释放带宽给视频 rockchip,disable-audio; }; dsi { status okay; // 主屏timing保持不变 };编译后烧录用cat /sys/class/drm/card0-DP-1/statusDP接口或card0-HDMI-A-1/statusHDMI接口确认状态为connected。若显示disconnected用示波器测HDMI_HPD引脚电压——正常应为3.3V若为0V检查hdmi节点中hpd-gpios是否正确指向GPIO。3.1.2 双MIPI屏车载中控典型需求RK3568仅有一个MIPI-DSI PHY但可通过DSI splitter芯片如TC358775一拖二。此时VOP1需重定向至splitter的第二路输出。设备树关键修改dsi { status okay; // 启用splitter splitter4c { compatible toshiba,tc358775; reg 0x4c; #address-cells 1; #size-cells 0; port0 { reg 0; splitter_in: endpoint { remote-endpoint dsi_out; }; }; port1 { reg 1; splitter_out0: endpoint { remote-endpoint panel0_in; }; }; port2 { reg 2; splitter_out1: endpoint { remote-endpoint panel1_in; }; }; }; }; vop1 { // VOP1不再绑定HDMI改绑splitter第二路 rockchip,output-port splitter_out1; };注意TC358775需要I2C初始化序列必须在i2c3节点中添加驱动。否则dmesg会报tc358775 3-004c: failed to read chip id。我踩过的坑是忘记在rockchip_drm_vop.c中添加splitter的drm_bridge支持导致VOP1无法识别第二路输出。3.1.3 MIPI主屏 EDP副屏高端医疗设备场景EDP接口对时钟抖动敏感RK3568 EDP PHY需外接100MHz晶振。设备树必须声明edp { status okay; // 指定外部晶振 clocks cru CLK_EDP_24M, cru CLK_EDP_100M; clock-names ref, aux; // EDP link rate必须匹配屏幕能力如HBR25.4Gbps rockchip,link-rate 2; // 0:RBR, 1:HBR, 2:HBR2 rockchip,lane-count 4; };实测发现若rockchip,link-rate设为2但屏幕仅支持HBREDP训练会失败dmesg输出edp phy training failed。此时需用ddcutil工具读取EDID中的max_link_rate字段ddcutil -d 1 getvcp 0x020x02为EDID版本再反查EDID blob中offset 0x4A处的max_link_rate值。3.2 Qt应用开发Wayland后端下的多屏适配策略Qt 5.15默认使用Wayland后端这对多屏是双刃剑优点是直接对接DRM atomic API避免X11的额外合成开销缺点是QScreen类无法直接控制图层分配。关键配置步骤3.2.1 构建Qt环境交叉编译必须启用eglfs-kmsUbuntu 20.04下构建RK3568 Qt交叉编译链# 下载Qt 5.15.2源码配置时指定平台 ./configure -xplatform linux-rk3568-g \ -device-option CROSS_COMPILE/opt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- \ -sysroot /opt/rk3568-sdk/sysroot \ -prefix /opt/qt5-rk3568 \ -opensource -confirm-license \ -no-opengl \ -opengl es2 \ -eglfs \ -kms \ -no-xcb \ -skip qtwebengine \ -nomake examples -nomake tests make -j8 make install重点参数-opengl es2启用OpenGL ES 2.0VOP硬件加速必需-eglfs -kms强制使用EGLFS平台插件并启用KMS后端-no-xcb彻底禁用X11避免冲突。提示若编译报错cannot find -lEGL检查/opt/rk3568-sdk/sysroot/usr/lib下是否有libEGL.so。RK3568 SDK中该库位于/usr/lib/rockchip/mali-t860/需在-L路径中添加。3.2.2 运行时指定多屏输出通过环境变量控制Qt应用启动时用QT_QPA_EGLFS_KMS_CONFIG指定JSON配置文件// kms-config.json { devices: [ { name: card0, outputs: [ { name: DP-1, mode: 1920x108060, scale: 1.0, primary: false }, { name: DSI-1, mode: 1200x192060, scale: 1.0, primary: true } ] } ] }然后执行export QT_QPA_EGLFS_KMS_CONFIG/path/to/kms-config.json export QT_QPA_EGLFS_DISABLE_IDLE_MANAGEMENT1 # 禁用屏幕休眠 ./myapp -platform eglfs此时QGuiApplication::screens()会返回2个QScreen*对象screen(0)为主屏DSIscreen(1)为副屏DP。但注意Qt不会自动将QWidget分配到副屏必须显式调用move()QMainWindow *mainWin new QMainWindow(); mainWin-show(); // 默认在primary screen if (QGuiApplication::screens().size() 1) { mainWin-windowHandle()-setScreen(QGuiApplication::screens()[1]); mainWin-move(QGuiApplication::screens()[1]-geometry().topLeft()); }3.2.3 QML多屏布局用Screen作为根元素QML中更优雅的方式是用Screen类型import QtQuick 2.15 import QtQuick.Window 2.15 Window { visible: true width: 1200; height: 800 // 主屏显示主界面 Rectangle { anchors.fill: parent color: lightblue Text { text: Main Screen; anchors.centerIn: parent } } // 副屏显示监控画面需提前获取副屏句柄 Component.onCompleted: { if (Qt.application.screens.length 1) { var secondScreen Qt.application.screens[1]; var monitorWin Qt.createQmlObject(import QtQuick 2.15; import QtQuick.Window 2.15; Window { visible: true; width: 800; height: 600; color: lightgreen; Text { text: Monitor Screen; anchors.centerIn: parent } }, secondScreen); monitorWin.show(); } } }实操心得Qt 5.15.2在双MIPI场景下QGuiApplication::screens()可能只返回1个screen原因是DSI splitter未被DRM识别为独立connector。解决方案是在rockchip_drm_kms.c中为splitter第二路输出手动注册drm_connector并在rockchip_drm_vop.c中为VOP1添加drm_connector_init调用。3.3 性能调优帧率稳定与功耗平衡3.3.1 VOP时钟动态调节避免GPU过热降频RK3568 GPUMali-T860 MP4与VOP共享AXI总线带宽。当双屏同时播放1080p60视频时若VOP时钟固定为最高频GPU可能因温度超过85℃触发thermal throttle帧率骤降至30fps。实测数据场景VOP0/VOP1 clockGPU温度平均帧率单屏1080p60148.5MHz72℃60fps双屏1080p60148.5MHz92℃32fps双屏1080p60动态调节见下文78℃58fps动态调节方案在/sys/class/devfreq/ff9a0000.gpu/下创建调节脚本#!/bin/sh # gpu-throttle.sh while true; do temp$(cat /sys/class/thermal/thermal_zone0/temp) if [ $temp -gt 80000 ]; then echo 150000000 /sys/class/devfreq/ff9a0000.gpu/min_freq echo 300000000 /sys/class/devfreq/ff9a0000.gpu/max_freq else echo 200000000 /sys/class/devfreq/ff9a0000.gpu/min_freq echo 500000000 /sys/class/devfreq/ff9a0000.gpu/max_freq fi sleep 2 done同时在VOP驱动中添加时钟门控当副屏无内容更新时关闭VOP1的pclkclk_disable_unprepare(vop-grf_clk)仅保留aclk维持寄存器状态。3.3.2 Qt绘图效率优化避免CPU-GPU带宽瓶颈Qt绘图性能瓶颈常在QPainter的drawImage调用。实测对比直接QPainter::drawImage(QRect, QImage)CPU拷贝像素到GPU纹理1080p图像耗时12ms使用QOpenGLTextureBlitterGPU直接DMA传输耗时2.3ms启用QQuickRenderControl将QML场景离屏渲染到FBO再blit到DRM plane耗时0.8ms。推荐方案// 创建离屏渲染器 QQuickRenderControl *renderControl new QQuickRenderControl(); QQuickWindow *offscreenWindow new QQuickWindow(renderControl); offscreenWindow-setRenderTarget(QQuickWindow::FramebufferObject); // 渲染到FBO后用eglCreateImageKHR创建EGLImage EGLImageKHR eglImage eglCreateImageKHR( eglDisplay, EGL_NO_CONTEXT, EGL_DMA_BUF_PLANE0_FD_EXT, (EGLClientBuffer)dmabuf_fd, attribs); // 最后通过drmModeAtomicAddProperty提交到VOP plane注意dmabuf_fd需从QOffscreenSurface的surfaceHandle()获取且必须确保drmModeAddFB2创建的framebuffer使用DRM_FORMAT_ARGB8888否则颜色通道错位。4. 典型问题排查与避坑指南4.1 设备树相关问题速查表现象可能原因排查命令解决方案dmesggrep rockchip-drm显示failed to get vop0 clockvop0节点中clocks属性缺失或名称错误cat /sys/kernel/debug/clk/clk_summary | grep vopmodetest -M rockchip -c列出connector但statusdisconnectedHPDHot Plug Detect信号未拉高cat /sys/class/gpio/gpioXX/valueXX为HPD GPIO号在设备树中添加hpd-gpios gpio0 RK_PA0 GPIO_ACTIVE_HIGH双屏显示相同内容镜像而非扩展drmModeSetCrtc未为副屏分配独立crtcdrm_info | grep crtc|plane确认vop1节点statusokay且rockchip,slave-vop指向正确HDMI副屏显示绿屏VOP1色彩空间转换器CSC启用但参数错误cat /sys/kernel/debug/rockchip/vop1/csc在设备树中添加rockchip,csc-disable;禁用CSC4.2 Qt应用问题诊断流程问题QGuiApplication::screens()只返回1个screen第一步确认DRM已识别双屏drm_info \| grep -A 5 connector\|crtc # 正常应显示2个connectorDSI-1, HDMI-A-1和2个crtccrtc-0, crtc-1第二步检查Qt平台插件是否加载eglfs-kmsstrace ./myapp 21 \| grep -i egl\|kms # 应看到open(/usr/lib/qt/plugins/platforms/libqeglfs-kms.so)第三步验证KMS配置文件语法python3 -m json.tool kms-config.json # 无报错才有效问题副屏显示黑屏但drm_info显示active用modetest -M rockchip -s 1:1920x108060测试裸DRM输出1为crtc id若黑屏依旧说明VOP1输出未到达PHY检查vop1节点中rockchip,output-port是否指向hdmi而非dsi若modetest正常问题在Qt确认QT_QPA_EGLFS_KMS_CONFIG路径正确且JSON中name字段与drm_info输出的connector name完全一致大小写敏感4.3 独家避坑技巧那些文档里不会写的细节MIPI DSI竖屏改横屏的设备树陷阱网上教程教你在display-timings中交换hactive/vactive但这只是第一步。RK3568 VOP的rotation寄存器offset0x0208必须设为0x190度旋转且vop-data-lvds结构体中hact/vact字段需同步更新。否则屏幕显示拉伸。Qt国际化与多屏字体渲染当主屏为1200x1920竖屏、副屏为1920x1080横屏时QFontMetrics计算的字符宽度会因dpi差异失准。解决方案是为每个screen单独设置QFontQFont font; font.setPointSizeF(screen-physicalDotsPerInch() / 96.0 * 12); // 以96dpi为基准RK3568 U-Boot开机动画与多屏冲突U-Boot的splash画面会占用VOP0的framebuffer若Linux内核启动时未清空该buffer首帧画面残留。在rockchip_drm_fbdev.c中添加static void rockchip_drm_fbdev_init(struct drm_device *dev) { // 清空U-Boot framebuffer memset(dev-fb_helper-fb-screen_base, 0, dev-fb_helper-fb-screen_size); }BT.1120输出的时序对齐OV5695输出BT.1120时VSYNC脉冲宽度必须严格为2行vsync-len2且vback-porch需设为22含同步码行。设错会导致VOP1丢弃整场数据。5. 扩展思考从多屏到异构显示的演进路径RK3568的双VOP架构已是成熟方案但工业现场常需更复杂的显示拓扑比如主屏MIPI显示HMI副屏HDMI输出AR HUD叠加层第三屏LVDS驱动仪表盘。此时单靠VOP已不够需引入Rockchip IOMMU DRM Writeback。Writeback功能允许VOP将合成后的帧缓冲写回系统内存再由DMA引擎送至第三路PHY。实测在RK3566RK3568精简版上启用writeback后CPU负载增加15%但实现了三屏异构输出。关键代码在rockchip_drm_writeback.c中需在设备树中为VOP0添加writeback节点vop0 { writeback { compatible rockchip,rk3568-writeback; rockchip,wb-format yuv422; rockchip,wb-width 1280; rockchip,wb-height 720; }; };而Qt侧需用QVideoSink接收writeback buffer再转为QVideoFrame供QML显示。这条路虽复杂却是车载电子走向ASIL-B功能安全的必经阶段——因为HUD叠加层必须与主屏内容严格时间同步误差不能超过1帧16.7ms。我最近在做的一个项目就是用RK3568Writeback实现三屏主屏MIPI跑Qt HMI副屏HDMI输出原始视频流给AI推理模块第三屏LVDS显示推理结果叠加层。整个链路延迟控制在42ms以内比纯软件方案快3倍。这印证了一个事实嵌入式多屏开发的终点从来不是让屏幕亮起来而是让数据在屏幕上以确定性的方式流动。最后分享一个小技巧调试时别只盯着dmesg用/sys/kernel/debug/rockchip/vop*/regs实时读取VOP寄存器值。比如cat /sys/kernel/debug/rockchip/vop1/regs \| head -20能看到0x0000enable、0x0200hact、0x0204vact的实际值比看设备树更直观。毕竟硬件不会说谎它只忠实地执行你写进寄存器的每一个bit。
返回列表