DRM full analysis - ADD_FB (5)

Continued from the previous article: DRM full analysis - ADD_FB (4)

This article refers to the following blog posts:

ADD_FB of DRM driver (4)

Thank you very much!

Last time, I explained the second step in the drm_mode_addfb function and the second function drm_driver_legacy_fb_format. This time, we will continue to analyze the drm_mode_addfb function. In order to facilitate understanding, the code is posted again, in drivers/gpu/drm/drm_framebuffer.c, as follows:

/**
 * drm_mode_addfb - add an FB to the graphics configuration
 * @dev: drm device for the ioctl
 * @or: pointer to request structure
 * @file_priv: drm file
 *
 * Add a new FB to the specified CRTC, given a user request. This is the
 * original addfb ioctl which only supported RGB formats.
 *
 * Called by the user via ioctl, or by an in-kernel client.
 *
 * Returns:
 * Zero on success, negative errno on failure.
 */
int drm_mode_addfb(struct drm_device *dev, struct drm_mode_fb_cmd *or,
		   struct drm_file *file_priv)
{
	struct drm_mode_fb_cmd2 r = {};
	int ret;
 
	if (!drm_core_check_feature(dev, DRIVER_MODESET))
		return -EOPNOTSUPP;
 
	r.pixel_format = drm_driver_legacy_fb_format(dev, or->bpp, or->depth);
	if (r.pixel_format == DRM_FORMAT_INVALID) {
		drm_dbg_kms(dev, "bad {bpp:%d, depth:%d}\n", or->bpp, or->depth);
		return -EINVAL;
	}
 
	/* convert to new format and call new ioctl */
	r.fb_id = or->fb_id;
	r.width = or->width;
	r.height = or->height;
	r.pitches[0] = or->pitch;
	r.handles[0] = or->handle;
 
	ret = drm_mode_addfb2(dev, &r, file_priv);
	if (ret)
		return ret;
 
	or->fb_id = r.fb_id;
 
	return 0;
}

Next is the third step, which is also the most core and critical step: drm_mode_addfb2.

Let's take a look at this sentence first:

struct drm_mode_fb_cmd2 r = {};

The definition of struct drm_mode_fb_cmd2 is in include/uapi/drm/drm_mode.h, the code is as follows:

/**
 * struct drm_mode_fb_cmd2 - Frame-buffer metadata.
 *
 * This struct holds frame-buffer metadata. There are two ways to use it:
 *
 * - User-space can fill this struct and perform a &DRM_IOCTL_MODE_ADDFB2
 *   ioctl to register a new frame-buffer. The new frame-buffer object ID will
 *   be set by the kernel in @fb_id.
 * - User-space can set @fb_id and perform a &DRM_IOCTL_MODE_GETFB2 ioctl to
 *   fetch metadata about an existing frame-buffer.
 *
 * In case of planar formats, this struct allows up to 4 buffer objects with
 * offsets and pitches per plane. The pitch and offset order are dictated by
 * the format FourCC as defined by ``drm_fourcc.h``, e.g. NV12 is described as:
 *
 *     YUV 4:2:0 image with a plane of 8-bit Y samples followed by an
 *     interleaved U/V plane containing 8-bit 2x2 subsampled colour difference
 *     samples.
 *
 * So it would consist of a Y plane at ``offsets[0]`` and a UV plane at
 * ``offsets[1]``.
 *
 * To accommodate tiled, compressed, etc formats, a modifier can be specified.
 * For more information see the "Format Modifiers" section. Note that even
 * though it looks like we have a modifier per-plane, we in fact do not. The
 * modifier for each plane must be identical. Thus all combinations of
 * different data layouts for multi-plane formats must be enumerated as
 * separate modifiers.
 *
 * All of the entries in @handles, @pitches, @offsets and @modifier must be
 * zero when unused. Warning, for @offsets and @modifier zero can't be used to
 * figure out whether the entry is used or not since it's a valid value (a zero
 * offset is common, and a zero modifier is &DRM_FORMAT_MOD_LINEAR).
 */
struct drm_mode_fb_cmd2 {
	/** @fb_id: Object ID of the frame-buffer. */
	__u32 fb_id;
	/** @width: Width of the frame-buffer. */
	__u32 width;
	/** @height: Height of the frame-buffer. */
	__u32 height;
	/**
	 * @pixel_format: FourCC format code, see ``DRM_FORMAT_*`` constants in
	 * ``drm_fourcc.h``.
	 */
	__u32 pixel_format;
	/**
	 * @flags: Frame-buffer flags (see &DRM_MODE_FB_INTERLACED and
	 * &DRM_MODE_FB_MODIFIERS).
	 */
	__u32 flags;

