注:文章都是通过阅读各位前辈总结的资料 Android 11.0 && Linux(Kernel 4.19)Rockchip平台源码、加上自己的思考分析总结出来的,其中难免有理解不对的地方,欢迎大家批评指正。文章为个人学习、研究、欣赏之用,图文内容整理自互联网,如有侵权,请联系删除(◕‿◕),转载请注明出处(©Rockchip ©Android @Linux 版权所有),谢谢。

(==文章基于 Kernel-4.19==)&&(==文章基于 Android 11.0==)

【zhoujinjian.com博客原图链接】

【开发板 RockPi4bPlusV1.6】

【开发板 RockPi4bPlusV1.6 Android 11.0 && Linux(Kernel 4.19)源码链接】:(repo init -u https://github.com/radxa/manifests.git -b Android11_Radxa_rk11.1 -m rockchip-r-release.xml)

【开发板 RockPi4bPlusV1.6 Android 11.0 && Linux(Kernel 4.19)编译指南】

正是由于前人(各位大神)的分析和总结,帮助我节约了大量的时间和精力,特别感谢,由于不喜欢图片水印,去除了水印,敬请谅解!!!

本文转自Rockchip RK3399 - DRM framebuffer、plane基础知识,如有侵权,请联系删除。


开发板 :ROCK Pi 4B+开发板
eMMC32GB
LPDDR44GB
显示屏 :7英寸HDMI接口显示屏
u-boot2017.09
linux4.19


我们在文章Rockchip RK3399 - DRM子系统》Rockchip RK3399 - DRM驱动程序》中介绍了RM子系统的整体框架,并对DRM各个模块进行了简单的介绍,本节我们将会详细介绍framebufferplane

一、显示处理器

我们知道一个显示子系统由显示处理器(vopvideo output processor)、接口控制器(mipilvdshdmiedpdprgbBT1120BT656I8080MCU 显示接口)等)、液晶背光,电源等多个独立的功能模块构成。

其中显示处理器格外重要,那么问题来了,什么是显示/处理器?

  • 将在内存中的图像数据,转化为电信号送到显示设备称为显示控制器,比如早期的LCDC
  • 后面进行了拓展,可以处理一些简单的图像,比如缩放、旋转、合成等,如瑞芯的vop,高通的sde称为显示处理器;

1.1 功能介绍

显示处理器可以在没有CPU参与的情况下可以做一些简单的图像处理,比如:

  • 缩放,旋转等操作;
  • 支持多层,并可以进行合成,支持porter-duff
  • 支持多种显存格式(ARGB888,RGB565, 包括GPU输出的tile格式以及fbdc压缩格式)等;
  • 支持生成时序信号如tcon,送给如mipilvds等接口;
  • 支持多种分辨率;

这里我们为什么要介绍显示处理器的功能呢?这主要是因为DRM中的planecrtc等功能模块就是对显示处理器的抽象;

  • plane对应显示处理器中的layer
  • crtc对应显示处理器中的图层合成以及时序的生成的模块;

各家芯片厂商显示处理器实现各有不同,但均有如下两个重要的部件:多个layer、负责图层合成的模块以及输出接口,整个处理流程可以称为display pipelane

img

1.2 layer

layer是一个图层,可以理解为里面放了一张图片,如下图。实际放在各个layer的是以某种格式填充在memory里的图像数据。

layer需要配置的信息包括但不限于:图像formatwidth x heightstride、显存地址等;如果支持缩放还需要原图像大小以及要缩放大小等。现在很多芯片都支持多layer,如RK3399vop可支持4个layerRK3568vop可支持6个layer

1.3 合成

主要用于将各个layer送来的图像进行合成为一层送出显示,例如将上面三层合成如下效果。

img

可以理解为在图像合成模块里有个画布,把每层按照一定次序和规则画上去。合成如上效果需要配置那些参数呢?

  • 每个layer在画布的位置(x, y);
  • 每个layerzorder,也就是哪个layer在上面,哪个layer在下面;
  • 最终合成图像的大小;
  • 按照哪种方式合成(porter-duff);

framebuffer数据结构

DRM框架中,framebuffer用于存储需要显示的内容,存储的信息包括:

  • 需要显示的内容在内存区域的引用;

  • 存储在内存中的帧的格式;

  • 内存区域的激活区域(即将要显示的内容);

DRM Framebuffer是一个虚拟的对象,它依赖于特定的实现。framebuffer实现依赖于:

  • 依赖于底层内存管理器来分配后备存储,比如GEMTTM
  • 依赖于显示处理器的能力:
    • 支持DMA传输类型(可以是连续内存或者Scatter Gather);
    • IOMMU;

2.1 struct drm_framebuffer

linux内核使用struct drm_framebuffer表示一个framebuffer,包括像素格式、分辨率和显存地址等信息。定义在include/drm/drm_framebuffer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/**
* struct drm_framebuffer - frame buffer object
*
* Note that the fb is refcounted for the benefit of driver internals,
* for example some hw, disabling a CRTC/plane is asynchronous, and
* scanout does not actually complete until the next vblank. So some
* cleanup (like releasing the reference(s) on the backing GEM bo(s))
* should be deferred. In cases like this, the driver would like to
* hold a ref to the fb even though it has already been removed from
* userspace perspective. See drm_framebuffer_get() and
* drm_framebuffer_put().
*
* The refcount is stored inside the mode object @base.
*/
struct drm_framebuffer {
/**
* @dev: DRM device this framebuffer belongs to
*/
struct drm_device *dev;
/**
* @head: Place on the &drm_mode_config.fb_list, access protected by
* &drm_mode_config.fb_lock.
*/
struct list_head head;

/**
* @base: base modeset object structure, contains the reference count.
*/
struct drm_mode_object base;

/**
* @comm: Name of the process allocating the fb, used for fb dumping.
*/
char comm[TASK_COMM_LEN];

/**
* @format: framebuffer format information
*/
const struct drm_format_info *format;
/**
* @funcs: framebuffer vfunc table
*/
const struct drm_framebuffer_funcs *funcs;
/**
* @pitches: Line stride per buffer. For userspace created object this
* is copied from drm_mode_fb_cmd2.
*/
unsigned int pitches[DRM_FORMAT_MAX_PLANES];
/**
* @offsets: Offset from buffer start to the actual pixel data in bytes,
* per buffer. For userspace created object this is copied from
* drm_mode_fb_cmd2.
*
* Note that this is a linear offset and does not take into account
* tiling or buffer layout per @modifier. It is meant to be used when
* the actual pixel data for this framebuffer plane starts at an offset,
* e.g. when multiple planes are allocated within the same backing
* storage buffer object. For tiled layouts this generally means its
* @offsets must at least be tile-size aligned, but hardware often has
* stricter requirements.
*
* This should not be used to specifiy x/y pixel offsets into the buffer
* data (even for linear buffers). Specifying an x/y pixel offset is
* instead done through the source rectangle in &struct drm_plane_state.
*/
unsigned int offsets[DRM_FORMAT_MAX_PLANES];
/**
* @modifier: Data layout modifier. This is used to describe
* tiling, or also special layouts (like compression) of auxiliary
* buffers. For userspace created object this is copied from
* drm_mode_fb_cmd2.
*/
uint64_t modifier;
/**
* @width: Logical width of the visible area of the framebuffer, in
* pixels.
*/
unsigned int width;
/**
* @height: Logical height of the visible area of the framebuffer, in
* pixels.
*/
unsigned int height;
/**
* @flags: Framebuffer flags like DRM_MODE_FB_INTERLACED or
* DRM_MODE_FB_MODIFIERS.
*/
int flags;
/**
* @hot_x: X coordinate of the cursor hotspot. Used by the legacy cursor
* IOCTL when the driver supports cursor through a DRM_PLANE_TYPE_CURSOR
* universal plane.
*/
int hot_x;
/**
* @hot_y: Y coordinate of the cursor hotspot. Used by the legacy cursor
* IOCTL when the driver supports cursor through a DRM_PLANE_TYPE_CURSOR
* universal plane.
*/
int hot_y;
/**
* @filp_head: Placed on &drm_file.fbs, protected by &drm_file.fbs_lock.
*/
struct list_head filp_head;
/**
* @obj: GEM objects backing the framebuffer, one per plane (optional).
*
* This is used by the GEM framebuffer helpers, see e.g.
* drm_gem_fb_create().
*/
struct drm_gem_object *obj[DRM_FORMAT_MAX_PLANES];
};

