Linux内核4.14版本——drm框架分析(4)——crtc分析
drm框架分析crtc分析
目录
3.1 struct drm_crtc_helper_funcs
4.1 drmModeSetCrtc (drmlib库里)---> drm_mode_setcrtc
本文主要分析一下crtc相关的内容。
1. struct drm_crtc结构体
/**
* struct drm_crtc - central CRTC control structure
* @dev: parent DRM device
* @port: OF node used by drm_of_find_possible_crtcs()
* @head: list management
* @name: human readable name, can be overwritten by the driver
* @mutex: per-CRTC locking
* @base: base KMS object for ID tracking etc.
* @primary: primary plane for this CRTC
* @cursor: cursor plane for this CRTC
* @cursor_x: current x position of the cursor, used for universal cursor planes
* @cursor_y: current y position of the cursor, used for universal cursor planes
* @enabled: is this CRTC enabled?
* @mode: current mode timings
* @hwmode: mode timings as programmed to hw regs
* @x: x position on screen
* @y: y position on screen
* @funcs: CRTC control functions
* @gamma_size: size of gamma ramp
* @gamma_store: gamma ramp values
* @helper_private: mid-layer private data
* @properties: property tracking for this CRTC
*
* Each CRTC may have one or more connectors associated with it. This structure
* allows the CRTC to be controlled.
*/
struct drm_crtc {
struct drm_device *dev;
struct device_node *port;
struct list_head head;
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;
struct drm_mode_object base;
/* primary and cursor planes for CRTC */
struct drm_plane *primary;
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;
/* position of cursor plane on crtc */
int cursor_x;
int cursor_y;
bool enabled;
/* Requested mode from modesetting. */
struct drm_display_mode mode;
/* Programmed mode in hw, after adjustments for encoders,
* crtc, panel scaling etc. Needed for timestamping etc.
*/
struct drm_display_mode hwmode;
int x, y;
const struct drm_crtc_funcs *funcs;
/* Legacy FB CRTC gamma size for reporting to userspace */
uint32_t gamma_size;
uint16_t *gamma_store;
/* if you are using the helper */
const struct drm_crtc_helper_funcs *helper_private;
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_crtc_in_state(), 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 doesn't hold its own full
* reference, but burrows it from the ongoing commit. Commit entries
* must be removed from this list once the commit is fully completed,
* but before it's correspoding &drm_atomic_state gets destroyed.
*/
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];
};
2. crtc相关的API
2.1 drm_crtc_init_with_planes
/**
* drm_crtc_init_with_planes - Initialise a new CRTC object with
* specified primary and cursor planes.
* @dev: DRM device
* @crtc: CRTC object to init
* @primary: Primary plane for CRTC
* @cursor: Cursor plane for CRTC
* @funcs: callbacks for the new CRTC
* @name: printf style format string for the CRTC name, or NULL for default name
*
* Inits a new object created as base part of a driver crtc object. Drivers
* should use this function instead of drm_crtc_init(), which is only provided
* for backwards compatibility with drivers which do not yet support universal
* planes). For really simple hardware which has only 1 plane look at
* drm_simple_display_pipe_init() instead.
*
* Returns:
* Zero on success, error code on failure.
*/
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->dev = dev;
crtc->funcs = funcs;
INIT_LIST_HEAD(&crtc->commit_list);
spin_lock_init(&crtc->commit_lock);
drm_modeset_lock_init(&crtc->mutex);
ret = drm_mode_object_add(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); // (1)
if (ret)
return ret;
if (name) { // (2)
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); // (3)
spin_lock_init(&crtc->fence_lock);
snprintf(crtc->timeline_name, sizeof(crtc->timeline_name),
"CRTC:%d-%s", crtc->base.id, crtc->name);
crtc->base.properties = &crtc->properties; // (4)
list_add_tail(&crtc->head, &config->crtc_list);
crtc->index = config->num_crtc++;
crtc->primary = primary; // (5)
crtc->cursor = cursor;
if (primary && !primary->possible_crtcs)
primary->possible_crtcs = 1 << drm_crtc_index(crtc);
if (cursor && !cursor->possible_crtcs)
cursor->possible_crtcs = 1 << drm_crtc_index(crtc);
ret = drm_crtc_crc_init(crtc); // (6)
if (ret) {
drm_mode_object_unregister(dev, &crtc->base);
return ret;
}
if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { // (7)
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);
}
return 0;
}
(1)创建一个类型为DRM_MODE_OBJECT_CRTC的struct drm_mode_object结构体。
(2)为创建的crtc赋名字。
(3)暂不知道。
(4)初始化properties
(5)初始化primary plane、cursor plane。
(6)drm_crtc_crc_init是一些调试方面的。
(7)attach一些properties变量。
2.5 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_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 -EINVAL;
/*
* 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 = drm_crtc_find(dev, crtc_req->crtc_id); // (1)
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);
mutex_lock(&crtc->dev->mode_config.mutex);
drm_modeset_acquire_init(&ctx, 0); // (2)
retry:
ret = drm_modeset_lock_all_ctx(crtc->dev, &ctx); // (3)
if (ret)
goto out;
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) { // (4)
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;
}
}
mode = drm_mode_create(dev); // (5)
if (!mode) {
ret = -ENOMEM;
goto out;
}
ret = drm_mode_convert_umode(mode, &crtc_req->mode); // (6)
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) { // (7)
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); // (8)
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++) { // (9)
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); // (10)
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;
ret = __drm_mode_set_config_internal(&set, &ctx); // (11)
out:
if (fb)
drm_framebuffer_put(fb); // (12)
if (connector_set) {
for (i = 0; i < crtc_req->count_connectors; i++) {
if (connector_set[i])
drm_connector_put(connector_set[i]); // (13)
}
}
kfree(connector_set);
drm_mode_destroy(dev, mode);
if (ret == -EDEADLK) {
drm_modeset_backoff(&ctx);
goto retry;
}
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
mutex_unlock(&crtc->dev->mode_config.mutex);
return ret;
}
(1)找到需要的crtc实体。
(2)初始化一个ctrc的需求内容结构体。
(3)connection、crtc、plane等锁上锁。
(4)得到或者生成primary plane的framebuffer。
(5~6)创建一个struct drm_display_mode结构体,并把应用层传递下来的参数赋值给给结构体。
(7)检查primary plane支持的pixel format。
(8)检查一下framebuffer是否足够大,能否满足crtc viewport.
(9~10)找到crtc绑定链接的connetcor。
(11)设置struct drm_mode_set结构体。
3. func的一些介绍
3.1 struct drm_crtc_helper_funcs
//struct drm_crtc_helper_funcs - helper operations for CRTCs
struct drm_crtc_helper_funcs {
//电源管理接口,一般由drm_helper_connector_dpms调用
void (*dpms)(struct drm_crtc *crtc, int mode);
/*为modeset做准备,一般就是调用dpms接口关闭crtc(DRM_MODE_DPMS_OFF),
*drm_crtc_heler_set_mode接口会调用
*atomic接口使用atomic_disable替代
*/
void (*prepare)(struct drm_crtc *crtc);
/* 和prepare接口对应,是在modeset完成后,调用该接口来enable crtc
* 一般就是调用dmps接口( DRM_MODE_DPMS_ON)
* atomic 操作是用atomic_enable替代
*/
void (*commit)(struct drm_crtc *crtc);
/*检查显示mode的有效性, drm_helper_probe_single_connector_modes()和
* drm_atomic_helper_check_modeset()会调用到, 该接口仅作初步检查
* 更详细的检查有@mode_fixup or @atomic_check
*/
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配置,
* 注意调用该接口的时候, display pipe应该是完全off的
*/
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,
* drm_atomic_helper_check() --> drm_atomic_helper_check_planes()会调用
*/
int (*atomic_check)(struct drm_crtc *crtc,
struct drm_atomic_state *state);
//多plane的atomic update之前需要调用该接口(drm_atomic_helper_commit_planes中调用)
void (*atomic_begin)(struct drm_crtc *crtc,
struct drm_atomic_state *state);
/*多plane的atomic update之后需要调用该接口(drm_atomic_helper_commit_planes中调用)
* atomic_begin/atomic_flush具体的作用好像也不是太清晰,应该就是为driver提供在更新
* planes的前后能做一些额外操作吧?
*/
void (*atomic_flush)(struct drm_crtc *crtc,
struct drm_atomic_state *state);
//atomic encable crtc
void (*atomic_enable)(struct drm_crtc *crtc,
struct drm_atomic_state *state);
atomic disable crtc
void (*atomic_disable)(struct drm_crtc *crtc,
struct drm_atomic_state *state);
//获取扫描信息,由drm_crtc_vblank_helper_get_vblank_timestamp()调用
bool (*get_scanout_position)(struct drm_crtc *crtc,
bool in_vblank_irq, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime,
const struct drm_display_mode *mode);
};
3.2 struct drm_crtc_funcs
//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)对
应的handle, 该接口最新版本已经被废弃,使用鼠标层代替
*/
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_register接口相反, 在drm_dev_unregister之前调用
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);
//关闭vbank中断
void (*disable_vblank)(struct drm_crtc *crtc);
//获取vblank时间戳
bool (*get_vblank_timestamp)(struct drm_crtc *crtc,
int *max_error,
ktime_t *vblank_time,
bool in_vblank_irq);
};
4. 应用层的调用
crtc的设置主要有ACTIVE/MODE_ID属性, 其中MODE_ID属性,是设置其显示时序,常用的设置接口如下:
4.1 drmModeSetCrtc (drmlib库里)---> drm_mode_setcrtc
该接口设置crtc的显示buf, 连接的connector_id, 显示时序mode, 从代码中可以看出来,该接口绑定fb到crtc时,实际上是绑定该crtc的primary plane上。前面已经介绍了。
更多推荐
所有评论(0)