4、DPU中断系统概述
DPU(Display Processing Unit)中断系统是连接显示硬件与内核驱动的重要桥梁。当显示控制器完成一帧扫描、检测到缓冲区欠载或发生配置错误时,硬件会触发相应的中断信号,驱动层通过注册的中断处理函数响应这些事件,从而完成帧同步、错误恢复和状态监控等关键任务。本章将系统梳理SA8155平台DPU中断的类型、中断控制器的层级结构以及中断注册的标准流程。
4.1 DPU中断类型
SA8155的DPU模块支持多种中断类型,按事件来源可分为同步类、缓冲区类和错误类三大类别。下表列出了主要的中断类型及其触发条件和典型用途:
| 中断类型 | 触发条件 | 典型用途 | 优先级 |
|---|---|---|---|
| VSYNC | 每帧扫描完成,垂直消隐期开始 | 帧同步、缓冲区交换、Fence信号释放 | 高 |
| UNDERFLOW | 显示控制器读取FIFO时数据不足 | 检测缓冲区饥饿、触发重填或降频 | 高 |
| OVERFLOW | 写入FIFO的数据超过容量 | 检测写入过快或DMA配置错误 | 中 |
| ERROR | 配置参数冲突或硬件状态异常 | 触发驱动错误恢复、打印寄存器状态 | 最高 |
| DMA_DONE | DMA传输完成 | 通知缓冲区数据已就绪 | 中 |
| PING_PONG | 双缓冲切换完成 | 确认缓冲区更新生效 | 中 |
4.2 中断控制器层级:MDP与DPU_INTF
SA8155平台的中断控制采用两级架构:顶层为MDP(Media Display Processor)中断控制器,底层为DPU_INTF(Display Processing Unit Interface)中断控制器。MDP负责汇总来自多个DPU_INTF实例的中断信号,并向CPU的GIC(Generic Interrupt Controller)上报;DPU_INTF则直接管理每个显示接口(如DP、DSI、HDMI)的物理中断源。
下图展示了中断控制器的层级关系与信号流向:
4.3 中断注册流程
在SA8155的DRM/KMS驱动中,DPU中断的注册遵循标准的Linux内核中断注册模式,但增加了与DRM框架的集成步骤。完整的注册流程包含以下五个阶段:
- 设备树解析:从设备树中读取中断控制器信息,获取MDP和DPU_INTF的中断号(IRQ number)及触发类型。
- 中断控制器初始化:初始化MDP中断控制器的寄存器基址、使能寄存器掩码和状态寄存器偏移。
- 中断处理函数注册:调用
devm_request_irq()或request_threaded_irq()注册顶半部和底半部处理函数。 - 中断使能与屏蔽配置:通过MDP控制器的使能寄存器,按需开启VSYNC、UNDERFLOW等中断类型。
- DRM事件绑定:将VSYNC中断与DRM的
drm_crtc_vblank机制绑定,实现帧同步回调。
以下是一个典型的中断注册代码示例,展示了在DPU驱动中注册VSYNC中断的核心逻辑:
/* dpu_irq.c - DPU中断注册示例 */
#include <linux/interrupt.h>
#include <drm/drm_vblank.h>
struct dpu_irq_data {
struct drm_device *drm;
struct dpu_mdp *mdp;
int irq_num;
};
/* 中断顶半部处理函数 */
static irqreturn_t dpu_irq_handler(int irq, void *dev_id)
{
struct dpu_irq_data *irq_data = dev_id;
struct dpu_mdp *mdp = irq_data->mdp;
u32 status;
/* 读取MDP中断状态寄存器 */
status = readl_relaxed(mdp->mmio + MDP_INTR_STATUS);
/* 检查VSYNC中断位 */
if (status & MDP_INTR_VSYNC_MASK) {
/* 清除中断标志 */
writel_relaxed(MDP_INTR_VSYNC_MASK,
mdp->mmio + MDP_INTR_CLEAR);
/* 通知DRM vblank事件 */
drm_crtc_handle_vblank(&irq_data->drm->crtc);
return IRQ_HANDLED;
}
/* 检查UNDERFLOW中断 */
if (status & MDP_INTR_UNDERFLOW_MASK) {
writel_relaxed(MDP_INTR_UNDERFLOW_MASK,
mdp->mmio + MDP_INTR_CLEAR);
dev_warn(mdp->dev, "DPU underflow detected!\n");
/* 触发错误恢复流程 */
schedule_work(&mdp->underflow_work);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
/* 中断注册函数 */
int dpu_irq_register(struct drm_device *drm, struct dpu_mdp *mdp)
{
struct dpu_irq_data *irq_data;
int ret;
irq_data = devm_kzalloc(mdp->dev, sizeof(*irq_data), GFP_KERNEL);
if (!irq_data)
return -ENOMEM;
irq_data->drm = drm;
irq_data->mdp = mdp;
irq_data->irq_num = platform_get_irq(mdp->pdev, 0);
/* 注册中断处理函数 */
ret = devm_request_irq(mdp->dev, irq_data->irq_num,
dpu_irq_handler,
IRQF_TRIGGER_HIGH | IRQF_SHARED,
"dpu_mdp", irq_data);
if (ret) {
dev_err(mdp->dev, "Failed to request IRQ %d\n",
irq_data->irq_num);
return ret;
}
/* 使能VSYNC和UNDERFLOW中断 */
dpu_mdp_intr_enable(mdp, MDP_INTR_VSYNC_MASK |
MDP_INTR_UNDERFLOW_MASK);
dev_info(mdp->dev, "DPU IRQ %d registered successfully\n",
irq_data->irq_num);
return 0;
}
- 中断处理函数中应避免调用可能导致睡眠的函数(如
mutex_lock、kmalloc),必要时使用request_threaded_irq将耗时操作移至底半部。 - VSYNC中断的清除操作必须在读取状态之后立即执行,防止同一中断被重复处理。
- UNDERFLOW中断处理中建议使用
schedule_work将恢复操作推迟到工作队列中执行。
4.4 中断与显示时序的关联
VSYNC中断是显示时序配置中最关键的同步信号。在SA8155平台上,DPU_INTF控制器在每帧的垂直消隐期(VBlank)开始时触发VSYNC中断,驱动通过该中断完成以下时序相关操作:
- 帧计数器更新:递增DRM的vblank计数器,为应用层提供精确的帧序号。
- 缓冲区交换确认:确认上一帧的缓冲区已消耗完毕,允许提交新缓冲区。
- Fence信号释放:释放等待该帧完成的同步Fence,唤醒渲染线程。
- 时序参数校验:在VSYNC中断中读取硬件状态,验证当前配置的时序参数(如HFP、HBP、VFP、VBP)是否与硬件实际扫描一致。
下图展示了VSYNC中断与显示时序的时间关系:
/sys/kernel/debug 接口导出中断统计信息。
4.5 中断调试与验证
在开发过程中,可以通过以下方法验证中断系统是否正常工作:
| 验证方法 | 操作步骤 | 预期结果 |
|---|---|---|
| 中断计数 | 读取 /proc/interrupts 查看DPU中断计数 |
每秒计数增加约60次(对应60Hz刷新率) |
| 时序验证 | 使用示波器或逻辑分析仪测量VSYNC引脚 | 脉冲间隔与配置的帧周期一致 |
| 错误注入 | 手动触发UNDERFLOW(如降低DMA带宽) | 中断处理函数打印警告并触发恢复 |
| DRM测试 | 运行 modetest -v 观察vblank事件 |
vblank序列号连续递增 |
dpu_irq_handler 中添加 atomic_inc 统计不同类型中断的触发次数,并通过debugfs导出,可以快速定位中断丢失或异常触发的问题。对于VSYNC中断,建议同时监控硬件寄存器中的帧计数器(Frame Counter)与软件维护的vblank计数器是否一致。