在drm框架内,显示的一般流程如下:
内存 (Framebuffer) → Plane (图层配置与合成) → CRTC (时序生成与扫描输出) → Encoder → Connector → 显示器
plane
代表一个图像源。在扫描输出过程中可与crtc混合或叠加在其上方。 其从drm_framebuffer对象获取输入数据。其指定该图像的裁剪和缩放,以及它在drm_crtc表示显示管线可见区域上的放置位置。 其还可具有其他属性,这些属性指定像素的定位和混合方式,如旋转或Z轴位置。所有这些属性都存储在drm_plane_state中。
类型 说明
DRM 定义了三种标准 Plane 类型,驱动根据硬件能力进行分配:
- Primary 主图层,每个 CRTC 必须有一个。通常支持全屏显示,是页面翻转的主要对象。
- Cursor 光标图层,用于硬件光标。尺寸通常较小(如 64×64),支持异步更新,延迟极低。
- Overlay 叠加图层,用于视频播放、UI 合成等。数量因硬件而异,能力也各不相同(是否支持缩放、旋转等)。
代码说明
drm_plane由kms驱动分配、初始化、注册 调用drm_universal_plane_init()
cursor overlay plane是可选择的,所有的驱动应该提供一个primary plane为每一个crtc。特殊的plane通过调用drm_crtc_init_with_planes()函数与crtc进行关联
struct drm_plane_state {
struct drm_plane *plane; //指向plane对象 struct drm_crtc *crtc; //当前绑定的crtc,NULL表示禁用,不能直接赋值,需要使用函数drm_atomic_set_crtc_for_plane() struct drm_framebuffer *fb; //当前绑定的framebuffer,不能直接赋值,需要使用drm_atomic_set_fb_for_plane() struct dma_fence *fence; //在扫描输出fb前可选择fence等待,当用户使用显示fence时atomic fence将会被设置 int32_t crtc_x; //plane显示在crtc左侧位置 int32_t crtc_y; //plane显示在crtc上侧位置 uint32_t crtc_w, crtc_h; //crtc可见位置的高度和宽度 uint32_t src_x; //平面内可见部分的左侧位置 uint32_t src_y; //平面内可见部分的上侧位置 uint32_t src_h, src_w; //平面可见位置的高度和宽度 u16 alpha; //平面的不透明度,0表示完全透明,0xffff表示完全不透明 uint16_t pixel_blend_mode; //alpha混合方式选择,描述当前平面的像素如何与背景合成 unsigned int rotation; //平面的旋转 unsigned int zpos; //平面在crtc上的优先级 unsigned int normalized_zpos; //zpos的归一化值:唯一,范围从0到N-1,其中N是给定crtc的活动平面数 enum drm_color_encoding color_encoding; //非RGB格式的颜色编码 enum drm_color_range color_range; //非RGB格式的颜色范围 struct drm_property_blob *fb_damage_clips;//平面帧缓冲区中自上次平面更新以来发生变化的区域 struct drm_rect src, dst; //平面的剪切源/目地坐标 bool visible; //平面是否可见,与fb和crtc是否赋值无关,与裁剪相关 struct drm_crtc_commit *commit; //跟踪挂起的提交,以防止在空闲条件后使用,并用于异步平面更新。可能为空。 struct drm_atomic_state *state; //指向drm_atomic_state对象 };plane驱动的控制回调函数
struct drm_plane_funcs { 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); int (*disable_plane)(struct drm_plane *plane, struct drm_modeset_acquire_ctx *ctx); void (*destroy)(struct drm_plane *plane); void (*reset)(struct drm_plane *plane); int (*set_property)(struct drm_plane *plane, struct drm_property *property, uint64_t val); struct drm_plane_state *(*atomic_duplicate_state)(struct drm_plane *plane); void (*atomic_destroy_state)(struct drm_plane *plane, struct drm_plane_state *state); int (*atomic_set_property)(struct drm_plane *plane,struct drm_plane_state *state,struct drm_property *property, uint64_t val); int (*atomic_get_property)(struct drm_plane *plane,const struct drm_plane_state *state,struct drm_property *property, uint64_t *val); int (*late_register)(struct drm_plane *plane); void (*early_unregister)(struct drm_plane *plane); void (*atomic_print_state)(struct drm_printer *p, const struct drm_plane_state *state); bool (*format_mod_supported)(struct drm_plane *plane, uint32_t format, uint64_t modifier); };- update_plane:是为给指定的crtc和fb启用和配置plane的入口点,不能用来禁用plane
- disable_plane: 这是禁用plane的入口点,DRM调用此方法响应帧缓冲区ID设置为0DRM_USDMODE_SETPLANE的调用
- destory:清空plane资源
- reset:重置plane硬件,软件状态重置成off.只能通过drm_mode_config_reset()函数来调用
- set_property: 这个是更新plane属性的入口函数
- atomic_duplicate_state:复制plane当前原子状态并返回
- atomic_destroy_state: 销毁一个拷贝的state
- atomic_set_property:对驱动程序私有属性值进行解码,并将解码后的值存储到传入的状态结构中。set_property接口一次只能设置单个属性,改动可能立即生效但缺乏同步保障;原子接口则把多个属性打包到一个请求里,通过 drmModeAtomicCommit() 统一校验和提交,保证同一帧内一致生效
- atomic_get_property:读取plane的私有属性。此函数不能直接调用,需要使用drm_atomic_plane_get_property()
- late_register:用于注册额外的用户空间接口,将会在驱动加载后面由drm_dev_register()调用
- early_unregister:此函数与上面的函数对应,用于卸载上面函数注册的用户空间接口。由 drm_dev_unregister()调用
plane结构体定义
struct drm_plane { struct drm_device *dev; //plane属性哪个drm设备 struct list_head head; //在设备上所有的plane列表 char *name; //name可读,由驱动进行修改 struct drm_modeset_lock mutex; //锁 struct drm_mode_object base; //base对象 uint32_t possible_crtcs; //此plane可以使用的crtc uint32_t *format_types; //此plane支持的format unsigned int format_count; //格式数组大小 bool format_default; // uint64_t *modifiers; unsigned int modifier_count; struct drm_crtc *crtc; //绑定的crtc仅对非原子操作有交,原子操作此为NULL struct drm_framebuffer *fb; //绑定的fb仅对非原子操作有交,原子操作此为NULL struct drm_framebuffer *old_fb; const struct drm_plane_funcs *funcs; //plane控制回调函数 struct drm_object_properties properties; //plane的属性 enum drm_plane_type type; //plane type unsigned index; //在mode_config内部位置,可以当数组下标使用 const struct drm_plane_helper_funcs *helper_private; struct drm_plane_state *state; //当前plane的atomic状态 struct drm_property *alpha_property; //plane可选择的alpha属性 struct drm_property *zpos_property; //plane可选择的zpos属性 struct drm_property *rotation_property; //plane可选择的rotation属性 struct drm_property *blend_mode_property; //plane可选择的像素混合模式,代表像素与背景如何合成 struct drm_property *color_encoding_property; //可选择的COLOR_ENCODING模式,用于非rgb模式 struct drm_property *color_range_property; //可选择的COLOR_RANGE模式,用于非rgb模式 };crtc
说明
drm_crtc 是显示控制器硬件的抽象,它负责从内存中读取像素数据,并生成带有正确时序的视频信号。
核心职责
- 生成显示时序:这是 CRTC 最根本的任务。它根据 drm_display_mode(包含像素时钟、水平/垂直同步参数、前后沿等)来驱动硬件,产生符合显示器要求的行同步(HSYNC)和场同步(VSYNC)信号,确保画面稳定。
- 管理扫描输出与页面翻转:CRTC 负责在垂直消隐期间切换当前扫描输出的 Framebuffer,实现无撕裂的页面翻转。这是合成器实现流畅动画和视频播放的基础。
- 驱动图层合成:CRTC 与一个或多个 Plane 绑定。它负责将 Plane 上的内容(可能经过缩放、色彩空间转换等)合成为最终的扫描输出画面。一个 CRTC 通常对应一个物理显示管道。
- 执行模式设置:当用户空间请求更改分辨率或刷新率时,CRTC 负责执行 mode_set 操作,重新配置硬件时序发生器。这个过程通常需要先关闭输出,再重新配置,最后重新使能。
- 管理电源状态:CRTC 参与 DPMS(Display Power Management Signaling)电源管理。它可以在空闲时关闭扫描输出,以节省功耗。
与其他对象的关系
- 与 Plane:一个 CRTC 可以绑定多个 Plane(主图层、光标层、叠加层)。CRTC 负责在扫描输出时,将这些 Plane 的内容按 Z 序合成为最终画面。
- 与 Encoder:一个 CRTC 可以连接到一个或多个 Encoder。当连接多个 Encoder 时,可以实现“克隆显示”(同一画面输出到多个接口)。
- 与 Connector:CRTC 不直接感知 Connector 的存在,它只负责生成信号。Connector 负责物理接口和显示器能力,两者通过 Encoder 间接关联。
- 与用户空间:CRTC 是 KMS 中暴露给用户空间的核心对象。合成器通过它来设置显示模式、执行页面翻转、配置 Plane 属性,从而完成整个显示画面的更新。
为了创建一个crtc,kms驱动需要申请、初始化、注册drm_crtc结构体。通过调用drm_crtc_init_with_planes()
crtc回调函数
struct drm_crtc_funcs { void (*reset)(struct drm_crtc *crtc); int (*cursor_set)(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height); 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); int (*cursor_move)(struct drm_crtc *crtc, int x, int y); int (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,uint32_t size, struct drm_modeset_acquire_ctx *ctx); void (*destroy)(struct drm_crtc *crtc); int (*set_config)(struct drm_mode_set *set, struct drm_modeset_acquire_ctx *ctx); 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); 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); int (*set_property)(struct drm_crtc *crtc, struct drm_property *property, uint64_t val); struct drm_crtc_state *(*atomic_duplicate_state)(struct drm_crtc *crtc); void (*atomic_destroy_state)(struct drm_crtc *crtc, struct drm_crtc_state *state); int (*atomic_set_property)(struct drm_crtc *crtc,struct drm_crtc_state *state,struct drm_property *property, uint64_t val); int (*atomic_get_property)(struct drm_crtc *crtc,const struct drm_crtc_state *state,struct drm_property *property, uint64_t *val); int (*late_register)(struct drm_crtc *crtc); void (*early_unregister)(struct drm_crtc *crtc); int (*set_crc_source)(struct drm_crtc *crtc, const char *source); int (*verify_crc_source)(struct drm_crtc *crtc, const char *source, size_t *values_cnt); const char *const *(*get_crc_sources)(struct drm_crtc *crtc, size_t *count); void (*atomic_print_state)(struct drm_printer *p, const struct drm_crtc_state *state); u32 (*get_vblank_counter)(struct drm_crtc *crtc); int (*enable_vblank)(struct drm_crtc *crtc); void (*disable_vblank)(struct drm_crtc *crtc); };- reset:重置crtc硬件并将软件状态设置为off,这个函数不能被直接调用,需要通过drm_mode_config_reset()
- cursor_set:更新cursor图像,光标位置相对于crtc,可以部分或者完全位于可见区域之外
- cursor_set2:更新光标图像,包括热点信息。其它工作方式与cursor_set完全相同
- cursor_move:更新光标位置,此函数目前已被弃用
- gamma_set:在crtc上设置gamma
- destroy:清空crtc资源
- set_config:这是crtc模式的主要入口,原子调用由drm_atomic_helper_set_config()
- page_flip:翻页是一种同步机制,在垂直消隐期间用新的帧缓冲区替换CRTC扫描出的帧缓冲,避免撕裂
- page_flip_target:与page_flip相同,但有一个额外的参数指定绝对目标垂直空白期
- set_property:这是更新crtc属性的基本入口点,这个回调函数是可选择的
- atomic_duplicate_state:拷贝当前crtc的原子状态并返回
- atomic_destroy_state:用以消毁上面函数生成的对象
- atomic_set_property:原子设置crtc私有属性,可以一次性设置多个属性
- atomic_get_property:读取crtc编译的私有属性,不能被直接调用需要使用drm_atomic_crtc_get_property()此函数来实现
- late_register:这是个可选择的钩子被用来注册额外的用户空间接口
encoder
简介
encoder连接crtc与connector,从crtc获取像素数据,
drm_encoder 的核心作用是在 CRTC(显示控制器)与 Connector(物理接口)之间进行信号转换与路由。
它负责把 CRTC 产生的像素数据,转换成特定显示接口(如 HDMI、LVDS)所需的电信号格式
具体的功能角色
- 信号转换器:这是它最本质的功能。它根据 encoder_type(如 DRM_MODE_ENCODER_TMDS, DRM_MODE_ENCODER_LVDS)来适配不同的物理层协议,实现“像素数据 → 符合接口标准的电信号”的转变。
- 路由与连接限制:Encoder 是 CRTC 和 Connector 之间的“插线板”。它的 possible_crtcs 字段决定了它能接受哪些 CRTC 的数据;而一个 CRTC 也可以驱动多个 Encoder,从而实现“克隆显示”(同一画面输出到多个接口)
其功能回调函
struct drm_encoder_funcs { void (*reset)(struct drm_encoder *encoder); void (*destroy)(struct drm_encoder *encoder); int (*late_register)(struct drm_encoder *encoder); void (*early_unregister)(struct drm_encoder *encoder); };- reset
重启encoder硬件并将软件状态设置成off - destroy
清空encoder资源 - late_register
这是一个可选择的沟子被用于注册额外的用户空间接口 - early_unregister
此函数用于释放上面函数产生内存
结构体定义
struct drm_encoder { struct drm_device *dev; struct list_head head; struct drm_mode_object base; char *name; int encoder_type; unsigned index; uint32_t possible_crtcs; uint32_t possible_clones; struct drm_crtc *crtc; struct drm_bridge *bridge; const struct drm_encoder_funcs *funcs; const struct drm_encoder_helper_funcs *helper_private; };- dev:父drm device
- head:链表管理
- base: base kms对象
- encoder_type:编码类型
- DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A.
- DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort.
- DRM_MODE_ENCODER_LVDS for display panels, or in general any panel with a proprietary parallel connector.
- DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video, Component, SCART).
- DRM_MODE_ENCODER_VIRTUAL for virtual machine displays
- DRM_MODE_ENCODER_DSI for panels connected using the DSI serial bus.
- DRM_MODE_ENCODER_DPI for panels connected using the DPI parallel bus.
- DRM_MODE_ENCODER_DPMST for special fake encoders used to allow mutliple DP MST streams to share one physical encoder.
- index :位于mode_config的位置
- possible_crtcs:潜在的绑定crtc的掩码
- crtc:目前绑定的crtc
- funcs:控制回调函数
- helper_private:中间层私有数据
bridge
简介
drm_bridge 是一个表示编码器链中中间硬件设备的抽象对象。它的核心作用是解决单个 drm_encoder 无法完整描述复杂显示输出链的问题。当 SoC 的显示控制器输出信号后,可能还需要经过一系列外部芯片(如 HDMI 电平转换器、MIPI DSI 转 eDP 芯片等)才能最终驱动屏幕时,drm_bridge 就派上了用场。它允许将这些中间设备串联成链:encoder —> bridge B —> bridge A
具体作用体
- 参与显示流水线操作:一旦与 encoder 关联,bridge 就会参与 mode_set、enable、disable 等显示控制流程。驱动程序通过 drm_bridge_funcs 中定义的回调函数,在合适的时机对桥接芯片进行配置。
- 实现级联与链式调用:多个 bridge 可以通过 next 指针链接起来。DRM 框架提供了一系列链式辅助函数(如 drm_bridge_chain_enable),会按照正确的顺序(从靠近 encoder 的到远离的)遍历整条链,调用每个 bridge 的对应操作,确保信号经过的所有中间设备都被正确使能。
- 对用户空间透明:与 drm_panel 类似,drm_bridge 不是像 CRTC、Plane 或 Connector 那样的 drm_mode_object。它对用户空间不可见,纯粹是内核驱动用来管理硬件的内部钩子。
其回调函数定义
struct drm_bridge_funcs { int (*attach)(struct drm_bridge *bridge); void (*detach)(struct drm_bridge *bridge); enum drm_mode_status (*mode_valid)(struct drm_bridge *bridge, const struct drm_display_mode *mode); bool (*mode_fixup)(struct drm_bridge *bridge,const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); void (*disable)(struct drm_bridge *bridge); void (*post_disable)(struct drm_bridge *bridge); void (*mode_set)(struct drm_bridge *bridge,const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode); void (*pre_enable)(struct drm_bridge *bridge); void (*enable)(struct drm_bridge *bridge); void (*atomic_pre_enable)(struct drm_bridge *bridge, struct drm_atomic_state *state); void (*atomic_enable)(struct drm_bridge *bridge, struct drm_atomic_state *state); void (*atomic_disable)(struct drm_bridge *bridge, struct drm_atomic_state *state); void (*atomic_post_disable)(struct drm_bridge *bridge, struct drm_atomic_state *state); };connector
简介
drm_connector 是显示器(或显示端点)的抽象对象,它负责管理物理接口的连接状态与显示能力。kms驱动必须:为一个显示接收器 创建、初始化、注册、绑定一个drm_connector,connector必须 绑定一个encoder以便使用
核心职责
- 探测与报告连接状态:这是它最动态的功能。通过 detect 回调或热插拔(HPD)中断,内核能感知显示器是否插入或拔出。状态(已连接/未连接/未知)会报告给用户空间。它必须被附加到一个 Encoder 上才能工作,通常是一对一的关系。
- 管理显示能力(EDID):当显示器连接后,Connector 负责通过 I2C 总线(DDC)读取其 EDID 数据。这些数据包含了显示器的原生分辨率、刷新率、支持的模式列表以及物理尺寸,是内核构建可用显示模式列表的基础。
承载标准属性:Connector 是内核向用户空间暴露控制接口的载体。它包含一系列标准属性,例如:- DPMS:控制电源状态(开/待机/挂起/关)。
- EDID Blob:向用户空间提供原始 EDID 数据。
- Content Protection (HDCP):用于请求和报告内容保护状态。
- HDR Metadata:用于传递 HDR 元数据。
- 区分连接器类型:通过 connector_type(如 HDMI-A、DisplayPort、eDP、LVDS)来标识物理接口类型。驱动必须为每个显示端点创建并注册一个 Connector 实例