	/**
	 * @handles: GEM buffer handle, one per plane. Set to 0 if the plane is
	 * unused. The same handle can be used for multiple planes.
	 */
	__u32 handles[4];
	/** @pitches: Pitch (aka. stride) in bytes, one per plane. */
	__u32 pitches[4];
	/** @offsets: Offset into the buffer in bytes, one per plane. */
	__u32 offsets[4];
	/**
	 * @modifier: Format modifier, one per plane. See ``DRM_FORMAT_MOD_*``
	 * constants in ``drm_fourcc.h``. All planes must use the same
	 * modifier. Ignored unless &DRM_MODE_FB_MODIFIERS is set in @flags.
	 */
	__u64 modifier[4];
};

According to the comments of the structure, the drm_mode_fb_cmd2 structure contains the metadata of the framebuffer, which can be understood as the attribute data of the framebuffer.

The drm_mode_addfb2 function is also in drivers/gpu/drm/drm_framebuffer.c, the code is as follows:

/**
 * 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;
}

The drm_mode_addfb2 function also calls the drm_core_check_feature function to check whether the DRIVER_MODESET feature is supported. For this part, refer to the previous DRM full analysis - ADD_FB (3) . Some readers may ask, haven't you called this function before and checked it, why call it again? Isn't this redundant? In fact, the drm_core_check_feature function is called here to check because the drm_mode_addfb2 function may be called directly, not in the drm_mode_addfb function, so it is necessary to check.

Next comes the key function: drm_internal_framebuffer_create. This function is also in drivers/gpu/drm/drm_framebuffer.c (actually above the drm_mode_addfb2 function), the code is as follows:

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_dbg_kms(dev, "bad framebuffer flags 0x%08x\n", r->flags);
		return ERR_PTR(-EINVAL);
	}

	if ((config->min_width > r->width) || (r->width > config->max_width)) {
		drm_dbg_kms(dev, "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_dbg_kms(dev, "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.fb_modifiers_not_supported) {
		drm_dbg_kms(dev, "driver does not support fb modifiers\n");
		return ERR_PTR(-EINVAL);
	}

	ret = framebuffer_check(dev, r);
	if (ret)
		return ERR_PTR(ret);

	fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
	if (IS_ERR(fb)) {
		drm_dbg_kms(dev, "could not create framebuffer\n");
		return fb;
	}

	return fb;
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_internal_framebuffer_create);

The drm_internal_framebuffer_create function first checks whether each incoming parameter is legal, and note that the framebuffer_check function is also included.

Then call dev->mode_config.funcs->fb_create(dev, file_priv, r). fb_create is also a function pointer, which can also be said to be a callback function, which is initialized when the drm driver is registered. Still take Intel graphics card and AMD graphics card as an example.

  • Intel i915

The mode_config.funcs initialization code corresponding to the Intel i915 graphics card is also in drivers/gpu/drm/i915/i915_driver.c, as follows:

static const struct drm_mode_config_funcs intel_mode_funcs = {
	.fb_create = intel_user_framebuffer_create,
	.get_format_info = intel_fb_get_format_info,
	.output_poll_changed = intel_fbdev_output_poll_changed,
	.mode_valid = intel_mode_valid,
	.atomic_check = intel_atomic_check,
	.atomic_commit = intel_atomic_commit,
	.atomic_state_alloc = intel_atomic_state_alloc,
	.atomic_state_clear = intel_atomic_state_clear,
	.atomic_state_free = intel_atomic_state_free,
};

The intel_user_framebuffer_create function is in drivers/gpu/drm/i915/display/intel_fb.c, the code is as follows:

struct drm_framebuffer *
intel_user_framebuffer_create(struct drm_device *dev,
			      struct drm_file *filp,
			      const struct drm_mode_fb_cmd2 *user_mode_cmd)
{
	struct drm_framebuffer *fb;
	struct drm_i915_gem_object *obj;
	struct drm_mode_fb_cmd2 mode_cmd = *user_mode_cmd;
	struct drm_i915_private *i915;

	obj = i915_gem_object_lookup(filp, mode_cmd.handles[0]);
	if (!obj)
		return ERR_PTR(-ENOENT);

	/* object is backed with LMEM for discrete */
	i915 = to_i915(obj->base.dev);
	if (HAS_LMEM(i915) && !i915_gem_object_can_migrate(obj, INTEL_REGION_LMEM_0)) {
		/* object is "remote", not in local memory */
		i915_gem_object_put(obj);
		return ERR_PTR(-EREMOTE);
	}

	fb = intel_framebuffer_create(obj, &mode_cmd);
	i915_gem_object_put(obj);

	return fb;
}
  • AMD Raedon