该结构体包括以下成员:

  • dev:该framebuffer所属的DRM设备;
  • head:链表节点,用于将当前节点添加到drm_mode_config.fb_list链表;
  • base:该framebuffer的基类,struct drm_mode_object类型;
  • comnchar数组,数组长度为32,用于存放分配该fb的进程的名字;
  • formatframebuffer的格式信息;
  • funcsframebuffer的函数表;
  • pitches:无符号int数组,数组长度为4,存放每个缓冲区的行跨度(以字节为单位);
  • offsets:无符号int数组,数组长度为4,存放每个缓冲区中实际像素数据相对于缓冲区起始处的偏移量(以字节为单位);
  • modifier:数据布局修饰符,用于描述缓冲区的平铺或特殊布局;
  • widthframebuffer可见区域的逻辑宽度(以像素为单位);
  • heightframebuffer可见区域的逻辑高度(以像素为单位);
  • flagsframebuffer的标志,例如DRM_MODE_FB_INTERLACEDDRM_MODE_FB_MODIFIERS
  • hot_x:光标热点的X坐标, Used by the legacy cursor IOCTL when the driver supports cursor through a DRM_PLANE_TYPE_CURSOR universal plane
  • hot_y:光标热点的Y坐标,Used by the legacy cursor IOCTL when the driver supports cursor through a DRM_PLANE_TYPE_CURSOR universal plane
  • filp_head:链表节点,用于将该帧缓冲对象加入到drm_file结构的fbs链表中,访问此链表需要使用fbs_lock进行保护;
  • objstruct drm_gem_object类型指针数组,数组长度为4,备份framebufferGEM对象,每个plane一个对象;

struct drm_framebuffer主要元素的展示如下图所示,其中Memory Region表示帧缓冲区,Dsisplayed Area表示实际显示区域,图中棕色区域表示实际像素数据相对于framebuffer起始处的偏移量;

这里需要介绍一下pitchesoffsets为啥是个数组,数组的长度为DRM_FORMAT_MAX_PLANES,通过这个宏我们大概可以了解到每种DRM格式应该包含多个color plane,因此数组元素的值依次对应每个color plane;比如RGB格式由RGB三种颜色图层组成。

2.2 struct drm_format_info

linux内核使用struct drm_format_info表示DRM格式信息,定义在include/drm/drm_fourcc.h;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* struct drm_format_info - information about a DRM format
*/
struct drm_format_info {
/** @format: 4CC format identifier (DRM_FORMAT_*) */
u32 format;

/**
* @depth:
*
* Color depth (number of bits per pixel excluding padding bits),
* valid for a subset of RGB formats only. This is a legacy field, do
* not use in new code and set to 0 for new formats.
*/
u8 depth;

/** @num_planes: Number of color planes (1 to 3) */
u8 num_planes;

union {
/**
* @cpp:
*
* Number of bytes per pixel (per plane), this is aliased with
* @char_per_block. It is deprecated in favour of using the
* triplet @char_per_block, @block_w, @block_h for better
* describing the pixel format.
*/
u8 cpp[DRM_FORMAT_MAX_PLANES];

/**
* @char_per_block:
*
* Number of bytes per block (per plane), where blocks are
* defined as a rectangle of pixels which are stored next to
* each other in a byte aligned memory region. Together with
* @block_w and @block_h this is used to properly describe tiles
* in tiled formats or to describe groups of pixels in packed
* formats for which the memory needed for a single pixel is not
* byte aligned.
*
* @cpp has been kept for historical reasons because there are
* a lot of places in drivers where it's used. In drm core for
* generic code paths the preferred way is to use
* @char_per_block, drm_format_info_block_width() and
* drm_format_info_block_height() which allows handling both
* block and non-block formats in the same way.
*
* For formats that are intended to be used only with non-linear
* modifiers both @cpp and @char_per_block must be 0 in the
* generic format table. Drivers could supply accurate
* information from their drm_mode_config.get_format_info hook
* if they want the core to be validating the pitch.
*/
u8 char_per_block[DRM_FORMAT_MAX_PLANES];
};

/**
* @block_w:
*
* Block width in pixels, this is intended to be accessed through
* drm_format_info_block_width()
*/
u8 block_w[DRM_FORMAT_MAX_PLANES];

/**
* @block_h:
*
* Block height in pixels, this is intended to be accessed through
* drm_format_info_block_height()
*/
u8 block_h[DRM_FORMAT_MAX_PLANES];

/** @hsub: Horizontal chroma subsampling factor */
u8 hsub;
/** @vsub: Vertical chroma subsampling factor */
u8 vsub;

/** @has_alpha: Does the format embeds an alpha component? */
bool has_alpha;

/** @is_yuv: Is it a YUV format? */
bool is_yuv;

/** @is_color_indexed: Is it a color-indexed format? */
bool is_color_indexed;
};

它包括以下字段:

  • format4CC格式标识符(DRM_FORMAT_*);
  • depth:颜色深度(每像素的位数,不包括填充位),仅适用于部分RGB格式。这是一个过时字段,在新代码中不要使用,并且对于新格式设置为0。
  • num_planescolor plane数量(1到3)。
  • pp:数组长度为4,依次存储每个plane每像素的字节数,与char_per_block具有别名。对于更好地描述像素格式,已弃用此字段,推荐使用三元组char_per_blockblock_wblock_h
  • char_per_block:数组长度为4,依次存储每个块的字节数,块被定义为以字节对齐的内存区域中相邻存储的像素矩形。与block_wblock_h一起使用,可以正确描述平铺格式中的图块,或者对于单个像素所需的字节对齐的打包格式中的像素组;
  • block_w:数组长度为4,依次存储块宽度(以像素为单位),应通过drm_format_info_block_width()访问;
  • block_h:数组长度为4,依次存储块高度(以像素为单位),应通过drm_format_info_block_height()访问;
  • hsub:水平色度亚采样因子;
  • vsub:垂直色度亚采样因子;
  • has_alpha:格式是否包含Alpha分量;
  • is_yuv:格式是否为YUV格式;
  • is_color_indexed:格式是否为索引颜色格式;
2.2.1 FOURCC

什么是4CC格式(或者说FOURCC format )?它是一种用于标识数据格式的约定,每个FOURCC编码由4个ASCII字符组成,通过表示特定类型的数据格式、编码或标记。

FOURCC代码最初由Microsoft引入并广泛应用于多媒体和图像处理领域。它们通常用于标识和区分不同的文件格式、视频编解码器、像素格式、音频格式等。

在计算机图形中,FOURCC代码常用于指定像素格式,例如在视频编码和解码中。通过使用FOURCC编码,可以快速识别和解释特定的数据格式,以便进行适当的处理和解码。

需要注意的是,FOURCC代码只是一种标识符号,代表了特定的格式或类型,具体的实现和处理方式仍需根据具体的应用和上下文来确定。

DRM支持的FOURCC格式定义在include/drm/drm_fourcc.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* DRM_FORMAT_MAX_PLANES - maximum number of planes a DRM format can have
*/
#define DRM_FORMAT_MAX_PLANES 4u // DRM格式最多包含的plane数量

/*
* DRM formats are little endian. Define host endian variants for the
* most common formats here, to reduce the #ifdefs needed in drivers.
*
* Note that the DRM_FORMAT_BIG_ENDIAN flag should only be used in
* case the format can't be specified otherwise, so we don't end up
* with two values describing the same format.
*/
#ifdef __BIG_ENDIAN
# define DRM_FORMAT_HOST_XRGB1555 (DRM_FORMAT_XRGB1555 | \
DRM_FORMAT_BIG_ENDIAN)
# define DRM_FORMAT_HOST_RGB565 (DRM_FORMAT_RGB565 | \
DRM_FORMAT_BIG_ENDIAN)
# define DRM_FORMAT_HOST_XRGB8888 DRM_FORMAT_BGRX8888
# define DRM_FORMAT_HOST_ARGB8888 DRM_FORMAT_BGRA8888
#else
# define DRM_FORMAT_HOST_XRGB1555 DRM_FORMAT_XRGB1555
# define DRM_FORMAT_HOST_RGB565 DRM_FORMAT_RGB565
# define DRM_FORMAT_HOST_XRGB8888 DRM_FORMAT_XRGB8888
# define DRM_FORMAT_HOST_ARGB8888 DRM_FORMAT_ARGB8888
#endif

RGB是颜色的编码格式,它代表红色(R)、绿色(G)和蓝色(B)三个颜色通道。

