天天看点

Linux DRM Developer's GuideLinux DRM开发人员指南

Linux DRM开发人员指南

http://landley.net/kdocs/htmldocs/drm.html

Jesse Barnes

Initial version Intel Corporation

<[email protected]>

Laurent Pinchart

Driver internals Ideas on board SPRL

<[email protected]>

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

第1章简介

Linux DRM层包含旨在满足复杂图形设备需求的代码,通常包含非常适合3D图形加速的可编程管线[pipeline]。内核中的图形驱动程序可以利用DRM功能来简化诸如内存管理,中断处理和DMA之类的任务,并为应用程序提供统一的接口。

版本说明:本指南涵盖了DRM树中的功能,包括TTM内存管理器,输出配置和模式设置以及新的vblank内部,以及当前内核中的所有常规功能。

[在此处插入典型DRM堆栈的图]

第2章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()方法将被调用来设置请求版本。

名称,说明和日期

char *name;
char *desc;
char *date;
           

驱动程序名称在初始化时被打印到内核日志中,用于IRQ注册,并通过DRM_IOCTL_VERSION传递给用户空间。

驱动程序描述是一个纯信息性的字符串,它通过DRM_IOCTL_VERSION ioctl传递给用户空间,否则不会被内核使用。

驱动程序日期,格式为YYYYMMDD,用于标识对驱动程序的最新修改日期。但是,由于大多数驱动程序无法更新它,因此它的值几乎没有用。DRM内核在初始化时将其打印到内核日志,并通过DRM_IOCTL_VERSION ioctl将其传递到用户空间。

驱动加载

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相关的性能信息,以供性能监视工具和应用程序使用。

IRQ注册

DRM核心试图通过提供

drm_irq_install

和 

drm_irq_uninstall

功能来促进IRQ处理程序的注册和注销。这些功能每个设备仅支持单个中断,使用多个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。

手动IRQ注册

需要多个中断处理程序的驱动程序不能使用托管的IRQ注册功能。在这种情况下,必须手动注册和注销IRQ(通常使用

request_irq

 和

free_irq

函数,或者它们的devm_ *等效项)。

手动注册IRQ时,驱动程序不得设置DRIVER_HAVE_IRQ驱动程序功能标志,并且不得提供 

irq_handler

驱动程序操作。他们必须在注册IRQ 时将drm_device 

irq_enabled

字段设置为1,并在取消注册IRQ之后将其清除为0。

内存管理器初始化

每个DRM驱动程序都需要一个内存管理器,必须在加载时对其进行初始化。DRM当前包含两个内存管理器,即转换表管理器(TTM)和图形执行管理器(GEM)。本文档仅描述GEM内存管理器的用法。有关详细信息,请参见 “内存管理”部分。

杂项设备配置

配置期间PCI设备可能需要执行的另一项任务是映射视频BIOS。在许多设备上,VBIOS描述设备配置,LCD面板时钟(timing)(如果有),并包含指示设备状态的标志。可以使用pci_map_rom()调用完成BIOS映射,pci_map_rom()是一个便捷函数,负责映射实际的ROM,无论该ROM已被隐藏到内存中(通常位于地址0xc0000),还是存在于ROM BAR中的PCI设备上。请注意,在映射ROM并提取了所有必要的信息之后,应取消映射。在许多设备上,ROM地址解码器与其他BAR共享,因此,将其保留为映射状态可能会导致不良行为,如挂起或内存损坏。

内存管理

现代Linux系统需要大量的图形内存来存储帧缓冲区,纹理,顶点和其他与图形相关的数据。考虑到许多数据的动态特性,因此有效地管理图形内存对于图形堆栈至关重要,并且在DRM基础架构中发挥着核心作用。

DRM核心包括两个内存管理器,即转换表映射(TTM)和图形执行管理器(GEM)。TTM是第一个开发的DRM内存管理器,并试图成为一种适用于所有人的解决方案。它提供了一个单一的用户空间API,可以满足所有硬件的需求,同时支持统一内存体系结构(UMA)设备和具有专用视频RAM的设备(即大多数离散视频卡)。这导致了一个庞大,复杂的代码片段,结果证明这些代码难以用于驱动程序开发。

GEM最初是由英特尔赞助的项目,以应对TTM的复杂性。它的设计理念是完全不同的:GEM没有为每个与图形内存相关的问题提供解决方案,而是确定了驱动程序之间的通用代码,并创建了一个共享它的支持库。与TTM相比,GEM的初始化和执行要求更简单,但没有视频RAM管理功能,因此仅限于UMA设备。

转换表管理器(TTM)

TTM设计背景和信息属于此处。

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)

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初始化

使用GEM的驱动程序必须在struct drm_driver 

driver_features

字段中设置DRIVER_GEM位 。然后,DRM内核将在调用

load

操作之前自动初始化GEM内核 。在后台,这将创建DRM内存管理器对象,该对象提供用于对象分配的地址空间池。

在KMS配置中,如果硬件需要,驱动程序需要在核心GEM初始化之后分配和初始化命令环缓冲区。UMA设备通常具有所谓的“被盗”存储区,该存储区为初始帧缓冲区和设备所需的大而连续的存储区提供了空间。该空间通常不由GEM管理,必须单独初始化为它自己的DRM MM对象。

GEM对象创建

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对象均由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对象命名

用户空间和内核之间的通信使用本地句柄,全局名称或更近期使用文件描述符来引用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驱动程序,这两个操作是必需的。

DRM PRIME辅助功能参考

驱动程序可以使用辅助功能更简单的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对象映射

由于映射操作相当繁重,因此与通过将缓冲区映射到用户空间相比,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文件操作处理程序。

Dumb GEM对象

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

函数来处理此操作。

锁定

除了一些具有自己的锁定的查找结构(隐藏在接口功能后面)之外,大多数模式集状态还受

dev-<mode_config.lock

