4.13.4. crtc代码分析
4.13.4.1. struct drm_crtc结构体
struct drm_crtc {
/** @dev: parent DRM device */
struct drm_device *dev;
/** @port: OF node used by drm_of_find_possible_crtcs(). */
struct device_node *port;
/**
* @head:
*
* List of all CRTCs on @dev, linked from &drm_mode_config.crtc_list.
* Invariant over the lifetime of @dev and therefore does not need
* locking.
*/
struct list_head head;
/** @name: human readable name, can be overwritten by the driver */
char *name;
/**
* @mutex:
*
* This provides a read lock for the overall CRTC state (mode, dpms
* state, ...) and a write lock for everything which can be update
* without a full modeset (fb, cursor data, CRTC properties ...). A full
* modeset also need to grab &drm_mode_config.connection_mutex.
*
* For atomic drivers specifically this protects @state.
*/
struct drm_modeset_lock mutex;
/** @base: base KMS object for ID tracking etc. */
struct drm_mode_object base;
/**
* @primary:
* Primary plane for this CRTC. Note that this is only
* relevant for legacy IOCTL, it specifies the plane implicitly used by
* the SETCRTC and PAGE_FLIP IOCTLs. It does not have any significance
* beyond that.
*/
struct drm_plane *primary;
/**
* @cursor:
* Cursor plane for this CRTC. Note that this is only relevant for
* legacy IOCTL, it specifies the plane implicitly used by the SETCURSOR
* and SETCURSOR2 IOCTLs. It does not have any significance
* beyond that.
*/
struct drm_plane *cursor;
/**
* @index: Position inside the mode_config.list, can be used as an array
* index. It is invariant over the lifetime of the CRTC.
*/
unsigned index;
/**
* @cursor_x: Current x position of the cursor, used for universal
* cursor planes because the SETCURSOR IOCTL only can update the
* framebuffer without supplying the coordinates. Drivers should not use
* this directly, atomic drivers should look at &drm_plane_state.crtc_x
* of the cursor plane instead.
*/
int cursor_x;
/**
* @cursor_y: Current y position of the cursor, used for universal
* cursor planes because the SETCURSOR IOCTL only can update the
* framebuffer without supplying the coordinates. Drivers should not use
* this directly, atomic drivers should look at &drm_plane_state.crtc_y
* of the cursor plane instead.
*/
int cursor_y;
/**
* @enabled:
*
* Is this CRTC enabled? Should only be used by legacy drivers, atomic
* drivers should instead consult &drm_crtc_state.enable and
* &drm_crtc_state.active. Atomic drivers can update this by calling
* drm_atomic_helper_update_legacy_modeset_state().
*/
bool enabled;
/**
* @mode:
*
* Current mode timings. Should only be used by legacy drivers, atomic
* drivers should instead consult &drm_crtc_state.mode. Atomic drivers
* can update this by calling
* drm_atomic_helper_update_legacy_modeset_state().
*/
struct drm_display_mode mode;
/**
* @hwmode:
*
* Programmed mode in hw, after adjustments for encoders, crtc, panel
* scaling etc. Should only be used by legacy drivers, for high
* precision vblank timestamps in
* drm_calc_vbltimestamp_from_scanoutpos().
*
* Note that atomic drivers should not use this, but instead use
* &drm_crtc_state.adjusted_mode. And for high-precision timestamps
* drm_calc_vbltimestamp_from_scanoutpos() used &drm_vblank_crtc.hwmode,
* which is filled out by calling drm_calc_timestamping_constants().
*/
struct drm_display_mode hwmode;
/**
* @x:
* x position on screen. Should only be used by legacy drivers, atomic
* drivers should look at &drm_plane_state.crtc_x of the primary plane
* instead. Updated by calling
* drm_atomic_helper_update_legacy_modeset_state().
*/
int x;
/**
* @y:
* y position on screen. Should only be used by legacy drivers, atomic
* drivers should look at &drm_plane_state.crtc_y of the primary plane
* instead. Updated by calling
* drm_atomic_helper_update_legacy_modeset_state().
*/
int y;
/** @funcs: CRTC control functions */
const struct drm_crtc_funcs *funcs;
/**
* @gamma_size: Size of legacy gamma ramp reported to userspace. Set up
* by calling drm_mode_crtc_set_gamma_size().
*/
uint32_t gamma_size;
/**
* @gamma_store: Gamma ramp values used by the legacy SETGAMMA and
* GETGAMMA IOCTls. Set up by calling drm_mode_crtc_set_gamma_size().
*/
uint16_t *gamma_store;
/** @helper_private: mid-layer private data */
const struct drm_crtc_helper_funcs *helper_private;
/** @properties: property tracking for this CRTC */
struct drm_object_properties properties;
/**
* @state:
*
* Current atomic state for this CRTC.
*
* This is protected by @mutex. Note that nonblocking atomic commits
* access the current CRTC state without taking locks. Either by going
* through the &struct drm_atomic_state pointers, see
* for_each_oldnew_crtc_in_state(), for_each_old_crtc_in_state() and
* for_each_new_crtc_in_state(). Or through careful ordering of atomic
* commit operations as implemented in the atomic helpers, see
* &struct drm_crtc_commit.
*/
struct drm_crtc_state *state;
/**
* @commit_list:
*
* List of &drm_crtc_commit structures tracking pending commits.
* Protected by @commit_lock. This list holds its own full reference,
* as does the ongoing commit.
*
* "Note that the commit for a state change is also tracked in
* &drm_crtc_state.commit. For accessing the immediately preceding
* commit in an atomic update it is recommended to just use that
* pointer in the old CRTC state, since accessing that doesn't need
* any locking or list-walking. @commit_list should only be used to
* stall for framebuffer cleanup that's signalled through
* &drm_crtc_commit.cleanup_done."
*/
struct list_head commit_list;
/**
* @commit_lock:
*
* Spinlock to protect @commit_list.
*/
spinlock_t commit_lock;
#ifdef CONFIG_DEBUG_FS
/**
* @debugfs_entry:
*
* Debugfs directory for this CRTC.
*/
struct dentry *debugfs_entry;
#endif
/**
* @crc:
*
* Configuration settings of CRC capture.
*/
struct drm_crtc_crc crc;
/**
* @fence_context:
*
* timeline context used for fence operations.
*/
unsigned int fence_context;
/**
* @fence_lock:
*
* spinlock to protect the fences in the fence_context.
*/
spinlock_t fence_lock;
/**
* @fence_seqno:
*
* Seqno variable used as monotonic counter for the fences
* created on the CRTC's timeline.
*/
unsigned long fence_seqno;
/**
* @timeline_name:
*
* The name of the CRTC's fence timeline.
*/
char timeline_name[32];
/**
* @self_refresh_data: Holds the state for the self refresh helpers
*
* Initialized via drm_self_refresh_helper_init().
*/
struct drm_self_refresh_data *self_refresh_data;
};
4.13.4.2. crtc相关的API
drm_crtc_init_with_planes
int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
struct drm_plane *primary,
struct drm_plane *cursor,
const struct drm_crtc_funcs *funcs,
const char *name, ...)
{
struct drm_mode_config *config = &dev->mode_config;
int ret;
WARN_ON(primary && primary->type != DRM_PLANE_TYPE_PRIMARY);
WARN_ON(cursor && cursor->type != DRM_PLANE_TYPE_CURSOR);
/* crtc index is used with 32bit bitmasks */
if (WARN_ON(config->num_crtc >= 32))
return -EINVAL;
WARN_ON(drm_drv_uses_atomic_modeset(dev) &&
(!funcs->atomic_destroy_state ||
!funcs->atomic_duplicate_state));
crtc->dev = dev;
crtc->funcs = funcs;
INIT_LIST_HEAD(&crtc->commit_list);
spin_lock_init(&crtc->commit_lock);
drm_modeset_lock_init(&crtc->mutex);
//创建类型为DRM_MODE_OBJECT_CRTC的struct drm_mode_object结构体
ret = drm_mode_object_add(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
if (ret)
return ret;
//设置名称
if (name) {
va_list ap;
va_start(ap, name);
crtc->name = kvasprintf(GFP_KERNEL, name, ap);
va_end(ap);
} else {
crtc->name = kasprintf(GFP_KERNEL, "crtc-%d",
drm_num_crtcs(dev));
}
if (!crtc->name) {
drm_mode_object_unregister(dev, &crtc->base);
return -ENOMEM;
}
crtc->fence_context = dma_fence_context_alloc(1);
spin_lock_init(&crtc->fence_lock);
snprintf(crtc->timeline_name, sizeof(crtc->timeline_name),
"CRTC:%d-%s", crtc->base.id, crtc->name);
//初始化properties
crtc->base.properties = &crtc->properties;
list_add_tail(&crtc->head, &config->crtc_list);
crtc->index = config->num_crtc++;
/初始化primary plane, cursor plane
crtc->primary = primary;
crtc->cursor = cursor;
if (primary && !primary->possible_crtcs)
primary->possible_crtcs = drm_crtc_mask(crtc);
if (cursor && !cursor->possible_crtcs)
cursor->possible_crtcs = drm_crtc_mask(crtc);
//调试相关的初始化
ret = drm_crtc_crc_init(crtc);
if (ret) {
drm_mode_object_unregister(dev, &crtc->base);
return ret;
}
//attach一些properties变量
if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
drm_object_attach_property(&crtc->base, config->prop_active, 0);
drm_object_attach_property(&crtc->base, config->prop_mode_id, 0);
drm_object_attach_property(&crtc->base,
config->prop_out_fence_ptr, 0);
drm_object_attach_property(&crtc->base,
config->prop_vrr_enabled, 0);
}
return 0;
}
drm_mode_setcrtc
int drm_mode_setcrtc(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_mode_config *config = &dev->mode_config;
struct drm_mode_crtc *crtc_req = data;
struct drm_crtc *crtc;
struct drm_plane *plane;
struct drm_connector **connector_set = NULL, *connector;
struct drm_framebuffer *fb = NULL;
struct drm_display_mode *mode = NULL;
struct drm_mode_set set;
uint32_t __user *set_connectors_ptr;
struct drm_modeset_acquire_ctx ctx;
int ret;
int i;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EOPNOTSUPP;
/*
* Universal plane src offsets are only 16.16, prevent havoc for
* drivers using universal plane code internally.
*/
if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000)
return -ERANGE;
//找到需要的crtc实体
crtc = drm_crtc_find(dev, file_priv, 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);
plane = crtc->primary;
/* allow disabling with the primary plane leased */
if (crtc_req->mode_valid && !drm_lease_held(file_priv, plane->base.id))
return -EACCES;
mutex_lock(&crtc->dev->mode_config.mutex);
DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx,
DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret);
//得到或者生成primary plane的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) {
struct drm_framebuffer *old_fb;
if (plane->state)
old_fb = plane->state->fb;
else
old_fb = plane->fb;
if (!old_fb) {
DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
ret = -EINVAL;
goto out;
}
fb = old_fb;
/* Make refcounting symmetric with the lookup path. */
drm_framebuffer_get(fb);
} else {
fb = drm_framebuffer_lookup(dev, file_priv, crtc_req->fb_id);
if (!fb) {
DRM_DEBUG_KMS("Unknown FB ID%d\n",
crtc_req->fb_id);
ret = -ENOENT;
goto out;
}
}
//创建struct drm_display_mode结构体,并把应用层传递下来的参数赋值给结构体
mode = drm_mode_create(dev);
if (!mode) {
ret = -ENOMEM;
goto out;
}
if (!file_priv->aspect_ratio_allowed &&
(crtc_req->mode.flags & DRM_MODE_FLAG_PIC_AR_MASK) != DRM_MODE_FLAG_PIC_AR_NONE) {
DRM_DEBUG_KMS("Unexpected aspect-ratio flag bits\n");
ret = -EINVAL;
goto out;
}
//赋值操作
ret = drm_mode_convert_umode(dev, mode, &crtc_req->mode);
if (ret) {
DRM_DEBUG_KMS("Invalid mode (ret=%d, status=%s)\n",
ret, drm_get_mode_status_name(mode->status));
drm_mode_debug_printmodeline(mode);
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 (!plane->format_default) {
//检查pixel format是否支持
ret = drm_plane_check_pixel_format(plane,
fb->format->format,
fb->modifier);
if (ret) {
struct drm_format_name_buf format_name;
DRM_DEBUG_KMS("Invalid pixel format %s, modifier 0x%llx\n",
drm_get_format_name(fb->format->format,
&format_name),
fb->modifier);
goto out;
}
}
//检查framebuffer是否足够大,能否满足crtc viewport
ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
mode, fb);
if (ret)
goto out;
}
if (crtc_req->count_connectors == 0 && mode) {
DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
ret = -EINVAL;
goto out;
}
if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
crtc_req->count_connectors);
ret = -EINVAL;
goto out;
}
if (crtc_req->count_connectors > 0) {
u32 out_id;
/* Avoid unbounded kernel memory allocation */
if (crtc_req->count_connectors > config->num_connector) {
ret = -EINVAL;
goto out;
}
connector_set = kmalloc_array(crtc_req->count_connectors,
sizeof(struct drm_connector *),
GFP_KERNEL);
if (!connector_set) {
ret = -ENOMEM;
goto out;
}
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;
}
//找到crtc绑定链接的connector
connector = drm_connector_lookup(dev, file_priv, 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;
}
}
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;
//设置struct drm_mode_set结构体
if (drm_drv_uses_atomic_modeset(dev))
ret = crtc->funcs->set_config(&set, &ctx);
else
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);
/* In case we need to retry... */
connector_set = NULL;
fb = NULL;
mode = NULL;
DRM_MODESET_LOCK_ALL_END(ctx, ret);
mutex_unlock(&crtc->dev->mode_config.mutex);
return ret;
}
4.13.4.3. func的一些介绍
//crtc控制接口,一般填写helper函数
struct drm_crtc_funcs {
//重置软硬件state, 由drm_mode_config_reset调用,一般赋值为
//drm_atomic_helper_crtc_reset
void (*reset)(struct drm_crtc *crtc);
//设置鼠标图片,width/height应该是鼠标的宽高,handle是鼠标图片buf(drm_gem_obj
//该接口已经废弃,使用鼠标层代替
int (*cursor_set)(struct drm_crtc *crtc, struct drm_file *file_priv,
uint32_t handle, uint32_t width, uint32_t height);
//同上,多了hot_x, hot_y
int (*cursor_set2)(struct drm_crtc *crtc, struct drm_file *file_priv,
uint32_t handle, uint32_t width, uint32_t height,
int32_t hot_x, int32_t hot_y);
//移动鼠标操作
int (*cursor_move)(struct drm_crtc *crtc, int x, int y);
//gamma设置
int (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
uint32_t size,
struct drm_modeset_acquire_ctx *ctx);
//drm_mode_config_cleanup调用该接口
void (*destroy)(struct drm_crtc *crtc);
//设置crtc的fb/connector/mode的属性,对应用户态drmModeSetCrtc接口atomic modeset操作
//使用drm_atomic_helper_set_config接口赋值
int (*set_config)(struct drm_mode_set *set,
struct drm_modeset_acquire_ctx *ctx);
//page翻转接口,vsync同步的
int (*page_flip)(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
uint32_t flags,
struct drm_modeset_acquire_ctx *ctx);
//和page_flip类似,但该接口会等待特定的vbank
int (*page_flip_target)(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
uint32_t flags, uint32_t target,
struct drm_modeset_acquire_ctx *ctx);
//设置属性
int (*set_property)(struct drm_crtc *crtc,
struct drm_property *property, uint64_t val);
//拷贝crtc的drm_crtc_state对象,一般赋值为drm_atomic_helper_crtc_duplicate_state
struct drm_crtc_state *(*atomic_duplicate_state)(struct drm_crtc *crtc);
void (*atomic_destroy_state)(struct drm_crtc *crtc,
struct drm_crtc_state *state);
//atomic操作中设置特定属性到state中,该接口一般可由drm_atomic_set_property调用
int (*atomic_set_property)(struct drm_crtc *crtc,
struct drm_crtc_state *state,
struct drm_property *property,
uint64_t val);
//获取atomic属性
int (*atomic_get_property)(struct drm_crtc *crtc,
const struct drm_crtc_state *state,
struct drm_property *property,
uint64_t *val);
//drm_dev_register之后,调用该接口进行额外的crtc操作
int (*late_register)(struct drm_crtc *crtc);
//与late接口相反
void (*early_unregister)(struct drm_crtc *crtc);
//以下接口与crc相关
int (*set_crc_source)(struct drm_crtc *crtc, const char *source);
int (*verify_crc_source)(struct drm_crtc *crtc, const char *source,
size_t *values_cnt);
const char *const *(*get_crc_sources)(struct drm_crtc *crtc,
size_t *count);
//打印crtc的atomic state属性,一般由drm_atomic_print_state调用
void (*atomic_print_state)(struct drm_printer *p,
const struct drm_crtc_state *state);
//获取硬件vblank counter计数
u32 (*get_vblank_counter)(struct drm_crtc *crtc);
//使能vblank中断
int (*enable_vblank)(struct drm_crtc *crtc);
void (*disable_vblank)(struct drm_crtc *crtc);
};
struct drm_crtc_helper_funcs
struct drm_crtc_helper_funcs {
//电源管理接口,一般由drm_helper_connector_dpms调用
void (*dpms)(struct drm_crtc *crtc, int mode);
//为modeset做准备,一般就是调用dpms接口关闭crtc(DRM_MODE_DPMS_OFF)
void (*prepare)(struct drm_crtc *crtc);
//与prepare接口对应,在modeset完成后,调用该接口来enable crtc
void (*commit)(struct drm_crtc *crtc);
//检查显示mode的有效性
enum drm_mode_status (*mode_valid)(struct drm_crtc *crtc,
const struct drm_display_mode *mode);
//验证并修正mode,由drm_atomic_helper_check_modeset调用
bool (*mode_fixup)(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
//设置display mode, crtc_set_mode会调用该接口
int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, int x, int y,
struct drm_framebuffer *old_fb);
//更新crtc的display mode, 但不会修改其primary plane配置
void (*mode_set_nofb)(struct drm_crtc *crtc);
//设置fb和显示位置,drm_crtc_helper_set_config会调用该接口
int (*mode_set_base)(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb);
int (*mode_set_base_atomic)(struct drm_crtc *crtc,
struct drm_framebuffer *fb, int x, int y,
enum mode_set_atomic);
//关闭crtc
void (*disable)(struct drm_crtc *crtc);
//检查待更新的drm_crtc_state
int (*atomic_check)(struct drm_crtc *crtc,
struct drm_crtc_state *state);
//多plane的atomic update之前需要调用该接口
void (*atomic_begin)(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state);
//多plane的atomic update之后需要调用该接口
void (*atomic_flush)(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state);
//atomic enable crtc
void (*atomic_enable)(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state);
void (*atomic_disable)(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state);
};
4.13.4.4. rcar_du_crtc_create
rcar_du_crtc_create
drm_crtc_init_with_planes
drm_crtc_helper_add
drm_crtc_vblank_off
rcar_du_crtc_crc_init
static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
.atomic_check = rcar_du_crtc_atomic_check,
.atomic_begin = rcar_du_crtc_atomic_begin,
.atomic_flush = rcar_du_crtc_atomic_flush,
.atomic_enable = rcar_du_crtc_atomic_enable,
.atomic_disable = rcar_du_crtc_atomic_disable,
.mode_valid = rcar_du_crtc_mode_valid,
};