Rockchip RK3399 - DRM crtc基础知识

一、LCD硬件原理

1.1 CRT介绍

CRT是阴极射线管(Cathode Ray Tube)的缩写,它是一种使用电子束在荧光屏上创建图像的显示设备。CRT显示器在过去很长一段时间内是主流的显示技术,现已被液晶显示屏或其他新兴技术所替代。

CRT显示器中,扫描电子束从左到右、从上到下移动,照亮屏幕上的荧光点,从而创建图像。电子束每秒多次扫描整个屏幕,产生闪烁效果,需要与正在显示的内容同步。

随着LCDLEDOLED等新型显示技术的出现,底层原理已经发生了变化。这些显示器不再使用扫描电子束,而是使用可以单独控制发光或阻挡光的像素矩阵。这使得图像更加清晰,刷新率更快,能源效率也得到了提高。

然而,刷新显示的基本概念并没有发生很大变化。显示内容仍然通过向每个像素发送信号来控制其亮度和颜色来进行更新。这些信号由显示控制器根据输入视频信号生成,并且它们确定屏幕上显示的内容。

1.2 LCD示意图

下图是LCD示意图,里面的每个点就是一个像素点。它里面有一个电子枪,一边移动,一边发出各种颜色的光。用动态图表示如下:

电子枪是如何移动的?

  • 有一条Pixel Clock时钟线与LCD相连,每发出一次Pixel Clock,电子枪就移动一个像素。

颜色如何确定?

  • 由连接LCD的三组线RGB三原色混合而成:R(Red)、G(Green)、B(Blue)确定。

电子枪如何得知应跳到下一行?

  • 有一条HSYNC信号线与LCD相连,每发出一次脉冲(高低电平),电子枪就跳到下一行,该信号叫做行同步信号。

电子枪如何得知应跳到原点?

  • 有一条VSYNC信号线与LCD相连,每发出一次脉冲(高低电平),电子枪就跳到原点,该信号叫做帧同步信号。

RGB线上的数据从何而来?

  • 内存里面划分一块显存(FrameBuffer),里面存放了要显示的数据,LCD控制器(比如RK3399VOP)从里面将数据读出来,通过显示接口(比如mipilvdshdmiedp、``dp)传给电子枪,电子枪再依次打到显示屏上。

因此要想在显示设备上显示图像,至少需要3个信号:

  • Pixel Clock:时钟信号,在每个时钟周期更新屏幕上一个像素;
  • HSYNC:水平同步信号,引脚每发出一个脉冲,表示一行的数据开始发送;
  • VSYNC:垂直同步信号,引脚每发出一个脉冲,表示一帧的数据开始发送。
1.3 时序参数


在上图中我们发现除了Pixel ClockHSYNCVSYNC信号外,还包含了大量的时序参数,这里我们一一介绍:

  • HBPD(hback porch):行同步信号的后肩,单位为1个Pixel Clock时钟周期;
  • HFPD(hfront porch):行同步信号的前肩,单位为1个Pixel Clock时钟周期;
  • HSPW(hsync pulse):行同步信号的脉宽,单位为1个Pixel Clock时钟周期;
  • HOZVAL(hdisplay):LCD的水平宽度;
  • VBPD(vback porch):帧同步信号的后肩,单位为1个HSYNC时钟周期;
  • VFPD(vfront porch):帧同步信号的前肩,单位为1个HSYNC时钟周期;
  • VSPW(vsync pulse):帧同步信号的脉宽,单位为1个HSYNC时钟周期;
  • LINEVAL (vdisplay):LCD的垂直宽度;

如果这部分内容看不到,可以参考我之前写的这篇文章:Mini2440裸机开发之LCD基础

二、CRTC核心数据结构

DRM框架中,CRTCFramebuffer中读取待显示的图像,并按照响应的格式输出给encoder,其主要承担的作用为:

  • 配置适合显示的显示模式、分辨率、刷新率等参数,并输出相应的时序;
  • 扫描Framebuffer发送到一个或多个显示器;
  • 更新Framebuffer

概括下就是,对显示器进行扫描,产生时序信号的模块、负责帧切换、电源控制、色彩调整等等。

2.1 struct drm_crtc

linux内核使用struct drm_crtc来表示一个CRTC控制器,包括显示模式、分辨率、刷新率等参数。定义在include/drm/drm_crtc.h

c 复制代码
/**
 * struct drm_crtc - central CRTC control structure
 *
 * Each CRTC may have one or more connectors associated with it.  This structure
 * allows the CRTC to be controlled.
 */
struct drm_crtc {
        /** @dev: parent DRM device */
        struct drm_device *dev;
        /** @port: OF node used by drm_of_find_possible_crtcs(). */
        struct device_node *port;
        /**
         * @head:
         *
         * List of all CRTCs on @dev, linked from &drm_mode_config.crtc_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:
         *
         * This provides a read lock for the overall CRTC state (mode, dpms
         * state, ...) and a write lock for everything which can be update
         * without a full modeset (fb, cursor data, CRTC properties ...). A full
         * modeset also need to grab &drm_mode_config.connection_mutex.
         *
         * For atomic drivers specifically this protects @state.
         */
        struct drm_modeset_lock mutex;

        /** @base: base KMS object for ID tracking etc. */
        struct drm_mode_object base;

        /**
         * @primary:
         * Primary plane for this CRTC. Note that this is only
         * relevant for legacy IOCTL, it specifies the plane implicitly used by
         * the SETCRTC and PAGE_FLIP IOCTLs. It does not have any significance
         * beyond that.
         */
        struct drm_plane *primary;

        /**
         * @cursor:
         * Cursor plane for this CRTC. Note that this is only relevant for
         * legacy IOCTL, it specifies the plane implicitly used by the SETCURSOR
         * and SETCURSOR2 IOCTLs. It does not have any significance
         * beyond that.
         */
        struct drm_plane *cursor;
        /**
         * @index: Position inside the mode_config.list, can be used as an array
         * index. It is invariant over the lifetime of the CRTC.
         */
        unsigned index;

        /**
         * @cursor_x: Current x position of the cursor, used for universal
         * cursor planes because the SETCURSOR IOCTL only can update the
         * framebuffer without supplying the coordinates. Drivers should not use
         * this directly, atomic drivers should look at &drm_plane_state.crtc_x
         * of the cursor plane instead.
         */
        int cursor_x;
        /**
         * @cursor_y: Current y position of the cursor, used for universal
         * cursor planes because the SETCURSOR IOCTL only can update the
         * framebuffer without supplying the coordinates. Drivers should not use
         * this directly, atomic drivers should look at &drm_plane_state.crtc_y
         * of the cursor plane instead.
         */
        int cursor_y;

        /**
         * @enabled:
         *
         * Is this CRTC enabled? Should only be used by legacy drivers, atomic
         * drivers should instead consult &drm_crtc_state.enable and
         * &drm_crtc_state.active. Atomic drivers can update this by calling
         * drm_atomic_helper_update_legacy_modeset_state().
         */
        bool enabled;

        /**
         * @mode:
         *
         * Current mode timings. Should only be used by legacy drivers, atomic
         * drivers should instead consult &drm_crtc_state.mode. Atomic drivers
         * can update this by calling
         * drm_atomic_helper_update_legacy_modeset_state().
         */
        struct drm_display_mode mode;
        /**
         * @hwmode:
         *
         * Programmed mode in hw, after adjustments for encoders, crtc, panel
         * scaling etc. Should only be used by legacy drivers, for high
         * precision vblank timestamps in
         * drm_crtc_vblank_helper_get_vblank_timestamp().
         *
         * Note that atomic drivers should not use this, but instead use
         * &drm_crtc_state.adjusted_mode. And for high-precision timestamps
         * drm_crtc_vblank_helper_get_vblank_timestamp() used
         * &drm_vblank_crtc.hwmode,
         * which is filled out by calling drm_calc_timestamping_constants().
         */
        struct drm_display_mode hwmode;

        /**
         * @x:
         * x position on screen. Should only be used by legacy drivers, atomic
         * drivers should look at &drm_plane_state.crtc_x of the primary plane
         * instead. Updated by calling
         * drm_atomic_helper_update_legacy_modeset_state().
         */
        int x;
        /**
         * @y:
         * y position on screen. Should only be used by legacy drivers, atomic
         * drivers should look at &drm_plane_state.crtc_y of the primary plane
         * instead. Updated by calling
         * drm_atomic_helper_update_legacy_modeset_state().
         */
        int y;