RGB编码中,每个像素使用一个包含三个数字值的元组来表示颜色。这三个值分别表示红色、绿色和蓝色的亮度或强度。每个值的范围是从0到255,其中0表示没有该颜色的亮度,而255表示最高强度或亮度。

ARGB是在RGB编码的基础上增加了一个额外的通道——Alpha通道。Alpha通道用于表示像素的透明度,即图像中该像素的不透明程度。Alpha通道的取值范围也是0到255,其中0表示完全透明,255表示完全不透明。

XRGB是指在RGB编码中,其中一个字节(8位)被保留但未使用。通常这个保留位是填充0,因此它不影响颜色的表示。这种编码方式常用于某些特定的图形处理操作和计算中,可以提高效率和内存利用率。

2.3 struct drm_framebuffer_funcs

linux内核使用struct drm_framebuffer_funcs表示framebuffer的基本操作,定义在include/drm/drm_framebuffer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/**
* struct drm_framebuffer_funcs - framebuffer hooks
*/
struct drm_framebuffer_funcs {
/**
* @destroy:
*
* Clean up framebuffer resources, specifically also unreference the
* backing storage. The core guarantees to call this function for every
* framebuffer successfully created by calling
* &drm_mode_config_funcs.fb_create. Drivers must also call
* drm_framebuffer_cleanup() to release DRM core resources for this
* framebuffer.
*/
void (*destroy)(struct drm_framebuffer *framebuffer);

/**
* @create_handle:
*
* Create a buffer handle in the driver-specific buffer manager (either
* GEM or TTM) valid for the passed-in &struct drm_file. This is used by
* the core to implement the GETFB IOCTL, which returns (for
* sufficiently priviledged user) also a native buffer handle. This can
* be used for seamless transitions between modesetting clients by
* copying the current screen contents to a private buffer and blending
* between that and the new contents.
*
* GEM based drivers should call drm_gem_handle_create() to create the
* handle.
*
* RETURNS:
*
* 0 on success or a negative error code on failure.
*/
int (*create_handle)(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned int *handle);
/**
* @dirty:
*
* Optional callback for the dirty fb IOCTL.
*
* Userspace can notify the driver via this callback that an area of the
* framebuffer has changed and should be flushed to the display
* hardware. This can also be used internally, e.g. by the fbdev
* emulation, though that's not the case currently.
*
* See documentation in drm_mode.h for the struct drm_mode_fb_dirty_cmd
* for more information as all the semantics and arguments have a one to
* one mapping on this function.
*
* Atomic drivers should use drm_atomic_helper_dirtyfb() to implement
* this hook.
*
* RETURNS:
*
* 0 on success or a negative error code on failure.
*/
int (*dirty)(struct drm_framebuffer *framebuffer,
struct drm_file *file_priv, unsigned flags,
unsigned color, struct drm_clip_rect *clips,
unsigned num_clips);
};

其中:

  • destory:清理framebuffer资源的回调函数;
  • create_handle:创建驱动程序特定缓冲区管理器(GEMTTM)中与给定文件对象drm_file相关联的缓冲区句柄;
  • dirty:选的回调函数,用于处理dirty fb IOCTL

2.4 关系图

为了更加清晰的了解struct drm_framebufferstruct drm_format_infostruct drm_framebuffer_func数据结构的关系,我们绘制了如下关系图:

三、plane数据结构

DRM子系统中的plane对应显示处理器的layer,负责配置layer的一些属性:formatwidthheightstride、显存地址等;

一个crtc可以接收多个plane的输入,如下图所示:

其中:

  • Background FramebufferDRM_PLANE_TYPE_PRIMARY类型的plane
  • Overplay FramerbufferDRM_PLANE_TYPE_OVERLAY类型的plane
  • Cursor FramerbufferDRM_PLANE_TYPE_CURSOR类型的plane

3.1 struct drm_plane

linux内核使用struct drm_plane表示一个plane,它们从一个drm_framebuffer接收输入数据,并将其传递给一个drm_crtc

struct drm_plane成员中包含了了struct drm_framebuffer *fbstruct drm_crtc *crtc,定义include/drm/drm_plane.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/**
* struct drm_plane - central DRM plane control structure
*
* Planes represent the scanout hardware of a display block. They receive their
* input data from a &drm_framebuffer and feed it to a &drm_crtc. Planes control
* the color conversion, see `Plane Composition Properties`_ for more details,
* and are also involved in the color conversion of input pixels, see `Color
* Management Properties`_ for details on that.
*/
struct drm_plane {
/** @dev: DRM device this plane belongs to */
struct drm_device *dev;

/**
* @head:
*
* List of all planes on @dev, linked from &drm_mode_config.plane_list.
* Invariant over the lifetime of @dev and therefore does not need
* locking.
*/
struct list_head head;

/** @name: human readable name, can be overwritten by the driver */
char *name;

/**
* @mutex:
*
* Protects modeset plane state, together with the &drm_crtc.mutex of
* CRTC this plane is linked to (when active, getting activated or
* getting disabled).
*
* For atomic drivers specifically this protects @state.
*/
struct drm_modeset_lock mutex;

/** @base: base mode object */
struct drm_mode_object base;

/**
* @possible_crtcs: pipes this plane can be bound to constructed from
* drm_crtc_mask()
*/
uint32_t possible_crtcs;
/** @format_types: array of formats supported by this plane */
uint32_t *format_types;
/** @format_count: Size of the array pointed at by @format_types. */
unsigned int format_count;
/**
* @format_default: driver hasn't supplied supported formats for the
* plane. Used by the non-atomic driver compatibility wrapper only.
*/
bool format_default;

/** @modifiers: array of modifiers supported by this plane */
uint64_t *modifiers;
/** @modifier_count: Size of the array pointed at by @modifier_count. */
unsigned int modifier_count;

/**
* @crtc:
*
* Currently bound CRTC, only meaningful for non-atomic drivers. For
* atomic drivers this is forced to be NULL, atomic drivers should
* instead check &drm_plane_state.crtc.
*/
struct drm_crtc *crtc;

/**
* @fb:
*
* Currently bound framebuffer, only meaningful for non-atomic drivers.
* For atomic drivers this is forced to be NULL, atomic drivers should
* instead check &drm_plane_state.fb.
*/
struct drm_framebuffer *fb;

/**
* @old_fb:
*
* Temporary tracking of the old fb while a modeset is ongoing. Only
* used by non-atomic drivers, forced to be NULL for atomic drivers.
*/
struct drm_framebuffer *old_fb;

/** @funcs: plane control functions */
const struct drm_plane_funcs *funcs;

/** @properties: property tracking for this plane */
struct drm_object_properties properties;

/** @type: Type of plane, see &enum drm_plane_type for details. */
enum drm_plane_type type;

/**
* @index: Position inside the mode_config.list, can be used as an array
* index. It is invariant over the lifetime of the plane.
*/
unsigned index;

/** @helper_private: mid-layer private data */
const struct drm_plane_helper_funcs *helper_private;
/**
* @state:
*
* Current atomic state for this plane.
*
* This is protected by @mutex. Note that nonblocking atomic commits
* access the current plane state without taking locks. Either by going
* through the &struct drm_atomic_state pointers, see
* for_each_oldnew_plane_in_state(), for_each_old_plane_in_state() and
* for_each_new_plane_in_state(). Or through careful ordering of atomic
* commit operations as implemented in the atomic helpers, see
* &struct drm_crtc_commit.
*/
struct drm_plane_state *state;

/**
* @alpha_property:
* Optional alpha property for this plane. See
* drm_plane_create_alpha_property().
*/
struct drm_property *alpha_property;
/**
* @zpos_property:
* Optional zpos property for this plane. See
* drm_plane_create_zpos_property().
*/
struct drm_property *zpos_property;
/**
* @rotation_property:
* Optional rotation property for this plane. See
* drm_plane_create_rotation_property().
*/
struct drm_property *rotation_property;
/**
* @blend_mode_property:
* Optional "pixel blend mode" enum property for this plane.
* Blend mode property represents the alpha blending equation selection,
* describing how the pixels from the current plane are composited with
* the background.
*/
struct drm_property *blend_mode_property;

/**
* @color_encoding_property:
*
* Optional "COLOR_ENCODING" enum property for specifying
* color encoding for non RGB formats.
* See drm_plane_create_color_properties().
*/
struct drm_property *color_encoding_property;
/**
* @color_range_property:
*
* Optional "COLOR_RANGE" enum property for specifying
* color range for non RGB formats.
* See drm_plane_create_color_properties().
*/
struct drm_property *color_range_property;

/**
* @scaling_filter_property: property to apply a particular filter while
* scaling.
*/
struct drm_property *scaling_filter_property;
};

