LInux DRM Developer Guide && Linux GPU Driver Developer’s Guide

LInux DRM Developer Guide

http://www.landley.net/kdocs/htmldocs/drm.html#drmIntroduction

Linux GPU Driver Developer’s Guide

https://www.kernel.org/doc/html/latest/gpu/index.html

 

 

/

 

 

总览

该框架以基于Wayland的Windowing system为例,描述了linux graphic系统在DRI框架下,通过两条路径(DRM和KMS),分别实现Rendering和送显两个显示步骤。

1)Application(如3D game)根据用户动作,需要重绘界面,此时它会通过OpenGL|ES、EGL等接口,将一系列的绘图请求,提交给GPU。

a)OpenGL|ES、EGL的实现,可以有多种形式,这里以Mesa 3D为例,所有的3D rendering请求,都会经过该软件库,它会根据实际情况,通过硬件或者软件的方式,响应Application的rendering请求。

b)当系统存在基于DRI的硬件rendering机制时,Mesa 3D会通过libGL-meas-DRI,调用DRI提供的rendering功能。

c)libGL-meas-DRI会调用libdrm,libdrm会通过ioctl调用kernel态的DRI驱动,这里称作DRM(Direct Rendering Module)。

d)kernel的DRM模块,最终通过GPU完成rendering动作。

2)GPU绘制完成后,将rendering的结果返回给Application。

rendering的结果是以image buffer的形式返回给应用程序。

3)Application将这些绘制完成的图像buffer(可能不止一个)送给Wayland compositor,Wayland compositor会控制硬件,将buffer显示到屏幕上。

Wayland compositor会搜集系统Applications送来的所有image buffers,并处理buffer在屏幕上的坐标、叠加方式后,直接通过ioctl,交给kernel KMS(kernel mode setting)模块,该模块会控制显示控制器将图像显示到具体的显示设备上。

DRM和KMS

DRM是Direct Rendering Module的缩写,是DRI框架在kernel中的实现,负责管理GPU(或显卡,graphics card)及相应的graphics memory,主要功能有二:

1)统一管理、调度多个应用程序向显卡发送的命令请求,可以类比为管理CPU资源的进程管理(process management)模块。

2)统一管理显示有关的memory(memory可以是GPU专用的,也可以是system ram划给GPU的,后一种方法在嵌入式系统比较常用),该功能由GEM(Graphics Execution Manager)模块实现,主要包括:

a) 允许用户空间程序创建、管理、销毁video memory对象(称作“"GEM objects”,以handle为句柄)。

b)允许不同用户空间程序共享同一个"GEM objects”(需要将不唯一的handle转换为同一个driver唯一的GEM name,后续使用dma buf)。

c)处理CPU和GPU之间内存一致性的问题。

d)video memory都在kernel管理,便于给到display controller进行送显(Application只需要把句柄通过Wayland Compositor递给kernel即可,kernel会自行获取memory及其内容)。

KMS是Kernel Mode Setting的缩写,也称作Atomic KMS,它是一个在linux 4.2版本的kernel上,才最终定性的技术。从字面意义上理解,它要实现的功能比较简单,即:显示模式(display mode)的设置,包括屏幕分辨率(resolution)、颜色深的(color depth)、屏幕刷新率(refresh rate)等等。一般来说,是通过控制display controller的来实现上述功能的。

目前的kernel版本(如4.2之后),KMS和DRM基本上没有什么逻辑耦合(除了代码位于相同目录,以及通过相同的设备节点提供ioctl之外),可以当做独立模块看待

http://www.wowotech.net/linux_kenrel/dri_overview.html

 

 

DRM层为图形驱动程序提供了多种服务,其中许多服务由libdrm提供应用程序接口,libdrm是包装大多数DRM ioctl的库,包括vblank事件处理,内存管理,输出管理,帧缓冲管理,命令提交和防护,睡眠/唤醒支持和DMA服务。

现代Linux系统需要大量图形内存来存储帧缓冲区,纹理,顶点和其他与图形相关的数据。鉴于许多数据非常动态,有效管理图形内存这件事对于图形堆栈至关重要,在DRM基础架构中发挥核心作用。

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

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

 

图形执行管理器(GEM)

GEM设计方法导致内存管理器无法完全覆盖其用户空间或内核API中的所有(甚至所有常见)用例。 GEM向用户空间公开了一组与标准内存相关的操作,并向驱动程序公开了一组辅助函数,另外让驱动程序使用自己的私有API实现特定于硬件的操作。

GEM --LWN上的图形执行管理器文章中描述了GEM用户空间API。虽然略微过时,但该文档提供了GEM API原则的良好概述。作为通用GEM API的一部分描述的缓冲区分配和读写操作,目前最新的实现使用特定于驱动程序的ioctl。

GEM与数据无关。它管理抽象缓冲区对象,而不知道单个缓冲区包含什么。因此,需要了解缓冲区内容或目的的API(例如缓冲区分配或同步原语)不属于GEM的范围,必须使用特定于驱动程序的ioctl来实现。

从根本上讲,GEM涉及多项任务:

    内存分配和释放
    命令执行
    命令执行时的间隙管理[Aperture management at command execution time]