The mode_config.funcs initialization code corresponding to the AMD Raedon graphics card is in drivers/gpu/drm/radeon/radeon_display.c, as follows:

static const struct drm_mode_config_funcs radeon_mode_funcs = {
	.fb_create = radeon_user_framebuffer_create,
	.output_poll_changed = drm_fb_helper_output_poll_changed,
};

The radeon_user_framebuffer_create function is also in drivers/gpu/drm/radeon/radeon_display.c, the code is as follows:

static struct drm_framebuffer *
radeon_user_framebuffer_create(struct drm_device *dev,
			       struct drm_file *file_priv,
			       const struct drm_mode_fb_cmd2 *mode_cmd)
{
	struct drm_gem_object *obj;
	struct drm_framebuffer *fb;
	int ret;

	obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]);
	if (obj ==  NULL) {
		dev_err(dev->dev, "No GEM object associated to handle 0x%08X, "
			"can't create framebuffer\n", mode_cmd->handles[0]);
		return ERR_PTR(-ENOENT);
	}

	/* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */
	if (obj->import_attach) {
		DRM_DEBUG_KMS("Cannot create framebuffer from imported dma_buf\n");
		drm_gem_object_put(obj);
		return ERR_PTR(-EINVAL);
	}

	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
	if (fb == NULL) {
		drm_gem_object_put(obj);
		return ERR_PTR(-ENOMEM);
	}

	ret = radeon_framebuffer_init(dev, fb, mode_cmd, obj);
	if (ret) {
		kfree(fb);
		drm_gem_object_put(obj);
		return ERR_PTR(ret);
	}

	return fb;
}
  • AMD AMDGPU

The mode_config.funcs initialization code corresponding to the AMD AMDGPU graphics card is in drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c, as follows:

static const struct drm_mode_config_funcs amdgpu_dm_mode_funcs = {
	.fb_create = amdgpu_display_user_framebuffer_create,
	.get_format_info = amd_get_format_info,
	.output_poll_changed = drm_fb_helper_output_poll_changed,
	.atomic_check = amdgpu_dm_atomic_check,
	.atomic_commit = drm_atomic_helper_commit,
};

The amdgpu_display_user_framebuffer_create function is in drivers/gpu/drm/amd/amdgpu/amdgpu_display.c, the code is as follows:

struct drm_framebuffer *
amdgpu_display_user_framebuffer_create(struct drm_device *dev,
				       struct drm_file *file_priv,
				       const struct drm_mode_fb_cmd2 *mode_cmd)
{
	struct amdgpu_framebuffer *amdgpu_fb;
	struct drm_gem_object *obj;
	struct amdgpu_bo *bo;
	uint32_t domains;
	int ret;

	obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]);
	if (obj ==  NULL) {
		drm_dbg_kms(dev, "No GEM object associated to handle 0x%08X, "
			    "can't create framebuffer\n", mode_cmd->handles[0]);
		return ERR_PTR(-ENOENT);
	}

	/* Handle is imported dma-buf, so cannot be migrated to VRAM for scanout */
	bo = gem_to_amdgpu_bo(obj);
	domains = amdgpu_display_supported_domains(drm_to_adev(dev), bo->flags);
	if (obj->import_attach && !(domains & AMDGPU_GEM_DOMAIN_GTT)) {
		drm_dbg_kms(dev, "Cannot create framebuffer from imported dma_buf\n");
		drm_gem_object_put(obj);
		return ERR_PTR(-EINVAL);
	}

	amdgpu_fb = kzalloc(sizeof(*amdgpu_fb), GFP_KERNEL);
	if (amdgpu_fb == NULL) {
		drm_gem_object_put(obj);
		return ERR_PTR(-ENOMEM);
	}

	ret = amdgpu_display_gem_fb_verify_and_init(dev, amdgpu_fb, file_priv,
						    mode_cmd, obj);
	if (ret) {
		kfree(amdgpu_fb);
		drm_gem_object_put(obj);
		return ERR_PTR(ret);
	}

	drm_gem_object_put(obj);
	return &amdgpu_fb->base;
}

Whether it is the intel_user_framebuffer_create function in Intel i915, the radeon_user_framebuffer_create function in AMD Raedon or the amdgpu_display_user_framebuffer_create function in AMD AMDGPU, the drm_framebuffer_init function is called. For the analysis of this function, please see the next chapter.

Guess you like

Origin blog.csdn.net/phmatthaus/article/details/132665904