4. DPU显示控制器驱动:DPU硬件模块介绍、DPU驱动初始化、中断处理、时钟与电源管理

DPU(Display Processing Unit)是高通骁龙平台显示子系统的核心硬件模块,负责将GPU渲染完成的帧缓冲区数据经过合成、缩放、色彩校正、时序生成等流水线处理,最终输出到物理显示接口(如DP、DSI、HDMI)。本章深入剖析DPU的硬件架构、驱动初始化流程、中断处理机制以及时钟与电源管理策略。

4.1 DPU硬件模块介绍

SA8155平台的DPU集成在MDSS(Mobile Display Subsystem)内部,包含多个专用硬件块,每个块完成显示流水线中的一个特定阶段。下图展示了DPU内部的主要模块及其数据流关系。

DPU 显示控制器硬件模块架构 SSPP 源接口 (4x 图层输入) LM 图层混合器 (Alpha/Blend) PIPE 色彩处理 (GC/IGC/PCC) INTF 时序 (DP/DSI/HDMI) VBIF 总线接口 (AXI/AHB) DMA 引擎 (帧传输控制) 控制寄存器总线 (AHB-Lite) — 连接所有模块的配置接口 DPU 中断控制器

DPU核心模块功能说明:

模块缩写全称功能描述
SSPPSource Surface Processor从内存中获取图层数据,支持最多4个独立图层输入
LMLayer Mixer执行Alpha混合与图层叠加,生成合成帧
PIPEColor Processing PipeGamma校正(GC)、逆Gamma(IGC)、色彩变换(PCC)
INTFInterface Controller生成显示时序信号,驱动DP/DSI/HDMI物理层
VBIFVideo Bus Interface通过AXI总线与系统内存交互,获取帧数据
DMADirect Memory Access控制帧数据从VBIF到SSPP的传输
架构要点: DPU采用流水线设计,每一帧数据从VBIF进入,经过SSPP解包、LM混合、PIPE色彩处理,最后由INTF按时序输出。这种设计允许每个阶段独立配置,支持多图层合成和实时色彩调整。

4.2 DPU驱动初始化

DPU驱动初始化遵循DRM/KMS子系统的标准流程,分为平台设备探测、子模块绑定、CRTC/Encoder/Connector创建三个阶段。初始化顺序直接影响显示输出的正确性。

4.2.1 初始化流程

DPU 驱动初始化流程 1. platform_driver.probe 2. 解析DTB & 映射寄存器 3. 初始化时钟 & 电源域 4. 注册IRQ & 中断处理 5. 创建DRM CRTC/Encoder

4.2.2 关键数据结构

DPU驱动使用 dpu_kms 结构体作为顶层抽象,包含所有子模块的实例指针:

struct dpu_kms {
    struct drm_device *dev;
    struct platform_device *pdev;

    /* 寄存器映射基地址 */
    void __iomem *mmio;
    unsigned long mmio_len;

    /* 子模块实例 */
    struct dpu_hw_sspp *sspp[SSPP_MAX];
    struct dpu_hw_lm   *lm[LM_MAX];
    struct dpu_hw_intf *intf[INTF_MAX];
    struct dpu_hw_dspp *dspp[DSPP_MAX];

    /* 时钟与电源 */
    struct clk_bulk_data *clocks;
    int num_clocks;
    struct regulator *vdd;

    /* 中断相关 */
    struct dpu_irq_controller irq_controller;
    spinlock_t irq_lock;
};
初始化要点: 驱动在probe阶段必须按顺序完成:
1) 从设备树获取寄存器地址范围并ioremap;
2) 获取所有时钟句柄(包括mdp_clk、ahb_clk、vsync_clk等);
3) 注册中断处理函数,绑定到DPU中断控制器;
4) 最后调用dpu_kms_init()创建DRM核心对象。

4.3 中断处理

DPU中断系统负责处理垂直同步(VSYNC)、帧完成、错误恢复等事件。中断控制器采用分层设计:硬件层产生原始中断,驱动层进行屏蔽、状态读取和分发。

4.3.1 中断类型与映射

中断源硬件位域触发条件驱动处理动作
VSYNCINTF_VSYNC每帧垂直消隐期开始唤醒drm_vblank_work,更新CRC
帧完成DMA_FRAME_DONEDMA传输完成触发commit完成回调,释放fence
溢出错误FIFO_OVERFLOWFIFO缓冲区溢出重置对应管道,打印错误日志
总线错误VBIF_AXI_ERRORAXI总线访问异常触发系统错误恢复流程

