DRM full analysis - ADD_FB (6)

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

This article refers to the following blog posts:

ADD_FB of DRM driver (4)

Thank you very much!

Last time, it was mentioned that the drm_framebuffer_init function is called in the Intel i915, AMD Raedon and AMDGP graphics card drivers. This time I will explain this function.

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

/**
 * drm_framebuffer_init - initialize a framebuffer
 * @dev: DRM device
 * @fb: framebuffer to be initialized
 * @funcs: ... with these functions
 *
 * Allocates an ID for the framebuffer's parent mode object, sets its mode
 * functions & device file and adds it to the master fd list.
 *
 * IMPORTANT:
 * This functions publishes the fb and makes it available for concurrent access
 * by other users. Which means by this point the fb _must_ be fully set up -
 * since all the fb attributes are invariant over its lifetime, no further
 * locking but only correct reference counting is required.
 *
 * Returns:
 * Zero on success, error code on failure.
 */
int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
			 const struct drm_framebuffer_funcs *funcs)
{
	int ret;

	if (WARN_ON_ONCE(fb->dev != dev || !fb->format))
		return -EINVAL;

	INIT_LIST_HEAD(&fb->filp_head);

	fb->funcs = funcs;
	strcpy(fb->comm, current->comm);

	ret = __drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB,
				    false, drm_framebuffer_free);
	if (ret)
		goto out;

	mutex_lock(&dev->mode_config.fb_lock);
	dev->mode_config.num_fb++;
	list_add(&fb->head, &dev->mode_config.fb_list);
	mutex_unlock(&dev->mode_config.fb_lock);

	drm_mode_object_register(dev, &fb->base);
out:
	return ret;
}
EXPORT_SYMBOL(drm_framebuffer_init);

The components of drm are managed through the linked list fb_list in dev->mode_config, as long as you get mode_config, you can get drm related information. As shown below:

dev->mode_config is also specific to the graphics card. Still take Intel i915, AMD Radeon and AMDGPU as examples.

  • Intel i915

Assign a value in the intel_mode_config_init function of drivers/gpu/drm/i915/display/intel_display.c, the code is as follows:

struct drm_mode_config *mode_config = &i915->drm.mode_config;
  • AMD Radeon

Assign a value in the radeon_hotplug_work_func function of drivers/gpu/drm/radeon/radeon_irq_kms.c, the code is as follows:

struct drm_mode_config *mode_config = &dev->mode_config;
  • AMD AMDGPU

Assign a value in the amdgpu_hotplug_work_func function of drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c, the code is as follows:

struct drm_mode_config *mode_config = &dev->mode_config;

If you want to follow up and want to know where mode_config is assigned, you need to study the initialization code of the graphics card device, which requires further in-depth follow-up.

What we need to figure out now is the definition of struct drm_framebuffer *fb and its creation (allocation) and initialization. First look at the definition of struct drm_framebuffer, in include/drm/drm_framebuffer.h, the code is as follows:

/**
 * struct drm_framebuffer - frame buffer object
 *
 * Note that the fb is refcounted for the benefit of driver internals,
 * for example some hw, disabling a CRTC/plane is asynchronous, and
 * scanout does not actually complete until the next vblank.  So some
 * cleanup (like releasing the reference(s) on the backing GEM bo(s))
 * should be deferred.  In cases like this, the driver would like to
 * hold a ref to the fb even though it has already been removed from
 * userspace perspective. See drm_framebuffer_get() and
 * drm_framebuffer_put().
 *
 * The refcount is stored inside the mode object @base.
 */
struct drm_framebuffer {
	/**
	 * @dev: DRM device this framebuffer belongs to
	 */
	struct drm_device *dev;
	/**
	 * @head: Place on the &drm_mode_config.fb_list, access protected by
	 * &drm_mode_config.fb_lock.
	 */
	struct list_head head;

	/**
	 * @base: base modeset object structure, contains the reference count.
	 */
	struct drm_mode_object base;

	/**
	 * @comm: Name of the process allocating the fb, used for fb dumping.
	 */
	char comm[TASK_COMM_LEN];