该结构体包括以下成员:

  • dev:该plane所属的DRM设备;
  • head:链表节点,用于将当前节点追加到drm_mode_config.plane_list链表;
  • name:可读性良好的名称,可以被驱动程序覆盖;
  • mutex:互斥锁,用于保存modset plane state
  • base:该plane的基类,struct drm_mode_object类型;
  • possible_crtcsplane可以绑定到的管道,由drm_crtc_mask()构建;
  • format_typesplane支持的格式数组;
  • format_countformat_types数组的大小;
  • format_default:驱动程序没有为该plane提供支持的格式。仅由non-atomic driver兼容性包装器使用;
  • modifiers:该plane支持的修饰符数组;
  • modifier_countmodifiers数组的大小。
  • crtc:当前绑定的CRTC,仅对non-atomic driver有意义。对于atomic driver,它被强制为NULLatomic driver应该检查drm_plane_state.crtc
  • fb:当前绑定的framebuffer,仅对non-atomic driver有意义。对于atomic driver,它被强制为NULLatomic driver应该检查drm_plane_state.fb
  • old_fb:在模式设置进行中临时跟踪旧的帧缓冲。仅由non-atomic driver使用,对于atomic driver,它被强制为NULL
  • funcsplane控制函数;
  • properties:用于跟踪plane的属性。
  • typeplane的类型,详见enum drm_plane_type
  • index:在mode_config.list中的位置,可用作数组索引。在plane的生命周期中保持不变;
  • helper_private:中间层私有数据;
  • state:当前的原子状态;
  • alpha_property:表示plane的可选alpha属性,参见drm_plane_create_alpha_property()
  • zpos_property:表示plane的可选zpos属性,参见drm_plane_create_zpos_property()
  • rotation_property:表示plane的可选旋转属性,参见drm_plane_create_rotation_property()
  • blend_mode_property:表示plane的可选pixel blend mode枚举属性;
  • color_encoding_property:表示plane的可选COLOR_ENCODING枚举属性,用于指定非RGB格式的颜色编码。参见drm_plane_create_color_properties()
  • color_range_property:表示plane的可选COLOR_RANGE枚举属性,用于指定非RGB格式的颜色范围。参见drm_plane_create_color_properties()
  • scaling_filter_property:用于在缩放时应用特定滤镜的属性;
3.1.1 plane类型

plane的类型定义在include/drm/drm_plane.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* enum drm_plane_type - uapi plane type enumeration
*
* For historical reasons not all planes are made the same. This enumeration is
* used to tell the different types of planes apart to implement the different
* uapi semantics for them. For userspace which is universal plane aware and
* which is using that atomic IOCTL there's no difference between these planes
* (beyong what the driver and hardware can support of course).
*
* For compatibility with legacy userspace, only overlay planes are made
* available to userspace by default. Userspace clients may set the
* &DRM_CLIENT_CAP_UNIVERSAL_PLANES client capability bit to indicate that they
* wish to receive a universal plane list containing all plane types. See also
* drm_for_each_legacy_plane().
*
* In addition to setting each plane's type, drivers need to setup the
* &drm_crtc.primary and optionally &drm_crtc.cursor pointers for legacy
* IOCTLs. See drm_crtc_init_with_planes().
*
* WARNING: The values of this enum is UABI since they're exposed in the "type"
* property.
*/
enum drm_plane_type {
/**
* @DRM_PLANE_TYPE_OVERLAY:
*
* Overlay planes represent all non-primary, non-cursor planes. Some
* drivers refer to these types of planes as "sprites" internally.
*/
DRM_PLANE_TYPE_OVERLAY,

/**
* @DRM_PLANE_TYPE_PRIMARY:
*
* A primary plane attached to a CRTC is the most likely to be able to
* light up the CRTC when no scaling/cropping is used and the plane
* covers the whole CRTC.
*/
DRM_PLANE_TYPE_PRIMARY,

/**
* @DRM_PLANE_TYPE_CURSOR:
*
* A cursor plane attached to a CRTC is more likely to be able to be
* enabled when no scaling/cropping is used and the framebuffer has the
* size indicated by &drm_mode_config.cursor_width and
* &drm_mode_config.cursor_height. Additionally, if the driver doesn't
* support modifiers, the framebuffer should have a linear layout.
*/
DRM_PLANE_TYPE_CURSOR,
};

plane一共有三种,分别是:

  • DRM_PLANE_TYPE_PRIMARY:标注主图层,必须,每个CRTC至少包含一个;
    • Typically used to display a background image or graphics content
  • DRM_PLANE_TYPE_CURSOR:光标图层,可选,每个CRTC可以包含一个;
    • Used to display a cursor (like a mouse cursor)
  • DRM_PLANE_TYPE_OVERLAY:覆盖图层,可选,每个CRTC可以包含0~N个;
    • Used to benefit from hardware composition
    • Typically used to display windows with dynamic content (like a video)
    • In case of multiple CRTCs in the display controller, the overlay planes can often be dynamically attached to a specific CRTC when required
3.1.2 plane状态

struct drm_plane_state用于表示plane的状态,定义在include/drm/drm_plane.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/**
* struct drm_plane_state - mutable plane state
*
* Please note that the destination coordinates @crtc_x, @crtc_y, @crtc_h and
* @crtc_w and the source coordinates @src_x, @src_y, @src_h and @src_w are the
* raw coordinates provided by userspace. Drivers should use
* drm_atomic_helper_check_plane_state() and only use the derived rectangles in
* @src and @dst to program the hardware.
*/
struct drm_plane_state {
/** @plane: backpointer to the plane */
struct drm_plane *plane;

/**
* @crtc:
*
* Currently bound CRTC, NULL if disabled. Do not this write directly,
* use drm_atomic_set_crtc_for_plane()
*/
struct drm_crtc *crtc;

/**
* @fb:
*
* Currently bound framebuffer. Do not write this directly, use
* drm_atomic_set_fb_for_plane()
*/
struct drm_framebuffer *fb;

/**
* @fence:
*
* Optional fence to wait for before scanning out @fb. The core atomic
* code will set this when userspace is using explicit fencing. Do not
* write this field directly for a driver's implicit fence.
*
* Drivers should store any implicit fence in this from their
* &drm_plane_helper_funcs.prepare_fb callback. See
* drm_gem_plane_helper_prepare_fb() for a suitable helper.
*/
struct dma_fence *fence;

/**
* @crtc_x:
*
* Left position of visible portion of plane on crtc, signed dest
* location allows it to be partially off screen.
*/

int32_t crtc_x;
/**
* @crtc_y:
*
* Upper position of visible portion of plane on crtc, signed dest
* location allows it to be partially off screen.
*/
int32_t crtc_y;

/** @crtc_w: width of visible portion of plane on crtc */
/** @crtc_h: height of visible portion of plane on crtc */
uint32_t crtc_w, crtc_h;

/**
* @src_x: left position of visible portion of plane within plane (in
* 16.16 fixed point).
*/
uint32_t src_x;
/**
* @src_y: upper position of visible portion of plane within plane (in
* 16.16 fixed point).
*/
uint32_t src_y;
/** @src_w: width of visible portion of plane (in 16.16) */
/** @src_h: height of visible portion of plane (in 16.16) */
uint32_t src_h, src_w;

/**
* @alpha:
* Opacity of the plane with 0 as completely transparent and 0xffff as
* completely opaque. See drm_plane_create_alpha_property() for more
* details.
*/
u16 alpha;

/**
* @pixel_blend_mode:
* The alpha blending equation selection, describing how the pixels from
* the current plane are composited with the background. Value can be
* one of DRM_MODE_BLEND_*
*/
uint16_t pixel_blend_mode;

/**
* @rotation:
* Rotation of the plane. See drm_plane_create_rotation_property() for
* more details.
*/
unsigned int rotation;
/**
* @zpos:
* Priority of the given plane on crtc (optional).
*
* User-space may set mutable zpos properties so that multiple active
* planes on the same CRTC have identical zpos values. This is a
* user-space bug, but drivers can solve the conflict by comparing the
* plane object IDs; the plane with a higher ID is stacked on top of a
* plane with a lower ID.
*
* See drm_plane_create_zpos_property() and
* drm_plane_create_zpos_immutable_property() for more details.
*/
unsigned int zpos;

/**
* @normalized_zpos:
* Normalized value of zpos: unique, range from 0 to N-1 where N is the
* number of active planes for given crtc. Note that the driver must set
* &drm_mode_config.normalize_zpos or call drm_atomic_normalize_zpos() to
* update this before it can be trusted.
*/
unsigned int normalized_zpos;

/**
* @color_encoding:
*
* Color encoding for non RGB formats
*/
enum drm_color_encoding color_encoding;

/**
* @color_range:
*
* Color range for non RGB formats
*/
enum drm_color_range color_range;

/**
* @fb_damage_clips:
*
* Blob representing damage (area in plane framebuffer that changed
* since last plane update) as an array of &drm_mode_rect in framebuffer
* coodinates of the attached framebuffer. Note that unlike plane src,
* damage clips are not in 16.16 fixed point.
*
* See drm_plane_get_damage_clips() and
* drm_plane_get_damage_clips_count() for accessing these.
*/
struct drm_property_blob *fb_damage_clips;

/**
* @src:
*
* source coordinates of the plane (in 16.16).
*
* When using drm_atomic_helper_check_plane_state(),
* the coordinates are clipped, but the driver may choose
* to use unclipped coordinates instead when the hardware
* performs the clipping automatically.
*/
/**
* @dst:
*
* clipped destination coordinates of the plane.
*
* When using drm_atomic_helper_check_plane_state(),
* the coordinates are clipped, but the driver may choose
* to use unclipped coordinates instead when the hardware
* performs the clipping automatically.
*/
struct drm_rect src, dst;

/**
* @visible:
*
* Visibility of the plane. This can be false even if fb!=NULL and
* crtc!=NULL, due to clipping.
*/
bool visible;

/**
* @scaling_filter:
*
* Scaling filter to be applied
*/
enum drm_scaling_filter scaling_filter;

/**
* @commit: Tracks the pending commit to prevent use-after-free conditions,
* and for async plane updates.
*
* May be NULL.
*/
struct drm_crtc_commit *commit;

/** @state: backpointer to global drm_atomic_state */
struct drm_atomic_state *state;
};

