本系列文章基于linux 5.15
DRM驱动的显存由GEM(Graphics execution management)管理。
一、创建流程
创建buf时,user层提供需要buf的width,height以及bpp(bite per pixel),然后调用drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create)接口进入kernel层,kernel层根据这些信息分配出handle和pitch提供给user层使用。
二、kernel层调用流程
kernel层会调用drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create),然后通过DRM_IOCTL_DEF 宏进行映射到drm_mode_create_dumb_ioctl中。
/*DRM_IOCTL_DEF 宏用于简化 DRM 子系统中 IOCTL 命令的定义和映射。
它通过将 IOCTL 命令、处理函数和标志打包到一个 drm_ioctl_desc 结构体中,
方便内核开发者管理和扩展 DRM 的 IOCTL 接口。*/
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, 0),
int drm_mode_create_dumb_ioctl(struct drm_device *dev,void *data, struct drm_file *file_priv)
{return drm_mode_create_dumb(dev, data, file_priv);
}
2.drm_mode_create_dumb
drm_mode_create_dumb首先user层传来的参数width/height/bpp是否符合条件,然后调用dev->driver->dumb_create这一回调获取handle和pitch。
int drm_mode_create_dumb(struct drm_device *dev,struct drm_mode_create_dumb *args,struct drm_file *file_priv)
{u32 cpp, stride, size;
/*判别user层提供的width/height/bpp是否符合条件*/if (!dev->driver->dumb_create)return -ENOSYS;if (!args->width || !args->height || !args->bpp)return -EINVAL;/* overflow checks for 32bit size calculations */if (args->bpp > U32_MAX - 8)return -EINVAL;cpp = DIV_ROUND_UP(args->bpp, 8);if (cpp > U32_MAX / args->width)return -EINVAL;stride = cpp * args->width;if (args->height > U32_MAX / stride)return -EINVAL;/* test for wrap-around */size = args->height * stride;if (PAGE_ALIGN(size) == 0)return -EINVAL;args->handle = 0;args->pitch = 0;args->size = 0;return dev->driver->dumb_create(file_priv, dev, args);
}
dumb_create这一回调是各大厂商自己实现的,以msm厂商为例,会调用msm_gem_dumb_create这一函数.
static const struct drm_driver msm_driver = {.driver_features = DRIVER_GEM |DRIVER_RENDER |DRIVER_ATOMIC |DRIVER_MODESET |DRIVER_SYNCOBJ,.open = msm_open,.postclose = msm_postclose,.lastclose = drm_fb_helper_lastclose,.dumb_create = msm_gem_dumb_create,.dumb_map_offset = msm_gem_dumb_map_offset,.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 = msm_gem_prime_import_sg_table,.gem_prime_mmap = drm_gem_prime_mmap,
#ifdef CONFIG_DEBUG_FS.debugfs_init = msm_debugfs_init,
#endif.ioctls = msm_ioctls,.num_ioctls = ARRAY_SIZE(msm_ioctls),.fops = &fops,.name = "msm",.desc = "MSM Snapdragon DRM",.date = "20130625",.major = MSM_VERSION_MAJOR,.minor = MSM_VERSION_MINOR,.patchlevel = MSM_VERSION_PATCHLEVEL,
};
int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,struct drm_mode_create_dumb *args)
{args->pitch = align_pitch(args->width, args->bpp);args->size = PAGE_ALIGN(args->pitch * args->height);return msm_gem_new_handle(dev, file, args->size,MSM_BO_SCANOUT | MSM_BO_WC, &args->handle, "dumb");
}int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,uint32_t size, uint32_t flags, uint32_t *handle,char *name)
{struct drm_gem_object *obj;int ret;/*创建一个新的 GEM 对象*/obj = msm_gem_new(dev, size, flags);if (IS_ERR(obj))return PTR_ERR(obj);if (name)msm_gem_object_set_name(obj, "%s", name);/*为 GEM 对象创建句柄*/ret = drm_gem_handle_create(file, obj, handle);/* 减少对象的引用计数(句柄已经持有引用) */drm_gem_object_put(obj);return ret;
}
2.1drm_gem_handle_create
drm_gem_handle_create主要使用idr_alloc将drm_gem_object对象添加到file_priv->object_idr,并返回handle。
idr_alloc:是为了使用一个id与一个obj绑定。这样就可以通过id找到对应obj。这里将handle与分配的gem_object进行绑定,后面通过handle可以找到gem_object。
int drm_gem_handle_create(struct drm_file *file_priv,struct drm_gem_object *obj,u32 *handlep)
{mutex_lock(&obj->dev->object_name_lock);return drm_gem_handle_create_tail(file_priv, obj, handlep);
}int drm_gem_handle_create_tail(struct drm_file *file_priv,struct drm_gem_object *obj,u32 *handlep)
{struct drm_device *dev = obj->dev;u32 handle;int ret;WARN_ON(!mutex_is_locked(&dev->object_name_lock));if (obj->handle_count++ == 0)drm_gem_object_get(obj);/** Get the user-visible handle using idr. Preload and perform* allocation under our spinlock.*/idr_preload(GFP_KERNEL);spin_lock(&file_priv->table_lock);ret = idr_alloc(&file_priv->object_idr, obj, 1, 0, GFP_NOWAIT);spin_unlock(&file_priv->table_lock);idr_preload_end();mutex_unlock(&dev->object_name_lock);if (ret < 0)goto err_unref;handle = ret;ret = drm_vma_node_allow(&obj->vma_node, file_priv);if (ret)goto err_remove;if (obj->funcs->open) {ret = obj->funcs->open(obj, file_priv);if (ret)goto err_revoke;}*handlep = handle;return 0;err_revoke:drm_vma_node_revoke(&obj->vma_node, file_priv);
err_remove:spin_lock(&file_priv->table_lock);idr_remove(&file_priv->object_idr, handle);spin_unlock(&file_priv->table_lock);
err_unref:drm_gem_object_handle_put_unlocked(obj);return ret;
}