drm/vc4 博通 VC4 图形驱动

博通 VideoCore 4(存在于树莓派中)包含一个与 OpenGL ES 2.0 兼容的 3D 引擎,称为 V3D,以及一个高度可配置的显示输出管线,支持 HDMI、DSI、DPI 和复合电视输出。

3D 引擎还有一个接口,用于提交任意的计算着色器风格的作业,使用与 GLES 2.0 中顶点和片段着色器相同的着色器处理器。但是,考虑到硬件无法暴露任何标准的接口,如 OpenGL 计算着色器或 OpenCL,因此该驱动程序不支持它。

显示硬件处理

本节涵盖与显示硬件相关的所有内容,包括模式设置基础设施、平面、精灵和光标处理和显示、输出探测以及相关主题。

像素阀 (DRM CRTC)

在 VC4 中,像素阀最接近 DRM 的 CRTC 概念。PV 从编码器的时钟及其配置生成视频时序。它以该时序从 HVS 中提取缩放的像素,并将其馈送到编码器。

但是,DRM CRTC 也收集所有附加到它的 DRM 平面的配置。因此,CRTC 还负责编写 CRTC 将使用的 HVS 通道的显示列表。

2835 有 3 个不同的像素阀。音频电源域中的 pv0 提供 DSI0 或 DPI,而 pv1 提供 DS1 或 SMI。图像域中的 pv2 可以提供 HDMI 或 SDTV 控制器。像素阀根据多路复用器中选择的输出类型从 CPRMAN 时钟(HDMI 为 HSM,SDTV 为 VEC 等)中选择。

对于电源管理,像素阀的寄存器都由 AXI 时钟计时,而时序和 FIFO 则使用特定于输出的时钟。由于编码器也直接消耗 CPRMAN 时钟,并且知道它们需要的时序,因此它们是设置时钟的。

HVS

硬件视频缩放器 (HVS) 是硬件的一部分,它执行帧缓冲区中存储的像素的转换、缩放、颜色空间转换和合成,然后将像素的 FIFO 输出到像素阀 (CRTC)。它以系统时钟速率(特别是系统音频时钟门控)运行,该速率远高于像素时钟速率。

有一个全局 HVS,带有多个可以被 PV 消耗的输出 FIFO。此文件仅管理 HVS 的资源,而 vc4_crtc.c 代码实际上驱动每个 CRTC 的 HVS 设置。

HVS 平面

每个 DRM 平面都是 HVS 扫描输出的像素层。

在原子模式设置检查时,我们计算显示平面所需的 HVS 显示元素状态(使我们有机会确定平面配置是否无效),然后在原子刷新时,CRTC 将要求我们将我们的元素状态写入 HVS 为我们分配的区域。

HDMI 编码器

HDMI 内核具有状态机和 PHY。在 BCM2835 上,大部分单元都使用 CPRMAN 的 HSM 时钟运行。它还在内部使用 PLLH_PIX 时钟用于 PHY。

HDMI 信息帧保存在一个小的包 RAM 中,每个包都可以单独启用以包含在帧中。

HDMI 音频完全在 HDMI IP 块中实现。HDMI 编码器中的一个寄存器从 DMA 引擎接收 SPDIF 帧,并通过内部 MAI(多通道音频互连)总线将其传输到编码器端,以便插入到视频消隐区域中。

驱动程序的 HDMI 编码器尚不支持电源管理。HDMI 编码器的电源域和 HSM/像素时钟保持持续运行,只有 HDMI 逻辑和数据包 RAM 在禁用/启用时才关闭/打开。

该驱动程序尚不支持 CEC 控制,尽管 HDMI 编码器块具有 CEC 支持。

DSI 编码器

BCM2835 包含两个 DSI 模块,DSI0 和 DSI1。DSI0 是一个单通道 DSI 控制器,而 DSI1 是一个更现代的 4 通道 DSI 控制器。

大多数树莓派板将 DSI1 作为其“显示”连接器公开,而计算模块则将 DSI0 和 DSI1 都引出。

此驱动程序目前仅针对 DSI1 视频模式显示进行了测试,大部分必要的信息(希望如此)都在 DSI0 中。

DPI 编码器