互斥锁和per-crtc锁的保护,以允许光标更新,页面翻转和类似操作与后台任务(例如输出)同时发生检测。跨域的操作(如完整模式集)始终会抓住所有锁。驱动程序需要通过额外的锁定来保护crtcs之间共享的资源。如果modset功能碰到crtc状态(例如,用于负载检测(仅抓取

mode_config.lock

 允许实时crtc上的并发屏幕更新),则他们还必须小心始终抓住相关的crtc锁。

KMS初始化和清理

KMS设备被抽象并公开为一组,平面(panel),CRTC,编码器(encoder)和连接器(connector)。因此,在初始化模式设置之后,KMS驱动程序必须在加载时创建和初始化所有这些对象。

CRTC(结构drm_crtc)

CRTC是表示芯片一部分的抽象,其中包含指向扫描缓冲区的指针。因此,可用的CRTC数量决定了在任何给定时间可以激活多少个独立的扫描缓冲区。CRTC结构包含几个字段来支持此操作:指向某些视频内存的指针(抽象为帧缓冲区对象),显示模式,以及视频内存中的(x,y)偏移量以支持平移或配置,其中一个视频存储器跨越多个CRTC。

CRTC初始化

KMS设备必须创建并注册至少一个struct drm_crtc实例。该实例由驱动程序分配并归零(可能是较大结构的一部分),并通过指向CRTC函数的指针

drm_crtc_init

调用进行注册。

CRTC操作

设置配置

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初始化和清理”部分。

Plane(struct drm_plane)

平面表示可以在扫描过程中与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初始化和清理”部分。

编码器(struct drm_encoder)

编码器从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。

编码器操作

  • void (*destroy)(struct drm_encoder *encoder);
               
    在不再需要时调用销毁编码器。请参阅 “ KMS初始化和清理”部分。
  • void (*set_property)(struct drm_plane *plane,
                         struct drm_property *property, uint64_t value);
               
    将给定平面属性的值设置为 

    value

    。有关属性的更多信息,请参见“ KMS属性”一节。

连接器(struct drm_connector)

连接器是设备上像素数据的最终目的地,通常直接连接到外部显示设备,例如显示器或笔记本电脑面板。一个连接器一次只能连接到一个编码器。连接器也是保留有关显示器信息的附加结构,因此它包含显示数据,EDID数据,DPMS和连接状态, 以及有关显示器支持的模式的信息的字段。

连接器初始化

最后,KMS驱动程序必须创建,初始化,注册并附加至少一个struct drm_connector实例。该实例将与其他KMS对象一起创建,并通过设置以下字段进行初始化。

interlace_allowed

连接器是否可以处理隔行模式。

doublescan_allowed

连接器是否可以处理双重扫描。

display_info

当检测到显示时,显示信息由EDID信息填充。对于非热插拔显示器(例如嵌入式系统中的平板),驱动程序应初始化 

display_info.

width_mm

 和 

display_info.

height_mm

 ,带有显示器实际尺寸的字段。

polled

连接器轮询模式,组合以下属性

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

。该功能将延迟的工作进行排序,以检查所有连接器的状态,但不会进行定期轮询。

连接器操作

注意

除非另有说明,否则所有操作都是强制性的。

DPMS

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注册新连接器,使其属性对应用程序可用。

KMS API函数

名称

drm_modeset_lock_all —获取所有模式集锁

概要

void fsfuncdrm_modeset_lock_all (	dev);	 
struct drm_device * dev;
           

参数

dev

 DRM设备

描述

此函数具有所有模式集锁定,适用于尚未实现更细粒度的方案的情况。

名称

drm_modeset_unlock_all —删除所有模式集锁

概要

void fsfuncdrm_modeset_unlock_all (	dev);	 
struct drm_device * dev;
           

参数

dev

 设备

名称

drm_warn_on_modeset_not_all_locked —检查是否已锁定所有模式设置锁

概要

void fsfuncdrm_warn_on_modeset_not_all_locked (	dev);	 
struct drm_device * dev;
           

参数

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;
           

参数

dev

 DRM设备

id

 模式对象的ID

type

 模式对象的类型

描述

请注意,framebuffer不能用这个函数查找——因为它们是引用计数的,所以需要特殊处理。

名称

drm_framebuffer_init —初始化帧缓冲区

概要

int fsfuncdrm_framebuffer_init (	dev,	 
 	fb,	 
 	funcs);	 
struct drm_device * dev;
struct drm_framebuffer * fb;
const struct drm_framebuffer_funcs * funcs;
 
           

参数

dev

 DRM设备

fb

 要初始化的帧缓冲区

funcs

 ...具有这些功能

描述

为帧缓冲区的父模式对象分配一个ID,设置其模式函数和设备文件,并将其添加到主fd列表中。

重要

此功能将发布fb,并使其可供其他用户并发访问。这意味着此时必须完全设置fb -由于所有fb属性在其生命周期内都是不变的,因此无需进一步锁定,而仅需要正确的引用计数即可。

返回

成功为零,失败为错误代码。

名称

drm_framebuffer_lookup —查找drm帧缓冲区并获取引用

概要

struct drm_framebuffer * fsfuncdrm_framebuffer_lookup (	dev,	 
 	id);	 
struct drm_device * dev;
uint32_t id;
           

参数

dev

 DRM设备

id 

fb对象的ID

描述

如果成功,这将获取对帧缓冲区的附加引用-调用者需要确保最终再次取消对返回的帧缓冲区的引用。

名称

drm_framebuffer_unreference —取消引用帧缓冲区

概要

void fsfuncdrm_framebuffer_unreference (	fb);	 
struct drm_framebuffer * fb;
           

参数

fb

 帧缓冲区以取消引用

描述

此功能将fb的refcount递减,并在其降至零时释放它。

名称

drm_framebuffer_reference —增加fb引用

概要

void fsfuncdrm_framebuffer_reference (	fb);	 
struct drm_framebuffer * fb;
           

参数

fb

 帧缓冲区

名称

drm_framebuffer_unregister_private —查找IDR中注销私有fb

概要

void fsfuncdrm_framebuffer_unregister_private (	fb);	 
struct drm_framebuffer * fb;
           

参数

fb

 要取消注册的fb

描述

驱动程序需要在清理驱动程序私有的帧缓冲区时调用它,例如那些用于fbdev的帧缓冲区。请注意,调用者必须持有自己的引用,即对象不能通过这个调用被销毁(因为它会导致锁定反转)。

名称

drm_framebuffer_cleanup —删除一个帧缓冲对象

概要

void fsfuncdrm_framebuffer_cleanup (	fb);	 
struct drm_framebuffer * fb;
 
           

参数

fb

 要删除帧缓冲区

描述

清理对用户创建的帧缓冲区的引用。该功能旨在从驱动程序-> destroy回调中使用。

请注意,这个函数并没有将fb从活动usuage中移除——如果它仍然在任何地方使用,则会出现hilarity,因为用户空间可以在id上调用getfb并返回-EINVAL。显然,不关心驱动程序卸载时间。

同样,不会从查找idr中删除帧缓冲区-对于用户创建的帧缓冲区,这将在rmfb ioctl中发生。对于驱动程序专用对象(例如,对于fbdev),驱动程序需要显式调用drm_framebuffer_unregister_private。

名称

drm_framebuffer_remove —删除和取消引用帧缓冲区对象

概要

void fsfuncdrm_framebuffer_remove (fb);	 
struct drm_framebuffer * fb;
           

参数

fb

 删除帧缓冲区

描述

dev

的mode_config中扫描所有CRTC和平面。如果他们使用

fb

,将其删除,并将其设置为NULL。然后删除对传入的帧缓冲区的引用。可能会占用模式设置锁。

请注意,如果调用方持有对帧缓冲区的最后一个引用,则此函数可优化清除操作。在这种情况下,还可以确保不使用模式集锁定。

名称

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;
           

参数

dev

 DRM设备

crtc 

需要初始化的CRTC对象

funcs

 新CRTC的回调函数

描述

创建一个新对象作为驱动程序crtc对象的基础部分。

返回

成功为零,失败为错误代码。

名称

drm_crtc_cleanup —清理核心crtc用法

概要

void fsfuncdrm_crtc_cleanup (	crtc);	 
struct drm_crtc * crtc;
           

参数

crtc

 进行清理的CRTC

描述

此功能清除

crtc

并从DRM模式设置核心中将其删除。请注意,该函数“不会 ”释放crtc结构本身,这是调用者的责任。

名称

drm_mode_probed_add —将模式添加到连接器的探测模式列表

概要

void fsfuncdrm_mode_probed_add (	connector,	 
 	mode);	 
struct drm_connector * connector;
struct drm_display_mode * mode;
 
           

参数

connector

 连接器新模式

mode

 模式数据

描述

添加

mode

connector

的模式列表以供以后使用。

名称

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;
 
           

参数

dev

 DRM设备

connector

 初始化连接器

funcs

 此连接器的回调

connector_type

 用户可见的连接器类型

描述

初始化预分配的连接器。连接器应细分为驱动程序连接器对象的一部分。

返回

成功为零,失败为错误代码。

名称

drm_connector_cleanup —清理一个初始化的连接器

概要

void fsfuncdrm_connector_cleanup (	connector);	 
struct drm_connector * 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;
           

参数

dev

 DRM设备

plane

 平面对象初始化

possible_crtcs

 可能的CRTC的位掩码

funcs

 新plane的回调

formats

 支持的格式数组(

DRM_FORMAT_

*)

format_count

 格式中的元素数

priv

 plane是私人的(从用户空间隐藏)?

描述

初始化一个新对象,该对象创建为驱动程序平面对象的基础部分。

返回

成功为零,失败为错误代码。

名称

drm_plane_cleanup —清理核心平面的使用

概要 

void fsfuncdrm_plane_cleanup (	plane);	 
struct drm_plane * plane;
           

参数

plane

 准备清理的plane

描述

此功能清除

plane

并从DRM模式设置核心中将其删除。请注意,该函数*不会*释放平面结构本身,这是调用者的责任。

名称

drm_plane_force_disable —强制禁用plane

概要

void fsfuncdrm_plane_force_disable (	plane);	 
struct drm_plane * plane;
           

参数

plane

 准备禁用plane

描述

强制禁用plane。

在销毁平面的当前帧缓冲区以及恢复fbdev模式时使用。

名称

drm_mode_create —创建新的显示模式

概要

struct drm_display_mode * fsfuncdrm_mode_create (	dev);	 
struct drm_device * dev;
           

参数

dev

 DRM设备

描述

创建一个新的drm_display_mode,给它一个ID,然后返回它。

返回

成功时指向新模式的指针,错误时指向NULL。

名称

drm_mode_destroy —删除模式

概要

void fsfuncdrm_mode_destroy (	dev,	 
 	mode);	 
struct drm_device * dev;
struct drm_display_mode * mode;
           

参数

dev

 DRM设备

mode

 准备删除的模式

描述

释放

mode

的唯一标识符,然后释放它。

名称

drm_mode_create_dvi_i_properties —创建特定于DVI-I的连接器属性

概要

int fsfuncdrm_mode_create_dvi_i_properties (	dev);	 
struct drm_device * dev;
           

参数

dev

 DRM设备

描述

第一次建立DVI-I连接器时由驱动程序调用。

名称

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[];
           

参数

dev

 DRM设备

num_modes

 支持的不同TV格式(模式)的数量

modes[]

 包含每种格式名称的字符串的指针数组

描述

由驱动的TV初始化例程调用,此函数为给定设备创建TV特定的连接器属性。调用者负责分配格式名称列表,并将其传递给此例程。

名称

drm_mode_create_scaling_mode_property-创建缩放模式属性

概要

int fsfuncdrm_mode_create_scaling_mode_property (	dev);	 
struct drm_device * dev;
           

参数

dev

 DRM设备

描述

第一次需要时由驱动程序调用,必须将其连接到所需的连接器。

名称

drm_mode_create_dirty_info_property-创建脏属性

概要

int fsfuncdrm_mode_create_dirty_info_property (	dev);	 
struct drm_device * dev;
           

参数

dev

 DRM设备

描述

第一次需要时由驱动程序调用,必须将其连接到所需的连接器。

名称

drm_mode_set_config_internal —辅助调用-> set_config函数

概要

int fsfuncdrm_mode_set_config_internal (set);	 
struct drm_mode_set * set;
 
           

参数

set

 模式设置配置

描述

这是一个小助手包装内部调用到->set_config驱动程序接口。

名称

drm_format_num_planes —获取指定格式的平面数

概要

int fsfuncdrm_format_num_planes (format);	 
uint32_t format;
           

参数

format

像素格式(DRM_FORMAT_ *)

返回

指定的像素格式使用的平面数。

名称

drm_format_plane_cpp —确定每个像素的字节值

概要

int fsfuncdrm_format_plane_cpp (format,	 
 	plane);	 
uint32_t format;
int plane;
           

参数

format

 像素格式(DRM_FORMAT_ *)

plane

 平面索引

返回

指定平面的每个像素的字节值。

名称

drm_format_horz_chroma_subsampling —获取水平色度子采样因子

概要

int fsfuncdrm_format_horz_chroma_subsampling (format);	 
uint32_t format;
 
           

参数

format

 像素格式(DRM_FORMAT_ *)

返回

指定像素格式的水平色度子采样因子。

名称

drm_format_vert_chroma_subsampling —获取垂直色度子采样因子

概要

int fsfuncdrm_format_vert_chroma_subsampling (format);	 
uint32_t format;
           

参数

format

 像素格式(DRM_FORMAT_ *)

返回

指定像素格式的垂直色度子采样因子。

名称

drm_mode_config_init —初始化DRM mode_configuration结构

概要

void fsfuncdrm_mode_config_init (dev);	 
struct drm_device * dev;
           

参数

dev

 DRM设备

描述

初始化

dev

的mode_config结构,用于跟踪的图形配置

dev

由于这会初始化模式集锁定,因此无法进行锁定。因为这应该在单线程时初始化,所以没有问题,确保安全是驱动的问题。

名称

drm_mode_config_cleanup-释放DRM mode_config信息

概要

void fsfuncdrm_mode_config_cleanup (dev);	 
struct drm_device * dev;
           

参数

dev

 DRM设备

描述

释放与此DRM设备关联的所有连接器和CRTC,然后释放帧缓冲区和关联的缓冲区对象。

请注意,由于驱动/设备拆卸/应该/在单线程时操作,,因此不需要锁定。确保这项操作正确是驱动的工作。

FIXME

tongshi 清理所有悬空的用户缓冲区对象

模式设置助手功能

驱动程序提供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

    辅助函数是一个CRTC 

    set_config

    的实现。它首先尝试通过调用连接器

    best_encoder

    帮助程序,来找到每个连接器的最佳编码器。

    找到合适的编码器后,帮助函数将调用

    mode_fixup

    编码器和CRTC帮助操作来调整请求的模式;或者完全拒绝它,在这种情况下,错误将返回给应用程序。如果模式调整后的新配置与当前配置相同,则辅助功能将返回而不执行任何其他操作。

    如果调整后的模式与当前模式相同,但需要对帧缓冲区进行更改,则该 

    drm_crtc_helper_set_config

    函数将调用CRTC 

    mode_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

    为所探测列表中的每种模式,以检查该模式是否对连接器有效。

CRTC辅助操作

  • 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;
           

参数

dev

进行操作的drm设备

描述

某些用户空间假定第一个连接的连接器是主显示屏,应该在其中显示例如登录屏幕。对于笔记本电脑,这应该是主面板。使用此功能可将所有(eDP / LVDS)面板排序到连接器列表的最前面,而不必费力地尝试按正确的顺序对其进行初始化。

名称

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;
           

参数

connector

需要探测的连接器

maxX

模式的最大宽度

maxY

模式的最大高度

锁定

调用者必须持有模式配置锁。

基于

connector

实现的帮助程序回调,尝试检测所有有效模式。模式将首先被添加到连接器的probed_modes列表,然后剔除不符合的(基于有效性和

maxX

maxY

参数),其余投入正常模式列表。

打算用作->

probe

connector

 回调的通用实现,为用crtc辅助函数进行输出模式过滤和检测的驱动程序所使用 。

返回

connector

上找到的模式数量。

名称

drm_helper_encoder_in_use —检查是否正在使用给定的编码器

概要

bool fsfuncdrm_helper_encoder_in_use (	encoder);	 
struct drm_encoder * encoder;
 
           

参数

encoder

需要检测的编码器

锁定

调用者必须持有模式配置锁。

遍历

encoders

对应的

DRM设备的mode_config,看看它是否正在使用中。

返回

如果

encoder

属于mode_config,则为true,否则为false。

名称

drm_helper_crtc_in_use —检查给定的CRTC是否在mode_config中

概要

bool fsfuncdrm_helper_crtc_in_use (	crtc);	 
struct drm_crtc * crtc;
 
           

参数

crtc

需要检查的CRTC

锁定

调用者必须持有模式配置锁。

遍历

crtc对应的

DRM设备的mode_config,看看它是否正在使用中。

返回

如果

crtc

属于mode_config,则为true,否则为false。

名称

drm_helper_disable_unused_functions —禁用未使用的对象

概要

void fsfuncdrm_helper_disable_unused_functions (dev);	 
struct drm_device * dev;
           

参数

dev

对应的DRM

锁定

调用者必须持有模式配置锁。

如果连接器或CRTC不属于

dev的

mode_config的一部分,则可以通过调用其dpms函数将其关闭,以将其关闭。

名称

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;
           

参数

crtc

需要处理的CRTC

mode

需要使用的mode

x

进入surface的水平偏移

y

进入surface的垂直偏移

old_fb

旧的帧缓冲区,用于清理

锁定

调用者必须持有模式配置锁。

尝试

crtc

上设置

mode

crtc

在尝试设置模式之前,这是一个内部辅助功能,驱动程序可以用来更新属性,要求在新的配置中禁用和重新启用整个输出管道。例如,用于更改是否在hdmi链接上启用了音频,或用于更改面板fitter或抖动属性。

drm_crtc_helper_set_config

辅助函数也调用它 来驱动模式设置序列。

返回

如果成功设置了模式,则为true,否则为false。

名称

drm_crtc_helper_set_config-从用户空间设置新配置

概要

int fsfuncdrm_crtc_helper_set_config (	set);	 
struct drm_mode_set * set;
           

参数

set

模式设置配置

锁定

调用者必须持有模式配置锁。

set

中设置由上层提供的新配置(来自用户空间的ioctl调用或内部的fbdev支持代码,例如fbdev支持代码),并启用他。这是驱动程序的主要辅助函数,它使用crtc辅助函数实现内核模式设置,这些驱动程序使用crtc帮助程序功能以及各种-> 

prepare

,-> 

modeset

和-> 

commit

helper回调来实现内核模式设置。

返回

成功返回0,失败返回-ERRNO。

名称

drm_helper_connector_dpms —连接器dpms辅助实现

概要

void fsfuncdrm_helper_connector_dpms (	connector,	 
 	mode);	 
struct drm_connector * connector;
int mode;
 
           

参数

connector

受控制的连接器

mode

DPMS模式

描述

这是crtc辅助程序框架提供的用于实现DPMS连接器属性的主要帮助程序功能。它为输出网格中的所有编码器和crtcs计算新的所需DPMS状态,并适当地调用驱动程序提供的->

dpms

 回调。

fbdev辅助程序参考

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;
           

参数

fb_helper

 用drm_fb_helper_init初始化的fbdev

描述

此函数将添加所有可用的连接器,以与给定的fb_helper一起使用。这是一个单独的步骤,允许驱动程序自由地将连接器分配给fbdev,例如,如果某些连接器保留用于特殊目的或不足以用于fbcon。

由于这是发布fbdev之前的初始设置的一部分,因此不需要锁定。

名称

drm_fb_helper_debug_enter —-> fb_debug_enter的实现

概要

int fsfunc drm_fb_helper_debug_enter (info);	 
struct fb_info * info;
           

参数

info

fbdev由辅助函数注册

名称

drm_fb_helper_debug_leave —-> fb_debug_leave的实现

概要

int fsfunc drm_fb_helper_debug_leave (info);	 
struct fb_info * info;
           

参数

info

fbdev由辅助函数注册

名称

drm_fb_helper_restore_fbdev_mode —恢复fbdev配置

概要

bool fsfunc drm_fb_helper_restore_fbdev_mode (fb_helper);	 
struct drm_fb_helper * fb_helper;
           

参数

fb_helper

fbcon(fbconfig?)恢复

描述

使用此帮助程序在kms顶部实现fbcon时,应从驱动程序的drm-> lastclose回调中调用此函数。这样可以确保当X(x11,显示的上层server)死了时,用户不会看到黑屏。

名称

drm_fb_helper_blank —-> fb_blank的实现

概要

int fsfunc drm_fb_helper_blank (blank,	 
 	info);	 
int blank;
struct fb_info * info;
           

参数

blank

所需的消隐状态

info

fbdev由辅助函数注册

名称

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

最大连接器数

描述

这将使用给定的限制为fbdev帮助程序分配结构。请注意,这还不会(通过驱动程序接口)接触硬件,也不会注册fbdev。 这仅在

drm_fb_helper_initial_config完成,

是为了允许驱动程序对确切的初始化顺序进行更多控制。

驱动程序必须在调用

drm_fb_helper_initial_config

之前设置fb_helper-> funcs 。

返回

如果一切正常,则为零,否则为非零。

名称

drm_fb_helper_setcmap —-> fb_setcmap的实现

概要

int fsfuncdrm_fb_helper_setcmap (	cmap,	 
 	info);	 
struct fb_cmap * cmap;
struct fb_info * info;
           

参数

cmap

要设置的cmap

info

fbdev由辅助函数注册

名称

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;
           

参数

var

屏幕信息检查

info

fbdev由辅助函数注册

名称

drm_fb_helper_set_par —-> fb_set_par的实现

概要

int fsfuncdrm_fb_helper_set_par (	info);	 
struct fb_info * info;
           

参数

info

fbdev由辅助函数注册

描述

这将使fbcon进行模式初始化,并在初始化时由fbdev内核在注册驱动程序时进行调用,然后再通过hotplug回调进行调用。

名称

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;
           

参数

var

更新的屏幕信息

info

fbdev由助手注册

名称

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;
 
           

参数

info

fbdev由助手注册

pitch

所需pitch

depth

所需深度

描述

帮助程序可填充固定的fbdev信息,这些信息对于非加速的fbdev仿真非常有用。支持加有其他约束的加速方法的驱动程序需要设置自己的限制。

驱动程序应从其-> fb_probe回调中调用此代码(或等效的安装代码)。

名称

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;
           

参数

info

fbdev实例设置

fb_helper

fb帮助程序实例用作模板

fb_width

所需的fb宽度

fb_height

所需的fb高度

描述

根据给定的fb帮助程序实例和fb_helper-> fb中分配的drm帧缓冲区设置变量fbdev元信息。

在分配了fbdev后备存储帧缓冲区之后,驱动程序应从其-> fb_probe回调中调用此代码(或等效的安装代码)。

名称

drm_fb_helper_initial_config —设置合理的初始连接器配置

概要

bool fsfuncdrm_fb_helper_initial_config (	fb_helper,	 
 	bpp_sel);	 
struct drm_fb_helper * fb_helper;
int bpp_sel;
           

参数

fb_helper

fb_helper设备结构

bpp_sel

用于帧缓冲区配置的bpp值

描述

扫描CRTC和连接器,并尝试将初始设置放在一起。目前,这是所有源头的克隆配置,其中有一个新的framebuffer对象作为后备存储。

请注意,这还会注册fbdev,因此允许用户空间通过fbdev接口调用驱动程序。

该函数将调用-> fb_probe回调,以使驱动程序分配和初始化fbdev信息结构以及用于支持fbdev的drm帧缓冲区。

drm_fb_helper_fill_var

和 

drm_fb_helper_fill_fix

作为帮助程序来设置fbdev info结构的简单默认值。

返回

如果一切正常,则为零,否则为非零。

名称

drm_fb_helper_hotplug_event —通过探测附加到fb的所有输出来响应热插拔通知

概要

int fsfuncdrm_fb_helper_hotplug_event (	fb_helper);	 
struct drm_fb_helper * fb_helper;
           

争论

fb_helper

drm_fb_helper

描述

通知输出配置的更改后,扫描连接到fb_helper的连接器,并尝试将设置汇总在一起。

在运行时调用,使用模式配置锁可以检查/更改模式集配置。必须从进程上下文运行(这通常意味着输出轮询工作或从驱动程序的热插拔中断启动的工作项)。

注意,驱动程序必须确保这仅是fb已完全设置后的唯一调用,即在调用drm_fb_helper_initial_config之后。

返回

成功时返回0,否则返回非零错误代码。

名称

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);
}; 
           