	/**
	 * @format: framebuffer format information
	 */
	const struct drm_format_info *format;
	/**
	 * @funcs: framebuffer vfunc table
	 */
	const struct drm_framebuffer_funcs *funcs;
	/**
	 * @pitches: Line stride per buffer. For userspace created object this
	 * is copied from drm_mode_fb_cmd2.
	 */
	unsigned int pitches[DRM_FORMAT_MAX_PLANES];
	/**
	 * @offsets: Offset from buffer start to the actual pixel data in bytes,
	 * per buffer. For userspace created object this is copied from
	 * drm_mode_fb_cmd2.
	 *
	 * Note that this is a linear offset and does not take into account
	 * tiling or buffer layout per @modifier. It is meant to be used when
	 * the actual pixel data for this framebuffer plane starts at an offset,
	 * e.g. when multiple planes are allocated within the same backing
	 * storage buffer object. For tiled layouts this generally means its
	 * @offsets must at least be tile-size aligned, but hardware often has
	 * stricter requirements.
	 *
	 * This should not be used to specifiy x/y pixel offsets into the buffer
	 * data (even for linear buffers). Specifying an x/y pixel offset is
	 * instead done through the source rectangle in &struct drm_plane_state.
	 */
	unsigned int offsets[DRM_FORMAT_MAX_PLANES];
	/**
	 * @modifier: Data layout modifier. This is used to describe
	 * tiling, or also special layouts (like compression) of auxiliary
	 * buffers. For userspace created object this is copied from
	 * drm_mode_fb_cmd2.
	 */
	uint64_t modifier;
	/**
	 * @width: Logical width of the visible area of the framebuffer, in
	 * pixels.
	 */
	unsigned int width;
	/**
	 * @height: Logical height of the visible area of the framebuffer, in
	 * pixels.
	 */
	unsigned int height;
	/**
	 * @flags: Framebuffer flags like DRM_MODE_FB_INTERLACED or
	 * DRM_MODE_FB_MODIFIERS.
	 */
	int flags;
	/**
	 * @hot_x: X coordinate of the cursor hotspot. Used by the legacy cursor
	 * IOCTL when the driver supports cursor through a DRM_PLANE_TYPE_CURSOR
	 * universal plane.
	 */
	int hot_x;
	/**
	 * @hot_y: Y coordinate of the cursor hotspot. Used by the legacy cursor
	 * IOCTL when the driver supports cursor through a DRM_PLANE_TYPE_CURSOR
	 * universal plane.
	 */
	int hot_y;
	/**
	 * @filp_head: Placed on &drm_file.fbs, protected by &drm_file.fbs_lock.
	 */
	struct list_head filp_head;
	/**
	 * @obj: GEM objects backing the framebuffer, one per plane (optional).
	 *
	 * This is used by the GEM framebuffer helpers, see e.g.
	 * drm_gem_fb_create().
	 */
	struct drm_gem_object *obj[DRM_FORMAT_MAX_PLANES];
};

The more important fields are format, pitches, offsets, width, height, obj, etc. Seeing this structure, the author can't help but feel familiar. Recalling carefully, many members of this structure are the same as the struct drm_mode_fb_cmd2 mentioned above. That is to say, the members in the drm_framebuffer structure are obtained by drm_mode_fb_cmd2, and the relationship between the two is relatively close.

Let's take a look at the upper-level function that calls the drm_framebuffer_init function. As mentioned earlier, different types of graphics cards will call the drm_framebuffer_init function. Here we take the well-understood AMD Radeon as an example to explain.

For Radeon, the drm_framebuffer_init function is called in the radeon_framebuffer_init function of drivers/gpu/drm/radeon/radeon_display.c, the code is as follows:

int
radeon_framebuffer_init(struct drm_device *dev,
			struct drm_framebuffer *fb,
			const struct drm_mode_fb_cmd2 *mode_cmd,
			struct drm_gem_object *obj)
{
	int ret;
	fb->obj[0] = obj;
	drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
	ret = drm_framebuffer_init(dev, fb, &radeon_fb_funcs);
	if (ret) {
		fb->obj[0] = NULL;
		return ret;
	}
	return 0;
}

Chasing up, the radeon_framebuffer_init function is called in the radeon_user_framebuffer_create function below it, 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;
}

The radeon_user_framebuffer_create function and the above call chain will be explained in detail in the next chapter.

Guess you like

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