        /** @funcs: CRTC control functions */
        const struct drm_crtc_funcs *funcs;

        /**
         * @gamma_size: Size of legacy gamma ramp reported to userspace. Set up
         * by calling drm_mode_crtc_set_gamma_size().
         *
         * Note that atomic drivers need to instead use
         * &drm_crtc_state.gamma_lut. See drm_crtc_enable_color_mgmt().
         */
        uint32_t gamma_size;

        /**
         * @gamma_store: Gamma ramp values used by the legacy SETGAMMA and
         * GETGAMMA IOCTls. Set up by calling drm_mode_crtc_set_gamma_size().
         *
         * Note that atomic drivers need to instead use
         * &drm_crtc_state.gamma_lut. See drm_crtc_enable_color_mgmt().
         */
        uint16_t *gamma_store;
        /** @helper_private: mid-layer private data */
        const struct drm_crtc_helper_funcs *helper_private;

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

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

        /**
         * @state:
         *
         * Current atomic state for this CRTC.
         *
         * This is protected by @mutex. Note that nonblocking atomic commits
         * access the current CRTC state without taking locks. Either by going
         * through the &struct drm_atomic_state pointers, see
         * for_each_oldnew_crtc_in_state(), for_each_old_crtc_in_state() and
         * for_each_new_crtc_in_state(). Or through careful ordering of atomic
         * commit operations as implemented in the atomic helpers, see
         * &struct drm_crtc_commit.
         */
        struct drm_crtc_state *state;

        /**
         * @commit_list:
         *
         * List of &drm_crtc_commit structures tracking pending commits.
         * Protected by @commit_lock. This list holds its own full reference,
         * as does the ongoing commit.
         *
         * "Note that the commit for a state change is also tracked in
         * &drm_crtc_state.commit. For accessing the immediately preceding
         * commit in an atomic update it is recommended to just use that
         * pointer in the old CRTC state, since accessing that doesn't need
         * any locking or list-walking. @commit_list should only be used to
         * stall for framebuffer cleanup that's signalled through
         * &drm_crtc_commit.cleanup_done."
         */
        struct list_head commit_list;

        /**
         * @commit_lock:
         *
         * Spinlock to protect @commit_list.
         */
        spinlock_t commit_lock;
        /**
         * @debugfs_entry:
         *
         * Debugfs directory for this CRTC.
         */
        struct dentry *debugfs_entry;

        /**
         * @crc:
         *
         * Configuration settings of CRC capture.
         */
        struct drm_crtc_crc crc;

        /**
         * @fence_context:
         *
         * timeline context used for fence operations.
         */
        unsigned int fence_context;

        /**
         * @fence_lock:
         *
         * spinlock to protect the fences in the fence_context.
         */
        spinlock_t fence_lock;
        /**
         * @fence_seqno:
         *
         * Seqno variable used as monotonic counter for the fences
         * created on the CRTC's timeline.
         */
        unsigned long fence_seqno;

        /**
         * @timeline_name:
         *
         * The name of the CRTC's fence timeline.
         */
        char timeline_name[32];

        /**
         * @self_refresh_data: Holds the state for the self refresh helpers
         *
         * Initialized via drm_self_refresh_helper_init().
         */
        struct drm_self_refresh_data *self_refresh_data;
};

该结构体包含以下成员变量:

  • dev:该CRTC所属的DRM设备;
  • port:设备节点,被drm_of_find_possible_crtcs()使用;
  • head:链表节点,用于将当前节点添加到&drm_mode_config.crtc_list链表;
  • name:名称,可以被驱动程序覆盖;
  • mutex:用于保护CRTC状态的互斥锁;
  • basestruct drm_mode_object
  • primary:与该CRTC相关的primary plane
  • cursor:与该CRTC相关的cursor plane
  • index:在mode_config.list中的位置,可以用作数组索引;
  • cursor_x:光标的x坐标;
  • cursor_y:光标的y坐标;
  • enabled:指示该CRTC是否已启用;
  • mode:当前模式时序信息;
  • hwmode:硬件中编程的模式;
  • x:在屏幕上的x坐标;
  • y:在屏幕上的y坐标;
  • funcsCRTC控制函数;
  • gamma_size:报告给用户空间的legacy gamma ramp大小;
  • gamma_storelegacy gamma values数据存储区域;
  • helper_private:中间层私有数据;
  • properties:跟踪该CRTC的属性;
  • scaling_filter_property:应用于缩放时的特定滤镜的属性;
  • state:当前CRTC的原子状态;
  • commit_list:跟踪挂起提交的列表;
  • commit_lock:保护commit_list的自旋锁;
  • debugfs_entry:用于调试的目录条目;
  • crcCRC捕获的配置设置;
  • fence_context:用于围栏操作的时间线上下文;
  • fence_lock:保护时间线上的围栏的自旋锁;
  • fence_seqno:用作CRTC时间线上创建的围栏的单调计数器;
  • timeline_nameCRTC时间线的名称;
  • self_refresh_data:保存自刷新辅助程序状态的数据;
2.1.1 struct drm_display_mode

struct drm_display_mode用于表示显示模式,包含了显示的何种时序参数。定义在include/drm/drm_modes.h

c 复制代码
/**
 * struct drm_display_mode - DRM kernel-internal display mode structure
 * @hdisplay: horizontal display size
 * @hsync_start: horizontal sync start
 * @hsync_end: horizontal sync end
 * @htotal: horizontal total size
 * @hskew: horizontal skew?!
 * @vdisplay: vertical display size
 * @vsync_start: vertical sync start
 * @vsync_end: vertical sync end
 * @vtotal: vertical total size
 * @vscan: vertical scan?!
 * @crtc_hdisplay: hardware mode horizontal display size
 * @crtc_hblank_start: hardware mode horizontal blank start
 * @crtc_hblank_end: hardware mode horizontal blank end
 * @crtc_hsync_start: hardware mode horizontal sync start
 * @crtc_hsync_end: hardware mode horizontal sync end
 * @crtc_htotal: hardware mode horizontal total size
 * @crtc_hskew: hardware mode horizontal skew?!
 * @crtc_vdisplay: hardware mode vertical display size
 * @crtc_vblank_start: hardware mode vertical blank start
 * @crtc_vblank_end: hardware mode vertical blank end
 * @crtc_vsync_start: hardware mode vertical sync start
 * @crtc_vsync_end: hardware mode vertical sync end
 * @crtc_vtotal: hardware mode vertical total size
 *
 * This is the kernel API display mode information structure. For the
 * user-space version see struct drm_mode_modeinfo.
 *
 * The horizontal and vertical timings are defined per the following diagram.
 *
 * ::
 *
 *
 *               Active                 Front           Sync           Back
 *              Region                 Porch                          Porch
 *     <-----------------------><----------------><-------------><-------------->
 *       //|
 *      // |
 *     //  |..................               ................
 *                                                _______________
 *     <----- [hv]display ----->
 *     <------------- [hv]sync_start ------------>
 *     <--------------------- [hv]sync_end --------------------->
 *     <-------------------------------- [hv]total ----------------------------->*
 *
 * This structure contains two copies of timings. First are the plain timings,
 * which specify the logical mode, as it would be for a progressive 1:1 scanout
 * at the refresh rate userspace can observe through vblank timestamps. Then
 * there's the hardware timings, which are corrected for interlacing,
 * double-clocking and similar things. They are provided as a convenience, and
 * can be appropriately computed using drm_mode_set_crtcinfo().
 *
 * For printing you can use %DRM_MODE_FMT and DRM_MODE_ARG().
 */