成员

gamma_set

在给定的crtc上设置给定的gamma lut寄存器。

gamma_get

读取给定crtc上给gamma lut寄存器,该寄存器用于在强制恢复fbdev(例如kdbg)时保存当前lut。

fb_probe

驱动程序回调以分配和初始化fbdev信息结构。此外,它还需要分配用于支持fbdev的drm帧缓冲区。

initial_config

设置初始fbdev显示配置

描述

fbdev仿真帮助程序库使用的驱动程序回调。

(DP)显示端口帮助程序功能参考

这些功能包含各种抽象级别的一些通用逻辑和帮助程序,用于处理Display Port接收器设备和相关内容,例如DP辅助通道传输,在DP辅助通道上读取EDID,解码某些DPCD块,...

名称

struct i2c_algo_dp_aux_data —基于dp aux算法的i2c的驱动程序接口结构

概要

struct i2c_algo_dp_aux_data {
  bool running;
  u16 address;
  int (* aux_ch) (struct i2c_adapter *adapter,int mode, uint8_t write_byte,uint8_t *read_byte);
};  
           

成员

running

由算法设置的值,指示i2c是否正在进行或i2c总线是否处于静态

address

当前正在进行传输的i2c目标地址

aux_ch

驱动程序回调以传输i2c有效负载的单个字节

