drm/vc4 Broadcom VC4 图形驱动

Broadcom VideoCore 4(存在于 Raspberry Pi 中)包含一个与 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 时钟(HSM 用于 HDMI,VEC 用于 SDTV 等)中进行选择。

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

HVS

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

有一个全局 HVS,带有多个输出 FIFO,可以被 PV 消耗。 该文件仅管理 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 控制器。

大多数 Raspberry Pi 板将 DSI1 公开为其“DISPLAY”连接器,而计算模块则同时引出 DSI0 和 DSI1。

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

DPI 编码器

VC4 DPI 硬件支持 MIPI DPI 类型 4 和 Nokia 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 到 PixelValve 动态 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 在其生命周期内是不可变的(通过不允许 mmap、GEM prime 导出或从 CL 渲染到着色器 BO 来强制执行),因此此验证仅在 BO 创建时执行。

V3D 中断

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

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

当我们采用渲染帧中断时,我们需要唤醒等待某个帧完成的进程,并尽快提交下一个帧(以便在有工作要做时硬件不会闲置)。

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