drm/tegra NVIDIA Tegra GPU 和显示驱动程序

NVIDIA Tegra SoC 通过 host1x 控制器支持一组显示、图形和视频功能。host1x 通过通道将其从 CPU 直接提供的推送缓冲区收集的命令流提供给其客户端。软件或块之间可以使用同步点进行同步。

直到但不包括 Tegra124(又名 Tegra K1),drm/tegra 驱动程序都支持内置 GPU,该 GPU 由 gr2d 和 gr3d 引擎组成。从 Tegra124 开始,GPU 基于 NVIDIA 桌面 GPU 架构,并由 drm/nouveau 驱动程序支持。

drm/tegra 驱动程序支持自 Tegra20 以来的 NVIDIA Tegra SoC 代。它有三个部分

  • 一个 host1x 驱动程序,提供基础结构和对 host1x 服务的访问。

  • 一个 KMS 驱动程序,支持显示控制器以及许多输出,例如 RGB、HDMI、DSI 和 DisplayPort。

  • 一组自定义的用户空间 IOCTL,可用于通过 host1x 将作业提交到 GPU 和视频引擎。

驱动程序基础设施

各种 host1x 客户端需要绑定在一起形成一个逻辑设备,以便向用户公开其功能。支持此功能的基础结构在 host1x 驱动程序中实现。当驱动程序在基础结构中注册时,它会提供一个兼容字符串列表,指定它需要的设备。基础结构会创建一个逻辑设备,并扫描设备树以查找匹配的设备节点,将所需的客户端添加到列表中。单个客户端的驱动程序也向基础结构注册,并添加到逻辑 host1x 设备中。

一旦所有客户端都可用,基础结构将使用驱动程序提供的函数初始化逻辑设备,该函数将设置特定于子系统的位,并反过来初始化其每个客户端。

同样,当其中一个客户端被取消注册时,基础结构将通过回调到驱动程序来销毁逻辑设备,从而确保拆除特定于子系统的位,并依次销毁客户端。

Host1x 基础设施参考

struct host1x_bo_cache

host1x 缓冲区对象缓存

定义:

struct host1x_bo_cache {
    struct list_head mappings;
    struct mutex lock;
};

成员

mappings

映射列表

lock

同步对映射列表的访问

描述

请注意,条目不会定期从此缓存中逐出,而是需要显式释放。这主要用于 DRM/KMS,其中当删除此缓存中映射表示的缓冲区对象的最后一个引用时,缓存的引用将被释放。

struct host1x_client_ops

host1x 客户端操作

定义:

struct host1x_client_ops {
    int (*early_init)(struct host1x_client *client);
    int (*init)(struct host1x_client *client);
    int (*exit)(struct host1x_client *client);
    int (*late_exit)(struct host1x_client *client);
    int (*suspend)(struct host1x_client *client);
    int (*resume)(struct host1x_client *client);
};

成员

early_init

host1x 客户端早期初始化代码

init

host1x 客户端初始化代码

exit

host1x 客户端拆卸代码

late_exit

host1x 客户端后期拆卸代码

suspend

host1x 客户端挂起代码

resume

host1x 客户端恢复代码

struct host1x_client

host1x 客户端结构

定义:

struct host1x_client {
    struct list_head list;
    struct device *host;
    struct device *dev;
    struct iommu_group *group;
    const struct host1x_client_ops *ops;
    enum host1x_class class;
    struct host1x_channel *channel;
    struct host1x_syncpt **syncpts;
    unsigned int num_syncpts;
    struct host1x_client *parent;
    unsigned int usecount;
    struct mutex lock;
    struct host1x_bo_cache cache;
};

成员

list

host1x 客户端的列表节点

host

指向表示 host1x 控制器的 struct device 的指针

dev

指向支持此 host1x 客户端的 struct device 的指针

group

此客户端所属的 IOMMU 组

ops

host1x 客户端操作

此客户端表示的 host1x 类

channel

与此客户端关联的 host1x 通道

syncpts

为此客户端请求的同步点数组

num_syncpts

为此客户端请求的同步点数

parent

指向父结构的指针

usecount

