程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

Rockchip RK3399 - DRM framebuffer、plane基础知识

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

开发板 :NanoPC-T4开发板
eMMC16GB
LPDDR34GB
显示屏 :15.6英寸HDMI接口显示屏
u-boot2023.04
linux6.3
----------------------------------------------------------------------------------------------------------------------------

我们在文章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

/**
 * 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;

/**
 * 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

/**
 * 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:创建驱动程序特定缓冲区管理器(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

/**
 * 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

/**
 * 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_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

/**
 * 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

/**
 * 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

/**
 * 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

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

/*
 * 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);
}

参考文章

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

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

[3] linux gem initialization

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

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

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

posted @ 2023-10-19 22:34  大奥特曼打小怪兽  阅读(687)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步