VC4 DPI 硬件支持 MIPI DPI 类型 4 和诺基亚 ViSSI 信号。在 BCM2835 上,这些可以使用 ALT2 功能路由到 GPIO0-27。

VEC (复合电视输出) 编码器

VEC 编码器生成 PAL 或 NTSC 复合视频输出。

电视模式选择是通过编码器上的原子属性完成的,因为 drm_mode_modeinfo 不足以区分 PAL 和 PAL-M 或 NTSC 和 NTSC-J。

KUnit 测试

VC4 驱动程序使用 KUnit 来执行特定于驱动程序的单元和集成测试。

这些测试使用模拟驱动程序,可以使用下面的命令在 arm 或 arm64 架构上运行,

$ ./tools/testing/kunit/kunit.py run \
        --kunitconfig=drivers/gpu/drm/vc4/tests/.kunitconfig \
        --cross_compile aarch64-linux-gnu- --arch arm64
当前测试涵盖的驱动程序部分是
  • BCM2835-7 和 BCM2711 的 HVS 到像素阀动态 FIFO 分配。

内存管理和 3D 命令提交

本节介绍 vc4 驱动程序中的 GEM 实现。

GPU 缓冲区对象 (BO) 管理

VC4 GPU 架构(扫描输出和渲染)可以直接访问系统内存,中间没有 MMU。为了支持它,我们使用 GEM DMA 帮助函数为我们的 BO 分配连续的物理内存范围。

由于 DMA 分配器非常慢,我们保留最近释放的 BO 的缓存,以便内核为 3D 渲染分配的对象可以快速返回。

V3D 分箱器命令列表 (BCL) 验证

由于 VC4 在它和系统内存之间没有 IOMMU,因此有权执行命令列表的用户可以通过覆盖系统内存(将其绘制为帧缓冲区)或读取不应该读取的系统内存(将其读取为顶点缓冲区或索引缓冲区)来提升权限。

我们验证分箱器命令列表,以确保所有访问都在提交的作业引用的 GEM 对象的范围内。它显式地列出了数据包的白名单,并查看任何地址字段中的偏移量,以确保它们包含在它们引用的 BO 中。

请注意,由于 CL 验证已经在读取用户提交的 CL 并将验证后的副本写回 GPU 实际读取的内存中,因此这也是发生 GEM 重定位处理的地方(将 BO 引用转换为 GPU 要使用的实际地址)。

V3D 渲染命令列表 (RCL) 生成

在 V3D 硬件中,渲染命令列表加载和存储帧缓冲区的图块,并可选择调用分箱器生成的命令列表来执行该图块的 3D 绘图。

在 VC4 驱动程序中,渲染命令列表的生成由内核而不是用户空间执行。我们这样做是因为验证用户提交的命令列表很难正确,并且 CPU 开销很高,而渲染命令列表的有效配置数量实际上相当少。

VC4 的着色器验证器

由于 VC4 在它和系统内存之间没有 IOMMU,因此有权执行着色器的用户可以通过覆盖系统内存(在通用 DMA 模式下使用 VPM 写入地址寄存器)或读取不应该读取的系统内存(将其读取为纹理、统一数据或直接寻址的 TMU 查找)来提升权限。

着色器验证器遍历着色器的 BO,确保其访问受到适当的限制,并记录进行纹理访问的位置,以便我们可以在统一流中对它们进行重定位。

着色器 BO 在其生命周期内是不可变的(通过不允许 mmaps、GEM 原始导出或从 CL 渲染来强制执行),因此此验证仅在 BO 创建时执行。

V3D 中断

我们有一个中断状态寄存器 (V3D_INTCTL),它报告中断,并且写入 1 位会清除这些中断。还有一对中断寄存器 (V3D_INTENA/V3D_INTDIS),其中向它们的位写入 1 会启用或禁用该特定中断,并且写入的 0 会被忽略(读取任何一个都会返回已启用中断的集合)。

当我们收到一个完成的分箱刷新中断时,我们需要提交下一个用于分箱的帧,并将完成的帧移动到渲染线程。

当我们收到渲染帧中断时,我们需要唤醒等待完成某些帧的进程,并尽快提交下一个帧(这样硬件在有工作要做时不会空闲)。

当我们收到分箱器内存不足中断时,我们需要分配一些新内存并将其传递给分箱器,以便当前作业可以取得进展。