• LCD DRM驱动框架分析二


    本文是基于rk3566 / rk3568平台从LCD代码层面上对LCD DRM框架进行分析。

    一、uboot阶段

    1、涉及的驱动文件

    5a821d904c9f4a7bb2f853dd7779a774.png

     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)系列函数加载的

    1. ->rockchip_display_bind
    2. ->rockchip_vop_bind
    3. ->dw_mipi_dsi_bind
    4. ->rockchip_display_probe
    5. ->uclass_get_device_by_ofnode(UCLASS_VIDEO_CRTC, np_to_ofnode(vop_node), &crtc_dev)
    6. ->rockchip_vop_probe
    7. ->rockchip_of_find_connector
    8. ->uclass_get_device_by_ofnode(UCLASS_DISPLAY, conn, &dev)
    9. ->dw_mipi_dsi_probe
    10. ->rockchip_of_find_phy
    11. ->uclass_get_device_by_phandle(UCLASS_PHY, dev, "phys", &phy_dev)
    12. ->inno_video_phy_probe
    13. ->rockchip_of_find_bridge
    14. ->uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, node, &dev)
    15. ->rockchip_of_find_panel
    16. ->uclass_get_device_by_ofnode(UCLASS_PANEL, panel_node, &panel_dev)
    17. ->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图像数据)

    1. board_late_init
    2. ->rockchip_show_logo
    3. ->load_bmp_logo
    4. ->find_or_alloc_logo_cache
    5. ->rockchip_read_resource_file
    6. ->get_display_buffer
    7. ->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重新上电、各模块使能、亮背光等操作。

    1. ->board_late_init
    2. ->rockchip_show_logo
    3. ->display_logo
    4. ->display_init
    5. ->panel_matching
    6. ->conn_funcs->init(state)
    7. ->rockchip_panel_init
    8. ->rockchip_panel_getId
    9. ->panel->funcs->getId(panel)=rockchip_dsi_panel_getId //读取实际panel id和dtsi文件的id进行比较
    10. ->mipi_dsi_dcs_read
    11. ->conn_funcs->disable(state)
    12. ->conn_funcs->unprepare(state)
    13. ->crtc_funcs->preinit(state)
    14. ->rockchip_panel_init
    15. ->conn_funcs->init(state)
    16. ->rockchip_phy_init
    17. ->crtc_funcs->init
    18. ->display_set_plane
    19. ->crtc_funcs->set_plane(state)=rockchip_vop_set_plane
    20. ->display_enable
    21. ->crtc_funcs->prepare(state)=rockchip_vop_prepare
    22. ->conn_funcs->prepare(state)=dw_mipi_dsi_connector_prepare
    23. ->rockchip_bridge_pre_enable(conn_state->bridge)
    24. ->rockchip_panel_prepare(panel_state->panel)=panel_simple_prepare
    25. ->crtc_funcs->enable(state)=rockchip_vop_enable
    26. ->conn_funcs->enable(state)=dw_mipi_dsi_connector_enable
    27. ->rockchip_bridge_enable(conn_state->bridge)
    28. ->rockchip_panel_enable(panel_state->panel)=panel_simple_enable

    二、kernel 阶段

    1、涉及的驱动文件

    bdd9387383a44511946337de3c83fc95.png

     2、内核LCD驱动加载流程

    7e06deb92fcb4e50a0c220fe5ab17d0c.png

     3、LCD驱动加载具体分析

    通过各模块probe函数和bind函数的执行来实现各模块的初始化,如下:

    1. rockchip_drm_init
    2. ->vop2_probe
    3. ->dw_hdmi_rockchip_probe
    4. ->dw_mipi_dsi_probe
    5. ->rockchip_drm_platform_probe
    6. ->rockchip_drm_bind
    7. ->rockchip_drm_mode_config_init
    8. ->component_bind_all
    9. ->show_loader_logo
    10. ->rockchip_drm_fbdev_init
    11. ->drm_fb_helper_prepare
    12. ->drm_dev_register
    13. ->vop2_bind
    14. ->vop2_win_init
    15. ->vop2_create_crtc
    16. ->vop2_plane_init
    17. ->drm_crtc_init_with_planes
    18. ->drm_crtc_helper_add
    19. ->rockchip_register_crtc_funcs
    20. ->dw_hdmi_rockchip_bind
    21. ->dw_mipi_dsi_bind
    22. ->drm_encoder_init
    23. ->drm_encoder_helper_add
    24. ->drm_connector_init
    25. ->drm_connector_helper_add
    26. ->panel_simple_dsi_probe
    27. ->panel_simple_probe
    28. ->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_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函数填充对应回调函数:

    1. static const struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = {
    2. .fill_modes = drm_helper_probe_single_connector_modes,
    3. .destroy = dw_mipi_dsi_drm_connector_destroy,
    4. .reset = drm_atomic_helper_connector_reset,
    5. .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
    6. .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
    7. .atomic_get_property = dw_mipi_dsi_atomic_connector_get_property,
    8. };

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

    1. drm_connector_helper_add(connector, &dw_mipi_dsi_connector_helper_funcs);
    2. static inline void drm_connector_helper_add(struct drm_connector *connector,
    3. const struct drm_connector_helper_funcs *funcs)
    4. {
    5. connector->helper_private = funcs;
    6. }
    7. static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = {
    8. .get_modes = dw_mipi_dsi_connector_get_modes,
    9. };

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

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

    1. static const struct file_operations rockchip_drm_driver_fops = {
    2. .owner = THIS_MODULE,
    3. .open = drm_open,
    4. .mmap = rockchip_gem_mmap,
    5. .poll = drm_poll,
    6. .read = drm_read,
    7. .unlocked_ioctl = drm_ioctl,
    8. .compat_ioctl = drm_compat_ioctl,
    9. .release = drm_release,
    10. };
    11. long drm_ioctl(struct file *filp,
    12. unsigned int cmd, unsigned long arg)
    13. {
    14. struct drm_file *file_priv = filp->private_data;
    15. struct drm_device *dev;
    16. const struct drm_ioctl_desc *ioctl = NULL;
    17. drm_ioctl_t *func;
    18. unsigned int nr = DRM_IOCTL_NR(cmd);
    19. int retcode = -EINVAL;
    20. char stack_kdata[128];
    21. char *kdata = NULL;
    22. unsigned int in_size, out_size, drv_size, ksize;
    23. bool is_driver_ioctl;
    24. dev = file_priv->minor->dev;
    25. if (drm_dev_is_unplugged(dev))
    26. return -ENODEV;
    27. is_driver_ioctl = nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END;
    28. if (is_driver_ioctl) {
    29. /* driver ioctl */
    30. unsigned int index = nr - DRM_COMMAND_BASE;
    31. if (index >= dev->driver->num_ioctls)
    32. goto err_i1;
    33. index = array_index_nospec(index, dev->driver->num_ioctls);
    34. ioctl = &dev->driver->ioctls[index];
    35. } else {
    36. /* core ioctl */
    37. if (nr >= DRM_CORE_IOCTL_COUNT)
    38. goto err_i1;
    39. nr = array_index_nospec(nr, DRM_CORE_IOCTL_COUNT);
    40. ioctl = &drm_ioctls[nr];
    41. }
    42. drv_size = _IOC_SIZE(ioctl->cmd);
    43. out_size = in_size = _IOC_SIZE(cmd);
    44. if ((cmd & ioctl->cmd & IOC_IN) == 0)
    45. in_size = 0;
    46. if ((cmd & ioctl->cmd & IOC_OUT) == 0)
    47. out_size = 0;
    48. ksize = max(max(in_size, out_size), drv_size);
    49. DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n",
    50. task_pid_nr(current),
    51. (long)old_encode_dev(file_priv->minor->kdev->devt),
    52. file_priv->authenticated, ioctl->name);
    53. /* Do not trust userspace, use our own definition */
    54. func = ioctl->func;
    55. if (unlikely(!func)) {
    56. DRM_DEBUG("no function\n");
    57. retcode = -EINVAL;
    58. goto err_i1;
    59. }
    60. if (ksize <= sizeof(stack_kdata)) {
    61. kdata = stack_kdata;
    62. } else {
    63. kdata = kmalloc(ksize, GFP_KERNEL);
    64. if (!kdata) {
    65. retcode = -ENOMEM;
    66. goto err_i1;
    67. }
    68. }
    69. if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) {
    70. retcode = -EFAULT;
    71. goto err_i1;
    72. }
    73. if (ksize > in_size)
    74. memset(kdata + in_size, 0, ksize - in_size);
    75. retcode = drm_ioctl_kernel(filp, func, kdata, ioctl->flags);
    76. if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
    77. retcode = -EFAULT;
    78. err_i1:
    79. if (!ioctl)
    80. DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
    81. task_pid_nr(current),
    82. (long)old_encode_dev(file_priv->minor->kdev->devt),
    83. file_priv->authenticated, cmd, nr);
    84. if (kdata != stack_kdata)
    85. kfree(kdata);
    86. if (retcode)
    87. DRM_DEBUG("pid=%d, ret = %d\n", task_pid_nr(current), retcode);
    88. return retcode;
    89. }

    通过访问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:

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

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

    1. if (drm_drv_uses_atomic_modeset(dev))
    2. ret = crtc->funcs->set_config(&set, &ctx);
    3. else
    4. ret = __drm_mode_set_config_internal(&set, &ctx);

    struct drm_crtc_funcs结构体:

    1. static const struct drm_crtc_funcs vop2_crtc_funcs =
    2. {
    3. .gamma_set = vop2_crtc_legacy_gamma_set,
    4. .set_config = drm_atomic_helper_set_config,
    5. .page_flip = drm_atomic_helper_page_flip,
    6. .destroy = vop2_crtc_destroy,
    7. .reset = vop2_crtc_reset,
    8. .atomic_get_property = vop2_crtc_atomic_get_property,
    9. .atomic_set_property = vop2_crtc_atomic_set_property,
    10. .atomic_duplicate_state = vop2_crtc_duplicate_state,
    11. .atomic_destroy_state = vop2_crtc_destroy_state,
    12. .enable_vblank = vop2_crtc_enable_vblank,
    13. .disable_vblank = vop2_crtc_disable_vblank,
    14. .set_crc_source = vop2_crtc_set_crc_source,
    15. .verify_crc_source = vop2_crtc_verify_crc_source,
    16. };

    drm_atomic_helper_set_config实现:

    1. int drm_atomic_helper_set_config(struct drm_mode_set *set,
    2. struct drm_modeset_acquire_ctx *ctx)
    3. {
    4. struct drm_atomic_state *state;
    5. struct drm_crtc *crtc = set->crtc;
    6. int ret = 0;
    7. state = drm_atomic_state_alloc(crtc->dev);
    8. if (!state)
    9. return -ENOMEM;
    10. state->acquire_ctx = ctx;
    11. ret = __drm_atomic_helper_set_config(set, state);
    12. if (ret != 0)
    13. goto fail;
    14. ret = handle_conflicting_encoders(state, true);
    15. if (ret)
    16. goto fail;
    17. ret = drm_atomic_commit(state);
    18. fail:
    19. drm_atomic_state_put(state);
    20. return ret;
    21. }

    用户态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实现为:

    1. int drm_mode_atomic_ioctl(struct drm_device *dev,
    2. void *data, struct drm_file *file_priv)
    3. {
    4. //省略无关代码
    5. drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
    6. state = drm_atomic_state_alloc(dev);
    7. if (!state)
    8. return -ENOMEM;
    9. state->acquire_ctx = &ctx;
    10. state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET);
    11. retry:
    12. copied_objs = 0;
    13. copied_props = 0;
    14. fence_state = NULL;
    15. num_fences = 0;
    16. for (i = 0; i < arg->count_objs; i++) {
    17. uint32_t obj_id, count_props;
    18. struct drm_mode_object *obj;
    19. if (get_user(obj_id, objs_ptr + copied_objs)) {
    20. ret = -EFAULT;
    21. goto out;
    22. }
    23. obj = drm_mode_object_find(dev, file_priv, obj_id, DRM_MODE_OBJECT_ANY);
    24. if (!obj) {
    25. ret = -ENOENT;
    26. goto out;
    27. }
    28. if (!obj->properties) {
    29. drm_mode_object_put(obj);
    30. ret = -ENOENT;
    31. goto out;
    32. }
    33. if (get_user(count_props, count_props_ptr + copied_objs)) {
    34. drm_mode_object_put(obj);
    35. ret = -EFAULT;
    36. goto out;
    37. }
    38. copied_objs++;
    39. for (j = 0; j < count_props; j++) {
    40. uint32_t prop_id;
    41. uint64_t prop_value;
    42. struct drm_property *prop;
    43. if (get_user(prop_id, props_ptr + copied_props)) {
    44. drm_mode_object_put(obj);
    45. ret = -EFAULT;
    46. goto out;
    47. }
    48. prop = drm_mode_obj_find_prop_id(obj, prop_id);
    49. if (!prop) {
    50. drm_mode_object_put(obj);
    51. ret = -ENOENT;
    52. goto out;
    53. }
    54. if (copy_from_user(&prop_value,
    55. prop_values_ptr + copied_props,
    56. sizeof(prop_value))) {
    57. drm_mode_object_put(obj);
    58. ret = -EFAULT;
    59. goto out;
    60. }
    61. ret = drm_atomic_set_property(state, obj, prop,
    62. prop_value);
    63. if (ret) {
    64. drm_mode_object_put(obj);
    65. goto out;
    66. }
    67. copied_props++;
    68. }
    69. drm_mode_object_put(obj);
    70. }
    71. ret = prepare_signaling(dev, state, arg, file_priv, &fence_state,
    72. &num_fences);
    73. if (ret)
    74. goto out;
    75. if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) {
    76. ret = drm_atomic_check_only(state);
    77. } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) {
    78. ret = drm_atomic_nonblocking_commit(state);
    79. } else {
    80. if (unlikely(drm_debug & DRM_UT_STATE))
    81. drm_atomic_print_state(state);
    82. ret = drm_atomic_commit(state);
    83. }
    84. //省略无关代码
    85. return ret;
    86. }

    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接口。

    用户态相关接口:

    1. typedef struct _drmModeAtomicReq drmModeAtomicReq, *drmModeAtomicReqPtr;
    2. extern drmModeAtomicReqPtr drmModeAtomicAlloc(void);
    3. extern drmModeAtomicReqPtr drmModeAtomicDuplicate(drmModeAtomicReqPtr req);
    4. extern int drmModeAtomicMerge(drmModeAtomicReqPtr base,
    5. drmModeAtomicReqPtr augment);
    6. extern void drmModeAtomicFree(drmModeAtomicReqPtr req);
    7. extern int drmModeAtomicGetCursor(drmModeAtomicReqPtr req);
    8. extern void drmModeAtomicSetCursor(drmModeAtomicReqPtr req, int cursor);
    9. extern int drmModeAtomicAddProperty(drmModeAtomicReqPtr req,
    10. uint32_t object_id,
    11. uint32_t property_id,
    12. uint64_t value);
    13. extern int drmModeAtomicCommit(int fd,
    14. drmModeAtomicReqPtr req,
    15. uint32_t flags,
    16. 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表示:

    1. struct drm_atomic_state {
    2. struct kref ref;
    3. struct drm_device *dev;
    4. bool allow_modeset : 1;
    5. bool legacy_cursor_update : 1;
    6. bool async_update : 1;
    7. bool duplicated : 1;
    8. struct __drm_planes_state *planes;
    9. struct __drm_crtcs_state *crtcs;
    10. int num_connector;
    11. struct __drm_connnectors_state *connectors;
    12. int num_private_objs;
    13. struct __drm_private_objs_state *private_objs;
    14. struct drm_modeset_acquire_ctx *acquire_ctx;
    15. struct drm_crtc_commit *fake_commit;
    16. struct work_struct commit_work;
    17. };

    可以看到他由每个独立组件(即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中。

    1. struct __drm_{object}_state {
    2. struct drm_{object} *ptr;
    3. struct drm_{object}_state *state, *old_state, *new_state;
    4. /* extra fields may exist */
    5. };

    这里的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进行抽象:

    1. struct drm_crtc_commit {
    2. struct drm_crtc *crtc;
    3. struct kref ref;
    4. struct completion flip_done;
    5. struct completion hw_done;
    6. struct completion cleanup_done;
    7. struct list_head commit_entry;
    8. struct drm_pending_vblank_event *event;
    9. bool abort_completion;
    10. };

    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函数实现, 如下:

    1. int drm_atomic_commit(struct drm_atomic_state *state)
    2. {
    3. struct drm_mode_config *config = &state->dev->mode_config;
    4. int ret;
    5. ret = drm_atomic_check_only(state);
    6. if (ret)
    7. return ret;
    8. DRM_DEBUG_ATOMIC("committing %p\n", state);
    9. return config->funcs->atomic_commit(state->dev, state, false);
    10. }

    主要分为检查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分成两段,如下:

    1. if (state->async_update) {
    2. ret = drm_atomic_helper_prepare_planes(dev, state);
    3. if (ret)
    4. return ret;
    5. drm_atomic_helper_async_commit(dev, state);
    6. drm_atomic_helper_cleanup_planes(dev, state);
    7. return 0;
    8. }

    在非异步模式下,函数首先调用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函数,如下:

    1. if (nonblock)
    2. queue_work(system_unbound_wq, &state->commit_work);
    3. else
    4. commit_tail(state);

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

    1. static void commit_work(struct work_struct *work)
    2. {
    3. struct drm_atomic_state *state = container_of(work,
    4. struct drm_atomic_state,
    5. commit_work);
    6. commit_tail(state);
    7. }

    commit_tail的实现用到了多个helper:

    1. static void commit_tail(struct drm_atomic_state *old_state)
    2. {
    3. struct drm_device *dev = old_state->dev;
    4. const struct drm_mode_config_helper_funcs *funcs;
    5. funcs = dev->mode_config.helper_private;
    6. drm_atomic_helper_wait_for_fences(dev, old_state, false);
    7. drm_atomic_helper_wait_for_dependencies(old_state);
    8. if (funcs && funcs->atomic_commit_tail)
    9. funcs->atomic_commit_tail(old_state);
    10. else
    11. drm_atomic_helper_commit_tail(old_state);
    12. drm_atomic_helper_commit_cleanup_done(old_state);
    13. drm_atomic_state_put(old_state);
    14. }

    6.4)drm_atomic_helper_setup_commit

    函数首先遍历所有状态发生改变的CRTC,然后对其创建drm_crtc_commit,前面看到这个对象是对Commit操作的进度进行追踪用的。创建之后的drm_crtc_commit就保存在new_crtc_state->commit中。之后函数会调用stall_checks检查当前的commit队列中是否有停滞的commit:

    1. list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
    2. if (i == 0) {
    3. completed = try_wait_for_completion(&commit->flip_done);
    4. /* Userspace is not allowed to get ahead of the previous
    5. * commit with nonblocking ones. */
    6. if (!completed && nonblock) {
    7. spin_unlock(&crtc->commit_lock);
    8. return -EBUSY;
    9. }
    10. } else if (i == 1) {
    11. stall_commit = drm_crtc_commit_get(commit);
    12. break;
    13. }
    14. i++;
    15. }
    16. spin_unlock(&crtc->commit_lock);

    从逻辑上来看,从commit队列中取下第一个drm_commit,然后检查其flip_donecompletion是否已经被完成了。如果没有完成,且运行drm_atomic_helper_commit时为为非阻塞模式(nonblock参数为true),则直接让整次commit操作返回-EBUSY。随后,取下第二个drm_commit(如果存在的话),并认为其为stall的,直接以10秒为timeout等待其cleanup_done完成。

    函数接下来做了两个简单的优化:

    1. /* Drivers only send out events when at least either current or
    2. * new CRTC state is active. Complete right away if everything
    3. * stays off. */
    4. if (!old_crtc_state->active && !new_crtc_state->active) {
    5. complete_all(&commit->flip_done);
    6. continue;
    7. }
    8. /* Legacy cursor updates are fully unsynced. */
    9. if (state->legacy_cursor_update) {
    10. complete_all(&commit->flip_done);
    11. continue;
    12. }

    新旧两个状态的CRTC都为关闭状态时肯定flip_done是直接完成的。且使用Legacy Cursor相关的API时,因为这个API本身就不同步,所以可以直接视为完成了flip_done。随后函数为每个CRTC创建drm_pending_event并放入new_crtc_state->event中,注意新创建的drm_pending_eventcompletion指针直接指向前面创建的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_doneflip_done

    6.7)drm_atomic_helper_commit_tail

    commit_tail中调用了drm_cmode_config_helpers->atomic_commit_tail回调函数,在其为空的情况下,则直接调用drm_atomic_helper_commit_tail

    1. void drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state)
    2. {
    3. struct drm_device *dev = old_state->dev;
    4. drm_atomic_helper_commit_modeset_disables(dev, old_state);
    5. drm_atomic_helper_commit_planes(dev, old_state, 0);
    6. drm_atomic_helper_commit_modeset_enables(dev, old_state);
    7. drm_atomic_helper_fake_vblank(old_state);
    8. drm_atomic_helper_commit_hw_done(old_state);
    9. drm_atomic_helper_wait_for_vblanks(dev, old_state);
    10. drm_atomic_helper_cleanup_planes(dev, old_state);
    11. }

  • 相关阅读:
    05.[图解TCPIP(第5版) 文档简单整理
    保姆级教程 | 表格自动行合并实现
    AI虚拟主播开发实战(附源码)
    操作系统 day09(线程)
    【附源码】计算机毕业设计SSM社区留守儿童帮扶系统
    基于maven的项目搭建(已跑通)
    Jvm上如何运行其他语言?JSR223规范最详细讲解
    基于X86+FPGA+AI的芯片缺陷检测方案
    泉盛UV-K5/K6全功能中文固件
    VMware tools的安装以及按钮灰色的解决方法
  • 原文地址:https://blog.csdn.net/qq_33782617/article/details/126214791