缓冲区对象分配相对简单,主要由Linux的shmem层提供,提供内存来支持每个对象。
特定于设备的操作(例如命令执行,固定,缓冲区读写,映射和域所有权传输)留给特定于驱动程序的ioctl。

GEM初始化

使用GEM的驱动程序必须在结构体 struct drm_driver driver_features字段中设置DRIVER_GEM位。然后,DRM core将在调用load操作之前自动初始化GEM core。在这之后,这将创建一个DRM内存管理器对象,该对象为对象分配提供地址空间池。

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

KMS初始化

驱动程序必须通过调用DRM设备上的drm_mode_config_init()来初始化模式设置核心。该函数初始化struct drm_device mode_config字段,永远不会失败。完成后,必须通过初始化以下字段来设置模式配置。

  • int min_width, min_height; int max_width, max_height; Minimum and maximum width and height of the frame buffers in pixel units.
  • struct drm_mode_config_funcs *funcs; Mode setting functions.

KMS Display Pipeline Overview

KMS向用户空间呈现的基本对象结构非常简单。帧缓冲区(由struct drm_framebuffer表示,请参阅帧缓冲区抽象)提供给平面。平面由struct drm_plane表示,有关详细信息,请参见Plane Abstraction。一个或多个(甚至没有)平面将其像素数据馈送到CRTC(由struct drm_crtc表示,参见CRTC抽象)以进行混合。平面组成属性和相关章节中更详细地解释了混合步骤。

对于输出路由,第一步是编码器(由struct drm_encoder表示,参见编码器抽象)。这些只是用于实现KMS驱动程序的辅助库的内部工件。除此之外,它们使用户空间变得更加复杂,无法确定CRTC和连接器之间的连接是什么,以及支持哪种克隆,它们在用户空间API中没有用处。不幸的是编码器已暴露给用户空间,因此目前无法删除它们。此外,暴露的限制通常由驱动工程师错误地设定,并且在许多情况下不足以表达真正的限制。 CRTC可以连接到多个编码器,对于有源CRTC,必须至少有一个编码器。

显示链中的最终和实际端点是连接器(由struct drm_connector表示,请参阅Connector Abstraction)。连接器可以有不同的编码器,但内核驱动程序选择用于每个连接器的编码器。用例是DVI,可以在模拟和数字编码器之间切换。编码器还可以驱动多个不同的连接器。每个活动编码器只有一个活动连接器。

在内部,输出管道有点复杂,并且更紧密地匹配如今的硬件:

KMS Output Pipeline

CRTC抽象

CRTC代表整个显示管道。它从drm_plane接收像素数据并将它们混合在一起。 drm_display_mode也附加到CRTC,指定显示时间。在输出端,数据被馈送到一个或多个drm_encoder,然后每个drm_encoder连接到一个drm_connector。

要创建CRTC,KMS驱动程序会分配和清零struct drm_crtc的实例(可能作为更大结构的一部分),并通过调用drm_crtc_init_with_planes()来注册它。

CRTC也是传统模式集操作的入口点,请参阅drm_crtc_funcs.set_config,传统平面操作,请参阅drm_crtc_funcs.page_flip和drm_crtc_funcs.cursor_set2以及其他遗留操作,如drm_crtc_funcs.gamma_set。对于原子驱动程序,所有这些功能都通过drm_property和drm_mode_config_funcs.atomic_check和drm_mode_config_funcs.atomic_check来控制。

 

帧缓冲抽象[framebuffer]

帧缓冲区抽象存储器对象,提供扫描到CRTC的像素源。应用程序通过DRM_IOCTL_MODE_ADDFB(2)ioctls显式请求创建帧缓冲区,并返回一个可以传递给KMS CRTC控件,平面配置和页面翻转函数的不透明句柄fd。

帧缓冲区依赖底层内存管理器来分配后备存储。创建帧缓冲区时,应用程序通过struct drm_mode_fb_cmd2参数传递内存句柄(或多平面格式的内存句柄列表)。对于使用GEM作为其用户空间缓冲区管理界面的驱动程序,这将是一个GEM句柄。然而,驱动程序可以自由地使用它们自己的后备存储对象句柄。 vmwgfx直接向用户空间公开特殊的TTM句柄,因此需要创建ioctl而不是GEM句柄中的TTM句柄。

使用struct drm_framebuffer跟踪帧缓冲区。它们使用drm_framebuffer_init()发布 - 在调用该函数后,用户空间可以使用并访问framebuffer对象。辅助函数drm_helper_mode_fill_fb_struct()可用于预填充所需的元数据字段。

drm帧缓冲区的生命周期由引用计数控制,驱动程序可以使用drm_framebuffer_get()获取其他引用,并使用drm_framebuffer_put()再次删除它们。对于永远不会丢弃最后一个引用的驱动程序专用帧缓冲区(例如,当struct struct drm_framebuffer嵌入到fbdev辅助结构中时,对于fbdev帧缓冲区)驱动程序可以使用drm_framebuffer_unregister_private()在模块卸载时手动清理帧缓冲区。但不建议这样做,最好有一个普通的独立式结构drm_framebuffer。

