2024年LCD DRM驱动框架分析二_drm lcd初始化流程,2024年最新程序员中年危机

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

4)设置mode_config->funcs指针,本质上是一组由驱动实现的回调函数,涵盖KMS中一些相当基本的操作。

5)最后初始化drm_device中包含的drm_connectordrm_crtc等对象.

4、DRM helper架构

基本思想是通过一组回调函数抽象特定组件的操作,比如drm_connector_funcs,同时又使用另外一组helper函数给出了原先那组回调函数的通用实现,让开发最者实现这组helper函数抽象出的回调函数即可。可以保证开发者有足够高的自由度(完全不用helper函数),也能简化开发者的开发(使用helper函数),同时提供给开发者hook特定helper函数的能力。以drm_connector为例说明helper架构的实现方式与使用方式。

正常情况下,创建drm_connector对象时需要提供struct drm_connector_funcs回调函数组,而使用helper函数时,可以直接用helper函数填充对应回调函数:

static const struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = {
	.fill_modes = drm_helper_probe_single_connector_modes,
	.destroy = dw_mipi_dsi_drm_connector_destroy,
	.reset = drm_atomic_helper_connector_reset,
	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
	.atomic_get_property = dw_mipi_dsi_atomic_connector_get_property,
};

事实上helper函数并不万能,只是抽象出了大多数驱动程序应该共享的行为,而特定于硬件的部分,则需要以回调函数的形式提供给helper函数,这个回调函数组由struct drm_connector_helper_funcs提供。在创建drm_connector时,需要通过drm_connector_helper_add函数注册。函数将对应的回调函数对象的地址保存在了drm_connector中的helper_private指针中,如下:

drm_connector_helper_add(connector, &dw_mipi_dsi_connector_helper_funcs);
static inline void drm_connector_helper_add(struct drm_connector *connector,
					    const struct drm_connector_helper_funcs *funcs)
{
	connector->helper_private = funcs;
}

static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = {
	.get_modes = dw_mipi_dsi_connector_get_modes,
};

5、用户态和内核态间的交互

驱动会注册一个支持KMS的DRM设备时,会在/dev/drm/下创建一个card%d文件,用户态可以通过打开该文件,并对文件描述符做相应的操作实现相应的功能。该文件描述符对应的文件操作回调函数(filesystem_operations)位于drm_driver中,并由驱动程序填充。典型如下:

static const struct file_operations rockchip_drm_driver_fops = {
	.owner = THIS_MODULE,
	.open = drm_open,
	.mmap = rockchip_gem_mmap,
	.poll = drm_poll,
	.read = drm_read,
	.unlocked_ioctl = drm_ioctl,
	.compat_ioctl = drm_compat_ioctl,
	.release = drm_release,
};

long drm_ioctl(struct file *filp,
	      unsigned int cmd, unsigned long arg)
{
	struct drm_file *file_priv = filp->private_data;
	struct drm_device *dev;
	const struct drm_ioctl_desc *ioctl = NULL;
	drm_ioctl_t *func;
	unsigned int nr = DRM_IOCTL_NR(cmd);
	int retcode = -EINVAL;
	char stack_kdata[128];
	char *kdata = NULL;
	unsigned int in_size, out_size, drv_size, ksize;
	bool is_driver_ioctl;

	dev = file_priv->minor->dev;

	if (drm_dev_is_unplugged(dev))
		return -ENODEV;

	is_driver_ioctl = nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END;

	if (is_driver_ioctl) {
		/* driver ioctl */
		unsigned int index = nr - DRM_COMMAND_BASE;

		if (index >= dev->driver->num_ioctls)
			goto err_i1;
		index = array_index_nospec(index, dev->driver->num_ioctls);
		ioctl = &dev->driver->ioctls[index];
	} else {
		/* core ioctl */
		if (nr >= DRM_CORE_IOCTL_COUNT)
			goto err_i1;
		nr = array_index_nospec(nr, DRM_CORE_IOCTL_COUNT);
		ioctl = &drm_ioctls[nr];
	}

	drv_size = _IOC_SIZE(ioctl->cmd);
	out_size = in_size = _IOC_SIZE(cmd);
	if ((cmd & ioctl->cmd & IOC_IN) == 0)
		in_size = 0;
	if ((cmd & ioctl->cmd & IOC_OUT) == 0)
		out_size = 0;
	ksize = max(max(in_size, out_size), drv_size);

	DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n",
		  task_pid_nr(current),
		  (long)old_encode_dev(file_priv->minor->kdev->devt),
		  file_priv->authenticated, ioctl->name);

	/* Do not trust userspace, use our own definition */
	func = ioctl->func;

	if (unlikely(!func)) {
		DRM_DEBUG("no function\n");
		retcode = -EINVAL;
		goto err_i1;
	}

	if (ksize <= sizeof(stack_kdata)) {
		kdata = stack_kdata;
	} else {
		kdata = kmalloc(ksize, GFP_KERNEL);
		if (!kdata) {
			retcode = -ENOMEM;
			goto err_i1;
		}
	}

	if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) {
		retcode = -EFAULT;
		goto err_i1;
	}

	if (ksize > in_size)
		memset(kdata + in_size, 0, ksize - in_size);

	retcode = drm_ioctl_kernel(filp, func, kdata, ioctl->flags);
	if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
		retcode = -EFAULT;

      err_i1:
	if (!ioctl)
		DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
			  task_pid_nr(current),
			  (long)old_encode_dev(file_priv->minor->kdev->devt),
			  file_priv->authenticated, cmd, nr);

	if (kdata != stack_kdata)
		kfree(kdata);
	if (retcode)
		DRM_DEBUG("pid=%d, ret = %d\n", task_pid_nr(current), retcode);
	return retcode;
}

