目录
1. drm_mode_setcrtc
1.1 根据应用传入的crtc_id找到crtc
1.2 根据应用传入的fb_id,找到对应的drm_framebuffer
1.3 根据应用传入的mode,创建一个drm_display_mode
1.4 根据传入的set_connectors_ptr,找到驱动对应的connector
1.5 将以上信息转为struct drm_mode_set并调用__drm_mode_set_config_internal
1.6 __drm_mode_set_config_internal
2. drm_atomic_helper_set_config
3. 流程图
本文分析一下drm_mode_setcrtc。
1. drm_mode_setcrtc
1.1 根据应用传入的crtc_id找到crtc
int drm_mode_setcrtc(struct drm_device *dev, void *data,struct drm_file *file_priv)
{......crtc = drm_crtc_find(dev, crtc_req->crtc_id);if (!crtc) {DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);return -ENOENT;}DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
1.2 根据应用传入的fb_id,找到对应的drm_framebuffer
if (crtc_req->mode_valid) {/* If we have a mode we need a framebuffer. *//* If we pass -1, set the mode with the currently bound fb */if (crtc_req->fb_id == -1) {if (!crtc->primary->fb) {DRM_DEBUG_KMS("CRTC doesn't have current FB\n");ret = -EINVAL;goto out;}fb = crtc->primary->fb;/* Make refcounting symmetric with the lookup path. */drm_framebuffer_get(fb);} else {fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);if (!fb) {DRM_DEBUG_KMS("Unknown FB ID%d\n",crtc_req->fb_id);ret = -ENOENT;goto out;}
1.3 根据应用传入的mode,创建一个drm_display_mode
mode = drm_mode_create(dev);if (!mode) {ret = -ENOMEM;goto out;}ret = drm_mode_convert_umode(mode, &crtc_req->mode);if (ret) {DRM_DEBUG_KMS("Invalid mode\n");goto out;}/** Check whether the primary plane supports the fb pixel format.* Drivers not implementing the universal planes API use a* default formats list provided by the DRM core which doesn't* match real hardware capabilities. Skip the check in that* case.*/if (!crtc->primary->format_default) {ret = drm_plane_check_pixel_format(crtc->primary,fb->format->format);if (ret) {struct drm_format_name_buf format_name;DRM_DEBUG_KMS("Invalid pixel format %s\n",drm_get_format_name(fb->format->format,&format_name));goto out;}}ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,mode, fb);if (ret)goto out;
1.4 根据传入的set_connectors_ptr,找到驱动对应的connector
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;if (get_user(out_id, &set_connectors_ptr[i])) {ret = -EFAULT;goto out;}connector = drm_connector_lookup(dev, out_id);if (!connector) {DRM_DEBUG_KMS("Connector id %d unknown\n",out_id);ret = -ENOENT;goto out;}DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",connector->base.id,connector->name);connector_set[i] = connector;}
1.5 将以上信息转为struct drm_mode_set并调用__drm_mode_set_config_internal
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);
1.6 __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;int ret;/** NOTE: ->set_config can also disable other crtcs (if we steal all* connectors from it), hence we need to refcount the fbs across all* crtcs. Atomic modeset will have saner semantics ...*/drm_for_each_crtc(tmp, crtc->dev)tmp->primary->old_fb = tmp->primary->fb;fb = set->fb;ret = crtc->funcs->set_config(set, ctx);if (ret == 0) {crtc->primary->crtc = crtc;crtc->primary->fb = fb;}drm_for_each_crtc(tmp, crtc->dev) {if (tmp->primary->fb)drm_framebuffer_get(tmp->primary->fb);if (tmp->primary->old_fb)drm_framebuffer_put(tmp->primary->old_fb);tmp->primary->old_fb = NULL;}return ret;
}
只要是调用ret = crtc->funcs->set_config(set, ctx),即drm_atomic_helper_set_config,
static const struct drm_crtc_funcs malidp_crtc_funcs = {.gamma_set = drm_atomic_helper_legacy_gamma_set,.destroy = drm_crtc_cleanup,.set_config = drm_atomic_helper_set_config,.page_flip = drm_atomic_helper_page_flip,.reset = malidp_crtc_reset,.atomic_duplicate_state = malidp_crtc_duplicate_state,.atomic_destroy_state = malidp_crtc_destroy_state,.enable_vblank = malidp_crtc_enable_vblank,.disable_vblank = malidp_crtc_disable_vblank,
};
2. 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);if (!state)return -ENOMEM;state->acquire_ctx = ctx;ret = __drm_atomic_helper_set_config(set, state);if (ret != 0)goto fail;ret = handle_conflicting_encoders(state, true);if (ret)goto fail;ret = drm_atomic_commit(state);fail:drm_atomic_state_put(state);return ret;
}
drm_atomic_helper_set_config主要涉及到结构体struct drm_atomic_state和drm_atomic_commit函数,我们后面会单独讨论这2个。