ARTICLE DETAIL

资讯详情

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

高通DPU与DRM/KMS显示链路解析:从竖屏改横屏调试事故说起

高通DPU与DRM/KMS显示链路解析:从竖屏改横屏调试事故说起 1. 从一次竖屏改横屏的调试事故说起去年帮一个做工业手持终端的团队排查显示异常设备用的是高通某代SoC屏幕原生竖屏但客户要求系统跑横屏UI。他们改了设备树里的panel时序参数又在内核启动参数里加了旋转角度结果开机后画面是横过来了但触摸坐标完全对不上而且第二路MIPI DSI输出直接黑屏。折腾了三天没搞定最后找到我。这个问题的根子不在参数本身而在于他们没搞清楚高通Adreno DPU这套显示架构里旋转到底发生在哪一层、DRM/KMS怎么把两路DSI当成一个逻辑显示来管理、以及MIPI DSI的时序配置和DPU内部的图层混合器是什么关系。这三个问题串起来就是一条从应用层到物理屏幕的完整链路。这篇内容我打算把这条链路从头到尾拆一遍。适合谁看做高通平台BSP的驱动工程师、做嵌入式显示调试的底层开发者、以及需要理解Android显示合成链路的系统工程师。如果你只是写上层App这篇可能偏底层但理解DPU和DRM/KMS的分工对你排查花屏、撕裂、掉帧这类问题同样有帮助。先给一个全局认知高通平台的显示子系统DPUDisplay Processing Unit是硬件DRM/KMS是内核软件框架MIPI DSI是物理传输接口。三者是引擎—变速箱—传动轴的关系。很多人调试时只盯着DSI时序改却忽略了DPU内部的图层合成和DRM的原子提交机制这就是为什么改了参数还是出问题。2. DPU在整条显示链路里到底管什么2.1 DPU不是GPU别把两者混为一谈刚接触高通平台的人最容易犯的错就是把DPU当成GPU的一部分。实际上在高通的架构里Adreno GPU负责3D渲染和通用计算DPU负责显示后端的图层合成、缩放、色彩处理和时序生成。两者是独立的硬件模块通过内存里的framebuffer交换数据。打个比方GPU是厨房里做菜的厨师DPU是传菜员加摆盘师。厨师把菜做好放在窗口framebuffer传菜员负责把多道菜按顺序摆到托盘上图层合成再按客人的用餐节奏端出去时序输出。你改屏幕旋转改的是摆盘方式不是做菜方式。DPU内部大致分几个关键模块Layer Mixer图层混合器把多个图层按Z-order和alpha混合成一个合成帧。高通通常有多个LM每个LM可以驱动一路显示。Scaler缩放器对图层做上下采样支持不同分辨率的图层合成到同一输出。DSCDisplay Stream Compression显示流压缩高分辨率高刷新率场景下用来降低DSI带宽压力。Timing Engine时序引擎生成HSYNC、VSYNC、DE等时序信号直接对接MIPI DSI控制器。DSI Controller把并行的像素数据串化成MIPI DSI的差分信号。理解这个分工很重要。当你遇到画面颜色不对可能是DPU的色彩矩阵配置问题遇到画面撕裂可能是Timing Engine和GPU的fence同步问题遇到某一路DSI不亮可能是LM和DSI的绑定关系没配对。2.2 高通DPU的版本演进与命名坑高通的DPU在不同代SoC上叫法不一样这是调试时的一个大坑。早期叫MDSSMobile Display SubSystem后来拆分成DPU。具体版本号在设备树里通常体现为qcom,sde-*这样的compatible字符串SDE就是Snapdragon Display Engine的缩写。SoC代际显示子系统名称设备树compatible典型LM数量较早世代MDSSqcom,mdss-*2-3中代SDE/DPUqcom,sde-*3-4较新世代DPUqcom,dpu-*4这个表格不是让你背而是提醒你拿到一个高通平台第一件事是确认DPU版本和LM数量。因为LM数量直接决定了你能同时驱动几路独立显示以及能否做双屏异显。很多双屏项目失败就是因为没确认LM资源够不够。我踩过一次坑某平台标称支持双屏但实际只有2个LM其中1个被主屏占用副屏只能用剩下的1个LM结果副屏无法做图层叠加只能直接输出单层framebuffer。客户要求副屏也显示通知栏叠加层硬件上就做不到最后只能软件合成性能掉了一半。2.3 为什么DPU的配置必须走设备树DPU的硬件资源LM、DSI、PHY、时钟都是SoC内部固定的不像USB设备可以热插拔。所以高通的方案是全部通过设备树静态描述内核启动时解析设备树把DPU的各个模块实例化并建立连接关系。设备树里几个关键节点mdss { status okay; }; mdss_dsi0 { status okay; qcom,dsi-ctrl-num 0; qcom,dsi-phy-num 0; qcom,dsi-select-clocks mux_byte_clk0, mux_pixel_clk0; ports { port0 { dsi0_in: endpoint { remote-endpoint dpu_intf1_out; }; }; }; }; dpu_intf1_out { remote-endpoint dsi0_in; };这段设备树的核心是remote-endpoint它把DPU的INTF1输出和DSI0的输入连起来。如果你改屏幕或者加第二路DSI必须同步修改这个连接关系否则内核里DRM的encoder和connector就建立不起来屏幕自然不亮。提示设备树里DSI和DPU的绑定关系是硬连接不是运行时动态分配的。改硬件方案时先画一张LM→INTF→DSI→PHY→Panel的连接图再对照设备树逐个核对。3. DRM/KMS框架如何接管DPU硬件3.1 DRM/KMS的四个核心对象DRMDirect Rendering Manager是Linux内核的显示框架KMSKernel Mode Setting是其中的模式设置部分。高通DPU的驱动就是注册到DRM框架下的一个platform driver。理解DRM关键是搞清四个对象CRTC对应DPU的一个LM加Timing Engine负责扫描输出。你可以理解为一路显示控制器。Encoder把CRTC输出的像素数据转换成DSI能接受的格式对应DPU的INTF。Connector代表物理连接器对应DSI接口和Panel。Plane图层对应DPU的图层混合输入。这四个对象的关系是Plane挂在CRTC上CRTC通过Encoder连到Connector。用户空间通过atomic ioctl提交一次显示配置内核DRM框架校验后下发给DPU驱动DPU驱动再写硬件寄存器。3.2 竖屏改横屏旋转到底在哪一层做回到开头那个事故。竖屏改横屏理论上可以在三个地方做旋转应用层旋转Android的SurfaceFlinger做合成时旋转GPU参与性能开销大。DPU硬件旋转DPU的图层混合器支持90/180/270度旋转不占GPU但需要硬件支持。Panel扫描方向旋转改Panel的扫描方向寄存器让物理扫描从横屏方向开始。那个团队犯的错是在设备树里改了Panel时序又在启动参数里加了旋转相当于在两层同时做了旋转结果触摸坐标和显示坐标的变换矩阵对不上。正确的做法是只在一层做旋转并且同步更新触摸驱动的坐标变换。高通DPU的硬件旋转能力体现在DRM的plane属性里。你可以通过drmModeObjectSetProperty设置rotation属性。但要注意不是所有DPU版本都支持任意角度的硬件旋转有些只支持0和180度90/270度需要走GPU或者软件。// 设置plane旋转属性的示意 drmModeObjectSetProperty(fd, plane_id, DRM_MODE_OBJECT_PLANE, rotation_prop_id, DRM_MODE_ROTATE_90);实测下来如果DPU支持硬件旋转横竖屏切换的功耗和帧率表现明显优于GPU旋转。但前提是你要确认DPU的capability可以通过DRM_CLIENT_CAP_ATOMIC和DRM_CLIENT_CAP_UNIVERSAL_PLANES查询。3.3 Atomic Commit一次显示配置的完整旅程现代DRM驱动都走atomic路径。一次屏幕更新从用户空间到硬件大致经历这些步骤用户空间准备drmModeAtomicReq添加plane、crtc、connector的属性。调用drmModeAtomicCommit带上DRM_MODE_ATOMIC_TEST_ONLY先做测试。内核DRM框架调用各驱动的atomic_check校验资源冲突、带宽、时钟。测试通过后再提交一次不带TEST_ONLY的commit。DPU驱动的atomic_flush被调用写硬件寄存器。硬件在下一个VSYNC生效通过fence通知用户空间。这个流程里最容易出问题的是atomic_check阶段的带宽校验。高通DPU驱动会计算当前配置需要的DSI带宽、DPU时钟、内存带宽如果超了就直接拒绝commit。很多改了分辨率后屏幕不亮的问题其实是带宽校验没过但日志级别不够看不到。注意调试DRM问题时把内核日志级别调到7并且打开CONFIG_DRM_MSM_DEBUG能看到atomic_check的详细拒绝原因。这个信息比盲目改时序有用得多。4. MIPI DSI这条物理链路的关键细节4.1 DSI的Lane配置不是越多越好MIPI DSI用差分Lane传输数据常见配置是1 clock lane 1/2/4 data lane。很多人以为Lane越多越好实际上Lane数量要和分辨率、刷新率、色深匹配多了浪费功耗少了带宽不够。带宽估算公式所需带宽 水平像素 × 垂直像素 × 刷新率 × 色深 × 开销系数以1080×2340、90Hz、24bpp为例1080 × 2340 × 90 × 24 ≈ 5.46 Gbps4 lane DSI在1.5Gbps/lane下能提供6Gbps刚好够。如果换成120Hz就需要更高的lane速率或者DSC压缩。这个计算必须在选屏阶段就做不能等调试时才发现带宽不够。4.2 DSI时序参数里最容易配错的几个DSI的时序参数分两块视频模式时序HSYNC、VSYNC、HFP、HBP、VFP、VBP和DSI协议参数LPX、HSX、LP11等。前者决定画面位置后者决定信号完整性。参数含义配错的后果hfp/hbp水平前后沿画面左右偏移或压缩vfp/vbp垂直前后沿画面上下偏移或滚动hsync/vsync同步脉冲宽度画面抖动或不同步lp11LP11持续时间低功耗模式切换失败hsxHS准备时间高速传输误码这些参数必须严格按Panel规格书填写不能凭经验猜。我见过有人把hfp和hbp写反结果画面整体偏移半个屏幕还以为是DPU的问题。4.3 RGB到MIPI DSI的数据流转换Panel规格书里经常写RGB接口或MIPI DSI接口这指的是Panel端的接收接口。如果Panel是RGB接口而SoC输出的是DSI中间就需要一颗DSI转RGB的桥接芯片比如常见的转换IC。这个转换链路里桥接芯片的配置往往被忽略。它需要正确的上电时序、I2C初始化、以及和DSI的同步。很多DSI有信号但屏幕不亮的问题最后查出来是桥接芯片没初始化。数据流大致是DPU Layer Mixer → INTF → DSI Controller → DSI PHY → 差分信号 → 桥接芯片 → RGB → Panel每一级都可能有坑。DSI PHY的驱动强度、预加重、时序参数都会影响信号质量。如果走线长或者有干扰可能需要用示波器看眼图来调。5. 双路DSI与多屏显示的绑定逻辑5.1 什么场景需要双路DSI双路DSI有两种典型用法双屏异显两块独立屏幕显示不同内容。比如折叠屏手机的内外屏或者车载的中控加仪表。单屏双路一块高分辨率屏幕用两路DSI同时驱动提高带宽。比如4K屏用两路DSI各驱动一半。这两种用法在DPU配置上完全不同。双屏异显需要两个独立的LM和两个CRTC单屏双路则是一个LM输出到两个DSI或者两个DSI拼接。5.2 设备树里双路DSI的绑定双路DSI的设备树关键是两个DSI节点都要有正确的endpoint连接并且LM的分配要明确。mdss_dsi0 { status okay; ports { port0 { dsi0_in: endpoint { remote-endpoint dpu_intf1_out; }; }; }; }; mdss_dsi1 { status okay; ports { port0 { dsi1_in: endpoint { remote-endpoint dpu_intf2_out; }; }; }; };这里INTF1和INTF2分别对应两个LM。如果两个DSI连到同一个INTF那就是单屏双路模式需要Panel驱动里做特殊处理。5.3 双屏异显的常见坑双屏异显最容易出的问题是时钟资源冲突。两路DSI需要各自的byte clock和pixel clock如果时钟树配置不当第二路会拿不到时钟表现为第二屏不亮或者闪屏。另一个坑是内存带宽。两个屏同时刷新DPU从DDR读framebuffer的带宽翻倍。如果DDR带宽不够会出现两个屏都掉帧。这个要在系统设计阶段用带宽计算工具评估不能等调试时才发现。我处理过一个车载双屏项目主屏1920×720副屏1280×720都是60Hz。理论上带宽够但实测副屏偶尔撕裂。最后查出来是两个CRTC的VSYNC没有对齐导致DPU在同一个时刻要处理两个屏的framebuffer交换瞬时带宽峰值超标。解决办法是把两个屏的刷新率错开或者用fence机制做更精细的同步。6. 调试实战从黑屏到点亮的排查链路6.1 黑屏问题的分层排查法屏幕不亮可能的原因从下到上有很多层。我的排查顺序是从物理层往上查供电和背光先量Panel的VDD、VSP、VSN、背光使能。这一步用万用表就能做别急着看代码。时钟和复位量DSI PHY的参考时钟、Panel的复位信号。没有时钟后面都是白搭。DSI信号用示波器或者MIPI分析仪看DSI差分线上有没有信号。有信号但屏幕不亮问题在Panel或桥接芯片。DPU输出如果DSI没信号查DPU的INTF有没有输出。可以通过读DPU寄存器确认。DRM配置如果DPU没输出查DRM的atomic commit有没有成功。看内核日志。设备树如果DRM配置失败查设备树的连接关系和资源分配。这个顺序的核心逻辑是先排除硬件问题再查软件。很多人一上来就改代码结果查了半天发现是排线没插好。6.2 用sysfs和debugfs看DPU状态高通DPU驱动会在debugfs里暴露一些状态信息路径通常在/sys/kernel/debug/dri/0/下。可以看的包括dpms当前显示电源状态。msm_dpuDPU的寄存器dump。dsi0/1DSI控制器的状态。# 查看当前CRTC状态 cat /sys/kernel/debug/dri/0/crtc-0/state # dump DPU寄存器 cat /sys/kernel/debug/dri/0/msm_dpu/regs这些信息能帮你确认DPU有没有在正常工作。如果寄存器全是0说明DPU根本没上电或者时钟没开。6.3 一个真实的时序调试案例有个项目屏幕能亮但画面每隔几秒闪一下。查了供电、时钟、DSI信号都正常。最后用示波器抓VSYNC信号发现VSYNC周期偶尔会多出一个脉冲。根因是Panel规格书里的vfp参数写的是最小值而实际DPU输出的vfp在某种情况下会小于这个值导致Panel误判为新的一帧。把vfp改成规格书的典型值后问题消失。这个案例的教训是时序参数不要用最小值要用典型值。最小值只在特定条件下成立系统运行时的动态变化可能突破这个边界。7. 性能与功耗的平衡取舍7.1 DSC压缩什么时候该开DSCDisplay Stream Compression能在视觉无损的前提下把DSI带宽降低到原来的1/3左右。高分辨率高刷新率场景下开DSC是必须的。但DSC也有代价增加编解码延迟且不是所有Panel都支持。判断要不要开DSC看两个条件DSI带宽是否够用。不够就必须开。Panel是否支持DSC解码。不支持就不能开。如果两个条件都满足还要评估延迟是否可接受。游戏场景对延迟敏感DSC的几毫秒延迟可能有影响。7.2 图层合成的性能优化DPU的图层混合器能同时处理的图层数量有限。如果应用提交的图层数超过硬件能力DRM会触发GPU合成性能和功耗都会变差。优化思路是尽量减少图层数量不透明的全屏图层可以合并。静态内容可以提前合成到framebuffer。用DRM_MODE_ATOMIC_TEST_ONLY提前测试图层配置是否可行。Android的SurfaceFlinger会根据DPU能力做合成策略选择。如果发现GPU合成占比高可以查SurfaceFlinger的日志看是哪个图层导致的。7.3 低功耗模式下的显示保持息屏显示AOD场景下DPU需要进入低功耗模式只保持部分图层刷新。高通DPU支持命令模式Panel可以把一帧内容存在Panel的GRAM里DPU停止扫描输出只定期唤醒刷新。这个模式的配置涉及DSI的命令模式参数和Panel的GRAM管理。配不好会出现AOD闪烁或者功耗降不下来。关键参数是TETearing Effect信号的配置它决定DPU什么时候可以安全地更新GRAM。8. 我个人在实际调试中的几点体会做高通显示调试这些年最大的感受是文档永远滞后于硬件很多细节只能靠实测和反汇编。高通给的显示驱动文档通常只讲框架不讲具体寄存器。真正调试时要么看内核源码要么用调试工具dump寄存器对比。第二个体会是设备树是显示调试的主战场。80%的显示问题根因都在设备树的连接关系、时序参数、资源分配上。改代码之前先把设备树捋清楚。第三个体会是分层排查比盲目试错高效得多。从供电到DSI到DPU到DRM一层层往上查每层都有明确的验证手段。最怕的是一上来就改参数改到最后自己都不知道改了什么。最后分享一个实用技巧建立自己的显示调试检查清单。每次遇到新问题按清单过一遍能快速定位到大概范围。我的清单包括供电电压、时钟频率、复位时序、DSI Lane数、时序参数、设备树连接、DRM日志、DPU寄存器。这个清单帮我省了大量时间也推荐你根据自己的平台整理一份。
返回列表