通过访问drmModeSetCrtc相关的legacy接口,从而调用到了drm_ioctl_kernel - > IOCTL上:

return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETCRTC, &crtc);

而所有与drm相关的定义都在drivers/gpu/drm/drm_ioctl.c中:

DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER),

即最终处理函数是drm_mode_setcrtc。函数首先检查DRM设备的feature:

 if (!drm_core_check_feature(dev, DRIVER_MODESET))                
        return -EOPNOTSUPP;

最终调用的就是drm_crtc_funcs->set_config回调函数,也就是drm_atomic_helper_set_config函数:

if (drm_drv_uses_atomic_modeset(dev))
    ret = crtc->funcs->set_config(&set, &ctx);
else
    ret = __drm_mode_set_config_internal(&set, &ctx);

struct drm_crtc_funcs结构体:

static const struct drm_crtc_funcs vop2_crtc_funcs = 
{
    .gamma_set = vop2_crtc_legacy_gamma_set,
    .set_config = drm_atomic_helper_set_config,
    .page_flip = drm_atomic_helper_page_flip,
    .destroy = vop2_crtc_destroy,
    .reset = vop2_crtc_reset,
    .atomic_get_property = vop2_crtc_atomic_get_property,
    .atomic_set_property = vop2_crtc_atomic_set_property,
    .atomic_duplicate_state = vop2_crtc_duplicate_state,
    .atomic_destroy_state = vop2_crtc_destroy_state,
    .enable_vblank = vop2_crtc_enable_vblank,
    .disable_vblank = vop2_crtc_disable_vblank,
    .set_crc_source = vop2_crtc_set_crc_source,
    .verify_crc_source = vop2_crtc_verify_crc_source,
};

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;
}

用户态A-KMS调用的入口函数drmModeAtomicCommit内部使用了不同的IOCTL调用:

 ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ATOMIC, &atomic);

对应内核态为:

DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER),

drm_mode_atomic_ioctl实现为:

int drm_mode_atomic_ioctl(struct drm_device *dev,
			  void *data, struct drm_file *file_priv)
{
    //省略无关代码

	drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);

	state = drm_atomic_state_alloc(dev);
	if (!state)
		return -ENOMEM;

	state->acquire_ctx = &ctx;
	state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET);

retry:
	copied_objs = 0;
	copied_props = 0;
	fence_state = NULL;
	num_fences = 0;

	for (i = 0; i < arg->count_objs; i++) {
		uint32_t obj_id, count_props;
		struct drm_mode_object *obj;

		if (get_user(obj_id, objs_ptr + copied_objs)) {
			ret = -EFAULT;
			goto out;
		}

		obj = drm_mode_object_find(dev, file_priv, obj_id, DRM_MODE_OBJECT_ANY);
		if (!obj) {
			ret = -ENOENT;
			goto out;
		}

		if (!obj->properties) {
			drm_mode_object_put(obj);
			ret = -ENOENT;
			goto out;
		}

		if (get_user(count_props, count_props_ptr + copied_objs)) {
			drm_mode_object_put(obj);
			ret = -EFAULT;
			goto out;
		}

		copied_objs++;

		for (j = 0; j < count_props; j++) {
			uint32_t prop_id;
			uint64_t prop_value;
			struct drm_property *prop;

			if (get_user(prop_id, props_ptr + copied_props)) {
				drm_mode_object_put(obj);
				ret = -EFAULT;
				goto out;
			}

			prop = drm_mode_obj_find_prop_id(obj, prop_id);
			if (!prop) {
				drm_mode_object_put(obj);
				ret = -ENOENT;
				goto out;
			}

			if (copy_from_user(&prop_value,
					   prop_values_ptr + copied_props,
					   sizeof(prop_value))) {
				drm_mode_object_put(obj);
				ret = -EFAULT;
				goto out;
			}

			ret = drm_atomic_set_property(state, obj, prop,
						      prop_value);
			if (ret) {
				drm_mode_object_put(obj);
				goto out;
			}

			copied_props++;
		}

		drm_mode_object_put(obj);
	}

	ret = prepare_signaling(dev, state, arg, file_priv, &fence_state,
				&num_fences);
	if (ret)
		goto out;

	if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) {
		ret = drm_atomic_check_only(state);
	} else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) {
		ret = drm_atomic_nonblocking_commit(state);
	} else {
		if (unlikely(drm_debug & DRM_UT_STATE))
			drm_atomic_print_state(state);

		ret = drm_atomic_commit(state);
	}
    //省略无关代码
	return ret;
}

