Framebuffer

帧缓冲区是抽象的内存对象,提供了扫描到 CRTC 的像素源。应用程序通过 IOCTLDRM_IOCTL_MODE_ADDFB(2)来创建 framebuffer,会返回一个不透明句柄,该句柄可以传递给 KMS CRTC、来控制 plane configuration 和 page flip 功能。

framebuffer 依赖底层的内存管理器来分配内存。创建 framebuffer 时,app 需要通过struct drm_mode_fb_cmd2传入一个 memory handle。

Data structures and apis

struct drm_framebuffer {
	struct drm_device *dev;
	struct list_head head; // framebuffer 链表,可能有多个 fb
	struct drm_mode_object base;
	char comm[TASK_COMM_LEN]; // allocate fb 的进程名
	const struct drm_format_info *format; // fb pixel format
	const struct drm_framebuffer_funcs *funcs;
	// 一行多少 bytes, 会从用户空间的 drm_mode_fb_cmd2 拷贝过来
	unsigned int pitches[DRM_FORMAT_MAX_PLANES];
	// framebuffer 和 actual pixel data 的 offset,也从 drm_mode_fb_cmd2 拷贝过来
	unsigned int offsets[DRM_FORMAT_MAX_PLANES];
	uint64_t modifier; // 从 drm_mode_fb_cmd2 的 modifier 拷贝过来,DRM_FORMAT_MOD_XXX
	unsigned int width; // framebuffer 宽
	unsigned int height; // framebuffer 高
	int flags; // DRM_MODE_FB_INTERLACED, DRM_MODE_FB_MODIFIERS
	struct list_head filp_head;
	struct drm_gem_object *obj[DRM_FORMAT_MAX_PLANES];
};
struct drm_framebuffer_funcs {
	void (*destroy)(struct drm_framebuffer *framebuffer);
	int (*create_handle)(struct drm_framebuffer *fb,
			     struct drm_file *file_priv,
			     unsigned int *handle);
	// 有些硬件在 fb 内容更新后不会主动刷新内容到屏幕上。
	// userspace 需要通过 DRM_IOCTL_MODE_DIRTYFB ioctl 调用到 dirty 函数来刷新屏幕的某块区域。
	int (*dirty)(struct drm_framebuffer *framebuffer,
		     struct drm_file *file_priv, unsigned flags,
		     unsigned color, struct drm_clip_rect *clips,
		     unsigned num_clips);
};

注册 framebuffer 流程

用户空间通过 drmModeAddFB2()drmModeAddFB2WithModifiers() 函数来注册 framebuffer。

struct drm_mode_fb_cmd2 {
	__u32 fb_id;
	__u32 width;
	__u32 height;
	__u32 pixel_format;
	__u32 flags;
	__u32 handles[4];
	__u32 pitches[4];
	__u32 offsets[4];
	__u64 modifier[4];
};

app 传入:

width: fb 的宽。
height: fb 的高。
pixel_format: fourcc 格式的 format, DRM_FORMAT_XXXX drm_fourcc.h。
flags: 可传入 DRM_MODE_FB_INTERLACED 和 DRM_MODE_FB_MODIFIERS。
handles: 某个 plane 的内存 handle, 从 DRM_IOCTL_MODE_CREATE_DUMB ioctrl 获得。
pitches: 某个 plane 一行的 bytes 数。
offsets: 某个 plane 和 fb 起始地址的 offset, 比如 yuv420 planer 格式,那么第二个 plane 和 fb 是有 offset 的。
modifier: DRM_FORMAT_MOD_XXX,不同厂商自定义的 format 额外信息,4 个 modifier 都要和 modifier[0] 相同。
另外 DRM_MODE_FB_MODIFIERS 需要被置起,所有 plane 的 modifier 需要相同。
底层 driver 一般还需要实现 get_format_info 回调来获取自定义 format。

kernel 返回:

fb_id: 注册的 fb id。

注册 framebuffer 流程:

drm_mode_addfb2_ioctl();
	drm_mode_addfb2();
		drm_internal_framebuffer_create();
			framebuffer_check();
			dev->mode_config.funcs->fb_create();

.fb_create回调有两个 drm 通用的函数,drm_gem_fb_createdrm_gem_fb_create_with_dirty

struct drm_framebuffer *drm_gem_fb_create(struct drm_device *dev,
		struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
{
	return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
					    &drm_gem_fb_funcs);
}

struct drm_framebuffer * drm_gem_fb_create_with_dirty(struct drm_device *dev,
		struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd)
{
	return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
					    &drm_gem_fb_funcs_dirtyfb);
}

可以看出区别是传入drm_gem_fb_create_with_funcdrm_framebuffer_funcs不同,多实现了一个.dirty回调,该回调的作用见 drm_framebuffer_funcs 中的注释。

static const struct drm_framebuffer_funcs drm_gem_fb_funcs = {
	.destroy	= drm_gem_fb_destroy,
	.create_handle	= drm_gem_fb_create_handle,
};

