第2章 显示子系统框架:MDP架构、DSI/DP/eDP接口与显示时钟树
各位同学,咱们今天聊聊MTK8678的显示子系统。说实话,这块内容我当年刚接触时也绕了不少弯路。显示子系统说白了就是手机SoC里负责把图像数据从内存搬到屏幕上的那套硬件逻辑。你想想看,从APP生成一帧画面,到最终在屏幕上亮起来,中间要经过多少道工序?嗯,这就是咱们今天要拆解的核心。
2.1 MDP(多媒体显示处理器)架构
MDP,全称是Multimedia Display Processor。我个人习惯把它理解成「显示流水线的总调度中心」。它不负责具体的像素渲染,而是负责把各路图像数据(比如GPU渲染的、视频解码器输出的、Camera预览的)进行合成、缩放、旋转、叠加,最后输出给显示接口。
MTK8678的MDP架构,我画个简化的框图给你看:
+------------------+ +------------------+
| GPU渲染帧 | | 视频解码帧 |
| (内存中的Buffer) | | (内存中的Buffer) |
+--------+---------+ +--------+---------+
| |
v v
+------------------+ +------------------+
| Layer 0 | | Layer 1 |
| (主图层) | | (视频图层) |
+--------+---------+ +--------+---------+
| |
+----------+-------------+
|
v
+-----------------------+
| MDP 合成引擎 |
| (硬件合成器) |
+-----------+-----------+
|
v
+-----------------------+
| 显示接口控制器 |
| (DSI/DP/eDP) |
+-----------------------+
这里有个关键点:MDP内部有多个硬件图层(Layer),每个图层可以独立配置位置、大小、透明度、旋转角度。我在项目中遇到过一个问题:客户要求画中画显示,一个视频窗口悬浮在游戏画面上。如果只用软件合成,帧率直接掉到20fps。后来我改成用MDP的两个硬件图层叠加,帧率稳稳的60fps。这就是硬件加速的价值。
核心要点:MDP的硬件图层数量是有限的。MTK8678通常支持4-6个硬件图层。超过这个数量,系统会自动回退到GPU软件合成,性能会下降。所以设计UI时,图层数量要心里有数。
2.2 DSI/DP/eDP接口介绍
MDP合成完图像后,需要把数据送到屏幕。这就涉及到显示接口了。MTK8678支持三种主流接口:DSI、DP、eDP。我一个个说。
2.2.1 DSI(Display Serial Interface)
DSI是手机和平板上最常用的接口。它基于MIPI协议,采用差分信号传输,抗干扰能力强。说白了,就是一根排线里走高速串行数据。
DSI有几个关键参数:
- Lane数量:通常1-4 Lane。4 Lane的带宽最高。
- 速率:每Lane可达1.5Gbps甚至更高。MTK8678支持到2.5Gbps per Lane。
- 工作模式:Command Mode(命令模式,带帧缓冲)和Video Mode(视频模式,实时刷新)。
我记得有一次调试一个1080p@60fps的屏幕,死活刷不出来。查了半天,发现是DSI的时钟配置错了。DSI的时钟计算公式是这样的:
DSI时钟频率 = (水平像素数 + HBP + HFP + HSA) * (垂直像素数 + VBP + VFP + VSA) * 刷新率 * 每像素位数 / (Lane数 * 2)
嗯,这个公式我建议你记下来。实际项目中,屏幕的时序参数(HBP、HFP等)都在屏幕的规格书里。拿到规格书第一件事,就是把这些参数填进去算一遍。
小技巧:调试DSI时,先用示波器量一下时钟线(CLK Lane)的频率。如果频率不对,多半是PLL配置或者分频系数算错了。我习惯在驱动里加个打印,把计算出的目标频率和实际锁相环频率都打出来对比。
2.2.2 DP(DisplayPort)
DP接口主要用于外接显示器、电视或者扩展坞。MTK8678的DP支持eDP(嵌入式DP)和标准DP两种模式。eDP通常用于笔记本的内屏,标准DP用于外接。
DP的优势是带宽高、支持长距离传输。MTK8678的DP支持到4K@60fps,HDR也没问题。
DP的链路训练(Link Training)是个容易出坑的地方。链路训练说白了就是主机和显示器之间协商速率、Lane数、电压摆幅等参数。我曾经遇到一个兼容性问题:某品牌的显示器在链路训练时总是失败,导致黑屏。后来发现是显示器的EDID(扩展显示标识数据)里写了一个不支持的速率组合。解决办法是在驱动里强制指定一个兼容的速率。
// 强制DP链路训练参数示例
static struct dp_link_config forced_link_cfg = {
.lane_count = 2, // 强制使用2 Lane
.rate = DP_LINK_RATE_5_4G, // 5.4Gbps per Lane
.voltage_swing = 0, // 低电压摆幅,兼容性更好
};
2.2.3 eDP(嵌入式DisplayPort)
eDP本质上是DP的嵌入式版本,主要用于笔记本和平板的内屏。它和DP的区别在于:eDP通常走的是板内走线,距离短,功耗优化更好。MTK8678的eDP支持PSR(面板自刷新)功能,可以降低静态画面时的功耗。
PSR的原理很简单:当画面静止时,eDP把最后一帧存在屏幕的缓冲里,然后关闭主链路。屏幕自己刷新缓冲里的内容。等画面变化时再唤醒主链路。这个功能在阅读场景下特别省电。
注意:PSR功能需要屏幕硬件支持。不是所有eDP屏幕都支持PSR。另外,PSR的唤醒延迟一般在几毫秒到十几毫秒之间。如果应用对延迟敏感(比如游戏),建议关闭PSR。
2.3 显示时钟树
显示时钟树,说白了就是给MDP、DSI、DP这些模块提供时钟的「水管网络」。每个模块需要不同频率的时钟,而且这些时钟之间还有相位关系。时钟树设计不好,显示就会出各种诡异问题:闪屏、花屏、不同步。
MTK8678的显示时钟树大致如下:
+-------------------+
| 主PLL (26MHz) |
| (晶振输入) |
+--------+----------+
|
v
+-------------------+
| DPLL (显示PLL) |
| 输出: 594MHz |
| (用于4K@60fps) |
+--------+----------+
|
+------------------+------------------+
| | |
v v v
+----------------+ +----------------+ +----------------+
| MDP Core Clock | | DSI PHY Clock | | DP PHY Clock |
| (300-600MHz) | | (根据分辨率) | | (根据链路速率) |
+----------------+ +----------------+ +----------------+
这里有个关键点:DSI的PHY时钟和MDP的核心时钟必须满足一定的比例关系。否则会出现「帧不同步」的问题。我举个例子:
- MDP核心时钟决定了一秒钟能处理多少像素。
- DSI PHY时钟决定了数据从SoC传到屏幕的速率。
- 如果MDP处理得太快,DSI传得太慢,缓冲区就会溢出,出现撕裂(Tearing)。
- 反过来,MDP太慢,DSI太快,屏幕就会反复读取同一帧,造成卡顿。
解决这个问题的标准做法是使用「垂直同步(VSync)」机制。VSync信号由屏幕或者DSI控制器产生,MDP根据VSync的节奏来送帧。MTK8678的MDP内部有一个VSync生成器,可以配置成内部模式或者外部模式。
避坑指南:我曾经在一个项目里,为了省电把MDP核心时钟降到了200MHz。结果发现播放4K视频时,画面每隔几秒就卡一下。用trace工具一抓,发现MDP处理一帧的时间超过了VSync间隔(16.67ms)。说白了就是MDP「忙不过来」。解决办法是把MDP时钟调到400MHz以上。所以,时钟频率不是越低越好,要满足帧率要求。
最后,我建议你在调试显示驱动时,先把时钟树的所有节点频率都打印出来。MTK的调试接口一般会提供这样的命令:
// 查看显示时钟状态(伪代码)
cat /sys/kernel/debug/clk/clk_summary | grep "disp"
// 输出示例:
// disp_mdp_clk 400000000 // MDP核心时钟 400MHz
// disp_dsi0_phy_clk 1500000000 // DSI0 PHY时钟 1.5GHz
// disp_dp_phy_clk 540000000 // DP PHY时钟 540MHz
嗯,看到这些数字,你就能快速判断时钟配置是否合理。如果某个时钟频率是0,那说明对应的模块没使能,或者PLL没锁住。
好了,这一章的内容就到这里。显示子系统框架是后面所有驱动开发的基础。你把这些接口和时钟树搞清楚了,后面写MDP驱动、DSI驱动、DP驱动时就会顺手很多。下一章咱们开始实战,写一个最简单的MDP驱动,点亮一块屏幕。