Linux DRM开发人员指南
http://landley.net/kdocs/htmldocs/drm.html
Jesse Barnes
Initial version Intel Corporation
<jesse.barnes@intel.com>
Laurent Pinchart
Driver internals Ideas on board SPRL
<laurent.pinchart@ideasonboard.com>
Copyright © 2008-2009, 2012 Intel Corporation, Laurent Pinchart
The contents of this file may be used under the terms of the GNU General Public License version 2 (the "GPL") as distributed in the kernel source COPYING file.
Revision History | ||
---|---|---|
Revision 1.0 | 2012-07-13 | LP |
Added extensive documentation about driver internals. |
目录
1.简介
2. DRM内部
驱动程序初始化
- 驱动信息
- 驱动加载
内存管理
- 转换表管理器(TTM)
- 图形执行管理器(GEM)
模式设定
- 帧缓冲区创建
- 输出轮询
- 锁定
KMS初始化和清理
- CRTC(struct drm_crtc)
- Plane(struct drm_plane)
- Encoder(struct drm_encoder)
- Connector(struct drm_connector)
- 清理
- 输出发现和初始化示例
- KMS API函数
模式设置帮助函数
- 帮助函数
- CRTC帮助操作
- Encoder帮助操作
- Connector帮助操作
- Modeset帮助程序功能参考
- fbdev帮助程序功能参考
- 显示端口帮助程序功能参考
- EDID帮助程序功能参考
- 矩形实用程序参考
- 翻转工作助手参考
- VMA偏移管理器
KMS属性
V(垂直) blank
打开/关闭,文件操作和IOCTL
- 打开和关闭
- 文件操作
- IOCTL
命令提交和防护
睡眠/唤醒
DMA服务
3.用户接口
渲染节点
VBlank事件处理
A.DRM驱动程序API
Linux DRM层包含旨在满足复杂图形设备需求的代码,通常包含非常适合3D图形加速的可编程管线[pipeline]。内核中的图形驱动程序可以利用DRM功能来简化诸如内存管理,中断处理和DMA之类的任务,并为应用程序提供统一的接口。
版本说明:本指南涵盖了DRM树中的功能,包括TTM内存管理器,输出配置和模式设置以及新的vblank内部,以及当前内核中的所有常规功能。
[在此处插入典型DRM堆栈的图]
本章介绍了与驱动程序作者和开发人员相关的DRM内部,这些工作人员和开发人员致力于为现有驱动程序添加对最新功能的支持。
首先,我们讨论一些典型的驱动程序初始化要求,例如设置命令缓冲区,创建并初始化输出配置以及初始化核心服务。后续部分将更详细地介绍核心内部结构,并提供实施说明和示例。
DRM层为图形驱动程序提供了多种服务,其中许多服务是由它通过libdrm提供的应用程序接口驱动的,libdrm是包装大多数DRM ioctl的库。这些包括vblank事件处理,内存管理,输出管理,帧缓冲区管理,命令提交和防护,挂起/恢复支持以及DMA服务。
每个DRM驱动程序的核心都是drm_driver 结构。驱动程序通常会静态初始化drm_driver结构,然后将其传递给drm_*_init()
函数之一以在DRM子系统中注册它。
该drm_driver结构包含了描述驱动程序和功能支持,以及指向方法的DRM核心将调用来实现DRM API静态信息。我们将首先浏览drm_driver静态信息字段,然后在以后的部分中详细描述各个操作。
驱动程序通过在driver_features
字段中设置适当的标志来告知DRM内核其要求和支持的功能 。由于这些标志自注册就会影响DRM核心行为,因此必须将大多数标志在注册drm_driver 实例时便设置。高通对应msm_drv.c里面的msm_driver结构体
u32 driver_features;
驱动程序功能标志
DRIVER_USE_AGP
驱动程序使用AGP接口,DRM核心将管理AGP资源。
DRIVER_REQUIRE_AGP
驱动程序需要AGP接口才能运行。AGP初始化失败将成为致命错误。
DRIVER_PCI_DMA
驱动程序具有PCI DMA的功能,将启用PCI DMA缓冲区到用户空间的映射。不推荐使用。
DRIVER_SG
驱动程序可以执行分散/收集DMA,将启用分散/收集缓冲区的分配和映射。不推荐使用。
DRIVER_HAVE_DMA
驱动程序支持DMA,将支持用户空间DMA API。不推荐使用。
DRIVER_HAVE_IRQ,DRIVER_IRQ_SHARED
DRIVER_HAVE_IRQ指示驱动程序是否具有由DRM Core管理的IRQ处理程序。设置该标志后,内核将支持简单的IRQ处理程序安装。安装过程在“ IRQ注册”一节中介绍。
DRIVER_IRQ_SHARED指示设备和处理程序是否支持共享的IRQ(请注意,这是PCI驱动程序所必需的)。
DRIVER_GEM
驱动程序使用GEM内存管理器。
DRIVER_MODESET
驱动程序支持模式设置界面(KMS)。
DRIVER_PRIME
驱动程序实现DRM PRIME缓冲区共享。
DRIVER_RENDER
驱动程序支持专用渲染节点。
int major;
int minor;
int patchlevel;
DRM核心通过主要,次要和补丁程序级别的三元组来标识驱动程序版本。该信息在初始化时被打印到内核日志中,并通过DRM_IOCTL_VERSION ioctl传递到用户空间。
主编号和次编号也用于验证[应用程序]通过DRM_IOCTL_SET_VERSION的请求的驱动程序API版本。当驱动程序API在次要版本之间更改时,应用程序可以调用DRM_IOCTL_SET_VERSION以选择特定版本的API。如果请求的主版本不等于驱动程序主版本,或者所请求的次版本大于驱动程序次版本,则DRM_IOCTL_SET_VERSION调用将返回错误。否则,驱动程序的set_version()方法将被调用来设置请求版本。
该load
方法是驱动程序和设备初始化的入口点。该方法负责分配和初始化驱动程序私有数据,指定支持的性能计数器,执行资源分配和映射(例如,获取时钟,映射寄存器或分配命令缓冲区),初始化内存管理器(称为“内存管理”的部分),安装IRQ处理程序(称为“ IRQ注册”的部分),设置垂直消隐处理(称为“垂直消隐” 的部分),模式设置(称为“ Mode Setting”的部分)和初始输出配置(称为“ KMS初始化和清理”部分)。
注意
如果需要考虑兼容性(例如,将驱动程序从“用户模式设置”转换为“内核模式设置”),则必须注意防止设备初始化和控制与当前活动的用户空间驱动程序不兼容。例如,如果正在使用用户级别模式设置驱动程序,则在加载时执行输出发现和配置会很成问题。同样,如果使用了不了解内存管理的用户级驱动程序,则可能需要省略内存管理和命令缓冲区设置。这些要求是特定于驱动程序的,因此必须注意使新旧应用程序和库均能正常工作。
int (*load) (struct drm_device *, unsigned long flags);
该方法有两个参数,一个指向新创建的drm_device的指针 和标志。这些标志用于传递设备id的driver_data字段,该设备id对应于传递给drm_*_init()的设备。当前只有PCI设备使用此方法,USB和平台DRM驱动程序将其load
方法,参数标志为0。
驱动程序私有挂起(隐藏?)了主要的 drm_device结构,可用于跟踪设备特定的各种信息位,例如寄存器偏移,命令缓冲区状态,用于挂起/恢复的寄存器状态等。在加载时,驱动程序可以简单地分配并适当地设置一个drm_device.dev_priv;它应该被释放和drm_device.dev_priv设置为空当驱动程序被卸载。
DRM支持几个用于粗略性能描述的计数器。该统计计数器系统已弃用,不应使用。如果需要性能监视,则开发人员应调查并可能增强内核性能和跟踪基础结构,以导出GPU相关的性能信息,以供性能监视工具和应用程序使用。
DRM核心试图通过提供drm_irq_install
和 drm_irq_uninstall
功能来促进IRQ处理程序的注册和注销。这些功能每个设备仅支持单个中断,使用多个IRQ的设备需要手动处理。
drm_irq_install和
drm_irq_uninstall
都是通过调用函数drm_dev_to_irq
获取设备的IRQ 。此内联函数将调用特定于总线的操作以检索IRQ号。对于平台设备,platform_get_irq
(...,0)用于检索IRQ号。
drm_irq_install
通过调用 irq_preinstall
驱动程序操作启动。该操作是可选的,必须确保在通过清除所有挂起的中断标志或禁用中断时不会触发中断。
然后,通过调用request_irq
来请求IRQ。如果设置了DRIVER_IRQ_SHARED驱动功能标志,则将请求共享(IRQF_SHARED)IRQ。
IRQ处理函数必须作为必需的irq_handler驱动程序操作提供。它会直接传递给 request_irq
,所有IRQ处理程序相同的原型。他将作为第二个参数被调用,一个指向DRM设备的指针。
最后,该函数调用可选的 irq_postinstall
驱动程序操作。该操作通常启用中断(vblank中断除外,vblank中断是单独启用的),但是驱动程序可以选择在其他时间启用/禁用中断。
drm_irq_uninstall与drm_irq_install
相似,用于卸载IRQ处理程序。首先唤醒所有等待vblank中断的进程,以确保它们不会挂起,然后调用可选的 irq_uninstall
驱动程序操作。该操作必须禁用所有硬件中断。最后,该函数通过调用 free_irq
释放IRQ。
每个DRM驱动程序都需要一个内存管理器,必须在加载时对其进行初始化。DRM当前包含两个内存管理器,即转换表管理器(TTM)和图形执行管理器(GEM)。本文档仅描述GEM内存管理器的用法。有关详细信息,请参见 “内存管理”部分。
现代Linux系统需要大量的图形内存来存储帧缓冲区,纹理,顶点和其他与图形相关的数据。考虑到许多数据的动态特性,因此有效地管理图形内存对于图形堆栈至关重要,并且在DRM基础架构中发挥着核心作用。
DRM核心包括两个内存管理器,即转换表映射(TTM)和图形执行管理器(GEM)。TTM是第一个开发的DRM内存管理器,并试图成为一种适用于所有人的解决方案。它提供了一个单一的用户空间API,可以满足所有硬件的需求,同时支持统一内存体系结构(UMA)设备和具有专用视频RAM的设备(即大多数离散视频卡)。这导致了一个庞大,复杂的代码片段,结果证明这些代码难以用于驱动程序开发。
GEM最初是由英特尔赞助的项目,以应对TTM的复杂性。它的设计理念是完全不同的:GEM没有为每个与图形内存相关的问题提供解决方案,而是确定了驱动程序之间的通用代码,并创建了一个共享它的支持库。与TTM相比,GEM的初始化和执行要求更简单,但没有视频RAM管理功能,因此仅限于UMA设备。
TTM设计背景和信息属于此处。
警告
本节已过时。
希望支持TTM的驱动程序必须填写drm_bo_driver结构。该结构包含几个带有函数指针的字段,这些函数指针用于初始化TTM,分配和释放内存,等待命令完成和栅栏同步以及内存迁移。有关用法的示例,请参见radeon_ttm.c文件。
ttm_global_reference结构由几个字段组成:
struct ttm_global_reference {
enum ttm_global_types global_type;
size_t size;
void *object;
int (*init) (struct ttm_global_reference *);
void (*release) (struct ttm_global_reference *);
};
整个内存管理器应该有一个全局引用结构,而内存管理器在运行时为每个对象创建的其他引用结构也应有。您的全局TTM应该具有TTM_GLOBAL_TTM_MEM类型。全局对象的大小字段应为sizeof(struct ttm_mem_global),并且init和release钩子应指向特定于驱动程序的init和release例程,这可能最终分别调用ttm_mem_global_init和ttm_mem_global_release。
通过调用ttm_global_item_ref()设置并初始化全局TTM记帐结构后,您需要创建一个缓冲区对象TTM,以提供一个供客户端和内核本身分配缓冲区池对象。该对象的类型应为TTM_GLOBAL_TTM_BO,其大小应为sizeof(struct ttm_bo_global)。同样,可以提供特定于驱动程序的初始化和释放功能,可能最终分别调用ttm_bo_global_init()和ttm_bo_global_release()。同样,与前一个对象一样,ttm_global_item_ref()用于为TTM创建初始引用计数,该计数将调用您的初始化函数。
GEM设计方法导致了内存管理器无法在其用户空间或内核API中提供所有(甚至所有常见)用例的完整覆盖。GEM向用户空间公开了一组与内存相关的标准操作,并向驱动程序提供了一组帮助程序功能,并允许驱动程序使用自己的私有API来实现特定于硬件的操作。[承上启下的中间层]
GEM-“图形执行管理器”文章中 介绍了GEM用户空间API 。尽管有些过时,但该文档很好地概述了GEM API原则。目前,使用特定于驱动程序的ioctl来实现缓冲区分配以及读写操作(作为通用GEM API的一部分进行描述)。
GEM与数据无关。它管理抽象缓冲区对象,而无需知道各个缓冲区包含哪些内容。因此,需要了解缓冲区内容或用途(例如缓冲区分配或同步原语)的API不在GEM的范围内,必须使用特定于驱动程序的ioctl来实现。
从根本上讲,GEM涉及以下几种操作:
- 内存分配和释放
- 命令执行
- 执行命令时的光圈管理//Aperture management at command execution time
缓冲区对象分配相对简单,并且主要由Linux的shmem层提供,后者提供了支持每个对象的内存。
特定于设备的操作(例如命令执行,固定,缓冲区读和写,映射和域所有权转移)留给特定于驱动程序的ioctl。
使用GEM的驱动程序必须在struct drm_driver driver_features
字段中设置DRIVER_GEM位 。然后,DRM内核将在调用load
操作之前自动初始化GEM内核 。在后台,这将创建DRM内存管理器对象,该对象提供用于对象分配的地址空间池。
在KMS配置中,如果硬件需要,驱动程序需要在核心GEM初始化之后分配和初始化命令环缓冲区。UMA设备通常具有所谓的“被盗”存储区,该存储区为初始帧缓冲区和设备所需的大而连续的存储区提供了空间。该空间通常不由GEM管理,必须单独初始化为它自己的DRM MM对象。
GEM将GEM对象的创建和支持它们的内存分配分为两个不同的操作。
GEM对象由struct drm_gem_object的实例表示 。驱动程序通常需要使用私有信息来扩展GEM对象,从而创建特定于驱动程序的GEM对象结构类型,以嵌入struct drm_gem_object的实例 。
要创建GEM对象,驱动程序会为其特定GEM对象类型的实例分配内存,并通过调用drm_gem_object_init来初始化嵌入的结构drm_gem_object。该函数需要指向DRM设备的指针,指向GEM对象的指针以及缓冲区对象的大小(以字节为单位)。
GEM使用shmem分配匿名可分页内存。 drm_gem_object_init
将创建所需大小的shmfs文件,并将其存储到struct drm_gem_object filp
字段中。当图形硬件直接使用系统内存时,该内存可用作对象的主要存储,否则用作后备存储。
驱动程序通过调用shmem_read_mapping_page_gfp
来负责每个页面实际的物理页面分配。请注意,它们可以在初始化GEM对象时决定分配页面,或者将分配延迟到需要内存之前(例如,由于用户空间内存访问而导致页面错误时,或者驱动程序需要启动涉及内存内容的DMA传输时)。
例如,当硬件需要物理上连续的系统内存时(例如嵌入式设备中的常见情况),并不总是需要匿名的可分页内存分配。驱动程序可以通过调用drm_gem_private_object_init
代替drm_gem_object_init
来初始化没有shmfs支持的GEM对象(称为私有GEM对象)。专用GEM对象的存储必须由驱动程序管理。
不需要使用私有信息扩展GEM对象的驱动程序可以调用drm_gem_object_alloc
函数来分配和初始化struct drm_gem_object 实例。用drm_gem_object_init
初始化GEM对象后,GEM内核将调用可选的驱动程序操作gem_init_object
。
int (*gem_init_object) (struct drm_gem_object *obj);
私有GEM对象不存在alloc-and-init函数。
所有GEM对象均由GEM内核引用计数。引用可以分别通过calling drm_gem_object_reference
和drm_gem_object_unreference
申请和释放。调用者必须持有drm_device struct_mutex
锁。为方便起见,GEM提供了drm_gem_object_reference_unlocked
和 drm_gem_object_unreference_unlocked
功能,无需锁即可调用它们。
当GEM对象的最后一个引用被释放时,GEM内核将调用drm_driver gem_free_object
操作。对于启用GEM的驱动程序,该操作是必需的,并且必须释放GEM对象和所有相关资源。
void (*gem_free_object) (struct drm_gem_object *obj);
驱动程序负责释放所有GEM对象资源,包括GEM核心创建的资源。如果已为对象创建了mmap偏移量(在这种情况下 drm_gem_object :: map_list
:: map
不为NULL),则必须通过调用drm_gem_free_mmap_offset
释放它。必须通过调用drm_gem_object_release
释放shmfs后备存储 (如果尚未创建shmfs后备存储,也可以安全地调用该函数)。
用户空间和内核之间的通信使用本地句柄,全局名称或更近期使用文件描述符来引用GEM对象。所有这些都是32位整数值。通常的Linux内核限制适用于文件描述符。
GEM句柄是DRM文件。应用程序通过特定于驱动程序的ioctl获取GEM对象的句柄,并且可以使用该句柄引用其他标准或特定于驱动程序的ioctl中的GEM对象。关闭DRM文件句柄将释放其所有GEM句柄并取消引用关联的GEM对象。
要为GEM对象驱动程序创建句柄,请调用 drm_gem_handle_create
。该函数获取指向DRM文件和GEM对象的指针,并返回本地唯一的句柄。当不再需要该句柄时,驱动程序通过调用drm_gem_handle_delete
来删除它 。最后,可以通过调用 drm_gem_object_lookup
来检索与句柄关联的GEM对象。
句柄不拥有GEM对象的所有权,它们仅引用在句柄销毁时将被删除的对象上。为避免泄漏GEM对象,驱动程序必须确保适当地删除其拥有的引用(例如在对象创建时获取的初始引用),而无需对句柄进行任何特殊考虑。例如,在dumb_create
操作的实现中结合了GEM对象和句柄创建的特定情况下 ,驱动程序必须在返回句柄之前删除对GEM对象的初始引用。
GEM名称在用途上类似于句柄,但不是DRM文件的本地名称。它们可以在进程之间传递,以全局引用GEM对象。名称不能直接用于引用DRM API中的对象,应用程序必须分别使用DRM_IOCTL_GEM_FLINK和DRM_IOCTL_GEM_OPEN ioctls将句柄转换为名称,并将名称转换为句柄。转换是由DRM核心处理的,不需要任何特定于驱动程序的支持。
与全局名称相似,GEM文件描述符也用于跨进程共享GEM对象。它们提供了额外的安全性:由于必须通过UNIX域套接字显式发送文件描述符以在应用程序之间共享,因此不能像全局唯一的GEM名称那样猜测它们。[GEM名称可以猜测并调用对应api]
支持GEM文件描述符(也称为DRM PRIME API)的驱动程序必须在struct drm_driver driver_features
字段中设置DRIVER_PRIME位 ,并实现 prime_handle_to_fd
和 prime_fd_to_handle
操作。
int (*prime_handle_to_fd)(struct drm_device *dev,
struct drm_file *file_priv, uint32_t handle,
uint32_t flags, int *prime_fd);
int (*prime_fd_to_handle)(struct drm_device *dev,
struct drm_file *file_priv, int prime_fd,
uint32_t *handle);
这两个操作将句柄转换为PRIME文件描述符,反之亦然。驱动程序必须使用内核dma-buf缓冲区共享框架来管理PRIME文件描述符。
非GEM驱动程序必须自己实现操作,而GEM驱动程序必须使用drm_gem_prime_handle_to_fd
和drm_gem_prime_fd_to_handle
帮助程序功能。这些帮助程序依靠驱动程序 gem_prime_export
和 gem_prime_import
操作从GEM对象(dma-buf exporter role)创建dma-buf实例,和从dma-buf实例(dma-buf importer role)创建GEM对象。
struct dma_buf * (*gem_prime_export)(struct drm_device *dev,
struct drm_gem_object *obj,
int flags);
struct drm_gem_object * (*gem_prime_import)(struct drm_device *dev,
struct dma_buf *dma_buf);
对于支持DRM PRIME的GEM驱动程序,这两个操作是必需的。
驱动程序可以使用辅助功能更简单的API的条款drm_gem_prime_export
和 drm_gem_prime_import
实现gem_prime_export
和gem_prime_import
。这些函数通过五个较低级别的驱动程序回调实现dma-buf支持:
导出回调:
- gem_prime_pin
(可选):准备要导出的GEM对象
- gem_prime_get_sg_table
:提供固定页面的分散/聚集表
- gem_prime_vmap
:vmap驱动程序导出的缓冲区// vmap a buffer exported by your driver
- gem_prime_vunmap
:映射驱动程序导出的缓冲区// vunmap a buffer exported by your driver
导入回调:
- gem_prime_import_sg_table
(导入):从另一个驱动程序的散点图/聚集表生成GEM对象
由于映射操作相当繁重,因此与通过将缓冲区映射到用户空间相比,GEM更喜欢通过驱动程序特定的ioctl实现对缓冲区的类似于读/写的访问。但是,当需要随机访问缓冲区(例如执行软件渲染)时,直接访问对象可能会更有效率。
mmap系统调用不能直接用于映射GEM对象,因为它们没有自己的文件句柄。当前共存在两种将GEM对象映射到用户空间的替代方法。第一种方法使用特定于驱动程序的ioctl进行映射操作,调用do_mmap
在后台进行 。这通常被认为是可疑的,似乎不建议使用支持GEM的新驱动程序,因此在此不再赘述。
第二种方法在DRM文件句柄上使用mmap系统调用。
void *mmap(void *addr, size_t length, int prot, int flags, int fd,
off_t offset);
DRM通过mmap offset参数传递的伪偏移量标识要映射的GEM对象。因此,在映射之前,GEM对象必须与假偏移量关联。为此,驱动程序必须在该对象上调用 drm_gem_create_mmap_offset
。该函数从池中分配一个假偏移范围,并将偏移量除以PAGE_SIZE的值存储在 obj->map_list.hash.key
中。如果已经为该对象分配了伪偏移,则必须小心不要调用drm_gem_create_mmap_offset
。可以通过将obj->map_list.map
其设置为non-NULL进行测试 。
分配后,假偏移值(obj->map_list.hash.key << PAGE_SHIFT
)必须以特定于驱动程序的方式传递给应用程序,然后可以用作mmap offset参数。
GEM核心提供了一种辅助方法drm_gem_mmap
来处理对象映射。该方法可以直接设置为mmap文件操作处理程序。它将基于偏移值查找GEM对象,并将VMA操作设置为 drm_driver gem_vm_ops
字段。请注意,drm_gem_mmap
这不会将内存映射到用户空间,而是依赖于驱动程序提供的故障处理程序来单独映射页面。
要使用drm_gem_mmap
,驱动程序必须填充struct drm_driver gem_vm_ops
字段,一个指向VM操作的指针。
struct vm_operations_struct *gem_vm_ops
struct vm_operations_struct {
void (*open)(struct vm_area_struct * area);
void (*close)(struct vm_area_struct * area);
int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf);
};
在open
与close
操作中必须更新GEM对象的引用计数。驱动程序可以直接使用drm_gem_vm_open
和 drm_gem_vm_close
帮助函数作为打开和关闭处理程序。
fault处理程序负责在发生页面错误时将各个页面映射到用户空间。根据内存分配方案,驱动程序可以在故障时分配页面,或者可以在创建对象时决定为GEM对象分配内存。
想要预先映射GEM对象而不是处理页面错误的驱动程序可以实现自己的mmap文件操作处理程序。
GEM API并未标准化GEM对象的创建,而是将其留给特定于驱动程序的ioctl。虽然对于包含特定于设备的用户空间组件(例如在libdrm中)的成熟图形堆栈来说这不是问题,但此限制使基于DRM的early-boot graphics不必要地复杂。
Dumb GEM对象通过提供一个标准API来创建适合于scanout的Dumb缓冲区,从而在一定程度上缓解了这个问题,然后可以使用它来创建KMS帧缓冲区。
为了支持dumb GEM对象程序必须实现 dumb_create
, dumb_destroy
和 dumb_map_offset
操作。
-
int (*dumb_create)(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args);
该
dumb_create
操作根据struct drm_mode_create_dumb 的宽度,高度和深度参数,创建适合于扫描的GEM对象。它用新创建的GEM对象的句柄及其行间距和大小(以字节为单位)填充参数的句柄、间距和大小字段。 -
int (*dumb_destroy)(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle);
dumb_destroy操作会销毁dumb_create创建的dumb GEM对象。
-
int (*dumb_map_offset)(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset);
该
dumb_map_offset
操作将mmap伪偏移与该句柄给定的GEM对象相关联并返回。驱动程序必须使用drm_gem_create_mmap_offset
函数关联虚假偏移量,如“ GEM对象映射”一节中 所述。
当映射到设备或在命令缓冲区中使用时,对象的备份页面将刷新到内存中并标记为写,以便与GPU保持一致。同样,如果在GPU完成渲染对象之后CPU访问对象,则必须使对象与CPU的内存视图保持一致,通常涉及各种GPU缓存刷新。该CPU <-> GPU一致性管理重点由特定于设备的ioctl提供,该ioctl评估对象的当前域并执行任何必要的刷新或同步操作以将对象放入所需的一致性域中(请注意,对象可能很忙,即有效的渲染目标;在这种情况下,在执行任何必要的刷新操作之前,需要设置域阻塞client端并等待渲染完成)。
对于GPU设备来说,最重要的GEM功能可能是为客户端提供一个命令执行接口。客户端程序构造包含对先前分配的内存对象的引用的命令缓冲区,然后将其提交给GEM。那时,GEM会小心地将所有对象绑定到GTT中,执行缓冲区,并在访问相同缓冲区的客户端之间提供必要的同步。这通常涉及从GTT中清除某些对象并重新绑定其他对象(这是一项相当昂贵的操作),并提供重定位支持,从而向客户端隐藏了固定的GTT偏移量。客户端必须注意不要提交引用比GTT中可容纳的对象更多的命令缓冲区;否则GEM将拒绝它们,并且不会进行渲染。同样,如果缓冲区中的多个对象要求分配fence寄存器以进行正确渲染(例如965之前的芯片上的2D Blits),则必须注意不要要求比客户端可用的更多的fence寄存器。这种资源管理应该从libdrm中的客户端中抽象出来。
驱动程序必须通过drm_mode_config_init
在DRM设备上调用来初始化模式设置核心 。该函数将初始化drm_device mode_config
字段,并且永远不会失败。完成后,必须通过初始化以下字段来设置模式配置。
-
int min_width, min_height; int max_width, max_height;
帧缓冲区的最小和最大宽度和高度,以像素为单位。
-
struct drm_mode_config_funcs *funcs;
模式设定函数。
struct drm_framebuffer *(*fb_create)(struct drm_device *dev,
struct drm_file *file_priv,
struct drm_mode_fb_cmd2 *mode_cmd);
帧缓冲区是抽象的内存对象,它提供像素源以扫描到CRTC[framebuffer -> crtc]。应用程序通过ioctl DRM_IOCTL_MODE_ADDFB(2)显式请求创建帧缓冲区,并接收不透明的句柄,该句柄可以传递给KMS CRTC控制器,平面配置和页面翻转功能。
帧缓冲区依靠底层内存管理器进行低级内存操作。在创建帧缓冲区时,应用程序通过参数传递内存句柄(或一系列内存句柄列表用于多平面模式)drm_mode_fb_cmd2
。本文档假定驱动程序使用GEM,因此这些句柄将引用GEM对象。
驱动程序必须首先验证通过mode_cmd传递的请求帧缓冲区参数。特别是在这里可以捕获无效的尺寸,像素格式或间距。
如果认为这些参数有效,则驱动程序将进行创建,初始化并返回struct drm_framebuffer的实例。如果需要,可以将实例嵌入更大的特定于驱动程序的结构中。驱动必须从drm_mode_fb_cmd2
参数传递的值中
填写的width
, height
,pitches
, offsets
,depth
, bits_per_pixel
和 pixel_format
字段。他们应该调用drm_helper_mode_fill_fb_struct
辅助函数来实现。
新的帧缓冲区实例的初始化通过调用drm_framebuffer_init
完成,该调用接收指向DRM帧缓冲区操作的指针(结构 drm_framebuffer_funcs)。请注意,此函数发布了帧缓冲区,因此从这一点出发,可以从其他线程并发访问它。因此,它必须是驱动程序的帧缓冲区初始化序列中的最后一步。帧缓冲区操作是
-
int (*create_handle)(struct drm_framebuffer *fb, struct drm_file *file_priv, unsigned int *handle);
创建用于底层内存对象的帧缓冲区的句柄。如果帧缓冲区使用多平面模式,则句柄将引用与第一个平面关联的内存对象。
驱动程序调用
drm_gem_handle_create
以创建句柄。 -
void (*destroy)(struct drm_framebuffer *framebuffer);
销毁帧缓冲区对象并释放所有关联的资源。驱动程序必须调用
drm_framebuffer_cleanup
释放由DRM核心分配给帧缓冲区对象的资源,并且必须确保取消与帧缓冲区关联的所有内存对象的引用。由create_handle
操作创建的句柄由 DRM核心释放。 -
int (*dirty)(struct drm_framebuffer *framebuffer, struct drm_file *file_priv, unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips);
此可选操作通知驱动程序,在响应DRM_IOCTL_MODE_DIRTYFB ioctl调用时,帧缓冲区的一个区域发生了变化。
drm帧缓冲区的生命周期由引用计数控制,驱动程序可以使用 drm_framebuffer_reference
获取额外的引用
drm_framebuffer_unreference
删除引用。对于未删除的最后一个引用的驱动程序专用帧缓冲区(例如,将结构drm_framebuffer嵌入fbdev helper结构中时的fbdev帧缓冲区 ),驱动程序可以在模块卸载时使用drm_framebuffer_unregister_private
手动清除帧缓冲区 。
void (*output_poll_changed)(struct drm_device *dev);
此操作通知驱动程序一个或多个连接器的状态已更改。使用fb助手的驱动程序可以调用drm_fb_helper_hotplug_event
函数来处理此操作。
KMS设备被抽象并公开为一组,平面(panel),CRTC,编码器(encoder)和连接器(connector)。因此,在初始化模式设置之后,KMS驱动程序必须在加载时创建和初始化所有这些对象。
CRTC是表示芯片一部分的抽象,其中包含指向扫描缓冲区的指针。因此,可用的CRTC数量决定了在任何给定时间可以激活多少个独立的扫描缓冲区。CRTC结构包含几个字段来支持此操作:指向某些视频内存的指针(抽象为帧缓冲区对象),显示模式,以及视频内存中的(x,y)偏移量以支持平移或配置,其中一个视频存储器跨越多个CRTC。
KMS设备必须创建并注册至少一个struct drm_crtc实例。该实例由驱动程序分配并归零(可能是较大结构的一部分),并通过指向CRTC函数的指针drm_crtc_init
调用进行注册。
int (*set_config)(struct drm_mode_set *set);
将新的CRTC配置应用于设备。该配置指定CRTC,要从中扫描的帧缓冲区,帧缓冲区中的(x,y)位置,显示模式以及与CRTC一起驱动的一组连接器。
如果配置中指定的帧缓冲区为NULL,则驱动程序必须分离所有连接到CRTC的编码器和所有连接到这些编码器的连接器,并禁用它们。
在保持模式配置锁定的情况下调用此操作。
注意
FIXME:set_config应该如何与DPMS[显示器电源管理信号(Display POWER MANAGEMENT Signalling)]交互?如果CRTC被睡眠,是否应该唤醒?
int (*page_flip)(struct drm_crtc *crtc, struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event);
将页面翻转安排到CRTC的给定帧缓冲区。在保持模式配置互斥锁的情况下调用此操作。
页面翻转是一种同步机制,可以在垂直消隐(vbank,打无效像素点的时候)期间将CRTC扫描出的帧缓冲区替换为新的帧缓冲区,从而避免撕裂。当应用程序请求翻页时,DRM内核将验证新的帧缓冲区是否足够大,以供CRTC在当前配置的模式下进行扫描,然后使用指向新帧缓冲区的指针调用CRTC的page_flip
操作。
该page_flip
实现翻页操作。一旦任何针对新帧缓冲区的等待渲染目标完成,CRTC将重新编程为在下一次垂直刷新后显示该帧缓冲区。该操作必须立即返回,而不必等待渲染或页面翻转完成,并且必须阻止任何新的渲染到帧缓冲区,直到页面翻转完成。
如果可以成功调度页面翻转,则驱动程序必须将该drm_crtc-<fb
字段设置为指向的新帧缓冲区fb
。这一点很重要,这样才能使基于帧缓冲区的引用计数保持平衡。
如果页面翻转已经挂起,则 page_flip
操作必须返回-EBUSY。
为了将页面翻转同步到垂直消隐,驱动程序可能需要启用垂直消隐中断。它应该为此目的而调用drm_vblank_get
,并在页面翻转完成后调用drm_vblank_put
。
如果应用程序在翻页完成时请求得到通知,则将使用指向drm_pending_vblank_event实例的非空事件参数调用page_flip操作。翻页完成后,驱动程序必须调用drm_send_vblank_event
填写事件并发送,以唤醒所有等待的进程。这可以用
spin_lock_irqsave(&dev->event_lock, flags);
...
drm_send_vblank_event(dev, pipe, event);
spin_unlock_irqrestore(&dev->event_lock, flags);
注意
FIXME:不需要等待渲染完成的驱动程序是否可以将事件添加到dev->vblank_event_list
,并让DRM内核处理所有事情,例如“正常的”垂直消隐事件?
在等待页面翻转完成时,驱动程序可以自由使用列表头event->base.link
,以将pending事件存储在特定于驱动程序的列表中。
如果在发出事件信号之前关闭了文件句柄,则驱动程序必须注意在其preclose
操作中销毁该事件 (并在需要时调用 drm_vblank_put
)。
-
void (*set_property)(struct drm_crtc *crtc, struct drm_property *property, uint64_t value);
将给定的CRTC属性的值设置为
value
。有关属性的更多信息,请参见“ KMS属性”一节。 -
void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start, uint32_t size);
将伽玛表应用于设备上。该操作是可选的。
-
void (*destroy)(struct drm_crtc *crtc);
不再需要时销毁CRTC。请参阅“ KMS初始化和清理”部分。
平面表示可以在扫描过程中与CRTC混合或叠加在CRTC顶部的图像源。平面与帧缓冲区关联,以裁剪图像存储器(源)的一部分,并可以选择将其缩放到目标大小。然后将结果与CRTC混合或叠加在CRTC之上。
平面是可选的。要创建平面,KMS驱动程序会分配struct drm_plane实例 (可能是较大结构的一部分)的实例并将其清零,并通过调用drm_plane_init
进行注册。该函数采用可与平面关联的CRTC的位掩码,指向平面函数的指针以及格式支持的格式的列表。
-
int (*update_plane)(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h);
使用给定的CRTC和帧缓冲区启用并配置平面以。
帧缓冲存储器坐标源矩形由给定
src_x
,src_y
,src_w
和src_h
参数(如16.16固定点值)。不支持亚像素平面坐标的设备可以忽略小数部分。CRTC坐标目标矩形由给定
crtc_x
,crtc_y
,crtc_w
和crtc_h
参数(如整数的值)。设备将源矩形缩放为目标矩形。如果不支持缩放,并且源矩形大小与目标矩形大小不匹配,则驱动程序必须返回-EINVAL错误。 -
int (*disable_plane)(struct drm_plane *plane);
禁用plane。DRM内核调用此方法以响应将帧缓冲区ID设置为0的DRM_IOCTL_MODE_SETPLANE ioctl调用。CRTC不得处理禁用的平面。
-
void (*destroy)(struct drm_plane *plane);
不再需要时销毁Plane。请参阅“ KMS初始化和清理”部分。
编码器从CRTC获取像素数据,并将其转换为适合任何连接着的连接器的格式。在某些设备上,CRTC可能会向多个编码器发送数据。在那种情况下,多个个编码器都将从同一个扫描缓冲区接收数据,从而导致跨连接到每个编码器的连接器的“克隆”显示配置。
对于CRTC,KMS驱动程序必须创建,初始化和注册至少一个struct drm_encoder实例。该实例由驱动程序分配并归零,可能是较大结构的一部分。
驱动程序必须在注册编码器之前初始化struct drm_encoder 的possible_crtcs
和 possible_clones
字段。这两个字段分别是:编码器可以连接到的CRTC的位掩码,用于克隆的同级编码器。
初始化后,必须调用drm_encoder_init
注册编码器 。该函数获取指向编码器功能的指针和编码器类型。支持的类型是
- DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A
- DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort
- DRM_MODE_ENCODER_LVDS for display panels
- DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video, Component, SCART)
- DRM_MODE_ENCODER_VIRTUAL for virtual machine displays
编码器必须连接到CRTC才能使用。DRM驱动程序在初始化时不附加编码器。应用程序(或实现时的fbdev兼容性层)负责将其要使用的编码器附加到CRTC。
连接器是设备上像素数据的最终目的地,通常直接连接到外部显示设备,例如显示器或笔记本电脑面板。一个连接器一次只能连接到一个编码器。连接器也是保留有关显示器信息的附加结构,因此它包含显示数据,EDID数据,DPMS和连接状态, 以及有关显示器支持的模式的信息的字段。
最后,KMS驱动程序必须创建,初始化,注册并附加至少一个struct drm_connector实例。该实例将与其他KMS对象一起创建,并通过设置以下字段进行初始化。
interlace_allowed
连接器是否可以处理隔行模式。
doublescan_allowed
连接器是否可以处理双重扫描。
display_info
当检测到显示时,显示信息由EDID信息填充。对于非热插拔显示器(例如嵌入式系统中的平板),驱动程序应初始化 display_info.
width_mm
和 display_info.
height_mm
,带有显示器实际尺寸的字段。
连接器轮询模式,组合以下属性
DRM_CONNECTOR_POLL_HPD
连接器会生成热插拔事件,不需要定期进行轮询。不能将CONNECT和DISCONNECT标志与HPD标志一起设置。
DRM_CONNECTOR_POLL_CONNECT
定期轮询连接器以进行连接。
DRM_CONNECTOR_POLL_DISCONNECT
定期轮询连接器是否断开连接。
将不支持连接状态发现的连接器设置为0。
然后,使用指向连接器功能和连接器类型的指针调用drm_connector_init
来注册该连接器,并通过调用drm_sysfs_connector_add
将其显示公开到sysfs 。
支持的连接器类型为
- DRM_MODE_CONNECTOR_VGA
- DRM_MODE_CONNECTOR_DVII
- DRM_MODE_CONNECTOR_DVID
- DRM_MODE_CONNECTOR_DVIA
- DRM_MODE_CONNECTOR_Composite
- DRM_MODE_CONNECTOR_SVIDEO
- DRM_MODE_CONNECTOR_LVDS
- DRM_MODE_CONNECTOR_Component
- DRM_MODE_CONNECTOR_9PinDIN
- DRM_MODE_CONNECTOR_DisplayPort
- DRM_MODE_CONNECTOR_HDMIA
- DRM_MODE_CONNECTOR_HDMIB
- DRM_MODE_CONNECTOR_TV
- DRM_MODE_CONNECTOR_eDP
- DRM_MODE_CONNECTOR_VIRTUAL
连接器必须连接到编码器上才能使用。对于将连接器1:1映射至编码器的设备,应在初始化时通过调用drm_mode_connector_attach_encoder
来连接该连接器 。驱动程序还必须设置drm_connector encoder
字段为指向附加的编码器。
最后,驱动程序必须通过调用drm_kms_helper_poll_init
来初始化连接器状态更改检测。如果至少一个连接器是可轮询的,但不能生成热插拔中断(由DRM_CONNECTOR_POLL_CONNECT和DRM_CONNECTOR_POLL_DISCONNECT连接器标志指示),则延迟的工作将自动排队以定期轮询更改。可以生成热插拔中断的连接器必须改用DRM_CONNECTOR_POLL_HPD标志进行标记,并且它们的中断处理程序必须调用drm_helper_hpd_irq_event
。该功能将延迟的工作进行排序,以检查所有连接器的状态,但不会进行定期轮询。
注意
除非另有说明,否则所有操作都是强制性的。
void (*dpms)(struct drm_connector *connector, int mode);
DPMS操作设置连接器的电源状态。模式参数是以下之一
-
DRM_MODE_DPMS_ON
-
DRM_MODE_DPMS_STANDBY
-
DRM_MODE_DPMS_SUSPEND
-
DRM_MODE_DPMS_OFF
在除DPMS_ON模式以外的所有模式下,连接器所连接的编码器均应通过适当地驱动其信号,将显示器置于低功耗模式。如果编码器上连接了多个连接器,则应注意不要改变其他显示器的电源状态。当所有相关的连接器都置于低功耗模式时,应将低功耗模式传播到编码器和CRTC。[逻辑类似于suspend/resume]
int (*fill_modes)(struct drm_connector *connector, uint32_t max_width,
uint32_t max_height);
用连接器支持的所有模式填充模式列表。如果 max_width
和max_height
参数不为零,则实现必须忽略大于max_width
或大于max_height
的所有模式。
连接器还必须以连接的显示器物理尺寸(以毫米为单位)填写其display_info
width_mm
和height_mm
字段。如果该值未知或不适用(例如,对于投影仪设备),则应将字段设置为0。
如果支持,则通过轮询或热插拔事件更新连接状态(请参阅参考资料polled
)。状态值通过ioctls报告给用户空间,并且不能在驱动程序内部使用,因为状态值只能从用户空间调用drm_mode_getconnector
来初始化 。
enum drm_connector_status (*detect)(struct drm_connector *connector,
bool force);
检查连接器上是否连接了任何东西。force参数在轮询时设置为false,在根据用户请求检查连接器时设置为true。在自动探测期间,驱动程序可以使用force来避免昂贵的、破坏性的操作。
如果连接器已连接东西,则返回connector_status_connected;如果未连接任何东西,则返回connector_status_disconnected;如果连接状态未知,则返回connector_status_unknown。
如果连接状态确实被探测为已连接,驱动程序应该只返回connector_status_connected。无法检测连接状态或连接状态探测失败的连接器应该返回connector_status_unknown。
-
void (*set_property)(struct drm_connector *connector, struct drm_property *property, uint64_t value);
将给定连接器属性的值设置为
value
。有关属性 的更多信息,请参见“ KMS属性”一节。 -
void (*destroy)(struct drm_connector *connector);
不再需要时销毁连接器。请参阅 “ KMS初始化和清理”。
DRM核心管理其对象的生存周期。当不再需要某个对象时,内核调用其destroy函数,该函数必须清除并释放为该对象分配的所有资源。每个 drm_*_init
调用必须与相应的drm_*_cleanup
调用相匹配,以清理CRTC(drm_crtc_cleanup
),平面(drm_plane_cleanup
),编码器(drm_encoder_cleanup
)和连接器(drm_connector_cleanup
)。此外,在调用drm_connector_cleanup之前,必须通过调用drm_sysfs_connector_remove来删除添加到sysfs中的连接器。
连接器状态更改检测必须通过调用 drm_kms_helper_poll_fini
进行清理。
void intel_crt_init(struct drm_device *dev)
{
struct drm_connector *connector;
struct intel_output *intel_output;
intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
if (!intel_output)
return;
connector = &intel_output->base;
drm_connector_init(dev, &intel_output->base,
&intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);
drm_encoder_init(dev, &intel_output->enc, &intel_crt_enc_funcs,
DRM_MODE_ENCODER_DAC);
drm_mode_connector_attach_encoder(&intel_output->base,
&intel_output->enc);
/* Set up the DDC bus. */
intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
if (!intel_output->ddc_bus) {
dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
"failed.\n");
return;
}
intel_output->type = INTEL_OUTPUT_ANALOG;
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs);
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
drm_sysfs_connector_add(connector);
}
在上面的示例(取自i915驱动程序)中,创建了CRTC,连接器和编码器组合。还创建了特定于设备的i2c总线,以获取EDID数据并执行监视器检测。完成该过程后,将向sysfs注册新连接器,使其属性对应用程序可用。
名称
drm_modeset_lock_all —获取所有模式集锁
概要
void fsfuncdrm_modeset_lock_all ( dev);
struct drm_device * dev;
名称
drm_modeset_unlock_all —删除所有模式集锁
概要
void fsfuncdrm_modeset_unlock_all ( dev);
struct drm_device * dev;
名称
drm_warn_on_modeset_not_all_locked —检查是否已锁定所有模式设置锁
概要
void fsfuncdrm_warn_on_modeset_not_all_locked ( dev);
struct drm_device * dev;
名称
drm_mode_object_find —查找具有静态生存期的drm对象
概要
struct drm_mode_object * fsfuncdrm_mode_object_find ( dev,
id,
type);
struct drm_device * dev;
uint32_t id;
uint32_t type;
名称
drm_framebuffer_init —初始化帧缓冲区
概要
int fsfuncdrm_framebuffer_init ( dev,
fb,
funcs);
struct drm_device * dev;
struct drm_framebuffer * fb;
const struct drm_framebuffer_funcs * funcs;
名称
drm_framebuffer_lookup —查找drm帧缓冲区并获取引用
概要
struct drm_framebuffer * fsfuncdrm_framebuffer_lookup ( dev,
id);
struct drm_device * dev;
uint32_t id;
名称
drm_framebuffer_unreference —取消引用帧缓冲区
概要
void fsfuncdrm_framebuffer_unreference ( fb);
struct drm_framebuffer * fb;
名称
drm_framebuffer_reference —增加fb引用
概要
void fsfuncdrm_framebuffer_reference ( fb);
struct drm_framebuffer * fb;
名称
drm_framebuffer_unregister_private —查找IDR中注销私有fb
概要
void fsfuncdrm_framebuffer_unregister_private ( fb);
struct drm_framebuffer * fb;
名称
drm_framebuffer_cleanup —删除一个帧缓冲对象
概要
void fsfuncdrm_framebuffer_cleanup ( fb);
struct drm_framebuffer * fb;
名称
drm_framebuffer_remove —删除和取消引用帧缓冲区对象
概要
void fsfuncdrm_framebuffer_remove (fb);
struct drm_framebuffer * fb;
名称
drm_crtc_init —初始化新的CRTC对象
概要
int fsfuncdrm_crtc_init ( dev,
crtc,
funcs);
struct drm_device * dev;
struct drm_crtc * crtc;
const struct drm_crtc_funcs * funcs;
名称
drm_mode_probed_add —将模式添加到连接器的探测模式列表
概要
void fsfuncdrm_mode_probed_add ( connector,
mode);
struct drm_connector * connector;
struct drm_display_mode * mode;
名称
drm_connector_init —初始化预分配的连接器
概要
int fsfuncdrm_connector_init ( dev,
connector,
funcs,
connector_type);
struct drm_device * dev;
struct drm_connector * connector;
const struct drm_connector_funcs * funcs;
int connector_type;
名称
drm_connector_cleanup —清理一个初始化的连接器
概要
void fsfuncdrm_connector_cleanup ( connector);
struct drm_connector * connector;
名称
drm_plane_init —初始化一个新的平面对象
概要
int fsfuncdrm_plane_init ( dev,
plane,
possible_crtcs,
funcs,
formats,
format_count,
priv);
struct drm_device * dev;
struct drm_plane * plane;
unsigned long possible_crtcs;
const struct drm_plane_funcs * funcs;
const uint32_t * formats;
uint32_t format_count;
bool priv;
名称
drm_plane_force_disable —强制禁用plane
概要
void fsfuncdrm_plane_force_disable ( plane);
struct drm_plane * plane;
名称
drm_mode_create —创建新的显示模式
概要
struct drm_display_mode * fsfuncdrm_mode_create ( dev);
struct drm_device * dev;
名称
drm_mode_destroy —删除模式
概要
void fsfuncdrm_mode_destroy ( dev,
mode);
struct drm_device * dev;
struct drm_display_mode * mode;
名称
drm_mode_create_dvi_i_properties —创建特定于DVI-I的连接器属性
概要
int fsfuncdrm_mode_create_dvi_i_properties ( dev);
struct drm_device * dev;
名称
drm_mode_create_tv_properties —创建TV特定的连接器属性
概要
int fsfuncdrm_mode_create_tv_properties ( dev,
num_modes,
modes[]);
struct drm_device * dev;
int num_modes;
char * modes[];
名称
drm_mode_create_scaling_mode_property-创建缩放模式属性
概要
int fsfuncdrm_mode_create_scaling_mode_property ( dev);
struct drm_device * dev;
名称
drm_mode_create_dirty_info_property-创建脏属性
概要
int fsfuncdrm_mode_create_dirty_info_property ( dev);
struct drm_device * dev;
名称
drm_mode_set_config_internal —辅助调用-> set_config函数
概要
int fsfuncdrm_mode_set_config_internal (set);
struct drm_mode_set * set;
名称
drm_format_plane_cpp —确定每个像素的字节值
概要
int fsfuncdrm_format_plane_cpp (format,
plane);
uint32_t format;
int plane;
名称
drm_format_horz_chroma_subsampling —获取水平色度子采样因子
概要
int fsfuncdrm_format_horz_chroma_subsampling (format);
uint32_t format;
名称
drm_format_vert_chroma_subsampling —获取垂直色度子采样因子
概要
int fsfuncdrm_format_vert_chroma_subsampling (format);
uint32_t format;
驱动程序提供DRM API实现CRTC,编码器和连接器的功能。它们(DRM_API)由DRM核心和ioctl处理程序调用以处理设备状态更改和配置请求。由于实现这些功能通常需要特定于驱动程序的逻辑,因此可以使用中间层帮助程序功能来避免重复样板代码。
DRM核心包含一个中间层实现。中间层提供了几个CRTC,编码器和连接器功能的实现(从中间层的顶部调用),这些功能可预处理请求并调用驱动程序提供的较低级功能(在中间层的底部) 。例如,该 drm_crtc_helper_set_config
函数可用于填充struct drm_crtc_funcs 的set_config
字段。调用时,它将把set_config
操作拆分为更小,更简单的操作,并调用驱动程序进行处理。
要使用中间层,驱动程序调用drm_crtc_helper_add
, drm_encoder_helper_add
以及 drm_connector_helper_add
功能安装他们中间层底部的操作处理,并填写 drm_crtc_funcs, drm_encoder_funcs和 drm_connector_funcs结构的指针到中间层顶API函数。最好在注册相应的KMS对象之后立即安装中间层底部操作处理程序。
中间层未在CRTC,编码器和连接器操作之间划分。要使用它,驱动程序必须为所有三个KMS实体提供底层功能。
-
int drm_crtc_helper_set_config(struct drm_mode_set *set);
该
drm_crtc_helper_set_config
辅助函数是一个CRTCset_config
的实现。它首先尝试通过调用连接器best_encoder
帮助程序,来找到每个连接器的最佳编码器。找到合适的编码器后,帮助函数将调用
mode_fixup
编码器和CRTC帮助操作来调整请求的模式;或者完全拒绝它,在这种情况下,错误将返回给应用程序。如果模式调整后的新配置与当前配置相同,则辅助功能将返回而不执行任何其他操作。如果调整后的模式与当前模式相同,但需要对帧缓冲区进行更改,则该
drm_crtc_helper_set_config
函数将调用CRTCmode_set_base
帮助操作。如果从当前模式的调整模式的不同,或者如果mode_set_base
辅助操作未提供时,则辅助函数通过调用prepare、mode_set和commit CRTC和编码器帮助操作来执行完整的模式集序列。 -
void drm_helper_connector_dpms(struct drm_connector *connector, int mode);
该
drm_helper_connector_dpms
辅助函数是一个连接器dpms实现,它跟踪连接器的电源状态。要使用该功能,驱动程序必须为CRTC和编码器提供dpms
辅助函数,以将DPMS状态应用于设备。中间层不跟踪CRTC和编码器的电源状态。
dpms
辅助操作因此可以被相同于当前活动的模式的模式调用。 -
int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY);
该
drm_helper_probe_single_connector_modes
辅助函数是一个连接器fill_modes
实现,它更新连接器的连接状态,然后通过调用连接器get_modes助手操作检索模式列表。该功能会滤除大于
max_width
和max_height
指定的模式 。然后,它调用连接器 帮助程序操作mode_valid
为所探测列表中的每种模式,以检查该模式是否对连接器有效。
-
bool (*mode_fixup)(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
让CRTC调整请求的模式或完全拒绝它。如果模式被接受(可能在调整之后),则此操作返回true;如果模式被拒绝,则返回false。
如果该
mode_fixup
操作无法合理使用,则应拒绝该模式。在这种情况下,“合理”的定义目前是模糊的。一种可能的行为是,当将固定模式面板与能够缩放的硬件一起使用时,将调整后的模式设置为面板定时。另一行为是接受任何输入模式并将其调整为硬件支持的最接近模式(FIXME:这需要澄清)。 -
int (*mode_set_base)(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb)
将当前帧缓冲区(存储在
crtc->fb
中)上的CRTC 移到位置(x,y)。帧缓冲区,x位置或y位置中的任何一个都可能已被修改。此辅助操作是可选的。如果未提供,该
drm_crtc_helper_set_config
功能将退回到mode_set
帮助程序操作。注意
FIXME:为什么x和y作为参数传递,因为可以通过
crtc->x
和 访问它们crtc->y
? -
void (*prepare)(struct drm_crtc *crtc);
准备CRTC以进行模式设置。验证请求的模式后,将调用此操作。驱动程序使用它来执行设置新模式之前所需的设备特定操作。
-
int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb);
设置新的模式,位置和帧缓冲区。根据设备要求,该模式可以由驱动程序在内部存储并在
commit
操作中应用,或立即编程到硬件。如果
mode_set
成功,则操作返回0;如果发生错误,则返回负错误代码。 -
void (*commit)(struct drm_crtc *crtc);
提交模式。设置新模式后将调用此操作。返回时,设备必须使用新模式并可以完全操作。
-
bool (*mode_fixup)(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
让编码器调整请求的模式或完全拒绝它。如果模式被接受(可能在调整之后),则此操作返回true;如果模式被拒绝,则返回false。有关允许的调整的说明,请参见 mode_fixup CRTC辅助操作。
-
void (*prepare)(struct drm_encoder *encoder);
准备编码器以进行模式设置。验证请求的模式后,将调用此操作。驱动程序使用它来执行设置新模式之前所需的设备特定操作。
-
void (*mode_set)(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
设置新模式。根据设备要求,该模式可以由驱动程序在内部存储并在
commit
操作中应用,或立即编程到硬件。 -
void (*commit)(struct drm_encoder *encoder);
提交模式。设置新模式后将调用此操作。返回时,设备必须使用新模式并可以完全操作。
-
struct drm_encoder *(*best_encoder)(struct drm_connector *connector);
返回一个指向此连接器的最佳编码器的指针。连接器1:1映射到编码器的设备只需返回到关联的编码器指针即可。此操作是强制性的。
-
int (*get_modes)(struct drm_connector *connector);
使用drm_add_edid_modes解析EDID数据,或者直接为每个支持的模式调用drm_mode_probed_add,以填充连接器的probed_modes列表,并返回它已检测到的模式数目。此操作是强制性的。
手动添加模式时,驱动程序会通过调用
drm_mode_create
来创建每个模式,必须填写以下字段。-
__u32 type;
模式类型位掩码,组合
DRM_MODE_TYPE_BUILTIN
不曾用过?
DRM_MODE_TYPE_CLOCK_C
不曾用过?
DRM_MODE_TYPE_CRTC_C
不曾用过?
DRM_MODE_TYPE_PREFERRED-连接器的首选模式
不曾用过?
DRM_MODE_TYPE_DEFAULT
不曾用过?
DRM_MODE_TYPE_USERDEF
不曾用过?
DRM_MODE_TYPE_DRIVER
该模式已由驱动程序创建(与用户创建的模式相反)。
驱动程序必须为他们创建的所有模式设置DRM_MODE_TYPE_DRIVER位,并为首选模式设置DRM_MODE_TYPE_PREFERRED位。
-
__u32 clock;
像素时钟频率(kHz)
-
__u16 hdisplay, hsync_start, hsync_end, htotal; __u16 vdisplay, vsync_start, vsync_end, vtotal;
水平和垂直时序信息
Active Front Sync Back Region Porch Porch <-----------------------><----------------><-------------><--------------> //| // | // |.................. ................ _______________ <----- [hv]display -----> <------------- [hv]sync_start ------------> <--------------------- [hv]sync_end ---------------------> <-------------------------------- [hv]total ----------------------------->
-
__u16 hskew; __u16 vscan;
未知
-
__u32 flags;
模式标志,组合包含
DRM_MODE_FLAG_PHSYNC
水平同步为高电平有效
DRM_MODE_FLAG_NHSYNC
水平同步为低电平有效
DRM_MODE_FLAG_PVSYNC
垂直同步高电平有效
DRM_MODE_FLAG_NVSYNC
垂直同步为低电平有效
DRM_MODE_FLAG_INTERLACE
模式隔行
DRM_MODE_FLAG_DBLSCAN
模式使用双重扫描
DRM_MODE_FLAG_CSYNC
模式使用复合同步
DRM_MODE_FLAG_PCSYNC
复合同步高电平有效
DRM_MODE_FLAG_NCSYNC
复合同步低电平有效
DRM_MODE_FLAG_HSKEW
提供hskew(未使用?)
DRM_MODE_FLAG_BCAST
不曾用过?
DRM_MODE_FLAG_PIXMUX
不曾用过?
DRM_MODE_FLAG_DBLCLK
不曾用过?
DRM_MODE_FLAG_CLKDIV2
?
请注意,如果连接器的
interlace_allowed
或doublescan_allowed
字段设置为0 ,则标记为INTERLACE或DBLSCAN标志的模式将被drm_helper_probe_single_connector_modes
滤除 。 -
char name[DRM_DISPLAY_MODE_LEN];
模式名称。填充相应的字段后,驱动程序必须调用drm_mode_set_name来填充来自hdisplay、vdisplay和interlace标志的模式名,然后填充相应的字段。
该
vrefresh
值由drm_helper_probe_single_connector_modes
计算 。解析EDID数据时,
drm_add_edid_modes
填写连接器display_info
width_mm
和height_mm
字段。手动创建模式时,如果尚未设置字段,则get_modes
辅助操作必须设置display_info
width_mm
和height_mm
字段(例如,初始化时在将固定大小的面板连接到连接器)。模式width_mm
和height_mm
字段仅在EDID解析期间在内部使用,在手动创建模式时不应设置。 -
-
int (*mode_valid)(struct drm_connector *connector, struct drm_display_mode *mode);
验证模式对于连接器是否有效。对于支持的模式,返回MODE_OK;对于不支持的模式,返回枚举drm_mode_status值(MODE_ *)之一。此操作是强制性的。
由于模式拒绝原因目前不用于立即删除不受支持的模式,因此实现可以返回MODE_BAD,而不管模式无效的确切原因是什么。
注意
注意,mode_valid helper操作只对设备检测到的模式调用,而不会对用户通过CRTC set_config操作设置的模式调用。
名称
drm_helper_move_panel_connectors_to_head —将面板移到连接器列表的最前面
概要
void fsfuncdrm_helper_move_panel_connectors_to_head (dev);
struct drm_device * dev;
名称
drm_helper_probe_single_connector_modes —获取完整的显示模式集
概要
int fsfuncdrm_helper_probe_single_connector_modes (connector,
maxX,
maxY);
struct drm_connector * connector;
uint32_t maxX;
uint32_t maxY;
名称
drm_helper_encoder_in_use —检查是否正在使用给定的编码器
概要
bool fsfuncdrm_helper_encoder_in_use ( encoder);
struct drm_encoder * encoder;
名称
drm_helper_crtc_in_use —检查给定的CRTC是否在mode_config中
概要
bool fsfuncdrm_helper_crtc_in_use ( crtc);
struct drm_crtc * crtc;
名称
drm_helper_disable_unused_functions —禁用未使用的对象
概要
void fsfuncdrm_helper_disable_unused_functions (dev);
struct drm_device * dev;
名称
drm_crtc_helper_set_mode —设置模式的内部辅助
概要
bool fsfuncdrm_crtc_helper_set_mode ( crtc,
mode,
x,
y,
old_fb);
struct drm_crtc * crtc;
struct drm_display_mode * mode;
int x;
int y;
struct drm_framebuffer * old_fb;
名称
drm_crtc_helper_set_config-从用户空间设置新配置
概要
int fsfuncdrm_crtc_helper_set_config ( set);
struct drm_mode_set * set;
fb帮助程序功能可用于在drm内核模式设置驱动程序之上提供fbdev。它们可以独立于许多驱动程序用来实现内核模式设置接口的crtc helper函数来独立使用。
初始化完成需要有三个步骤drm_fb_helper_init
, drm_fb_helper_single_add_all_connectors
和drm_fb_helper_initial_config
。要求比默认行为更高的驱动程序可以使用自己的代码覆盖第二步。拆卸通过drm_fb_helper_fini
完成。
在运行时,驱动程序应通过从其-> lastclose回调中进行调用drm_fb_helper_restore_fbdev_mode
来还原fbdev控制台 。他们还应该通过调用drm_fb_helper_hotplug_event
来通知fb辅助程序有关输出配置更新的信息。为了更轻松地与drm_crtc_helper.c中的输出轮询代码集成,模式集代码被提供一个-> output_poll_changed回调。
fb帮助程序库导出的所有其他函数均可用于由驱动程序实现fbdev驱动程序接口。
名称
drm_fb_helper_single_add_all_connectors —将所有连接器添加到fbdev仿真助手
概要
int fsfuncdrm_fb_helper_single_add_all_connectors (fb_helper);
struct drm_fb_helper * fb_helper;
名称
drm_fb_helper_debug_enter —-> fb_debug_enter的实现
概要
int fsfunc drm_fb_helper_debug_enter (info);
struct fb_info * info;
名称
drm_fb_helper_debug_leave —-> fb_debug_leave的实现
概要
int fsfunc drm_fb_helper_debug_leave (info);
struct fb_info * info;
名称
drm_fb_helper_restore_fbdev_mode —恢复fbdev配置
概要
bool fsfunc drm_fb_helper_restore_fbdev_mode (fb_helper);
struct drm_fb_helper * fb_helper;
名称
drm_fb_helper_blank —-> fb_blank的实现
概要
int fsfunc drm_fb_helper_blank (blank,
info);
int blank;
struct fb_info * info;
名称
drm_fb_helper_init —初始化一个drm_fb_helper结构
概要
int fsfunc drm_fb_helper_init ( dev,
fb_helper,
crtc_count,
max_conn_count);
struct drm_device * dev;
struct drm_fb_helper * fb_helper;
int crtc_count;
int max_conn_count;
参数
dev
DRM设备
fb_helper
驱动程序分配的fbdev助手结构进行初始化
crtc_count
此fbdev仿真中支持的crtcs的最大数量
max_conn_count
最大连接器数
名称
drm_fb_helper_setcmap —-> fb_setcmap的实现
概要
int fsfuncdrm_fb_helper_setcmap ( cmap,
info);
struct fb_cmap * cmap;
struct fb_info * info;
名称
drm_fb_helper_check_var —-> fb_check_var的实现
概要
int fsfuncdrm_fb_helper_check_var ( var,
info);
struct fb_var_screeninfo * var;
struct fb_info * info;
名称
drm_fb_helper_set_par —-> fb_set_par的实现
概要
int fsfuncdrm_fb_helper_set_par ( info);
struct fb_info * info;
名称
drm_fb_helper_pan_display —-> fb_pan_display的实现
概要
int fsfuncdrm_fb_helper_pan_display ( var,
info);
struct fb_var_screeninfo * var;
struct fb_info * info;
名称
drm_fb_helper_fill_fix —初始化固定的fbdev信息
概要
void fsfuncdrm_fb_helper_fill_fix ( info,
pitch,
depth);
struct fb_info * info;
uint32_t pitch;
uint32_t depth;
名称
drm_fb_helper_fill_var —初始化变量fbdev信息
概要
void fsfuncdrm_fb_helper_fill_var ( info,
fb_helper,
fb_width,
fb_height);
struct fb_info * info;
struct drm_fb_helper * fb_helper;
uint32_t fb_width;
uint32_t fb_height;
名称
drm_fb_helper_initial_config —设置合理的初始连接器配置
概要
bool fsfuncdrm_fb_helper_initial_config ( fb_helper,
bpp_sel);
struct drm_fb_helper * fb_helper;
int bpp_sel;
名称
drm_fb_helper_hotplug_event —通过探测附加到fb的所有输出来响应热插拔通知
概要
int fsfuncdrm_fb_helper_hotplug_event ( fb_helper);
struct drm_fb_helper * fb_helper;
名称
struct drm_fb_helper_funcs — fbdev仿真库的驱动程序回调
概要
struct drm_fb_helper_funcs {
void (* gamma_set) (struct drm_crtc *crtc, u16 red, u16 green,u16 blue, int regno);
void (* gamma_get) (struct drm_crtc *crtc, u16 *red, u16 *green,u16 *blue, int regno);
int (* fb_probe) (struct drm_fb_helper *helper,struct drm_fb_helper_surface_size *sizes);
bool (* initial_config) (struct drm_fb_helper *fb_helper,struct drm_fb_helper_crtc **crtcs,struct drm_display_mode **modes,bool *enabled, int width, int height);
};
这些功能包含各种抽象级别的一些通用逻辑和帮助程序,用于处理Display Port接收器设备和相关内容,例如DP辅助通道传输,在DP辅助通道上读取EDID,解码某些DPCD块,...
名称
drm_get_edid —获取EDID数据(如果有)
概要
struct edid * fsfuncdrm_get_edid ( connector,
adapter);
struct drm_connector * connector;
struct i2c_adapter * adapter;
名称
drm_match_cea_mode —查找与给定模式匹配的CEA模式
概要
u8 fsfuncdrm_match_cea_mode ( to_match);
const struct drm_display_mode * to_match;
名称
drm_edid_to_eld —从EDID生成ELD
概要
void fsfuncdrm_edid_to_eld ( connector,
edid);
struct drm_connector * connector;
struct edid * edid;
名称
drm_edid_to_sad —从EDID中提取SAD
概要
int fsfuncdrm_edid_to_sad ( edid,
sads);
struct edid * edid;
struct cea_sad ** sads;
名称
drm_edid_to_speaker_allocation —从EDID中提取扬声器分配的数据块
概要
int fsfuncdrm_edid_to_speaker_allocation ( edid,
sadb);
struct edid * edid;
u8 ** sadb;
名称
drm_av_sync_delay — HDMI / DP接收器音频-视频同步延迟(以毫秒为单位)
概要
int fsfuncdrm_av_sync_delay ( connector,
mode);
struct drm_connector * connector;
struct drm_display_mode * mode;
名称
drm_select_eld —从多个HDMI / DP接收器中选择一个ELD
概要
struct drm_connector * fsfuncdrm_select_eld ( encoder,
mode);
struct drm_encoder * encoder;
struct drm_display_mode * mode;
名称
drm_detect_hdmi_monitor —检测显示器是否为hdmi。
概要
bool fsfuncdrm_detect_hdmi_monitor ( edid);
struct edid * edid;
名称
drm_detect_monitor_audio —检查监视器音频功能
概要
bool fsfuncdrm_detect_monitor_audio ( edid);
struct edid * edid;
名称
drm_rgb_quant_range_selectable — RGB量化范围可以选择吗?
概要
bool fsfuncdrm_rgb_quant_range_selectable ( edid);
struct edid * edid;
描述
检查显示器是否报告支持的RGB量化范围选择。然后可以使用AVI信息帧通知监视器使用了哪个量化范围(完整或有限)。
名称
drm_add_edid_modes —从EDID数据添加模式(如果有)
概要
int fsfuncdrm_add_edid_modes ( connector,
edid);
struct drm_connector * connector;
struct edid * edid;
描述
将指定的模式添加到连接器的模式列表中。
返回添加的模式数量,如果找不到则返回0。
名称
drm_add_modes_noedid —为没有EDID的连接器添加模式
概要
int fsfuncdrm_add_modes_noedid ( connector,
hdisplay,
vdisplay);
struct drm_connector * connector;
int hdisplay;
int vdisplay;
名称
drm_hdmi_avi_infoframe_from_display_mode —用来自DRM显示模式的数据填充HDMI AVI信息帧
概要
int fsfuncdrm_hdmi_avi_infoframe_from_display_mode ( frame,
mode);
struct hdmi_avi_infoframe * frame;
const struct drm_display_mode * mode;
实用程序功能可帮助管理矩形区域以进行裁剪,缩放等计算。
名称
drm_rect_adjust_size —调整矩形的大小
概要
void fsfuncdrm_rect_adjust_size ( r,
dw,
dh);
struct drm_rect * r;
int dw;
int dh;
名称
drm_rect_translate —平移矩形
概要
void fsfuncdrm_rect_translate ( r,
dx,
dy);
struct drm_rect * r;
int dx;
int dy;
名称
drm_rect_downscale —缩小矩形
概要
void fsfuncdrm_rect_downscale ( r,
horz,
vert);
struct drm_rect * r;
int horz;
int vert;
名称
drm_rect_equals —确定两个矩形是否相等
概要
bool fsfuncdrm_rect_equals ( r1,
r2);
const struct drm_rect * r1;
const struct drm_rect * r2;
名称
drm_rect_intersect —与两个矩形相交
概要
bool fsfuncdrm_rect_intersect ( r1,
r2);
struct drm_rect * r1;
const struct drm_rect * r2;
名称
drm_rect_clip_scaled —执行缩放的剪辑操作
概要
bool fsfuncdrm_rect_clip_scaled ( src,
dst,
clip,
hscale,
vscale);
struct drm_rect * src;
struct drm_rect * dst;
const struct drm_rect * clip;
int hscale;
int vscale;
名称
drm_rect_calc_hscale —计算水平缩放因子
概要
int fsfuncdrm_rect_calc_hscale ( src,
dst,
min_hscale,
max_hscale);
const struct drm_rect * src;
const struct drm_rect * dst;
int min_hscale;
int max_hscale;
名称
drm_rect_calc_vscale —计算垂直比例因子
概要
int fsfuncdrm_rect_calc_vscale ( src,
dst,
min_vscale,
max_vscale);
const struct drm_rect * src;
const struct drm_rect * dst;
int min_vscale;
int max_vscale;
名称
drm_rect_calc_hscale_relaxed —计算水平缩放因子
概要
int fsfuncdrm_rect_calc_hscale_relaxed ( src,
dst,
min_hscale,
max_hscale);
struct drm_rect * src;
struct drm_rect * dst;
int min_hscale;
int max_hscale;
名称
drm_rect_calc_vscale_relaxed —计算垂直比例因子
概要
int fsfuncdrm_rect_calc_vscale_relaxed ( src,
dst,
min_vscale,
max_vscale);
struct drm_rect * src;
struct drm_rect * dst;
int min_vscale;
int max_vscale;
在flip / vblank之后,可以使工作排队以从工作队列上下文中运行。通常,这可以用于将帧缓冲区,鼠标bo等的取消引用推迟到vblank之后。这些API都是安全的(无锁),最多可同时有一个生产者和一个消费者。通过将排队的工作提交到单个工作队列来确保单用户方面。
名称
struct drm_flip_work —翻转工作队列
概要
struct drm_flip_work {
const char * name;
atomic_t pending;
atomic_t count;
drm_flip_func_t func;
struct work_struct worker;
};
名称
drm_flip_work_queue —工作队列
概要
void fsfuncdrm_flip_work_queue ( work,
val);
struct drm_flip_work * work;
void * val;
名称
drm_flip_work_commit —提交排队的工作
概要
void fsfuncdrm_flip_work_commit ( work,
wq);
struct drm_flip_work * work;
struct workqueue_struct * wq;
vma-manager负责将依赖于驱动程序的任意内存区域映射到线性用户地址空间。它为调用方提供偏移量,然后可以在drm设备的address_space上使用它。注意不要重叠区域,适当调整它们的大小,并且不要因不一致的伪造vm_pgoff字段而混淆mm-core。驱动程序不应将其用于VMEM中的对象放置。该管理器仅应用于管理到线性用户空间VM的映射。
我们使用drm_mm作为后端来管理对象分配。但是它针对分配/释放调用(而非查找)进行了高度优化。因此,我们使用rb-tree[红黑树]来加速偏移量查找。
您不得在单个address_space上使用多个偏移量管理器。否则,mm-core将无法拆除内存映射,因为VM将不再是线性的。在这种情况下,请使用VM_NONLINEAR并实现您自己的偏移量管理器。
此偏移量管理器适用于基于页面的地址。也就是说,每个参数和返回码(除外drm_vma_node_offset_addr
)都以页数而不是字节数给出。这意味着对象大小和偏移量必须始终与页面对齐(通常)。如果要获取给定偏移量的有效的基于字节的用户空间地址,请参见drm_vma_node_offset_addr
。
除了偏移量管理,vma偏移量管理器还处理访问管理。对于每个允许访问给定节点的开放文件上下文,必须调用drm_vma_node_allow
。否则,mmap
对该节点偏移的开放文件的调用将失败并返回-EACCES。要再次撤消访问权限,请使用drm_vma_node_revoke
。但是,如果需要,调用者负责销毁已经存在的映射。
名称
drm_vma_offset_manager_init —初始化新的偏移量管理器
概要
void fsfuncdrm_vma_offset_manager_init ( mgr,
page_offset,
size);
struct drm_vma_offset_manager * mgr;
unsigned long page_offset;
unsigned long size;
名称
drm_vma_offset_manager_destroy —销毁偏移量管理器
概要
void fsfuncdrm_vma_offset_manager_destroy ( mgr);
struct drm_vma_offset_manager * mgr;
名称
drm_vma_offset_lookup —在偏移空间中查找节点
概要
struct drm_vma_offset_node * fsfuncdrm_vma_offset_lookup ( mgr,
start,
pages);
struct drm_vma_offset_manager * mgr;
unsigned long start;
unsigned long pages;
名称
drm_vma_offset_lookup_locked —在偏移空间中查找节点
概要
struct drm_vma_offset_node * fsfuncdrm_vma_offset_lookup_locked ( mgr,
start,
pages);
struct drm_vma_offset_manager * mgr;
unsigned long start;
unsigned long pages;
名称
drm_vma_offset_add —将偏移量节点添加到管理器
概要
int fsfuncdrm_vma_offset_add ( mgr,
node,
pages);
struct drm_vma_offset_manager * mgr;
struct drm_vma_offset_node * node;
unsigned long pages;
名称
drm_vma_offset_remove —从管理器中删除偏移节点
概要
void fsfuncdrm_vma_offset_remove ( mgr,
node);
struct drm_vma_offset_manager * mgr;
struct drm_vma_offset_node * node;
名称
drm_vma_node_allow-将打开文件添加到允许的用户列表中
概要
int fsfuncdrm_vma_node_allow ( node,
filp);
struct drm_vma_offset_node * node;
struct file * filp;
名称
drm_vma_node_revoke —从允许的用户列表中删除打开文件
概要
void fsfuncdrm_vma_node_revoke ( node,
filp);
struct drm_vma_offset_node * node;
struct file * filp;
名称
drm_vma_node_is_allowed —检查是否授予打开文件访问权限
概要
bool fsfuncdrm_vma_node_is_allowed ( node,
filp);
struct drm_vma_offset_node * node;
struct file * filp;
名称
drm_vma_offset_exact_lookup —按确切地址查找节点
概要
struct drm_vma_offset_node * fsfuncdrm_vma_offset_exact_lookup ( mgr,
start,
pages);
struct drm_vma_offset_manager * mgr;
unsigned long start;
unsigned long pages;
名称
drm_vma_offset_lock_lookup-锁定查找以供扩展私人使用
概要
void fsfuncdrm_vma_offset_lock_lookup ( mgr);
struct drm_vma_offset_manager * mgr;
名称
drm_vma_offset_unlock_lookup —解锁查找以供扩展私人使用
概要
void fsfuncdrm_vma_offset_unlock_lookup ( mgr);
struct drm_vma_offset_manager * mgr;
名称
drm_vma_node_reset —初始化或重置节点对象
概要
void fsfuncdrm_vma_node_reset ( node);
struct drm_vma_offset_node * node;
名称
drm_vma_node_start —返回基于页面的寻址的起始地址
概要
unsigned long fsfuncdrm_vma_node_start ( node);
struct drm_vma_offset_node * node;
名称
drm_vma_node_size —返回大小(基于页面)
概要
unsigned long fsfuncdrm_vma_node_size ( node);
struct drm_vma_offset_node * node;
名称
drm_vma_node_has_offset —检查是否将节点添加到偏移管理器
概要
bool fsfuncdrm_vma_node_has_offset ( node);
struct drm_vma_offset_node * node;
名称
drm_vma_node_offset_addr —返回用户空间mmap的已清理偏移量
概要
__u64 fsfuncdrm_vma_node_offset_addr ( node);
struct drm_vma_offset_node * node;
驱动程序可能需要向应用程序公开除前几节所述之外的其他参数。KMS支持将属性附加到CRTC,连接器和平面,并提供用户空间API来列出,获取和设置属性值。
属性由唯一定义属性用途的名称标识,并存储关联的值。对于除blob属性以外的所有属性类型,该值为64位无符号整数。
KMS区分属性和属性实例。驱动程序首先创建属性,然后创建这些属性的各个实例并将其与对象关联。一个属性可以被实例化多次并与不同的对象关联。值存储在属性实例中,所有其他属性信息存储在属性中,并在属性的所有实例之间共享。
每个属性的创建类型都会影响KMS核心处理该属性的方式。支持的属性类型是:
DRM_MODE_PROP_RANGE
范围属性报告其最小和最大允许值。KMS核心将验证应用程序设置的值是否在该范围内。
DRM_MODE_PROP_ENUM
枚举属性采用从0到该属性定义的枚举值的数量的数字值减去1,并将自由格式的字符串名称与每个值相关联。应用程序可以检索已定义的值-名称对的列表,并使用数值来获取和设置属性实例值。
DRM_MODE_PROP_BITMASK
位掩码属性是枚举属性,它另外将所有枚举值限制在0..63范围内。位掩码属性实例值组合了该属性定义的一个或多个枚举位。
DRM_MODE_PROP_BLOB
Blob属性存储二进制Blob,没有任何格式限制。二进制Blob被创建为KMS独立对象,并且Blob属性实例值存储其关联的Blob对象的ID。
Blob属性仅用于连接器EDID属性,不能由驱动程序创建。
要创建属性驱动程序,请根据属性类型调用以下函数之一。所有属性创建函数均采用属性标志和名称以及特定于类型的参数。
-
struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, const char *name, uint64_t min, uint64_t max);
用给定的最小值和最大值创建一个range属性。
-
struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, int num_values);
创建一个枚举的属性。该
props
参数指向num_values
值-名称对的数组。 -
struct drm_property *drm_property_create_bitmask(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, int num_values);
创建一个位掩码属性。该
props
参数指向num_values
值-名称对的数组。
可以另外将属性创建为不可变的,在这种情况下,它们对于应用程序将是只读的,但可以由驱动程序进行修改。要创建不可变属性,驱动程序必须在属性创建时设置DRM_MODE_PROP_IMMUTABLE标志。
如果在属性创建时没有值名称对数组可用于枚举或范围属性,驱动程序可以使用该drm_property_create
函数创建属性,并通过调用该drm_property_add_enum
函数手动添加枚举值名称对 。必须注意通过flags
参数正确指定属性类型。
创建属性后,驱动程序可以通过调用drm_object_attach_property
将属性实例附加到CRTC,连接器和平面对象 。该函数获取指向目标对象的指针,指向先前创建的属性的指针和初始实例值。
垂直消隐在图形渲染中起主要作用。为了实现无撕裂显示,用户必须将页面翻转和/或渲染与垂直消隐同步。DRM API提供了ioctl来执行与垂直消隐同步的页面翻转,并等待垂直消隐。
DRM内核处理大多数垂直消隐管理逻辑,其中包括滤除虚假中断,保留无竞争的消隐计数器,应对计数器环绕和复位以及保持使用计数。它依靠驱动程序生成垂直消隐中断,并可选地提供硬件垂直消隐计数器。驱动程序必须执行以下操作。
-
int (*enable_vblank) (struct drm_device *dev, int crtc); void (*disable_vblank) (struct drm_device *dev, int crtc);
启用或禁用给定CRTC的垂直消隐中断。
-
u32 (*get_vblank_counter) (struct drm_device *dev, int crtc);
检索给定CRTC的垂直消隐计数器的值。如果硬件保持垂直消隐计数器,则应返回其值。否则,驱动程序可以使用
drm_vblank_count
助手功能来处理此操作。
驱动程序必须在load
操作中通过drm_vblank_init
调用来初始化垂直消隐处理核心 。该函数会将struct drm_device vblank_disable_allowed
字段设置 为0。这将使垂直消隐中断永久启用,直到第一个模式设置操作(其中vblank_disable_allowed
设置为1)为止。其背后的原因尚不清楚。调用drm_vblank_init后,
驱动程序可以将字段设置为1,以便从头开始动态管理垂直消隐中断。
垂直消隐中断可以由DRM内核或驱动程序本身启用(例如,处理页面翻转操作)。DRM内核维护垂直消隐使用计数,以确保在用户仍需要中断时不禁用这些中断。要增加使用次数,驱动请调用drm_vblank_get
。返回时,保证将使能垂直消隐中断。
要减少使用计数驱动程序,请调用 drm_vblank_put
。仅当使用计数降至零时,DRM内核才会通过调度计时器在延迟后禁用垂直消隐中断。可通过vblankoffdelay模块参数或drm_vblank_offdelay
全局变量访问该延迟,并以毫秒为单位表示。其默认值为5000毫秒。
当发生垂直消隐中断时,驱动程序只需要调用该 drm_handle_vblank
函数即可解决该中断。
必须通过drm_vblank_cleanup
在驱动程序 unload
操作处理程序中调用来释放 由drm_vblank_init
分配的资源。
int (*firstopen) (struct drm_device *);
void (*lastclose) (struct drm_device *);
int (*open) (struct drm_device *, struct drm_file *);
void (*preclose) (struct drm_device *, struct drm_file *);
void (*postclose) (struct drm_device *, struct drm_file *);
firstopen
仅当应用程序打开没有其他打开的文件句柄的设备时,DRM内核才会为旧版UMS(用户模式设置)驱动程序调用 该方法。UMS驱动程序可以实现它以获取设备资源。KMS驱动程序不能使用该方法,而必须在该load
方法中获取资源。
同样lastclose
,对于UMS和KMS驱动程序,当最后一个在设备上打开了打开文件句柄的应用程序关闭时,将调用该方法。此外,在模块卸载时或对于可热插拔的设备,在拔出设备时也调用该方法。在firstopen
和 lastclose
这样的调用是不平衡的。
每次由应用程序打开设备时都会调用open
方法。驱动程序可以使用此方法分配每个文件的私有数据,并将其存储在struct drm_file driver_priv
字段中。请注意,该open
方法是在firstopen
之前调用的。
关闭操作分为preclose
和 postclose
方法。preclose
方法中的,驱动程序必须停止并清除该所有per-file操作。例如,必须取消待处理的垂直消隐和页面翻转事件。从preclose
方法返回后,不允许对文件句柄进行按文件的操作。
最后,该postclose
方法被称为关闭操作的最后一步,如果设备不存在其他打开文件句柄,则在调用lastclose
方法之前 调用。在open
方法中分配了每个per-file私有数据的驱动程序应在此处释放它。
该lastclose
方法应将CRTC和平面属性恢复为默认值,以便设备的后续打开不会继承先前用户的状态。它还可以用于执行延迟的电源开关状态更改,例如,与vga-switcheroo基础架构结合使用。除此之外,KMS驱动程序不应进行任何进一步的清理。只有旧版UMS驱动程序可能需要清理设备状态,以便vga控制台或独立的fbdev驱动程序可以接管。
const struct file_operations *fops
驱动程序必须定义构成DRM用户空间API入口点的文件操作结构,即使其中大多数操作是在DRM核心中实现的也是如此。的open
, release
和ioctl
操作由处理
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = drm_compat_ioctl,
#endif
实现需要32/64位兼容性支持的私有ioctl的驱动程序必须提供自己的 compat_ioctl
处理程序,该处理器处理私有ioctl并调用drm_compat_ioctl
核心ioctl。
在read
与poll
操作读取DRM事件和轮询他们提供支持。它们由
.poll = drm_poll,
.read = drm_read,
.llseek = no_llseek
内存映射的实现方式取决于驱动程序如何管理内存。将使用Pre-GEM驱动程序drm_mmap
,而将使用支持GEM的驱动程序drm_gem_mmap
。请参阅 “图形执行管理器(GEM)”部分。
.mmap = drm_gem_mmap,
DRM API不支持其他文件操作。
struct drm_ioctl_desc *ioctls;
int num_ioctls
特定于驱动程序的ioctl数字从DRM_COMMAND_BASE开始。ioctl描述符表由距基准值的ioctl编号索引。驱动程序可以使用DRM_IOCTL_DEF_DRV()宏来初始化表条目。
DRM_IOCTL_DEF_DRV(ioctl, func, flags)
ioctl
是ioctl名称。驱动程序必须将DRM _ ## ioctl和DRM_IOCTL _ ## ioctl宏定义为分别偏离DRM_COMMAND_BASE和ioctl编号的ioctl编号。第一个宏是设备专用的,而第二个宏必须在公共头文件中公开给用户空间。
func
是指向与drm_ioctl_t类型兼容的ioctl处理函数的指针。
typedef int drm_ioctl_t(struct drm_device *dev, void *data,
struct drm_file *file_priv);
flags
是以下值的位掩码组合。它限制了如何调用ioctl。
-
DRM_AUTH-仅允许通过身份验证的呼叫者
-
DRM_MASTER-ioctl只能在主文件句柄上调用
-
DRM_ROOT_ONLY-仅允许具有SYSADMIN功能的呼叫者
-
DRM_CONTROL_ALLOW-ioctl只能在控制设备上调用
-
DRM_UNLOCKED-将在不锁定DRM全局互斥锁的情况下调用ioctl处理程序
DRM核心提供了一些挂起/恢复代码,但是想要完全挂起/恢复支持的驱动程序应提供save()和restore()函数。这些在挂起,休眠或恢复时间被调用,并且应在挂起或休眠状态之间执行设备所需的任何状态保存或还原。
int (*suspend) (struct drm_device *, pm_message_t state);
int (*resume) (struct drm_device *);
这些是传统的挂起和恢复方法。新的驱动程序应该使用自己的总线类型提供的电源管理接口(通常是通过结构的device_driver dev_pm_ops),并设置这些方法为NULL。
DRM核心将几个接口导出到应用程序,这些接口通常旨在通过相应的libdrm包装函数使用。另外,驱动程序通过ioctl和sysfs文件导出设备专用的接口,以供用户空间驱动程序和支持设备的应用程序使用。
外部接口包括:内存映射,上下文管理,DMA操作,AGP管理,vblank控制,fence管理,内存管理和输出管理。
在这里介绍通用的ioctl和sysfs布局。我们只需要高级信息,因为手册页应涵盖其余内容。
DRM内核提供了多个字符设备供用户空间使用。根据打开哪个设备,用户空间可以执行一组不同的操作(主要是ioctl)。主节点始终被创建并称为<term> card <num> </ term>。此外,还将创建一个称为<term> controlD <num> </ term>的当前未使用的控制节点。主节点提供所有旧操作,并且历史上是用户空间使用的唯一接口。通过KMS,引入了控制节点。但是,尚未编写计划的KMS控制界面,因此该控制节点至今仍未使用。
随着屏幕外渲染器和GPGPU应用程序使用的增加,客户端不再需要运行合成器或图形服务器来使用GPU。但是DRM API要求非特权客户端在访问GPU之前必须先向DRM-Master进行身份验证。为了避免此步骤并在不进行身份验证的情况下授予客户端GPU访问权限,引入了渲染节点。渲染节点仅服务于渲染客户端,也就是说,无法在渲染节点上发布模式设置或特权ioctl。仅允许使用非全局渲染命令。如果驱动程序支持渲染节点,则它必须通过<term> DRIVER_RENDER </ term>进行播发 DRM驱动程序功能。如果不支持,则必须将主节点与旧版drmAuth身份验证过程一起用于渲染客户端。
如果驱动程序宣告支持渲染节点,则DRM核心将创建一个名为<term> renderD <num> </ term>的单独渲染节点。每个设备将有一个渲染节点。除了与PRIME相关的ioctl之外,此节点上均不允许其他ioctl。特别是明确禁止<term> GEM_OPEN </ term>。渲染节点旨在避免缓冲区泄漏,如果客户端猜测旧界面上的flink名称或mmap偏移量,则会发生缓冲区泄漏。除此基本界面外,驱动程序必须将其依赖于驱动程序的仅渲染ioctl标记为 <term> DRM_RENDER_ALLOW </ term>因此渲染客户端可以使用它们。驱动程序作者必须小心,不要在渲染节点上允许任何特权的ioctl。
使用渲染节点,用户空间现在可以通过基本文件系统访问模式来控制对渲染节点的访问。不再需要用于在特权的主/旧版节点上对客户端进行身份验证的图形服务器。相反,客户端可以打开渲染节点并立即被授予GPU访问权限。客户端(或服务器)之间的通信是通过PRIME完成的。不支持从渲染节点到旧版节点的FLINK。新客户端不得使用不安全的FLINK接口。
除了删除所有模式集/全局图标外,渲染节点还删除DRM-Master概念。没有理由将渲染客户端与DRM-Master关联,因为它们独立于任何图形服务器。此外,无论如何,它们必须在没有任何运行主机的情况下工作。如果驱动程序支持渲染节点,则它们必须能够在没有主对象的情况下运行。另一方面,如果驱动程序要求客户端之间的共享状态对用户空间可见并且可以在打开文件边界之外访问,则它们不支持渲染节点。