名称

i2c_dp_aux_add_bus —使用辅助通道帮助程序注册i2c适配器

概要

int fsfunci2c_dp_aux_add_bus (	adapter);	 
struct i2c_adapter * adapter;
           

参数

adapter

i2c适配器进行注册

描述

这将注册一个使用dp aux通道作为传输基础的i2c适配器。驱动程序需要填写i2c_algo_dp_aux_data结构并将其存储在algo_data成员的

adapter

参数中。i2c over dp aux算法将使用它来驱动硬件。

返回

成功时为0,失败时为-ERRNO。

EDID辅助功能参考

名称

drm_edid_is_valid —完整性检查EDID数据

概要

bool fsfuncdrm_edid_is_valid (	edid);	 
struct edid * edid;
           

参数

edid

EDID数据

描述

完整检查整个EDID记录(包括扩展名)

名称

drm_probe_ddc —

概要

bool fsfuncdrm_probe_ddc (	adapter);	 
struct i2c_adapter * adapter;
           

争论

adapter

-未描述-

描述

\ param适配器:i2c设备适配器\成功返回1

名称

drm_get_edid —获取EDID数据(如果有)

概要

struct edid * fsfuncdrm_get_edid (	connector,	 
 	adapter);	 
struct drm_connector * connector;
struct i2c_adapter * adapter;
           