struct drm_display_mode {
        /**
         * @clock:
         *
         * Pixel clock in kHz.
         */
        int clock;              /* in kHz */
        u16 hdisplay;
        u16 hsync_start;
        u16 hsync_end;
        u16 htotal;
        u16 hskew;
        u16 vdisplay;
        u16 vsync_start;
        u16 vsync_end;
        u16 vtotal;
        u16 vscan;
        /**
         * @flags:
         *
         * Sync and timing flags:
         *
         *  - DRM_MODE_FLAG_PHSYNC: horizontal sync is active high.
         *  - DRM_MODE_FLAG_NHSYNC: horizontal sync is active low.
         *  - DRM_MODE_FLAG_PVSYNC: vertical sync is active high.
         *  - DRM_MODE_FLAG_NVSYNC: vertical sync is active low.
         *  - DRM_MODE_FLAG_INTERLACE: mode is interlaced.
         *  - DRM_MODE_FLAG_DBLSCAN: mode uses doublescan.
         *  - DRM_MODE_FLAG_CSYNC: mode uses composite sync.
         *  - DRM_MODE_FLAG_PCSYNC: composite sync is active high.
         *  - DRM_MODE_FLAG_NCSYNC: composite sync is active low.
         *  - DRM_MODE_FLAG_HSKEW: hskew provided (not used?).
         *  - DRM_MODE_FLAG_BCAST: <deprecated>
         *  - DRM_MODE_FLAG_PIXMUX: <deprecated>
         *  - DRM_MODE_FLAG_DBLCLK: double-clocked mode.
         *  - DRM_MODE_FLAG_CLKDIV2: half-clocked mode.
         *
         * Additionally there's flags to specify how 3D modes are packed:
         *
         *  - DRM_MODE_FLAG_3D_NONE: normal, non-3D mode.
         *  - DRM_MODE_FLAG_3D_FRAME_PACKING: 2 full frames for left and right.
         *  - DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE: interleaved like fields.
         *  - DRM_MODE_FLAG_3D_LINE_ALTERNATIVE: interleaved lines.
         *  - DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL: side-by-side full frames.
         *  - DRM_MODE_FLAG_3D_L_DEPTH: ?
         *  - DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH: ?
         *  - DRM_MODE_FLAG_3D_TOP_AND_BOTTOM: frame split into top and bottom
         *    parts.
         *  - DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: frame split into left and
         *    right parts.
         */
        u32 flags;
        u32 flags;

        /**
         * @crtc_clock:
         *
         * Actual pixel or dot clock in the hardware. This differs from the
         * logical @clock when e.g. using interlacing, double-clocking, stereo
         * modes or other fancy stuff that changes the timings and signals
         * actually sent over the wire.
         *
         * This is again in kHz.
         *
         * Note that with digital outputs like HDMI or DP there's usually a
         * massive confusion between the dot clock and the signal clock at the
         * bit encoding level. Especially when a 8b/10b encoding is used and the
         * difference is exactly a factor of 10.
         */
        int crtc_clock;
        u16 crtc_hdisplay;
        u16 crtc_hblank_start;
        u16 crtc_hblank_end;
        u16 crtc_hsync_start;
        u16 crtc_hsync_end;
        u16 crtc_htotal;
        u16 crtc_hskew;
        u16 crtc_vdisplay;
        u16 crtc_vblank_start;
        u16 crtc_vblank_end;
        u16 crtc_vsync_start;
        u16 crtc_vsync_end;
        u16 crtc_vtotal;

        /**
         * @width_mm:
         *
         * Addressable size of the output in mm, projectors should set this to
         * 0.
         */
        u16 width_mm;

        /**
         * @height_mm:
         *
         * Addressable size of the output in mm, projectors should set this to
         * 0.
         */
        u16 height_mm;
        /**
         * @type:
         *
         * A bitmask of flags, mostly about the source of a mode. Possible flags
         * are:
         *
         *  - DRM_MODE_TYPE_PREFERRED: Preferred mode, usually the native
         *    resolution of an LCD panel. There should only be one preferred
         *    mode per connector at any given time.
         *  - DRM_MODE_TYPE_DRIVER: Mode created by the driver, which is all of
         *    them really. Drivers must set this bit for all modes they create
         *    and expose to userspace.
         *  - DRM_MODE_TYPE_USERDEF: Mode defined or selected via the kernel
         *    command line.
         *
         * Plus a big list of flags which shouldn't be used at all, but are
         * still around since these flags are also used in the userspace ABI.
         * We no longer accept modes with these types though:
         *
         *  - DRM_MODE_TYPE_BUILTIN: Meant for hard-coded modes, unused.
         *    Use DRM_MODE_TYPE_DRIVER instead.
         *  - DRM_MODE_TYPE_DEFAULT: Again a leftover, use
         *    DRM_MODE_TYPE_PREFERRED instead.
         *  - DRM_MODE_TYPE_CLOCK_C and DRM_MODE_TYPE_CRTC_C: Define leftovers
         *    which are stuck around for hysterical raisins only. No one has an
         *    idea what they were meant for. Don't use.
         */
        u8 type;

        /**
         * @expose_to_userspace:
         *
         * Indicates whether the mode is to be exposed to the userspace.
         * This is to maintain a set of exposed modes while preparing
         * user-mode's list in drm_mode_getconnector ioctl. The purpose of
         * this only lies in the ioctl function, and is not to be used
         * outside the function.
         */
        bool expose_to_userspace;

        /**
         * @head:
         *
         * struct list_head for mode lists.
         */
        struct list_head head;

        /**
         * @name:
         *
         * Human-readable name of the mode, filled out with drm_mode_set_name().
         */
        char name[DRM_DISPLAY_MODE_LEN];

        /**
         * @status:
         *
         * Status of the mode, used to filter out modes not supported by the
         * hardware. See enum &drm_mode_status.
         */
        enum drm_mode_status status;
        /**
         * @picture_aspect_ratio:
         *
         * Field for setting the HDMI picture aspect ratio of a mode.
         */
        enum hdmi_picture_aspect picture_aspect_ratio;

};

结构体包含以下成员变量:

  • hdisplay:水平显示大小;
  • hsync_start:水平同步起始;
  • hsync_end:水平同步结束;
  • htotal:水平总大小;
  • hskew:水平倾斜(暂不清楚具体含义);
  • vdisplay:垂直显示大小;
  • vsync_start:垂直同步起始;
  • vsync_end:垂直同步结束;
  • vtotal:垂直总大小;
  • vscan:垂直扫描(暂不清楚具体含义);
  • crtc_hdisplay:硬件模式的水平显示大小;
  • crtc_hblank_start:硬件模式的水平空白起始;
  • crtc_hblank_end:硬件模式的水平空白结束;
  • crtc_hsync_start:硬件模式的水平同步起始;
  • crtc_hsync_end:硬件模式的水平同步结束;
  • crtc_htotal:硬件模式的水平总大小;
  • crtc_hskew:硬件模式的水平倾斜(暂不清楚具体含义);
  • crtc_vdisplay:硬件模式的垂直显示大小;
  • crtc_vblank_start:硬件模式的垂直空白起始;
  • crtc_vblank_end:硬件模式的垂直空白结束;
  • crtc_vsync_start:硬件模式的垂直同步起始;
  • crtc_vsync_end:硬件模式的垂直同步结束;
  • crtc_vtotal:硬件模式的垂直总大小;

关于hdisplayhsync_starthsync_endhtotal对应着我们第一节中介绍的水平时序参数;

同理vdisplayvsync_startvsync_endvtotal对应着垂直时序参数;

2.1.2 struct drm_crtc_state

struct drm_crtc_state用于表示crtc的状态,定义在include/drm/drm_crtc.h

c 复制代码
/**
 * struct drm_crtc_state - mutable CRTC state
 *
 * Note that the distinction between @enable and @active is rather subtle:
 * Flipping @active while @enable is set without changing anything else may
 * never return in a failure from the &drm_mode_config_funcs.atomic_check
 * callback. Userspace assumes that a DPMS On will always succeed. In other
 * words: @enable controls resource assignment, @active controls the actual
 * hardware state.
 *
 * The three booleans active_changed, connectors_changed and mode_changed are
 * intended to indicate whether a full modeset is needed, rather than strictly
 * describing what has changed in a commit. See also:
 * drm_atomic_crtc_needs_modeset()
 *
 * WARNING: Transitional helpers (like drm_helper_crtc_mode_set() or
 * drm_helper_crtc_mode_set_base()) do not maintain many of the derived control
 * state like @plane_mask so drivers not converted over to atomic helpers should
 * not rely on these being accurate!
 */
