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

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

直到但不包括 Tegra124(又名 Tegra K1),drm/tegra 驱动程序都支持内置 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;
};

成员

映射

映射列表

锁定

同步对映射列表的访问

描述

请注意,条目不会定期从该缓存中清除,而是需要显式释放。 这主要用于 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 客户端操作

class

此客户端表示的 host1x 类

channel

与此客户端关联的 host1x 通道

syncpts

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

num_syncpts

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

parent

指向父结构的指针

usecount

此结构的引用计数

锁定

互斥锁

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

list node for the driver

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

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 以及各种其他参数,例如作业提交中使用的同步点或重定位。