DRM全解析 —— ADD_FB(6)

news/2024/9/16 7:49:09 标签: DRM, Linux内核, libdrm

接前一篇文章: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函数以及上述调用链,在下一回中进行详细讲解。


http://www.niftyadmin.cn/n/5002608.html

相关文章

arduino的Serials使用笔记

arduino的Serials代码笔记 本文目的Serials 本文目的 每次使用arduino都需要去查资料,又懒得记。所以直接一次查好。 Serials Serial.begin(9600);//Serial or Serial1 or Serial2 Serial.end()//与上相反初始化物理端口到软件里的映射。 Serial.available()//等…

数据结构零基础入门篇(C语言实现)

前言:数据结构属于C学习中较难的一部分,对应学习者的要求较高,如基础不扎实,建议着重学习C语言中的指针和结构体,万丈高楼平地起。 一,链表 1)单链表的大致结构实现 用C语言实现链表一般是使…

独立站新手引流,谷歌SEO工具汇总

俗话说“工欲善其事,必先利其器”,做谷歌SEO也一样,要想做好并提升SEO效果,卖家就需要了解并利用好SEO工具。那我们今天就来盘点一下,常用的SEO工具有哪些吧~ 网站检测工具 1、PageSpeed Insights:这是谷…

将conda环境打包成docker步骤

1. 第一步,将conda环境的配置导出到environment.yml 要获取一个Conda环境的配置文件 environment.yml,你可以使用以下命令从已存在的环境中导出: conda env export --name your_env_name > environment.yml请将 your_env_name 替换为你要…

Linux监测进程打开文件

分析问题过程中,追踪进程打开的文件可以在许多不同情况下有用,体现在以下几个方面: 故障排除和调试: 当程序出现问题、崩溃或异常行为时,追踪进程打开的文件可以帮助找出问题的根本原因。这有助于快速定位错误&#x…

java和js实现MD5加密

java import java.security.MessageDigest;public class Demo2 {public static void main(String[] args) {Demo2 demo2 new Demo2();String encry demo2.md5("admin");System.out.println("加密后:" encry);}/*** md5加密*/private static…

手动实现uni-app可用的new URL

使用 import URL from url const url new URL(https://www.aaa.com:8989/bbb/ccc/ddd.html?e1&f2&g#h3&i4&j?k5#l6&e4) console.log(url)结果 {"href": "https://www.aaa.com:8989/bbb/ccc/ddd.html?e1&f2&g#h3&i4&j…

苹果“嘴硬”?下载超出预期,否认开发者对 Vision Pro 兴趣不高

据报道,苹果于上个月在全球多个城市开设了Vision Pro开发者实验室,旨在让开发者尽早体验并研发这款令人期待的头显技术。这一为期一天的实验室活动邀请了一些开发人员前来测试和上手Vision Pro头显,并亲身体验其应用的真实效果。 在活动中&am…