书接上回,使用drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create)接口创建了一块内存放在了gem cma的对象里。

gem obj创建之后需要与一块drm_framebuffer进行绑定使用,因为最终刷图使用的是fb。使用下面接口会创建drm_framebuffer并与上节创建的gem obj进行绑定。

int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth,uint8_t bpp, uint32_t pitch, uint32_t bo_handle,uint32_t *buf_id) //老接口

目前用的比较多的是下面两个

int drmModeAddFB2(int fd, uint32_t width, uint32_t height,  uint32_t pixel_format, const uint32_t bo_handles[4],  const uint32_t pitches[4], const uint32_t offsets[4], uint32_t *buf_id, uint32_t flags) 

int drmModeAddFB2WithModifiers(int fd, uint32_t width,  uint32_t height, uint32_t pixel_format, const uint32_t bo_handles[4],  const uint32_t pitches[4], const uint32_t offsets[4], const uint64_t modifier[4], uint32_t *buf_id, uint32_t flags)

重点介绍drmModeAddFB,因为drmModeAddFB会调用add_fb2

上面接口完成了如下的功能

user space的接口就不多说,比较简单,重点看下kernel中的实现

kernel:

后面的介绍全是围绕drm_framebuffer,先看下drm_framebuffer是何方神圣

struct drm_framebuffer {
	struct drm_device *dev;
	struct list_head head;
	struct drm_mode_object base;
	const struct drm_format_info *format;
	const struct drm_framebuffer_funcs *funcs;
	unsigned int pitches[4];
	unsigned int offsets[4];
	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[4];
};

其中比较重要的字段format, pitches,offsets,width,height,obj等。

通过应用层ioctl会调到drm的drm_mode_addfb

int drm_mode_addfb(struct drm_device *dev,
 void *data, struct drm_file *file_priv)
{
	struct drm_mode_fb_cmd *or = data;
	struct drm_mode_fb_cmd2 r = {};
	int ret;

	/* convert to new format and call new ioctl */
	r.fb_id = or->fb_id;
	r.width = or->width;
	r.height = or->height;
	r.pitches[0] = or->pitch;
	r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
	r.handles[0] = or->handle;

	if (r.pixel_format == DRM_FORMAT_XRGB2101010 &&
	    dev->driver->driver_features & DRIVER_PREFER_XBGR_30BPP)
		r.pixel_format = DRM_FORMAT_XBGR2101010;

	ret = drm_mode_addfb2(dev, &r, file_priv);
	if (ret)
		return ret;

	or->fb_id = r.fb_id;
	return 0;
}

可以看到bpp和depth的作用是找到对应的format。

大致看下drm_mode_legacy_fb_format的实现,不过多介绍,因为大多已经不使用drm_mode_addfb,而是使用drm_mode_addfb2。

uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
{
	uint32_t fmt;

	switch (bpp) {
	case 8:
		fmt = DRM_FORMAT_C8;
		break;
	case 16:
		if (depth == 15)
			fmt = DRM_FORMAT_XRGB1555;
		else
			fmt = DRM_FORMAT_RGB565;
		break;
	case 24:
		fmt = DRM_FORMAT_RGB888;
		break;
	case 32:
		if (depth == 24)
			fmt = DRM_FORMAT_XRGB8888;
		else if (depth == 30)
			fmt = DRM_FORMAT_XRGB2101010;
		else
			fmt = DRM_FORMAT_ARGB8888;
		break;
	default:
		DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n");
		fmt = DRM_FORMAT_XRGB8888;
		break;
	}

	return fmt;
}

drm_mode_addfb会调用drm_mode_addfb2。从代码可以看到addfb2里检查了一下权限,就直接调用的drm_internal_framebuffer_create。(下面代码有进行删减)

int drm_mode_addfb2(struct drm_device *dev,
		    void *data, struct drm_file *file_priv)
{
	struct drm_mode_fb_cmd2 *r = data;
	struct drm_framebuffer *fb;

	fb = drm_internal_framebuffer_create(dev, r, file_priv);

	r->fb_id = fb->base.id;

	list_add(&fb->filp_head, &file_priv->fbs);
}

