☰
MTK显示架构演进:从MDP到DDP的深度解析
2026/9/29 18:39:50 网站建设 项目流程

说到MTK的显示架构,很多做底层驱动或系统优化的朋友一定不陌生。早几年在MT6589、MT6753这些平台上追显示丢帧、花屏问题,入口基本都是MDP相关的那一套寄存器;到了MT6785、MT6895这类新平台,再看代码和调试节点,已经是DDP的天下了。这个从MDP到DDP的变化,不是简单的换个名字,而是MTK显示子系统在数据通路、模块划分和电源管理上的一次系统性重构。

这篇文章我想把这条演进路线完整拆一遍:先说明MDP和DDP分别解决什么问题,再展开新架构里OVL、RDMA、DSC这些模块是怎么协作的,接着给出一套实操验证方法,最后聊聊我们在维护老平台和适配新平台时踩过的一些坑。内容偏底层,但我会尽量用通俗的类比和实际日志来讲,适合刚接触MTK平台的驱动开发者,也适合做上层系统优化的同学用来建立全局认识。

1. 为什么显示架构需要一次“换心”:从MDP到DDP的演进动因

很多朋友第一次接触MTK显示子系统时,会被mdp、ddp、mtkfb、disp这一堆缩写搞晕。其实它们代表的是两代设计思路。早期MTK把显示相关的大部分任务收敛在一个叫MDP的模块里,全称可以理解为Media Data Path或Main Display Path,负责把内存中的图像数据搬到屏幕。这个设计在WVGA、720P时代完全够用,但到了FullHD+高刷、多摄像头同时预览、折叠屏多屏输出之后,单靠一个MDP去处理所有数据流就非常吃力。

1.1 老架构MDP解决什么问题

MDP时代的设计核心是“单通路优先”。它的任务很纯粹:从一个或者少数几个图层源读像素,做必要的格式转换,然后通过显示接口送到LCD/HDMI。你可以把它想成一根很粗的管子,图像数据从这头进去,从另一头出来,中间只做缩放和裁剪这类基本操作。

这套架构最大的优点是简单、可控。寄存器数量少,状态机清晰,出问题时直接看MDP中断状态就能定位。早期MTK平台上,Camera preview和Video playback的默认通路基本都是走MDP,因为它不需要复杂的合成逻辑,只需要把视频层叠在UI层上输出就行。

但缺点也非常明显:第一,图层数量一旦超过MDP能承载的上限(通常只有2到3层),多出来的图层就必须CPU/GPU合成到内存再送显,功耗高、延迟大;第二,MDP的带宽调度能力弱,面对高分辨率高刷新率时,行场同步很容易被内存访问竞争干扰,出现屏幕一边刷一边顿的现象;第三,扩展多屏输出非常笨拙,因为整个MDP通路天然是单显示接口的。

1.2 新架构DDP到底新在哪里

DDP,也就是Display Data Pipeline,是MTK在进入4K/高刷时代后重构的显示数据流方案。它不是把MDP换个名字,而是把原来“一个大模块”拆成了若干个小而专的硬件引擎,再通过一个可编程的流水线把它们串起来。每个引擎只干一件事:OVL做图层叠加,RDMA负责从内存读像素,WDMA负责把结果显示写回内存,AAL/GAMMA做色彩和亮度调节,DSI/DP负责物理链路输出。

这套架构最大的变化是“拼接式流水线”。不同使用场景可以组装不同引擎组合,比如普通UI显示是OVL->RDMA->DSI,而截图功能是WDMA独立把当前画面写回内存,视频增强场景可以额外加入PQ(Picture Quality)模块。每个模块通过标准接口互联,数据流动方向和时机由DDP管理器统一控制,和MDP那种“一根管子走到底”完全不一样。

从软件层面看,DDP催生了更清晰的驱动分层:底层是disp_*各个引擎驱动,中间是DDP manager负责时钟、电源、帧同步的统筹,上层才对接mtkfb或 DRM/KMS。这就是为什么新平台看代码时处处都能看到DDP_OVL、DDP_RDMA这样的抽象宏,而老平台则是一大坨MDP_*寄存器直接裸操作。对做系统优化的人来说,这意味着我们可以更精确地定位到每一帧的瓶颈出在哪个引擎上,而不是靠猜。

2. MTK显示数据通路的核心模块与工作流

要理解DDP,必须把数据通路的几个核心模块先吃透。我平时排查问题最常打交道的四个模块是OVL、RDMA、WDMA和DSI Display。下面我用一条HDMI输出的场景来串这四者,解释数据从内存到屏幕的完整路径。

