这里我们介绍一个DRM
驱动的案例,具体流程如下:
(1) 定义struct drm_driver
,并初始化成员name
、desc
、data
、major
、minor
、driver_features
、fops
、dumb_create
等;
(2)调用drm_dev_alloc
函数分配一个struct drm_device
;
(3) 调用drm_mode_config_init
初始化drm_device
中mode_config
结构体;
(4) 调用drm_xxx_init
创建 plane
、crtc
、encoder
、connector
这4个 drm_mode_object
;
( 5) 调用drm_dev_register
注册drm_device
;
一、rockchip_drm_driver
这里我们以RK3399 DRM
驱动为例进行介绍:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# ll drivers/gpu/drm/rockchip/rockchip_drm*.c
-rw-rw-r-- 1 root root 13006 Jun 13 20:29 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
-rw-rw-r-- 1 root root 2460 Apr 24 03:02 drivers/gpu/drm/rockchip/rockchip_drm_fb.c
-rw-rw-r-- 1 root root 13430 Apr 24 03:02 drivers/gpu/drm/rockchip/rockchip_drm_gem.c
-rw-rw-r-- 1 root root 80149 Apr 24 03:02 drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
-rw-rw-r-- 1 root root 60878 Apr 24 03:02 drivers/gpu/drm/rockchip/rockchip_drm_vop.c
其中:
rockchip_drm_drv.c
:实现了DRM
驱动;rockchip_drm_fb.c
:实现了framebuffer
驱动;rockchip_drm_gem.c
:实现了gem
驱动, 主要负责显示buffer
的分配和释放;
下面我们以源码rockchip_drm_drv.c
中的rockchip_drm_driver
全局变量作为切入点进行介绍;
static const struct drm_driver rockchip_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
.dumb_create = rockchip_gem_dumb_create,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table,
.gem_prime_mmap = drm_gem_prime_mmap,
.fops = &rockchip_drm_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
};
1.1 driver_features
- 添加上
DRIVER_GEM
标志位,告诉DRM Core
该驱动支持GEM
操作; - 添加上
DRIVER_MODESET
标志位,告诉DRM Core
该驱动支持kernel Mode Setting
操作; - 添加上
DRIVER_ATOMIC
标志位,告诉DRM Core
该驱动支持Atomic
操作。
1.2 宏变量
#define DRIVER_NAME "rockchip"
#define DRIVER_DESC "RockChip Soc DRM"
#define DRIVER_DATE "20140818"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
二、dumb_create
其中dumb_create
配置为rockchip_gem_dumb_create
,该函数用于分配物理内存dumb buffer
,函数定义在drivers/gpu/drm/rockchip/rockchip_drm_gem.c
:
/*
* rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
* function
*
* This aligns the pitch and size arguments to the minimum required. wrap
* this into your own function if you need bigger alignment.
*/
int rockchip_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
struct rockchip_gem_object *rk_obj;
int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); // 宽度*每像素位数/8
/*
* align to 64 bytes since Mali requires it.
*/
args->pitch = ALIGN(min_pitch, 64);
args->size = args->pitch * args->height; // 宽*高*每像素位数/8,即得到总大小(单位字节)
rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
&args->handle);
return PTR_ERR_OR_ZERO(rk_obj);
}
这里主要实现函数是rockchip_gem_create_with_handle
,定义如下;
/*
* rockchip_gem_create_with_handle - allocate an object with the given
* size and create a gem handle on it
*
* returns a struct rockchip_gem_object* on success or ERR_PTR values
* on failure.
*/
static struct rockchip_gem_object *
rockchip_gem_create_with_handle(struct drm_file *file_priv,
struct drm_device *drm, unsigned int size,
unsigned int *handle)
{
struct rockchip_gem_object *rk_obj;
struct drm_gem_object *obj;
bool is_framebuffer;
int ret;
is_framebuffer = drm->fb_helper && file_priv == drm->fb_helper->client.file;
rk_obj = rockchip_gem_create_object(drm, size, is_framebuffer);
if (IS_ERR(rk_obj))
return ERR_CAST(rk_obj);
obj = &rk_obj->base; // 获取GEM对象
/*
* allocate a id of idr table where the obj is registered
* and handle has the id what user can see.
*/
ret = drm_gem_handle_create(file_priv, obj, handle);
if (ret)
goto err_handle_create;
/* drop reference from allocate - handle holds it now. */
drm_gem_object_put(obj);
return rk_obj;
err_handle_create:
rockchip_gem_free_object(obj);
return ERR_PTR(ret);
}
函数执行流程如下:
(1) 调用rockchip_gem_create_object
创建并初始化Rockchip
驱动自定义的GEM
对象struct rockchip_gem_objec
;
数据结构rockchip_gem_object
是Rockchip
驱动自定义的GEM
对象,内部包含 struct drm_gem_object
,定义在drivers/gpu/drm/rockchip/rockchip_drm_gem.h
:
struct rockchip_gem_object { // Rockchip私有的GEM对象
struct drm_gem_object base; // GEM对象
unsigned int flags;
void *kvaddr;
dma_addr_t dma_addr; // dma地址
/* Used when IOMMU is disabled */
unsigned long dma_attrs; // dma buffer虚拟地址
/* Used when IOMMU is enabled */
struct drm_mm_node mm;
unsigned long num_pages;
struct page **pages;
struct sg_table *sgt;
size_t size;
};
(2) 调用drm_gem_handle_create
创建GEM
对象handle
;
(3) 调用drm_gem_object_put
将GEM
对象的引用计数-1;
rockchip_gem_create_with_handle
函数调用栈:
rockchip_gem_create_with_handle(file_priv, dev, args->size,&args->handle)
// #1 创建并初始化Rockchip驱动自定义的GEM对象
rockchip_gem_create_object(drm, size, is_framebuffer)
// #1.1 创建Rockchip驱动自定义的GEM对象
rockchip_gem_alloc_object(drm, size)
// GEM对象初始化
drm_gem_object_init(drm, obj, size)
// #1.2 为Rockchip GEM对象分配DMA缓冲区,或者在IOMMU上分配内存
rockchip_gem_alloc_buf(rk_obj, alloc_kmap)
rockchip_gem_alloc_dma(rk_obj, alloc_kmap) //
// #2 创建GEM对象handle
drm_gem_handle_create(file_priv, obj, handle)
// 3# 引用计数-1
drm_gem_object_put(obj)
2.1 rockchip_gem_create_object
rockchip_gem_create_object
函数定义在drivers/gpu/drm/rockchip/rockchip_drm_gem.c
:
struct rockchip_gem_object *
rockchip_gem_create_object(struct drm_device *drm, unsigned int size,
bool alloc_kmap)
{
struct rockchip_gem_object *rk_obj;
int ret;
// 创建Rockchip驱动自定义的GEM对象
rk_obj = rockchip_gem_alloc_object(drm, size);
if (IS_ERR(rk_obj))
return rk_obj;
// 为Rockchip GEM对象分配DMA缓冲区,或者在IOMMU上分配内存
ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap);
if (ret)
goto err_free_rk_obj;
return rk_obj;
err_free_rk_obj:
rockchip_gem_release_object(rk_obj);
return ERR_PTR(ret);
}
2.1.2 rockchip_gem_alloc_object
rockchip_gem_alloc_object
用于创建Rockchip
驱动自定义的GEM
对象;
static const struct drm_gem_object_funcs rockchip_gem_object_funcs = {
.free = rockchip_gem_free_object,
.get_sg_table = rockchip_gem_prime_get_sg_table,
.vmap = rockchip_gem_prime_vmap,
.vunmap = rockchip_gem_prime_vunmap,
.mmap = rockchip_drm_gem_object_mmap,
.vm_ops = &drm_gem_dma_vm_ops,
};
static struct rockchip_gem_object *
rockchip_gem_alloc_object(struct drm_device *drm, unsigned int size)
{
struct rockchip_gem_object *rk_obj;
struct drm_gem_object *obj;
size = round_up(size, PAGE_SIZE);
rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
if (!rk_obj)
return ERR_PTR(-ENOMEM);
obj = &rk_obj->base;
obj->funcs = &rockchip_gem_object_funcs;
drm_gem_object_init(drm, obj, size);
return rk_obj;
}
流程如此:
- 调用
kzalloc
动态分配Rockchip
驱动自定义的GEM
对象struct rockchip_gem_object
; - 然后设定
GEM
对象funcs
为rockchip_gem_object_funcs
; - 最后调用
drm_gem_object_init
初始化GEM
对象,创建一个大小为size
的shmfs
(共享内存文件系统),并将这个shmfs file
放到struct drm_gem_object
的filp
区。
2.1.2 rockchip_gem_alloc_buf
rockchip_gem_alloc_buf
函数用于为Rockchip GEM
对象分配DMA
缓冲区,或者在IOMMU
上分配内存;
static int rockchip_gem_alloc_iommu(struct rockchip_gem_object *rk_obj,
bool alloc_kmap)
{
int ret;
ret = rockchip_gem_get_pages(rk_obj); // 获取页面
if (ret < 0)
return ret;
ret = rockchip_gem_iommu_map(rk_obj); // 将GEM对象映射到IOMMU
if (ret < 0)
goto err_free;
if (alloc_kmap) {
// 将获取到的页面映射到内核虚拟地址空间中
rk_obj->kvaddr = vmap(rk_obj->pages, rk_obj->num_pages, VM_MAP,
pgprot_writecombine(PAGE_KERNEL));
if (!rk_obj->kvaddr) {
DRM_ERROR("failed to vmap() buffer\n");
ret = -ENOMEM;
goto err_unmap;
}
}
return 0;
err_unmap:
// 解除IOMMU映射
rockchip_gem_iommu_unmap(rk_obj);
err_free:
// 释放页面
rockchip_gem_put_pages(rk_obj);
return ret;
}
static int rockchip_gem_alloc_dma(struct rockchip_gem_object *rk_obj,
bool alloc_kmap)
{
struct drm_gem_object *obj = &rk_obj->base;
struct drm_device *drm = obj->dev;
rk_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE;
if (!alloc_kmap)
rk_obj->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
// 分配DMA缓冲区。分配的大小为obj->size字节,属性为rk_obj->dma_attrs
rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
&rk_obj->dma_addr, GFP_KERNEL,
rk_obj->dma_attrs);
if (!rk_obj->kvaddr) {
DRM_ERROR("failed to allocate %zu byte dma buffer", obj->size);
return -ENOMEM;
}
return 0;
}
static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
bool alloc_kmap)
{
struct drm_gem_object *obj = &rk_obj->base;
struct drm_device *drm = obj->dev;
struct rockchip_drm_private *private = drm->dev_private; // 获取drm驱动私有数据
if (private->domain)
// 为Rockchip GEM对象在IOMMU上分配内存
return rockchip_gem_alloc_iommu(rk_obj, alloc_kmap);
else
// 为Rockchip GEM对象分配DMA缓冲区
return rockchip_gem_alloc_dma(rk_obj, alloc_kmap);
}
三、DRM设备节点文件操作集
其中DRM
字符设备节点文件操作集fops
设置为rockchip_drm_driver_fops
,内容如下:
DEFINE_DRM_GEM_FOPS(rockchip_drm_driver_fops);
DEFINE_DRM_GEM_FOPS
宏定义在include/drm/drm_gem.h
:
/**
* DRM_GEM_FOPS - Default drm GEM file operations
*
* This macro provides a shorthand for setting the GEM file ops in the
* &file_operations structure. If all you need are the default ops, use
* DEFINE_DRM_GEM_FOPS instead.
*/
#define DRM_GEM_FOPS \
.open = drm_open,\
.release = drm_release,\
.unlocked_ioctl = drm_ioctl,\
.compat_ioctl = drm_compat_ioctl,\
.poll = drm_poll,\
.read = drm_read,\
.llseek = noop_llseek,\
.mmap = drm_gem_mmap
/**
* DEFINE_DRM_GEM_FOPS() - macro to generate file operations for GEM drivers
* @name: name for the generated structure
*
* This macro autogenerates a suitable &struct file_operations for GEM based
* drivers, which can be assigned to &drm_driver.fops. Note that this structure
* cannot be shared between drivers, because it contains a reference to the
* current module using THIS_MODULE.
*
* Note that the declaration is already marked as static - if you need a
* non-static version of this you're probably doing it wrong and will break the
* THIS_MODULE reference by accident.
*/
#define DEFINE_DRM_GEM_FOPS(name) \
static const struct file_operations name = {\
.owner = THIS_MODULE,\
DRM_GEM_FOPS,\
}
其中nmap
用于将物理内存dumb buffer
映射到用户空间,让应用程序可以直接访问物理内存。
drm_gem_mmap
函数定义在drivers/gpu/drm/drm_gem.c
,内容如下:
/**
* drm_gem_mmap - memory map routine for GEM objects
* @filp: DRM file pointer
* @vma: VMA for the area to be mapped
*
* If a driver supports GEM object mapping, mmap calls on the DRM file
* descriptor will end up here.
*
* Look up the GEM object based on the offset passed in (vma->vm_pgoff will
* contain the fake offset we created when the GTT map ioctl was called on
* the object) and map it with a call to drm_gem_mmap_obj().
*
* If the caller is not granted access to the buffer object, the mmap will fail
* with EACCES. Please see the vma manager for more information.
*/
int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->minor->dev;
struct drm_gem_object *obj = NULL;
struct drm_vma_offset_node *node;
int ret;
// 检查设备是否已被拔出
if (drm_dev_is_unplugged(dev))
return -ENODEV;
drm_vma_offset_lock_lookup(dev->vma_offset_manager);
node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager,
vma->vm_pgoff,
vma_pages(vma));
if (likely(node)) {
obj = container_of(node, struct drm_gem_object, vma_node);
/*
* When the object is being freed, after it hits 0-refcnt it
* proceeds to tear down the object. In the process it will
* attempt to remove the VMA offset and so acquire this
* mgr->vm_lock. Therefore if we find an object with a 0-refcnt
* that matches our range, we know it is in the process of being
* destroyed and will be freed as soon as we release the lock -
* so we have to check for the 0-refcnted object and treat it as
* invalid.
*/
if (!kref_get_unless_zero(&obj->refcount))
obj = NULL;
}
drm_vma_offset_unlock_lookup(dev->vma_offset_manager);
if (!obj)
return -EINVAL;
if (!drm_vma_node_is_allowed(node, priv)) {
drm_gem_object_put(obj);
return -EACCES;
}
ret = drm_gem_mmap_obj(obj, drm_vma_node_size(node) << PAGE_SHIFT,
vma);
drm_gem_object_put(obj);
return ret;
}
四、rockchip_drm_bind
我们定位到drivers/gpu/drm/rockchip/rockchip_drm_drv.c
文件的函数rockchip_drm_bind
,该函数用于绑定设备并初始化DRM
驱动;
static int rockchip_drm_bind(struct device *dev)
{
struct drm_device *drm_dev;
struct rockchip_drm_private *private;
int ret;
/* 1. Remove existing drivers that may own the framebuffer memory. */
ret = drm_aperture_remove_framebuffers(false, &rockchip_drm_driver);
if (ret) {
DRM_DEV_ERROR(dev,
"Failed to remove existing framebuffers - %d.\n",
ret);
return ret;
}
// 2. 动态分配并初始化struct drm_device实例
drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
if (IS_ERR(drm_dev))
return PTR_ERR(drm_dev);
// 3. 设置drm设备驱动私有数据为drm设备
dev_set_drvdata(dev, drm_dev);
// 4. 动态分配rockchip_drm_private
private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
if (!private) {
ret = -ENOMEM;
goto err_free;
}
drm_dev->dev_private = private;
// 5. 初始化drm_device中mode_config结构体
ret = drmm_mode_config_init(drm_dev);
if (ret)
goto err_free;
// 初始化drm_device中mode_config的中成员
rockchip_drm_mode_config_init(drm_dev);
/* 6. Try to bind all sub drivers. 执行按顺序执行显示子系统的各个组件的bind函数 */
ret = component_bind_all(dev, drm_dev);
if (ret)
goto err_free;
// 7. 初始化IOMMU
ret = rockchip_drm_init_iommu(drm_dev);
if (ret)
goto err_unbind_all;
// 8.初始化vblank
ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
if (ret)
goto err_iommu_cleanup;
// 9. 重置模式配置
drm_mode_config_reset(drm_dev);
/* 10.init kms poll for handling hpd */
drm_kms_helper_poll_init(drm_dev);
// 11.注册drm设备
ret = drm_dev_register(drm_dev, 0);
if (ret)
goto err_kms_helper_poll_fini;
// 12.设置帧缓冲设备
drm_fbdev_generic_setup(drm_dev, 0);
return 0;
err_kms_helper_poll_fini:
drm_kms_helper_poll_fini(drm_dev);
err_iommu_cleanup:
rockchip_iommu_cleanup(drm_dev);
err_unbind_all:
component_unbind_all(dev, drm_dev);
err_free:
drm_dev_put(drm_dev);
return ret;
}
函数主要步骤如下:
(1) 首先,通过调用drm_aperture_remove_framebuffers
函数,移除可能拥有framebuffer memory
的现有驱动程序;
为什么要执行这个操作呢?对于一个图形设备来说,可能有多个驱动程序提供支持,但在任何给定的事件只能有一个驱动程序处于活动状态。许多系统在引导过程的早期加载通用图形驱动程序,例如EFI-GOP
或VESA
,在后续的引导阶段,它们会将通用驱动程序替换为专用的、针对具体硬件的驱动程序。为了接管该设备,专用驱动程序首先必须移除通用驱动程序。DRM aperture
函数负责管理DRM framebuffer
内存的所有权和驱动程序之间的交接;
更多内容可以参考:Managing Ownership of the Framebuffer Aperture。
(2) 接下来,使用drm_dev_alloc
函数为设备动态分配一个struct drm_device
结构体,并将其保存在drm_dev
指针中;
drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
(3) 然后,使用dev_set_drvdata
函数将设备的驱动私有数据设置为drm_dev
;
dev_set_drvdata(dev, drm_dev);
(4) 使用devm_kzalloc
函数为drm_dev
动态分配一个rockchip_drm_private
结构体,并将其保存在drm_dev->dev_private
指针中;
private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL)
(5) 调用drmm_mode_config_init
函数和rockchip_drm_mode_config_init
函数对drm_dev
进行模式配置的初始化;
(6) 调用component_bind_all
函数,执行按顺序执行显示子系统的各个组件的bind
函数;
(7) 调用rockchip_drm_init_iommu
函数初始化IOMMU
;
(8) 调用drm_vblank_init
函数初始化vblank
;
(9) 调用drm_mode_config_reset
函数重置模式配置,
(10) 调用drm_kms_helper_poll_init
函数初始化KMS
轮询以处理HPD(Hot Plug Detect)
;
(11) 在所有准备工作都完成后,调用drm_dev_register
函数注册DRM
设备;
(12) 调用drm_fbdev_generic_setup
函数设置帧缓冲设备;
4.1 初始化drm
设备
一个drm
驱动的设备由struct drm_device
来表示,这里调用drm_dev_alloc
进行分配和初始化一个struct drm_device
实例,drm_dev_alloc
函数定义在drivers/gpu/drm/drm_drv.c
;
/**
* drm_dev_alloc - Allocate new DRM device
* @driver: DRM driver to allocate device for
* @parent: Parent device object
*
* This is the deprecated version of devm_drm_dev_alloc(), which does not support
* subclassing through embedding the struct &drm_device in a driver private
* structure, and which does not support automatic cleanup through devres.
*
* RETURNS:
* Pointer to new DRM device, or ERR_PTR on failure.
*/
struct drm_device *drm_dev_alloc(const struct drm_driver *driver,
struct device *parent)
{
struct drm_device *dev;
int ret;
// 动态分配一个struct drm_device
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
ret = drm_dev_init(dev, driver, parent);
if (ret) {
kfree(dev);
return ERR_PTR(ret);
}
drmm_add_final_kfree(dev, dev);
return dev;
}
drm_dev_alloc
函数时一个已废弃版本,它不支持在驱动程序的私有结构中嵌入struct drm_device
来实现子类化,并且不支持通过devres
进行自动清理。
drm_dev_alloc
函数内部调用drm_dev_init
去初始化struct drm_device
实例。
4.2 模式配置初始化
下面我们分析一下drmm_mode_config_init
函数和rockchip_drm_mode_config_init
函数;
// 调用drm_mode_config_init初始化drm_device中mode_config结构体
ret = drmm_mode_config_init(drm_dev);
if (ret)
goto err_free;
// 填充mode_config中min_width, min_height、max_width, max_height的值,这些值是framebuffer的大小限制
// 设置mode_config->funcs指针,本质是一组由驱动实现的回调函数
rockchip_drm_mode_config_init(drm_dev);
rockchip_drm_mode_config_init
函数定义在drivers/gpu/drm/rockchip/rockchip_drm_fb.c
,内容如下:
void rockchip_drm_mode_config_init(struct drm_device *dev)
{
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
/*
* set max width and height as default value(4096x4096).
* this value would be used to check framebuffer size limitation
* at drm_mode_addfb().
*/
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
dev->mode_config.helper_private = &rockchip_mode_config_helpers;
dev->mode_config.normalize_zpos = true;
}
(1) drm
显示器模式设置mode_config
回调函数funcs
被设置为rockchip_drm_mode_config_funcs
;
static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
.fb_create = rockchip_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
fb_create
回调接口用于创建framebuffer object
,并绑定GEM
对象。rockchip_fb_create
定义如下;
static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
.destroy = drm_gem_fb_destroy,
.create_handle = drm_gem_fb_create_handle,
.dirty = drm_atomic_helper_dirtyfb,
};
static struct drm_framebuffer *
rockchip_fb_create(struct drm_device *dev, struct drm_file *file,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
struct drm_afbc_framebuffer *afbc_fb;
const struct drm_format_info *info;
int ret;
// 获取drm格式
info = drm_get_format_info(dev, mode_cmd);
if (!info)
return ERR_PTR(-ENOMEM);
afbc_fb = kzalloc(sizeof(*afbc_fb), GFP_KERNEL);
if (!afbc_fb)
return ERR_PTR(-ENOMEM);
ret = drm_gem_fb_init_with_funcs(dev, &afbc_fb->base, file, mode_cmd,
&rockchip_drm_fb_funcs);
if (ret) {
kfree(afbc_fb);
return ERR_PTR(ret);
}
if (drm_is_afbc(mode_cmd->modifier[0])) {
int ret, i;
ret = drm_gem_fb_afbc_init(dev, mode_cmd, afbc_fb);
if (ret) {
struct drm_gem_object **obj = afbc_fb->base.obj;
for (i = 0; i < info->num_planes; ++i)
drm_gem_object_put(obj[i]);
kfree(afbc_fb);
return ERR_PTR(ret);
}
}
return &afbc_fb->base;
}
(2) drm
显示器模式设置mode_config
中间层私有数据helper_private
被设置为了rockchip_mode_config_helpers
;
static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = {
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
};
4.3 component_bind_all
在component_bind_all
函数我们已经在Rockchip RK3399 - component
框架](https://www.cnblogs.com/zyly/p/17678424.html#_label2_4)详细介绍了;
/**
* component_bind_all - bind all components of an aggregate driver
* @parent: parent device of the aggregate driver
* @data: opaque pointer, passed to all components
*
* Binds all components of the aggregate @dev by passing @data to their
* &component_ops.bind functions. Should be called from
* &component_master_ops.bind.
*/
int component_bind_all(struct device *parent, void *data)
{
struct aggregate_device *adev;
struct component *c;
size_t i;
int ret = 0;
WARN_ON(!mutex_is_locked(&component_mutex));
adev = __aggregate_find(parent, NULL);
if (!adev)
return -EINVAL;
/* Bind components in match order */
for (i = 0; i < adev->match->num; i++)
// duplicate为false时进入
if (!adev->match->compare[i].duplicate) {
c = adev->match->compare[i].component;
// 执行component的bind函数
ret = component_bind(c, adev, data);
if (ret)
break;
}
if (ret != 0) { // 正常不会走这里
for (; i > 0; i--)
if (!adev->match->compare[i - 1].duplicate) {
c = adev->match->compare[i - 1].component;
component_unbind(c, adev, data);
}
}
return ret;
}
首先通过设备parent
找到与之关联的的aggregate_device
,再按照aggregate_device
→component_match_array
→component
的顺序找到component
,然后就能调用component
的bind
函数。
调用drm_xxx_init
创建 plane
、crtc
、encoder
、connector
这4个 drm_mode_object
;
4.4 注册drm
设备
最后调用drm_dev_register
注册drm
设备。
参考文章
[1] DRM (Direct Rendering Manager
)
[2] linux Display driver example