note:所谓 “dumb buffer”,就是该 buffer 的操作不依赖于硬件或相关加速器,多用于简单 buffer 的应用场合,适合做 framebuffer 使用。它的目的是降低应用程序操作 DRM 的复杂度,比如 Plymouth 这种应用程序,本身使用场景就比较简单,无需使用 driver 所提供的特定 API 来完成 buffer 的操作。

平面抽象[plane]

Plane是连接FB与CRTC的纽带,是内存的搬运工。

平面表示在扫描输出过程期间可以与CRTC混合或重叠在CRTC顶部的图像源。 Planes从drm_framebuffer对象获取输入数据。平面本身指定该图像的裁剪和缩放,它放置在显示管道的可见区域则由drm_crtc表示。平面还可以具有指定像素如何定位和混合的附加属性,例如旋转或Z位置。所有这些属性都存储在drm_plane_state中。

要创建一个平面,KMS驱动程序会分配和清零struct drm_plane的实例(可能作为更大结构的一部分),并通过调用drm_universal_plane_init()来注册它。

光标和叠加平面是可选的。所有驱动程序应为每个CRTC提供一个主要平面。有关这些特殊的与uapi相关的平面类型的更深入讨论,请参见enum drm_plane_type。通过调用drm_crtc_init_with_planes()将特殊平面与其CRTC相关联。

平面的类型在不可变“类型”枚举属性中公开,该属性具有以下值之一:“Overlay”,“Primary”,“Cursor”

---------------------

一个高级的Plane,通常具有如下功能:

功能    说明
Crop    裁剪,如上图
Scaling    缩放,放大或缩小
Rotation    旋转,90° 180° 270° X/Y镜像
Z-Order    Z-顺序,调整当前层在总图层中的Z轴顺序
Blending    合成,pixel alpha / global alpha
Format    颜色格式,ARGB888 XRGB888 YUV420 等
再次强调,以上这些功能都是由硬件直接完成的,而非软件实现。

在DRM框架中,Plane又分为如下3种类型:

类型    说明
Cursor    光标图层,一般用于PC系统,用于显示鼠标
Overlay    叠加图层,通常用于YUV格式的视频图层
Primary    主要图层,通常用于仅支持RGB格式的简单图层
---------------------
作者:何小龙
来源:CSDN
原文:https://blog.csdn.net/hexiaolong2009/article/details/84934294
版权声明:本文为博主原创文章,转载请附上博文链接!

连接器抽象Connector Abstraction

在DRM中,连接器是显示接收器的一般抽象,包括固定面板或可以某种形式显示像素的任何其他东西。与代表硬件的所有其他KMS对象(如CRTC,encodeer或平plane​抽象)相反,连接器可以在运行时进行热插拔。因此,使用drm_connector_get()和drm_connector_put()对它们进行引用计数。

KMS驱动程序必须在struct drm_connector中为每个这样的接收器进行创建,初始化,注册和添加操作。该实例作为其他KMS对象创建,并通过设置以下字段进行初始化。通过调用drm_connector_init()并使用指向struct drm_connector_funcs和connector类型的指针初始化连接器,然后通过调用drm_connector_register()将其暴露给用户空间。

connector必须连接到要使用的encoder。对于将connector映射到encoder的1对1的设备,应在初始化时通过调用drm_connector_attach_encoder()连接connector。驱动程序还必须将drm_connector.encoder字段设置为指向附加的encoder。

对于未固定的连接器(如内置面板),驱动程序需要支持热插拔通知。最简单的方法是使用探针助手,请参阅drm_kms_helper_poll_init(),了解没有热插拔中断硬件支持的连接器。具有硬件热插拔支持的连接器可以改为使用例如drm_helper_hpd_irq_event()。

编码器抽象Encoder Abstraction

编码器表示CRTC(作为整体像素管道,由struct drm_crtc表示)和连接器(作为通用接收器实体,由struct drm_connector表示)之间的连接元素。编码器从CRTC获取像素数据并将其转换为适合任何连接的连接器的格式。编码器是暴露给用户空间的对象,最初允许用户空间推断克隆和对连接器/ CRTC的限制[不对]。不幸的是,几乎所有的驱动都错了,使得uabi几乎没用。最重要的是,暴露的限制对于今天的硬件来说太简单了,推测限制的推荐方法是使用原子IOCTL的DRM_MODE_ATOMIC_TEST_ONLY标志。

否则编码器根本不在uapi中使用(来自用户空间的任何模式集请求直接将连接器与CRTC连接),因此驱动程序可以随意使用它们。 Modeset辅助库大量地使用编码器来促进代码共享。但是对于更复杂的设置,通常最好将共享代码移动到单独的drm_bridge中。与编码器相比,桥接器还具有纯粹的内部抽象的优点,因为它们根本不暴露于用户空间。

使用drm_encoder_init()初始化编码器,并使用drm_encoder_cleanup()进行清理。

 

 

//

preview

 

https://www.zhihu.com/question/20722310/answer/50344183

https://01.org/linuxgraphics

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值