此结构的引用计数

lock

用于互斥并发的互斥锁

cache

host1x 缓冲区对象缓存

struct host1x_driver

host1x 逻辑设备驱动程序

定义:

struct host1x_driver {
    struct device_driver driver;
    const struct of_device_id *subdevs;
    struct list_head list;
    int (*probe)(struct host1x_device *device);
    int (*remove)(struct host1x_device *device);
    void (*shutdown)(struct host1x_device *device);
};

成员

driver

核心驱动程序

subdevs

与此驱动程序的子设备匹配的 OF 设备 ID 表

list

驱动程序的列表节点

probe

在探测 host1x 逻辑设备时调用

remove

在删除 host1x 逻辑设备时调用

shutdown

在关闭 host1x 逻辑设备时调用

int host1x_device_init(struct host1x_device *device)

初始化 host1x 逻辑设备

参数

struct host1x_device *device

host1x 逻辑设备

描述

host1x 逻辑设备的驱动程序可以在执行其 host1x_driver.probe 实现期间调用此函数,以初始化其每个客户端。客户端驱动程序使用 host1x_client.parent 字段和与之关联的驱动程序数据来访问子系统特定的驱动程序数据(通常通过调用 dev_get_drvdata())。

int host1x_device_exit(struct host1x_device *device)

取消初始化 host1x 逻辑设备

参数

struct host1x_device *device

host1x 逻辑设备

描述

当卸载 host1x 逻辑设备的驱动程序时,它可以调用此函数来拆卸其每个客户端。通常,这在删除特定于子系统的数据结构且不再可以使用该功能后完成。

int host1x_driver_register_full(struct host1x_driver *driver, struct module *owner)

注册 host1x 驱动程序

参数

struct host1x_driver *driver

host1x 驱动程序

struct module *owner

所有者模块

描述

host1x 逻辑设备的驱动程序调用此函数以向基础结构注册驱动程序。请注意,由于这些驱动程序是逻辑设备,因此驱动程序的注册实际上会触发逻辑设备的创建。将为每个 host1x 实例创建一个逻辑设备。

void host1x_driver_unregister(struct host1x_driver *driver)

注销 host1x 驱动程序

参数

struct host1x_driver *driver

host1x 驱动程序

描述

从它绑定到的每个 host1x 逻辑设备中取消绑定驱动程序,从而有效地删除它们表示的子系统设备。

void __host1x_client_init(struct host1x_client *client, struct lock_class_key *key)

初始化 host1x 客户端

参数

struct host1x_client *client

host1x 客户端

struct lock_class_key *key

客户端特定互斥锁的锁类键

void host1x_client_exit(struct host1x_client *client)

取消初始化 host1x 客户端

参数

struct host1x_client *client

host1x 客户端

int __host1x_client_register(struct host1x_client *client)

注册 host1x 客户端

参数

struct host1x_client *client

host1x 客户端

描述

将 host1x 客户端注册到每个 host1x 控制器实例。请注意,每个客户端只会匹配其父 host1x 控制器,并且只会与该实例关联。一旦所有客户端都已注册到其父 host1x 控制器,基础设施将设置逻辑设备并调用 host1x_device_init(),这反过来将调用每个客户端的 host1x_client_ops.init 实现。

void host1x_client_unregister(struct host1x_client *client)

注销 host1x 客户端

参数

struct host1x_client *client

host1x 客户端

描述

从其 host1x 控制器实例中移除 host1x 客户端。如果逻辑设备已初始化,它将被拆除。

Host1x 同步点参考

struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, unsigned long flags, const char *name)

分配一个同步点

参数

struct host1x *host

host1x 设备数据

unsigned long flags

HOST1X_SYNCPT_* 标志的位域

const char *name

用于调试打印的同步点名称

描述

为调用者分配一个硬件同步点以供使用。然后,调用者拥有唯一的权限来更改同步点的值,直到再次释放它。

如果没有可用的空闲同步点,或指定了 NULL 名称,则返回 NULL。

u32 host1x_syncpt_id(struct host1x_syncpt *sp)

检索同步点 ID

参数