static const struct drm_framebuffer_funcs drm_gem_fb_funcs_dirtyfb = {
	.destroy	= drm_gem_fb_destroy,
	.create_handle	= drm_gem_fb_create_handle,
	.dirty		= drm_atomic_helper_dirtyfb,
};
drm_gem_fb_create_with_funcs();
	drm_gem_fb_init_with_funcs();
		drm_gem_fb_init();
			fb->obj[i] = obj[i];
			drm_helper_mode_fill_fb_struct(); /// 根据 userspace 传入的 mode_cmd,填充 fb 结构体
				fb->dev = dev;
				fb->format = drm_get_format_info(dev, mode_cmd);
				fb->width = mode_cmd->width;
				fb->height = mode_cmd->height;
				for (i = 0; i < 4; i++) {
					fb->pitches[i] = mode_cmd->pitches[i];
					fb->offsets[i] = mode_cmd->offsets[i];
				}
				fb->modifier = mode_cmd->modifier[0];
				fb->flags = mode_cmd->flags;
			drm_framebuffer_init(); /// 填充 fb->funcs 和 drm_mode_object
				fb->funcs = funcs;
				strcpy(fb->comm, current->comm);
				list_add(&fb->head, &dev->mode_config.fb_list);
				__drm_mode_object_add();
  %%{init: {'theme': 'default' }}%%
flowchart LR
1("drm_mode_addfb2_ioctl") --> 2("drm_mode_addfb2")
2 --> 3("drm_internal_framebuffer_create") & 4("r->fb_id = fb->base.id<br>list_add(&fb->filp_head, &file_priv->fbs);"):::yellow

3 --> 5("framebuffer_check") & 6(".fb_create<br>(drm_gem_fb_create)<br>(drm_gem_fb_create_with_dirty)")

classDef yellow fill:#fdfd00,color:#000000,stroke:#e0e000
  %%{init: {'theme': 'default' }}%%
flowchart LR

1(".fb_create<br>drm_gem_fb_create") --> 2("fb = kzalloc(sizeof(*fb))<br>"):::yellow & 3("drm_gem_fb_init_with_funcs")
3 -- get drm_format_info from app fourcc code--> 4("drm_get_format_info")
3 --> 5("drm_gem_fb_init")
5 --> 6("drm_helper_mode_fill_fb_struct")
6 --> 9("fb->dev = dev<br>fb->format = drm_get_format_info(dev, mode_cmd)<br>fb->width = mode_cmd->width<br>..."):::yellow
5 -- fb->obj为gem obj--> 7("fb->obj[i] = obj[i];"):::yellow
5 --> 8("drm_framebuffer_init")
8 --> 10("fb->funcs = funcs<br>list_add(&fb->head, &dev->mode_config.fb_list)"):::yellow

classDef yellow fill:#fdfd00,color:#000000,stroke:#e0e000

apis:

int drm_framebuffer_init(struct drm_device *dev,
			 struct drm_framebuffer *fb,
			 const struct drm_framebuffer_funcs *funcs);
struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
					       struct drm_file *file_priv,
					       uint32_t id);
void drm_framebuffer_cleanup(struct drm_framebuffer *fb);
static inline void drm_framebuffer_get(struct drm_framebuffer *fb);
static inline void drm_framebuffer_put(struct drm_framebuffer *fb);
static inline uint32_t drm_framebuffer_read_refcount(const struct drm_framebuffer *fb);
static inline void drm_framebuffer_assign(struct drm_framebuffer **p,
					  struct drm_framebuffer *fb);
#define drm_for_each_fb(fb, dev);

DRM Format Handling

在 DRM 子系统中,framebuffer 的 pixel format 使用在 include/uapi/drm/drm_fourcc.h 中定义的 fourcc codes 来描述。除了 fourcc 外,还可以选择性地提供一个 Format Modifier,用于进一步描述 fb 的格式 - 例如平铺(tiling)或压缩(compression)。

Format Modifiers

// TODO:

Data structures and apis

struct drm_format_info {
	u32 format; // FOURCC 格式,DRM_FORMAT_*
	u8 depth; // color depth, legacy field, 设置为 0。
	u8 num_planes; // Number of color planes (1 to 3)
	union {
		// 每个 plane 的 bytes per pixel, legacy field。
		u8 cpp[DRM_FORMAT_MAX_PLANES];
		// 每个 plane 的 bytes per block。用于单个 pixel 不是 byte 对齐的情况。
		// block 大小用下面的 block_w 和 block_h 来描述。
		u8 char_per_block[DRM_FORMAT_MAX_PLANES];
	};
	u8 block_w[DRM_FORMAT_MAX_PLANES]; // block width 占几个 bytes
	u8 block_h[DRM_FORMAT_MAX_PLANES]; // block height 占几个 bytes
	u8 hsub; // 行采样因子
	u8 vsub; // 列采样因子,比如 yuv422 那么 hsub=2, vsub=1
	bool has_alpha; // pixel format 中是否含有 alpha
	bool is_yuv; // 是不是 yuv 格式
	bool is_color_indexed; // 是不是 color_indexed 格式,即伪彩,存 index 进 color LUT 查找对应颜色
};