参数

connector

我们正在探索的连接器

adapter

用于DDC的i2c适配器

描述

如有可能,拨入给定的i2c通道以获取EDID数据。如果找到,将其连接到连接器。

返回edid数据;如果找不到,则返回NULL。

名称

drm_match_cea_mode —查找与给定模式匹配的CEA模式

概要

u8 fsfuncdrm_match_cea_mode (	to_match);	 
const struct drm_display_mode * to_match;
           

参数

to_match

显示模式

描述

返回该模式的CEA视频ID(VIC);如果不是CEA-861模式,则返回0。

名称

drm_edid_to_eld —从EDID生成ELD

概要

void fsfuncdrm_edid_to_eld (	connector,	 
 	edid);	 
struct drm_connector * connector;
struct edid * edid;
 
           

参数

connector

对应于HDMI / DP接收器的接口

edid

需要解析的EDID

描述

填充ELD(类似于EDID的数据)缓冲区以传递给音频驱动程序。

一些ELD字段留给图形驱动程序调用方

-Conn_Type-HDCP-Port_ID

名称

drm_edid_to_sad —从EDID中提取SAD

概要

int fsfuncdrm_edid_to_sad (	edid,	 
 	sads);	 
struct edid * edid;
struct cea_sad ** sads;
 
           

争论

edid

需要解析的EDID

sads

将设置为提取到的SAD的指针

描述

查找CEA EDID块并从中提取SAD(短音频描述符)。

注意

返回的指针需要kfreed

返回找到的SAD数或错误时返回负数。

名称

drm_edid_to_speaker_allocation —从EDID中提取扬声器分配的数据块

概要

int fsfuncdrm_edid_to_speaker_allocation (	edid,	 
 	sadb);	 
struct edid * edid;
u8 ** sadb;
 
           

参数

edid

需要解析的EDID

sadb

指向扬声器模块的指针

描述

查找CEA EDID块,并从中提取扬声器分配数据块。

注意

返回的指针需要kfreed

返回找到的扬声器分配块数或错误时返回负数。

名称

drm_av_sync_delay — HDMI / DP接收器音频-视频同步延迟(以毫秒为单位)

概要

int fsfuncdrm_av_sync_delay (	connector,	 
 	mode);	 
struct drm_connector * connector;
struct drm_display_mode * mode;
           

参数

connector

与HDMI / DP接收器关联的连接器

mode

显示模式

名称

drm_select_eld —从多个HDMI / DP接收器中选择一个ELD

概要

struct drm_connector * fsfuncdrm_select_eld (	encoder,	 
 	mode);	 
struct drm_encoder * encoder;
struct drm_display_mode * mode;
           

参数

encoder

编码器刚刚更改了显示模式

mode

调整后的显示模式

描述

一个编码器可能与多个HDMI / DP接收器关联。现在,已对该策略进行了硬编码,以仅使用第一个HDMI / DP接收器的ELD。

名称

drm_detect_hdmi_monitor —检测显示器是否为hdmi。

概要

bool fsfuncdrm_detect_hdmi_monitor (	edid);	 
struct edid * edid;
           

