libdrm-modetest原理及图显系统验证方法

2021-07-30 16:03:51 浏览数 (1)

linux平台普遍采用的DRM软件架构中,不仅包含了内核空间驱动层的代码,而且提供应用层的支撑库libdrm。libdrm基于DRI协议通过ioctl与2D图显驱动进行交互,配置图显处理器以及HDMI、MIPI、LVDS等编解码单元。

from rockchip

验证SoC的图显处理器及其他编解码模块时,可以基于libdrm modetest所提供的功能来丰富我们的verify条目。如单帧、多帧、旋转、缩放、裁剪等等。

modetest功能及流程

1

解析命令行参数

通过库函数getopt()处理modetest的命令行参数(图片可放大查看)

支持的命令行参数主要包括三类:

1) 查询类

2) 测试类

3) 通用选项

与解析命令函参数有关的三个API:

代码语言:javascript复制
static int parse_connector(struct pipe_arg *pipe, const char *arg)
static int parse_plane(struct plane_arg *plane, const char *p)
static int parse_property(struct property_arg *p, const char *arg)
static void parse_fill_patterns(char *arg)

打开DRM设备

打开DRM设备的流程如下:

modetest只能打开static const char * const modules[]内定义的DRM驱动,默认支持的DRM驱动包括:

代码语言:javascript复制
static const char * const modules[] = {
    "i915",
    "amdgpu",
    "radeon",
    "nouveau",
    "vmwgfx",
    "omapdrm",
    "exynos",
    "tilcdc",
    "msm",
    "sti",
    "tegra",
    "imx-drm",
    "rockchip",
    "atmel-hlcdc",
    "fsl-dcu-drm",
    "vc4",
    "virtio_gpu",
    "mediatek",
    "meson",
    "pl111",
    "stm",
    "sun4i-drm",
    "armada-drm",
};

当我们自己的图显驱动需要使用modetest进行验证的时候,需要在这里增加驱动名字。DRM驱动的名字定义在kernel driver的drm_driver数据结构中。

代码语言:javascript复制
struct drm_driver {
...
    /** @major: driver major number */
    int major;
    /** @minor: driver minor number */
    int minor;
    /** @patchlevel: driver patch level */
    int patchlevel;
    /** @name: driver name */
    char *name;
    /** @desc: driver description */
    char *desc;
    /** @date: driver date */
    char *date;
...
};

在打开设备的过程中,若通过-M参数指定了DRM驱动名,那么打开特定驱动;若未指定DRM驱动名,那么遍历modules[]中指定的DRM驱动。

另外,若没有指定-D参数(没有指定设备名),默认按照DRM驱动名打开DRM设备。这里面的-D参数是/dev/drixxx编号。例如-D 0,指定打开0号DRM设备。若指定了-D参数,那么首先按照-D指定设备编号来打开DRM设备。

获取设备资源

代码语言:javascript复制
static struct resources *get_resources(struct device *dev)
{
…
    drmModeGetCrtc();
    drmModeGetEncoder();
    drmModeGetConnector ();
    drmModeGetFB();
    drmModeGetPlane();
…
    drmModeObjectGetProperties(,,CRTC);
drmModeObjectGetProperties(,,CONNECTOR);
…
}

dump_resource(&dev, encoders);
dump_resource(&dev, connectors);
dump_resource(&dev, crtcs);
dump_resource(&dev, planes);
dump_resource(&dev, framebuffers);

配置property

若通过-w参数指定了property设置,那么执行

代码语言:javascript复制
for (i = 0; i < prop_count;   i)
        set_property(&dev, &prop_args[i]);

DRM配置

DRM的配置根据-a参数决定是否采用原子操作,当使用原子操作时原子更新属性,然后利用drmModeAtomicCommit()统一提交配置信息;否则,依次调用各个模块的配置API,如:set_mode()、set_planes()、set_cursors()、test_page_flip()等。

下面的内容是针对原子操作的。

模式配置

代码语言:javascript复制
static void atomic_set_mode(
    struct device *dev, 
    struct pipe_arg *pipes, 
    unsigned int count)

plane配置

这部分的代码应该是最精彩的部分,里面包括了生成待显示图像数据、配置gamma系数、内存数据填充、配置fb等。

支持4种图像格式,分别是:

代码语言:javascript复制
enum util_fill_pattern {
    UTIL_PATTERN_TILES,
    UTIL_PATTERN_PLAIN,
    UTIL_PATTERN_SMPTE,
    UTIL_PATTERN_GRADIENT,
};

