modetest 是由 libdrm 提供的测试程序,可以查询显示设备的特性,进行基本的显示测试,以及设置显示的模式。
我们可以借助该工具来学习 Linux DRM 应用编程,另外为了深入分析 Rockchip DRM driver,有必要先了解一下这个工具的使用方法和内部实现。
一、准本工作
1、开发板:ATK-DLRK3568 开发板
2、环境:ubuntu20.0 正点原子网盘提供的版本
二、modetest编译
本人没自己编译过,编译过程只供参考,正点的板子自带了
编写一个libdrm的测试程序较为复杂,这里我们使用libdrm官方的测试工具来进行测试,我们可以在这里下载源码并进行交叉编译出测试工具,以供在开发板上使用: libdrm .
新版的libdrm使用meson+ninja的构建方式,而不是老版的autotools,没有基础的同学构建新版libdrm会比较痛苦。 建议直接使用我们给大家编译好的测试程序,测试程序位于配套例程 linux_driver/framework_drm/modetest。
如果要自己编译libdrm,可以参考下面命令:
git clone https://gitlab.freedesktop.org/mesa/drmsudo apt -y install python3-pip cmake git ninja-buildpython3 -m pip install meson /*安装之后,重启板卡*/meson . build && ninja -C build
编译之后在build/tests/modetest/下会有modetest程序, 对libdrm测试程序感兴趣的同学,可以下载libdrm源码解压,在其目录/drm/tests/modetest/下,查看modetest.c文件,此为测试程序源码。
三、modetest 使用示例
modetest在bin目录下,板子上电后打开终端进入bin目录
cd /bin
1、 查看帮助信息
modetest -h
2、查看组件的信息
modetest -M rockchip
参数说明:
-M
:用于指定访问 rockchip DRM driver
关键内容:
- Encoders / Connectors / CRTCs / Planes 的 id,modetest 通过 id 来用于引用这些组件。
- Connectors 的 modes/props:
- prop: 任何你想设置的参数,都可以做成 property,是 DRM 驱动中最灵活、最方便的 Mode setting 机制;
- modes: 显示模式,mode 里包含分辨率/刷新率等显示相关的信息;
- CRTCs 的 props;
- Planes 的 formats/props;
3、查看各组件的 id
modetest -M rockchip | cut -f1 | grep -E ^[0-9A-Z]\|id
正点原子的RK3568提供了一路的MIPI DSI 接口,所以测试MIPI
modetest -M rockchip -s 163@115:720x1280
四、最简单DRM(drm-single)
直接上代码,目录根据yolov5的那个程序修改的,替换mian.cc文件后需要添加drm库和头文件。
// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License./*-------------------------------------------Includes
-------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <dlfcn.h>
#include <vector>
#include <string>#include "drm_func.h"
#include "rga_func.h"
#include "rknn_api.h"
#include "yolo.h"#include "RgaUtils.h"
#include "im2d.h"
#include "opencv2/core/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <opencv2/opencv.hpp>
#include "rga.h"
#include <xf86drm.h>
#include <xf86drmMode.h>#define RED 0XFF0000
#define GREEN 0X00FF00
#define BLUE 0X0000FF
#define BLACK 0X000000
#define WHITE 0XFFFFFF
#define BLACK_BLUE 0X123456struct drm_device {uint32_t width; //显示器的宽的像素点数量uint32_t height; //显示器的高的像素点数量uint32_t pitch; //每行占据的字节数uint32_t handle; //drm_mode_create_dumb的返回句柄uint32_t size; //显示器占据的总字节数uint32_t *vaddr; //mmap的首地址uint32_t fb_id; //创建的framebuffer的id号struct drm_mode_create_dumb create ; //创建的dumbstruct drm_mode_map_dumb map; //内存映射结构体
};drmModeConnector *conn; //connetor相关的结构体
drmModeRes *res; //资源
uint32_t conn_id; //connetor的id号
uint32_t crtc_id; //crtc的id号
int fd; //文件描述符#define RED 0XFF0000
#define GREEN 0X00FF00
#define BLUE 0X0000FFuint32_t color_table[6] = {RED,GREEN,BLUE,BLACK,WHITE,BLACK_BLUE};uint32_t plane_id[3]; //图层id数组struct drm_device buf;static int drm_create_fb(struct drm_device *bo)
{/* create a dumb-buffer, the pixel format is XRGB888 */bo->create.width = bo->width;bo->create.height = bo->height;bo->create.bpp = 32;/* handle, pitch, size will be returned */drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &bo->create);/* bind the dumb-buffer to an FB object */bo->pitch = bo->create.pitch;bo->size = bo->create.size;bo->handle = bo->create.handle;drmModeAddFB(fd, bo->width, bo->height, 24, 32, bo->pitch,bo->handle, &bo->fb_id);//每行占用字节数,共占用字节数,MAP_DUMB的句柄printf("pitch = %d ,size = %d, handle = %d \n",bo->pitch,bo->size,bo->handle);/* map the dumb-buffer to userspace */bo->map.handle = bo->create.handle;drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &bo->map);bo->vaddr = (uint32_t*)mmap(0, bo->create.size, PROT_READ | PROT_WRITE,MAP_SHARED, fd, bo->map.offset);/* initialize the dumb-buffer with white-color */memset(bo->vaddr, 0xff,bo->size);return 0;
}static void drm_destroy_fb(struct drm_device *bo)
{struct drm_mode_destroy_dumb destroy = {};drmModeRmFB(fd, bo->fb_id);munmap(bo->vaddr, bo->size);destroy.handle = bo->handle;drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
}int drm_init()
{//打开drm设备,设备会随设备树的更改而改变,多个设备时,请留一下每个屏幕设备对应的drm设备fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);if(fd < 0){printf("wrong\n");return 0;}//获取drm的信息res = drmModeGetResources(fd);crtc_id = res->crtcs[1];conn_id = res->connectors[1];//打印CRTCS,以及conneter的idprintf("crtc = %d , conneter = %d\n",crtc_id,conn_id);conn = drmModeGetConnector(fd, conn_id);buf.width = conn->modes[0].hdisplay;buf.height = conn->modes[0].vdisplay;//打印屏幕分辨率printf("width = %d , height = %d\n",buf.width,buf.height);//创建framebuffer层drm_create_fb(&buf);//设置CRTCSdrmModeSetCrtc(fd, crtc_id, buf.fb_id,0, 0, &conn_id, 1, &conn->modes[0]);return 0;
}void drm_exit()
{drm_destroy_fb(&buf);drmModeFreeConnector(conn);drmModeFreeResources(res);close(fd);
}int main(int argc, char **argv)
{int i,j;drm_init();sleep(2);printf("display colour\n");//显示三色for(j=0;j<3;j++){for(i =j*buf.width*buf.height/3;i< (j+1)*buf.width*buf.height/3;i++)buf.vaddr[i] = color_table[j];}getchar();printf("getchar\n");drm_exit();exit(0);
}
运行结果
程序需要注意一点。
//获取drm的信息res = drmModeGetResources(fd);crtc_id = res->crtcs[1];conn_id = res->connectors[1];
接下来,实现采集视频显示。