drm 驱动是如何创建 fb device 的
什么是 drm?
drm 是一个 Linux 内核的显示系统驱动框架,区别于另外一个 DRM数字版权保护
- drm 是一个管理 GPU 的显示框架
 - 在内核级别提供内存管理,中断处理, DMA控
 - 为应用程序提供统一的操作接口
 
如何使用 drm 接口
- libdrm
 - fb device
 
libdrm
内核提供的 IOCTRL 太多,libdrm 用于简化编程管理当前的显示器,并修改当前的模式成为 KMS ( drm-kms - Kernel Mode-Setting)
借助 libdrm 的强大 API 接口,如果内核支持 PRIME API ,也可以使用 PRIME 接口实现更为灵活的内存操作。
fb device
drm 驱动可以模拟一个 fb device, 默认是 default CRTC, 更多关于 fb device ,可以参考 内核 framebuffer 文档, fb device 是大多数 Linux 系统显示的基础。
- The X Server, Linux 桌面系统的显示服务
 - Android gralloc, 安卓系统显示 HAL
 
本文讨论的问题是 drm 驱动是如何虚拟 drm crts 为 fb device
文章基于内核版本 linux-3.18
VERSION = 3
PATCHLEVEL = 18
SUBLEVEL = 0
EXTRAVERSION = -linux4sam_5.0-alpha7
NAME = Diseased Newt
drm 的代码位于:
drivers/gpu/drm/
1. 设备驱动创建 fbdev
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c: dc->fbdev = drm_fbdev_cma_init(dev, 24,
drivers/gpu/drm/sti/sti_drm_drv.c: drm_fbdev_cma_init(dev, 32,
drivers/gpu/drm/tilcdc/tilcdc_drv.c: priv->fbdev = drm_fbdev_cma_init(dev, bpp,
drivers/gpu/drm/rcar-du/rcar_du_kms.c: fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc,
调用的是 drm_fbdev_cma_init
struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
unsigned int preferred_bpp, unsigned int num_crtc,
unsigned int max_conn_count)
{
struct drm_fbdev_cma *fbdev_cma;
struct drm_fb_helper *helper;
int ret;
fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL);
...
helper = &fbdev_cma->fb_helper;
drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
...
ret = drm_fb_helper_initial_config(helper, preferred_bpp);
    if (ret < 0) {
        dev_err(dev->dev, "Failed to set initial hw configuration.\n");
    goto err_drm_fb_helper_fini;
}
return fbdev_cma;
其中最重要的数据结构是 drm_fb_helper_funcs
static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
.fb_probe = drm_fbdev_cma_create,
};
2. 完成 fb 设备的创建:
static int drm_fbdev_cma_create(struct drm_fb_helper *helper,
    struct drm_fb_helper_surface_size *sizes)
{
    struct drm_fbdev_cma *fbdev_cma = to_fbdev_cma(helper);
    struct drm_mode_fb_cmd2 mode_cmd = { 0 };
    struct drm_device *dev = helper->dev;
    struct drm_gem_cma_object *obj;
    struct drm_framebuffer *fb;
    unsigned int bytes_per_pixel;
    unsigned long offset;
    struct fb_info *fbi;
    size_t size;
    int ret;
    DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
            sizes->surface_width, sizes->surface_height,
            sizes->surface_bpp);
    bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
    mode_cmd.width = sizes->surface_width;
    mode_cmd.height = sizes->surface_height;
    mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
    mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
        sizes->surface_depth);
    size = mode_cmd.pitches[0] * mode_cmd.height;
    obj = drm_gem_cma_create(dev, size);
    if (IS_ERR(obj))
        return -ENOMEM;
    fbi = framebuffer_alloc(0, dev->dev);
    if (!fbi) {
        dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
        ret = -ENOMEM;
        goto err_drm_gem_cma_free_object;
    }
    fbdev_cma->fb = drm_fb_cma_alloc(dev, &mode_cmd, &obj, 1);
    if (IS_ERR(fbdev_cma->fb)) {
        dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
        ret = PTR_ERR(fbdev_cma->fb);
        goto err_framebuffer_release;
    }
    fb = &fbdev_cma->fb->fb;
    helper->fb = fb;
    helper->fbdev = fbi;
    fbi->par = helper;
    fbi->flags = FBINFO_FLAG_DEFAULT;
    fbi->fbops = &drm_fbdev_cma_ops;
    ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
    if (ret) {
        dev_err(dev->dev, "Failed to allocate color map.\n");
        goto err_drm_fb_cma_destroy;
    }
    drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
    drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
    offset = fbi->var.xoffset * bytes_per_pixel;
    offset += fbi->var.yoffset * fb->pitches[0];
    dev->mode_config.fb_base = (resource_size_t)obj->paddr;
    fbi->screen_base = obj->vaddr + offset;
    fbi->fix.smem_start = (unsigned long)(obj->paddr + offset);
    fbi->screen_size = size;
    fbi->fix.smem_len = size;
    return 0;
err_drm_fb_cma_destroy:
    drm_framebuffer_unregister_private(fb);
    drm_fb_cma_destroy(fb);
err_framebuffer_release:
    framebuffer_release(fbi);
err_drm_gem_cma_free_object:
    drm_gem_cma_free_object(&obj->base);
    return ret;
}
流程图
Created with Raphaël 2.1.0 drm_fbdev_cma_init drm_fb_helper_initial_config drm_fb_helper_single_fb_probe register_framebuffer
 
 
 
 
 