6、Atomic KMS 架构

Atomic Mode Setting(后续简称A-KMS)。该架构会弥补之前API的不足,由于原先的API不支持同时更新整个DRM显示pipeline的状态,因此KMS过程中会出现一些中间状态,容易造成开发者不希望看见的结果,影响用户体验。同时,原先的KMS接口也不支持回滚,需要应用程序自己记录原先的配置状态,Atomic Mode Setting也解决了这个问题。Atomic commit 表示:本次 commit 操作,要么成功,要么保持原来的状态不变。即如果中途操作失败了,那些已经生效的配置需要恢复成之前的状态,就像没发生过 commit 操作似的。而Commit,则是因为本次操作可能会修改到多个参数,等修改好这些参数后,再一次性发起操作请求,有点类似与填表后“提交”材料的意思

Atomic Mode Setting接口在用户态看来,是将原先各个KMS object的状态由隐式的通过API更新,变成了显式的对象属性。用户态程序可以通过通用的属性操作接口读写KMS object上的属性,更改不会立即生效,而是缓存起来。当应用程序更新完其所有想要更新的属性时,可以通过Commit操作告知要求KMS层真正的更新硬件的状态。此时驱动程序需要验证应用程序要求进行的修改是否合法,在合法的情况下,可以一次性完成整个显示状态的修改。A-KMS也实现了只用于检查新状态是否合法的接口。

KMS框架提供了一套helper函数以帮助驱动程序作者实现原先的Legacy KMS接口,本质上,就是原先的legacy相关的接口都通过A-KMS兼容层实现的helper函数实现,实质上就是使用带有drm_atomic_helper前缀的helper函数实现原有的legacy接口。

用户态相关接口:

typedef struct _drmModeAtomicReq drmModeAtomicReq, *drmModeAtomicReqPtr;

extern drmModeAtomicReqPtr drmModeAtomicAlloc(void);
extern drmModeAtomicReqPtr drmModeAtomicDuplicate(drmModeAtomicReqPtr req);
extern int drmModeAtomicMerge(drmModeAtomicReqPtr base,
                              drmModeAtomicReqPtr augment);
extern void drmModeAtomicFree(drmModeAtomicReqPtr req);
extern int drmModeAtomicGetCursor(drmModeAtomicReqPtr req);
extern void drmModeAtomicSetCursor(drmModeAtomicReqPtr req, int cursor);
extern int drmModeAtomicAddProperty(drmModeAtomicReqPtr req,
                                    uint32_t object_id,
                                    uint32_t property_id,
                                    uint64_t value);
extern int drmModeAtomicCommit(int fd,
                               drmModeAtomicReqPtr req,
                               uint32_t flags,
                               void *user_data);

用户态接口的本质就是扩展原有的属性接口,允许用户描述一个状态集合,然后通过drmModeAtomicCommit函数进行commit操做。从libdrm的代码中可以看到,commit的操作最后实质上调用了DRM_IOCTL_MODE_ATOMICioctl,这就是Native接口唯一的入口。从内核代码中可以看到,该ioctl的处理函数为drm_mode_atomic_ioctl。以drm_mode_atomic_ioctl为线索,可以发现许多相关的实现。

1)状态对象

A-KMS的核心是整个显示控制器的状态集合,由一个独立的状态对象表示。一个DRM显示pipeline的整体状态由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;
  bool duplicated : 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 drm_crtc_commit *fake_commit;
  struct work_struct commit_work;
};

可以看到他由每个独立组件(即drm_mode_object)的状态对象组成。

2)state的创建

drm_atomic_state的创建由drm_atomic_state_alloc实现。函数中可以看到,drm_mode_config_funcs中提供了名为atomic_state_alloc的hook,允许我们自己实现state对象的创建。在默认情况下,函数会调用简单分配内存,然后使用drm_atomic_state_init进行初始化。初始化函数仅仅是简单分配分配drm_atomic_state中几个指针指向的内存区域。

