Rockchip RK3399 - DRM framebuffer、plane基础知识
----------------------------------------------------------------------------------------------------------------------------
开发板 :NanoPC-T4
开发板
eMMC
:16GB
LPDDR3
:4GB
显示屏 :15.6
英寸HDMI
接口显示屏
u-boot
:2023.04
linux
:6.3
----------------------------------------------------------------------------------------------------------------------------
我们在文章《Rockchip RK3399 - DRM
子系统》和《Rockchip RK3399 - DRM
驱动程序》中介绍了RM子系统的整体框架,并对DRM
各个模块进行了简单的介绍,本节我们将会详细介绍framebuffer
、plane
。
一、显示处理器
我们知道一个显示子系统由显示处理器(vop
,video output processor
)、接口控制器(mipi
,lvds
,hdmi
、edp
、dp
、rgb
、BT1120
、BT656
、I8080
(MCU
显示接口)等)、液晶背光,电源等多个独立的功能模块构成。
其中显示处理器格外重要,那么问题来了,什么是显示处理器?
- 将在内存中的图像数据,转化为电信号送到显示设备称为显示控制器,比如早期的
LCDC
; - 后面进行了拓展,可以处理一些简单的图像,比如缩放、旋转、合成等,如瑞芯的
vop
,高通的sde
称为显示处理器;
1.1 功能介绍
显示处理器可以在没有CPU
参与的情况下可以做一些简单的图像处理,比如:
- 缩放,旋转等操作;
- 支持多层,并可以进行合成,支持
porter-duff
; - 支持多种显存格式(
ARGB888
,RGB565
, 包括GPU
输出的tile
格式以及fbdc
压缩格式)等; - 支持生成时序信号如
tcon
,送给如mipi
、lvds
等接口; - 支持多种分辨率;
这里我们为什么要介绍显示处理器的功能呢?这主要是因为DRM
中的plane
、crtc
等功能模块就是对显示处理器的抽象;
plane
对应显示处理器中的layer
;crtc
对应显示处理器中的图层合成以及时序的生成的模块;
各家芯片厂商显示处理器实现各有不同,但均有如下两个重要的部件:多个layer
、负责图层合成的模块以及输出接口,整个处理流程可以称为display pipelane
;
1.2 layer
layer
是一个图层,可以理解为里面放了一张图片,如下图。实际放在各个layer
的是以某种格式填充在memory
里的图像数据。
layer
需要配置的信息包括但不限于:图像format
、width x height
、stride
、显存地址等;如果支持缩放还需要原图像大小以及要缩放大小等。现在很多芯片都支持多layer
,如RK3399
的vop
可支持4个layer
,RK3568
的vop
可支持6个layer
。
1.3 合成
主要用于将各个layer
送来的图像进行合成为一层送出显示,例如将上面三层合成如下效果。
可以理解为在图像合成模块里有个画布,把每层按照一定次序和规则画上去。合成如上效果需要配置那些参数呢?
- 每个
layer
在画布的位置(x, y); - 每个
layer
的zorder
,也就是哪个layer
在上面,哪个layer
在下面; - 最终合成图像的大小;
- 按照哪种方式合成(
porter-duff
);
二 framebuffer
数据结构
在DRM
框架中,framebuffer
用于存储需要显示的内容,存储的信息包括:
-
需要显示的内容在内存区域的引用;
-
存储在内存中的帧的格式;
-
内存区域的激活区域(即将要显示的内容);
DRM Framebuffer
是一个虚拟的对象,它依赖于特定的实现。framebuffer
实现依赖于:
- 依赖于底层内存管理器来分配后备存储,比如
GEM
、TTM
; - 依赖于显示处理器的能力:
- 支持
DMA
传输类型(可以是连续内存或者Scatter Gather
); IOMMU
;
- 支持
2.1 struct drm_framebuffer
linux
内核使用struct drm_framebuffer
表示一个framebuffer
,包括像素格式、分辨率和显存地址等信息。定义在include/drm/drm_framebuffer.h
;
/**
* 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
类型;comn
:char
数组,数组长度为32,用于存放分配该fb
的进程的名字;format
:framebuffer
的格式信息;funcs
:framebuffer
的函数表;pitches
:无符号int数组,数组长度为4,存放每个缓冲区的行跨度(以字节为单位);offsets
:无符号int数组,数组长度为4,存放每个缓冲区中实际像素数据相对于缓冲区起始处的偏移量(以字节为单位);modifier
:数据布局修饰符,用于描述缓冲区的平铺或特殊布局;width
:framebuffer
可见区域的逻辑宽度(以像素为单位);height
:framebuffer
可见区域的逻辑高度(以像素为单位);flags
:framebuffer
的标志,例如DRM_MODE_FB_INTERLACED
或DRM_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
进行保护;obj
:struct drm_gem_object
类型指针数组,数组长度为4,备份framebuffer
的GEM
对象,每个plane
一个对象;
struct drm_framebuffer
主要元素的展示如下图所示,其中Memory Region
表示帧缓冲区,Dsisplayed Area
表示实际显示区域,图中棕色区域表示实际像素数据相对于framebuffer
起始处的偏移量;
这里需要介绍一下pitches
、offsets
为啥是个数组,数组的长度为DRM_FORMAT_MAX_PLANES
,通过这个宏我们大概可以了解到每种DRM
格式应该包含多个color plane
,因此数组元素的值依次对应每个color plane
;比如RGB
格式由R
、G
、B
三种颜色图层组成。
2.2 struct drm_format_info
linux
内核使用struct drm_format_info
表示DRM
格式信息,定义在include/drm/drm_fourcc.h
;
/**
* 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;
};
它包括以下字段:
format
:4CC
格式标识符(DRM_FORMAT_*
);depth
:颜色深度(每像素的位数,不包括填充位),仅适用于部分RGB
格式。这是一个过时字段,在新代码中不要使用,并且对于新格式设置为0。num_planes
:color plane
数量(1到3)。pp
:数组长度为4,依次存储每个plane
每像素的字节数,与char_per_block
具有别名。对于更好地描述像素格式,已弃用此字段,推荐使用三元组char_per_block
、block_w
、block_h
;char_per_block
:数组长度为4,依次存储每个块的字节数,块被定义为以字节对齐的内存区域中相邻存储的像素矩形。与block_w
和block_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
:
/**
* 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
:
/**
* 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
:创建驱动程序特定缓冲区管理器(GEM
或TTM
)中与给定文件对象drm_file
相关联的缓冲区句柄;dirty
:选的回调函数,用于处理dirty fb IOCTL
;
2.4 关系图
为了更加清晰的了解struct drm_framebuffer
、struct drm_format_info
、struct drm_framebuffer_func
数据结构的关系,我们绘制了如下关系图:
三、plane
数据结构
DRM
子系统中的plane
对应显示处理器的layer
,负责配置layer
的一些属性:format
、width
、height
、stride
、显存地址等;
一个crtc
可以接收多个plane
的输入,如下图所示:
其中:
Background Framebuffer
为DRM_PLANE_TYPE_PRIMARY
类型的plane
;Overplay Framerbuffer
为DRM_PLANE_TYPE_OVERLAY
类型的plane
;Cursor Framerbuffer
为DRM_PLANE_TYPE_CURSOR
类型的plane
;
3.1 struct drm_plane
linux
内核使用struct drm_plane
表示一个plane
,它们从一个drm_framebuffer
接收输入数据,并将其传递给一个drm_crtc
。
struct drm_plane
成员中包含了struct drm_framebuffer *fb
和struct drm_crtc *crtc
,定义include/drm/drm_plane.h
;
/**
* 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_crtcs
:plane
可以绑定到的管道,由drm_crtc_mask()
构建;format_types
:plane
支持的格式数组;format_count
:format_types
数组的大小;format_default
:驱动程序没有为该plane
提供支持的格式。仅由non-atomic driver
兼容性包装器使用;modifiers
:该plane
支持的修饰符数组;modifier_count
:modifiers
数组的大小。crtc
:当前绑定的CRTC
,仅对non-atomic driver
有意义。对于atomic driver
,它被强制为NULL
,atomic driver
应该检查drm_plane_state.crtc
;fb
:当前绑定的framebuffer
,仅对non-atomic driver
有意义。对于atomic driver
,它被强制为NULL
,atomic driver
应该检查drm_plane_state.fb
;old_fb
:在模式设置进行中临时跟踪旧的帧缓冲。仅由non-atomic driver
使用,对于atomic driver
,它被强制为NULL
;funcs
:plane
控制函数;properties
:用于跟踪plane
的属性。type
:plane
的类型,详见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
;
/**
* 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
;
/**
* 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_x
、src_y
、src_h
和src_w
指定framebuffer
的源区域,crtc_x
、crtc_y
、crtc_h
和crtc_w
指定其显示在crtc
上的目标区域。关系如下图所示;
3.2 操作函数
3.2.1 struct drm_plane_funcs
struct drm_plane_funcs
用于描述plane
的控制函数,定义在include/drm/drm_plane.h
;
/**
* 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
:这个函数已经过时了,为给定的CRTC
和framebuffer
启用并配置plane
;disable_plane
:这个函数也过时了,用于禁用plane
;destroy
:用于清理plane
的所有资源;在驱动卸载时调用drm_mode_config_cleanup
函数时会被回调;reset
:用于将plane
硬件和软件重置为关闭状态,这个函数不会直接调用,只会通过drm_mode_config_reset
调用;set_property
:这个函数也过时了,用于更新plane
的property
;atomic_duplicate_state
:用于复制当前的plane
状态并返回;atomic_destroy_state
:销毁使用atomic_duplicate_state
复制的状态,并释放或取消引用其引用的所有资源;atomic_set_property
:原子操作用于设置plane
的property
;atomic_get_property
:原子操作用户获取plane
的property
;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
;
/**
* 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
分配的给framebuffer
和plane
的资源;begin_fb_access
:end_fb_access
:atomic_check
:检查该挂钩中的plane
特定约束;atomic_update
:更新plane
状态;atomic_disable
:关闭;atomic_async_check
:异步检查;atomic_async_update
:异步更新状态;
3.3 关系图
为了更加清晰的了解struct drm_framebuffer
、struct drm_plane
、struct drm_plane_funcs
、struct drm_plane_helper_funcs
数据结构的关系,我们绘制了如下关系图:
四、核心API
4.1 plane
初始化
drm_universal_plane_init
函数用于初始化一个通用的plane
对象,定义在drivers/gpu/drm/drm_plane.c
:
/**
* 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;
}
函数接收以下参数:
dev
:drm
设备;plane
:要初始化的plane
对象;possible_crtcs
:可能的CRTCs
的位掩码;funcs
:plane
的控制函数集合;formats
:支持的格式数组(DRM_FORMAT_*
);format_count
:formats
数组的长度;format_modifiers
:以DRM_FORMAT_MOD_INVALID
结尾的drm_format modifiers
结构体数组;type
:plane
的类型 (overlay
,primary,
cursor
);name
:plane
名字的格式化字符串,如果为NULL
则使用默认名称;
具体实现位于函数__drm_universal_plane_init
:
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
成员的初始化:
base
:plane
的基类,struct drm_mode_object
类型;这里初始化成员id
、type
以及properties
;dev
:该plane
所属的DRM
设备;func
:plane
控制函数;format_types
:plane
支持的格式数组;format_count
:format_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
:
/*
* 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
;
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
:
/**
* 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
;
/**
* 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
;
/**
* 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
:
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);
}
参考文章
[2] DRM
框架(vkms
)分析(6)---- plane
初始化
[4] DRM
驱动mmap
详解:(二)CMA Helper
[5] DRM
驱动(一)之显示处理器介绍