• KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(3)


    接前一篇文章:KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(2)

    前文书说到drmModeAddFB、drmModeAddFB2和drmModeAddFB2WithModifiers函数最终“三分归一统”,在内核层统一调用到drm_mode_addfb2函数。上回书深入回顾了从上到下逐步合并统一的过程,本文正式开始对于drm_mode_addfb2函数进行详细解析。

    实际上之前笔者进行过分析,参见DRM全解析 —— ADD_FB2(4)

    但是这里由于最近研究的深入,因此要比之前进行更细致一些的代码解析。

    drm_mode_addfb2函数在Linux内核源码根目录下的drivers/gpu/drm/drm_framebuffer.c中,代码如下:

    1. /**
    2. * drm_mode_addfb2 - add an FB to the graphics configuration
    3. * @dev: drm device for the ioctl
    4. * @data: data pointer for the ioctl
    5. * @file_priv: drm file for the ioctl call
    6. *
    7. * Add a new FB to the specified CRTC, given a user request with format. This is
    8. * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
    9. * and uses fourcc codes as pixel format specifiers.
    10. *
    11. * Called by the user via ioctl.
    12. *
    13. * Returns:
    14. * Zero on success, negative errno on failure.
    15. */
    16. int drm_mode_addfb2(struct drm_device *dev,
    17. void *data, struct drm_file *file_priv)
    18. {
    19. struct drm_mode_fb_cmd2 *r = data;
    20. struct drm_framebuffer *fb;
    21. if (!drm_core_check_feature(dev, DRIVER_MODESET))
    22. return -EOPNOTSUPP;
    23. fb = drm_internal_framebuffer_create(dev, r, file_priv);
    24. if (IS_ERR(fb))
    25. return PTR_ERR(fb);
    26. drm_dbg_kms(dev, "[FB:%d]\n", fb->base.id);
    27. r->fb_id = fb->base.id;
    28. /* Transfer ownership to the filp for reaping on close */
    29. mutex_lock(&file_priv->fbs_lock);
    30. list_add(&fb->filp_head, &file_priv->fbs);
    31. mutex_unlock(&file_priv->fbs_lock);
    32. return 0;
    33. }

    先来看第一步暨第一个函数:drm_core_check_feature。对应的代码片段为:

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

    drm_core_check_feature函数在include/drm/drm_drv.h中,代码如下:

    1. /**
    2. * drm_core_check_feature - check driver feature flags
    3. * @dev: DRM device to check
    4. * @feature: feature flag
    5. *
    6. * This checks @dev for driver features, see &drm_driver.driver_features,
    7. * &drm_device.driver_features, and the various &enum drm_driver_feature flags.
    8. *
    9. * Returns true if the @feature is supported, false otherwise.
    10. */
    11. static inline bool drm_core_check_feature(const struct drm_device *dev,
    12. enum drm_driver_feature feature)
    13. {
    14. return drm_core_check_all_features(dev, feature);
    15. }

    可以看到,实际的工作drm_core_check_all_features函数完成的。drm_core_check_all_features函数就在上边,代码如下:

    1. /**
    2. * drm_core_check_all_features - check driver feature flags mask
    3. * @dev: DRM device to check
    4. * @features: feature flag(s) mask
    5. *
    6. * This checks @dev for driver features, see &drm_driver.driver_features,
    7. * &drm_device.driver_features, and the various &enum drm_driver_feature flags.
    8. *
    9. * Returns true if all features in the @features mask are supported, false
    10. * otherwise.
    11. */
    12. static inline bool drm_core_check_all_features(const struct drm_device *dev,
    13. u32 features)
    14. {
    15. u32 supported = dev->driver->driver_features & dev->driver_features;
    16. return features && (supported & features) == features;
    17. }

    函数的功能是先将struct drm_device *dev(设备实例)对应的struct drm_driver *driver(驱动实例)中的driver_features与struct drm_device *dev(设备实例)自身的driver_features相与,得到仅限于设备自身特性空间的几项。然后看这几项中是否支持features特性。如果features本身不为0并且支持,则返回真(true);否则返回假(false)。而一旦返回false,则drm_core_check_feature函数最终返回-EOPNOTSUPP(include/uapi/asm-generic/errno.h中定义),表示“Operation not supported on transport endpoint”即传输端点上不支持该操作。

    深入跟进到具体调用细节。DRIVER_MODESET是一个宏,在include/drm/drm_drv.h中定义,如下:

    1. /**
    2. * @DRIVER_MODESET:
    3. *
    4. * Driver supports mode setting interfaces (KMS).
    5. */
    6. DRIVER_MODESET = BIT(1),

    在此传入drm_core_check_all_features函数的features为DRIVER_MODESET(值为2),代表支持KMS(Kernel Mode Setting)接口(与否)。

    重点看一下dev->driver->driver_features。dev->driver的意思是drm设备对应的drm驱动。实际上dev->driver对于不同的显卡驱动,是对应不同的struct drm_driver实例的。这里以笔者实际使用的电脑中的显卡Intel i915为例进行说明。

    Intel i915显卡驱动对应的struct drm_driver初始化代码在drivers/gpu/drm/i915/i915_driver.c中,如下:

    1. static const struct drm_driver i915_drm_driver = {
    2. /* Don't use MTRRs here; the Xserver or userspace app should
    3. * deal with them for Intel hardware.
    4. */
    5. .driver_features =
    6. DRIVER_GEM |
    7. DRIVER_RENDER | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_SYNCOBJ |
    8. DRIVER_SYNCOBJ_TIMELINE,
    9. .release = i915_driver_release,
    10. .open = i915_driver_open,
    11. .lastclose = i915_driver_lastclose,
    12. .postclose = i915_driver_postclose,
    13. .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
    14. .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
    15. .gem_prime_import = i915_gem_prime_import,
    16. .dumb_create = i915_gem_dumb_create,
    17. .dumb_map_offset = i915_gem_dumb_mmap_offset,
    18. .ioctls = i915_ioctls,
    19. .num_ioctls = ARRAY_SIZE(i915_ioctls),
    20. .fops = &i915_driver_fops,
    21. .name = DRIVER_NAME,
    22. .desc = DRIVER_DESC,
    23. .date = DRIVER_DATE,
    24. .major = DRIVER_MAJOR,
    25. .minor = DRIVER_MINOR,
    26. .patchlevel = DRIVER_PATCHLEVEL,
    27. };

    由代码可见,对于i915显卡驱动来说,其支持的特性为:

    1. .driver_features =
    2. DRIVER_GEM |
    3. DRIVER_RENDER | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_SYNCOBJ |
    4. DRIVER_SYNCOBJ_TIMELINE,

    可见,以上3款显卡都是支持DRIVER_MODESET这一feature的。

    而对于dev->driver_features即设备实例的driver_features,就不像dev->driver->driver_features即驱动实例的driver_features那样明显了。在DRM即内核源码路径下,共有几处相关,分别如下:

    1)drivers/gpu/drm/drm_drv.c的drm_dev_init函数中:

    1. static int drm_dev_init(struct drm_device *dev,
    2. const struct drm_driver *driver,
    3. struct device *parent)
    4. {
    5. struct inode *inode;
    6. int ret;
    7. ……
    8. /* no per-device feature limits by default */
    9. dev->driver_features = ~0u;
    10. ……
    11. }

    2)drivers/vdpa/vdpa_user/vduse_dev.c的vduse_dev_reset函数中:

    1. static void vduse_dev_reset(struct vduse_dev *dev)
    2. {
    3. int i;
    4. struct vduse_iova_domain *domain = dev->domain;
    5. /* The coherent mappings are handled in vduse_dev_free_coherent() */
    6. if (domain->bounce_map)
    7. vduse_domain_reset_bounce_map(domain);
    8. down_write(&dev->rwsem);
    9. dev->status = 0;
    10. dev->driver_features = 0;
    11. dev->generation++;
    12. spin_lock(&dev->irq_lock);
    13. dev->config_cb.callback = NULL;
    14. dev->config_cb.private = NULL;
    15. spin_unlock(&dev->irq_lock);
    16. flush_work(&dev->inject);
    17. ……
    18. }

    3)drivers/vdpa/vdpa_user/vduse_dev.c的vduse_vdpa_set_driver_features函数中:

    1. static int vduse_vdpa_set_driver_features(struct vdpa_device *vdpa, u64 features)
    2. {
    3. struct vduse_dev *dev = vdpa_to_vduse(vdpa);
    4. dev->driver_features = features;
    5. return 0;
    6. }

    可以想见,虽然目前尚不完全清楚设置dev->driver_features即设备实例的driver_features的具体过程和细节,但是由于代码可以正常执行而并未返回false,可以推断出一定在某处(很可能是vduse_vdpa_set_driver_features函数中)将相应位设置为了DRIVER_MODESET。

    至此,drm_mode_addfb2函数中的第一步:drm_core_check_feature函数就分析完了。余下的步骤和函数在接下来的文章中继续进行解析。

  • 相关阅读:
    Chapter 函数
    C 语言教程:条件和 if...else 语句
    菲律宾公司注册
    ChatGPT 即将诞生一周年,OpenAI 将有大动作
    【目标检测】英雄联盟能用YOLOv5实时目标检测了 支持onnx推理
    容器编排学习(十)控制器介绍与使用
    C# Winfrom 常用功能整合-2
    当 AI 遇上 web3,会碰撞出什么火花?
    Maven Helper 安装使用
    【图像处理】使用各向异性滤波器和分割图像处理从MRI图像检测脑肿瘤(Matlab代码实现)
  • 原文地址:https://blog.csdn.net/phmatthaus/article/details/133752943