DRM全解析 —— ADD_FB2(1)

接前一篇文章:DRM全解析 —— ADD_FB2(0)

本文参考以下博文:

DRM驱动(四)之ADD_FB

特此致谢!

本文正式开始对于drmModeAddFB2WithModifiers和drmModeAddFB2函数进行深入解析。

drmModeAddFB2函数在xf86drmMode.c中,代码如下:

drm_public 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)
{
	return drmModeAddFB2WithModifiers(fd, width, height,
					  pixel_format, bo_handles,
					  pitches, offsets, NULL,
					  buf_id, flags);
}

drmModeAddFB2WithModifiers函数在同文件中(就在上边),代码如下:

drm_public 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)
{
	struct drm_mode_fb_cmd2 f;
	int ret;

	memclear(f);
	f.width  = width;
	f.height = height;
	f.pixel_format = pixel_format;
	f.flags = flags;
	memcpy(f.handles, bo_handles, 4 * sizeof(bo_handles[0]));
	memcpy(f.pitches, pitches, 4 * sizeof(pitches[0]));
	memcpy(f.offsets, offsets, 4 * sizeof(offsets[0]));
	if (modifier)
		memcpy(f.modifier, modifier, 4 * sizeof(modifier[0]));

	if ((ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB2, &f)))
		return ret;

	*buf_id = f.fb_id;
	return 0;
}

可以看到,drmModeAddFB2函数实际也是调用了drmModeAddFB2WithModifiers函数,只是有些参数限定了而已。

再结合KWin的实际调用代码理解一下:

ret = drmModeAddFB2WithModifiers(buffer->gpu()->fd(), size.width(), size.height(), buffer->format(), handles.data(), strides.data(), offsets.data(), modifier, &framebufferId, DRM_MODE_FB_MODIFIERS);

ret = drmModeAddFB2(buffer->gpu()->fd(), size.width(), size.height(), buffer->format(), handles.data(), strides.data(), offsets.data(), &framebufferId, 0);

struct drm_mode_fb_cmd2的定义在include/drm/drm_mode.h中,代码如下:

struct drm_mode_fb_cmd2 {
	__u32 fb_id;
	__u32 width;
	__u32 height;
	__u32 pixel_format; /* fourcc code from drm_fourcc.h */
	__u32 flags; /* see above flags */

	/*
	 * In case of planar formats, this ioctl allows up to 4
	 * buffer objects with offsets and pitches per plane.
	 * The pitch and offset order is dictated by the fourcc,
	 * e.g. NV12 (https://fourcc.org/yuv.php#NV12) is described as:
	 *
	 *   YUV 4:2:0 image with a plane of 8 bit Y samples
	 *   followed by an interleaved U/V plane containing
	 *   8 bit 2x2 subsampled colour difference samples.
	 *
	 * So it would consist of Y as offsets[0] and UV as
	 * offsets[1].  Note that offsets[0] will generally
	 * be 0 (but this is not required).
	 *
	 * To accommodate tiled, compressed, etc formats, a
	 * modifier can be specified.  The default value of zero
	 * indicates "native" format as specified by the fourcc.
	 * Vendor specific modifier token.  Note that even though
	 * it looks like we have a modifier per-plane, we in fact
	 * do not. The modifier for each plane must be identical.
	 * Thus all combinations of different data layouts for
	 * multi plane formats must be enumerated as separate
	 * modifiers.
	 */
	__u32 handles[4];
	__u32 pitches[4]; /* pitch for each plane */
	__u32 offsets[4]; /* offset of each plane */
	__u64 modifier[4]; /* ie, tiling, compress */
};

drm_mode_fb_cmd2结构中的大段注释意思如下:

在planar格式的情况下,此ioctl允许每个plane(图层)最多有4个具有offset(偏移)和pitch(间距)的buffer object(缓冲区对象)。

pitch和offset的顺序由fourcc决定,例如:NV12(https://fourcc.org/yuv.php#NV12)被描述为:

YUV 4:2:0图像,其具有8位Y样本的图层,随后是包含8位2x2个子采样色差样本的交织U/V图层。因此,它将由Y作为偏移[0]和UV作为偏移[1]组成。请注意,偏移量[0]通常为0(但这不是必需的)。

为了适应平铺、压缩等格式,可以指定一个修饰符。默认值零表示由fourcc指定的“本机”格式。供应商特定的修改器令牌。请注意,尽管看起来每个平面都有一个修改器,但实际上并没有。每个平面的修改器必须相同。因此,多图层格式的不同数据布局的所有组合都必须作为单独的修饰符进行枚举。

对于struct drm_mode_fb_cmd2有所了解之后,来看drmModeAddFB2WithModifiers函数的核心代码:

if ((ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB2, &f)))
    return ret;

DRM_IOCTL是一个内联函数,在xf86drmMode.c中,代码如下:

static inline int DRM_IOCTL(int fd, unsigned long cmd, void *arg)
{
	int ret = drmIoctl(fd, cmd, arg);
	return ret < 0 ? -errno : ret;
}

drmIoctl函数在xf86drm.c中,代码如下:

/**
 * Call ioctl, restarting if it is interrupted
 */
drm_public int
drmIoctl(int fd, unsigned long request, void *arg)
{
    int ret;

    do {
        ret = ioctl(fd, request, arg);
    } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
    return ret;
}

可见,传入的request值就是DRM_IOCTL_MODE_ADDFB2。

对于DRM_IOCTL_MODE_ADDFB2的ioctl系统调用的深入分析,请看下回。

猜你喜欢

转载自blog.csdn.net/phmatthaus/article/details/133353421
今日推荐