对于各个drm object对应的state,其创建操作由其对应的drm_{object}_funcs->atomic_duplicate_state实现,在驱动程序没有扩展drm_atomic_state的情况下,这个回调函数一般填写为drm_atomic_helper_{object}_duplicate_state。而在commit过程中,是由drm_atomic_get_{object}_state函数触发这个创建操作的。该函数触发复制state操作后,还会将复制后的state及原本的state填入drm_atomic_state中对应的__drm_{object}_state中。

struct __drm_{object}_state {
        struct drm_{object} *ptr;
        struct drm_{object}_state *state, *old_state, *new_state;
    
        /* extra fields may exist */
};

这里的old_state保存drm_{object}现有的state,而statenew_state就保存我们复制后的state。

最后描述一下commit时创建state的简单流程:

2.1)drm_mode_atomic_ioctl函数中会将用户态传入的property更新依次调用drm_atomic_set_property写入前面创建的drm_atomic_state

2.2)drm_atomic_set_property函数会根据传入object的类型调用对应的drm_atomic_get_{object}_state函数,得到对应于该object类型的drm_{object}_state。在这个调用中,如果drm_atomic_mode中对应的__drm_{object}_state不存在,则复制原有的state并填入

2.3)随后drm_atomic_set_property会调用drm_atomic_{object}_set_property将属性更新写入到新的state当中

2.4)最后drm_mode_atomic_ioctl调用对应函数(drm_atomic_commit及其非阻塞版本)进行commit操作(该操作前提是没有设置TEST_ONLY的标志)

3)state更新

state更新由drm_atomic_{object}_set_property函数实现,前面已经分析了整体流程。目前我们看到的state更新是作为一个整体出现的,即通过用户态的commit操作触发。事实上DRM还支持partial update。atomic_duplicate_stateatomic_state_alloc等hook的存在目的是允许驱动程序开发者在原有的state中加入自己的状态,通常情况下用既有helper即可。

4)状态检测 drm_atomic_check_only

由于整个接口是atomic的,这要求实现这套接口的驱动程序能够检测一个特定的显示pipeline(mode)状态是否合法(即能被硬件接受且正确运行)。drm_atomic_check_only函数即为汇总驱动这项功能的的入口。DRM的用户态API提供了DRM_MODE_ATOMIC_TEST_ONLY标志位,其目的是允许用户态直接要求驱动检测配置的合法性而不commit配置。

函数主要操作如下:

4.1)对所有的CRTC,Connector和Plane,分别调用drm_atomic_{crtc,connector,plane}_check对其进行基本的合法性检查,注意这个检查并不涉及驱动实现的回调,完全是DRM框架自己的检查。

4.2)如果mode_config->funcs->atomic_check回调函数存在,则调用其进行检查。注意这个函数一般情况下为drm_atomic_helper_check,或者是驱动自行实现的该函数的wrapper。

4.3)如果state->allow_modeset为false,即要求不进行modeset操作,则对所有的CRTC调用drm_atomic_crtc_needs_modeset函数进行检查。

5)drm_atomic_helper_check

A-KMS的主要操作主要分为两个:

5.1)检查显示mode的合法性,确认硬件确实在该mode下正常工作

5.2)commit操作,将硬件完整的设置成对应的状态

drm_atomic_helper_check就是一般情况下drm_mode_config_funcs->atomic_check内的回调函数。其主要包含两个大的功能点:

5.3)drm_atomic_helper_check_modeset

5.4)drm_atomic_helper_check_planes

前者逐级调用CRTC下面组件的atomic_check回调函数,确认modeset是否合法。

6)commit操作

6.1)drm_crtc_commit

commit操作从感念上来看是基于每一个CRTC的,因此每个commit操作由drm_crtc_commit进行抽象:

struct drm_crtc_commit {
    struct drm_crtc *crtc;
    struct kref ref;
    struct completion flip_done;
    struct completion hw_done;
    struct completion cleanup_done;
    struct list_head commit_entry;
    struct drm_pending_vblank_event *event;
    bool abort_completion;
};

drm_crtc_commit会被放入drm_crtc->commit_list中,且drm_crtc_commit实质上仅仅起到一个同步的作用,分别对应三个事件:

6.1.1)flip_down

6.1.2)hw_down

6.1.3)cleanup_down

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

list_head commit_entry;
struct drm_pending_vblank_event *event;
bool abort_completion;
};


`drm_crtc_commit`会被放入`drm_crtc->commit_list`中,且`drm_crtc_commit`实质上仅仅起到一个同步的作用,分别对应三个事件:


6.1.1)flip\_down


6.1.2)hw\_down


6.1.3)cleanup\_down


**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
[外链图片转存中...(img-WzqhX2cr-1715625156381)]
[外链图片转存中...(img-8No0Rjj2-1715625156382)]

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值