src_xsrc_ysrc_hsrc_w指定framebuffer的源区域,crtc_xcrtc_ycrtc_hcrtc_w指定其显示在crtc上的目标区域。关系如下图所示;

3.2 操作函数

3.2.1 struct drm_plane_funcs

struct drm_plane_funcs用于描述plane的控制函数,定义在include/drm/drm_plane.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/**
* struct drm_plane_funcs - driver plane control functions
*/
struct drm_plane_funcs {
/**
* @update_plane:
*
* This is the legacy entry point to enable and configure the plane for
* the given CRTC and framebuffer. It is never called to disable the
* plane, i.e. the passed-in crtc and fb paramters are never NULL.
*
* The source rectangle in frame buffer memory coordinates is given by
* the src_x, src_y, src_w and src_h parameters (as 16.16 fixed point
* values). Devices that don't support subpixel plane coordinates can
* ignore the fractional part.
*
* The destination rectangle in CRTC coordinates is given by the
* crtc_x, crtc_y, crtc_w and crtc_h parameters (as integer values).
* Devices scale the source rectangle to the destination rectangle. If
* scaling is not supported, and the source rectangle size doesn't match
* the destination rectangle size, the driver must return a
* -<errorname>EINVAL</errorname> error.
*
* Drivers implementing atomic modeset should use
* drm_atomic_helper_update_plane() to implement this hook.
*
* RETURNS:
*
* 0 on success or a negative error code on failure.
*/
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,
struct drm_modeset_acquire_ctx *ctx);

/**
* @disable_plane:
*
* This is the legacy entry point to disable the plane. The DRM core
* calls this method in response to a DRM_IOCTL_MODE_SETPLANE IOCTL call
* with the frame buffer ID set to 0. Disabled planes must not be
* processed by the CRTC.
*
* Drivers implementing atomic modeset should use
* drm_atomic_helper_disable_plane() to implement this hook.
*
* RETURNS:
*
* 0 on success or a negative error code on failure.
*/
int (*disable_plane)(struct drm_plane *plane,
struct drm_modeset_acquire_ctx *ctx);

/**
* @destroy:
*
* Clean up plane resources. This is only called at driver unload time
* through drm_mode_config_cleanup() since a plane cannot be hotplugged
* in DRM.
*/
void (*destroy)(struct drm_plane *plane);

/**
* @reset:
*
* Reset plane hardware and software state to off. This function isn't
* called by the core directly, only through drm_mode_config_reset().
* It's not a helper hook only for historical reasons.
*
* Atomic drivers can use drm_atomic_helper_plane_reset() to reset
* atomic state using this hook.
*/
void (*reset)(struct drm_plane *plane);

/**
* @set_property:
*
* This is the legacy entry point to update a property attached to the
* plane.
*
* This callback is optional if the driver does not support any legacy
* driver-private properties. For atomic drivers it is not used because
* property handling is done entirely in the DRM core.
*
* RETURNS:
*
* 0 on success or a negative error code on failure.
*/
int (*set_property)(struct drm_plane *plane,
struct drm_property *property, uint64_t val);

/**
* @atomic_duplicate_state:
*
* Duplicate the current atomic state for this plane and return it.
* The core and helpers guarantee that any atomic state duplicated with
* this hook and still owned by the caller (i.e. not transferred to the
* driver by calling &drm_mode_config_funcs.atomic_commit) will be
* cleaned up by calling the @atomic_destroy_state hook in this
* structure.
*
* This callback is mandatory for atomic drivers.
*
* Atomic drivers which don't subclass &struct drm_plane_state should use
* drm_atomic_helper_plane_duplicate_state(). Drivers that subclass the
* state structure to extend it with driver-private state should use
* __drm_atomic_helper_plane_duplicate_state() to make sure shared state is
* duplicated in a consistent fashion across drivers.
*
* It is an error to call this hook before &drm_plane.state has been
* initialized correctly.
*
* NOTE:
*
* If the duplicate state references refcounted resources this hook must
* acquire a reference for each of them. The driver must release these
* references again in @atomic_destroy_state.
*
* RETURNS:
*
* Duplicated atomic state or NULL when the allocation failed.
*/
struct drm_plane_state *(*atomic_duplicate_state)(struct drm_plane *plane);
/**
* @atomic_destroy_state:
*
* Destroy a state duplicated with @atomic_duplicate_state and release
* or unreference all resources it references
*
* This callback is mandatory for atomic drivers.
*/
void (*atomic_destroy_state)(struct drm_plane *plane,
struct drm_plane_state *state);

/**
* @atomic_set_property:
*
* Decode a driver-private property value and store the decoded value
* into the passed-in state structure. Since the atomic core decodes all
* standardized properties (even for extensions beyond the core set of
* properties which might not be implemented by all drivers) this
* requires drivers to subclass the state structure.
*
* Such driver-private properties should really only be implemented for
* truly hardware/vendor specific state. Instead it is preferred to
* standardize atomic extension and decode the properties used to expose
* such an extension in the core.
*
* Do not call this function directly, use
* drm_atomic_plane_set_property() instead.
*
* This callback is optional if the driver does not support any
* driver-private atomic properties.
*
* NOTE:
*
* This function is called in the state assembly phase of atomic
* modesets, which can be aborted for any reason (including on
* userspace's request to just check whether a configuration would be
* possible). Drivers MUST NOT touch any persistent state (hardware or
* software) or data structures except the passed in @state parameter.
*
* Also since userspace controls in which order properties are set this
* function must not do any input validation (since the state update is
* incomplete and hence likely inconsistent). Instead any such input
* validation must be done in the various atomic_check callbacks.
*
* RETURNS:
*
* 0 if the property has been found, -EINVAL if the property isn't
* implemented by the driver (which shouldn't ever happen, the core only
* asks for properties attached to this plane). No other validation is
* allowed by the driver. The core already checks that the property
* value is within the range (integer, valid enum value, ...) the driver
* set when registering the property.
*/
int (*atomic_set_property)(struct drm_plane *plane,
struct drm_plane_state *state,
struct drm_property *property,
uint64_t val);

/**
* @atomic_get_property:
*
* Reads out the decoded driver-private property. This is used to
* implement the GETPLANE IOCTL.
*
* Do not call this function directly, use
* drm_atomic_plane_get_property() instead.
*
* This callback is optional if the driver does not support any
* driver-private atomic properties.
*
* RETURNS:
*
* 0 on success, -EINVAL if the property isn't implemented by the
* driver (which should never happen, the core only asks for
* properties attached to this plane).
*/
int (*atomic_get_property)(struct drm_plane *plane,
const struct drm_plane_state *state,
struct drm_property *property,
uint64_t *val);
/**
* @late_register:
*
* This optional hook can be used to register additional userspace
* interfaces attached to the plane like debugfs interfaces.
* It is called late in the driver load sequence from drm_dev_register().
* Everything added from this callback should be unregistered in
* the early_unregister callback.
*
* Returns:
*
* 0 on success, or a negative error code on failure.
*/
int (*late_register)(struct drm_plane *plane);