2.1 从OVL到RDMA:数据如何从内存到屏幕

上游App通过SurfaceFlinger把图层提交给HWC(Hardware Composer),HWC经过策略比较,决定底层图层还是由硬件合成。硬件合成的入口就是OVL。OVL模块接收多个图层输入,每个输入channel可以配置不同的像素格式、起始地址、宽高和透明度。OVL内部做alpha blending和z-order排序,最终合成一张完整的画面。

注意:OVL虽然强大,但它的layer数量是有限的。中端平台通常是4层,旗舰平台可能到6层。如果UI图层超过这个数量,HWC会把其中一部分图层先由GPU合成,再作为一个图层送入OVL,这就是我们常说的GPU合成与硬件合成的临界点。排查性能问题时要先确认当前帧是不是越过了这个点。

合成完成后,数据还在“引擎内部”,接下来需要RDMA读取结果。更准确地说,OVL的结果会直接交给后端模块,而RDMA的作用是把画面数据从内存或者前端模块搬运到显示后端。RDMA的名字是Read DMA,它的核心职责是保证按像素时钟精准地从DDR搬数据,并且做好行缓冲,避免接口等数据。一些平台上RDMA还承担格式转换,比如ARGB8888转RGB565,这能减少后端接口的带宽压力。

然后数据到达DSI或DP Transmitter。DSI在手机上是主流,分D-PHY和C-PHY,通常有2到4条lane。这部分是物理层,关注的是时序参数,比如HSA、HBP、VFP、VBP。如果这些参数配错,屏幕会直接花屏或黑屏,而且这类问题不太容易从内核日志看到,得借示波器量。

2.2 模块之间的握手:帧同步与命令队列

DDP里的多个引擎不是各跑各的,它们通过帧同步机制(Frame Synchronization)紧密协作。每一帧开始,主时钟源产生VSYNC信号,OVL开始读图层,RDMA同步开始搬数据,DSI在TE(Tearing Effect)信号到来后启动输出。如果某个引擎没跟上,就会出现tearing或stutter。

MTK为了解决引擎速度不一致的问题,给每个引擎加了shadow register和command queue。驱动先把所有寄存器配置写到shadow register里,在VSYNC触发点统一加载到硬件寄存器。这样保证一帧内的所有配置是原子生效的,不会出现“RDMA已经是新配置,OVL还在跑旧配置”的半更新状态。调试时如果怀疑某个引擎配置没生效,建议先看它的shadow register状态位,确认是否处于pending状态。

另一个值得注意的机制是DDP的clock gating和bus QoS。每个引擎可以独立开关时钟,没有画面输出时自动gate掉。同时,DDP会向内存总线申请QoS带宽,确保和其他子系统(尤其GPU和Camera)竞争DDR时,显示通路有足够优先级。这条设计比MDP时代精细得多,但也带来一个新问题:QoS配置不合适反而会导致抢带宽,这个我们后面实操部分会讲。

3. 新旧架构切换带来的实际变化

这一章聊架构演进带来的可感知变化。带宽管理、功耗、多屏能力是最明显的三个维度。

3.1 带宽与功耗的权衡

MDP时代,显示路径的带宽基本“吃满”。因为它要在有限层面上把所有图层数据读一遍,再混合输出;一旦图层多了,带宽倍增。比如1080p@30fps,RGBA8888,一条显示通路带宽大约1920×1080×4×30≈248.8MB/s,MDP勉强扛住;到1080p@60fps就接近500MB/s,再加上GPU写回,DDR压力非常大。

DDP架构虽然也是要读图层,但它通过更智能的layer裁剪和像素格式压缩来降低带宽。最典型的是AFBC(Arm Frame Buffer Compression)的支持,UI图层在SurfaceFlinger阶段压缩,OVL读取时直接解压合成。这样同样一张1080p@60fps的画面,DDR到显示引擎的带宽可以减少一半以上。功耗也随之下降,毕竟显示通路在DDR总线上的读操作是耗电大户。

当然,DDP也带来额外的功耗来源:多个引擎的独立时钟树、总线和QoS电路。如果驱动不做动态电源管理,一直让所有引擎保持最高时钟,那功耗反而比MDP更差。所以新平台的显驱几乎都把suspend/resume和buffer状态挂钩,只有帧数超过阈值时才会把OVL/RDMA全部推到最高性能档。

3.2 多屏与折叠屏时代的新能力