struct host1x_syncpt *sp

host1x 同步点

描述

给定指向 struct host1x_syncpt 的指针,检索其 ID。此 ID 通常用作编程到寄存器中的值,用于控制硬件模块如何与同步点交互。

u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs)

更新发送到硬件的值

参数

struct host1x_syncpt *sp

host1x 同步点

u32 incrs

增量数

int host1x_syncpt_incr(struct host1x_syncpt *sp)

从 CPU 增加同步点值,更新缓存

参数

struct host1x_syncpt *sp

host1x 同步点

int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, u32 *value)

等待同步点达到给定值

参数

struct host1x_syncpt *sp

host1x 同步点

u32 thresh

阈值

long timeout

等待同步点达到给定值的最大时间

u32 *value

同步点值的返回位置

struct host1x_syncpt *host1x_syncpt_request(struct host1x_client *client, unsigned long flags)

请求同步点

参数

struct host1x_client *client

请求同步点的客户端

unsigned long flags

标志

描述

host1x 客户端驱动程序可以使用此函数分配同步点以供后续使用。此函数返回的同步点将专门为客户端保留。当不再使用同步点时,host1x 客户端驱动程序需要使用 host1x_syncpt_put() 释放它。

void host1x_syncpt_put(struct host1x_syncpt *sp)

释放请求的同步点

参数

struct host1x_syncpt *sp

host1x 同步点

描述

释放先前使用 host1x_syncpt_request() 分配的同步点。当不再使用同步点时,host1x 客户端驱动程序应调用此函数。

u32 host1x_syncpt_read_max(struct host1x_syncpt *sp)

读取最大同步点值

参数

struct host1x_syncpt *sp

host1x 同步点

描述

最大同步点值表示队列中有多少操作,无论是在通道中还是在软件线程中。

u32 host1x_syncpt_read_min(struct host1x_syncpt *sp)

读取最小同步点值

参数

struct host1x_syncpt *sp

host1x 同步点

描述

最小同步点值是硬件中当前同步点值的阴影。

u32 host1x_syncpt_read(struct host1x_syncpt *sp)

读取当前同步点值

参数

struct host1x_syncpt *sp

host1x 同步点

struct host1x_syncpt *host1x_syncpt_get_by_id(struct host1x *host, unsigned int id)

通过 ID 获取同步点

参数

struct host1x *host

host1x 控制器

unsigned int id

同步点 ID

struct host1x_syncpt *host1x_syncpt_get_by_id_noref(struct host1x *host, unsigned int id)

通过 ID 获取同步点,但不增加引用计数。

参数

struct host1x *host

host1x 控制器

unsigned int id

同步点 ID

struct host1x_syncpt *host1x_syncpt_get(struct host1x_syncpt *sp)

增加同步点引用计数

参数

struct host1x_syncpt *sp

同步点

struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp)

获取与同步点关联的等待基址。

参数

struct host1x_syncpt *sp

host1x 同步点

u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base)

检索同步点等待基址的 ID。

参数

struct host1x_syncpt_base *base

host1x 同步点等待基址。

void host1x_syncpt_release_vblank_reservation(struct host1x_client *client, u32 syncpt_id)

使 VBLANK 同步点可用于分配。

参数

struct host1x_client *client

host1x 总线客户端。

u32 syncpt_id

要使其可用的同步点 ID。

描述

如果 VBLANK<i> 同步点在初始化时被保留,则使其可用于分配。显示驱动程序应在确保已禁用引导链配置的任何 VBLANK 增量编程后调用此函数。

KMS 驱动程序

在各种 Tegra SoC 代中,显示硬件在很大程度上保持了向后兼容性,直到 Tegra186,它引入了一些使参数化驱动程序难以支持的更改。

显示控制器

Tegra SoC 有两个显示控制器,每个控制器可以与零个或多个输出关联。输出也可以共享一个显示控制器,但前提是它们以兼容的显示时序运行。两个显示控制器也可以共享一个帧缓冲区,即使两个输出上的模式不匹配,也允许克隆配置。在 KMS 术语中,显示控制器被建模为 CRTC。

