上节讲到《DRM驱动(四)之ADD_FB》调用drmModeAddFB创建drm_framebuffer。然后通过

drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);

vaddr = mmap(0, create.size, PROT_READ | PROT_WRITE,MAP_SHARED, fd, map.offset);

将物理地址map到用户空间后,就可以在这块内存上绘一张自己喜欢的图。接下来就需要把这块内存配置到硬件上面,进行刷图。

刷图有很多中方式,比如:

int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId, uint32_t x, uint32_t y, uint32_t *connectors, int count, drmModeModeInfoPtr mode)

fd:文件描述符

crtcId:crtc_id

bufferId:即上节创建的fb id

x,y:在屏幕上的显示坐标

connectors:connector id

count:connector 数量

mode:包括刷新率,分辨率等timing的信息

也可以使用下面接口:

drmModeAtomicAlloc();

drmModeAtomicAddProperty(..., property_id, property_value);

drmModeAtomicCommit(...);

本节以drmModeSetCrtc为例

int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
		   uint32_t x, uint32_t y, uint32_t *connectors, int count,
		   drmModeModeInfoPtr mode)
{
	struct drm_mode_crtc crtc;

	memclear(crtc);
	crtc.x             = x;
	crtc.y             = y;
	crtc.crtc_id       = crtcId;
	crtc.fb_id         = bufferId;
	crtc.set_connectors_ptr = VOID2U64(connectors);
	crtc.count_connectors = count;
	if (mode) {
	  memcpy(&crtc.mode, mode, sizeof(struct drm_mode_modeinfo));
	  crtc.mode_valid = 1;
	}

	return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETCRTC, &crtc);
}

主要用于填充如下数据结构,传给内核,drm会根据传入的信息配置display硬件。比如crtc_id来指定显示到哪个crtc,fb_id指定哪块使用dumb显存等等。

struct drm_mode_crtc {
__u64 set_connectors_ptr;
__u32 count_connectors;
__u32 crtc_id; /**< Id */
__u32 fb_id; /**< Id of framebuffer */

__u32 x; /**< x Position on the framebuffer */
__u32 y; /**< y Position on the framebuffer */

__u32 gamma_size;
__u32 mode_valid;
struct drm_mode_modeinfo mode;
}

按照之前的经验,在内核drm_ioctl.c中找到对应函数调用

DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED)

DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED)

drm_mode_setcrtc和drm_mode_atomic_ioctl从函数调用来看都会调用drm_atomic_commit(state) 也就是说应用传入的参数最终会转换成struct drm_atomic_state;

看下struct drm_atomic_state数据结构

struct drm_atomic_state {
	struct kref ref;

	struct drm_device *dev;
	bool allow_modeset : 1;
	bool legacy_cursor_update : 1;
	bool async_update : 1;
	struct __drm_planes_state *planes;
	struct __drm_crtcs_state *crtcs;
	int num_connector;
	struct __drm_connnectors_state *connectors;
	int num_private_objs;
	struct __drm_private_objs_state *private_objs;

	struct drm_modeset_acquire_ctx *acquire_ctx;

    struct work_struct commit_work;
};

drm_atomic_state和其他组件state的继承关系如下图:

数据结构之间的转换过程:

int drm_mode_setcrtc(struct drm_device *dev, void *data,
		     struct drm_file *file_priv)
{
	crtc = drm_crtc_find(dev, file_priv, crtc_req->crtc_id);

	if (crtc_req->mode_valid) {

		fb = drm_framebuffer_lookup(dev, file_priv, crtc_req->fb_id);
		mode = drm_mode_create(dev);
		ret = drm_mode_convert_umode(mode, &crtc_req->mode);
	}
	if (crtc_req->count_connectors > 0) {
		connector_set = kmalloc_array(crtc_req->count_connectors,
					      sizeof(struct drm_connector *),
					      GFP_KERNEL);

		for (i = 0; i < crtc_req->count_connectors; i++) {
			connector_set[i] = NULL;
			set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
			connector = drm_connector_lookup(dev, file_priv, out_id);
			connector_set[i] = connector;
		}
	}