参数

edid

显示器的EDID信息

描述

根据CEA-861-B解析CEA扩展名。如果HDMI,则返回true,否则返回false,否则返回未知。

名称

drm_detect_monitor_audio —检查监视器音频功能

概要

bool fsfuncdrm_detect_monitor_audio (	edid);	 
struct edid * edid;
 
           

参数

edid

显示器的EDID信息

描述

显示器应具有CEA扩展块。如果显示器具有“基本音频”,但没有CEA音频块,则仅是“基本音频”。如果有任何音频扩展块和支持的音频格式,则即使在EDID中未定义“基本音频”,也至少要假定“基本音频”支持。

名称

drm_rgb_quant_range_selectable — RGB量化范围可以选择吗?

概要

bool fsfuncdrm_rgb_quant_range_selectable (	edid);	 
struct edid * edid;
           

参数

edid

显示器的EDID信息

描述

检查显示器是否报告支持的RGB量化范围选择。然后可以使用AVI信息帧通知监视器使用了哪个量化范围(完整或有限)。

名称

drm_add_edid_modes —从EDID数据添加模式(如果有)

概要

int fsfuncdrm_add_edid_modes (	connector,	 
 	edid);	 
struct drm_connector * connector;
struct edid * edid;
           

参数

connector

我们正在探索的连接器

edid

显示器的EDID信息

描述

将指定的模式添加到连接器的模式列表中。

返回添加的模式数量,如果找不到则返回0。

名称

drm_add_modes_noedid —为没有EDID的连接器添加模式

概要

int fsfuncdrm_add_modes_noedid (	connector,	 
 	hdisplay,	 
 	vdisplay);	 
struct drm_connector * connector;
int hdisplay;
int vdisplay;
           

参数

connector

我们正在探索的连接器

hdisplay

水平显示极限

vdisplay

垂直显示限制

描述

将指定的模式添加到连接器的模式列表中。仅当hdisplay / vdisplay不超过给定限制时,才会添加它。

返回添加的模式数量,如果找不到则返回0。

名称

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;
           

参数

frame

HDMI AVI信息框

mode

DRM显示模式

描述

成功返回0,失败返回负错误代码。

名称

drm_hdmi_vendor_infoframe_from_display_mode —用来自DRM显示模式的数据填充HDMI信息帧

概要

int fsfuncdrm_hdmi_vendor_infoframe_from_display_mode (	frame,	 
 	mode);	 
struct hdmi_vendor_infoframe * frame;
const struct drm_display_mode * mode;
 
           

参数

frame

HDMI供应商信息框

mode

DRM显示模式

描述

请注意,仅在使用4k或立体3D模式时才需要发送HDMI供应商信息帧。因此,当提供任何其他模式作为输入时,此函数将返回-EINVAL,该错误可以安全地忽略。

成功返回0,失败返回负错误代码。

矩形实用程序参考

实用程序功能可帮助管理矩形区域以进行裁剪,缩放等计算。

名称

struct drm_rect —二维矩形

概要

struct drm_rect {
  int x1;
  int y1;
  int x2;
  int y2;
};  
           

成员

x1

水平起始坐标(含)

11

垂直起始坐标(含)

2倍

水平结束坐标(不包括)

22

垂直结束坐标(不包括)

名称

drm_rect_adjust_size —调整矩形的大小

概要

void fsfuncdrm_rect_adjust_size (	r,	 
 	dw,	 
 	dh);	 
struct drm_rect * r;
int dw;
int dh;
           

参数

r

要调整的矩形

dw

水平调整

dh

垂直调整

描述

改变矩形

r

的大小由

dw

在水平方向上,并且通过

dh

在垂直方向上,同时保持

r

的中心固定不动。

dw

dh

增加大小,负值减少它。

名称

drm_rect_translate —平移矩形

概要

void fsfuncdrm_rect_translate (	r,	 
 	dx,	 
 	dy);	 
struct drm_rect * r;
int dx;
int dy;
           

参数

r

要翻译的矩形

dx

水平翻译

dy

垂直翻译

描述

移动矩形

r,dx

在水平方向上 ,

dy

在垂直方向上移动矩形。

名称

drm_rect_downscale —缩小矩形

概要

void fsfuncdrm_rect_downscale (	r,	 
 	horz,	 
 	vert);	 
struct drm_rect * r;
int horz;
int vert;
           

参数

r

要缩小的矩形

horz

水平缩小系数

vert

垂直缩小系数

描述

矩形的坐标

r

除以

horz

vert

名称

drm_rect_width —确定矩形的宽度

概要

int fsfuncdrm_rect_width (	r);	 
const struct drm_rect * r;
           

参数

r

返回宽度的矩形

返回

矩形的宽度。

名称

drm_rect_height —确定矩形的高度

概要

int fsfuncdrm_rect_width (r);	 
const struct drm_rect * r;
           

参数

r

返回高度的矩形

返回

矩形的高度。

名称

drm_rect_visible —确定矩形是否可见

概要

int fsfuncdrm_rect_height (	r);	 
const struct drm_rect * r;
           

参数

r

返回可见性的矩形

返回

true

如果矩形可见,

false

否则。

名称

drm_rect_equals —确定两个矩形是否相等

概要

bool fsfuncdrm_rect_equals (	r1,	 
 	r2);	 
const struct drm_rect * r1;
const struct drm_rect * r2;
           

参数

r1

第一个矩形

r2

第二个矩形

返回

true

如果矩形相等,

false

否则。

名称

drm_rect_intersect —与两个矩形相交

概要

bool fsfuncdrm_rect_intersect (	r1,	 
 	r2);	 
struct drm_rect * r1;
const struct drm_rect * r2;
           

参数

r1

第一个矩形

r2

第二个矩形

描述

计算矩形

r1

和的交点

r2

。 

r1

将被相交处覆盖。

返回

true

如果矩形

r1

在操作后仍然可见, 

false

否则。

名称

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;
           

参数

src

源窗口矩形

dst

目标窗口矩形

clip

剪辑矩形

hscale

水平比例因子

vscale

垂直比例因子

描述

用矩形

clip

 剪辑矩形

dst

。裁剪矩形

src

以相同数量乘以

hscale

vscale

返回

true

如果矩形

dst

在裁剪后仍然可见, 

false

否则

名称

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;
           

参数

src

源窗口矩形

dst

目标窗口矩形

min_hscale

最小允许水平缩放比例

max_hscale

最大允许水平缩放比例

描述

将水平缩放系数计算为(

src

宽度)/(

dst

宽度)。

返回

水平缩放因子或超出范围的错误。

名称

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;
 
           

参数

src

源窗口矩形

dst

目标窗口矩形

min_vscale

最小允许垂直缩放系数

max_vscale

最大允许垂直缩放比例

描述

将垂直缩放系数计算为(

src

高度)/(

dst

高度)。

返回

垂直比例因子或超出范围的错误值。

名称

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;
           

参数

src

源窗口矩形

dst

目标窗口矩形

min_hscale

最小允许水平缩放比例

max_hscale

最大允许水平缩放比例

描述

将水平缩放系数计算为(

src

宽度)/(

dst

宽度)。

如果计算出的比例因子小于

min_vscale

,则减小矩形的高度以

dst

进行补偿。

如果计算出的比例因子大于

max_vscale

,则减小矩形的高度以

src

进行补偿。

返回

水平缩放因子。