在 Tegra186 上,显示控制器的数量已增加到三个。显示控制器不再能够驱动所有输出。虽然其中两个控制器可以驱动两个 DSI 输出和两个 SOR 输出,但第三个控制器无法驱动任何 DSI。

窗口

显示控制器控制一组窗口,这些窗口可用于将多个缓冲区合成到屏幕上。虽然可以为各个窗口分配任意 Z 顺序(通过编程相应的混合寄存器),但驱动程序当前不支持此功能。相反,它将假定窗口的固定 Z 顺序(窗口 A 是根窗口,即最低的窗口,而窗口 B 和 C 叠加在窗口 A 之上)。覆盖窗口支持多种像素格式,并且可以在扫描输出时自动从 YUV 转换为 RGB。这使得它们对于显示视频内容很有用。在 KMS 中,每个窗口都被建模为一个平面。每个显示控制器都有一个硬件光标,该光标作为光标平面公开。

输出

支持的输出类型和数量在 Tegra SoC 代之间有所不同。所有代都至少支持 HDMI。虽然早期代支持非常简单的 RGB 接口(每个显示控制器一个),但最近的代不再支持,而是提供标准的接口,例如 DSI 和 eDP/DP。

输出被建模为合成的编码器/连接器对。

RGB/LVDS

自 Tegra124 以来,此接口不再可用。它已被更标准的 DSI 和 eDP 接口取代。

HDMI

所有 Tegra SoC 都支持 HDMI。从 Tegra210 开始,HDMI 由多功能的 SOR 输出提供,该输出支持 eDP、DP 和 HDMI。SOR 能够支持 HDMI 2.0,但目前尚未合并对它的支持。

DSI

尽管 Tegra 自 Tegra30 以来就支持 DSI,但控制器在 Tegra114 中发生了多次更改。由于在 Dalmore(Tegra114)之前的任何公开可用的开发板都没有使用 DSI,因此 drm/tegra 驱动程序仅支持 Tegra114 及更高版本。

eDP/DP

eDP 首次在 Tegra124 中引入,用于驱动笔记本电脑外形尺寸的显示面板。Tegra210 添加了对完整 DisplayPort 支持,但目前尚未在 drm/tegra 驱动程序中实现。

用户空间接口

drm/tegra 提供的用户空间接口允许应用程序创建 GEM 缓冲区、访问和控制同步点以及向 host1x 提交命令流。

GEM 缓冲区

DRM_IOCTL_TEGRA_GEM_CREATE IOCTL 用于创建具有 Tegra 特定标志的 GEM 缓冲区对象。这对于应平铺或应倒置扫描输出(对 3D 内容有用)的缓冲区很有用。

创建 GEM 缓冲区对象后,应用程序可以使用 DRM_IOCTL_TEGRA_GEM_MMAP IOCTL 返回的 mmap 偏移量来映射其内存。

同步点

可以通过执行 DRM_IOCTL_TEGRA_SYNCPT_READ IOCTL 来获取同步点的当前值。使用 DRM_IOCTL_TEGRA_SYNCPT_INCR IOCTL 来实现同步点的递增。

用户空间也可以请求在同步点上阻塞。为此,它需要执行 DRM_IOCTL_TEGRA_SYNCPT_WAIT IOCTL,指定要等待的同步点的值。当同步点达到该值或在指定的超时时间后,内核将释放应用程序。

命令流提交

在应用程序可以向 host1x 提交命令流之前,它需要使用 DRM_IOCTL_TEGRA_OPEN_CHANNEL IOCTL 打开到引擎的通道。客户端 ID 用于标识通道的目标。当不再需要通道时,可以使用 DRM_IOCTL_TEGRA_CLOSE_CHANNEL IOCTL 关闭通道。要检索与通道关联的同步点,应用程序可以使用 DRM_IOCTL_TEGRA_GET_SYNCPT

打开通道后,提交命令流很容易。应用程序将命令写入 GEM 缓冲区对象支持的内存中,并将这些命令与各种其他参数(例如作业提交中使用的同步点或重定位)一起传递给 DRM_IOCTL_TEGRA_SUBMIT IOCTL。