blob: 7ac1ffbd3a8e29f3b52af8f34a6113f2a0f0e4d8 [file] [log] [blame]
/*
* Copyright 2017 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.
*/
#include <getopt.h>
#include "bs_drm.h"
#define BUFFERS 2
#define NUM_FRAMES 0x40
struct framebuffer {
struct gbm_bo *bo;
uint32_t id;
};
struct context {
int display_fd;
uint32_t crtc_id;
struct framebuffer fbs[BUFFERS];
struct bs_mapper *mapper;
int vgem_device_fd;
};
struct disabled_features {
char psr_prev_val[1];
};
const char psr_path[] = "/sys/module/i915/parameters/enable_psr";
static void disable_psr(struct disabled_features *dis_feat)
{
int psr_fd = open(psr_path, O_RDWR);
if (psr_fd < 0)
return;
if (read(psr_fd, dis_feat->psr_prev_val, 1) == -1) {
bs_debug_error("failed to read psr value");
} else {
printf("previous enable_psr value: %c\n", dis_feat->psr_prev_val[0]);
}
if (strncmp(dis_feat->psr_prev_val, "1", 1) == 0) {
if (write(psr_fd, "0", 1) == -1) {
bs_debug_error("failed to disable psr");
} else {
printf("disabled psr\n");
}
}
close(psr_fd);
}
static void restore_psr(struct disabled_features *dis_feat)
{
if (strncmp(dis_feat->psr_prev_val, "1", 1) == 0) {
int psr_fd = open(psr_path, O_WRONLY);
if (psr_fd < 0)
return;
if (write(psr_fd, "1", 1) == -1) {
bs_debug_error("failed to re-enable psr");
} else {
printf("re-enabled psr\n");
}
close(psr_fd);
}
}
static void do_fixes(struct disabled_features *dis_feat)
{
disable_psr(dis_feat);
}
static void restore_features(struct disabled_features *dis_feat)
{
restore_psr(dis_feat);
}
#define STEP_SKIP 0
#define STEP_MMAP 1
#define STEP_FAULT 2
#define STEP_FLIP 3
#define STEP_DRAW 4
static void show_sequence(const int *sequence)
{
int sequence_subindex;
printf("starting sequence: ");
for (sequence_subindex = 0; sequence_subindex < 4; sequence_subindex++) {
switch (sequence[sequence_subindex]) {
case STEP_SKIP:
break;
case STEP_MMAP:
printf("mmap ");
break;
case STEP_FAULT:
printf("fault ");
break;
case STEP_FLIP:
printf("flip ");
break;
case STEP_DRAW:
printf("draw ");
break;
default:
bs_debug_error("<unknown step %d> (aborting!)",
sequence[sequence_subindex]);
abort();
break;
}
}
printf("\n");
}
static void draw(struct context *ctx)
{
// Run the drawing routine with the key driver events in different
// sequences.
const int sequences[4][4] = {
{ STEP_MMAP, STEP_FAULT, STEP_FLIP, STEP_DRAW },
{ STEP_MMAP, STEP_FLIP, STEP_DRAW, STEP_SKIP },
{ STEP_MMAP, STEP_DRAW, STEP_FLIP, STEP_SKIP },
{ STEP_FLIP, STEP_MMAP, STEP_DRAW, STEP_SKIP },
};
int sequence_index = 0;
int sequence_subindex = 0;
int fb_idx = 1;
for (sequence_index = 0; sequence_index < 4; sequence_index++) {
show_sequence(sequences[sequence_index]);
for (int frame_index = 0; frame_index < NUM_FRAMES; frame_index += 2) {
struct framebuffer *fb = &ctx->fbs[fb_idx];
uint32_t bo_stride;
size_t bo_size = gbm_bo_get_plane_size(fb->bo, 0);
const uint32_t width = gbm_bo_get_width(fb->bo);
const uint32_t height = gbm_bo_get_height(fb->bo);
uint32_t *bo_ptr = NULL;
volatile uint32_t *ptr = NULL;
void *map_data;
for (sequence_subindex = 0; sequence_subindex < 4; sequence_subindex++) {
switch (sequences[sequence_index][sequence_subindex]) {
case STEP_MMAP:
bo_ptr = bs_mapper_map(ctx->mapper, fb->bo, 0,
&map_data, &bo_stride);
if (bo_ptr == MAP_FAILED)
bs_debug_error("failed to mmap gbm bo");
ptr = bo_ptr;
break;
case STEP_FAULT:
*ptr = 1234567;
break;
case STEP_FLIP:
drmModePageFlip(ctx->display_fd, ctx->crtc_id,
ctx->fbs[fb_idx].id, 0, NULL);
break;
case STEP_DRAW:
for (ptr = bo_ptr;
ptr < bo_ptr + (bo_size / sizeof(*bo_ptr));
ptr++) {
int y = ((void *)ptr - (void *)bo_ptr) /
bo_stride;
int x = ((void *)ptr - (void *)bo_ptr -
bo_stride * y) /
sizeof(*ptr);
x -= frame_index * (width / NUM_FRAMES);
y -= frame_index * (height / NUM_FRAMES);
*ptr = 0xff000000;
if (x * x + y * y <
frame_index * frame_index)
*ptr |= (frame_index % 0x100) << 8;
else
*ptr |= 0xff |
(sequence_index * 64 << 16);
}
break;
case STEP_SKIP:
default:
break;
}
}
bs_mapper_unmap(ctx->mapper, fb->bo, map_data);
usleep(1e6 / 120); /* 120 Hz */
fb_idx = fb_idx ^ 1;
}
}
}
static const struct option longopts[] = {
{ "help", no_argument, NULL, 'h' },
{ "dma-buf", no_argument, NULL, 'b' },
{ "gem", no_argument, NULL, 'g' },
{ "dumb", no_argument, NULL, 'd' },
{ "vgem", no_argument, NULL, 'v' },
{ "scanout", no_argument, NULL, 's' },
{ 0, 0, 0, 0 },
};
static void print_help(const char *argv0)
{
printf("Usage: %s [OPTIONS]\n", argv0);
printf(" -h, --help Print help.\n");
printf(" -b, --dma-buf Use dma-buf mmap (by default).\n");
printf(" -g, --gem Use GEM map.\n");
printf(" -d, --dumb Use dump map.\n");
printf(" -v, --vgem Use vgem dump map.\n");
printf(" -s, --scanout Use buffer optimized for scanout.\n");
}
int main(int argc, char **argv)
{
struct context ctx = { 0 };
struct disabled_features dis_feat = { 0 };
do_fixes(&dis_feat);
ctx.display_fd = bs_drm_open_main_display();
if (ctx.display_fd < 0) {
bs_debug_error("failed to open card for display");
return 1;
}
struct gbm_device *gbm = gbm_create_device(ctx.display_fd);
if (!gbm) {
bs_debug_error("failed to create gbm device");
return 1;
}
int c;
uint32_t flags = GBM_BO_USE_SCANOUT;
while ((c = getopt_long(argc, argv, "bgdvsh", longopts, NULL)) != -1) {
switch (c) {
case 'b':
ctx.mapper = bs_mapper_dma_buf_new();
flags |= GBM_BO_USE_LINEAR;
printf("started dma-buf mmap.\n");
break;
case 'g':
ctx.mapper = bs_mapper_gem_new();
flags |= GBM_BO_USE_SW_READ_OFTEN | GBM_BO_USE_SW_WRITE_OFTEN;
printf("started GEM map.\n");
break;
case 'd':
ctx.mapper = bs_mapper_dumb_new(gbm_device_get_fd(gbm));
flags |= GBM_BO_USE_LINEAR;
printf("started dumb map.\n");
break;
case 'v':
ctx.vgem_device_fd = bs_drm_open_vgem();
ctx.mapper = bs_mapper_dumb_new(ctx.vgem_device_fd);
printf("started vgem map.\n");
break;
case 's':
flags = GBM_BO_USE_SCANOUT;
break;
case 'h':
default:
print_help(argv[0]);
return 1;
}
}
// Use dma-buf mmap by default, in case any arguments aren't selected.
if (!ctx.mapper) {
ctx.mapper = bs_mapper_dma_buf_new();
flags |= GBM_BO_USE_LINEAR;
printf("started dma-buf mmap.\n");
}
if (ctx.mapper == NULL) {
bs_debug_error("failed to create mapper object");
return 1;
}
struct bs_drm_pipe pipe = { 0 };
if (!bs_drm_pipe_make(ctx.display_fd, &pipe)) {
bs_debug_error("failed to make pipe");
return 1;
}
drmModeConnector *connector = drmModeGetConnector(ctx.display_fd, pipe.connector_id);
drmModeModeInfo *mode = &connector->modes[0];
ctx.crtc_id = pipe.crtc_id;
printf("display size: %ux%u\n", mode->hdisplay, mode->vdisplay);
for (size_t fb_index = 0; fb_index < BUFFERS; ++fb_index) {
struct framebuffer *fb = &ctx.fbs[fb_index];
fb->bo =
gbm_bo_create(gbm, mode->hdisplay, mode->vdisplay, GBM_FORMAT_XRGB8888, flags);
if (!fb->bo) {
bs_debug_error("failed to create buffer object");
return 1;
}
fb->id = bs_drm_fb_create_gbm(fb->bo);
if (fb->id == 0) {
bs_debug_error("failed to create fb");
return 1;
}
}
if (drmModeSetCrtc(ctx.display_fd, pipe.crtc_id, ctx.fbs[0].id, 0, 0, &pipe.connector_id, 1,
mode)) {
bs_debug_error("failed to set CRTC");
return 1;
}
draw(&ctx);
restore_features(&dis_feat);
return 0;
}