DRM驱动(四)之ADD_FB
gem obj创建之后需要与一块drm_framebuffer进行绑定使用,因为最终刷图使用的是fb。使用接口drmModeAddFB会创建drm_framebuffer并与上节创建的gem obj进行绑定。本文阐述了内核创建drm_framebuffer的整个过程。
书接上回,使用drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create)接口创建了一块内存放在了gem cma的对象里。
gem obj创建之后需要与一块drm_framebuffer进行绑定使用,因为最终刷图使用的是fb。使用下面接口会创建drm_framebuffer并与上节创建的gem obj进行绑定。
int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth,uint8_t bpp, uint32_t pitch, uint32_t bo_handle,uint32_t *buf_id) //老接口
目前用的比较多的是下面两个
int drmModeAddFB2(int fd, uint32_t width, uint32_t height, uint32_t pixel_format, const uint32_t bo_handles[4], const uint32_t pitches[4], const uint32_t offsets[4], uint32_t *buf_id, uint32_t flags)
int drmModeAddFB2WithModifiers(int fd, uint32_t width, uint32_t height, uint32_t pixel_format, const uint32_t bo_handles[4], const uint32_t pitches[4], const uint32_t offsets[4], const uint64_t modifier[4], uint32_t *buf_id, uint32_t flags)
重点介绍drmModeAddFB,因为drmModeAddFB会调用add_fb2
上面接口完成了如下的功能
user space的接口就不多说,比较简单,重点看下kernel中的实现
kernel:
后面的介绍全是围绕drm_framebuffer,先看下drm_framebuffer是何方神圣
struct drm_framebuffer {
struct drm_device *dev;
struct list_head head;
struct drm_mode_object base;
const struct drm_format_info *format;
const struct drm_framebuffer_funcs *funcs;
unsigned int pitches[4];
unsigned int offsets[4];
uint64_t modifier;
unsigned int width;
unsigned int height;
int flags;
int hot_x;
int hot_y;
struct list_head filp_head;
struct drm_gem_object *obj[4];
};
其中比较重要的字段format, pitches,offsets,width,height,obj等。
通过应用层ioctl会调到drm的drm_mode_addfb
int drm_mode_addfb(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
struct drm_mode_fb_cmd *or = data;
struct drm_mode_fb_cmd2 r = {};
int ret;
/* convert to new format and call new ioctl */
r.fb_id = or->fb_id;
r.width = or->width;
r.height = or->height;
r.pitches[0] = or->pitch;
r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
r.handles[0] = or->handle;
if (r.pixel_format == DRM_FORMAT_XRGB2101010 &&
dev->driver->driver_features & DRIVER_PREFER_XBGR_30BPP)
r.pixel_format = DRM_FORMAT_XBGR2101010;
ret = drm_mode_addfb2(dev, &r, file_priv);
if (ret)
return ret;
or->fb_id = r.fb_id;
return 0;
}
可以看到bpp和depth的作用是找到对应的format。
大致看下drm_mode_legacy_fb_format的实现,不过多介绍,因为大多已经不使用drm_mode_addfb,而是使用drm_mode_addfb2。
uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
{
uint32_t fmt;
switch (bpp) {
case 8:
fmt = DRM_FORMAT_C8;
break;
case 16:
if (depth == 15)
fmt = DRM_FORMAT_XRGB1555;
else
fmt = DRM_FORMAT_RGB565;
break;
case 24:
fmt = DRM_FORMAT_RGB888;
break;
case 32:
if (depth == 24)
fmt = DRM_FORMAT_XRGB8888;
else if (depth == 30)
fmt = DRM_FORMAT_XRGB2101010;
else
fmt = DRM_FORMAT_ARGB8888;
break;
default:
DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n");
fmt = DRM_FORMAT_XRGB8888;
break;
}
return fmt;
}
drm_mode_addfb会调用drm_mode_addfb2。从代码可以看到addfb2里检查了一下权限,就直接调用的drm_internal_framebuffer_create。(下面代码有进行删减)
int drm_mode_addfb2(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
struct drm_mode_fb_cmd2 *r = data;
struct drm_framebuffer *fb;
fb = drm_internal_framebuffer_create(dev, r, file_priv);
r->fb_id = fb->base.id;
list_add(&fb->filp_head, &file_priv->fbs);
}
drm_internal_framebuffer_create检查传入参数是否合法,然后调用
dev->mode_config.funcs->fb_create(dev, file_priv, r)
此回调是在drm驱动注册时初始化的,以imx的为例使用的drm提供的创建函数;此函数可以自己实现,而很多芯片厂商都是根据自己需求实现。我们学习,就简单分析下drm提供默认创建fb的函数
static const struct drm_mode_config_funcs mxsfb_mode_config_funcs = {
.fb_create = drm_fb_cma_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
而drm_fb_cma_create->drm_gem_fb_create->drm_gem_fb_create_with_funcs
drm_gem_fb_create_with_funcs通过应用提供的handle,找到上次创建的gem obj。然后调用drm_gem_fb_alloc,分配一个drm_framebuffer,将gem obj赋值给framebuffer中的obj。
struct drm_framebuffer *
drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
const struct drm_mode_fb_cmd2 *mode_cmd,
const struct drm_framebuffer_funcs *funcs)
{
const struct drm_format_info *info;
struct drm_gem_object *objs[4];
struct drm_framebuffer *fb;
int ret, i;
for (i = 0; i < info->num_planes; i++) {
unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
unsigned int min_size;
objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]);
min_size = (height - 1) * mode_cmd->pitches[i]
+ width * info->cpp[i]
+ mode_cmd->offsets[i];
if (objs[i]->size < min_size) {
drm_gem_object_put_unlocked(objs[i]);
ret = -EINVAL;
goto err_gem_object_put;
}
}
fb = drm_gem_fb_alloc(dev, mode_cmd, objs, i, funcs);
return fb;
}
static struct drm_framebuffer *
drm_gem_fb_alloc(struct drm_device *dev,
const struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object **obj, unsigned int num_planes,
const struct drm_framebuffer_funcs *funcs)
{
struct drm_framebuffer *fb;
int ret, i;
fb = kzalloc(sizeof(*fb), GFP_KERNEL);
drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
for (i = 0; i < num_planes; i++)
fb->obj[i] = obj[i];
ret = drm_framebuffer_init(dev, fb, funcs);
return fb;
}
将应用传入的长,宽,format,pitch,flag等赋值给drm_framebuffer。
void drm_helper_mode_fill_fb_struct(struct drm_device *dev,
struct drm_framebuffer *fb,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
int i;
fb->dev = dev;
fb->format = drm_get_format_info(dev, mode_cmd);
fb->width = mode_cmd->width;
fb->height = mode_cmd->height;
for (i = 0; i < 4; i++) {
fb->pitches[i] = mode_cmd->pitches[i];
fb->offsets[i] = mode_cmd->offsets[i];
}
fb->modifier = mode_cmd->modifier[0];
fb->flags = mode_cmd->flags;
}
相关字段填充完成后会将fb添加进dev->mode_config.fb_list便于后面使用的时候通过id进行查找,
int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
const struct drm_framebuffer_funcs *funcs)
{
int ret;
INIT_LIST_HEAD(&fb->filp_head);
fb->funcs = funcs;
ret = __drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB,
false, drm_framebuffer_free);
dev->mode_config.num_fb++;
list_add(&fb->head, &dev->mode_config.fb_list);
drm_mode_object_register(dev, &fb->base);
return ret;
}
drm的组件都是通过dev->mode_config中的链表进行管理的,只要拿到mode_config就可以拿到drm相关信息
- struct list_head fb_list
至此drm_framebuffer已经创建完毕。通过r->fb_id = fb->base.id;将fb_id返回给用户。
大致流程:
总结:
整个流程就是创建一个drm_framebuffer并根据应用提供的参数进行填充对应字段(为了后面驱动根据对应字段配置显示的寄存器)。然后添加进mode_config的fb_list,返回给fb_id给用户。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)