本系列文章基于linux 5.15
在上一篇文章DRM系列七:Drm之DRM_IOCTL_MODE_CREATE_DUMB获取buf的handle和pitch之后,接着使用ioctl(fd, DRM_IOCTL_MODE_ADDFB2, &fb_cmd)创建一个新的帧缓冲区对象(framebuffer object),并将帧缓冲区对象与显存关联起来。
一、整体流程
用户层提供width、height、piexel_format、handle和pitch,然后调用ioctl(fd, DRM_IOCTL_MODE_ADDFB2, &fb_cmd)进入kernel层,调用drm_mode_addfb2_ioctl创建新的帧缓冲区对象(framebuffer object),并将其与显存中的一块内存区域关联起来,返回fb_id供用户层使用。其关系如下图所示:
1.drm_mode_addfb2_ioctl
主要作用是为用户空间的应用程序创建一个新的帧缓冲区对象(framebuffer object),并将其与显存中的一块内存区域关联起来。
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2_ioctl, 0),
int drm_mode_addfb2_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
#ifdef __BIG_ENDIAN
if (!dev->mode_config.quirk_addfb_prefer_host_byte_order) {
DRM_DEBUG_KMS("addfb2 broken on bigendian");
return -EOPNOTSUPP;
}
#endif
return drm_mode_addfb2(dev, data, file_priv);
}
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);
/*将新创建的 framebuffer 的 ID 存储到用户空间传递的 drm_mode_fb_cmd2 结构中,以便用户空间可以引用该 framebuffer*/
DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
r->fb_id = fb->base.id;
/* 帧缓冲区被添加到drm_file(file_priv)拥有的帧缓冲区列表中 */
mutex_lock(&file_priv->fbs_lock);
list_add(&fb->filp_head, &file_priv->fbs);
mutex_unlock(&file_priv->fbs_lock);
return 0;
}
1.1drm_internal_framebuffer_create
创建一个 drm_framebuffer 对象并初始化;接着会调用 framebuffer_check 函数对传入的参数进行检查,确保参数有效;最后调用dev->mode_config.funcs->fb_create回调,返回一个 drm_framebuffer 对象。
struct drm_framebuffer *drm_internal_framebuffer_create(struct drm_device *dev,
const struct drm_mode_fb_cmd2 *r,
struct drm_file *file_priv)
{
struct drm_mode_config *config = &dev->mode_config;
struct drm_framebuffer *fb;
int ret;
if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) {
DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
return ERR_PTR(-EINVAL);
}
/*mode_config的min_width和min_height和帧缓冲区对象的width和height的限制*/
if ((config->min_width > r->width) || (r->width > config->max_width)) {
DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
r->width, config->min_width, config->max_width);
return ERR_PTR(-EINVAL);
}
if ((config->min_height > r->height) || (r->height > config->max_height)) {
DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
r->height, config->min_height, config->max_height);
return ERR_PTR(-EINVAL);
}
if (r->flags & DRM_MODE_FB_MODIFIERS &&
!dev->mode_config.allow_fb_modifiers) {
DRM_DEBUG_KMS("driver does not support fb modifiers\n");
return ERR_PTR(-EINVAL);
}
/*对传入的参数进行检查,确保参数有效*/
ret = framebuffer_check(dev, r);
if (ret)
return ERR_PTR(ret);
/*调用mode_config func的fb_create回调*/
fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
if (IS_ERR(fb)) {
DRM_DEBUG_KMS("could not create framebuffer\n");
return fb;
}
return fb;
}
1.1.1framebuffer_check
对传入的参数进行检查,确保参数有效。
static int framebuffer_check(struct drm_device *dev,
const struct drm_mode_fb_cmd2 *r)
{
const struct drm_format_info *info;
int i;
/* check if the format is supported at all */
if (!__drm_format_info(r->pixel_format)) {
DRM_DEBUG_KMS("bad framebuffer format %p4cc\n",
&r->pixel_format);
return -EINVAL;
}
if (r->width == 0) {
DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width);
return -EINVAL;
}
if (r->height == 0) {
DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height);
return -EINVAL;
}
/* 获取与之相关的像素格式信息 */
info = drm_get_format_info(dev, r);
for (i = 0; i < info->num_planes; i++) {
unsigned int width = fb_plane_width(r->width, info, i);
unsigned int height = fb_plane_height(r->height, info, i);
unsigned int block_size = info->char_per_block[i];
u64 min_pitch = drm_format_info_min_pitch(info, i, width);
if (!block_size && (r->modifier[i] == DRM_FORMAT_MOD_LINEAR)) {
DRM_DEBUG_KMS("Format requires non-linear modifier for plane %d\n", i);
return -EINVAL;
}
if (!r->handles[i]) {
DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
return -EINVAL;
}
if (min_pitch > UINT_MAX)
return -ERANGE;
if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
return -ERANGE;
if (block_size && r->pitches[i] < min_pitch) {
DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
return -EINVAL;
}
if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) {
DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
r->modifier[i], i);
return -EINVAL;
}
if (r->flags & DRM_MODE_FB_MODIFIERS &&
r->modifier[i] != r->modifier[0]) {
DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
r->modifier[i], i);
return -EINVAL;
}
/* modifier specific checks: */
switch (r->modifier[i]) {
case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
/* NOTE: the pitch restriction may be lifted later if it turns
* out that no hw has this restriction:
*/
if (r->pixel_format != DRM_FORMAT_NV12 ||
width % 128 || height % 32 ||
r->pitches[i] % 128) {
DRM_DEBUG_KMS("bad modifier data for plane %d\n", i);
return -EINVAL;
}
break;
default:
break;
}
}
for (i = info->num_planes; i < 4; i++) {
if (r->modifier[i]) {
DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i);
return -EINVAL;
}
/* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */
if (!(r->flags & DRM_MODE_FB_MODIFIERS))
continue;
if (r->handles[i]) {
DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i);
return -EINVAL;
}
if (r->pitches[i]) {
DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i);
return -EINVAL;
}
if (r->offsets[i]) {
DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i);
return -EINVAL;
}
}
return 0;
}
1.1.1.1drm_get_format_info
用于根据用户空间提供的 framebuffer 创建请求(drm_mode_fb_cmd2),获取与之相关的像素格式信息。在这里会使用dev->mode_config.funcs->get_format_info回调。
const struct drm_format_info *drm_get_format_info(struct drm_device *dev,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
const struct drm_format_info *info = NULL;
if (dev->mode_config.funcs->get_format_info)
info = dev->mode_config.funcs->get_format_info(mode_cmd);
if (!info)
info = drm_format_info(mode_cmd->pixel_format);
return info;
}