KMS(Kernel Mode Setting)的框架如下,用户空间使用双framebuffer机制,分别对应两个plane对象。plane从drm_framebuffer获取显示数据。然后plane传送给crtc(lcdif控制器),经过编码后再送给显示屏(connector)进行显示。一般来说现在的soc都在lcdif外围设计了bridge ip来实现诸如mipi、lvds和hdmi的功能。这里芯片已经实现的bridge就是我们这里的drm encoder(显示编码器)。而在encoder下面外接转换芯片称为 drm bridge。
原子状态
atomic提供了modeset事务更新集,和尝试提交和回滚的事务不同的是:
①提交失败时不允许更改硬件状态。
②允许设置和回滚软件状态。
整体的原子状态分为成了plane,crtc和connector(drm_plane_state/drm_crtc_state/drm_connector_state
)。用户空间可以查看并修改这三个对象的原子状态。只有当状态被提交时,它才会应用于驱动程序和modeset集对象。这种方式回滚更新归结为释放内存和取消引用像帧缓冲区这样的对象。
帧缓冲
应用程序通过 DRM_IOCTL_MODE_ADDFB ioctl 显式请求创建帧缓冲区,并接收可以传递给 KMS CRTC 控制、plane配置和flip功能的不透明句柄。
帧缓冲区依赖底层内存管理器来分配后备存储。创建帧缓冲区时,应用程序通过参数传递内存句柄(或多平面格式的内存句柄列表) 。对于使用 GEM 作为其用户空间缓冲区管理接口的驱动程序,这将是一个 GEM 句柄。基于 GEM 的驱动程序应调用drm_gem_handle_create()以创建句柄。用户空间可以通过这个dirty回调通知驱动程序帧缓冲区的一个区域已经改变并且应该被刷新到显示硬件。
drm 帧缓冲区的生命周期由引用计数控制,驱动程序可以使用 drm_framebuffer_get()获取其他引用,并使用drm_framebuffer_put()再次删除它们。
struct drm_framebuffer {
struct drm_device *dev;帧缓冲区所属的 DRM 设备
struct list_head head;
struct drm_mode_object base;
char comm[TASK_COMM_LEN];
const struct drm_format_info *format;//帧缓冲格式信息
const struct drm_framebuffer_funcs *funcs;//帧缓冲 func 表
unsigned int pitches[DRM_FORMAT_MAX_PLANES];//每个缓冲区的行步长。对于用户空间创建的对象,这是从 drm_mode_fb_cmd2 复制的。
unsigned int offsets[DRM_FORMAT_MAX_PLANES];//每个缓冲区从缓冲区开始到实际像素数据的偏移量(以字节为单位)。
uint64_t modifier;//数据布局修饰符。这用于描述平铺或辅助缓冲区的特殊布局(如压缩)。
unsigned int width;
unsigned int height;
int flags;
int hot_x;
int hot_y;
struct list_head filp_head;
struct drm_gem_object *obj[DRM_FORMAT_MAX_PLANES];//支持帧缓冲区的 GEM 对象,每个平面一个。
};
请注意,offset这是一个线性偏移,不考虑每个修改器的平铺或缓冲层。当这个帧缓冲平面的实际像素数据从一个偏移量开始时使用它,例如,当在同一个后备存储缓冲区对象中分配多个平面时。对于平铺布局,这通常意味着它 的偏移量必须至少与平铺大小对齐,但硬件通常有更严格的要求。
这不应该用于将 x/y 像素偏移指定到缓冲区数据中(即使对于线性缓冲区)。指定 x/y 像素偏移是通过drm_plane_state。
plane
平面表示可以在扫描输出过程中与 CRTC 混合或覆盖在其顶部的图像源。plane从drm_framebuffer中获取输入数据。plane本身应该指定该图像的裁剪和缩放,以及可见区域的放置位置,还应指定像素如何混合和定位,如z位置/旋转。
每个 CRTC 必须有一个唯一的主平面用户空间可以附加以启用 CRTC。换句话说,用户空间必须能够同时将不同的主平面附加到每个 CRTC。主平面仍然可以与多个 CRTC 兼容。主平面的数量必须与 CRTC 的数量完全相同。
struct drm_plane_state {
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_framebuffer *fb;
struct dma_fence *fence;
int32_t crtc_x;//在crtc上的位置区域
int32_t crtc_y;
uint32_t crtc_w, crtc_h;
uint32_t src_x;//图像可见的位置区域
uint32_t src_y;
uint32_t src_h, src_w;
u16 alpha;
uint16_t pixel_blend_mode;//混和方式
unsigned int rotation;
unsigned int zpos; //平面在crtc上的优先级
unsigned int normalized_zpos;
enum drm_color_encoding color_encoding;
enum drm_color_range color_range;
struct drm_property_blob *fb_damage_clips;
struct drm_rect src, dst;
bool visible;
enum drm_scaling_filter scaling_filter;
struct drm_crtc_commit *commit;
struct drm_atomic_state *state;
};
请注意,目标坐标crtc_x、crtc_y、crtc_h和 crtc_w以及源坐标src_x、src_y、src_h和src_w是用户空间提供的原始坐标。驱动程序应该使用drm_atomic_helper_check_plane_state(),并且只使用src和dst中的派生矩形为硬件编程。
dumb buffer
KMS API 没有标准化支持存储对象的创建,而是将其留给特定于驱动程序的 ioctl。此外,即使对基于 GEM 的驱动程序,实际上创建缓冲区对象也是通过特定于驱动程序的 ioctl 完成的——GEM 只有一个通用的用户空间接口,用于共享和销毁对象。
dumb buffer通过提供标准 API 来创建适合扫描输出的dumb buffer部分缓解了该问题,然后可以使用该 API 创建 KMS 帧缓冲区。
为了支持dumb buffer,驱动程序必须实现drm_driver.dumb_create和drm_driver.dumb_map_offset。