struct drm_crtc_state {
        /** @crtc: backpointer to the CRTC */
        struct drm_crtc *crtc;

        /**
         * @enable: Whether the CRTC should be enabled, gates all other state.
         * This controls reservations of shared resources. Actual hardware state
         * is controlled by @active.
         */
        bool enable;

        /**
         * @active: Whether the CRTC is actively displaying (used for DPMS).
         * Implies that @enable is set. The driver must not release any shared
         * resources if @active is set to false but @enable still true, because
         * userspace expects that a DPMS ON always succeeds.
         *
         * Hence drivers must not consult @active in their various
         * &drm_mode_config_funcs.atomic_check callback to reject an atomic
         * commit. They can consult it to aid in the computation of derived
         * hardware state, since even in the DPMS OFF state the display hardware
         * should be as much powered down as when the CRTC is completely
         * disabled through setting @enable to false.
         */
        bool active;

        /**
         * @planes_changed: Planes on this crtc are updated. Used by the atomic
         * helpers and drivers to steer the atomic commit control flow.
         */
        bool planes_changed : 1;

        /**
         * @mode_changed: @mode or @enable has been changed. Used by the atomic
         * helpers and drivers to steer the atomic commit control flow. See also
         * drm_atomic_crtc_needs_modeset().
         *
         * Drivers are supposed to set this for any CRTC state changes that
         * require a full modeset. They can also reset it to false if e.g. a
         * @mode change can be done without a full modeset by only changing
         * scaler settings.
         */
        bool mode_changed : 1;
        /**
         * @active_changed: @active has been toggled. Used by the atomic
         * helpers and drivers to steer the atomic commit control flow. See also
         * drm_atomic_crtc_needs_modeset().
         */
        bool active_changed : 1;

        /**
         * @connectors_changed: Connectors to this crtc have been updated,
         * either in their state or routing. Used by the atomic
         * helpers and drivers to steer the atomic commit control flow. See also
         * drm_atomic_crtc_needs_modeset().
         *
         * Drivers are supposed to set this as-needed from their own atomic
         * check code, e.g. from &drm_encoder_helper_funcs.atomic_check
         */
        bool connectors_changed : 1;
 
 		/**
         * @zpos_changed: zpos values of planes on this crtc have been updated.
         * Used by the atomic helpers and drivers to steer the atomic commit
         * control flow.
         */
        bool zpos_changed : 1;
        /**
         * @color_mgmt_changed: Color management properties have changed
         * (@gamma_lut, @degamma_lut or @ctm). Used by the atomic helpers and
         * drivers to steer the atomic commit control flow.
         */
        bool color_mgmt_changed : 1;

        /**
         * @no_vblank:
         *
         * Reflects the ability of a CRTC to send VBLANK events. This state
         * usually depends on the pipeline configuration. If set to true, DRM
         * atomic helpers will send out a fake VBLANK event during display
         * updates after all hardware changes have been committed. This is
         * implemented in drm_atomic_helper_fake_vblank().
         *
         * One usage is for drivers and/or hardware without support for VBLANK
         * interrupts. Such drivers typically do not initialize vblanking
         * (i.e., call drm_vblank_init() with the number of CRTCs). For CRTCs
         * without initialized vblanking, this field is set to true in
         * drm_atomic_helper_check_modeset(), and a fake VBLANK event will be
         * send out on each update of the display pipeline by
         * drm_atomic_helper_fake_vblank().
         *
         * Another usage is CRTCs feeding a writeback connector operating in
         * oneshot mode. In this case the fake VBLANK event is only generated
         * when a job is queued to the writeback connector, and we want the
         * core to fake VBLANK events when this part of the pipeline hasn't
         * changed but others had or when the CRTC and connectors are being
         * disabled.
         *
         * __drm_atomic_helper_crtc_duplicate_state() will not reset the value
         * from the current state, the CRTC driver is then responsible for
         * updating this field when needed.
         *
         * Note that the combination of &drm_crtc_state.event == NULL and
         * &drm_crtc_state.no_blank == true is valid and usually used when the
         * writeback connector attached to the CRTC has a new job queued. In
         * this case the driver will send the VBLANK event on its own when the
         * writeback job is complete.
        bool no_vblank : 1;

        /**
         * @plane_mask: Bitmask of drm_plane_mask(plane) of planes attached to
         * this CRTC.
         */
        u32 plane_mask;

        /**
         * @connector_mask: Bitmask of drm_connector_mask(connector) of
         * connectors attached to this CRTC.
         */
        u32 connector_mask;

        /**
         * @encoder_mask: Bitmask of drm_encoder_mask(encoder) of encoders
         * attached to this CRTC.
         */
        u32 encoder_mask;

        /**
         * @adjusted_mode:
         *
         * Internal display timings which can be used by the driver to handle
         * differences between the mode requested by userspace in @mode and what
         * is actually programmed into the hardware.
         *
         * For drivers using &drm_bridge, this stores hardware display timings
         * used between the CRTC and the first bridge. For other drivers, the
         * meaning of the adjusted_mode field is purely driver implementation
         * defined information, and will usually be used to store the hardware
         * display timings used between the CRTC and encoder blocks.
         */
        struct drm_display_mode adjusted_mode;

        /**
         * @mode:
         *
         * Display timings requested by userspace. The driver should try to
         * match the refresh rate as close as possible (but note that it's
         * undefined what exactly is close enough, e.g. some of the HDMI modes
         * only differ in less than 1% of the refresh rate). The active width
         * and height as observed by userspace for positioning planes must match
         * exactly.
         *
         * For external connectors where the sink isn't fixed (like with a
         * built-in panel), this mode here should match the physical mode on the
         * wire to the last details (i.e. including sync polarities and
         * everything).
         */
        struct drm_display_mode mode;

        /**
         * @mode_blob: &drm_property_blob for @mode, for exposing the mode to
         * atomic userspace.
         */
        struct drm_property_blob *mode_blob;

        /**
         * @degamma_lut:
         *
         * Lookup table for converting framebuffer pixel data before apply the
         * color conversion matrix @ctm. See drm_crtc_enable_color_mgmt(). The
         * blob (if not NULL) is an array of &struct drm_color_lut.
         */
        struct drm_property_blob *degamma_lut;

        /**
         * @ctm:
         *
         * Color transformation matrix. See drm_crtc_enable_color_mgmt(). The
         * blob (if not NULL) is a &struct drm_color_ctm.
         */
        struct drm_property_blob *ctm;

        /**
         * @gamma_lut:
         *
         * Lookup table for converting pixel data after the color conversion
         * matrix @ctm.  See drm_crtc_enable_color_mgmt(). The blob (if not
         * NULL) is an array of &struct drm_color_lut.
         *
         * Note that for mostly historical reasons stemming from Xorg heritage,
         * this is also used to store the color map (also sometimes color lut,
         * CLUT or color palette) for indexed formats like DRM_FORMAT_C8.
         */
        struct drm_property_blob *gamma_lut;

        /**
         * @target_vblank:
         *
         * Target vertical blank period when a page flip
         * should take effect.
         */
        u32 target_vblank;

        /**
         * @async_flip:
         *
         * This is set when DRM_MODE_PAGE_FLIP_ASYNC is set in the legacy
         * PAGE_FLIP IOCTL. It's not wired up for the atomic IOCTL itself yet.
         */
        bool async_flip;

        /**
         * @vrr_enabled:
         *
         * Indicates if variable refresh rate should be enabled for the CRTC.
         * Support for the requested vrr state will depend on driver and
         * hardware capabiltiy - lacking support is not treated as failure.
         */
        bool vrr_enabled;

        /**
         * @self_refresh_active:
         *
         * Used by the self refresh helpers to denote when a self refresh
         * transition is occurring. This will be set on enable/disable callbacks
         * when self refresh is being enabled or disabled. In some cases, it may
         * not be desirable to fully shut off the crtc during self refresh.
         * CRTC's can inspect this flag and determine the best course of action.
         */
        bool self_refresh_active;

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

