第一章:RK显示驱动概览

做嵌入式显示开发这么多年,我接触过不少平台。但瑞芯微的显示架构,说实话,是我个人觉得最清晰的一套。今天咱们就来聊聊RK平台显示驱动的基础框架。

RK平台显示架构

先看整体架构。RK的显示系统,说白了就是三层结构:

  • 应用层:负责合成和提交显示内容
  • HAL层:硬件抽象,屏蔽底层差异
  • 内核层:直接操作VOP等硬件模块

嗯,这里要注意。很多新手容易把HAL层和内核层搞混。我刚开始做RK3399项目时,也踩过这个坑。后来发现,HAL层主要负责buffer管理和合成策略,而内核层才是真正跟寄存器打交道的。

核心要点:应用层通过SurfaceFlinger(Android)或Weston(Linux)提交图层,经过HAL层处理后,最终由内核驱动写入VOP的寄存器。

VOP模块介绍

VOP,全称Video Output Processor。你可以把它理解成显示数据的"调度中心"。它负责从内存中读取framebuffer,然后按照时序输出到显示接口。

我记得第一次看VOP的数据手册时,被那些寄存器搞得头大。其实你只要抓住几个关键点:

  • Win层:VOP内部有多个win(窗口),每个win可以绑定一个图层
  • DMA引擎:负责从DDR搬运数据到VOP内部
  • 时序发生器:生成行场同步信号

为什么会设计多个win层?因为实际产品中,往往需要叠加多个图层。比如视频播放时,视频层+UI层+字幕层,每个层独立更新,互不干扰。

我的经验:在RK3288上调试双屏异显时,我习惯把主屏绑定到win0,副屏绑定到win1。这样调试起来逻辑清晰,不容易乱。

显示数据流分析

数据是怎么从应用程序跑到屏幕上的?咱们一步步拆解:

  1. 应用创建buffer:通过gralloc分配图形buffer
  2. 填充数据:GPU或CPU渲染内容到buffer
  3. 提交到HAL:通过drmModeSetCrtc或queueBuffer提交
  4. 内核调度:drm驱动将buffer地址写入VOP寄存器
  5. VOP读取:DMA引擎按帧同步信号读取数据
  6. 输出显示:数据经编码后送到HDMI/MIPI/LVDS等接口

你想想看,这整个过程必须在16.6ms内完成(60fps)。一旦超时,就会出现掉帧或撕裂。我在项目中遇到过最典型的问题,就是buffer提交太慢导致画面卡顿。

避坑指南:我曾经在RK3566上调试4K显示,发现帧率上不去。查了两天才找到原因——VOP的DMA带宽被其他外设占用了。后来通过调整内存控制器优先级才解决。

关键数据结构

驱动里最核心的数据结构就这几个:

结构体 作用
rockchip_vop VOP设备实例,包含所有win和寄存器基址
vop_win 单个窗口的描述,包含buffer地址、格式、尺寸
vop_dma DMA传输控制,管理数据搬运

看代码时,我建议你从rockchip_vop_probe函数入手。这个函数会初始化所有硬件资源,是理解整个驱动的入口。

// 典型的VOP初始化流程
static int rockchip_vop_probe(struct platform_device *pdev)
{
    struct rockchip_vop *vop;
    
    // 分配VOP结构体
    vop = devm_kzalloc(dev, sizeof(*vop), GFP_KERNEL);
    
    // 映射寄存器地址
    vop->regs = devm_ioremap_resource(dev, res);
    
    // 初始化所有win层
    for (i = 0; i < VOP_MAX_WIN; i++)
        vop_win_init(vop, i);
    
    // 注册drm CRTC
    drm_crtc_init_with_planes(dev, crtc, primary, cursor, ...);
}

嗯,这段代码看起来简单,但实际调试时坑不少。比如寄存器地址映射失败,或者win层数量跟硬件不匹配,都会导致黑屏。

总结一下

RK的显示驱动,核心就是VOP这个硬件模块。你只要搞懂了它怎么管理win层、怎么搬运数据、怎么生成时序,剩下的就是配置各种接口协议了。

我个人习惯,拿到一个新板子,先看dts里VOP的配置,再看驱动probe流程。这样能快速建立起整体认知。下一章咱们会深入VOP的寄存器配置,到时候再细聊。

一句话记住:显示驱动就是"应用层画图 → 内核层搬运 → VOP输出"这条流水线。任何环节出问题,屏幕都不会亮。

公众号:蓝海资料掘金营,微信deep3321