目录

1. struct drm_crtc结构体

2. crtc相关的API

2.1 drm_crtc_init_with_planes

2.5 drm_mode_setcrtc

3. func的一些介绍

3.1 struct drm_crtc_helper_funcs

3.2 struct drm_crtc_funcs

4. 应用层的调用

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上。前面已经介绍了。

Logo

更多推荐