        /**
         * @event:
         *
         * Optional pointer to a DRM event to signal upon completion of the
         * state update. The driver must send out the event when the atomic
         * commit operation completes. There are two cases:
         *
         *  - The event is for a CRTC which is being disabled through this
         *    atomic commit. In that case the event can be send out any time
         *    after the hardware has stopped scanning out the current
         *    framebuffers. It should contain the timestamp and counter for the
         *    last vblank before the display pipeline was shut off. The simplest
         *    way to achieve that is calling drm_crtc_send_vblank_event()
         *    somewhen after drm_crtc_vblank_off() has been called.
         *
         *  - For a CRTC which is enabled at the end of the commit (even when it
         *    undergoes an full modeset) the vblank timestamp and counter must
         *    be for the vblank right before the first frame that scans out the
         *    new set of buffers. Again the event can only be sent out after the
         *    hardware has stopped scanning out the old buffers.
         *
         *  - Events for disabled CRTCs are not allowed, and drivers can ignore
         *    that case.
         *
         * For very simple hardware without VBLANK interrupt, enabling
         * &struct drm_crtc_state.no_vblank makes DRM's atomic commit helpers
         * send a fake VBLANK event at the end of the display update after all
         * hardware changes have been applied. See
         * drm_atomic_helper_fake_vblank().
         *
         * For more complex hardware this
         * can be handled by the drm_crtc_send_vblank_event() function,
         * which the driver should call on the provided event upon completion of
         * the atomic commit. Note that if the driver supports vblank signalling
         * and timestamping the vblank counters and timestamps must agree with
         * the ones returned from page flip events. With the current vblank
         * helper infrastructure this can be achieved by holding a vblank
         * reference while the page flip is pending, acquired through
         * drm_crtc_vblank_get() and released with drm_crtc_vblank_put().
         * Drivers are free to implement their own vblank counter and timestamp
         * tracking though, e.g. if they have accurate timestamp registers in
         * hardware.
         *
         * For hardware which supports some means to synchronize vblank

         * interrupt delivery with committing display state there's also
         * drm_crtc_arm_vblank_event(). See the documentation of that function
         * for a detailed discussion of the constraints it needs to be used
         * safely.
         *
         * If the device can't notify of flip completion in a race-free way
         * at all, then the event should be armed just after the page flip is
         * committed. In the worst case the driver will send the event to
         * userspace one frame too late. This doesn't allow for a real atomic
         * update, but it should avoid tearing.
         */
        struct drm_pending_vblank_event *event;

        /**
         * @commit:
         *
         * This tracks how the commit for this update proceeds through the
         * various phases. This is never cleared, except when we destroy the
         * state, so that subsequent commits can synchronize with previous ones.
         */
        struct drm_crtc_commit *commit;

        /** @state: backpointer to global drm_atomic_state */
        struct drm_atomic_state *state;
};
2.2 操作函数
2.2.1 struct drm_crtc_funcs

struct drm_crtc_funcs用于定义与CRTC相关的函数和操作,该结构体包含了一系列函数指针,用于实现包括模式设置、帧缓冲区映射、显示控制等功能。定义在include/drm/drm_crtc.h

c 复制代码
/**
 * struct drm_crtc_funcs - control CRTCs for a given device
 *
 * The drm_crtc_funcs structure is the central CRTC management structure
 * in the DRM.  Each CRTC controls one or more connectors (note that the name
 * CRTC is simply historical, a CRTC may control LVDS, VGA, DVI, TV out, etc.
 * connectors, not just CRTs).
 *
 * Each driver is responsible for filling out this structure at startup time,
 * in addition to providing other modesetting features, like i2c and DDC
 * bus accessors.
 */
struct drm_crtc_funcs {
        /**
         * @reset:
         *
         * Reset CRTC 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_crtc_reset() to reset
         * atomic state using this hook.
         */
        void (*reset)(struct drm_crtc *crtc);

        /**
         * @cursor_set:
         *
         * Update the cursor image. The cursor position is relative to the CRTC
         * and can be partially or fully outside of the visible area.
         *
         * Note that contrary to all other KMS functions the legacy cursor entry
         * points don't take a framebuffer object, but instead take directly a
         * raw buffer object id from the driver's buffer manager (which is
         * either GEM or TTM for current drivers).
         *
         * This entry point is deprecated, drivers should instead implement
         * universal plane support and register a proper cursor plane using
         * drm_crtc_init_with_planes().
         *
         * This callback is optional
         *
         * RETURNS:
         *
         * 0 on success or a negative error code on failure.
         */
        int (*cursor_set)(struct drm_crtc *crtc, struct drm_file *file_priv,
                          uint32_t handle, uint32_t width, uint32_t height);

        /**
         * @cursor_set2:
         *
         * Update the cursor image, including hotspot information. The hotspot
         * must not affect the cursor position in CRTC coordinates, but is only
         * meant as a hint for virtualized display hardware to coordinate the
         * guests and hosts cursor position. The cursor hotspot is relative to
         * the cursor image. Otherwise this works exactly like @cursor_set.
         *
         * This entry point is deprecated, drivers should instead implement
         * universal plane support and register a proper cursor plane using
         * drm_crtc_init_with_planes().
         *
         * This callback is optional.
         *
         * RETURNS:
         *
         * 0 on success or a negative error code on failure.
         */
        int (*cursor_set2)(struct drm_crtc *crtc, struct drm_file *file_priv,
                           uint32_t handle, uint32_t width, uint32_t height,
                           int32_t hot_x, int32_t hot_y);

        /**
         * @cursor_move:
         *
         * Update the cursor position. The cursor does not need to be visible
         * when this hook is called.
         *
         * This entry point is deprecated, drivers should instead implement
         * universal plane support and register a proper cursor plane using
         * drm_crtc_init_with_planes().
         *
         * This callback is optional.
         *
         * RETURNS:
         *
         * 0 on success or a negative error code on failure.
         */
        int (*cursor_move)(struct drm_crtc *crtc, int x, int y);

        /**
         * @gamma_set:
         *
         * Set gamma on the CRTC.
         *
         * This callback is optional.
         *
         * Atomic drivers who want to support gamma tables should implement the
         * atomic color management support, enabled by calling
         * drm_crtc_enable_color_mgmt(), which then supports the legacy gamma
         * interface through the drm_atomic_helper_legacy_gamma_set()
         * compatibility implementation.
         */
        int (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
                         uint32_t size,
                         struct drm_modeset_acquire_ctx *ctx);

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

        /**
         * @set_config:
         *
         * This is the main legacy entry point to change the modeset state on a
         * CRTC. All the details of the desired configuration are passed in a
         * &struct drm_mode_set - see there for details.
         *
         * Drivers implementing atomic modeset should use
         * drm_atomic_helper_set_config() to implement this hook.
         *
         * RETURNS:
         *
         * 0 on success or a negative error code on failure.
         */
        int (*set_config)(struct drm_mode_set *set,
                          struct drm_modeset_acquire_ctx *ctx);
        /**
         * @page_flip:
         *
         * Legacy entry point to schedule a flip to the given framebuffer.
         *
         * Page flipping is a synchronization mechanism that replaces the frame
         * buffer being scanned out by the CRTC with a new frame buffer during
         * vertical blanking, avoiding tearing (except when requested otherwise
         * through the DRM_MODE_PAGE_FLIP_ASYNC flag). When an application
         * requests a page flip the DRM core verifies that the new frame buffer
         * is large enough to be scanned out by the CRTC in the currently
         * configured mode and then calls this hook with a pointer to the new
         * frame buffer.
         *
         * The driver must wait for any pending rendering to the new framebuffer
         * to complete before executing the flip. It should also wait for any
         * pending rendering from other drivers if the underlying buffer is a
         * shared dma-buf.
         *
         * An application can request to be notified when the page flip has
         * completed. The drm core will supply a &struct drm_event in the event
         * parameter in this case. This can be handled by the
         * drm_crtc_send_vblank_event() function, which the driver should call on
         * the provided event upon completion of the flip. Note that if
         * the driver supports vblank signalling and timestamping the vblank
         * counters and timestamps must agree with the ones returned from page
         * flip events. With the current vblank helper infrastructure this can
         * be achieved by holding a vblank reference while the page flip is
         * pending, acquired through drm_crtc_vblank_get() and released with
         * drm_crtc_vblank_put(). Drivers are free to implement their own vblank
         * counter and timestamp tracking though, e.g. if they have accurate
         * timestamp registers in hardware.
         *
         * This callback is optional.
         *
         * NOTE:
         *
         * Very early versions of the KMS ABI mandated that the driver must
         * block (but not reject) any rendering to the old framebuffer until the
         * flip operation has completed and the old framebuffer is no longer
         * visible. This requirement has been lifted, and userspace is instead
         * expected to request delivery of an event and wait with recycling old
         * buffers until such has been received.
         *
         * RETURNS:
         *
         * 0 on success or a negative error code on failure. Note that if a
         * page flip operation is already pending the callback should return
         * -EBUSY. Pageflips on a disabled CRTC (either by setting a NULL mode
         * or just runtime disabled through DPMS respectively the new atomic
         * "ACTIVE" state) should result in an -EINVAL error code. Note that
         * drm_atomic_helper_page_flip() checks this already for atomic drivers.
         */
        int (*page_flip)(struct drm_crtc *crtc,
                         struct drm_framebuffer *fb,
                         struct drm_pending_vblank_event *event,
                         uint32_t flags,
                         struct drm_modeset_acquire_ctx *ctx);

