| /* |
| * 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; |
| } |