	set.crtc = crtc;
	set.x = crtc_req->x;
	set.y = crtc_req->y;
	set.mode = mode;
	set.connectors = connector_set;
	set.num_connectors = crtc_req->count_connectors;
	set.fb = fb;
	ret = __drm_mode_set_config_internal(&set, &ctx);

out:
	if (fb)
		drm_framebuffer_put(fb);

	if (connector_set) {
		for (i = 0; i < crtc_req->count_connectors; i++) {
			if (connector_set[i])
				drm_connector_put(connector_set[i]);
		}
	}
	kfree(connector_set);
	drm_mode_destroy(dev, mode);

	return ret;
}

drm_mode_setcrtc的主要作用:

  1. 根据应用传入的crtc_id找到crtc
  2. 根据应用传入的fb_id,找到对应的drm_framebuffer
  3. 根据应用传入的mode,创建一个drm_display_mode
  4. 根据传入的set_connectors_ptr,找到驱动对应的connector
  5. 将以上信息转为struct drm_mode_set并调用__drm_mode_set_config_internal
static int __drm_mode_set_config_internal(struct drm_mode_set *set,
					  struct drm_modeset_acquire_ctx *ctx)
{
	struct drm_crtc *crtc = set->crtc;
	struct drm_framebuffer *fb;
	struct drm_crtc *tmp;
	drm_for_each_crtc(tmp, crtc->dev)
		tmp->primary->old_fb = tmp->primary->fb;

	fb = set->fb;
	
	ret = drm_atomic_helper_set_config(set, ctx);
	if (ret == 0) {
		crtc->primary->crtc = crtc;
		crtc->primary->fb = fb;
	}

	return ret;
}

__drm_mode_set_config_internal的内容比较少,主要调用drm_atomic_helper_set_config

int drm_atomic_helper_set_config(struct drm_mode_set *set,
				 struct drm_modeset_acquire_ctx *ctx)
{
	struct drm_atomic_state *state;
	struct drm_crtc *crtc = set->crtc;
	int ret = 0;
	state = drm_atomic_state_alloc(crtc->dev);

	ret = __drm_atomic_helper_set_config(set, state);

	ret = drm_atomic_commit(state);
fail:
	drm_atomic_state_put(state);
	return ret;
}

drm_atomic_helper_set_config的主要作用:

  1. 创建struct drm_atomic_state
  2. 将struct drm_mode_set转换成为struct drm_atomic_state
  3. 调用drm_atomic_commit 将修改commit到硬件
int __drm_atomic_helper_set_config(struct drm_mode_set *set,
		struct drm_atomic_state *state)
{
	struct drm_crtc_state *crtc_state;
	struct drm_plane_state *primary_state;
	struct drm_crtc *crtc = set->crtc;
	int hdisplay, vdisplay;
	int ret;

	crtc_state = drm_atomic_get_crtc_state(state, crtc);

	primary_state = drm_atomic_get_plane_state(state, crtc->primary);

	ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode);

	crtc_state->active = true;

	ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);

	drm_mode_get_hv_timing(set->mode, &hdisplay, &vdisplay);

	drm_atomic_set_fb_for_plane(primary_state, set->fb);
	primary_state->crtc_x = 0;
	primary_state->crtc_y = 0;
	primary_state->crtc_w = hdisplay;
	primary_state->crtc_h = vdisplay;
	primary_state->src_x = set->x << 16;
	primary_state->src_y = set->y << 16;
	if (drm_rotation_90_or_270(primary_state->rotation)) {
		primary_state->src_w = vdisplay << 16;
		primary_state->src_h = hdisplay << 16;
	} else {
		primary_state->src_w = hdisplay << 16;
		primary_state->src_h = vdisplay << 16;
	}

	ret = update_output_state(state, set);

	return 0;
}

这块比较代码有点复杂,这里简单的用一个表格说明几个比较重要的结构体数据之间的对应关系,不再赘述代码里的内容。

本节介绍了应用调用drmModeSetCrtc时传入参数转换为struct drm_atomic_state的过程,下节将介绍drm利用drm_atomic_state中内容更新图像的过程。

注:文中代码仅作说明,删除了一些错误处理等内容,介意可以看下drm驱动的源码。

Logo

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

更多推荐