        /**
         * @page_flip_target:
         *
         * Same as @page_flip but with an additional parameter specifying the
         * absolute target vertical blank period (as reported by
         * drm_crtc_vblank_count()) when the flip should take effect.
         *
         * Note that the core code calls drm_crtc_vblank_get before this entry
         * point, and will call drm_crtc_vblank_put if this entry point returns
         * any non-0 error code. It's the driver's responsibility to call
         * drm_crtc_vblank_put after this entry point returns 0, typically when
         * the flip completes.
         */
        int (*page_flip_target)(struct drm_crtc *crtc,
                                struct drm_framebuffer *fb,
                                struct drm_pending_vblank_event *event,
                                uint32_t flags, uint32_t target,
                                struct drm_modeset_acquire_ctx *ctx);

        /**
         * @set_property:
         *
         * This is the legacy entry point to update a property attached to the
         * CRTC.
         *
         * 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_crtc *crtc,
                            struct drm_property *property, uint64_t val);

        /**
         * @atomic_duplicate_state:
         *
         * Duplicate the current atomic state for this CRTC 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_crtc_state should use
         * drm_atomic_helper_crtc_duplicate_state(). Drivers that subclass the
         * state structure to extend it with driver-private state should use
         * __drm_atomic_helper_crtc_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_crtc.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_crtc_state *(*atomic_duplicate_state)(struct drm_crtc *crtc);

        /**
         * @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_crtc *crtc,
                                     struct drm_crtc_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_crtc_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 should never happen, the core only
         * asks for properties attached to this CRTC). 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_crtc *crtc,
                                   struct drm_crtc_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 GETCRTC IOCTL.
         *
         * Do not call this function directly, use
         * drm_atomic_crtc_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 CRTC).
         */
        int (*atomic_get_property)(struct drm_crtc *crtc,
                                   const struct drm_crtc_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 crtc 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_crtc *crtc);

        /**
         * @early_unregister:
         *
         * This optional hook should be used to unregister the additional
         * userspace interfaces attached to the crtc 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_crtc *crtc);

        /**
         * @set_crc_source:
         *
         * Changes the source of CRC checksums of frames at the request of
         * userspace, typically for testing purposes. The sources available are
         * specific of each driver and a %NULL value indicates that CRC
         * generation is to be switched off.
         *
         * When CRC generation is enabled, the driver should call
         * drm_crtc_add_crc_entry() at each frame, providing any information
         * that characterizes the frame contents in the crcN arguments, as
         * provided from the configured source. Drivers must accept an "auto"
         * source name that will select a default source for this CRTC.
         *
         * This may trigger an atomic modeset commit if necessary, to enable CRC
         * generation.
         *
         * Note that "auto" can depend upon the current modeset configuration,
         * e.g. it could pick an encoder or output specific CRC sampling point.
         *
         * This callback is optional if the driver does not support any CRC
         * generation functionality.
         *
         * RETURNS:
         *
         * 0 on success or a negative error code on failure.
         */
        int (*set_crc_source)(struct drm_crtc *crtc, const char *source);

        /**
         * @verify_crc_source:
         *
         * verifies the source of CRC checksums of frames before setting the
         * source for CRC and during crc open. Source parameter can be NULL
         * while disabling crc source.
         *
         * This callback is optional if the driver does not support any CRC
         * generation functionality.
         *
         * RETURNS:
         *
         * 0 on success or a negative error code on failure.
         */
        int (*verify_crc_source)(struct drm_crtc *crtc, const char *source,
                                 size_t *values_cnt);
        /**
         * @get_crc_sources:
         *
         * Driver callback for getting a list of all the available sources for
         * CRC generation. This callback depends upon verify_crc_source, So
         * verify_crc_source callback should be implemented before implementing
         * this. Driver can pass full list of available crc sources, this
         * callback does the verification on each crc-source before passing it
         * to userspace.
         *
         * This callback is optional if the driver does not support exporting of
         * possible CRC sources list.
         *
         * RETURNS:
         *
         * a constant character pointer to the list of all the available CRC
         * sources. On failure driver should return NULL. count should be
         * updated with number of sources in list. if zero we don't process any
         * source from the list.
         */
        const char *const *(*get_crc_sources)(struct drm_crtc *crtc,
                                              size_t *count);

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

        /**
         * @get_vblank_counter:
         *
         * Driver callback for fetching a raw hardware vblank counter for the
         * CRTC. It's meant to be used by new drivers as the replacement of
         * &drm_driver.get_vblank_counter hook.
         *
         * This callback is optional. If a device doesn't have a hardware
         * counter, the driver can simply leave the hook as NULL. The DRM core
         * will account for missed vblank events while interrupts where disabled
         * based on system timestamps.
         *
         * Wraparound handling and loss of events due to modesetting is dealt
         * with in the DRM core code, as long as drivers call
         * drm_crtc_vblank_off() and drm_crtc_vblank_on() when disabling or
         * enabling a CRTC.
         *
         * See also &drm_device.vblank_disable_immediate and
         * &drm_device.max_vblank_count.
         *
         * Returns:
         *
         * Raw vblank counter value.
         */
        u32 (*get_vblank_counter)(struct drm_crtc *crtc);

        /**
         * @enable_vblank:
         *
         * Enable vblank interrupts for the CRTC. It's meant to be used by
         * new drivers as the replacement of &drm_driver.enable_vblank hook.
         *
         * Returns:
         *
         * Zero on success, appropriate errno if the vblank interrupt cannot
         * be enabled.
         */
        int (*enable_vblank)(struct drm_crtc *crtc);

        /**
         * @disable_vblank:
         *
         * Disable vblank interrupts for the CRTC. It's meant to be used by
         * new drivers as the replacement of &drm_driver.disable_vblank hook.
         */
        void (*disable_vblank)(struct drm_crtc *crtc);

        /**
         * @get_vblank_timestamp:
         *
         * Called by drm_get_last_vbltimestamp(). Should return a precise
         * timestamp when the most recent vblank interval ended or will end.
         *
         * Specifically, the timestamp in @vblank_time should correspond as
         * closely as possible to the time when the first video scanline of
         * the video frame after the end of vblank will start scanning out,
         * the time immediately after end of the vblank interval. If the
         * @crtc is currently inside vblank, this will be a time in the future.
         * If the @crtc is currently scanning out a frame, this will be the
         * past start time of the current scanout. This is meant to adhere
         * to the OpenML OML_sync_control extension specification.
         *
         * Parameters:
         *
         * crtc:
         *     CRTC for which timestamp should be returned.
         * max_error:
         *     Maximum allowable timestamp error in nanoseconds.
         *     Implementation should strive to provide timestamp
         *     with an error of at most max_error nanoseconds.
         *     Returns true upper bound on error for timestamp.
         * vblank_time:
         *     Target location for returned vblank timestamp.
         * in_vblank_irq:
         *     True when called from drm_crtc_handle_vblank().  Some drivers
         *     need to apply some workarounds for gpu-specific vblank irq quirks
         *     if flag is set.
         *
         * Returns:
         *
         * True on success, false on failure, which means the core should
         * fallback to a simple timestamp taken in drm_crtc_handle_vblank().
         */
        bool (*get_vblank_timestamp)(struct drm_crtc *crtc,
                                     int *max_error,
                                     ktime_t *vblank_time,
                                     bool in_vblank_irq);
};

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

  • reset:用于将CRTC硬件和软件重置为关闭状态,这个函数不会直接调用,只会通过drm_mode_config_reset()调用;
  • cursor_set:更新光标图像,光标位置是相对于CRTC,并且可以部分或完全位于可见区域之外;
  • cursor_set2:更新光标图像,包括热点信息;
  • cursor_move:更新光标位置。在调用此钩子时,光标不需要可见;
  • gamma_set:在CRTC上设置gamma
  • destroy:清理CRTC资源;
  • set_config:改变modeset state
  • page_flip:用于反转给定的framebuffer
  • ...