名称

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;
           

参数

src

源窗口矩形

dst

目标窗口矩形

min_vscale

最小允许垂直缩放系数

max_vscale

最大允许垂直缩放比例

描述

将垂直缩放系数计算为(

src

高度)/(

dst

高度)。

如果计算出的比例因子小于

min_vscale

,则减小矩形的高度以

dst

进行补偿。

如果计算出的比例因子大于

max_vscale

,则减小矩形的高度以

src

进行补偿。

返回

垂直比例因子。

名称

drm_rect_debug_print —打印矩形信息

概要

void fsfuncdrm_rect_debug_print (	r,	 
 	fixed_point);	 
const struct drm_rect * r;
bool fixed_point;
 
           

参数

r

要打印的矩形

fixed_point

矩形为16.16定点格式

翻转工作助手参考

在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;
};  
           

成员

name

调试名称

pending

排队但未提交的项目数

count

承诺项目数

func

为每个已提交项目调用回调函数

worker

调用func的工作队列对象

名称

drm_flip_work_queue —工作队列

概要

void fsfuncdrm_flip_work_queue (	work,	 
 	val);	 
struct drm_flip_work * work;
void * val;
 
           

参数

work

翻转工作

val

要排队的值

描述

队列工作,该工作随后将在

drm_flip_work_commit

调用后在工作队列上运行(传递回drm_flip_func_t func)。

名称

drm_flip_work_commit —提交排队的工作

概要

void fsfuncdrm_flip_work_commit (	work,	 
 	wq);	 
struct drm_flip_work * work;
struct workqueue_struct * wq;
           

参数

work

翻转工作

wq

在其上运行排队的工作的工作队列

描述

触发先前排队的工作

drm_flip_work_queue

以在工作队列上运行。典型的用法是(通过 

drm_flip_work_queue

)在任何点(通过vblank irq和/或之前)对工作进行排队,然后从vblank irq提交排队的工作。

名称

drm_flip_work_init —初始化翻转工作

概要

int fsfuncdrm_flip_work_init (	work,	 
 	size,	 
 	name,	 
 	func);	 
struct drm_flip_work * work;
int size;
const char * name;
drm_flip_func_t func;
           

参数

work

初始化的翻转工作

size

最大队列深度

name

调试名称

func

回调工作功能

描述

初始化/分配用于翻转作业的资源

返回

成功为零,失败为错误代码。

名称

drm_flip_work_cleanup —清理翻转工作

概要

void fsfuncdrm_flip_work_cleanup (	work);	 
struct drm_flip_work * work;
           

参数

work

需要清理filp-work

描述

销毁分配给filp-work的资源

VMA偏移管理器

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;
 
           

参数

mgr

管理对象

page_offset

可用存储区的偏移量(基于页面)

size

可用地址空间范围的大小(基于页面)

描述

初始化一个新的偏移量管理器。可用于管理器的偏移量和区域大小以

page_offset

size

给出。两者单位都为页码,而不是字节。

从管理器添加/删除节点在内部被锁定,并且可以防止并发访问。但是,节点分配和销毁留给调用方。调用vma-manager时,必须始终保证引用了给定节点。

名称

drm_vma_offset_manager_destroy —销毁偏移量管理器

概要

void fsfuncdrm_vma_offset_manager_destroy (	mgr);	 
struct drm_vma_offset_manager * mgr;
           

参数

mgr

管理对象

描述

销毁先前通过

drm_vma_offset_manager_init

创建的对象管理器 。在销毁管理器之前,调用者必须删除所有分配的节点。否则,drm_mm将拒绝释放请求的资源。

调用此函数后,不得再访问此管理器。

名称

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;
 
           

参数

mgr

管理对象

start

对象的起始地址(基于页面)

pages

对象大小(基于页面)

描述

查找具有给定起始地址和对象大小的节点。这将返回给定节点的_best_匹配项。也就是说,

start

只要该节点指向整个有效区域(给定的页面数为

pages

),就可以指向某个有效区域中的某个位置并返回给定的节点。

返回

如果找不到合适的节点,则返回NULL。否则,将返回最佳匹配。调用者有责任确保在调用者访问节点之前不会破坏该节点。

名称

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;
 
           

参数

mgr

管理对象

start

对象的起始地址(基于页面)

pages

对象大小(基于页面)

描述

drm_vma_offset_lookup

相同,但要求调用者手动锁定偏移量查找。请参阅

drm_vma_offset_lock_lookup

示例。

返回

如果找不到合适的节点,则返回NULL。否则,将返回最佳匹配。

名称

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;
           

参数

mgr

管理对象

node

要添加的节点

pages

用户空间可见的分配大小(以页数为单位)

描述

将节点添加到offset-manager。如果已添加节点,则不执行任何操作,并返回0。

pages

是对象的大小(以页数为单位)。成功执行此调用后,您可以访问节点的偏移量,直到再次将其删除为止。

如果此调用失败,则可以安全地重试该操作或调用

drm_vma_offset_remove

。但是,在这种情况下,不需要清理。

pages

不需要与要映射的基础内存对象大小相同。它仅限制了用户空间可以映射到其地址空间的大小。

返回

成功时为0,失败时为负错误代码。

名称

drm_vma_offset_remove —从管理器中删除偏移节点

概要

void fsfuncdrm_vma_offset_remove (	mgr,	 
 	node);	 
struct drm_vma_offset_manager * mgr;
struct drm_vma_offset_node * node;
 
           

参数

mgr

管理对象

node

要删除的节点

描述

从偏移管理器中删除节点。如果之前未添加节点,则不会执行任何操作。此调用返回后,偏移量和大小将为0,直到

drm_vma_offset_add

再次分配新的偏移量为止。辅助函数类似于 

drm_vma_node_start

drm_vma_node_offset_addr

如果没有分配偏移量,它将返回0。

名称

drm_vma_node_allow-将打开文件添加到允许的用户列表中

概要

int fsfuncdrm_vma_node_allow (	node,	 
 	filp);	 
struct drm_vma_offset_node * node;
struct file * filp;
           

参数

node

修改节点

filp

开放文件以添加

描述

添加

filp

到此节点允许的打开文件列表。如果

filp

已在此列表中,则引用计数将增加。

允许的用户列表保留在

drm_vma_offset_add

和 

drm_vma_offset_remove

调用之间。如果该节点当前未添加到任何偏移量管理器中,您甚至可以调用它。

在销毁节点之前,您必须删除所有打开文件的次数与添加它们的次数相同。否则,您将泄漏内存。

这是在内部禁止并发访问的。

返回

成功时为0,内部失败时为负错误代码(内存不足)

名称

drm_vma_node_revoke —从允许的用户列表中删除打开文件

概要

void fsfuncdrm_vma_node_revoke (	node,	 
 	filp);	 
struct drm_vma_offset_node * node;
struct file * filp;
           

参数

node

修改节点

filp

打开文件删除

描述

减少

filp

上允许的打开文件列表

node

中的ref-count 。如果引用计数降至零,

filp

则从列表中删除。你必须调用此一次,每

drm_vma_node_allow

filp

这是在内部禁止并发访问的。

如果

filp

不在列表中,则什么也不做。

名称

drm_vma_node_is_allowed —检查是否授予打开文件访问权限

概要

bool fsfuncdrm_vma_node_is_allowed (	node,	 
 	filp);	 