/**
* @early_unregister:
*
* This optional hook should be used to unregister the additional
* userspace interfaces attached to the plane from
* @late_register. It is called from drm_dev_unregister(),
* early in the driver unload sequence to disable userspace access
* before data structures are torndown.
*/
void (*early_unregister)(struct drm_plane *plane);

/**
* @atomic_print_state:
*
* If driver subclasses &struct drm_plane_state, it should implement
* this optional hook for printing additional driver specific state.
*
* Do not call this directly, use drm_atomic_plane_print_state()
* instead.
*/
void (*atomic_print_state)(struct drm_printer *p,
const struct drm_plane_state *state);

/**
* @format_mod_supported:
*
* This optional hook is used for the DRM to determine if the given
* format/modifier combination is valid for the plane. This allows the
* DRM to generate the correct format bitmask (which formats apply to
* which modifier), and to validate modifiers at atomic_check time.
*
* If not present, then any modifier in the plane's modifier
* list is allowed with any of the plane's formats.
*
* Returns:
*
* True if the given modifier is valid for that format on the plane.
* False otherwise.
*/
bool (*format_mod_supported)(struct drm_plane *plane, uint32_t format,
uint64_t modifier);
};

这里定义了一大堆回调函数:

  • update_plane:这个函数已经过时了,为给定的CRTCframebuffer启用并配置plane
  • disable_plane:这个函数也过时了,用于禁用plane
  • destroy:用于清理plane的所有资源;在驱动卸载时调用drm_mode_config_cleanup函数时会被回调;
  • reset:用于将plane硬件和软件重置为关闭状态,这个函数不会直接调用,只会通过drm_mode_config_reset调用;
  • set_property:这个函数也过时了,用于更新planeproperty
  • atomic_duplicate_state:用于复制当前的plane状态并返回;
  • atomic_destroy_state:销毁使用atomic_duplicate_state复制的状态,并释放或取消引用其引用的所有资源;
  • atomic_set_property:原子操作用于设置planeproperty
  • atomic_get_property:原子操作用户获取planeproperty
  • late_register:可选的钩子函数,会被drm_dev_register函数调用,可以用来为plane注册额外的用户空间接口,比如debugfs
  • early_unregister:可选的钩子函数,会被drm_dev_unregister函数调用,可以用来为卸载在用户空间通过late_register注册的接口;
  • atomic_print_state:该函数需要通过drm_atomic_plane_print_state调用;
  • format_mod_supported:可选的钩子函数,用于确定给定的format/modifier组合是否对plane有效;
3.2.2 struct drm_plane_helper_funcs

struct drm_plane_helper_funcs定义了一些常用的plane助手操作函数,定义在include/drm/drm_modeset_helper_vtables.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/**
* struct drm_plane_helper_funcs - helper operations for planes
*
* These functions are used by the atomic helpers and by the transitional plane
* helpers.
*/
struct drm_plane_helper_funcs {
/**
* @prepare_fb:
*
* This hook is to prepare a framebuffer for scanout by e.g. pinning
* its backing storage or relocating it into a contiguous block of
* VRAM. Other possible preparatory work includes flushing caches.
*
* This function must not block for outstanding rendering, since it is
* called in the context of the atomic IOCTL even for async commits to
* be able to return any errors to userspace. Instead the recommended
* way is to fill out the &drm_plane_state.fence of the passed-in
* &drm_plane_state. If the driver doesn't support native fences then
* equivalent functionality should be implemented through private
* members in the plane structure.
*
* For GEM drivers who neither have a @prepare_fb nor @cleanup_fb hook
* set drm_gem_plane_helper_prepare_fb() is called automatically to
* implement this. Other drivers which need additional plane processing
* can call drm_gem_plane_helper_prepare_fb() from their @prepare_fb
* hook.
*
* The resources acquired in @prepare_fb persist after the end of
* the atomic commit. Resources that can be release at the commit's end
* should be acquired in @begin_fb_access and released in @end_fb_access.
* For example, a GEM buffer's pin operation belongs into @prepare_fb to
* keep the buffer pinned after the commit. But a vmap operation for
* shadow-plane helpers belongs into @begin_fb_access, so that atomic
* helpers remove the mapping at the end of the commit.
*
* The helpers will call @cleanup_fb with matching arguments for every
* successful call to this hook.
*
* This callback is used by the atomic modeset helpers and by the
* transitional plane helpers, but it is optional. See @begin_fb_access
* for preparing per-commit resources.
*
* RETURNS:
*
* 0 on success or one of the following negative error codes allowed by
* the &drm_mode_config_funcs.atomic_commit vfunc. When using helpers
* this callback is the only one which can fail an atomic commit,
* everything else must complete successfully.
*/
int (*prepare_fb)(struct drm_plane *plane,
struct drm_plane_state *new_state);
/**
* @cleanup_fb:
*
* This hook is called to clean up any resources allocated for the given
* framebuffer and plane configuration in @prepare_fb.
*
* This callback is used by the atomic modeset helpers and by the
* transitional plane helpers, but it is optional.
*/
void (*cleanup_fb)(struct drm_plane *plane,
struct drm_plane_state *old_state);

/**
* @begin_fb_access:
*
* This hook prepares the plane for access during an atomic commit.
* In contrast to @prepare_fb, resources acquired in @begin_fb_access,
* are released at the end of the atomic commit in @end_fb_access.
*
* For example, with shadow-plane helpers, the GEM buffer's vmap
* operation belongs into @begin_fb_access, so that the buffer's
* memory will be unmapped at the end of the commit in @end_fb_access.
* But a GEM buffer's pin operation belongs into @prepare_fb
* to keep the buffer pinned after the commit.
*
* The callback is used by the atomic modeset helpers, but it is optional.
* See @end_fb_cleanup for undoing the effects of @begin_fb_access and
* @prepare_fb for acquiring resources until the next pageflip.
*
* Returns:
* 0 on success, or a negative errno code otherwise.
*/
int (*begin_fb_access)(struct drm_plane *plane, struct drm_plane_state *new_plane_state);
/**
* @end_fb_access:
*
* This hook cleans up resources allocated by @begin_fb_access. It it called
* at the end of a commit for the new plane state.
*/
void (*end_fb_access)(struct drm_plane *plane, struct drm_plane_state *new_plane_state);

/**
* @atomic_check:
*
* Drivers should check plane specific constraints in this hook.
*
* When using drm_atomic_helper_check_planes() plane's @atomic_check
* hooks are called before the ones for CRTCs, which allows drivers to
* request shared resources that the CRTC controls here. For more
* complicated dependencies the driver can call the provided check helpers
* multiple times until the computed state has a final configuration and
* everything has been checked.
*
* This function is also allowed to inspect any other object's state and
* can add more state objects to the atomic commit if needed. Care must
* be taken though to ensure that state check and compute functions for
* these added states are all called, and derived state in other objects
* all updated. Again the recommendation is to just call check helpers
* until a maximal configuration is reached.
*
* This callback is used by the atomic modeset helpers and by the
* transitional plane helpers, but it is optional.
*
* NOTE:
*
* This function is called in the check phase of an atomic update. The
* driver is not allowed to change anything outside of the
* &drm_atomic_state update tracking structure.
*
* RETURNS:
*
* 0 on success, -EINVAL if the state or the transition can't be
* supported, -ENOMEM on memory allocation failure and -EDEADLK if an
* attempt to obtain another state object ran into a &drm_modeset_lock
* deadlock.
*/
int (*atomic_check)(struct drm_plane *plane,
struct drm_atomic_state *state);
/**
* @atomic_update:
*
* Drivers should use this function to update the plane state. This
* hook is called in-between the &drm_crtc_helper_funcs.atomic_begin and
* drm_crtc_helper_funcs.atomic_flush callbacks.
*
* Note that the power state of the display pipe when this function is
* called depends upon the exact helpers and calling sequence the driver
* has picked. See drm_atomic_helper_commit_planes() for a discussion of
* the tradeoffs and variants of plane commit helpers.
*
* This callback is used by the atomic modeset helpers and by the
* transitional plane helpers, but it is optional.
*/
void (*atomic_update)(struct drm_plane *plane,
struct drm_atomic_state *state);
/**
* @atomic_disable:
*
* Drivers should use this function to unconditionally disable a plane.
* This hook is called in-between the
* &drm_crtc_helper_funcs.atomic_begin and
* drm_crtc_helper_funcs.atomic_flush callbacks. It is an alternative to
* @atomic_update, which will be called for disabling planes, too, if
* the @atomic_disable hook isn't implemented.
*
* This hook is also useful to disable planes in preparation of a modeset,
* by calling drm_atomic_helper_disable_planes_on_crtc() from the
* &drm_crtc_helper_funcs.disable hook.
*
* Note that the power state of the display pipe when this function is
* called depends upon the exact helpers and calling sequence the driver
* has picked. See drm_atomic_helper_commit_planes() for a discussion of
* the tradeoffs and variants of plane commit helpers.
*
* This callback is used by the atomic modeset helpers and by the
* transitional plane helpers, but it is optional.
*/
void (*atomic_disable)(struct drm_plane *plane,
struct drm_atomic_state *state);
/**
* @atomic_async_check:
*
* Drivers should set this function pointer to check if the plane's
* atomic state can be updated in a async fashion. Here async means
* "not vblank synchronized".
*
* This hook is called by drm_atomic_async_check() to establish if a
* given update can be committed asynchronously, that is, if it can
* jump ahead of the state currently queued for update.
*
* RETURNS:
*
* Return 0 on success and any error returned indicates that the update
* can not be applied in asynchronous manner.
*/
int (*atomic_async_check)(struct drm_plane *plane,
struct drm_atomic_state *state);

/**
* @atomic_async_update:
*
* Drivers should set this function pointer to perform asynchronous
* updates of planes, that is, jump ahead of the currently queued
* state and update the plane. Here async means "not vblank
* synchronized".
*
* This hook is called by drm_atomic_helper_async_commit().
*
* An async update will happen on legacy cursor updates. An async
* update won't happen if there is an outstanding commit modifying
* the same plane.
*
* When doing async_update drivers shouldn't replace the
* &drm_plane_state but update the current one with the new plane
* configurations in the new plane_state.
*
* Drivers should also swap the framebuffers between current plane
* state (&drm_plane.state) and new_state.
* This is required since cleanup for async commits is performed on
* the new state, rather than old state like for traditional commits.
* Since we want to give up the reference on the current (old) fb
* instead of our brand new one, swap them in the driver during the
* async commit.
*
* FIXME:
* - It only works for single plane updates
* - Async Pageflips are not supported yet
* - Some hw might still scan out the old buffer until the next
* vblank, however we let go of the fb references as soon as
* we run this hook. For now drivers must implement their own workers
* for deferring if needed, until a common solution is created.
*/
void (*atomic_async_update)(struct drm_plane *plane,
struct drm_atomic_state *state);
};