2.2.2 struct drm_crtc_helper_funcs

struct drm_crtc_helper_funcs用于定义与CRTC助手函数相关的操作。这些助手函数提供了一些辅助性的操作,如时序生成、同步信号发送、格式转换等,以便更好地协助实现扫描输出帧缓冲区内容的功能。定义在include/drm/drm_modeset_helper_vtables.h

c 复制代码
/**
 * struct drm_crtc_helper_funcs - helper operations for CRTCs
 *
 * These hooks are used by the legacy CRTC helpers, the transitional plane
 * helpers and the new atomic modesetting helpers.
 */
struct drm_crtc_helper_funcs {
        /**
         * @dpms:
         *
         * Callback to control power levels on the CRTC.  If the mode passed in
         * is unsupported, the provider must use the next lowest power level.
         * This is used by the legacy CRTC helpers to implement DPMS
         * functionality in drm_helper_connector_dpms().
         *
         * This callback is also used to disable a CRTC by calling it with
         * DRM_MODE_DPMS_OFF if the @disable hook isn't used.
         *
         * This callback is used by the legacy CRTC helpers.  Atomic helpers
         * also support using this hook for enabling and disabling a CRTC to
         * facilitate transitions to atomic, but it is deprecated. Instead
         * @atomic_enable and @atomic_disable should be used.
         */
        void (*dpms)(struct drm_crtc *crtc, int mode);

        /**
         * @prepare:
         *
         * This callback should prepare the CRTC for a subsequent modeset, which
         * in practice means the driver should disable the CRTC if it is
         * running. Most drivers ended up implementing this by calling their
         * @dpms hook with DRM_MODE_DPMS_OFF.
         *
         * This callback is used by the legacy CRTC helpers.  Atomic helpers
         * also support using this hook for disabling a CRTC to facilitate
         * transitions to atomic, but it is deprecated. Instead @atomic_disable
         * should be used.
         */
        void (*prepare)(struct drm_crtc *crtc);

        /**
         * @commit:
         *
         * This callback should commit the new mode on the CRTC after a modeset,
         * which in practice means the driver should enable the CRTC.  Most
         * drivers ended up implementing this by calling their @dpms hook with
         * DRM_MODE_DPMS_ON.
         *
         * This callback is used by the legacy CRTC helpers.  Atomic helpers
         * also support using this hook for enabling a CRTC to facilitate
         * transitions to atomic, but it is deprecated. Instead @atomic_enable
         * should be used.
         */
        void (*commit)(struct drm_crtc *crtc);
        /**
         * @mode_valid:
         *
         * This callback is used to check if a specific mode is valid in this
         * crtc. This should be implemented if the crtc has some sort of
         * restriction in the modes it can display. For example, a given crtc
         * may be responsible to set a clock value. If the clock can not
         * produce all the values for the available modes then this callback
         * can be used to restrict the number of modes to only the ones that
         * can be displayed.
         *
         * This hook is used by the probe helpers to filter the mode list in
         * drm_helper_probe_single_connector_modes(), and it is used by the
         * atomic helpers to validate modes supplied by userspace in
         * drm_atomic_helper_check_modeset().
         *
         * This function is optional.
         *
         * NOTE:
         *
         * Since this function is both called from the check phase of an atomic
         * commit, and the mode validation in the probe paths it is not allowed
         * to look at anything else but the passed-in mode, and validate it
         * against configuration-invariant hardward constraints. Any further
         * limits which depend upon the configuration can only be checked in
         * @mode_fixup or @atomic_check.
         *
         * RETURNS:
         *
         * drm_mode_status Enum
         */
        enum drm_mode_status (*mode_valid)(struct drm_crtc *crtc,
                                           const struct drm_display_mode *mode);

        /**
         * @mode_fixup:
         *
         * This callback is used to validate a mode. The parameter mode is the
         * display mode that userspace requested, adjusted_mode is the mode the
         * encoders need to be fed with. Note that this is the inverse semantics
         * of the meaning for the &drm_encoder and &drm_bridge_funcs.mode_fixup
         * vfunc. If the CRTC cannot support the requested conversion from mode
         * to adjusted_mode it should reject the modeset. See also
         * &drm_crtc_state.adjusted_mode for more details.
         *
         * This function is used by both legacy CRTC helpers and atomic helpers.
         * With atomic helpers it is optional.
         *
         * NOTE:
         *
         * This function is called in the check 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). Atomic drivers
         * MUST NOT touch any persistent state (hardware or software) or data
         * structures except the passed in adjusted_mode parameter.
         *
         * This is in contrast to the legacy CRTC helpers where this was
         * allowed.
         *
         * Atomic drivers which need to inspect and adjust more state should
         * instead use the @atomic_check callback, but note that they're not
         * perfectly equivalent: @mode_valid is called from
         * drm_atomic_helper_check_modeset(), but @atomic_check is called from
         * drm_atomic_helper_check_planes(), because originally it was meant for
         * plane update checks only.
         *
         * Also beware that userspace can request its own custom modes, neither
         * core nor helpers filter modes to the list of probe modes reported by
         * the GETCONNECTOR IOCTL and stored in &drm_connector.modes. To ensure
         * that modes are filtered consistently put any CRTC constraints and
         * limits checks into @mode_valid.
         *
         * RETURNS:
         *
         * True if an acceptable configuration is possible, false if the modeset
         * operation should be rejected.
         */
        bool (*mode_fixup)(struct drm_crtc *crtc,
                           const struct drm_display_mode *mode,
                           struct drm_display_mode *adjusted_mode);