struct drm_vma_offset_node * node;
struct file * filp;
 
           

参数

node

要检查的节点

filp

打开文件进行检查

描述

在当前

node

搜索列表是否

filp

在允许的打开文件列表中(请参阅参考资料

drm_vma_node_allow

)。

这是在内部禁止并发访问的。

返回

真的,如果

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;
           

参数

mgr

管理对象

start

起始地址(基于页面,而非基于字节)

pages

对象大小(基于页面)

描述

drm_vma_offset_lookup

节点相同,但不允许向节点偏移。它仅返回具有给定起始地址的确切对象。

返回

节点位于确切的起始地址

start

名称

drm_vma_offset_lock_lookup-锁定查找以供扩展私人使用

概要

void fsfuncdrm_vma_offset_lock_lookup (	mgr);	 
struct drm_vma_offset_manager * mgr;
           

参数

mgr

管理对象

描述

锁定VMA Manager以进行扩展查找。

_locked

按住此锁定仅允许* VMA函数调用。在通过

drm_vma_offset_unlock_lookup

释放锁定之前,所有其他上下文都无法访问VMA 。

如果您需要在重新释放此锁之前引用

drm_vma_offset_lookup_locked

返回的对象,请使用此方法 。

除扩展查找外,不得将此锁用于任何其他用途。按住此锁定时,不得调用任何其他VMA帮助器。

注意

持有此锁,您处于原子上下文中!

drm_vma_offset_lock_lookup(mgr);
     node = drm_vma_offset_lookup_locked(mgr);
     if (node)
         kref_get_unless_zero(container_of(node, sth, entr));
     drm_vma_offset_unlock_lookup(mgr);
           

名称

drm_vma_offset_unlock_lookup —解锁查找以供扩展私人使用

概要

void fsfuncdrm_vma_offset_unlock_lookup (	mgr);	 
struct drm_vma_offset_manager * mgr;
           

参数

mgr

管理对象

描述

释放查找锁定。请参阅

drm_vma_offset_lock_lookup

以获取更多信息。

名称

drm_vma_node_reset —初始化或重置节点对象

概要

void fsfuncdrm_vma_node_reset (	node);	 
struct drm_vma_offset_node * node;
           

参数

node

要初始化或重置的节点

描述

将节点重置为其初始状态。在与任何VMA偏移管理器一起使用之前,必须先调用它。

不得在已分配的节点上调用此方法,否则会泄漏内存。

名称

drm_vma_node_start —返回基于页面的寻址的起始地址

概要

unsigned long fsfuncdrm_vma_node_start (	node);	 
struct drm_vma_offset_node * node;
           

参数

node

要检查的节点

描述

返回给定节点的起始地址。可以用作VMA偏移管理器提供的线性VM空间中的偏移。请注意,这只能用于基于页面的寻址。如果您需要为用户空间映射提供适当的偏移量,则必须应用“ << PAGE_SHIFT ”或使用 

drm_vma_node_offset_addr

辅助程序。

返回

node

用于基于页面的寻址的 起始地址。如果节点未分配偏移量,则为0。

名称

drm_vma_node_size —返回大小(基于页面)

概要

unsigned long fsfuncdrm_vma_node_size (	node);	 
struct drm_vma_offset_node * node;
           

参数

node

要检查的节点

描述

以给定节点的页面数形式返回大小。这与传递给

drm_vma_offset_add

的大小相同。如果没有为节点分配偏移量,则为0。

返回

node

页数的 大小。如果节点未分配偏移量,则为0。

名称

drm_vma_node_has_offset —检查是否将节点添加到偏移管理器

概要

bool fsfuncdrm_vma_node_has_offset (	node);	 
struct drm_vma_offset_node * node;
 
           

参数

node

要检查的节点

返回

如果先前已为节点分配了偏移并将其添加到vma偏移管理器,则为true。

名称

drm_vma_node_offset_addr —返回用户空间mmap的已清理偏移量

概要

__u64 fsfuncdrm_vma_node_offset_addr (	node);	 
struct drm_vma_offset_node * node;
 
           

参数

node

链接偏移节点

描述

与相同,

drm_vma_node_start

但返回地址作为有效偏移量,可在期间用于用户空间映射

mmap

。不能在未链接的节点上调用它。

返回

node

基于字节的寻址的 偏移量。如果节点未分配对象,则为0。

名称

drm_vma_node_unmap —取消映射偏移量节点

概要

void fsfuncdrm_vma_node_unmap (	node,	 
 	file_mapping);	 
struct drm_vma_offset_node * node;
struct address_space * file_mapping;
 
           

参数

node

偏移节点

file_mapping

地址空间取消映射

node

描述

取消映射给定偏移节点的所有用户空间映射。映射必须与

file_mapping

地址空间关联。如果不存在偏移量或地址空间无效,则不执行任何操作。

此呼叫已解锁。调用方必须保证

drm_vma_offset_remove

 不会在此节点上同时调用。

名称

drm_vma_node_verify_access — TTM的访问验证帮助器

概要

int fsfuncdrm_vma_node_verify_access (	node,	 
 	filp);	 
struct drm_vma_offset_node * node;
struct file * filp;
           

参数

node

偏移节点

filp

打开文件

描述

这将检查是否

filp

授予对的访问权限

node

。它与

drm_vma_node_is_allowed

相同, 但很适合 TTM 

verify_access

回调的插入助手。

返回

如果授予访问权限,则为0,否则为-EACCES。

KMS属性

驱动程序可能需要向应用程序公开除前几节所述之外的其他参数。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,连接器和平面对象 。该函数获取指向目标对象的指针,指向先前创建的属性的指针和初始实例值。

垂直Blanking

垂直消隐在图形渲染中起主要作用。为了实现无撕裂显示,用户必须将页面翻转和/或渲染与垂直消隐同步。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

分配的资源。

打开/关闭,文件操作和IOCTL

打开和关闭

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设备节点的文件操作。

驱动程序必须定义构成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不支持其他文件操作。

IOCTLS

struct drm_ioctl_desc *ioctls;
int num_ioctls
           

  特定于驱动程序的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。

DMA服务

这应该涵盖内核如何支持DMA映射等。这些功能已被弃用,不应使用。

第3章User空间接口

目录

渲染节点

VBlank事件处理

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关联,因为它们独立于任何图形服务器。此外,无论如何,它们必须在没有任何运行主机的情况下工作。如果驱动程序支持渲染节点,则它们必须能够在没有主对象的情况下运行。另一方面,如果驱动程序要求客户端之间的共享状态对用户空间可见并且可以在打开文件边界之外访问,则它们不支持渲染节点。

VBlank事件处理

DRM核心公开了两个垂直的空白相关的ioctl:

DRM_IOCTL_WAIT_VBLANK

这以struct drm_wait_vblank结构作为其参数,并在发生指定的vblank事件时用于阻止或请求信号。

DRM_IOCTL_MODESET_CTL

在模式设置之前和之后,应由应用程序级别的驱动程序调用此方法,因为在许多设备上,垂直空白计数器当时会被重置。在内部,当使用_DRM_PRE_MODESET命令调用ioctl时,DRM会对最后的vblank计数进行快照,以使计数器不会向后移动(使用_DRM_POST_MODESET时将进行处理)。

附录A.DRM驱动程序API

在此处包括自动生成的API参考(也需要在以上段落中进行参考)。

继续阅读