以往我们在Linux 上为显示设备开发驱动时,通常使用的是FrameBuffer 的驱动框架,在Frame-Buffer 驱动框架下,我们能够快速开发出可供简单使用的显示驱动。但是随着芯片显示外设的性能逐渐增强及GPU 的引入,FrameBuffer 框架看起来似乎就有些落伍了,最直接的体现,就是在传统的框架下,对于许多芯片显示外设的新特性如:显示覆盖(菜单层级)、GPU 加速、硬件光标等功能并不能得到很好得支持,并且FrameBuffer 框架将底层的显存通过用户空间/dev/fb 接口,暴露给了用户空间,这很容易导致不同的应用程序在操作显存时,产生访问冲突,而且这种方式看起来似乎不是那么安全。

在这背景下,就需要一个现代的图形显示框架来解决这些问题,那么DRM(Direct Rendering Manager,直接图形管理器)诞生。

框架简述

那么DRM 图形显示框架是怎么解决FrameBuffer 框架遇到的困境呢?DRM 将现代显示领域中会涉及的一些操作进行分层并使这些模块独立,如过上层应用想操作显存、显示效果抑或是GPU,都必须在一些框架的约束下进行,我们可以来了解一下。

我们可以从用户空间、内核空间的两个角度去了解DRM 框架:

用户空间(libdrm driver):

  • Libdrm(DRM 框架在用户空间的Lib)

内核空间(DRM driver):

  • KMS(Kernel Mode Setting,内核显示模式设置)
  • GEM(Graphic Execution Manager,图形执行管理器)

在这里插入图片描述

Libdrm

DRM 框架在用户空间提供的Lib,用户或应用程序在用户空间调用libdrm 提供的库函数,即可访问到显示的资源,并对显示资源进行管理和使用。

这样通过libdrm 对显示资源进行统一访问,libdrm 将命令传递到内核最终由DRM 驱动接管各应用的请求并处理,可以有效避免访问冲突。

KMS(Kernel Mode Setting)

KMS 属于DRM 框架下的一个大模块,主要负责两个功能:显示参数及显示控制。这两个基本功能可以说是显示驱动必须基本的能力,在DRM 框架下,为了将这两部分适配得符合现代显示设备逻辑,又分出了几部分子模块配合框架。

Planes

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

CRTC

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

Encoder

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

Connector

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

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

在这里插入图片描述

参考资料Kernel Mode Setting (KMS) .

GEM(generic DRM memory-management)

顾名思义,GEM 负责对DRM 使用的内存(如显存)进行管理。

GEM 框架提供的功能包括:

  • 内存分配和释放
  • 命令执行
  • 执行命令时的管理

参考资料The Graphics Execution Manager (GEM) .

驱动简述

我们通过简单讲解了DRM 驱动的框架,简单地带领大家认识了DRM 框架下对显示功能的实现方法。实际的代码细节远比上述给大家介绍的内容复杂得多,给大家讲解框架组件功能只是起到一个抛砖引玉的作用,如果对代码细节感兴趣的同学,可以在目录drivers/gpu/drm 中,查看具体的驱动实现。

在实际的使用中,我这里将DRM 的驱动实现分为了两个部分,主机驱动和设备驱动。主机驱动指的是片上的负责显示功能的外设驱动,如MP157 上的LTDC 外设、DSI 外设的驱动,这些一般由芯片厂商如ST、NXP 等来负责实现,完成一个DRM-Host,主机驱动代码一般位于drivers/gpu/drm/xxx/ 目录下,这里xxx 代指芯片厂商如ST、NXP。

一般我们要做的,就是现实一个设备驱动,比如针对某款LCD 显示屏,将其参数(LCD Timing、Size⋯)、显示方式(DSI、HDMI⋯)等通过设备驱动,和主机驱动关联起来。

在内核的DRM 驱动目录中,给出了许多设备驱动的示例,详见目录:drivers/gpu/drm/panel.

一个简单的DRM 框架显示设备驱动例子可以参考drivers/gpu/drm/panel/panel-simple.c .

设备树插件描述

MP157 开发板支持两种接口的LCD 显示屏:RGB(MIPI DPI)、MIPI DSI,由于显示原理不同,所以分别对应着两款设备树插件,使用的也是不同的设备驱动,但是其驱动框架仍是DRM。


重要: 两种接口RGB、DSI 屏幕不可同时使用!


既然作为显示设备,那么设备树上描述的属性必然是和显示屏相关的内容,比如:屏幕的displaytiming、ddc 读取显示器EDID 结构等,详见及参考内核Documentation/devicetree/bindings/display/panel 目录下的各种dt-bingdings,如:

  • 通用的bingdings 文档:
    Documentation/devicetree/bindings/display/panel/panel-common.txt
    Documentation/devicetree/bindings/display/panel/panel-dpi.txt
    Documentation/devicetree/bindings/display/panel/panel-dsi-cm.txt
  • 各种实例显示设备的bingdings 文档。

各种的bingdings 文档,可以帮助你快速了解一个设备树里描述的属性内容,及如何去写一个对应的设备树节点。