        /**
         * @mode_set:
         *
         * This callback is used by the legacy CRTC helpers to set a new mode,
         * position and framebuffer. Since it ties the primary plane to every
         * mode change it is incompatible with universal plane support. And
         * since it can't update other planes it's incompatible with atomic
         * modeset support.
         *
         * This callback is only used by CRTC helpers and deprecated.
         *
         * RETURNS:
         *
         * 0 on success or a negative error code on failure.
         */
        int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
                        struct drm_display_mode *adjusted_mode, int x, int y,
                        struct drm_framebuffer *old_fb);
        /**
         * @mode_set_nofb:
         *
         * This callback is used to update the display mode of a CRTC without
         * changing anything of the primary plane configuration. This fits the
         * requirement of atomic and hence is used by the atomic helpers. It is
         * also used by the transitional plane helpers to implement a
         * @mode_set hook in drm_helper_crtc_mode_set().
         *
         * Note that the display pipe is completely off when this function is
         * called. Atomic drivers which need hardware to be running before they
         * program the new display mode (e.g. because they implement runtime PM)
         * should not use this hook. This is because the helper library calls
         * this hook only once per mode change and not every time the display
         * pipeline is suspended using either DPMS or the new "ACTIVE" property.
         * Which means register values set in this callback might get reset when
         * the CRTC is suspended, but not restored.  Such drivers should instead
         * move all their CRTC setup into the @atomic_enable callback.
         *
         * This callback is optional.
         */
        void (*mode_set_nofb)(struct drm_crtc *crtc);

        /**
         * @mode_set_base:
         *
         * This callback is used by the legacy CRTC helpers to set a new
         * framebuffer and scanout position. It is optional and used as an
         * optimized fast-path instead of a full mode set operation with all the
         * resulting flickering. If it is not present
         * drm_crtc_helper_set_config() will fall back to a full modeset, using
         * the @mode_set callback. Since it can't update other planes it's
         * incompatible with atomic modeset support.
         *
         * This callback is only used by the CRTC helpers and deprecated.
         *
         * RETURNS:
         *
         * 0 on success or a negative error code on failure.
         */
        int (*mode_set_base)(struct drm_crtc *crtc, int x, int y,
                             struct drm_framebuffer *old_fb);

        /**
         * @mode_set_base_atomic:
         *
         * This callback is used by the fbdev helpers to set a new framebuffer
         * and scanout without sleeping, i.e. from an atomic calling context. It
         * is only used to implement kgdb support.
         *
         * This callback is optional and only needed for kgdb support in the fbdev
         * helpers.
         *
         * RETURNS:
         *
         * 0 on success or a negative error code on failure.
         */
        int (*mode_set_base_atomic)(struct drm_crtc *crtc,
                                    struct drm_framebuffer *fb, int x, int y,
                                    enum mode_set_atomic);

        /**
         * @disable:
         *
         * This callback should be used to disable the CRTC. With the atomic
         * drivers it is called after all encoders connected to this CRTC have
         * been shut off already using their own
         * &drm_encoder_helper_funcs.disable hook. If that sequence is too
         * simple drivers can just add their own hooks and call it from this
         * CRTC callback here by looping over all encoders connected to it using
         * for_each_encoder_on_crtc().
         *
         * This hook is used both by legacy CRTC helpers and atomic helpers.
         * Atomic drivers don't need to implement it if there's no need to
         * disable anything at the CRTC level. To ensure that runtime PM
         * handling (using either DPMS or the new "ACTIVE" property) works
         * @disable must be the inverse of @atomic_enable for atomic drivers.
         * Atomic drivers should consider to use @atomic_disable instead of
         * this one.
         *
         * NOTE:
         *
         * With legacy CRTC helpers there's a big semantic difference between
         * @disable and other hooks (like @prepare or @dpms) used to shut down a
         * CRTC: @disable is only called when also logically disabling the
         * display pipeline and needs to release any resources acquired in
         * @mode_set (like shared PLLs, or again release pinned framebuffers).
         *
         * Therefore @disable must be the inverse of @mode_set plus @commit for
         * drivers still using legacy CRTC helpers, which is different from the
         * rules under atomic.
         */
        void (*disable)(struct drm_crtc *crtc);

        /**
         * @atomic_check:
         *
         * Drivers should check plane-update related CRTC constraints in this
         * hook. They can also check mode related limitations but need to be
         * aware of the calling order, since this hook is used by
         * drm_atomic_helper_check_planes() whereas the preparations needed to
         * check output routing and the display mode is done in
         * drm_atomic_helper_check_modeset(). Therefore drivers that want to
         * check output routing and display mode constraints in this callback
         * must ensure that drm_atomic_helper_check_modeset() has been called
         * beforehand. This is calling order used by the default helper
         * implementation in drm_atomic_helper_check().
         *
         * When using drm_atomic_helper_check_planes() this hook is called
         * after the &drm_plane_helper_funcs.atomic_check hook for planes, which
         * allows drivers to assign shared resources requested by planes in this
         * callback 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 free-standing
         * state object passed-in.
         *
         * Also beware that userspace can request its own custom modes, neither
         * core nor helpers filter modes to the list of probe modes reported by
         * the GETCONNECTOR IOCTL and stored in &drm_connector.modes. To ensure
         * that modes are filtered consistently put any CRTC constraints and
         * limits checks into @mode_valid.
         *
         * 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_crtc *crtc,
                            struct drm_atomic_state *state);
        /**
         * @atomic_begin:
         *
         * Drivers should prepare for an atomic update of multiple planes on
         * a CRTC in this hook. Depending upon hardware this might be vblank
         * evasion, blocking updates by setting bits or doing preparatory work
         * for e.g. manual update display.
         *
         * This hook is called before any plane commit functions are called.
         *
         * 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_begin)(struct drm_crtc *crtc,
                             struct drm_atomic_state *state);
        /**
         * @atomic_flush:
         *
         * Drivers should finalize an atomic update of multiple planes on
         * a CRTC in this hook. Depending upon hardware this might include
         * checking that vblank evasion was successful, unblocking updates by
         * setting bits or setting the GO bit to flush out all updates.
         *
         * Simple hardware or hardware with special requirements can commit and
         * flush out all updates for all planes from this hook and forgo all the
         * other commit hooks for plane updates.
         *
         * This hook is called after any plane commit functions are called.
         *
         * 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_flush)(struct drm_crtc *crtc,
                             struct drm_atomic_state *state);

        /**
         * @atomic_enable:
         *
         * This callback should be used to enable the CRTC. With the atomic
         * drivers it is called before all encoders connected to this CRTC are
         * enabled through the encoder's own &drm_encoder_helper_funcs.enable
         * hook.  If that sequence is too simple drivers can just add their own
         * hooks and call it from this CRTC callback here by looping over all
         * encoders connected to it using for_each_encoder_on_crtc().
         *
         * This hook is used only by atomic helpers, for symmetry with
         * @atomic_disable. Atomic drivers don't need to implement it if there's
         * no need to enable anything at the CRTC level. To ensure that runtime
         * PM handling (using either DPMS or the new "ACTIVE" property) works
         * @atomic_enable must be the inverse of @atomic_disable for atomic
         * drivers.
         *
         * This function is optional.
         */
        void (*atomic_enable)(struct drm_crtc *crtc,
                              struct drm_atomic_state *state);

        /**
         * @atomic_disable:
         *
         * This callback should be used to disable the CRTC. With the atomic
         * drivers it is called after all encoders connected to this CRTC have
         * been shut off already using their own
         * &drm_encoder_helper_funcs.disable hook. If that sequence is too
         * simple drivers can just add their own hooks and call it from this
         * CRTC callback here by looping over all encoders connected to it using
         * for_each_encoder_on_crtc().
         *
         * This hook is used only by atomic helpers. Atomic drivers don't
         * need to implement it if there's no need to disable anything at the
         * CRTC level.
         *
         * This function is optional.
         */
        void (*atomic_disable)(struct drm_crtc *crtc,
                               struct drm_atomic_state *state);

        /**
         * @get_scanout_position:
         *
         * Called by vblank timestamping code.
         *
         * Returns the current display scanout position from a CRTC and an
         * optional accurate ktime_get() timestamp of when the position was
         * measured. Note that this is a helper callback which is only used
         * if a driver uses drm_crtc_vblank_helper_get_vblank_timestamp()
         * for the @drm_crtc_funcs.get_vblank_timestamp callback.
         *
         * Parameters:
         *
         * crtc:
         *     The CRTC.
         * in_vblank_irq:
         *     True when called from drm_crtc_handle_vblank(). Some drivers
         *     need to apply some workarounds for gpu-specific vblank irq
         *     quirks if the flag is set.
         * vpos:
         *     Target location for current vertical scanout position.
         * hpos:
         *     Target location for current horizontal scanout position.
         * stime:
         *     Target location for timestamp taken immediately before
         *     scanout position query. Can be NULL to skip timestamp.
         * etime:
         *     Target location for timestamp taken immediately after
         *     scanout position query. Can be NULL to skip timestamp.
         * mode:
         *     Current display timings.
         *
         * Returns vpos as a positive number while in active scanout area.
         * Returns vpos as a negative number inside vblank, counting the number
         * of scanlines to go until end of vblank, e.g., -1 means "one scanline
         * until start of active scanout / end of vblank."
         *
         * Returns:
         *
         * True on success, false if a reliable scanout position counter could
         * not be read out.
         */
        bool (*get_scanout_position)(struct drm_crtc *crtc,
                                     bool in_vblank_irq, int *vpos, int *hpos,
                                     ktime_t *stime, ktime_t *etime,
                                     const struct drm_display_mode *mode);
};

三、CRTC核心API

3.1 drm_crtc_init_with_planes

drm_crtc_init_with_planes函数用于初始化的crtc对象,定义在drivers/gpu/drm/drm_crtc.c

参考文章

[1] DRM (Direct Rendering Manager)

[2] Wiki: Direct Rendering Manager

[3] The DRM/KMS subsystem from a newbie's point of view

[4] DRM驱动概念、组成、框架、源码分析

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

[6] DRM驱动程序开发(VKMS)

[7] MIPI自学笔记