MDP时代做双LCD或者LCD+HDMI扩展很痛苦,需要额外挂一整套外部显示控制器,两个屏幕之间没法共享合成结果。DDP架构直接用两条display pipeline,可以在每个pipeline里配置不同的OVL和RDMA资源,然后由同一个DDP manager做时钟和帧同步协调。

比如折叠屏的内屏和外屏,通常就是两个DSI接口接到同一个DDP上。铰链状态切换时,系统把显示内容从一个pipeline切换到另一个,动态改变OVL的图层配置和亮度gamma值,实现平滑过渡。这种场景在MDP下几乎是不可想象的,因为音频/传感器/显示要同步切换,没有一个统一的数据通路管理器很难做。

DDP还引入了secure display flow。在DRM PlayReady或Widevine L1播放场景下,视频内容不允许被CPU/GPU读取,MDP在这个问题上很吃力,因为它需要绕过系统内存做受保护路径。DDP则支持将OVL的某个图层标记为secure,直接由显示硬件从受保护内存读取并合成,不走普通DDR。这个能力对现在的流媒体体验至关重要。

4. 实操:在MTK设备上怎么快速识别当前显示架构与排查显示问题

理论聊完了,讲点落地的东西。作为系统工程师,拿到一台MTK设备,第一件事是搞清楚它跑的是MDP还是DDP,以及各个引擎的配置情况。下面是我常做的一套检查流程和问题排查方法。

4.1 通过内核节点和日志定位MDP/DDP版本

MTK平台的内核日志非常啰嗦,但也很诚实地暴露了显示架构。连接设备后先做三件事:

adb shell dmesg | grep -iE "mdp|ddp|disp_ovl|disp_rdma" adb shell cat /sys/kernel/debug/mtkfb/0 adb shell ls /sys/class/video/

第一行是看驱动初始化时打印的版本信息。如果是DDP平台,你会看到DDP开头的模块初始化序列,比如[DDP_OVL]、[DDP_RDMA];如果是老MDP平台,基本只能看到MDP和MTKFB相关的报错。第二行是查看Framebuffer当前的显示参数和输出路径,第三行是历史遗留的video设备节点,有些老项目会在/sys/class/video/下暴露FBI0、VBI0等节点,新DRM/DDP平台则更多走/sys/kernel/debug/dri/0/。

注意:新平台如果开了DRM,/sys/class/video可能完全不存在。不要只靠几个路径判断架构,最保险的是看dumpsys SurfaceFlinger里的HWC v2/v4能力,以及内核config里是否有CONFIG_MTK_DISP相关配置。

如果要看当前正在运行的显示流水线,可以读DRM调试节点:

adb shell cat /sys/kernel/debug/dri/0/state adb shell dumpsys display

state文件会列出每个plane的关联、格式、尺寸以及fb id,能直观看到硬件图层被分配给了哪几个plane。结合SurfaceFlinger的图层信息,就能判断当前UI帧是硬件合成还是GPU合成。

4.2 常见显示问题排查与优化建议

DDP架构下问题定位的路径更清晰,但坑也不少。我整理了几类高频问题,附上排查思路:

第一类:花屏或闪屏。优先查DSI时序和时钟,尤其是clk_get_rate是否低于pixel_clock * 1.05。DDP平台如果RDMA读不到数据也会闪,所以再查RDMA的underrun中断计数:

adb shell cat /sys/kernel/debug/mtkfb/0 | grep -i underrun

如果underrun一直增加,说明DDR带宽不足或QoS配置太低,需要调高RDMA的QoS level。

第二类:固定帧率掉到一半,比如60Hz变成30Hz。很大概率是DDP进入低功耗模式,帧率桶没对上。检查系统是否用了PSM(Panel Self Refresh)或者CABC,这两个特性在检测到静态画面时会降低刷新率。排查时可以临时关闭:

adb shell settings put global low_power_backlight 0 adb shell dumpsys display | grep -iE "refresh rate|display mode"

第三类:多屏串联时副屏卡顿。这种问题多半是DDP manager没有给副屏分配独立时钟域,导致副屏和主屏争抢RDMA带宽。可以查看两个pipeline的rate是否独立,如果共用,就调整dts里的clock property,给副屏单独一个PLL。

下面这张表是我常用的快速对照表:

