Linux DRM的基本框架和原理

一、概述

Linux内核有一个名为fbdev(Linux framebuffer,一个Linux子系统,用于在计算机显示器上显示图形,通常在系统控制台上https://en.wikipedia.org/wiki/Linux_framebuffer)的API,用于管理图形适配器(graphics card,也叫video carddisplay cardgraphics acceleratorgraphics adapterVGA card/VGAvideo adapterdisplay adapter等)的帧缓冲区,但它无法处理现代基于GPU的3D加速视频硬件的需求。如下图所示:

这些设备通常需要在自己的内存中设置和管理命令队列(command queue),以将命令分派到GPU,并且还需要管理该内存中的缓冲区和可用空间。最初,用户空间程序(例如X Server)直接管理这些资源,但它们通常表现得好像它们是唯一可以访问这些资源的程序。当两个或多个程序试图同时控制同一硬件,并以自己的方式设置各自的资源时,大多数时候它们都会以灾难性的方式结束。如下图所示:

因此,允许多个程序协作使用视频硬件资源成为了需要攻克的技术问题。而DRM的创建就是为了允许多个程序协作使用视频硬件资源。DRM获得对GPU的独占访问权限,并负责初始化和维护命令队列、内存和任何其他硬件资源。希望使用GPU的程序向DRM发送请求,DRM充当仲裁者并注意避免可能的冲突。

多年来,DRM的范围不断扩大,涵盖了以前由用户空间程序处理的更多功能,例如帧缓冲区管理(framebuffer managing)和模式设置(mode setting)、内存共享对象和内存同步。其中一些扩展被赋予了特定的名称,例如图形执行管理器(Graphics Execution Manager,GEM)或内核模式设置(kernel mode-setting,KMS),它们实际上都是整个内核DRM子系统的一部分。

在计算机中包含两个GPU(一个独立GPU和一个集成GPU)的趋势导致了GPU切换等新问题,这些问题也需要在DRM层解决。为了匹配Nvidia Optimus技术,DRM还提供了GPU卸载能力,称为PRIME。

· 1999年,在为XFree86开发DRI(Direct Rendering Infrastructure,包含现代 Linux 图形堆栈的框架,它允许非特权用户空间程序向图形硬件发出命令,而不会与其他程序发生冲突https://en.wikipedia.org/wiki/Direct_Rendering_Infrastructure)时,Precision Insight为3dfx显卡(video cards)创建了第一个版本的DRM,作为包含在Mesa源代码中的Linux内核补丁。

· 1999年晚些时候,DRM代码在Linux内核2.3.18中主流化,位于字符设备的/drivers/char/drm/目录下。

在接下来的几年里,支持的显卡数量不断增加。

· 2001年1月,当Linux 2.4.0于发布时,除了3dfx Voodoo3卡之外,还支持Creative Labs GMX 2000、Intel i810、Matrox G200/G400和ATI Rage 128,并且该列表在2.4期间得到了扩展。x系列,包含 ATI Radeon卡、某些SiS显卡以及Intel 830M和后续集成GPU的驱动程序。

· 2004年下半年,DRM 拆分为两个组件,即DRM核心和DRM驱动程序,称为DRM核心/个性拆分,并合并到内核版本2.6.11中。这种拆分允许多个设备的多个DRM驱动程序同时工作,从而为多GPU支持开辟了道路。

将所有视频模式设置代码放在内核中的一个位置的想法多年来已被认可,但显卡制造商认为进行模式设置的唯一方法是使用例程由它们自己提供并包含在每个显卡的视频BIOS中。此类代码必须使用x86实模式执行,这会阻止它被在保护模式下运行的内核调用。当Luc Verhaegen和其他开发人员找到一种本地模式设置而不是基于BIOS的方法时,情况发生了变化,表明可以使用正常的内核代码来完成此操作,并为未来的发展奠定了基础。成为内核模式设置(Kernel Mode Setting,即KMS)。

· 2007年5月,Jesse Barnes(英特尔)发布了第一个关于drm模式设置API的提案,以及i915 DRM驱动程序中英特尔GPU模式设置的本地工作实现。

· 2007年12月,Jerome Glisse开始将ATI卡的本机模式设置代码添加到radeon DRM驱动程序中。· · 2008年,API和驱动程序的工作继续进行,但由于内核空间中还需要内存管理器来处理帧缓冲区而被推迟。

· 2008年10月,Linux内核2.6.27对源代码进行了重大重组,随后进行了一些重大更改。DRM源代码树已移至其自己的源目录/drivers/gpu/drm/,不同的驱动程序也移至其自己的子目录中。头文件也被移动到新的/include/drm目录中。

视频内存管理日益复杂,导致出现了多种解决此问题的方法。第一个尝试是翻译表映射 (TTM) 内存管理器,由Thomas Hellstrom (Tungsten Graphics) 与Emma Anholt (Intel) 和Dave Airlie (Red Hat) 合作开发。

· 2007年11月,TTM被提议纳入主线内核2.6.25

· 2008年5月,TTM再次被提议纳入主线内核,但最终被放弃,转而采用称为图形执行管理器 (GEM) 的新方法。GEM最初由Intel的Keith Packard 和Emma Anholt 开发,作为i915驱动程序内存管理的更简单解决方案。

·2008年12月,受到好评的GEM合并到发布的Linux内核版本2.6.28中。

· 2009年9月,TTM才最终合并到Linux 2.6.31,以满足新Radeon KMS DRM驱动程序的要求。

通过内存管理来处理缓冲区对象,DRM开发人员最终可以将已经完成的API和代码添加到内核中以进行模式设置。这个扩展的API就是所谓的内核模式设置 (KMS),实现它的驱动程序通常称为KMS驱动程序。

· 2009 年 3 月,KMS被合并到 Linux 内核版本2.6.29中,以及对i915驱动程序的KMS支持。自libdrm 2.4.3起,KMS API已暴露给用户空间程序。Intel显卡的用户空间X.Org DDX驱动程序也是第一个使用新的GEM和KMS API的驱动程序。

· 2009年9月发行的Linux 2.6.31版本中添加了对 radeon DRM驱动程序的KMS支持。新的radeon KMS驱动程序使用TTM内存管理器,但公开了GEM兼容接口和ioctl,而不是TTM 接口和ioctl。

自2006年以来,nouveau项目一直在官方 Linux 内核之外为 NVIDIA GPU 开发免费软件DRM驱动程序。2010年,nouveau 源代码作为实验性驱动程序合并到Linux 2.6.33中。在合并时,驱动程序已经转换为KMS,并且在GEM API后面它使用TTM作为其内存管理器。

新的KMS API(包括 GEM API)是DRM发展中的一个重要里程碑,但它并没有阻止API在接下来的几年中得到增强。KMS在Linux 2.6.33中获得了对页面翻转与异步VBLANK通知的支持——仅适用于i915驱动程序,radeon和nouveau后来在Linux 2.6.38版本中添加了它。libdrm 2.4.17添加了新的翻页界面。

· 2011 年初,在Linux 2.6.39发布周期中,所谓的dumb buffers(一种独立于硬件的非加速方式,用于处理适合用作帧缓冲区的简单缓冲区)被添加到KMS API中。目标是降低Plymouth等应用程序的复杂性,这些应用程序不需要使用特定于驱动程序的ioctl提供的特殊加速操作。该功能由libdrm从2.4.25版本开始公开。

· 同年晚些时候,它还获得了一种新的主​​要类型,称为planesplanes被开发来表示扫描输出引擎支持的硬件覆盖。planes支持已合并到Linux 3.3和libdrm 2.4.30。在Linux 3.5和libdrm 2.4.36版本期间,添加到API的另一个概念是通用对象属性,这是一种向任何KMS对象添加通用值的方法。属性对于为CRTC和planes等对象设置特殊行为或功能特别有用。

· Dave Airlie于2010年开发了在DRM驱动程序之间提供GPU卸载的早期概念证明。由于Airlie试图模仿NVIDIA Optimus技术,因此他决定将其命名为“PRIME”。

· Airlie于2011年底恢复了PRIME的工作,但基于Linux内核3.3引入的新DMA-BUF缓冲区共享机制。

· 基本的DMA-BUF PRIME开发于2012年3月完成,并合并到Linux 3.4版本以及libdrm 2.4.34中。后来在Linux 3.5版本中,多个DRM驱动程序实现了PRIME支持,包括用于Intel卡的 i915、用于AMD卡的radeon和用于NVIDIA卡的nouveau。

近年来,DRM API随着新功能和改进功能的不断扩展而不断扩展。

· 2013年,作为GSoC的一部分,David Herrmann开发了多个渲染节点功能。他的代码被添加到 Linux内核版本3.12作为实验性功能,受i915、radeon和nouveau驱动程序支持,并且自Linux 3.17 起默认启用。

· 2014 年,Matt Roper(英特尔)提出了通用planes(或统一planes)概念,其中帧缓冲区(主planes)、覆盖层(辅助planes)和光标(光标planes)都被视为具有统一API的单一类型对象。通用planes支持提供了更一致的DRM API,具有更少、更通用的ioctl。为了保持API向后兼容,DRM核心将该功能公开为DRM驱动程序可以提供的附加功能。通用planes支持在Linux 3.15和libdrm 2.4.55中首次亮相。一些驱动程序,例如Intel i915已经实现了它。

最新的DRM API增强功能是原子模式设置API,它为DRM设备上的模式设置和页面翻转操作带来了原子性。用于模式设置的原子API的想法于2012年初首次提出。Ville Syrjälä(英特尔)接管了设计和实现此类原子API的任务。基于他的工作,Rob Clark(德州仪器)采取了类似的方法,旨在实现原子页面翻转。2013 年晚些时候,这两个提议的功能被重新组合到一个功能中,使用单个ioctl来完成这两个任务。由于这是一个需求,该功能必须等待通用平面的支持在2014年中期合并。2014 年下半年,Daniel Vetter(英特尔)和其他DRM开发人员极大地增强了原子代码,以促进现有 KMS驱动程序向新原子框架的过渡。所有这些工作最终合并到Linux 3.19和 Linux 4.0版本中,并从 Linux 4.2开始默认启用libdrm自2.4.62版本起公开了新的原子API。多个驱动程序已转换为新的原子API。到 2018 年,Linux内核中已添加十个基于此新原子模型的新 DRM 驱动程序。

二、软件架构

DRM是在内核空间中的,因此用户空间程序必须使用内核系统调用来请求其服务。但是,DRM没有定义自己的系统调用。它遵循“一切都是文件”的Unix原则,通过文件系统名称空间公开GPU,使用/dev结构下的设备文件。DRM检测到的每个GPU称为DRM设备,并创建一个设备文件/dev/dri/cardX(其中X是序列号)来与其连接。想要与GPU通信的用户空间程序必须打开此文件并使用ioctl调用与DRM通信。不同的ioctl对应DRM API的不同功能。

创建了一个名为libdrm的库,以促进用户空间程序与DRM子系统的接口。该库只是一个包装器,为 DRM API的每个ioctl提供用C编写的函数,以及常量、结构和其他辅助元素。使用libdrm不仅避免了将内核接口直接暴露给应用程序,而且还提供了在程序之间重用和共享代码的常见优点。

DRM由两部分组成:通用的“DRM核心”(generic DRM API)和针对每种类型支持的硬件的特定部分(“DRM驱动程序”)(specific driver API)。DRM核心提供了不同DRM驱动程序可以注册的基本框架,并为用户空间提供了一组具有通用、独立于硬件的功能的最小ioctl 集。另一方面,DRM驱动程序实现API的硬件相关部分,特定于它支持的GPU类型;它应该提供DRM核心未涵盖的其余ioctl的实现,但它也可以扩展API,提供额外的ioctl以及仅在此类硬件上可用的额外功能。当特定的DRM驱动程序提供增强的API时,用户空间libdrm也会通过额外的库libdrm-driver进行扩展,用户空间可以使用该库与其他ioctl进行交互。

常规意义中,DRM从模块上划分,可以简单分为3部分:libdrm、GEM、KMS。

模块功能
libdrm对底层接口进行封装,向上层提供通用的API接口,主要是对各种IOCTL接口进行封装
KMSKernel Mode Setting,所谓Mode setting,其实说白了就两件事:
更新画面:显示buffer的切换,多图层的合成方式,以及每个图层的显示位置。
设置显示参数:包括分辨率、刷新率、电源状态(休眠唤醒)等。
GEMGraphic Execution Manager,主要负责显示buffer的分配和释放,也是GPU唯一用到DRM的地方。

这些组件的关系如下列图所示:

①KMS

②GEM

③CMA

④PRIME

⑤GPUs

⑥GPU(Mali-400)

1.libdrm

DRM向用户空间应用程序导出多个接口,通常旨在通过相应的libdrm封装的函数来使用。此外,驱动程序通过ioctl和sysfs文件导出特定于设备的接口,供用户空间驱动程序和设备感知应用程序使用。外部接口包括:内存映射、上下文管理、DMA操作、AGP管理、vblank控制、fence管理、内存管理和输出管理。

DRM-Master and DRM-Auth

DRM API中有多个操作 (ioctls),出于安全目的或并发问题,必须限制为每个设备的单个用户空间进程使用。为了实现此限制,DRM限制此类ioctl只能由被视为DRM设备“master”的进程(通常称为 DRM-Master)调用。在打开设备节点/dev/dri/cardX的所有进程中,只有一个进程会将其文件句柄标记为master,特别是第一个调用SET_MASTER ioctl的进程。如果不是DRM主控,则任何尝试使用这些受限制的ioctl之一都将返回错误。进程还可以通过调用DROP_MASTER ioctl放弃其主角色,并让另一个进程获取它。

X 服务器(或任何其他显示服务器)通常是在其管理的每个DRM设备中获取DRM-Master状态的进程,通常是在启动期间打开相应的设备节点时,并在整个图形会话中保留这些权限,直到它结束或死亡。

对于其余的用户空间进程,还有另一种方法可以获取在DRM设备上调用某些受限操作的权限,称为DRM-Auth。它基本上是一种针对DRM设备进行身份验证的方法,以向其证明该进程已获得 DRM-Master 的批准以获得此类权限。流程如下:

①客户端使用GET_MAGIC ioctl从DRM设备获取一个唯一的令牌(32 位整数),并通过任何方式(通常是某种 IPC;例如,在DRI2中,有一个DRI2Authenticate请求)将其传递给DRM-Master进程。任何X客户端都可以发送到X服务器。)

②DRM-Master进程又通过调用AUTH_MAGIC ioctl将令牌发送回DRM设备。

③设备向其身份验证令牌与从DRM-Master接收到的令牌相匹配的进程文件句柄授予特殊权限。

2.KMS

为了显示的正常工作,视频卡或图形适配器必须设置一种模式(屏幕分辨率、颜色深度和刷新率的组合),该模式应在其自身和所连接的显示屏支持的值范围内。此操作称为模式设置,它通常需要对图形硬件的原始访问,即能够写入视频卡显示控制器的某些寄存器。在开始使用帧缓冲区之前,以及当应用程序或用户需要更改模式时,必须执行模式设置操作。

在概述中我们提到过,在早期,想要使用图形帧缓冲区的用户空间程序还负责提供模式设置操作,因此它们需要以对视频硬件的特权访问权限运行。在Unix类型的操作系统中,X Server是最突出的例子,其模式设置实现存在于每种特定类型显卡的DDX驱动程序中。这种方法后来称为用户空间模式设置或UMS。它不仅打破了操作系统应在程序和硬件之间提供的隔离,从而提高了稳定性和安全性问题,而且如果两个或多个用户空间程序尝试在同一位置进行模式设置,还可能使图形硬件处于不一致的状态。同时。为了避免这些冲突,X Server实际上成为唯一执行模式设置操作的用户空间程序;其余的用户空间程序依赖X Server 来设置适当的模式并处理涉及模式设置的任何其他操作。最初,模式设置仅在X Server启动过程中执行,但后来X Server获得了在运行时执行此操作的能力。XFree86-VidModeExtension扩展在XFree86 3.1.2中引入,允许任何X客户端请求对X服务器进行模型行(分辨率)更改。VidMode扩展后来被更通用的XRandR扩展取代。

然而,这并不是Linux系统中唯一进行模式设置的代码。在系统启动过程中,Linux内核必须为虚拟控制台设置最小文本模式(基于VESA BIOS扩展定义的标准模式)。Linux内核帧缓冲区驱动程序还包含用于配置帧缓冲区设备的模式设置代码。为了避免模式设置冲突,XFree86服务器(以及后来的X.Org服务器)通过保存其模式设置状态并在用户切换时恢复它来处理用户从图形环境切换到文本虚拟控制台时的情况回到X。此过程会在转换过程中引起烦人的闪烁,并且还可能失败,导​​致输出显示损坏或无法使用。

早期的显示控制过程如下:

用户空间模式设置方法还引起了其他问题:

①挂起/恢复过程必须依赖用户空间工具来恢复之前的模式。这些程序之一的一次故障或崩溃可能会导致系统由于模式集配置错误而无法正常显示,从而无法使用。

②当屏幕处于图形模式时(例如当X运行时),内核也不可能显示错误或调试消息,因为内核知道的唯一模式是VESA BIOS标准文本模式。

③更紧迫的问题是绕过X Server的图形应用程序的激增以及X的其他图形堆栈替代方案的出现,进一步扩展了整个系统中模式设置代码的重复。

为了解决这些问题,模式设置代码被移动到内核内的一个位置,特别是现有的DRM模块。然后,每个进程(包括X Server)都应该能够命令内核执行模式设置操作,并且内核将确保并发操作不会导致不一致的状态。添加到DRM模块以执行这些模式设置操作的新内核API和代码称为内核模式设置 (KMS)。

内核模式设置有几个好处。最直接的当然是从内核(Linux 控制台、fbdev)和用户空间(X Server DDX驱动程序)中删除重复的模式设置代码。KMS还使编写替代图形系统变得更加容易,这些系统现在不需要实现自己的模式设置代码。通过提供集中模式管理,KMS解决了在控制台和X之间以及不同X实例之间切换时的闪烁问题(快速用户切换)。由于它在内核中可用,因此也可以在启动过程开始时使用,从而避免在这些早期阶段因模式更改而导致的闪烁。

KMS 是内核的一部分,这一事实允许它使用仅在内核空间可用的资源,例如中断。挂起/恢复过程后的模式恢复通过由内核本身管理而简化了很多,并且顺便提高了安全性(不再需要根权限的用户空间工具)。内核还允许轻松热插拔新的显示设备,解决了一个长期存在的问题。模式设置也与内存管理密切相关——因为帧缓冲区基本上是内存缓冲区——因此强烈建议与图形内存管理器紧密集成。这就是为什么内核模式设置代码被合并到DRM中而不是作为单独的子系统的主要原因。

为了避免破坏DRM API的向后兼容性,内核模式设置作为某些DRM驱动程序的附加驱动程序功能提供。任何DRM驱动程序在向DRM核心注册时都可以选择提供DRIVER_MODESET标志,以指示支持KMS API。那些实现内核模式设置的驱动程序通常称为KMS驱动程序,以区别于传统(不带 KMS)的DRM驱动程序。

KMS设备模型

KMS将输出设备作为显示控制器的显示输出管道上常见的一系列抽象硬件块进行建模和管理。这些元素是:CRTC,ENCODER,CONNECTOR,PLANE,FB,VBLANK,property

元素说明
CRTC对显示buffer进行扫描,并产生时序信号的硬件模块,通常指Display Controller
ENCODER负责将CRTC输出的timing时序转换成外部设备所需要的信号的模块,如HDMI转换器或DSI Controller
CONNECTOR连接物理显示设备的连接器,如HDMI、DisplayPort、DSI总线,通常和Encoder驱动绑定在一起
PLANE硬件图层,有的Display硬件支持多层合成显示,但所有的Display Controller至少要有1个plane
FBFramebuffer,单个图层的显示内容,唯一一个和硬件无关的基本元素
VBLANK软件和硬件的同步机制,RGB时序中的垂直消影区,软件通常使用硬件VSYNC来实现
property任何你想设置的参数,都可以做成property,是DRM驱动中最灵活、最方便的Mode setting机制

其中,最重要的是以下四个组件:

CRTC:负责把要显示图像,转化为底层硬件层面上的具体时序要求,还负责着帧切换、电源控制、色彩调整等等。

每个CRTC(来自CRT控制器)代表显示控制器的一个扫描输出引擎,指向一个扫描输出缓冲区(帧缓冲区)。CRTC的目的是读取扫描输出缓冲区中当前的像素数据,并借助PLL电路从中生成视频模式时序信号。可用的CRTC数量决定了硬件可以同时处理多少个独立输出设备,因此为了使用多头配置,每个显示设备至少需要一个CRTC。如果两个或多个CRTC从同一帧缓冲区进行扫描以将相同的图像发送到多个输出设备,则它们也可以在克隆模式下工作。

Connector:负责硬件设备的接入、屏参获取等

Connector代表显示控制器从扫描输出操作发送视频信号以进行显示的位置。通常,Connector的KMS概念对应于输出设备(显示器、笔记本电脑面板等)硬件中的物理连接器(VGA、DVI、FPD-Link、HDMI、DisplayPort、S-Video等) 是永久或可以暂时附着的。与当前物理连接的输出设备相关的信息(例如连接状态、EDID数据、DPMS状态或支持的视频模式)也存储在连接器内。

Encoder:负责电源管理、视频输出格式封装(比如要将视频输出到HDMI接口、MIPI接口等)

显示控制器必须使用适合预期Connector的格式对来自CRTC的视频模式时序信号进行编码。Encoder代表能够执行这些编码之一的硬件块。数字输出的编码示例包括TMDS和LVDS;对于VGA和TV输出等模拟输出,通常使用特定的DAC模块。一个Connector一次只能接收来自一个Encoder的信号,并且每种类型的Connector仅支持某些编码。还可能存在额外的物理限制,并非每个CRTC都连接到每个可用的Encoder,从而限制了CRTC-Encoder-Connector的可能组合。

Plane:基本的显示控制单位,每个图像拥有一个Planes,Planes的属性控制着图像的显示区域、图像翻转、色彩混合方式等, 最终图像经过Planes并通过CRTC组件,得到多个图像的混合显示或单独显示的等等功能。

Plane不是硬件模块,而是包含缓冲区的内存对象,扫描输出引擎(CRTC)从中馈送。保存帧缓冲区的Plane称为主Plane,每个CRTC必须有一个关联的Plane,因为它是CRTC确定视频模式的源——显示分辨率(宽度和高度)、像素大小、像素格式、发送的最终图像等。

这些组件,最终完成了一个完整的DRM显示控制过程,如下图所示

3.GEM

由于视频内存的大小不断增加以及OpenGL等图形API的复杂性不断增加,在每次上下文切换时重新初始化显卡状态的策略在性能方面过于昂贵。此外,现代Linux桌面需要一种与合成管理器(compositing manager)共享离屏缓冲区的最佳方式。这些要求导致了管理内核内图形缓冲区需要开发新的方法。图形执行管理器 (GEM,Graphics Execution Manager ) 就是其中一种方法。

GEM提供了具有显式内存管理原语的API。通过GEM,用户空间程序可以创建、处理和销毁GPU 视频内存中的内存对象。这些对象被称为“GEM对象”,从用户空间程序的角度来看是持久的,不需要在每次程序重新获得GPU的控制权时重新加载。当用户空间程序需要一块视频内存(用于存储帧缓冲区、纹理或GPU所需的任何其他数据)时,它会使用GEM API请求分配给DRM驱动程序。 DRM驱动程序会跟踪已使用的视频内存,并且如果有可用的空闲内存,则能够满足请求,向用户空间返回“handle”,以便在接下来的操作中进一步引用分配的内存。GEM API还提供填充缓冲区并在不再需要时释放缓冲区的操作。当用户空间进程有意或因终止而关闭DRM设备文件描述符时,未释放的GEM handle中的内存将被恢复。

GEM还允许两个或多个用户空间进程使用相同的DRM设备(相同的DRM驱动程序)来共享GEM对象。GEM句柄是进程唯一的本地32位整数,但在其他进程中可重复,因此不适合共享。我们需要一个全局命名空间,GEM通过使用称为GEM名称的全局句柄提供了一个全局命名空间。GEM名称是指由同一DRM驱动程序使用唯一的32位整数在同一DRM设备内创建的一个且仅一个GEM对象。GEM提供了一个操作flink来从GEM句柄获取GEM名称。然后进程可以使用任何可用的 IPC机制将此GEM名称(32位整数)传递给另一个进程。接收进程可以使用GEM名称来获取指向原始GEM对象的本地GEM句柄。

不幸的是,使用GEM名称来共享缓冲区并不安全。访问同一DRM设备的恶意第三方进程可能会尝试猜测由其他两个共享的缓冲区的GEM名称只需探测32位整数即可进行处理。一旦找到GEM名称,就可以访问和修改其内容,从而违反了缓冲区信息的机密性和完整性。后来通过在DRM中引入DMA-BUF支持克服了这个缺点,因为DMA-BUF将用户空间中的缓冲区表示为文件描述符,可以安全地共享。

对于任何视频内存管理系统来说,除了管理视频内存空间之外,另一个重要任务是处理GPU和 CPU之间的内存同步。当前的内存架构非常复杂,通常涉及系统内存的各级缓存,有时还涉及视频内存。因此,视频内存管理器还应该处理缓存一致性,以确保CPU和GPU之间共享的数据一致。 这意味着视频内存管理内部结构通常高度依赖于GPU和内存架构的硬件细节,因此是特定于驱动程序的。

GEM最初由Intel工程师开发,为其i915驱动程序提供视频内存管理器。Intel GMA 9xx系列是具有统一内存架构 (UMA) 的集成GPU,其中GPU和CPU共享物理内存,并且没有专用的VRAM。  GEM定义了用于内存同步的“内存域”,虽然这些内存域独立于GPU,它们是专门针对UMA内存架构而设计的,这使得它们不太适合其他内存架构,例如具有单独VRAM的内存架构。因此,其他 DRM驱动程序决定向用户空间程序公开GEM API,但在内部它们实现了更适合其特定硬件和内存架构的不同内存管理器。

GEM API还提供了用于控制执行流(命令缓冲区)的ioctl,但它们是Intel特定的,可与Intel i915和更高版本的GPU一起使用。除了内存管理特定的ioctl之外,没有其他DRM驱动程序尝试实现GEM API的任何部分。

GEM框架涉及到的元素有:DUMB、PRIME、fence

元素说明
DUMB只支持连续物理内存,基于kernel中通用CMA API实现,多用于小分辨率简单场景
PRIME连续、非连续物理内存都支持,基于DMA-BUF机制,可以实现buffer共享,多用于大内存复杂场景
fencebuffer同步机制,基于内核dma_fence机制实现,用于防止显示内容出现异步问题

Translation Table Maps

转换表映射 (TTM,Translation Table Maps) 是在GEM之前开发的GPU通用内存管理器的名称。它专门设计用于管理GPU 可能访问的不同类型的内存,包括专用视频RAM(通常安装在视频卡中)和可通过称为图形地址重映射表 (GART) 的 I/O 内存管理单元访问的系统内存。考虑到用户空间图形应用程序通常处理大量视频数据,TTM还应该处理CPU无法直接寻址的视频RAM部分,并以尽可能最佳的性能进行处理。另一个重要的事情是保持所涉及的不同内存和缓存之间的一致性。

TTM的主要概念是“缓冲区对象”,即在某些时候必须可由GPU寻址的视频内存区域。当用户空间图形应用程序想要访问某个缓冲区对象(通常是用内容填充它)时,TTM可能需要将其重新定位到 CPU可寻址的内存类型。当GPU需要访问缓冲区对象但该对象尚未位于GPU地址空间中时,可能会发生进一步的重定位(或GART映射操作)。每个重定位操作都必须处理任何相关的数据和缓存一致性问题。

另一个重要的TTM概念是fencesfences本质上是一种管理CPU和GPU之间并发性的机制。当GPU不再使用缓冲区对象时,fences会进行跟踪,通常是为了通知任何有权访问该对象的用户空间进程。

事实上,TTM试图以合适的方式管理所有类型的内存架构,包括带有和不带有专用VRAM的内存架构,并在内存管理器中提供所有可以想象到的功能以供与任何类型的硬件一起使用,这导致了过于复杂的情况API远大于所需的解决方案。一些DRM开发人员认为它不太适合任何特定的驱动程序,尤其是API。当GEM作为一种更简单的内存管理器出现时,它的API比TTM更受青睐。但一些驱动程序开发人员认为TTM采取的方法更适合具有专用视频内存和IOMMU的离散显卡,因此他们决定在内部使用TTM,同时将其缓冲区对象公开为GEM对象,从而支持GEM API。当前使用TTM作为内部内存管理器但提供GEM API的驱动程序的示例是AMD显卡的radeon驱动程序和NVIDIA显卡的 nouveau驱动程序。

DMA Buffer Sharing and PRIME

DMA缓冲区共享API(通常缩写为DMA-BUF)是Linux内核内部API,旨在提供跨多个设备共享 DMA缓冲区的通用机制,可能由不同类型的设备驱动程序管理。例如,Video4Linux设备和图形适配器设备可以通过DMA-BUF共享缓冲区,以实现前者生成并由后者消耗的视频流数据的零复制。任何Linux设备驱动程序都可以作为导出者、用户(消费者)或两者来实现此API。

该功能首次在DRM中用于实现PRIME,这是一种GPU卸载解决方案,使用DMA-BUF在离散GPU 和集成GPU的DRM驱动程序之间共享生成的帧缓冲区。DMA-BUF的一个重要功能是将共享缓冲区作为文件描述符呈现给用户空间。为了开发PRIME,DRM API中添加了两个新的ioctl,一个用于将本地GEM句柄转换为DMA-BUF文件描述符与另一个用于完全相反的操作。

这两个新的ioctl后来被重用作为修复GEM缓冲区共享固有的不安全性的一种方法。与GEM名称不同,文件描述符无法猜测(它们不是全局命名空间),并且Unix操作系统提供了一个使用 SCM_RIGHTS语义通过Unix域套接字传递它们的安全方法。想要与另一个进程共享GEM对象的进程可以将其本地GEM句柄转换为DMA-BUF文件描述符并传递它发送给接收者,接收者又可以从接收到的文件描述符中获取自己的GEM句柄。DRI3使用此方法在客户端和X Server之间共享缓冲区,Wayland也使用此方法。

三、硬件支持

Linux DRM子系统包括免费开源驱动程序,以支持桌面计算机GPU的3个主要制造商(AMD、NVIDIA和Intel)以及越来越多的移动GPU和片上系统 (SoC) 的硬件积分器。每个驱动程序的质量差异很大,这取决于制造商的合作程度和其他因素。

DRM由用户模式图形设备驱动程序使用,例如AMD CatalystMesa 3D。用户空间程序使用 Linux 系统调用接口来访问DRM。DRM通过自己的系统调用增强了Linux系统调用接口。

DRM drivers
DriverSince kernelSupported hardwareVendor supportStatus/Notes
radeon2.4.1AMD (formerly ATi) Radeon GPU series with the architectures TeraScale and GCN 1st & 2nd gen. Including models from R100/200/300/400, Radeon X1000, HD 2000/4000/5000/6000/7000/8000, R5/R7/R9 200/300 series and Kaveri APUs.YesActive
i9152.6.9Intel GMA 830M, 845G, 852GM, 855GM, 865G, 915G, 945G, 965G, G35, G41, G43, G45 chipsets. Intel HD and Iris Graphics HD Graphics 2000/3000/2500/4000/4200/4400/4600/P4600/P4700/5000, Iris Graphics 5100, Iris Pro Graphics 5200 integrated GPUs.YesActive
nouveau2.6.33NVIDIA Tesla, Fermi, Kepler, Maxwell based GeForce GPUs, Tegra K1, X1 SoCPartialActive
exynos3.2Samsung ARM-based Exynos SoCs  
vmwgfx3.2 (from staging)Virtual GPU for the VMware SVGA2 virtual driver
gma5003.3 (from staging)Intel GMA 500 and other Imagination Technologies (PowerVR) based graphics GPUs experimental 2D KMS-only driver
mgag2003.5Matrox MGA-G200 server display engines KMS-only
ast3.5ASpeed Technologies 2000 series experimental
shmobile3.7Renesas SH Mobile  
tegra3.8Nvidia Tegra20, Tegra30 SoCsYesActive
omapdrm3.9Texas Instruments OMAP5 SoCs  
rcar-du3.11Renesas R-Car SoC Display Units  
msm3.12Qualcomm's Adreno A2xx/A3xx/A4xx GPU families (Snapdragon SOCs)[72]  
armada3.13Marvell Armada 510 SoCs  
bochs3.14Virtual VGA cards using the Bochs dispi vga interface (such as QEMU stdvga) virtual driver
sti3.17STMicroelectronics SoC stiH41x series  
rockchip3.19Rockchip SoC-based GPUs KMS-only
imx3.19 (from staging)Freescale i.MX SoCs  
virtio4.2Virtual GPU driver for QEMU based virtual machine managers (like KVM or Xen) virtual driver
amdgpu[57]4.2AMD Radeon GPU series with the architectures GCN 3rd & 4th gen. Including models from Radeon Rx 200/300/400/500[83] series and Carrizo and Bristol & Stoney Ridge APUs.YesActive
vc44.4Raspberry Pi's Broadcom BCM2835 and BCM2836 SoCs (VideoCore IV GPU)  
etnaviv4.5Vivante GPU cores found in several SoCs such as Marvell ARMADA and Freescale i.MX6 Series  
mediatek4.7MediaTek MT8173 SoC (Imagination PowerVR GX6250 GPU)  
kirin4.7HiSilicon Kirin hi6220 SoC (ARM Mali 450-MP4 GPU)  
sun4i4.7Allwinner SoCs (ARM Mali-400 GPU)  
hibmc4.1HiSilicon hi1710 Huawei iBMC SoC (Silicon Image SM750 GPU core[96]) KMS-only
vkms4.19Software-only model of a KMS driver that is useful for testing and for running X (or similar) on headless machines. virtual driver, experimental
panfrost5.2ARM Mali Txxx (Midgard) and Gxx (Bifrost) GPUs  
lima5.2ARM Mali 4xx GPUs  
vboxvideo5.2 (from staging)Virtual GPU driver for VirtualBox (VBoxVGA GPU) virtual driver
hyperv_drm5.14Virtual GPU driver for Hyper-V synthetic video device virtual driver
simpledrm5.14GPU Driver for firmware-provided framebuffers (UEFI GOP, VESA BIOS Extensions, embedded systems)  
ofdrm6.2GPU Driver for Open Firmware framebuffers  
loongson6.6GPU Driver for Loongson GPUs and SoCs  
xe6.8Intel Xe series GPUs (Gen12 integrated GPUs, Intel Arc discrete GPUs)Yesexperimental
powervr6.8Imagination Technologies PowerVR (Series 6 and later) & IMG Graphics GPUs  

参考文档

https://en.wikipedia.org/wiki/Direct_Rendering_Manager

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux DRM(Direct Rendering Manager)提供了一个通用的框架,用于开发LCD驱动程序。下面是Linux DRM LCD驱动框架的一般概述: 1. DRM核心框架Linux内核中的DRM核心框架提供了与图形和显示相关的基本功能,如模式设置、缓冲管理和DMA传输等。它还提供了与用户空间交互的接口,允许应用程序通过DRM设备节点与LCD驱动程序通信。 2. CRTC(Cathode Ray Tube Controller):CRTC是DRM框架中的一个重要组件,负责控制LCD的时序和显示参数。在LCD驱动程序中,您可以通过配置CRTC来设置分辨率、刷新率和时序参数等。 3. Encoder和Connector:Encoder和Connector是DRM框架中用于连接CRTC和显示设备(如LCD)的中间组件。Encoder负责将CRTC中的图像数据编码为特定格式,并将其发送到Connector上。Connector则负责与显示设备进行物理连接,并将编码后的图像数据传输到显示设备。 4. Plane:Plane是DRM框架中用于管理图像平面(如主显示平面、叠加平面等)的组件。它允许您在LCD上叠加多个图像或进行其他图像处理操作。 5. Framebuffer和Gem:Framebuffer是DRM框架中用于管理显存的组件,用于存储和管理显示数据。Gem(Graphics Execution Manager)是DRM框架中的内存管理器,用于分配和管理显存。您可以通过Framebuffer和Gem来设置和操作LCD的显示数据。 6. KMS(Kernel Mode Setting):KMS是DRM框架中的一项功能,它允许用户空间应用程序通过DRM接口来设置和管理显示模式。通过KMS,应用程序可以动态地更改分辨率、刷新率和旋转等显示参数。 以上是Linux DRM LCD驱动框架的一般概述。在编写LCD驱动程序时,您需要理解DRM框架的相关概念和组件,并根据LCD设备的特性和规格来配置和操作这些组件。具体的实现细节可能因您使用的硬件平台和LCD控制器而有所不同。建议您参考Linux内核文档、示例代码和相关社区资源,以获取更具体和针对性的指导。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会写代码的小可爱&&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值