当前位置:网站首页>LCD DRM驱动框架分析二
LCD DRM驱动框架分析二
2022-08-10 09:36:00 【沉沦者】
本文是基于rk3566 / rk3568平台从LCD代码层面上对LCD DRM框架进行分析。
一、uboot阶段
1、涉及的驱动文件
2、uboot代码流程分析
2.1)各probe函数的加载
2.1.1)各bind函数是在设备和驱动匹配时(uboot启动阶段)加载的。
2.1.2)各probe函数是在rockchip_display_probe中解析设备树时,通过uclass_get_device_by_xxx(uclass_get_device_by_ofnode/uclass_get_device_by_phandle)系列函数加载的
->rockchip_display_bind
->rockchip_vop_bind
->dw_mipi_dsi_bind
->rockchip_display_probe
->uclass_get_device_by_ofnode(UCLASS_VIDEO_CRTC, np_to_ofnode(vop_node), &crtc_dev)
->rockchip_vop_probe
->rockchip_of_find_connector
->uclass_get_device_by_ofnode(UCLASS_DISPLAY, conn, &dev)
->dw_mipi_dsi_probe
->rockchip_of_find_phy
->uclass_get_device_by_phandle(UCLASS_PHY, dev, "phys", &phy_dev)
->inno_video_phy_probe
->rockchip_of_find_bridge
->uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, node, &dev)
->rockchip_of_find_panel
->uclass_get_device_by_ofnode(UCLASS_PANEL, panel_node, &panel_dev)
->rockchip_panel_probe
2.2)开机后uboot阶段LCD的初始化流程
2.2.1)rockchip_show_logo 为显示 U-Boot logo 和 kernel logo。
2.2.2)load_bmp_logo加载logo数据,其中包括查找logo数据(find_or_alloc_logo_cache)和获取显示的缓存buffer(get_display_buffer)
2.2.3)display_logo显示进行logo的显示(传参为logo图像数据)
board_late_init
->rockchip_show_logo
->load_bmp_logo
->find_or_alloc_logo_cache
->rockchip_read_resource_file
->get_display_buffer
->display_logo
2.2.4)display_init函数为各模块进行初始化其中包括:
2.2.4.1)panel_matching进行屏匹配过程,主要是先对各模块上电达到链路为通的状态,接着通过读取硬件panel_id 来决定使用相应的dtsi文件,从而在后续使用匹配的dtsi文件(有效的面板数据)对屏进行初始化、操作时序等。
2.2.4.2)display_set_plane设置图层
2.2.4.3)display_enable重新上电、各模块使能、亮背光等操作。
->board_late_init
->rockchip_show_logo
->display_logo
->display_init
->panel_matching
->conn_funcs->init(state)
->rockchip_panel_init
->rockchip_panel_getId
->panel->funcs->getId(panel)=rockchip_dsi_panel_getId //读取实际panel id和dtsi文件的id进行比较
->mipi_dsi_dcs_read
->conn_funcs->disable(state)
->conn_funcs->unprepare(state)
->crtc_funcs->preinit(state)
->rockchip_panel_init
->conn_funcs->init(state)
->rockchip_phy_init
->crtc_funcs->init
->display_set_plane
->crtc_funcs->set_plane(state)=rockchip_vop_set_plane
->display_enable
->crtc_funcs->prepare(state)=rockchip_vop_prepare
->conn_funcs->prepare(state)=dw_mipi_dsi_connector_prepare
->rockchip_bridge_pre_enable(conn_state->bridge)
->rockchip_panel_prepare(panel_state->panel)=panel_simple_prepare
->crtc_funcs->enable(state)=rockchip_vop_enable
->conn_funcs->enable(state)=dw_mipi_dsi_connector_enable
->rockchip_bridge_enable(conn_state->bridge)
->rockchip_panel_enable(panel_state->panel)=panel_simple_enable
二、kernel 阶段
1、涉及的驱动文件
2、内核LCD驱动加载流程
3、LCD驱动加载具体分析
通过各模块probe函数和bind函数的执行来实现各模块的初始化,如下:
rockchip_drm_init
->vop2_probe
->dw_hdmi_rockchip_probe
->dw_mipi_dsi_probe
->rockchip_drm_platform_probe
->rockchip_drm_bind
->rockchip_drm_mode_config_init
->component_bind_all
->show_loader_logo
->rockchip_drm_fbdev_init
->drm_fb_helper_prepare
->drm_dev_register
->vop2_bind
->vop2_win_init
->vop2_create_crtc
->vop2_plane_init
->drm_crtc_init_with_planes
->drm_crtc_helper_add
->rockchip_register_crtc_funcs
->dw_hdmi_rockchip_bind
->dw_mipi_dsi_bind
->drm_encoder_init
->drm_encoder_helper_add
->drm_connector_init
->drm_connector_helper_add
->panel_simple_dsi_probe
->panel_simple_probe
->drm_panel_init
注:
drm_device
用于抽象一个完整的DRM设备,而其中与Mode Setting
相关的部分则由drm_mode_config
进行管理。为了让一个drm_device
支持KMS
相关的API,DRM框架要求驱动:
1)注册drm_driver
时,driver_features
标志位中需要存在DRIVER_MODESET。
2)在probe函数中调用drm_mode_config_init
函数初始化KMS框架,本质上是初始化drm_device
中的mode_config
结构体。
3)填充mode_config中int min_width, min_height; int max_width, max_height的值,这些值是framebuffer的大小限制。
4)设置mode_config->funcs指针,本质上是一组由驱动实现的回调函数,涵盖KMS
中一些相当基本的操作。
5)最后初始化drm_device
中包含的drm_connector
,drm_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_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);
if (funcs && funcs->atomic_commit_tail)
funcs->atomic_commit_tail(old_state);
else
drm_atomic_helper_commit_tail(old_state);
drm_atomic_helper_commit_cleanup_done(old_state);
drm_atomic_state_put(old_state);
}
6.4)drm_atomic_helper_setup_commit
函数首先遍历所有状态发生改变的CRTC
,然后对其创建drm_crtc_commit
,前面看到这个对象是对Commit操作的进度进行追踪用的。创建之后的drm_crtc_commit
就保存在new_crtc_state->commit
中。之后函数会调用stall_checks
检查当前的commit队列中是否有停滞的commit:
list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
if (i == 0) {
completed = try_wait_for_completion(&commit->flip_done);
/* Userspace is not allowed to get ahead of the previous
* commit with nonblocking ones. */
if (!completed && nonblock) {
spin_unlock(&crtc->commit_lock);
return -EBUSY;
}
} else if (i == 1) {
stall_commit = drm_crtc_commit_get(commit);
break;
}
i++;
}
spin_unlock(&crtc->commit_lock);
从逻辑上来看,从commit队列中取下第一个drm_commit
,然后检查其flip_done
的completion
是否已经被完成了。如果没有完成,且运行drm_atomic_helper_commit
时为为非阻塞模式(nonblock
参数为true),则直接让整次commit操作返回-EBUSY
。随后,取下第二个drm_commit
(如果存在的话),并认为其为stall的,直接以10秒为timeout等待其cleanup_done
完成。
函数接下来做了两个简单的优化:
/* Drivers only send out events when at least either current or
* new CRTC state is active. Complete right away if everything
* stays off. */
if (!old_crtc_state->active && !new_crtc_state->active) {
complete_all(&commit->flip_done);
continue;
}
/* Legacy cursor updates are fully unsynced. */
if (state->legacy_cursor_update) {
complete_all(&commit->flip_done);
continue;
}
新旧两个状态的CRTC都为关闭状态时肯定flip_done是直接完成的。且使用Legacy Cursor相关的API时,因为这个API本身就不同步,所以可以直接视为完成了flip_done
。随后函数为每个CRTC创建drm_pending_event
并放入new_crtc_state->event
中,注意新创建的drm_pending_event
的completion
指针直接指向前面创建的drm_commit->flip_event
,也就是这个drm_pending_event
进行处理的时候,会直接完成相应的flip_done
。
6.5)drm_atomic_helper_wait_for_fences
这个函数对于所有的新状态涉及的plane都会依次对new_plane_state->fence
调用dma_wait_fence
。也就是单纯研究这个函数没有什么意义,需要结合plane相关的实现进行分析。
6.6)drm_atomic_helper_wait_for_dependencies
该函数依次对本次commit的旧状态,即原先的状态对应的commit(将显示控制器置成oldstate的commit)中相应的事件进行等待。简单来说,就是等待old_{crtc,plane,connector}_state->commit
上的hw_done
和flip_done
。
6.7)drm_atomic_helper_commit_tail
commit_tail
中调用了drm_cmode_config_helpers->atomic_commit_tail
回调函数,在其为空的情况下,则直接调用drm_atomic_helper_commit_tail
void drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state)
{
struct drm_device *dev = old_state->dev;
drm_atomic_helper_commit_modeset_disables(dev, old_state);
drm_atomic_helper_commit_planes(dev, old_state, 0);
drm_atomic_helper_commit_modeset_enables(dev, old_state);
drm_atomic_helper_fake_vblank(old_state);
drm_atomic_helper_commit_hw_done(old_state);
drm_atomic_helper_wait_for_vblanks(dev, old_state);
drm_atomic_helper_cleanup_planes(dev, old_state);
}
边栏推荐
- 09 【Attributes继承 provide与inject】
- [OAuth2] 20. OAuth2 Extended Protocol PKCE
- DAY25: Logic vulnerability recurrence
- CSDN21天学习挑战赛——多态(05)
- DAY25: Logic Vulnerability
- 钻石价格预测的ML全流程!从模型构建调优道部署应用!
- 【API 管理】什么是 API 管理,为什么它很重要?
- DataStream API(基础篇) 完整使用 (第五章)
- Numpy学习
- Spotify expresses its architectural design using the C4 model
猜你喜欢
随机推荐
SQL优化总结
单例模式基类
Tencent releases the second-generation version of the quadruped robot Max, which completes jumps and somersaults on the plum blossom pile
shell之函数和数组
多线程浅谈
【元宇宙欧米说】看UCOUCO如何将行为艺术融入元宇宙
初识Flink 完整使用 (第一章)
"Guangzhou highway engineering measures for the supervision and administration of production safety, and revised from six aspects
多线程知识点总结之温故而知新
《广州市公路工程安全生产监督管理办法》印发,从六大方面进行修订
JVM探究
FPGA时钟篇(三) MRCC和SRCC的区别
shell iterates over folders and outputs
裸辞→自我放松→闭关→复习→斩获Offer
Message Queuing Overview
【API架构】使用 JSON API 的好处
J9 Number Theory: Macro Analysis of DAO Characteristics
不要把公司当成家,被通知裁员时会变得不幸...
PTA Exercise 2.2 Rotate an Array Left
Guo Jingjing's personal chess teaching, the good guy is a robot