接前一篇文章:DRM全解析 —— ADD_FB(5)
本文参考以下博文:
DRM驱动(四)之ADD_FB
特此致谢!
上回说到Intel i915、AMD Raedon和AMDGP三类显卡驱动中均调用了drm_framebuffer_init函数。本回就来讲解一下这个函数。
drm_framebuffer_init函数在drivers/gpu/drm/drm_framebuffer.c中,代码如下:
/**
* 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);
drm的组件都是通过dev->mode_config中的链表fb_list进行管理的,只要拿到mode_config就可以拿到drm相关信息。如下图所示:
dev->mode_config也是针对于具体显卡的。仍以Intel i915、AMD Radeon和AMDGPU为例。
- Intel i915
在drivers/gpu/drm/i915/display/intel_display.c的intel_mode_config_init函数中赋值,代码如下:
struct drm_mode_config *mode_config = &i915->drm.mode_config;
- AMD Radeon
在drivers/gpu/drm/radeon/radeon_irq_kms.c的radeon_hotplug_work_func函数中赋值,代码如下:
struct drm_mode_config *mode_config = &dev->mode_config;
- AMD AMDGPU
在drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c的amdgpu_hotplug_work_func函数中赋值,代码如下:
struct drm_mode_config *mode_config = &dev->mode_config;
如果再往上跟,想知道mode_config是在什么地方赋值的,则需要研究显卡设备的初始化代码了,这需要后续更进一步深入。
而现在就要弄明白的是struct drm_framebuffer *fb的定义及其创建(分配)与初始化。先来看一下struct drm_framebuffer的定义,在include/drm/drm_framebuffer.h中,代码如下:
/**
* 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];
};
其中比较重要的字段有format, pitches,offsets,width,height,obj等。看到这个结构,笔者不由感到似曾相识。仔细回想了一下,此结构体中的很多成员与前文讲过的struct drm_mode_fb_cmd2是相同的。也就是说这个drm_framebuffer结构中的成员是由drm_mode_fb_cmd2得到的,二者的联系比较紧密。
再来看一下调用drm_framebuffer_init函数的上一级函数。前文已讲过,不同型号的显卡都会调用drm_framebuffer_init函数。这里以比较好理解的AMD Radeon为例进行讲解。
对于Radeon,drm_framebuffer_init函数是在drivers/gpu/drm/radeon/radeon_display.c的radeon_framebuffer_init函数中被调用的,代码如下:
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;
}
再往上追,radeon_framebuffer_init函数又是在其下的radeon_user_framebuffer_create函数中被调用的,代码如下:
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;
}
对于radeon_user_framebuffer_create函数以及上述调用链,在下一回中进行详细讲解。