这里定义了一大堆回调函数:

  • prepare_fb:准备fb,主要包括设置fb fence, 映射fb虚拟地址等;
  • cleanup_fb:清除在prepare_fb分配的给framebufferplane的资源;
  • begin_fb_access
  • end_fb_access
  • atomic_check:检查该挂钩中的plane特定约束;
  • atomic_update:更新plane状态;
  • atomic_disable:关闭;
  • atomic_async_check:异步检查;
  • atomic_async_update:异步更新状态;

3.3 关系图

为了更加清晰的了解struct drm_framebufferstruct drm_planestruct drm_plane_funcsstruct drm_plane_helper_funcs数据结构的关系,我们绘制了如下关系图:

四、核心API

4.1 plane初始化

drm_universal_plane_init函数用于初始化一个通用的plane对象,定义在drivers/gpu/drm/drm_plane.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* drm_universal_plane_init - Initialize a new universal plane object
* @dev: DRM device
* @plane: plane object to init
* @possible_crtcs: bitmask of possible CRTCs
* @funcs: callbacks for the new plane
* @formats: array of supported formats (DRM_FORMAT\_\*)
* @format_count: number of elements in @formats
* @format_modifiers: array of struct drm_format modifiers terminated by
* DRM_FORMAT_MOD_INVALID
* @type: type of plane (overlay, primary, cursor)
* @name: printf style format string for the plane name, or NULL for default name
*
* Initializes a plane object of type @type. The &drm_plane_funcs.destroy hook
* should call drm_plane_cleanup() and kfree() the plane structure. The plane
* structure should not be allocated with devm_kzalloc().
*
* Note: consider using drmm_universal_plane_alloc() instead of
* drm_universal_plane_init() to let the DRM managed resource infrastructure
* take care of cleanup and deallocation.
*
* Drivers that only support the DRM_FORMAT_MOD_LINEAR modifier support may set
* @format_modifiers to NULL. The plane will advertise the linear modifier.
*
* Returns:
* Zero on success, error code on failure.
*/
int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
uint32_t possible_crtcs,
const struct drm_plane_funcs *funcs,
const uint32_t *formats, unsigned int format_count,
const uint64_t *format_modifiers,
enum drm_plane_type type,
const char *name, ...)
{
va_list ap;
int ret;

WARN_ON(!funcs->destroy);

va_start(ap, name);
ret = __drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
formats, format_count, format_modifiers,
type, name, ap);
va_end(ap);
return ret;
}

函数接收以下参数:

  • devdrm设备;
  • plane:要初始化的plane对象;
  • possible_crtcs:可能的CRTCs的位掩码;
  • funcsplane的控制函数集合;
  • formats:支持的格式数组(DRM_FORMAT_*);
  • format_countformats数组的长度;
  • format_modifiers:以DRM_FORMAT_MOD_INVALID结尾的drm_format modifiers结构体数组;
  • typeplane的类型 (overlay, primary, cursor);
  • nameplane名字的格式化字符串,如果为NULL则使用默认名称;

具体实现位于函数__drm_universal_plane_init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
static int __drm_universal_plane_init(struct drm_device *dev,
struct drm_plane *plane,
uint32_t possible_crtcs,
const struct drm_plane_funcs *funcs,
const uint32_t *formats,
unsigned int format_count,
const uint64_t *format_modifiers,
enum drm_plane_type type,
const char *name, va_list ap)
{
// 获取显示模式配置
struct drm_mode_config *config = &dev->mode_config;
static const uint64_t default_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
};
unsigned int format_modifier_count = 0;
int ret;

/* plane index is used with 32bit bitmasks */
if (WARN_ON(config->num_total_plane >= 32))
return -EINVAL;

/*
* First driver to need more than 64 formats needs to fix this. Each
* format is encoded as a bit and the current code only supports a u64.
*/
if (WARN_ON(format_count > 64))
return -EINVAL;

WARN_ON(drm_drv_uses_atomic_modeset(dev) &&
(!funcs->atomic_destroy_state ||
!funcs->atomic_duplicate_state));

// 初始化drm_mode_object成员id、type
ret = drm_mode_object_add(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
if (ret)
return ret;

// 初始化互斥锁
drm_modeset_lock_init(&plane->mutex);

// 初始化drm plane成员
plane->base.properties = &plane->properties;
plane->dev = dev;
plane->funcs = funcs;
plane->format_types = kmalloc_array(format_count, sizeof(uint32_t),
GFP_KERNEL);
if (!plane->format_types) {
DRM_DEBUG_KMS("out of memory when allocating plane\n");
drm_mode_object_unregister(dev, &plane->base);
return -ENOMEM;
}

if (format_modifiers) {
const uint64_t *temp_modifiers = format_modifiers;

while (*temp_modifiers++ != DRM_FORMAT_MOD_INVALID)
format_modifier_count++;
} else {
if (!dev->mode_config.fb_modifiers_not_supported) {
format_modifiers = default_modifiers;
format_modifier_count = ARRAY_SIZE(default_modifiers);
}
}
/* autoset the cap and check for consistency across all planes */
drm_WARN_ON(dev, config->fb_modifiers_not_supported &&
format_modifier_count);

plane->modifier_count = format_modifier_count;
plane->modifiers = kmalloc_array(format_modifier_count,
sizeof(format_modifiers[0]),
GFP_KERNEL);

if (format_modifier_count && !plane->modifiers) {
DRM_DEBUG_KMS("out of memory when allocating plane\n");
kfree(plane->format_types);
drm_mode_object_unregister(dev, &plane->base);
return -ENOMEM;
}

if (name) {
plane->name = kvasprintf(GFP_KERNEL, name, ap);
} else {
plane->name = kasprintf(GFP_KERNEL, "plane-%d",
drm_num_planes(dev));
}
if (!plane->name) {
kfree(plane->format_types);
kfree(plane->modifiers);
drm_mode_object_unregister(dev, &plane->base);
return -ENOMEM;
}

memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
plane->format_count = format_count;
memcpy(plane->modifiers, format_modifiers,
format_modifier_count * sizeof(format_modifiers[0]));
plane->possible_crtcs = possible_crtcs;
plane->type = type;

// 将当前plane节点添加到链表config->plane_list中
list_add_tail(&plane->head, &config->plane_list);
// 初始化plane索引
plane->index = config->num_total_plane++;

// 附加属性到plane->base
drm_object_attach_property(&plane->base,
config->plane_type_property,
plane->type);

if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
drm_object_attach_property(&plane->base, config->prop_in_fence_fd, -1);
drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
drm_object_attach_property(&plane->base, config->prop_src_x, 0);
drm_object_attach_property(&plane->base, config->prop_src_y, 0);
drm_object_attach_property(&plane->base, config->prop_src_w, 0);
drm_object_attach_property(&plane->base, config->prop_src_h, 0);
}

