Rockchip RK3399 - DRM驱动程序.md

发布时间 2023-09-07 23:53:51作者: 大奥特曼打小怪兽

这里我们介绍一个DRM驱动的案例,具体流程如下:

(1) 定义struct drm_driver,并初始化成员namedescdatamajorminordriver_featuresfopsdumb_create等;

(2)调用drm_dev_alloc函数分配一个struct drm_device

(3) 调用drm_mode_config_init初始化drm_devicemode_config结构体;

(4) 调用drm_xxx_init创建 planecrtcencoderconnector 这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_objectRockchip驱动自定义的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_putGEM对象的引用计数-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对象funcsrockchip_gem_object_funcs
  • 最后调用drm_gem_object_init初始化GEM对象,创建一个大小为sizeshmfs(共享内存文件系统),并将这个shmfs file放到struct drm_gem_objectfilp区。
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-GOPVESA,在后续的引导阶段,它们会将通用驱动程序替换为专用的、针对具体硬件的驱动程序。为了接管该设备,专用驱动程序首先必须移除通用驱动程序。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_devicecomponent_match_arraycomponent的顺序找到component,然后就能调用componentbind函数。

调用drm_xxx_init创建 planecrtcencoderconnector 这4个 drm_mode_object

4.4 注册drm设备

最后调用drm_dev_register注册drm设备。

参考文章

[1] DRM (Direct Rendering Manager)

[2] linux Display driver example