4、DPU中断系统概述

DPU(Display Processing Unit)中断系统是连接显示硬件与内核驱动的重要桥梁。当显示控制器完成一帧扫描、检测到缓冲区欠载或发生配置错误时,硬件会触发相应的中断信号,驱动层通过注册的中断处理函数响应这些事件,从而完成帧同步、错误恢复和状态监控等关键任务。本章将系统梳理SA8155平台DPU中断的类型、中断控制器的层级结构以及中断注册的标准流程。

核心要点:DPU中断系统是显示驱动实现垂直同步(VSYNC)、缓冲区管理和错误恢复的基础设施,理解中断类型与注册流程是后续配置显示时序的前提。

4.1 DPU中断类型

SA8155的DPU模块支持多种中断类型,按事件来源可分为同步类、缓冲区类和错误类三大类别。下表列出了主要的中断类型及其触发条件和典型用途:

中断类型 触发条件 典型用途 优先级
VSYNC 每帧扫描完成,垂直消隐期开始 帧同步、缓冲区交换、Fence信号释放 高
UNDERFLOW 显示控制器读取FIFO时数据不足 检测缓冲区饥饿、触发重填或降频 高
OVERFLOW 写入FIFO的数据超过容量 检测写入过快或DMA配置错误 中
ERROR 配置参数冲突或硬件状态异常 触发驱动错误恢复、打印寄存器状态 最高
DMA_DONE DMA传输完成 通知缓冲区数据已就绪 中
PING_PONG 双缓冲切换完成 确认缓冲区更新生效 中
开发提示:VSYNC中断是显示驱动中最频繁触发的中断类型(通常每秒60次),其处理函数必须高效,避免在中断上下文中执行耗时操作。UNDERFLOW中断一旦出现,通常意味着系统带宽不足或DMA配置不合理,需要优先排查。

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)的物理中断源。

下图展示了中断控制器的层级关系与信号流向:

DPU中断控制器层级架构 DPU中断控制器层级架构 CPU / GIC MDP 中断控制器 汇总/路由/屏蔽 DPU_INTF #0 DP / DSI 0 DPU_INTF #1 DP / DSI 1 DPU_INTF #2 HDMI / eDP VSYNC / UNDERFLOW VSYNC / UNDERFLOW VSYNC / ERROR 说明:MDP控制器统一管理所有DPU_INTF的中断使能/屏蔽/状态,DPU_INTF负责具体接口的物理中断信号采集 每个DPU_INTF实例对应一个显示接口,中断信号经MDP汇总后上报GIC
架构理解:MDP中断控制器相当于“中断路由器”,它接收来自各个DPU_INTF的中断请求,进行优先级仲裁和使能控制后,统一向GIC提交中断。这种两级设计使得驱动可以独立控制每个显示接口的中断行为,同时保持顶层中断号的稳定性。

4.3 中断注册流程

在SA8155的DRM/KMS驱动中,DPU中断的注册遵循标准的Linux内核中断注册模式,但增加了与DRM框架的集成步骤。完整的注册流程包含以下五个阶段:

  1. 设备树解析:从设备树中读取中断控制器信息,获取MDP和DPU_INTF的中断号(IRQ number)及触发类型。
  2. 中断控制器初始化:初始化MDP中断控制器的寄存器基址、使能寄存器掩码和状态寄存器偏移。
  3. 中断处理函数注册:调用 devm_request_irq() 或 request_threaded_irq() 注册顶半部和底半部处理函数。
  4. 中断使能与屏蔽配置:通过MDP控制器的使能寄存器,按需开启VSYNC、UNDERFLOW等中断类型。
  5. 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中断与显示时序的时间关系:

VSYNC中断与显示时序关系 VSYNC中断与显示时序关系 VBlank 有效显示区域(Active Video) VBlank VSYNC IRQ 一帧周期(Frame Period) VSYNC中断在垂直消隐期(VBlank)开始时触发,驱动在此刻完成缓冲区交换和帧同步操作
实践要点:在调试显示时序问题时,可以通过监测VSYNC中断的触发间隔来验证时序配置是否正确。如果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计数器是否一致。