4.3.2 中断处理流程

DPU 中断处理流程 硬件中断触发 读取INTR_STATUS 按位域分发 VSYNC处理 drm_crtc_handle_vblank() 帧完成处理 dpu_crtc_complete_commit() 错误处理 dpu_core_perf_crtc_release()
中断处理注意事项:
• 中断处理函数必须在原子上下文中执行,不能调用可能导致睡眠的函数(如kmalloc、mutex_lock)。
• 对于VSYNC中断,必须使用 drm_crtc_handle_vblank() 通知DRM核心,否则用户空间的page flip等待会超时。
• 错误中断(如FIFO溢出)需要记录寄存器快照,便于后期调试。

4.4 时钟与电源管理

DPU的时钟和电源管理直接影响显示性能和功耗。SA8155平台为DPU提供了多个独立时钟域和电源域,驱动需要根据显示状态动态调节。

4.4.1 时钟域划分

时钟名称时钟源典型频率控制模块
mdp_clkDPU核心时钟200-500 MHz所有内部流水线
ahb_clkAHB总线时钟100-200 MHz寄存器配置接口
vsync_clk垂直同步时钟60 HzINTF时序生成
axi_clkAXI总线时钟300-600 MHzVBIF与DMA
dsi_clkDSI链路时钟根据分辨率动态DSI物理层

4.4.2 电源管理策略

DPU驱动实现了基于运行时PM(Runtime PM)的电源管理,在显示空闲时关闭时钟和电源域以降低功耗。核心接口如下:

/* 运行时PM回调 */
static int dpu_runtime_suspend(struct device *dev)
{
    struct dpu_kms *dpu_kms = dev_get_drvdata(dev);

    /* 关闭所有DPU时钟 */
    clk_bulk_disable_unprepare(dpu_kms->num_clocks,
                               dpu_kms->clocks);

    /* 关闭电源域 */
    regulator_disable(dpu_kms->vdd);

    dev_dbg(dev, "DPU runtime suspended\n");
    return 0;
}

static int dpu_runtime_resume(struct device *dev)
{
    struct dpu_kms *dpu_kms = dev_get_drvdata(dev);
    int ret;

    /* 使能电源域 */
    ret = regulator_enable(dpu_kms->vdd);
    if (ret)
        return ret;

    /* 使能所有时钟 */
    ret = clk_bulk_prepare_enable(dpu_kms->num_clocks,
                                  dpu_kms->clocks);
    if (ret) {
        regulator_disable(dpu_kms->vdd);
        return ret;
    }

    /* 重新初始化DPU硬件状态 */
    dpu_hw_init(dpu_kms);

    dev_dbg(dev, "DPU runtime resumed\n");
    return 0;
}

static const struct dev_pm_ops dpu_pm_ops = {
    SET_RUNTIME_PM_OPS(dpu_runtime_suspend,
                       dpu_runtime_resume, NULL)
};

4.4.3 动态频率调节

DPU驱动根据当前显示分辨率和刷新率动态调整 mdp_clk 频率,避免不必要的功耗浪费。频率计算公式如下:

/* 计算所需最小MDP时钟频率 */
static unsigned long dpu_calc_mdp_clk(struct drm_display_mode *mode)
{
    unsigned long pclk = mode->clock;  /* pixel clock in kHz */
    unsigned long mdp_clk;

    /*
     * MDP时钟需要至少为像素时钟的2倍,
     * 以支持双像素处理模式。
     * 对于4K@60,pclk ≈ 594000 kHz,
     * 则mdp_clk ≥ 1188000 kHz。
     */
    mdp_clk = pclk * 2;

    /* 考虑额外开销,增加20%余量 */
    mdp_clk = mdp_clk * 120 / 100;

    return mdp_clk;
}
电源管理最佳实践:
• 在DPU空闲时(如屏幕关闭)调用 pm_runtime_put_sync() 触发挂起。
• 在每次commit操作前调用 pm_runtime_get_sync() 确保时钟已使能。
• 使用 clk_bulk API统一管理多个时钟,减少代码重复。
• 在suspend/resume过程中保存和恢复DPU关键寄存器状态,确保恢复后显示正常。

4.5 本章小结