现象可能原因快速验证方法
屏幕闪烁但日志干净DSI时序余量不足示波器量HBP/VFP,或加大porch参数观察
滚动时字体变形OVL layer数量超限,走到GPU合成dumpsys SurfaceFlinger看合成类型
屏幕局部一闪而过RDMA underrun查underrun中断计数
亮暗跳变AAL/GAMMA表刷新查看disp_aal寄存器状态
待机唤醒黑屏DDP pipeline 没正常resume`dmesg

4.3 手写一个DDP带宽估算小脚本

排查带宽问题,最基础的是先估算理论带宽。举个例子:一块1080p@60Hz的屏幕,像素格式RGB888,DDP读取图层时如果做双图层合成,那么OVL需要读两次全屏数据,RDMA输出一次全屏数据。读写总带宽大约是:

$$(1920 \times 1080 \times 3 \times 60) \times (2 + 1) = 1119.7MB/s$$

再考虑AFBC压缩到原来的一半,读取部分大约减半,最终大约746MB/s。这个数字要和DDR控制器带宽上限对比。现在中端DDR4的带宽通常有12.8GB/s以上,理论上不是瓶颈;但如果有GPU写回、Camera ISP同时抢带宽,就需要通过CPU的CCI或AXI QoS设置给显示通路预留带宽。

我平时写一个很小的Python脚本,用来快速估算多种格式下的带宽:

def calc_bandwidth(width, height, fps, bpp, layers, compression_ratio=1.0): frame_bytes = width * height * bpp // 8 raw_bandwidth = frame_bytes * fps * (layers + 1) # read layers + write output effective_bandwidth = raw_bandwidth / compression_ratio return effective_bandwidth / 1024 / 1024 # MB/s print(calc_bandwidth(1920, 1080, 60, 32, 2, compression_ratio=1.0)) print(calc_bandwidth(1920, 1080, 60, 32, 2, compression_ratio=2.0))

跑一下就能发现,在4K@60Hz、4图层、RGBA8888的场景下,哪怕是AFBC压缩,带宽压力也会接近1.4GB/s。如果设备在发热降频时DDR频率被压低,就很容易触发underrun。这就是为什么新平台都强制建议做AFBC或类似格式压缩。

5. 从MDP到DDP,给我们做系统优化的三点启示

按说我讲到这里可以直接收尾,但作为实际在MTK平台踩过不少坑的人,还是想说几句体会。如果你现在还在维护老MDP平台,或者刚切到DDP新平台,下面三点应该能帮上忙。

5.1 老平台维护:MDP还能再战吗

从功能上讲,老MDP平台只要不做高帧率、不多屏高清输出,日常使用仍然很稳。但有一个隐蔽问题:随着Android版本升级,SurfaceFlinger默认会增加一些新图层,比如圆角剪切、HDR色调映射,这些图层如果HWC不支持,就会在HWC层被强制拉回MDP合成,导致性能不升反降。维护老平台时,建议在HWC配置里明确声明支持的合成模式,宁可让GPU多干活,也不要让MDP在极限状态下反复重启通路。MDP的重启很伤画质,会出现明显的一帧闪黑。

5.2 新平台适配:拿到DDP之后先做什么

拿到一台DDP新平台的设备,我会建议从上电开始先做三件事:第一,抓一份完整的显示初始化dmesg并保存,这个基线日志比任何文档都准;第二,把每个引擎的独立clock全部列出来,确认哪些是在运行时可以动态开关的;第三,验证low power mode的进入退出路径,确保在屏保或者AOD(Always On Display)场景下,OVL和RDMA可以彻底gate掉,只保留最小的DSI-on到家。这些做完,后面调试帧率、功耗会事半功倍。

5.3 显示架构之外:MTK与高通的对比视角

顺带提一句,很多人喜欢拿MTK和高通比较。从显示架构看,两者方向相同,都在做模块化流水线和带宽QoS,但实现细节差异很大。高通的显示子系统叫MDSS,在Linux内核里通过DPU驱动管理,和MTK的DDP类似,都是多引擎组合。区别在于高通更早把显示驱动并入DRM框架,而MTK的DDP目前还保留不少私有接口,适配时要花一些精力把私有调用桥接到标准KMS。理解这一点,就不会在换平台时对着代码愣神了。

我个人在实际工作中最大的感触是:架构演进解决的是“能做什么”的问题,而真正的差距在“怎么用好它”。MDP到DDP这条路,表面是模块更丰富、带宽更高,本质是把复杂性从硬件端子转移到了软硬协同的管理层。无论平台怎么变,搞清楚每一帧数据从哪来、在哪个引擎停留多久、最终如何送到屏幕,永远是显示链路调试的核心。希望这篇梳理能让你少走些弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询