plane配置的命令行参数是:

代码语言:javascript复制
-P <plane_id>@<crtc_id>:<w>x<h>[ <x> <y>][*<scale>][@<format>]

进行plane配置之前首先按照上面的命令行参数逐项进行解析,最后一个format若不指定,默认使用XR24。

代码语言:javascript复制
static int parse_plane(struct plane_arg *plane, const char *p)
{
...
    plane->plane_id = strtoul(p, &end, 10);
...
    plane->crtc_id = strtoul(p, &end, 10);
...
    plane->w = strtoul(p, &end, 10);
...
    plane->h = strtoul(p, &end, 10);

    if (*end == ' ' || *end == '-') {
        plane->x = strtol(end, &end, 10);
...
        plane->y = strtol(end, &end, 10);
...
    }
...
    if (*end == '@') {
        strncpy(plane->format_str, end   1, 4);
        plane->format_str[4] = '';
    } else {
        strcpy(plane->format_str, "XR24");
    }

    plane->fourcc = util_format_fourcc(plane->format_str);
...
}

关于plane显示功能的配置包括三部分:

1) part 1 申请内存空间

user space的程序发起ioctl请求,kernel driver完成内存空间的分配

2) part 2 填充图显数据

根据format格式向申请的内存空间填充rgb或yuv格式的图显数据

3) part 3 framebuffer绑定

创建一个framebuffer-DMA空间,将已准备好的图显数据内存区域与framebuffer进行绑定。

上面的流程涉及到了3个ioctl命令码,如下:

vsync

这个的本意是进行刷新率的测试,但是可以基于这个做简单的2D多帧测试,就不用引入其他视频测试工具如mplayer。

原子操作统一提交commit

配置结束后统一结交给DRM驱动的ioctl API

代码语言:javascript复制
/* user space API */
drmModeAtomicCommit(
    dev.fd, 
    dev.req, 
    DRM_MODE_ATOMIC_ALLOW_MODESET, 
    NULL);

/* kernel DRM driver
 * drivers/gpu/drm/drm_atomic_uapi.c
 */
drm_mode_atomic_ioctl(
    struct drm_device *dev, 
    void *data, 
    struct drm_file *file_priv)
{
…
}

kernel DRM driver ioctl

提供的ioctl API函数定义在 1) drivers/gpu/drm/drm_atomic_uapi.c 2) drivers/gpu/drm/drm_ioctl.c 3) drivers/gpu/drm/drm_auth.c

IOCTL COMMAND码定义在 1) include/uapi/drm/drm.h 假如依赖原子操作,那么modetest图形显示参数配置调用了 DRM_IOCTL(fd, DRM_IOCTL_MODE_ATOMIC, &atomic);

在kernel驱动中对应drm_ioctl.c中定义: DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl 而drm_mode_atomic_ioctl代码实现在drm_atomic_uapi.c中

代码语言:javascript复制
int drm_mode_atomic_ioctl(struct drm_device *dev,
              void *data, struct drm_file *file_priv)
{
    struct drm_mode_atomic *arg = data;
    ...
    unsigned int copied_objs, copied_props;
    struct drm_atomic_state *state;
    struct drm_modeset_acquire_ctx ctx;
    struct drm_out_fence_state *fence_state;
    int ret = 0;
    unsigned int i, j, num_fences;
…
}

原子提交的用户空间代码与内核空间代码的交互流程如下:

modetest验证

2

虚拟机版本:

ubuntu版本:

libdrm代码版本:

ubuntu虚拟机需要切换到命令行模式,否则界面模式下会占用dri设备导致modetest无法正常执行。

进入命令行模式方法:

ctrl alt f1

退出命令行模式方法:

ctrl alt f7

切换到命令行模式下执行modetest

单帧验证:

代码语言:javascript复制
./modetest -M vmwgfx -D 0 -a -s 34@36:1280x960  -P 32@36:1280x960 -Ftiles

结果

多帧验证:

借助vsync这个选项修改代码,轮询显示modetest支持的4种图像格式来实现多个图像的输出

修改的代码如下:

命令行:

代码语言:javascript复制
./testmode -M vmwgfx -D 0 -a -s 34@36:1280x960  -P 32@36:1280x960 -v -Ftiles

限于篇幅没能把libdrm的功能完全的呈现出来,关于图显系统的验证还有很多的方法,这里只是冰山的一角。欢迎补充交流。

0 人点赞