这里再补充一些知识,MIPI 联盟提出了许多显示规范,有:DBI(Display Bus Interface 带控制器及显存的并行接口显示模块)、DPI(Display Pixel Interface 不带控制器或显存的并行接口显示模块)、DSI(Display Serial Interface 高速串行接口)、DCS(Display Command Set 用显示命令集操作的显示模块)。我们常说的RGB 接口屏幕,也就是不带控制器及显存的并行接口屏幕,DBI 的代表则有80 接口MCU 屏,至于DSI 则是串行接口屏幕了,手机中常用的就是DSI 接口屏幕。

参考资料:MIPI Specifications .

RGB-LCD 设备树插件

设备树插件的源码非常长,此处不便展示,源码位于如下目录:

arch/arm/boot/dts/overlays/stm-fire-lcd-overlay.dts

其驱动目录对应为:

drivers/gpu/drm/panel/panel-simple.c

DSI-LCD 设备树插件

源码位于如下目录:

arch/arm/boot/dts/overlays/stm-fire-mipi-overlay.dts

其驱动目录对应为:

drivers/gpu/drm/panel/panel-himax-hx8394.c

实验准备

下面我们就以野火的4.3 寸RGB 屏幕为例,为大家测试在DRM 驱动框架下的驱动效果,我们进行屏幕的测试。

添加设备树插件

方法参考如下:

在这里插入图片描述


重要: 如果使用DSI 屏幕,请将stm-fire-lcd.dtbo 及stm-fire-mipi.dtbo 设备树插件同时打开。若使用RGB 屏幕,则只将stm-fire-lcd.dtbo 打开。


以野火的4.3 寸RGB 屏幕为例,操作如下:

在这里插入图片描述

如需调整屏幕参数,请使用fire-config 工具调整,参考:

《fire-config 修改液晶参数》

如若运行代码时出现“Device or resource busy”或者运行代码卡死等等现象,请按上述情况检查并按上述步骤操作。

如出现Permission denied 或类似字样,请注意用户权限,大部分操作硬件外设的功能,几乎都需要root 用户权限,简单的解决方案是在执行语句前加入sudo 或以root 用户运行程序。

libdrm 测试程序

野火MP157 开发板中,DRM 驱动默认被配置为编译进内核,驱动加载后,会在/dev/dri/ 目录下创建显示设备的节点,我们的应用程序就是通过open 这些节点,调用libdrm 的API 去进行LCD 控制和显示的。

如下图,我们的RGB 显示屏即为/dev/dri/card0 节点.

在这里插入图片描述

编写一个libdrm 的测试程序较为复杂,这里我们使用libdrm 官方的测试工具来进行测试,我们可以在这里下载源码并进行交叉编译出测试工具,以供在开发板上使用:libdrm .

新版的libdrm 使用meson+ninja 的构建方式,而不是老版的autotools,没有基础的同学构建新版libdrm 会比较痛苦。建议直接使用我们给大家编译好的测试程序,测试程序位于\linux_driver\framework_drm\modetest。

对libdrm 测试程序感兴趣的同学,可以下载libdrm 源码解压,在其目录/libdrm-2.4.105/tests/下,查看modetest.c 文件,此为测试程序源码。

实验操作

将上述的modetest 测试程序上传至开发板中,并使用chmod 添加执行权限。

运行modetest 程序,点击图片可放大:

在这里插入图片描述

待程序检测执行完毕,会列举出开发板上的DRM 框架下的显示设备。

其中一些字样如Encoders、Connectors、CRTCs 经过前面的介绍,大家都应该有了一点印象。

DRM 框架下的显示过程如下图示:

在这里插入图片描述

我们要做的,就是找出前面终端中打印的RGB 屏幕对应的connectors、CRTCs 的ID:

在这里插入图片描述

图中状态为connected 的connectorsID 为31,并且从name 中可以看出为DPI 接口的屏幕,正是我们的RGB 屏。CRTCs 对象中,唯一个id 为34 的CRTC,参数也为我们的RGB 屏幕参数。

则我们可以执行如下命令进行测试:

./modetest -M stm -s 31@34:480x272

其中31、34 分别为我们屏幕的Connectors ID、CRTCs ID,实验现象如图示:

在这里插入图片描述

在终端中按下回车键退出测试。

关于测试的更多相关内容,可以参考linux DRM/KMS 测试工具modetest .

文章末尾补充小知识,虽说DRM 功能符合现代显示设备的需求,但是仍有众多的老设备及软件需要Framebuffer 的支持。所以在DRM 框架下,有部分代码用于实现在DRM 框架下,去模拟FB设备。

在ST 提供的显示驱动代码中,也有模拟FB 设备的相关代码,参见drivers/gpu/drm/stm/drv.c 文件,最终效果就是设备目录下,出现熟悉的身影/dev/fb0 。

我们可以通过传统测试FrameBuffer 设备的方式,使用如下命令来测试它:

# 会得到类似花屏的效果
cat /dev/random > /dev/fb0

相关新闻可以查看Generic-FBDEV-Emulation .


参考资料:嵌入式Linux 驱动开发实战指南-基于STM32MP1 系列

Logo

更多推荐