LCD DRM驱动框架分析二_drm lcd初始化流程

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

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

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

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

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

	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_ATOMIC`的`ioctl`,这就是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,而`state`及`new_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_state`和`atomic_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


6.2)drm\_atomic\_commit


真正的commit操作由`drm_atomic_commit`函数实现, 如下:



int drm_atomic_commit(struct drm_atomic_state *state)
{
struct drm_mode_config *config = &state->dev->mode_config;
int ret;

    ret = drm_atomic_check_only(state);
    if (ret)
            return ret;

    DRM_DEBUG_ATOMIC("committing %p\n", state);

    return config->funcs->atomic_commit(state->dev, state, false);

}


主要分为检查state合法性和调用`drm_mode_config_funcs->atomic_commit`函数进行commit操作。默认情况下,atomic\_commit回调函数的功能是由`drm_atomic_helper_commit`实现的。函数内部有两个code path:阻塞和非阻塞。此处以阻塞情况进行分析,因为上面看到,`drm_atomic_commit`调用的是非阻塞的实现。


6.3)drm\_atomic\_helper\_commit


大多数情况下,驱动程序会使用DRM框架中提供的默认实现。而DRM框架为`atomic_commit`回调函数提供个的默认实现为`drm_atomic_helper_commit`,接下来就对该函数进行分析。函数的实现很明显被`drm_atomic_state`参数中的`async_update`分成两段,如下:



    if (state->async_update) {
            ret = drm_atomic_helper_prepare_planes(dev, state);
            if (ret)
                    return ret;

            drm_atomic_helper_async_commit(dev, state);
            drm_atomic_helper_cleanup_planes(dev, state);

            return 0;
    }

在非异步模式下,函数首先调用`drm_atomic_helper_setup_commit`做合法性检查并且创建`drm_crtc_commit`。随后函数初始化`state->commit_work`,后续相应操作可能放到`workqueue`中完成。


函数最后调用`drm_atomic_helper_prepare_planes`对所有的state中新出现的plane依次调用其helper中的`prepare_fb`回调函数。对于非阻塞的情况,调用`drm_atomic_helper_wait_for_fences`进行等待操作。最后调用软件层核心的`drm_atomic_helper_swap_state`函数将新的状态更新到旧的状态,注意这里是软件层面的更新,单纯的是修改state对象。


如果函数调用时使用非阻塞模式,则直接调度起workqueue执行后续操作,反之则直接调用`commit_tail`函数,如下:



   if (nonblock)
            queue_work(system_unbound_wq, &state->commit_work);
    else
            commit_tail(state);

实际上`state->commit_work`的处理函数也是直接调用`commit_tail`:



static void commit_work(struct work_struct *work)
{
struct drm_atomic_state *state = container_of(work,
struct drm_atomic_state,
commit_work);
commit_tail(state);
}


而`commit_tail`的实现用到了多个helper:



static void commit_tail(struct drm_atomic_state *old_state)
{
struct drm_device *dev = old_state->dev;
const struct drm_mode_config_helper_funcs *funcs;

    funcs = dev->mode_config.helper_private;

    drm_atomic_helper_wait_for_fences(dev, old_state, false);

    drm_atomic_helper_wait_for_dependencies(old_state);

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

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

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

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

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

drm_mode_config_helper_funcs *funcs;

    funcs = dev->mode_config.helper_private;

    drm_atomic_helper_wait_for_fences(dev, old_state, false);

    drm_atomic_helper_wait_for_dependencies(old_state);

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

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

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

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

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

  • 28
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值