Rockchip平台双屏独立旋转调试:从DRM驱动到HWC的完整解决方案 📅 2026/8/24 6:35:55 1. 项目概述双屏旋转调试的“硬骨头”在嵌入式显示开发里双屏异显一个主屏一个副屏显示不同内容已经不算新鲜事但当你需要在两块屏幕上分别实现不同的旋转方向时比如主屏横屏显示仪表副屏竖屏显示控制菜单麻烦就来了。特别是基于Rockchip平台比如RK3568、RK3588这些热门芯片其显示子系统DRM/KMS功能强大但配置也相对复杂。最近我就啃了一块“硬骨头”为一个双屏设备调试旋转方向主屏一切正常副屏的旋转死活不生效画面方向不对触摸坐标更是乱飞。这不仅仅是改个配置文件那么简单它涉及内核驱动、显示服务、窗口管理器和应用层的一连串配合。如果你也在RK平台上被类似问题困扰比如搜到“libgl error: failed to load driver: rockchip”或者纠结于“ion-heap”配置那么我趟过的坑或许能给你省下几天时间。简单说这个调试的目标是让两块物理连接通常通过LVDS、eDP、MIPI-DSI等接口的屏幕在系统层面被识别为两个独立的显示设备并能够独立设置各自的旋转属性0°、90°、180°、270°最终保证图形渲染和触摸输入都与物理屏幕方向正确匹配。这不仅仅是UI好看更关系到设备的可用性。2. 核心思路与方案选型为什么不是改个ro.sf.rotation那么简单很多从Android手机开发转过来的工程师第一个想法就是去改系统属性ro.sf.rotation。这个属性在单屏设备上确实管用但它是一个全局设置作用于整个帧缓冲区Framebuffer。在双屏且需要独立旋转的场景下这条路直接走不通。因为两个屏幕共享同一个全局旋转状态你不可能让一个缓冲区同时横着又竖着。所以我们必须采用基于DRMDirect Rendering Manager/KMSKernel Mode Setting的每屏幕per-crtc旋转方案。这是现代Linux桌面和Android系统处理多屏显示的标准方式。其核心思路是内核层确保每个显示输出对应DRM中的crtc都能正确上报其支持的旋转属性rotationproperty并且驱动能正确处理旋转的变换。显示服务层如Android的SurfaceFlinger或Wayland/Weston合成器合成器需要查询每个显示设备的旋转属性并在合成图层时对输出到该屏幕的图层进行相应的几何变换旋转、缩放。窗口管理器与应用层应用需要根据其所在屏幕的旋转状态调整自己的UI布局通过Configuration.orientation或类似机制输入子系统也需要将触摸坐标根据屏幕旋转进行映射。对于Rockchip平台方案选型就明确了放弃修改全局ro.sf.rotation。采用内核DRM驱动配合显示服务如Android的hwcomposer.rockchip的方案。关键确保从内核到应用整个链路对“旋转”这一属性的传递和处理是一致的。注意有些简单的方案是在显示服务层对某个屏幕的整个帧缓冲区做一次旋转。这能解决显示问题但会带来两个严重副作用一是增加额外的GPU或CPU开销每一帧都要做一次全屏旋转变换二是输入坐标映射会变得极其复杂且容易出错。因此我们追求的是在管线中尽可能早的环节理想是内核或硬件层面处理旋转。3. 内核驱动层调试让硬件“认识”旋转一切的基础是内核。如果内核的DRM驱动没有为你的屏幕正确配置旋转能力上层再怎么折腾都是白费。3.1 确认设备树DTS配置首先检查你的设备树文件.dts或.dtsi。对于Rockchip平台屏幕通常通过dsi、edp或lvds节点定义。你需要确认两点屏幕是否被正确识别为独立的连接器connector在/sys/class/drm/目录下你应该能看到类似card0-HDMI-A-1、card0-DSI-1、card0-eDP-1这样的目录。每个目录代表一个显示接口。你的双屏应该对应两个这样的目录。如果只有一个可能是两个屏幕被绑定到了同一个输出端口这需要修改DTS确保它们使用不同的port和endpoint。旋转属性初始状态虽然DTS中一般不直接设置旋转角度但需要确保接口类型和时序正确。一个常见的坑是某些屏的驱动IC本身支持旋转但需要在初始化序列panel-init-sequence中发送特定命令。这需要查阅屏的规格书。你可以通过以下命令快速检查# 列出所有DRM设备 ls -l /sys/class/drm/ # 查看某个连接器的属性寻找rotation cat /sys/class/drm/card0-DSI-2/properties | grep -i rotation如果properties列表里根本没有rotation那说明驱动可能没有暴露这个属性这是第一个需要排查的点。3.2 调试DRM驱动与ION内存在调试过程中你可能会遇到图形库报错例如libgl error: failed to load driver: rockchip。这个错误通常不直接指向旋转问题但它揭示了更深层的图形栈问题。在Rockchip平台上这往往和ION内存分配器有关。ION是Android/Linux上用于图形、相机等模块的共享内存管理器。Rockchip的Mali GPU驱动和显示驱动对ION内存的类型heap type有要求。如果分配的内存类型不匹配比如GPU要求ION的rockchip_system_heap但分配的是cma_heap就会导致纹理上传失败进而引发各种奇怪的显示问题旋转失效也可能是其表现之一。排查步骤检查内核配置CONFIG_ION_ROCKCHIP是否启用。检查设备树中ion节点的配置确保rockchip,system-heap等heap被正确声明。通过cat /proc/ion/rockchip如果存在查看内存分配情况。在应用或HWC层确认分配缓冲区时请求的IONheap类型是否正确。实操心得我曾遇到副屏旋转后花屏的问题追查到底层发现是某个图层在旋转后使用了新的像素格式而该格式的缓冲区在错误的IONheap上分配导致GPU无法正常读取。解决方法是在HWC的validateDisplay阶段对需要旋转的图层强制指定其使用的gralloc内存标志位确保从正确的heap分配。3.3 验证内核旋转属性当驱动层面支持旋转后你可以直接通过sysfs来测试旋转是否生效这能绕过上层直接验证内核能力。# 假设副屏对应 card0-DSI-2 # 查看当前旋转值通常是 0 (正常), 1 (90度), 2 (180度), 3 (270度) cat /sys/class/drm/card0-DSI-2/rotation # 尝试设置旋转为90度 (注意可能需要root权限) echo 1 /sys/class/drm/card0-DSI-2/rotation设置后观察副屏的显示内容。如果屏幕物理方向变了但显示内容没有随之旋转说明驱动只改变了crtc的扫描方向但没有对帧缓冲区内容做变换。这需要驱动实现rotation property的atomic_set_property回调函数在其中处理旋转的变换矩阵。如果设置后屏幕直接黑屏或无信号可能是该显示接口的时序display-timings不支持当前旋转后的分辨率比如横屏时序用于竖屏分辨率。驱动中旋转相关的函数如drm_plane_helper_funcs.atomic_check和.atomic_update有bug对某些像素格式或缓冲区布局处理不当。4. 显示服务层HWC/SurfaceFlinger适配内核搞定后压力就来到了Android的硬件合成器Hardware Composer, HWC身上。Rockchip平台通常使用hwcomposer.rockchip这个HAL实现。4.1 解析HWC对旋转的处理流程HWC的核心工作之一就是决定每个图层Layer由谁GPU还是Overlay来合成以及如何合成。旋转信息在这里是关键参数。获取显示属性在prepare阶段HWC会通过drmModeGetConnector和drmModeObjectGetProperties等libdrm接口读取每个显示设备的属性其中就包括rotation。设置图层变换HWC需要将屏幕的旋转信息结合图层本身的变换比如应用设置的动画旋转计算出最终的变换矩阵并设置到DRM的平面Plane属性中通常是rotation和src_*/dst_*矩形。支持硬件旋转Overlay最理想的状况是旋转由显示控制器的硬件Overlay对应DRM Plane来完成这样不消耗GPU性能。Rockchip的VOPVideo Output Processor通常支持部分旋转功能如90/270度旋转。HWC需要判断当前图层是否可以被某个支持旋转的Overlay处理像素格式是否支持如果支持就将图层标记为HWC2::Composition::Device由硬件处理并设置相应的旋转属性。关键调试点查看HWC的调试日志。你需要打开HWC的详细日志通常通过setprop debug.hwc.log_level或修改HAL代码中的宏定义。在日志中搜索rotation、transform、plane等关键词看HWC是否成功读取到了副屏的旋转属性以及它是否尝试为图层设置旋转。4.2 常见HWC层旋转失效原因图层被强制GPU合成如果图层因为像素格式特殊如RGB565、带颜色空间转换、或者带有模糊等复杂效果HWC可能会判定Overlay无法处理将其降级为GPU合成HWC2::Composition::Client或GLES。GPU合成虽然也能处理旋转但需要应用或SurfaceFlinger的GLES后端来做如果链路没打通旋转就会失效。对策简化测试图层的属性如使用常见的RGBA8888格式无混合模式先确认硬件旋转通路是否畅通。Plane资源竞争VOP的Overlay Plane数量有限比如RK3568的某个VOP可能有2-3个Overlay Plane。当图层较多时HWC会优先将不需要旋转的图层分配给Overlay导致需要旋转的图层因为没有可用的Plane而被迫走GPU合成。对策调整HWC的图层分配策略或者优化UI减少同时显示的图层数量。旋转与缩放同时存在的BUG有些版本的驱动或HWC对同时进行旋转和缩放的变换处理存在bug。这会导致画面错乱。对策如果可能将旋转和缩放操作分开或者更新到修复了该问题的驱动/HWC版本。5. 应用层与窗口管理适配即使底层旋转正确应用看到的可能还是一个“错误”的世界。5.1 Android应用获取屏幕方向在Android多屏环境下应用通过Display.getRotation()来获取指定屏幕的旋转状态。这个信息来源于WindowManagerService而WindowManagerService又是从SurfaceFlinger最终来自HWC获取的。调试方法 写一个简单的测试应用在Activity中打印getDisplay().getRotation()的值。当你通过系统设置或底层命令改变副屏旋转时观察这个值是否实时变化。如果不变化问题可能出在SurfaceFlinger向WindowManagerService同步显示信息的过程。5.2 配置应用允许不同方向确保你的应用特别是需要显示在副屏上的应用在AndroidManifest.xml中配置了正确的screenOrientation属性。对于需要适配多方向的应用可以设置为fullSensor或sensor并处理好onConfigurationChanged回调。activity android:name.SecondaryScreenActivity android:screenOrientationfullSensor android:configChangesorientation|screenSize|smallestScreenSize /activity5.3 输入触摸旋转校正这是另一个大坑显示旋转了但触摸坐标没跟着转会导致点击位置完全错乱。触摸校正通常由输入子系统InputReader/InputDispatcher完成它需要知道每个触摸屏input device与哪个显示设备display id关联以及该显示设备的旋转矩阵。关联触摸屏与显示器在Android中通常通过InputReader的配置文件如idc文件或InputDeviceCalibrator或EventHub的规则将触摸设备的VID/PID或名称与一个特定的显示端口关联起来。例如在/system/usr/idc/目录下为副屏的触摸设备创建一个.idc文件其中指定touch.displayId。坐标变换关联成功后InputDispatcher会根据对应显示器的旋转角度对原始的触摸事件坐标进行旋转变换再分发给应用。你需要确认触摸设备是否正确关联到了副屏的display id。输入子系统是否正确获取到了副屏的旋转状态。可以通过getevent或dumpsys input命令来查看原始触摸事件和经过处理的事件对比坐标是否经过了正确的旋转。6. 系统级配置与调试命令汇总整个调试过程涉及多个层级以下是一些实用的命令和检查点可以帮助你快速定位问题所在。6.1 调试命令速查表层级命令/操作目的内核/DRMcat /sys/class/drm/card*/status查看显示连接器状态connected/disconnectedcat /sys/kernel/debug/dri/*/state详细DRM状态需要内核开启DEBUG_FSmodetest -M rockchipLibdrm测试工具可列出所有crtc/plane/connector及属性并直接设置模式与旋转强烈推荐echo 1 /sys/class/drm/card0-DSI-2/rotation直接测试内核旋转属性显示服务dumpsys SurfaceFlinger查看所有显示设备信息包括layerStack、orientationsetprop debug.hwc.log_level 7打开HWC详细日志值可能因版本而异logcat | grep -i hwc过滤HWC相关日志窗口系统dumpsys window displays查看系统所有显示器的详细信息包括rotationwm size和wm density查看和设置临时指定屏幕的分辨率和密度输入系统getevent -l查看原始输入事件包括设备名和原始坐标dumpsys input查看输入设备列表、与显示器的关联、事件处理策略6.2 使用modetest进行底层验证modetest是调试DRM驱动的神器。它可以绕过所有上层服务直接与内核DRM交互。# 1. 列出所有资源 modetest -M rockchip -p # 这会列出Connectors, Encoders, CRTCs, Planes。记下副屏Connector的ID比如128和一个可用的Plane ID。 # 2. 查看某个Connector的详细属性确认支持rotation modetest -M rockchip -P 128 # 3. 设置显示模式并测试旋转 # 首先找到一个合适的显示模式ID从-p输出中获取 # 假设connector id128, crtc id86, mode id为‘1920x1080’对应的模式索引比如69 # 使用一个支持旋转的plane比如plane id90 modetest -M rockchip -s 12886:1920x1080 -p 9086:1280x720AR24 -r 1 # 解释-s 设置connector 128使用crtc 86模式为1920x1080 # -p 设置plane 90在crtc 86上显示内容为1280x720的ARGB缓冲区并叠加到屏幕上 # -r 1 设置旋转为90度如果通过modetest设置旋转后屏幕显示内容能正确旋转说明内核驱动和硬件层面是OK的问题一定出在HWC或更上层。如果modetest设置旋转无效或黑屏那就要集中火力排查内核驱动。7. 实战问题排查实录从现象到根因这里分享两个我实际遇到并解决的典型案例。7.1 案例一副屏旋转后触摸狂跳现象副屏设置为90度旋转后图像显示正确但触摸操作时手指上下移动光标却左右移动完全错乱。排查过程确认显示旋转正确使用dumpsys SurfaceFlinger和dumpsys window displays确认副屏的orientation值已变为190度。检查触摸设备关联dumpsys input显示副屏的触摸设备event2确实关联到了副屏的display id。查看坐标变换对比getevent -l的原始坐标和logcat中应用收到的MotionEvent坐标。发现原始坐标ABS_MT_POSITION_X和ABS_MT_POSITION_Y值域是[0, 1920]和[0, 1080]横屏分辨率但应用收到的坐标值域却是[0, 1080]和[0, 1920]竖屏分辨率说明坐标被交换了。发现问题坐标确实被旋转处理了但处理方式不对。90度旋转不仅仅是X和Y交换还涉及到原点的映射。正确的变换应该是newX oldY; newY screenWidth - oldX。但当前系统似乎只做了交换。根因与解决问题出在输入子系统的触摸驱动校准矩阵input.h中的INPUT_PROP_DIRECT设备或idc配置文件。某些触摸驱动在报告坐标时已经假设屏幕是横屏的。当系统旋转屏幕时输入子系统应用了一个错误的变换矩阵。解决方法是在触摸驱动的idc文件中明确指定touch.orientationAware 1并确保驱动上报的坐标轴方向正确。更彻底的方法是在驱动代码或HAL层根据display-orientation动态计算并应用正确的旋转矩阵。7.2 案例二旋转导致特定格式图层花屏现象副屏旋转0度时一切正常。旋转90度后大部分UI正常但几个使用RGB565格式的背景图层出现花屏彩色条纹。排查过程缩小范围使用一个纯色、RGBA8888格式的测试应用旋转正常。问题锁定在RGB565格式或特定的图层内容。HWC日志分析打开HWC调试日志发现对于花屏的RGB565图层HWC日志显示“Plane XX cannot support rotation for format RGB565”。硬件限制确认查阅RK3568 TRM手册发现其VOP的Overlay Plane对RGB565格式的缓冲区在旋转时要求内存地址按某种特殊对齐比如64字节对齐。而当前gralloc分配的内存没有满足这个对齐要求。解决方案有两种思路。一是修改grallocHAL在分配用于旋转的RGB565缓冲区时使用特定的标志位或heap以满足硬件对齐要求。二是让HWC在遇到这种“无法硬件旋转的格式”时不要强制将该图层标记为Device合成而是降级为ClientGPU合成由GPU来完成旋转和格式转换。我们采用了第二种方案因为更通用。在HWC的validateDisplay逻辑中增加了对图层格式和变换组合的判断如果格式是RGB565且旋转非0度则强制将其类型设置为HWC2::Composition::Client。8. 总结与避坑指南调试Rockchip双屏旋转是一个典型的“牵一发而动全身”的系统工程。它要求你对从内核DRM、内存管理ION、显示合成HWC到窗口管理、输入子系统的整个链条都有清晰的了解。核心避坑点总结先底层后上层务必先用modetest等工具确认内核DRM驱动本身支持并可以正确设置旋转。这是所有工作的基石。内存是关键遇到奇怪的显示问题花屏、撕裂、libGL错误多从ION内存类型和对齐方面考虑。Rockchip平台对图形内存比较挑剔。HWC是枢纽它是连接DRM属性和Android图形系统的桥梁。它的日志是定位问题的金钥匙。务必学会查看和分析HWC的调试日志。触摸分离调试显示和触摸是两套系统。务必分别验证a) 显示旋转是否正确b) 触摸设备是否关联到正确的显示器c) 坐标变换矩阵是否正确。使用getevent和dumpsys input对比分析。简化测试环境在复杂UI上调试旋转问题如同大海捞针。创建一个全屏、单色、简单格式RGBA8888的测试应用能帮你快速隔离问题确定是通用性问题还是特定图层/格式的问题。关注硬件限制不是所有旋转组合角度格式缩放都能被硬件Overlay支持。仔细阅读芯片的TRM了解VOP/Overlay的能力限制并在HWC逻辑中做好降级处理的预案。最后保持耐心逐层分析。从/sys/class/drm开始到dumpsys SurfaceFlinger再到应用日志像侦探一样梳理证据链。每解决一个这样的深层系统问题你对整个Android图形栈的理解就会加深一层。这份经验远比仅仅让两个屏幕转起来更有价值。