drm_internal_framebuffer_create检查传入参数是否合法,然后调用

dev->mode_config.funcs->fb_create(dev, file_priv, r)

此回调是在drm驱动注册时初始化的,以imx的为例使用的drm提供的创建函数;此函数可以自己实现,而很多芯片厂商都是根据自己需求实现。我们学习,就简单分析下drm提供默认创建fb的函数

static const struct drm_mode_config_funcs mxsfb_mode_config_funcs = {
	.fb_create		= drm_fb_cma_create,
	.atomic_check		= drm_atomic_helper_check,
	.atomic_commit		= drm_atomic_helper_commit,
};

而drm_fb_cma_create->drm_gem_fb_create->drm_gem_fb_create_with_funcs

drm_gem_fb_create_with_funcs通过应用提供的handle,找到上次创建的gem obj。然后调用drm_gem_fb_alloc,分配一个drm_framebuffer,将gem obj赋值给framebuffer中的obj。

struct drm_framebuffer *
drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
			     const struct drm_mode_fb_cmd2 *mode_cmd,
			     const struct drm_framebuffer_funcs *funcs)
{
	const struct drm_format_info *info;
	struct drm_gem_object *objs[4];
	struct drm_framebuffer *fb;
	int ret, i;

	for (i = 0; i < info->num_planes; i++) {
		unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
		unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
		unsigned int min_size;

		objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);

		min_size = (height - 1) * mode_cmd->pitches[i]
			 + width * info->cpp[i]
			 + mode_cmd->offsets[i];

		if (objs[i]->size < min_size) {
			drm_gem_object_put_unlocked(objs[i]);
			ret = -EINVAL;
			goto err_gem_object_put;
		}
	}

	fb = drm_gem_fb_alloc(dev, mode_cmd, objs, i, funcs);
	return fb;
}
static struct drm_framebuffer *
drm_gem_fb_alloc(struct drm_device *dev,
		 const struct drm_mode_fb_cmd2 *mode_cmd,
		 struct drm_gem_object **obj, unsigned int num_planes,
		 const struct drm_framebuffer_funcs *funcs)
{
	struct drm_framebuffer *fb;
	int ret, i;

	fb = kzalloc(sizeof(*fb), GFP_KERNEL);

	drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);

	for (i = 0; i < num_planes; i++)
		fb->obj[i] = obj[i];

	ret = drm_framebuffer_init(dev, fb, funcs);

	return fb;
}

将应用传入的长,宽,format,pitch,flag等赋值给drm_framebuffer。

void drm_helper_mode_fill_fb_struct(struct drm_device *dev,
				    struct drm_framebuffer *fb,
				    const struct drm_mode_fb_cmd2 *mode_cmd)
{
	int i;

	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;
}

相关字段填充完成后会将fb添加进dev->mode_config.fb_list便于后面使用的时候通过id进行查找,

int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
			 const struct drm_framebuffer_funcs *funcs)
{
	int ret;


	INIT_LIST_HEAD(&fb->filp_head);

	fb->funcs = funcs;
	ret = __drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB,
				    false, drm_framebuffer_free);

	dev->mode_config.num_fb++;
	list_add(&fb->head, &dev->mode_config.fb_list);

	drm_mode_object_register(dev, &fb->base);
	return ret;
}

drm的组件都是通过dev->mode_config中的链表进行管理的,只要拿到mode_config就可以拿到drm相关信息

  • struct list_head fb_list

至此drm_framebuffer已经创建完毕。通过r->fb_id = fb->base.id;将fb_id返回给用户。

大致流程:

总结:

        整个流程就是创建一个drm_framebuffer并根据应用提供的参数进行填充对应字段(为了后面驱动根据对应字段配置显示的寄存器)。然后添加进mode_config的fb_list,返回给fb_id给用户。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