blob: 7930c64c99516eafd83bb5ed86449fa21a32e1ce [file] [log] [blame]
/*
* Copyright 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>
#define CHECK(cond) do {\
if (!(cond)) {\
printf("CHECK failed in %s() %s:%d\n", __func__, __FILE__, __LINE__);\
return 0;\
}\
} while(0)
#define ARRAY_SIZE(A) (sizeof(A)/sizeof(*(A)))
#define ENODRM -1
#define ENODISPLAY -2
static int fd;
static struct gbm_device *gbm;
static const uint32_t format_list[] = {
GBM_BO_FORMAT_XRGB8888,
GBM_BO_FORMAT_ARGB8888,
GBM_FORMAT_C8,
GBM_FORMAT_RGB332,
GBM_FORMAT_BGR233,
GBM_FORMAT_XRGB4444,
GBM_FORMAT_XBGR4444,
GBM_FORMAT_RGBX4444,
GBM_FORMAT_BGRX4444,
GBM_FORMAT_ARGB4444,
GBM_FORMAT_ABGR4444,
GBM_FORMAT_RGBA4444,
GBM_FORMAT_BGRA4444,
GBM_FORMAT_XRGB1555,
GBM_FORMAT_XBGR1555,
GBM_FORMAT_RGBX5551,
GBM_FORMAT_BGRX5551,
GBM_FORMAT_ARGB1555,
GBM_FORMAT_ABGR1555,
GBM_FORMAT_RGBA5551,
GBM_FORMAT_BGRA5551,
GBM_FORMAT_RGB565,
GBM_FORMAT_BGR565,
GBM_FORMAT_RGB888,
GBM_FORMAT_BGR888,
GBM_FORMAT_XRGB8888,
GBM_FORMAT_XBGR8888,
GBM_FORMAT_RGBX8888,
GBM_FORMAT_BGRX8888,
GBM_FORMAT_ARGB8888,
GBM_FORMAT_ABGR8888,
GBM_FORMAT_RGBA8888,
GBM_FORMAT_BGRA8888,
GBM_FORMAT_XRGB2101010,
GBM_FORMAT_XBGR2101010,
GBM_FORMAT_RGBX1010102,
GBM_FORMAT_BGRX1010102,
GBM_FORMAT_ARGB2101010,
GBM_FORMAT_ABGR2101010,
GBM_FORMAT_RGBA1010102,
GBM_FORMAT_BGRA1010102,
GBM_FORMAT_YUYV,
GBM_FORMAT_YVYU,
GBM_FORMAT_UYVY,
GBM_FORMAT_VYUY,
GBM_FORMAT_AYUV,
GBM_FORMAT_NV12,
};
static const uint32_t usage_list[] = {
GBM_BO_USE_SCANOUT,
GBM_BO_USE_CURSOR_64X64,
GBM_BO_USE_RENDERING,
GBM_BO_USE_WRITE,
GBM_BO_USE_LINEAR,
};
static int check_bo(struct gbm_bo *bo)
{
uint32_t format;
size_t num_planes, plane;
int fd;
int i;
CHECK(bo);
CHECK(gbm_bo_get_width(bo) >= 0);
CHECK(gbm_bo_get_height(bo) >= 0);
CHECK(gbm_bo_get_stride(bo) >= gbm_bo_get_width(bo));
format = gbm_bo_get_format(bo);
for (i = 0; i < ARRAY_SIZE(format_list); i++)
if (format_list[i] == format)
break;
CHECK(i < ARRAY_SIZE(format_list));
num_planes = gbm_bo_get_num_planes(bo);
if (format == GBM_FORMAT_NV12)
CHECK(num_planes == 2);
else
CHECK(num_planes == 1);
CHECK(gbm_bo_get_plane_handle(bo, 0).u32 == gbm_bo_get_handle(bo).u32);
CHECK(gbm_bo_get_plane_offset(bo, 0) == 0);
CHECK(gbm_bo_get_plane_size(bo, 0) >=
gbm_bo_get_width(bo) * gbm_bo_get_height(bo));
CHECK(gbm_bo_get_plane_stride(bo, 0) == gbm_bo_get_stride(bo));
for (plane = 0; plane < num_planes; plane++) {
CHECK(gbm_bo_get_plane_handle(bo, plane).u32);
fd = gbm_bo_get_plane_fd(bo, plane);
CHECK(fd > 0);
close(fd);
gbm_bo_get_plane_offset(bo, plane);
CHECK(gbm_bo_get_plane_size(bo, plane));
CHECK(gbm_bo_get_plane_stride(bo, plane));
}
return 1;
}
static drmModeConnector *find_first_connected_connector(int fd,
drmModeRes *resources)
{
int i;
for (i = 0; i < resources->count_connectors; i++) {
drmModeConnector *connector;
connector = drmModeGetConnector(fd, resources->connectors[i]);
if (connector) {
if ((connector->count_modes > 0) &&
(connector->connection == DRM_MODE_CONNECTED))
return connector;
drmModeFreeConnector(connector);
}
}
return NULL;
}
static int drm_open()
{
int fd;
unsigned i;
bool has_drm_device = false;
for (i = 0; i < DRM_MAX_MINOR; i++) {
char* dev_name;
drmModeRes *res = NULL;
int ret;
ret = asprintf(&dev_name, DRM_DEV_NAME, DRM_DIR_NAME, i);
if (ret < 0)
continue;
fd = open(dev_name, O_RDWR, 0);
free(dev_name);
if (fd < 0)
continue;
res = drmModeGetResources(fd);
if (!res) {
drmClose(fd);
continue;
}
if (res->count_crtcs > 0 && res->count_connectors > 0) {
has_drm_device = true;
if (find_first_connected_connector(fd, res)) {
drmModeFreeResources(res);
return fd;
}
}
drmClose(fd);
drmModeFreeResources(res);
}
if (has_drm_device)
return ENODISPLAY;
else
return ENODRM;
}
static int drm_open_vgem()
{
const char g_sys_card_path_format[] =
"/sys/bus/platform/devices/vgem/drm/card%d";
const char g_dev_card_path_format[] =
"/dev/dri/card%d";
char *name;
int i, fd;
for (i = 0; i < 16; i++) {
struct stat _stat;
int ret;
ret = asprintf(&name, g_sys_card_path_format, i);
assert(ret != -1);
if (stat(name, &_stat) == -1) {
free(name);
continue;
}
free(name);
ret = asprintf(&name, g_dev_card_path_format, i);
assert(ret != -1);
fd = open(name, O_RDWR);
free(name);
if (fd == -1) {
return -1;
}
return fd;
}
return -1;
}
static int create_vgem_bo(int fd, size_t size, uint32_t * handle)
{
struct drm_mode_create_dumb create;
int ret;
memset(&create, 0, sizeof(create));
create.height = size;
create.width = 1;
create.bpp = 8;
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
if (ret)
return ret;
assert(create.size >= size);
*handle = create.handle;
return 0;
}
/*
* Tests initialization.
*/
static int test_init()
{
fd = drm_open();
if (fd == ENODISPLAY)
return ENODISPLAY;
CHECK(fd >= 0);
gbm = gbm_create_device(fd);
CHECK(gbm_device_get_fd(gbm) == fd);
const char* backend_name = gbm_device_get_backend_name(gbm);
CHECK(backend_name);
return 1;
}
/*
* Tests reinitialization.
*/
static int test_reinit()
{
gbm_device_destroy(gbm);
close(fd);
fd = drm_open();
CHECK(fd >= 0);
gbm = gbm_create_device(fd);
CHECK(gbm_device_get_fd(gbm) == fd);
struct gbm_bo *bo;
bo = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
CHECK(check_bo(bo));
gbm_bo_destroy(bo);
return 1;
}
/*
* Tests repeated alloc/free.
*/
static int test_alloc_free()
{
int i;
for(i = 0; i < 1000; i++) {
struct gbm_bo *bo;
bo = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
CHECK(check_bo(bo));
gbm_bo_destroy(bo);
}
return 1;
}
/*
* Tests that we can allocate different buffer dimensions.
*/
static int test_alloc_free_sizes()
{
int i;
for(i = 1; i < 1920; i++) {
struct gbm_bo *bo;
bo = gbm_bo_create(gbm, i, i, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
CHECK(check_bo(bo));
gbm_bo_destroy(bo);
}
for(i = 1; i < 1920; i++) {
struct gbm_bo *bo;
bo = gbm_bo_create(gbm, i, 1, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
CHECK(check_bo(bo));
gbm_bo_destroy(bo);
}
for(i = 1; i < 1920; i++) {
struct gbm_bo *bo;
bo = gbm_bo_create(gbm, 1, i, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
CHECK(check_bo(bo));
gbm_bo_destroy(bo);
}
return 1;
}
/*
* Tests that we can allocate different buffer formats.
*/
static int test_alloc_free_formats()
{
int i;
for(i = 0; i < ARRAY_SIZE(format_list); i++) {
uint32_t format = format_list[i];
if (gbm_device_is_format_supported(gbm, format, GBM_BO_USE_RENDERING)) {
struct gbm_bo *bo;
bo = gbm_bo_create(gbm, 1024, 1024, format, GBM_BO_USE_RENDERING);
CHECK(check_bo(bo));
}
}
return 1;
}
/*
* Tests that we find at least one working format for each usage.
*/
static int test_alloc_free_usage()
{
int i, j;
for(i = 0; i < ARRAY_SIZE(usage_list); i++) {
uint32_t usage = usage_list[i];
int found = 0;
for(j = 0; j < ARRAY_SIZE(format_list); j++) {
uint32_t format = format_list[j];
if (gbm_device_is_format_supported(gbm, format, usage)) {
struct gbm_bo *bo;
bo = gbm_bo_create(gbm, 1024, 1024, format, usage);
CHECK(check_bo(bo));
found = 1;
}
}
CHECK(found);
}
return 1;
}
/*
* Tests user data.
*/
static int been_there1;
static int been_there2;
void destroy_data1(struct gbm_bo *bo, void *data)
{
been_there1 = 1;
}
void destroy_data2(struct gbm_bo *bo, void *data)
{
been_there2 = 1;
}
static int test_user_data()
{
struct gbm_bo *bo1, *bo2;
char *data1, *data2;
been_there1 = 0;
been_there2 = 0;
bo1 = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
bo2 = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
data1 = (char*)malloc(1);
data2 = (char*)malloc(1);
CHECK(data1);
CHECK(data2);
gbm_bo_set_user_data(bo1, data1, destroy_data1);
gbm_bo_set_user_data(bo2, data2, destroy_data2);
CHECK((char*)gbm_bo_get_user_data(bo1) == data1);
CHECK((char*)gbm_bo_get_user_data(bo2) == data2);
gbm_bo_destroy(bo1);
CHECK(been_there1 == 1);
gbm_bo_set_user_data(bo2, NULL, NULL);
gbm_bo_destroy(bo2);
CHECK(been_there2 == 0);
free(data1);
free(data2);
return 1;
}
/*
* Tests destruction.
*/
static int test_destroy()
{
gbm_device_destroy(gbm);
close(fd);
return 1;
}
/*
* Tests prime export.
*/
static int test_export()
{
struct gbm_bo *bo;
int prime_fd;
bo = gbm_bo_create(gbm, 1024, 1024, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
CHECK(check_bo(bo));
prime_fd = gbm_bo_get_fd(bo);
CHECK(prime_fd > 0);
close(prime_fd);
gbm_bo_destroy(bo);
return 1;
}
/*
* Tests prime import using VGEM sharing buffer.
*/
static int test_import_vgem()
{
struct gbm_import_fd_data fd_data;
int vgem_fd = drm_open_vgem();
struct drm_prime_handle prime_handle;
struct gbm_bo *bo;
const int width = 123;
const int height = 456;
const int bytes_per_pixel = 4;
const int size = width * height * bytes_per_pixel;
if (vgem_fd <= 0)
return 1;
CHECK(create_vgem_bo(vgem_fd, size, &prime_handle.handle) == 0);
prime_handle.flags = DRM_CLOEXEC;
CHECK(drmIoctl(vgem_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime_handle) == 0);
fd_data.fd = prime_handle.fd;
fd_data.width = width;
fd_data.height = height;
fd_data.stride = width * bytes_per_pixel;
fd_data.format = GBM_FORMAT_XRGB8888;
bo = gbm_bo_import(gbm, GBM_BO_IMPORT_FD, &fd_data, GBM_BO_USE_RENDERING);
CHECK(check_bo(bo));
gbm_bo_destroy(bo);
close(prime_handle.fd);
close(vgem_fd);
return 1;
}
/*
* Tests prime import using dma-buf API.
*/
static int test_import_dmabuf()
{
struct gbm_import_fd_data fd_data;
struct gbm_bo *bo1, *bo2;
const int width = 123;
const int height = 456;
int prime_fd;
bo1 = gbm_bo_create(gbm, width, height, GBM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING);
CHECK(check_bo(bo1));
prime_fd = gbm_bo_get_fd(bo1);
CHECK(prime_fd >= 0);
gbm_bo_destroy(bo1);
fd_data.fd = prime_fd;
fd_data.width = width;
fd_data.height = height;
fd_data.stride = gbm_bo_get_stride(bo1);
fd_data.format = GBM_FORMAT_XRGB8888;
bo2 = gbm_bo_import(gbm, GBM_BO_IMPORT_FD, &fd_data, GBM_BO_USE_RENDERING);
CHECK(check_bo(bo2));
gbm_bo_destroy(bo2);
close(prime_fd);
return 1;
}
int main(int argc, char *argv[])
{
int result;
result = test_init();
if (result == ENODISPLAY) {
printf("[ PASSED ] graphics_Gbm test no connected display found\n");
return EXIT_SUCCESS;
} else if (!result) {
printf("[ FAILED ] graphics_Gbm test initialization failed\n");
return EXIT_FAILURE;
}
result &= test_reinit();
result &= test_alloc_free();
result &= test_alloc_free_sizes();
result &= test_alloc_free_formats();
result &= test_alloc_free_usage();
result &= test_user_data();
result &= test_export();
result &= test_import_vgem();
result &= test_import_dmabuf();
result &= test_destroy();
if (!result) {
printf("[ FAILED ] graphics_Gbm test failed\n");
return EXIT_FAILURE;
} else {
printf("[ PASSED ] graphics_Gbm test success\n");
return EXIT_SUCCESS;
}
}