接前一篇文章:DRM全解析 —— ADD_FB2(3)
本文参考以下博文:
特此致谢!
本文开始正式对于drm_mode_addfb2函数进行解析。为了便于理解,再次贴出drm_mode_addfb2函数代码:
/**
* drm_mode_addfb2 - add an FB to the graphics configuration
* @dev: drm device for the ioctl
* @data: data pointer for the ioctl
* @file_priv: drm file for the ioctl call
*
* Add a new FB to the specified CRTC, given a user request with format. This is
* the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
* and uses fourcc codes as pixel format specifiers.
*
* Called by the user via ioctl.
*
* Returns:
* Zero on success, negative errno on failure.
*/
int drm_mode_addfb2(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
struct drm_mode_fb_cmd2 *r = data;
struct drm_framebuffer *fb;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EOPNOTSUPP;
fb = drm_internal_framebuffer_create(dev, r, file_priv);
if (IS_ERR(fb))
return PTR_ERR(fb);
drm_dbg_kms(dev, "[FB:%d]\n", fb->base.id);
r->fb_id = fb->base.id;
/* Transfer ownership to the filp for reaping on close */
mutex_lock(&file_priv->fbs_lock);
list_add(&fb->filp_head, &file_priv->fbs);
mutex_unlock(&file_priv->fbs_lock);
return 0;
}
先来看第一步暨第一个函数:drm_core_check_feature。对应的代码片段为:
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EOPNOTSUPP;
drm_core_check_feature函数在include/drm/drm_drv.h中,代码如下:
/**
* drm_core_check_feature - check driver feature flags
* @dev: DRM device to check
* @feature: feature flag
*
* This checks @dev for driver features, see &drm_driver.driver_features,
* &drm_device.driver_features, and the various &enum drm_driver_feature flags.
*
* Returns true if the @feature is supported, false otherwise.
*/
static inline bool drm_core_check_feature(const struct drm_device *dev,
enum drm_driver_feature feature)
{
return drm_core_check_all_features(dev, feature);
}
可以看到,实际的工作drm_core_check_all_features函数完成的。drm_core_check_all_features函数就在上边,代码如下:
/**
* drm_core_check_all_features - check driver feature flags mask
* @dev: DRM device to check
* @features: feature flag(s) mask
*
* This checks @dev for driver features, see &drm_driver.driver_features,
* &drm_device.driver_features, and the various &enum drm_driver_feature flags.
*
* Returns true if all features in the @features mask are supported, false
* otherwise.
*/
static inline bool drm_core_check_all_features(const struct drm_device *dev,
u32 features)
{
u32 supported = dev->driver->driver_features & dev->driver_features;
return features && (supported & features) == features;
}
函数的功能是先将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中定义,如下:
/**
* @DRIVER_MODESET:
*
* Driver supports mode setting interfaces (KMS).
*/
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和AMD为例进行说明。
- Intel i915
Intel i915显卡驱动对应的struct drm_driver初始化代码在drivers/gpu/drm/i915/i915_driver.c中,如下:
static const struct drm_driver i915_drm_driver = {
/* Don't use MTRRs here; the Xserver or userspace app should
* deal with them for Intel hardware.
*/
.driver_features =
DRIVER_GEM |
DRIVER_RENDER | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_SYNCOBJ |
DRIVER_SYNCOBJ_TIMELINE,
.release = i915_driver_release,
.open = i915_driver_open,
.lastclose = i915_driver_lastclose,
.postclose = i915_driver_postclose,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import = i915_gem_prime_import,
.dumb_create = i915_gem_dumb_create,
.dumb_map_offset = i915_gem_dumb_mmap_offset,
.ioctls = i915_ioctls,
.num_ioctls = ARRAY_SIZE(i915_ioctls),
.fops = &i915_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
};
由代码可见,对于i915显卡驱动来说,其支持的特性为:
.driver_features =
DRIVER_GEM |
DRIVER_RENDER | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_SYNCOBJ |
DRIVER_SYNCOBJ_TIMELINE,
- AMD Raedon
AMD显卡有两款驱动:Raedon和AMDGPU。
先说Raedon驱动。Raedon显卡驱动对应的struct drm_driver初始化代码在drivers/gpu/drm/radeon/radeon_drv.c中,如下:
static const struct drm_driver kms_driver = {
.driver_features =
DRIVER_GEM | DRIVER_RENDER | DRIVER_MODESET,
.load = radeon_driver_load_kms,
.open = radeon_driver_open_kms,
.postclose = radeon_driver_postclose_kms,
.lastclose = radeon_driver_lastclose_kms,
.unload = radeon_driver_unload_kms,
.ioctls = radeon_ioctls_kms,
.num_ioctls = ARRAY_SIZE(radeon_ioctls_kms),
.dumb_create = radeon_mode_dumb_create,
.dumb_map_offset = radeon_mode_dumb_mmap,
.fops = &radeon_driver_kms_fops,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import_sg_table = radeon_gem_prime_import_sg_table,
.gem_prime_mmap = drm_gem_prime_mmap,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = KMS_DRIVER_MAJOR,
.minor = KMS_DRIVER_MINOR,
.patchlevel = KMS_DRIVER_PATCHLEVEL,
};
由代码可见,对于AMD Raedon显卡驱动来说,其支持的特性为:
.driver_features =
DRIVER_GEM | DRIVER_RENDER | DRIVER_MODESET,
- AMD AMDGPU
再来看AMDGPU驱动。AMDGPU显卡驱动对应的struct drm_driver初始化代码在drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c中,如下:
static const struct drm_driver amdgpu_kms_driver = {
.driver_features =
DRIVER_ATOMIC |
DRIVER_GEM |
DRIVER_RENDER | DRIVER_MODESET | DRIVER_SYNCOBJ |
DRIVER_SYNCOBJ_TIMELINE,
.open = amdgpu_driver_open_kms,
.postclose = amdgpu_driver_postclose_kms,
.lastclose = amdgpu_driver_lastclose_kms,
.ioctls = amdgpu_ioctls_kms,
.num_ioctls = ARRAY_SIZE(amdgpu_ioctls_kms),
.dumb_create = amdgpu_mode_dumb_create,
.dumb_map_offset = amdgpu_mode_dumb_mmap,
.fops = &amdgpu_driver_kms_fops,
.release = &amdgpu_driver_release_kms,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import = amdgpu_gem_prime_import,
.gem_prime_mmap = drm_gem_prime_mmap,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = KMS_DRIVER_MAJOR,
.minor = KMS_DRIVER_MINOR,
.patchlevel = KMS_DRIVER_PATCHLEVEL,
};
由代码可见,对于AMD AMDGPU显卡驱动来说,其支持的特性为:
.driver_features =
DRIVER_ATOMIC |
DRIVER_GEM |
DRIVER_RENDER | DRIVER_MODESET | DRIVER_SYNCOBJ |
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函数中:
static int drm_dev_init(struct drm_device *dev,
const struct drm_driver *driver,
struct device *parent)
{
struct inode *inode;
int ret;
……
/* no per-device feature limits by default */
dev->driver_features = ~0u;
……
}
2)drivers/vdpa/vdpa_user/vduse_dev.c的vduse_dev_reset函数中:
static void vduse_dev_reset(struct vduse_dev *dev)
{
int i;
struct vduse_iova_domain *domain = dev->domain;
/* The coherent mappings are handled in vduse_dev_free_coherent() */
if (domain->bounce_map)
vduse_domain_reset_bounce_map(domain);
down_write(&dev->rwsem);
dev->status = 0;
dev->driver_features = 0;
dev->generation++;
spin_lock(&dev->irq_lock);
dev->config_cb.callback = NULL;
dev->config_cb.private = NULL;
spin_unlock(&dev->irq_lock);
flush_work(&dev->inject);
……
}
3)drivers/vdpa/vdpa_user/vduse_dev.c的vduse_vdpa_set_driver_features函数中:
static int vduse_vdpa_set_driver_features(struct vdpa_device *vdpa, u64 features)
{
struct vduse_dev *dev = vdpa_to_vduse(vdpa);
dev->driver_features = features;
return 0;
}
至此,drm_mode_addfb2函数中的第一步:drm_core_check_feature函数就分析完了。余下的步骤和函数在接下来的文章中继续进行解析。