上节讲到显示处理器会将一块含有图像数据的memory进行处理后送到下一级。后面几节我会根据龙哥的《最简单的DRM应用程序》一步一步分析用户调用的接口在drm驱动中有怎么样的处理。

DRM驱动的显存由GEM(Graphics execution management)管理。

我会根据我们如何创建一个drm的buf呢,或者你已经知道可以使用

drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create)

可以为显示创建buff,但此函数在内核又做了什么呢?下面一起学习一下内核如何为应用创建显存。

user space

创建显示buf需要三个参数

  • width //图像宽度
  • height //图像高度
  • bpp //每个像素占用bit数

将以上参数放入数据结构struct drm_mode_create_dumb create中,并作为参数调用drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create)后内核返回对应的handle赋值给handle,patch给用户使用;至于为什么会返回handle,后面介绍kernel部分的时候再说。

kernel space

在drm_ioctl.c中搜索DRM_IOCTL_MODE_CREATE_DUMB可知会调用drm_mode_create_dumb_ioctl

而此函数会调用drm_driver中注册的dev->driver->dumb_create(file_priv, dev, args)。

为了方便大家理解,拿出来了imx的部分代码:

此回调函数可以由厂商根据需要自己实现,这里采用默认的创建dump buf函数。

int drm_gem_cma_dumb_create(struct drm_file *file_priv,
        struct drm_device *drm, struct drm_mode_create_dumb *args)
{
	struct drm_gem_cma_object *cma_obj;

	args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
	args->size = args->pitch * args->height;

	cma_obj = drm_gem_cma_create_with_handle(file_priv, drm, args->size, &args->handle);
	return PTR_ERR_OR_ZERO(cma_obj);
}

此函数的目的有两个

  1. 计算patch,返回用户
  2. 创建对象drm_gem_cma_object,因为其中包括分配内存的物理地址和虚拟地址;还包含一个重要的结构drm_gem_object

static struct drm_gem_cma_object *
drm_gem_cma_create_with_handle(struct drm_file *file_priv,
    struct drm_device *drm, size_t size,
   uint32_t *handle)
{
	struct drm_gem_cma_object *cma_obj;
	struct drm_gem_object *gem_obj;
	int ret;

	cma_obj = drm_gem_cma_create(drm, size);

	gem_obj = &cma_obj->base;

	ret = drm_gem_handle_create(file_priv, gem_obj, handle);

	return cma_obj;
}

以上代码删除了错误处理

drm_gem_cma_create有两个作用

  1. 创建drm_gem_cma_object对象并初始化其中的drm_gem_object
  2. 使用dma_alloc_wc分配内存并将物理地址存入paddr,虚拟地址存入vaddr
struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm,
  size_t size)
{
	struct drm_gem_cma_object *cma_obj;

	size = round_up(size, PAGE_SIZE);

	cma_obj = __drm_gem_cma_create(drm, size);
	if (IS_ERR(cma_obj))
		return cma_obj;

	cma_obj->vaddr = dma_alloc_wc(drm->dev, size, &cma_obj->paddr,
				      GFP_KERNEL | __GFP_NOWARN);

	return cma_obj;
}

drm_gem_handle_create主要使用idr_alloc将drm_gem_object对象添加到file_priv->object_idr,并返回handle。

idr_alloc:是为了使用一个id与一个obj绑定。这样就可以通过id找到对应obj。这里将handle与分配的gem_object进行绑定,后面通过handle可以找到gem_object进而找到cma_object获取到物理地址或者虚拟地址

drm_gem_handle_create->drm_gem_handle_create_tail

int drm_gem_handle_create_tail(struct drm_file *file_priv,
                struct drm_gem_object *obj,
   u32 *handlep)
{
	struct drm_device *dev = obj->dev;
	u32 handle;
	int ret;

	idr_preload(GFP_KERNEL);

	ret = idr_alloc(&file_priv->object_idr, obj, 1, 0, GFP_NOWAIT);

	idr_preload_end();

	handle = ret;

	ret = drm_vma_node_allow(&obj->vma_node, file_priv);
	if (ret)
		goto err_remove;

	if (dev->driver->gem_open_object) {
		ret = dev->driver->gem_open_object(obj, file_priv);
		if (ret)
			goto err_revoke;
	}

	*handlep = handle;
	return 0;
}

到这里就完成了显示内存的获取。返回的handle和patch将在后面用到。

我也画了一个流程图,有需要的可以自取https://gitee.com/chaochao-feng/drm_driver.git

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