一、DRM简介
DRM,全称Direct Rending Manger。是目前Linux主流的图形显示框架。相比较传统的Framebuffer,DRM更能适应现代硬件。支持GPU、3D渲染显示等。DRM可以统一管理GPU、Display驱动,使得软件架构更统一、方便开发和维护。本文只介绍Display相关内容,GPU相关的,博主也不懂,无能为力,等以后学到相关的再来更新。
从模块上划分,DRM可以分为三个部分:libdrm、KMS、GEM。l
图1 DRM框架
1. lbdrm是DRM框架提供的、位于用户空间、操作DRM的库。应用程序调用内核里面的KMS和GEM,访问显示相关的资源。
2. KMS(Kernel Mode Setting)
KMS是DRM框架的一个大模块,主要功能是:显示参数及显示控制。
3. GEM(Graphics Execution Manager)
GEM负责DRM下的内存管理和释放。
本文只涉及KMS和GEM相关部分。使用的开发板是stm32mp157(正点原子)进行测试。系统是ubuntu18.04。
二、KMS
如图1,可以看到KMS主要负责显示相关功能。在DRM中,将其进行抽象,包括:
Framebuffer、CRTC,ENCODER,CONNECTOR,PLANE,VBLANK,property。他们之间的关系如图2
图2 KMS之间模块之间的功能关系
1、Framebuffer:
单个图层的显示内容,唯一一个与硬件无关的基本元素。
2、CRTC:
从framebuffer中读取待显示的图像,并按照响应的格式输出给encoder。其承担的主要作用为:
1).配置适合显示器的分辨率,并输出响应的时序。
2).扫描framebuffer送到一个或多个显示器
3).更新framebuffer
概括下就是,对显示器进行扫描,产生时序信号的模块、负责帧切换、电源控制、色彩调整等等。
3、Plane:
图层,实际输出的图像是多个图层叠加而成的,比如主图层、光标图层。其中有些图层由硬件加速模块生成,每个crtc至少一个plane。plane一共有三种,分别是:DRM_PLANE_TYPE_PRIMARY、DRM_PLANE_TYPE_OVERLAY、DRM_PLANE_TYPE_CURSOR。这是配置plane的三个枚举,标注主图层、覆盖图层、光标图层(自己翻译的,跟标准翻译可能有出入)。
4、Encoder:
将一定格式的图像信号(如RGB、YUV等)编码成connector需要的输出信号。以HDMI为例,数据都是通过TMDS data的串行总线输出,编码的任务就是encoder的任务。
5、Connector:
连接显示器的物理接口,负责硬件设备的接入、屏参获取等,如DP、HDMI等。
6、Vblank:
软、硬件同步机制,RGB时序中垂直消影区,软件通常使用硬件VSYNC实现。
7、Property:
任何想设置的参数都可以做成property,是DRM驱动中最灵活的部分
以HDMI接口为例说明:Soc内部一般包含一个Display模块,通过总线连接到HDMI接口上。则Display模块对应CRTC、HDMI接口对应Connector,Framebuffer对应的是显存部分。Plane是对Framebuffer进行描述的部分。Encoder是将像素转化为HDMI接口所需要的信号。一般Encoder和Connector放到一块初始化。
三、GEM
主要负责显示buffer的分配和释放,包括dumb、prime、fence
1、Dumb:
只支持连续物理内存,基于kernel中通用CMA API实现,多用于小分辨率简单场景。主要负责一些简单的buffer显示,可以直接使用CPU渲染,GPU不会使用dumb。
2、Prime:
连续、非连续物理内存都支持,基于DMA-BUF机制,可以实现buffer共享,多用于大内存复杂场景。
3、Fence:
buffer同步机制,基于内核dma_fence机制实现,用于防止显示内容出现异步问题。
四、部分代码介绍
st公司已经写好了DRM框架代码,位于路径:drivers\gpu\drm\stm。本次进行测试的时候,将该目录下的代码删除,参考厂家的代码重新写。设备树部分,修改compatible等代码,适合本次测试代码,仅作为学习使用。
1、struct drm_driver结构体
struct drm_driver是DRM框架的核心结构体。
图3 struct drm_driver结构体
如图3,driver_features描述的是DRM支持的相关操作。
1)、DRIVER_MODESET:表示支持modesetting 操作
2)、DRIVER_GEM:表示支持GEM 操作,用于操作对内存的分配、释放
3)、DRIVER_ATOMIC:支持 Atomic 操作,用于操作各种属性
dumb_create成员是创建dumb内存。本例中对其进行重写,其他的回调函数,使用cma api。
2、probe函数
在probe函数中,申请struct drm_device *ddev=drm_dev_alloc(&drv_driver, dev)结构体,在里面传入struct drm_driver结构体、配置KMS、注册DRM。部分代码如下:
//配置KMS信息,
static int my_modeset_init(struct drm_device *ddev)
{
struct platform_device *pdev = to_platform_device(ddev->dev);
struct ltdc_device *ldev;
int ret;
DRM_DEBUG("%s\n", __func__);
ldev = devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL);
if (!ldev)
return -ENOMEM;
ddev->dev_private = (void *)ldev;
drm_mode_config_init(ddev); //初始化drm_device
/*
* 设置最大/小宽、高值
* 绑定framebuffer结构体,drm可以模拟fb
*/
ddev->mode_config.min_width = 0;
ddev->mode_config.min_height = 0;
ddev->mode_config.max_width = MY_MAX_FB_WIDTH;
ddev->mode_config.max_height = MY_MAX_FB_HEIGHT;
ddev->mode_config.funcs = &drv_mode_config_funcs; //设置framebuffer的回调函数结构体
ret = ltdc_load(ddev); //初始化ltdc接口,包括初始化connector和encoder一起初始化。
//connector初始化的时候会调用drm_panel结构体离的获取屏幕参数函数
if (ret)
goto err;
drm_mode_config_reset(ddev);
drm_kms_helper_poll_init(ddev);
platform_set_drvdata(pdev, ddev);
return 0;
err:
drm_mode_config_cleanup(ddev);
return ret;
}
//驱动的probe函数
static int my_drm_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct drm_device *ddev;
int ret;
DRM_DEBUG("%s\n", __func__);
dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); //设置DMA
ddev = drm_dev_alloc(&drv_driver, dev); //分配一个drm_device结构体
if (IS_ERR(ddev))
return PTR_ERR(ddev);
ret = my_modeset_init(ddev); //初始化KMS
if (ret)
goto err_put;
ret = drm_dev_register(ddev, 0); //注册drm
if (ret)
goto err_put;
drm_fbdev_generic_setup(ddev, 16);
return 0;
err_put:
drm_dev_put(ddev);
return ret;
}
ddev->mode_config.funcs = &drv_mode_config_funcs; //设置framebuffer的回调函数结构体
这个部分,是DRM用于模拟Framebuffer框架的代码,结构体如下:
static const struct drm_mode_config_funcs drv_mode_config_funcs = {
.fb_create = drm_gem_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
ret = ltdc_load(ddev);代码中,对KMS中的相关功能进行完善,包括connector、encoder、plane、crtc等。代码太多了,具体的可以看后面连接放的代码。这里简单的说下大致内容。
KMS中的基本元素(CRTC,ENCODER,CONNECTOR,PLANE,Framebuffer已经在GEM中实现)均需要在DRM驱动中实现,没有硬件对应时,需要模拟出来。在DRM中,每一个部分都是使用一个结构体进行描述,需要使用对应的函数进行初始化。
注:各个Soc厂家的DRM部分设计的很多都比(互)较(相)相(抄)似(袭)。DRM框架将它们共同的代码使用xxx_funcs描述、xxx_init进行初始化,不同的部分使用xxx_helper_funcs描述、
drm_xxx_helper_add()添加。
- xxx_funcs 必须有,xxx_helper_funcs 可以没有。
- drm_xxx_init() 必须有,drm_xxx_helper_add() 可以没有。
- 只有当 xxx_funcs 采用 DRM 标准的 helper 函数实现时,才有可能 需要定义 xxx_helper_funcs 接口。
- xxx_funcs.destroy() 接口必须实现
例如:
static int ltdc_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state)
{
struct drm_framebuffer *fb = state->fb;
struct drm_crtc_state *crtc_state;
struct drm_rect *src = &state->src;
struct drm_rect *dst = &state->dst;
DRM_DEBUG_DRIVER("\n");
if (!fb)
return 0;
/* convert src from 16:16 format */
src->x1 = state->src_x >> 16;
src->y1 = state->src_y >> 16;
src->x2 = (state->src_w >> 16) + src->x1 - 1;
src->y2 = (state->src_h >> 16) + src->y1 - 1;
dst->x1 = state->crtc_x;
dst->y1 = state->crtc_y;
dst->x2 = state->crtc_w + dst->x1 - 1;
dst->y2 = state->crtc_h + dst->y1 - 1;
DRM_DEBUG_DRIVER("plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
plane->base.id, fb->base.id,
src->x2 - src->x1 + 1, src->y2 - src->y1 + 1,
src->x1, src->y1,
dst->x2 - dst->x1 + 1, dst->y2 - dst->y1 + 1,
dst->x1, dst->y1);
crtc_state = drm_atomic_get_existing_crtc_state(state->state,
state->crtc);
/* destination coordinates do not have to exceed display sizes */
if (crtc_state && (crtc_state->mode.hdisplay <= dst->x2 ||
crtc_state->mode.vdisplay <= dst->y2))
return -EINVAL;
/* source sizes do not have to exceed destination sizes */
if (dst->x2 - dst->x1 < src->x2 - src->x1 ||
dst->y2 - dst->y1 < src->y2 - src->y1)
return -EINVAL;
return 0;
}
static void ltdc_plane_atomic_update(struct drm_plane *plane,
struct drm_plane_state *oldstate)
{
struct ltdc_device *ldev = plane_to_ltdc(plane);
struct drm_plane_state *state = plane->state;
struct drm_rect *src = &state->src;
struct drm_rect *dst = &state->dst;
struct drm_framebuffer *fb = state->fb;
u32 lofs = plane->index * LAY_OFS;
u32 val, pitch_in_bytes, line_length, paddr, ahbp, avbp, bpcr;
enum ltdc_pix_fmt pf;
struct drm_rect dr;
if (!state->crtc || !fb) {
DRM_DEBUG_DRIVER("fb or crtc NULL");
return;
}
/* compute final coordinates of frame buffer */
dr.x1 = src->x1 + dst->x1;
dr.y1 = src->y1 + dst->y1;
dr.x2 = src->x2 + dst->x1;
dr.y2 = src->y2 + dst->y1;
bpcr = my_reg_read(ldev->regs, LTDC_BPCR);
ahbp = (bpcr & BPCR_AHBP) >> 16;
avbp = bpcr & BPCR_AVBP;
/* Configures the horizontal start and stop position */
val = ((dr.x2 + 1 + ahbp) << 16) + (dr.x1 + 1 + ahbp);
my_reg_update_bits(ldev->regs, LTDC_L1WHPCR + lofs,
LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
/* Configures the vertical start and stop position */
val = ((dr.y2 + 1 + avbp) << 16) + (dr.y1 + 1 + avbp);
my_reg_update_bits(ldev->regs, LTDC_L1WVPCR + lofs,
LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
/* Specifies the pixel format */
pf = to_ltdc_pixelformat(fb->format->format);
for (val = 0; val < NB_PF; val++)
if (ldev->caps.pix_fmt_hw[val] == pf)
break;
if (val == NB_PF) {
DRM_ERROR("Pixel format %.4s not supported\n",
(char *)&fb->format->format);
val = 0; /* set by default ARGB 32 bits */
}
my_reg_update_bits(ldev->regs, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
/* Configures the color frame buffer pitch in bytes & line length */
pitch_in_bytes = fb->pitches[0];
line_length = fb->format->cpp[0] * (dr.x2 - dr.x1 + 1) +
(ldev->caps.bus_width >> 3) - 1;
val = ((pitch_in_bytes << 16) | line_length);
my_reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs,
LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
/* Specifies the constant alpha value */
val = CONSTA_MAX;
my_reg_update_bits(ldev->regs, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
/* Specifies the blending factors */
val = BF1_PAXCA | BF2_1PAXCA;
if (!fb->format->has_alpha)
val = BF1_CA | BF2_1CA;
/* Manage hw-specific capabilities */
if (ldev->caps.non_alpha_only_l1 &&
plane->type != DRM_PLANE_TYPE_PRIMARY)
val = BF1_PAXCA | BF2_1PAXCA;
my_reg_update_bits(ldev->regs, LTDC_L1BFCR + lofs,
LXBFCR_BF2 | LXBFCR_BF1, val);
/* Configures the frame buffer line number */
val = dr.y2 - dr.y1 + 1;
my_reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, val);
/* Sets the FB address */
paddr = (u32)drm_fb_cma_get_gem_addr(fb, state, 0);
DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
my_reg_write(ldev->regs, LTDC_L1CFBAR + lofs, paddr);
/* Enable layer and CLUT if needed */
val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
val |= LXCR_LEN;
my_reg_update_bits(ldev->regs, LTDC_L1CR + lofs,
LXCR_LEN | LXCR_CLUTEN, val);
ldev->plane_fpsi[plane->index].counter++;
mutex_lock(&ldev->err_lock);
if (ldev->error_status & ISR_FUIF) {
DRM_WARN("ltdc fifo underrun: please verify display mode\n");
ldev->error_status &= ~ISR_FUIF;
}
if (ldev->error_status & ISR_TERRIF) {
DRM_WARN("ltdc transfer error\n");
ldev->error_status &= ~ISR_TERRIF;
}
mutex_unlock(&ldev->err_lock);
}
static void ltdc_plane_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *oldstate)
{
struct ltdc_device *ldev = plane_to_ltdc(plane);
u32 lofs = plane->index * LAY_OFS;
/* disable layer */
my_reg_clear(ldev->regs, LTDC_L1CR + lofs, LXCR_LEN);
DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
oldstate->crtc->base.id, plane->base.id);
}
static void ltdc_plane_atomic_print_state(struct drm_printer *p,
const struct drm_plane_state *state)
{
struct drm_plane *plane = state->plane;
struct ltdc_device *ldev = plane_to_ltdc(plane);
struct fps_info *fpsi = &ldev->plane_fpsi[plane->index];
int ms_since_last;
ktime_t now;
now = ktime_get();
ms_since_last = ktime_to_ms(ktime_sub(now, fpsi->last_timestamp));
drm_printf(p, "\tuser_updates=%dfps\n",
DIV_ROUND_CLOSEST(fpsi->counter * 1000, ms_since_last));
fpsi->last_timestamp = now;
fpsi->counter = 0;
}
static bool ltdc_plane_format_mod_supported(struct drm_plane *plane,
u32 format,
u64 modifier)
{
if (modifier == DRM_FORMAT_MOD_LINEAR)
return true;
return false;
}
static const struct drm_plane_funcs ltdc_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.atomic_print_state = ltdc_plane_atomic_print_state,
.format_mod_supported = ltdc_plane_format_mod_supported,
};
static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
.prepare_fb = drm_gem_fb_prepare_fb,
.atomic_check = ltdc_plane_atomic_check,
.atomic_update = ltdc_plane_atomic_update,
.atomic_disable = ltdc_plane_atomic_disable,
};
//创建图层
static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
enum drm_plane_type type)
{
unsigned long possible_crtcs = CRTC_MASK;
struct ltdc_device *ldev = ddev->dev_private;
struct device *dev = ddev->dev;
struct drm_plane *plane;
unsigned int i, nb_fmt = 0;
u32 formats[NB_PF * 2];
u32 drm_fmt, drm_fmt_no_alpha;
const u64 *modifiers = ltdc_format_modifiers;
int ret;
/* Get supported pixel formats NB_PF个*/
for (i = 0; i < NB_PF; i++) { //添加支持的图层格式
drm_fmt = to_drm_pixelformat(ldev->caps.pix_fmt_hw[i]);
if (!drm_fmt)
continue;
formats[nb_fmt++] = drm_fmt;
/* Add the no-alpha related format if any & supported */
drm_fmt_no_alpha = get_pixelformat_without_alpha(drm_fmt);
if (!drm_fmt_no_alpha)
continue;
/* Manage hw-specific capabilities */
if (ldev->caps.non_alpha_only_l1 &&
type != DRM_PLANE_TYPE_PRIMARY)
continue;
formats[nb_fmt++] = drm_fmt_no_alpha;
}
ltdc_plane_create函数用于创建plane相关部分,
drm_universal_plane_init:初始化plane结构体
drm_plane_helper_add:添加helper函数
五、测试
将代码放到drivers\gpu\drm\stm目录下进行覆盖(省心),重新编译内核(经过测试,修改设备树和stm目录下的代码之后,屏幕在uboot启动阶段能正常使用,在Image启动阶段无法使用,说明修改之后,内核中的DRM框架已经无法正常工作),下载Image和设备树。重新启动之后,可以在内核启动中看到这种输出字样。
[ 1.358037] panel-simple panel-rgb: panel-rgb supply power not found, using dummy regulator
有这个部分说明DRM能正常使用。
1、查看/dev/dri
/dev/dri下是DRM生成的节点,也可以看到/dev/fb0,这个节点是DRM框架兼容FB框架产生的节点。
2、modetest
modetest是libdrm库编译出来产生的一个DRM测试程序,输出结果如图,可以看到KSM相关的输出。libdrm使用版本为libdrm-2.4.109。
root@ATK-MP157:~# modetest
trying to open device 'i915'...failed
trying to open device 'amdgpu'...failed
trying to open device 'radeon'...failed
trying to open device 'nouveau'...failed
trying to open device 'vmwgfx'...failed
trying to open device 'omapdrm'...failed
trying to open device 'exynos'...failed
trying to open device 'tilcdc'...failed
trying to open device 'msm'...failed
trying to open device 'sti'...failed
trying to open device 'tegra'...failed
trying to open device 'imx-drm'...failed
trying to open device 'rockchip'...failed
trying to open device 'atmel-hlcdc'...failed
trying to open device 'fsl-dcu-drm'...failed
trying to open device 'vc4'...failed
trying to open device 'virtio_gpu'...failed
trying to open device 'mediatek'...failed
trying to open device 'meson'...failed
trying to open device 'pl111'...failed
trying to open device 'stm'...done
Encoders:
id crtc type possible crtcs possible clones
31 35 DPI 0x00000001 0x00000000
Connectors:
id encoder status name size (mm) modes encoders
32 31 connected DPI-1 0x0 1 31
modes:
index name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot)
#0 1024x600 59.99 1024 1164 1184 1344 600 620 623 635 51200 flags: phsync, pvsync; type: preferred, driver
props:
1 EDID:
flags: immutable blob
blobs:
value:
2 DPMS:
flags: enum
enums: On=0 Standby=1 Suspend=2 Off=3
value: 0
5 link-status:
flags: enum
enums: Good=0 Bad=1
value: 0
6 non-desktop:
flags: immutable range
values: 0 1
value: 0
4 TILE:
flags: immutable blob
blobs:
value:
20 CRTC_ID:
flags: object
value: 35
CRTCs:
id fb pos size
35 38 (0,0) (1024x600)
#0 1024x600 59.99 1024 1164 1184 1344 600 620 623 635 51200 flags: phsync, pvsync; type: preferred, driver
props:
22 ACTIVE:
flags: range
values: 0 1
value: 1
23 MODE_ID:
flags: blob
blobs:
value:
00c8000000048c04a004400500005802
6c026f027b0200003c00000005000000
48000000313032347836303000000000
00000000000000000000000000000000
00000000
19 OUT_FENCE_PTR:
flags: range
values: 0 18446744073709551615
value: 0
24 VRR_ENABLED:
flags: range
values: 0 1
value: 0
28 GAMMA_LUT:
flags: blob
blobs:
value:
29 GAMMA_LUT_SIZE:
flags: immutable range
values: 0 4294967295
value: 256
Planes:
id crtc fb CRTC x,y x,y gamma size possible crtcs
33 35 38 0,0 0,0 0 0x00000001
formats: AR24 XR24 RG24 RG16 AR15 XR15 AR12 XR12 C8
props:
8 type:
flags: immutable enum
enums: Overlay=0 Primary=1 Cursor=2
value: 1
17 FB_ID:
flags: object
value: 38
18 IN_FENCE_FD:
flags: signed range
values: -1 2147483647
value: -1
20 CRTC_ID:
flags: object
value: 35
13 CRTC_X:
flags: signed range
values: -2147483648 2147483647
value: 0
14 CRTC_Y:
flags: signed range
values: -2147483648 2147483647
value: 0
15 CRTC_W:
flags: range
values: 0 2147483647
value: 1024
16 CRTC_H:
flags: range
values: 0 2147483647
value: 600
9 SRC_X:
flags: range
values: 0 4294967295
value: 0
10 SRC_Y:
flags: range
values: 0 4294967295
value: 0
11 SRC_W:
flags: range
values: 0 4294967295
value: 67108864
12 SRC_H:
flags: range
values: 0 4294967295
value: 39321600
30 IN_FORMATS:
flags: immutable blob
blobs:
value:
01000000000000000900000018000000
01000000400000004152323458523234
52473234524731364152313558523135
41523132585231324338202000000000
ff010000000000000000000000000000
0000000000000000
in_formats blob decoded:
AR24: LINEAR
XR24: LINEAR
RG24: LINEAR
RG16: LINEAR
AR15: LINEAR
XR15: LINEAR
AR12: LINEAR
XR12: LINEAR
C8 : LINEAR
36 0 0 0,0 0,0 0 0x00000001
formats: AR24 RG24 RG16 AR15 AR12 C8
props:
8 type:
flags: immutable enum
enums: Overlay=0 Primary=1 Cursor=2
value: 0
17 FB_ID:
flags: object
value: 0
18 IN_FENCE_FD:
flags: signed range
values: -1 2147483647
value: -1
20 CRTC_ID:
flags: object
value: 0
13 CRTC_X:
flags: signed range
values: -2147483648 2147483647
value: 0
14 CRTC_Y:
flags: signed range
values: -2147483648 2147483647
value: 0
15 CRTC_W:
flags: range
values: 0 2147483647
value: 0
16 CRTC_H:
flags: range
values: 0 2147483647
value: 0
9 SRC_X:
flags: range
values: 0 4294967295
value: 0
10 SRC_Y:
flags: range
values: 0 4294967295
value: 0
11 SRC_W:
flags: range
values: 0 4294967295
value: 0
12 SRC_H:
flags: range
values: 0 4294967295
value: 0
30 IN_FORMATS:
flags: immutable blob
blobs:
value:
01000000000000000600000018000000
01000000300000004152323452473234
52473136415231354152313243382020
3f000000000000000000000000000000
0000000000000000
in_formats blob decoded:
AR24: LINEAR
RG24: LINEAR
RG16: LINEAR
AR15: LINEAR
AR12: LINEAR
C8 : LINEAR
Frame buffers:
id size pitch
输入modetest -M stm -s 32@35:1024x600进行测试,可以看到输出为
其中 32为 Connectors ID、35为CRTCs ID,分辨率为1024×600。屏幕显示如图
3、使用libdrm测试
博主自己参考libdrm库里面的例程写了一个简单的测试代码,编译之后显示如图。测试代码和DRM驱动一起放到百度云上面供学习参考。
声明:代码仅供学习参考,使用中出现的任何情况均与本人无关。
六、总结
要实现一个 DRM KMS 驱动,通常需要实现如下代码:
fops、drm_driver
dumb_create、fb_create、atomic_commit
drm_xxx_funcs、drm_xxx_helper_funcs
drm_xxx_init()、drm_xxx_helper_add()
drm_dev_init()、drm_dev_register()
核心是7个 objects,一切都围绕着这几个 objects 展开:
为了创建 crtc/plane/encoder/connector objects,需要调用 drm_xxx_init()。
为了创建 framebuffer object,需要实现 fb_create() callback。
为了创建 gem object,需要实现 dumb_create() callback。
为了创建 property objects,需要调用 drm_mode_config_init()。
为了让这些 objects 动起来,需要实现各种 funcs 和 helper funcs。
为了支持 atomic 操作,需要实现 atomic_commit() callback。
DRM框架是Linux内核中一个比较复杂的框架,本文只是介绍其中的一部分,并未完全介绍完,如DMA-Buf部分并未介绍,以后有时间再来更新。
百度云链接:
链接:https://pan.baidu.com/s/1zuqqy_nryNTD6YqLjazJ1w 提取码:6hgv