if (format_modifier_count)
create_in_format_blob(dev, plane);

return 0;
}

这段代码进行了plane成员的初始化:

  • baseplane的基类,struct drm_mode_object类型;这里初始化成员idtype以及properties
  • dev:该plane所属的DRM设备;
  • funcplane控制函数;
  • format_typesplane支持的格式数组;
  • format_countformat_types数组的大小;
  • name:名称;
  • index:在mode_config.list中的位置,可用作数组索引;
  • type:plane的类型;
  • ……
4.1.1 drm_mode_object_add

drm_mode_object_add基于基数树为drm_mode_object分配一个唯一id,定义在drivers/gpu/drm/drm_mode_object.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*
* Internal function to assign a slot in the object idr and optionally
* register the object into the idr.
*/
int __drm_mode_object_add(struct drm_device *dev, struct drm_mode_object *obj,
uint32_t obj_type, bool register_obj,
void (*obj_free_cb)(struct kref *kref))
{
int ret;

WARN_ON(!dev->driver->load && dev->registered && !obj_free_cb);

// 获取互斥锁
mutex_lock(&dev->mode_config.idr_mutex);

// 这里为framebuffer分配一个唯一id,基于idr(redix树)
ret = idr_alloc(&dev->mode_config.object_idr, register_obj ? obj : NULL,
1, 0, GFP_KERNEL);
if (ret >= 0) {
/*
* Set up the object linking under the protection of the idr
* lock so that other users can't see inconsistent state.
*/
obj->id = ret; // 设置唯一标识符
obj->type = obj_type; // 设置对象的类型
if (obj_free_cb) {
obj->free_cb = obj_free_cb;
kref_init(&obj->refcount); // 初始化引用计数
}
}
mutex_unlock(&dev->mode_config.idr_mutex); // 释放互斥锁

return ret < 0 ? ret : 0;
}

/**
* drm_mode_object_add - allocate a new modeset identifier
* @dev: DRM device
* @obj: object pointer, used to generate unique ID
* @obj_type: object type
*
* Create a unique identifier based on @ptr in @dev's identifier space. Used
* for tracking modes, CRTCs and connectors.
*
* Returns:
* Zero on success, error code on failure.
*/
int drm_mode_object_add(struct drm_device *dev,
struct drm_mode_object *obj, uint32_t obj_type)
{
// 第四个参数传入的是true
return __drm_mode_object_add(dev, obj, obj_type, true, NULL);
}

dev->mode_config.object_idr树中为该obj(这里是drm_framebuffer)分配唯一节点,并返回分配的节点id:由于register_obj=true,因此将这个节点与obj关联;通过id就可以定位到obj

1
2
ret = idr_alloc(&dev->mode_config.object_idr, register_obj ? obj : NULL,
1, 0, GFP_KERNEL);
4.1.2 drm_object_attach_property

drm_object_attach_property用于附加属性到drm_mode_object,定义在drivers/gpu/drm/drm_mode_object.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* drm_object_attach_property - attach a property to a modeset object
* @obj: drm modeset object
* @property: property to attach
* @init_val: initial value of the property
*
* This attaches the given property to the modeset object with the given initial
* value. Currently this function cannot fail since the properties are stored in
* a statically sized array.
*
* Note that all properties must be attached before the object itself is
* registered and accessible from userspace.
*/
void drm_object_attach_property(struct drm_mode_object *obj, // 需要附加属性的modeset object
struct drm_property *property, // 需要附加的属性
uint64_t init_val) // 属性值
{
// 获取属性个数
int count = obj->properties->count;
// 获取drm设备
struct drm_device *dev = property->dev;


if (obj->type == DRM_MODE_OBJECT_CONNECTOR) { // 如果是connector类型
struct drm_connector *connector = obj_to_connector(obj);

WARN_ON(!dev->driver->load &&
connector->registration_state == DRM_CONNECTOR_REGISTERED);
} else {
WARN_ON(!dev->driver->load && dev->registered);
}

if (count == DRM_OBJECT_MAX_PROPERTY) {
WARN(1, "Failed to attach object property (type: 0x%x). Please "
"increase DRM_OBJECT_MAX_PROPERTY by 1 for each time "
"you see this message on the same object type.\n",
obj->type);
return;
}

// 保存对象属性
obj->properties->properties[count] = property;
// 属性值
obj->properties->values[count] = init_val;
// 属性计数+1
obj->properties->count++;
}
4.1.3 drm_plane_helper_add

drm_plane_helper_add函数用于设置plane的辅助函数helper_private,定义在include/drm/drm_modeset_helper_vtables.h

1
2
3
4
5
6
7
8
9
10
/**
* drm_plane_helper_add - sets the helper vtable for a plane
* @plane: DRM plane
* @funcs: helper vtable to set for @plane
*/
static inline void drm_plane_helper_add(struct drm_plane *plane,
const struct drm_plane_helper_funcs *funcs)
{
plane->helper_private = funcs;
}

4.2 framebuffer初始化

drm_framebuffer_init函数用于初始化framebuffer对象,定义在drivers/gpu/drm/drm_framebuffer.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* drm_framebuffer_init - initialize a framebuffer
* @dev: DRM device
* @fb: framebuffer to be initialized
* @funcs: ... with these functions
*
* Allocates an ID for the framebuffer's parent mode object, sets its mode
* functions & device file and adds it to the master fd list.
*
* IMPORTANT:
* This functions publishes the fb and makes it available for concurrent access
* by other users. Which means by this point the fb _must_ be fully set up -
* since all the fb attributes are invariant over its lifetime, no further
* locking but only correct reference counting is required.
*
* Returns:
* Zero on success, error code on failure.
*/
int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
const struct drm_framebuffer_funcs *funcs)
{
int ret;

if (WARN_ON_ONCE(fb->dev != dev || !fb->format))
return -EINVAL;

INIT_LIST_HEAD(&fb->filp_head);

// 设置funcs
fb->funcs = funcs;
strcpy(fb->comm, current->comm);

// 基于基数树为drm_mode_object分配一个唯一id,需要注意第四个参数传入的为false
ret = __drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB,
false, drm_framebuffer_free);
if (ret)
goto out;

mutex_lock(&dev->mode_config.fb_lock);
// 计数
dev->mode_config.num_fb++;
// 将当前framebuffer添加到链表mode_config.fb_list
list_add(&fb->head, &dev->mode_config.fb_list);
mutex_unlock(&dev->mode_config.fb_lock);

// 将obj注册到mode_config.object_idr树中,索引为fb->base.id
drm_mode_object_register(dev, &fb->base);
out:
return ret;
}

drm_mode_object_register定义在drivers/gpu/drm/drm_mode_object.c

1
2
3
4
5
6
7
8
void drm_mode_object_register(struct drm_device *dev,
struct drm_mode_object *obj)
{
mutex_lock(&dev->mode_config.idr_mutex);
// 将obj注册到mode_config.object_idr,替换原有的obj(如果有的话)
idr_replace(&dev->mode_config.object_idr, obj, obj->id);
mutex_unlock(&dev->mode_config.idr_mutex);
}

参考文章

[1] linux驱动系列学习之DRM(十)

[2] DRM框架(vkms)分析(6)—- plane初始化

[3] linux gem initialization

[4] DRM 驱动mmap详解:(二)CMA Helper

[5] DRM驱动(一)之显示处理器介绍

[6] DRM驱动(二)之DRM驱动模块简介