summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaulo Zanoni <paulo.r.zanoni@intel.com>2014-12-02 10:40:05 -0200
committerPaulo Zanoni <paulo.r.zanoni@intel.com>2014-12-18 14:36:09 -0200
commitf57f7dfd2bbaeeb43b72cbccd436b876d3a52400 (patch)
treeb79df5ed9f0215571433c7017bd2f95216221650
parent0615e6eabca9dc74649f0af797730c978abcc73e (diff)
tests: add pm_fbcHEADmaster
A new test for FBC. Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.sources1
-rw-r--r--tests/pm_fbc.c1295
3 files changed, 1297 insertions, 0 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
index 0dc044e5..4ddccca1 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -135,6 +135,7 @@ kms_setmode
kms_sink_crc_basic
kms_universal_plane
multi-tests.txt
+pm_fbc
pm_lpsp
pm_psr
pm_rc6_residency
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 967dc8f8..6b6813a8 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -76,6 +76,7 @@ TESTS_progs_M = \
kms_rotation_crc \
kms_setmode \
kms_universal_plane \
+ pm_fbc \
pm_lpsp \
pm_rpm \
pm_rps \
diff --git a/tests/pm_fbc.c b/tests/pm_fbc.c
new file mode 100644
index 00000000..28e1c0bc
--- /dev/null
+++ b/tests/pm_fbc.c
@@ -0,0 +1,1295 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors: Paulo Zanoni <paulo.r.zanoni@intel.com>
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "drmtest.h"
+#include "igt_aux.h"
+#include "igt_kms.h"
+#include "igt_debugfs.h"
+#include "intel_chipset.h"
+#include "ioctl_wrappers.h"
+
+#define MAX_CONNECTORS 32
+
+#define N_RECTS 6
+
+int drm_fd;
+int fbc_fd;
+int devid;
+int gen;
+drmModeResPtr drm_res;
+drmModeConnectorPtr drm_connectors[MAX_CONNECTORS];
+
+igt_pipe_crc_t *pipe_crc;
+igt_crc_t blue_crc, rect_crc[N_RECTS];
+igt_crc_t *custom_crc;
+
+struct igt_fb rect_fbs[N_RECTS];
+
+bool check_status = true;
+bool check_crc = true;
+bool check_compression = true;
+bool check_last_action = true;
+
+bool manual = false;
+
+bool supports_last_action = false;
+bool supports_compressing = false;
+
+struct timespec last_action;
+
+enum fbc_status {
+ FBC_ENABLED,
+ FBC_DISABLED,
+ FBC_BAD, /* HW state doesn't match SW tracking state */
+};
+
+enum draw_method {
+ DRAW_MMAP_CPU,
+ DRAW_MMAP_GTT,
+ DRAW_PWRITE,
+ DRAW_BLT,
+ DRAW_RENDER,
+ DRAW_METHOD_COUNT,
+};
+
+const char *draw_method_names[] = {
+ "mmap-cpu",
+ "mmap-gtt",
+ "pwrite",
+ "blt",
+ "render",
+};
+
+struct modeset_params {
+ uint32_t crtc_id;
+ uint32_t connector_id;
+ struct igt_fb fb;
+ drmModeModeInfoPtr mode;
+};
+
+drmModeModeInfo std_1024_mode = {
+ .clock = 65000,
+ .hdisplay = 1024,
+ .hsync_start = 1048,
+ .hsync_end = 1184,
+ .htotal = 1344,
+ .vtotal = 806,
+ .hskew = 0,
+ .vdisplay = 768,
+ .vsync_start = 771,
+ .vsync_end = 777,
+ .vtotal = 806,
+ .vscan = 0,
+ .vrefresh = 60,
+ .flags = 0xA,
+ .type = 0x40,
+ .name = "Custom 1024x768",
+};
+
+struct modeset_params fbc_mode_params;
+struct modeset_params non_fbc_mode_params;
+
+#if 0
+static drmModeModeInfoPtr get_connector_smallest_mode(drmModeConnectorPtr c)
+{
+ int i;
+ drmModeModeInfoPtr smallest = NULL;
+
+ for (i = 0; i < c->count_modes; i++) {
+ drmModeModeInfoPtr mode = &c->modes[i];
+
+ if (!smallest)
+ smallest = mode;
+
+ if (mode->hdisplay * mode->vdisplay <
+ smallest->hdisplay * smallest->vdisplay)
+ smallest = mode;
+ }
+
+ return smallest;
+}
+#endif
+
+static void fill_igt_fb(struct igt_fb *fb, uint32_t color)
+{
+ int i;
+ uint32_t *ptr;
+
+ ptr = gem_mmap__gtt(drm_fd, fb->gem_handle, fb->size, PROT_WRITE);
+ for (i = 0; i < fb->size/sizeof(uint32_t); i++)
+ ptr[i] = color;
+ igt_assert(munmap(ptr, fb->size) == 0);
+}
+
+static bool init_modeset_cached_params(void)
+{
+ int i;
+ uint32_t fbc_connector_id = 0, non_fbc_connector_id = 0;
+ drmModeModeInfoPtr fbc_mode = NULL, non_fbc_mode = NULL;
+
+ for (i = 0; i < drm_res->count_connectors; i++) {
+ drmModeConnectorPtr c = drm_connectors[i];
+
+ if (c->connection == DRM_MODE_CONNECTED && c->count_modes) {
+ drmModeModeInfoPtr mode;
+
+ /* Because on some machines we don't have enough stolen
+ * memory to fit in those 3k panels. And on HSW the CRC
+ * WA is so awful that it makes you think everything is
+ * bugged. */
+ if (c->connector_type == DRM_MODE_CONNECTOR_eDP)
+ mode = &std_1024_mode;
+ else
+ mode = &c->modes[0];
+ /* mode = get_connector_smallest_mode(c); */
+
+ if (!fbc_connector_id) {
+ fbc_connector_id = c->connector_id;
+ fbc_mode = mode;
+ } else if (!non_fbc_connector_id) {
+ non_fbc_connector_id = c->connector_id;
+ non_fbc_mode = mode;
+ } else {
+ break;
+ }
+ }
+ }
+
+ if (!fbc_connector_id)
+ return false;
+
+ igt_create_fb(drm_fd, fbc_mode->hdisplay, fbc_mode->vdisplay,
+ DRM_FORMAT_XRGB8888, I915_TILING_X, &fbc_mode_params.fb);
+ fill_igt_fb(&fbc_mode_params.fb, 0xFF);
+
+ fbc_mode_params.crtc_id = drm_res->crtcs[0];
+ fbc_mode_params.connector_id = fbc_connector_id;
+ fbc_mode_params.mode = fbc_mode;
+
+ if (!non_fbc_connector_id) {
+ non_fbc_mode_params.connector_id = 0;
+ return true;
+ }
+
+ igt_create_fb(drm_fd, non_fbc_mode->hdisplay, non_fbc_mode->vdisplay,
+ DRM_FORMAT_XRGB8888, I915_TILING_X,
+ &non_fbc_mode_params.fb);
+ fill_igt_fb(&non_fbc_mode_params.fb, 0x88);
+
+ igt_assert(drm_res->count_crtcs >= 2);
+ non_fbc_mode_params.crtc_id = drm_res->crtcs[1];
+ non_fbc_mode_params.connector_id = non_fbc_connector_id;
+ non_fbc_mode_params.mode = non_fbc_mode;
+
+ return true;
+}
+
+static bool set_mode_for_params(struct modeset_params *params)
+{
+ int rc;
+
+ rc = drmModeSetCrtc(drm_fd, params->crtc_id, params->fb.fb_id, 0, 0,
+ &params->connector_id, 1, params->mode);
+ return (rc == 0);
+}
+
+#define DEBUGFS_MSG_SIZE 256
+
+#if 0
+struct debugfs_data {
+ enum fbc_status status;
+ struct timespec last_action;
+ bool is_compressing;
+};
+
+static struct debugfs_data get_debugfs_data(void) {
+ struct debugfs_data data;
+ char buf[DEBUGFS_MSG_SIZE], *s;
+ ssize_t n_read;
+
+ lseek(fbc_fd, 0, SEEK_SET);
+
+ n_read = read(fbc_fd, buf, DEBUGFS_MSG_SIZE -1);
+ igt_assert(n_read >= 0);
+ buf[n_read] = '\0';
+
+ s = strstr(buf, "FBC enabled");
+ data.status = s ? FBC_ENABLED : FBC_DISABLED;
+
+ s = strstr(buf, "Compressing: yes");
+ data.is_compressing = s != NULL;
+
+ s = strstr(buf, "Last action:");
+ igt_assert(s);
+
+ n_read = sscanf(s, "Last action: %ld.%ld",
+ &data.last_action.tv_sec, &data.last_action.tv_nsec);
+ igt_assert(n_read == 2);
+
+ return data;
+}
+#endif
+
+static void get_debugfs_string(char *buf)
+{
+ ssize_t n_read;
+
+ lseek(fbc_fd, 0, SEEK_SET);
+
+ n_read = read(fbc_fd, buf, DEBUGFS_MSG_SIZE -1);
+ igt_assert(n_read >= 0);
+ buf[n_read] = '\0';
+}
+
+static enum fbc_status get_fbc_status(void)
+{
+ char buf[DEBUGFS_MSG_SIZE];
+
+ get_debugfs_string(buf);
+
+ if (strstr(buf, "FBC enabled"))
+ return FBC_ENABLED;
+ else
+ return FBC_DISABLED;
+}
+
+static struct timespec get_last_action(void)
+{
+ struct timespec ret = { 0, 0 };
+ char buf[DEBUGFS_MSG_SIZE];
+ char *action;
+ ssize_t n_read;
+
+ get_debugfs_string(buf);
+
+ action = strstr(buf, "Last action:");
+ igt_assert(action);
+
+ n_read = sscanf(action, "Last action: %ld.%ld",
+ &ret.tv_sec, &ret.tv_nsec);
+ igt_assert(n_read == 2);
+
+ return ret;
+}
+
+static bool last_action_changed(void)
+{
+ struct timespec t_new, t_old;
+
+ t_old = last_action;
+ t_new = get_last_action();
+
+ last_action = t_new;
+
+#if 0
+ igt_log(IGT_LOG_INFO, "old: %ld.%ld\n",
+ t_old.tv_sec, t_old.tv_nsec);
+ igt_log(IGT_LOG_INFO, "new: %ld.%ld\n",
+ t_new.tv_sec, t_new.tv_nsec);
+#endif
+
+ return t_old.tv_sec != t_new.tv_sec ||
+ t_old.tv_nsec != t_new.tv_nsec;
+}
+
+static void update_last_action(void)
+{
+ if (!supports_last_action)
+ return;
+
+ last_action = get_last_action();
+
+#if 0
+ igt_log(IGT_LOG_INFO, "Last action: %ld.%ld\n",
+ last_action.tv_sec, last_action.tv_nsec);
+#endif
+}
+
+static void setup_last_action(void)
+{
+ ssize_t n_read;
+ char buf[DEBUGFS_MSG_SIZE];
+ char *action;
+
+ get_debugfs_string(buf);
+
+ action = strstr(buf, "Last action:");
+ if (!action) {
+ igt_log(IGT_LOG_INFO, "Last action not supported\n");
+ return;
+ }
+
+ supports_last_action = true;
+
+ n_read = sscanf(action, "Last action: %ld.%ld",
+ &last_action.tv_sec, &last_action.tv_nsec);
+ igt_assert(n_read == 2);
+}
+
+static bool is_compressing(void)
+{
+ char buf[DEBUGFS_MSG_SIZE];
+
+ get_debugfs_string(buf);
+ return strstr(buf, "Compressing: yes") != NULL;
+}
+
+static bool wait_for_compression(void)
+{
+ return igt_wait(is_compressing(), 5000, 1);
+}
+
+static void setup_compressing(void)
+{
+ char buf[DEBUGFS_MSG_SIZE];
+
+ get_debugfs_string(buf);
+
+ if (strstr(buf, "Compressing:")) {
+ supports_compressing = true;
+ } else {
+ igt_log(IGT_LOG_INFO,
+ "Compression information not supported\n");
+ }
+}
+
+static bool wait_for_status(enum fbc_status status)
+{
+ return igt_wait(get_fbc_status() == status, 5000, 1);
+}
+
+static int swizzle_addr(struct igt_fb *fb, int addr, int swizzle)
+{
+ int bit6;
+
+ if (swizzle == I915_BIT_6_SWIZZLE_9_10) {
+ bit6 = ((addr >> 6) & 1) ^ ((addr >> 9) & 1) ^
+ ((addr >> 10) & 1);
+ addr &= ~(1 << 6);
+ addr |= (bit6 << 6);
+ }
+
+ return addr;
+}
+
+/* It's all in "pixel coordinates", so make sure you multiply/divide by the bpp
+ * if you need to. */
+static int linear_x_y_to_tiled_pos(struct igt_fb *fb, int x, int y,
+ int swizzle)
+{
+ int x_tile_size, y_tile_size;
+ int x_tile_n, y_tile_n, x_tile_off, y_tile_off;
+ int line_size, tile_size;
+ int tile_n, tile_off;
+ int tiled_pos, tiles_per_line;
+ int bpp;
+
+ line_size = fb->stride;
+ x_tile_size = 512;
+ y_tile_size = 8;
+ tile_size = x_tile_size * y_tile_size;
+ tiles_per_line = line_size / x_tile_size;
+ bpp = sizeof(uint32_t); /* TODO: don't assume this. */
+
+ y_tile_n = y / y_tile_size;
+ y_tile_off = y % y_tile_size;
+
+ x_tile_n = (x * bpp) / x_tile_size;
+ x_tile_off = (x * bpp) % x_tile_size;
+
+ tile_n = y_tile_n * tiles_per_line + x_tile_n;
+ tile_off = y_tile_off * x_tile_size + x_tile_off;
+ tiled_pos = tile_n * tile_size + tile_off;
+
+ tiled_pos = swizzle_addr(fb, tiled_pos, swizzle);
+
+ return tiled_pos / bpp;
+}
+
+/* It's all in "pixel coordinates", so make sure you multiply/divide by the bpp
+ * if you need to. */
+static void tiled_pos_to_x_y_linear(struct igt_fb *fb, int tiled_pos,
+ int swizzle, int *x, int *y)
+{
+ int tile_n, tile_off, tiles_per_line, line_size;
+ int x_tile_off, y_tile_off;
+ int x_tile_n, y_tile_n;
+ int x_tile_size, y_tile_size, tile_size;
+ int bpp;
+
+ tiled_pos = swizzle_addr(fb, tiled_pos, swizzle);
+
+ line_size = fb->stride;
+ x_tile_size = 512;
+ y_tile_size = 8;
+ tile_size = x_tile_size * y_tile_size;
+ tiles_per_line = line_size / x_tile_size;
+ bpp = sizeof(uint32_t); /* TODO: don't assume this. */
+
+ tile_n = tiled_pos / tile_size;
+ tile_off = tiled_pos % tile_size;
+
+ y_tile_off = tile_off / x_tile_size;
+ x_tile_off = tile_off % x_tile_size;
+
+ x_tile_n = tile_n % tiles_per_line;
+ y_tile_n = tile_n / tiles_per_line;
+
+ *x = (x_tile_n * x_tile_size + x_tile_off) / bpp;
+ *y = y_tile_n * y_tile_size + y_tile_off;
+}
+
+struct rect {
+ int x;
+ int y;
+ int w;
+ int h;
+ uint32_t color;
+};
+
+static struct rect get_rect(int r, struct igt_fb *fb)
+{
+ struct rect rect;
+
+ switch (r) {
+ case 0:
+ rect.x = 0;
+ rect.y = 0;
+ rect.w = fb->width / 8;
+ rect.h = fb->height / 8;
+ rect.color = 0x00FF00;
+ break;
+ case 1:
+ rect.x = fb->width / 8;
+ rect.y = fb->height / 8;
+ rect.w = fb->width / 8;
+ rect.h = fb->height / 8;
+ rect.color = 0xFF0000;
+ break;
+ case 2:
+ rect.x = fb->width / 8 * 4;
+ rect.y = fb->height / 8 * 4;
+ rect.w = fb->width / 8 * 2;
+ rect.h = fb->height / 8 * 2;
+ rect.color = 0xFF00FF;
+ break;
+ case 3:
+ rect.x = fb->width / 16;
+ rect.y = fb->height / 16;
+ rect.w = fb->width / 8;
+ rect.h = fb->height / 8;
+ rect.color = 0x00FFFF;
+ break;
+ case 4: /* Cursor sized! */
+ rect.x = 0;
+ rect.y = 0;
+ rect.w = 64;
+ rect.h = 64;
+ rect.color = 0xFFFF00;
+ break;
+ case 5: /* Cursor sized!(2) */
+ rect.x = 64;
+ rect.y = 64;
+ rect.w = 64;
+ rect.h = 64;
+ rect.color = 0xFFFF00;
+ break;
+ default:
+ igt_assert(0);
+ }
+
+ return rect;
+}
+
+static void draw_rect_mmap_cpu(struct igt_fb *fb, int r)
+{
+ uint32_t *ptr;
+ int x, y, tiled_pos;
+ struct rect rect = get_rect(r, fb);
+ uint32_t tiling, swizzle;
+
+ gem_set_domain(drm_fd, fb->gem_handle, I915_GEM_DOMAIN_CPU,
+ I915_GEM_DOMAIN_CPU);
+ gem_get_tiling(drm_fd, fb->gem_handle, &tiling, &swizzle);
+
+ ptr = gem_mmap__cpu(drm_fd, fb->gem_handle, 0, fb->size, 0);
+ igt_assert(ptr);
+
+ for (y = rect.y; y < rect.y + rect.h; y++) {
+ for (x = rect.x; x < rect.x + rect.w; x++) {
+ tiled_pos = linear_x_y_to_tiled_pos(fb, x, y, swizzle);
+ ptr[tiled_pos] = rect.color;
+ }
+ }
+
+ gem_sw_finish(drm_fd, fb->gem_handle);
+
+ igt_assert(munmap(ptr, fb->size) == 0);
+#if 0
+ gem_set_domain(drm_fd, fb->gem_handle, I915_GEM_DOMAIN_GTT,
+ I915_GEM_DOMAIN_GTT);
+#endif
+}
+
+static void draw_rect_mmap_gtt(struct igt_fb *fb, int r)
+{
+ uint32_t *ptr;
+ int w, h;
+ struct rect rect = get_rect(r, fb);
+
+ ptr = gem_mmap__gtt(drm_fd, fb->gem_handle, fb->size,
+ PROT_READ | PROT_WRITE);
+ igt_assert(ptr);
+
+ for (h = rect.y; h < rect.y + rect.h; h++) {
+ int line_begin = h * fb->stride / sizeof(uint32_t);
+ for (w = rect.x; w < rect.x + rect.w; w++)
+ ptr[line_begin + w] = rect.color;
+ }
+
+ igt_assert(munmap(ptr, fb->size) == 0);
+}
+
+static void draw_rect_pwrite(struct igt_fb *fb, int r)
+{
+ int i;
+ int tiled_pos, bpp, x, y;
+ uint32_t buf[1024];
+ int buf_used = 0, buf_size = ARRAY_SIZE(buf);
+ bool flush_buf = false;
+ int buf_start_pos = 0;
+ struct rect rect = get_rect(r, fb);
+ uint32_t tiling, swizzle;
+
+ gem_get_tiling(drm_fd, fb->gem_handle, &tiling, &swizzle);
+ bpp = sizeof(uint32_t);
+
+ /* Instead of doing one pwrite per pixel, we try to group the maximum
+ * amount of consecutive green pixels we can in a single pwrite: that's
+ * why we use the "buf" variables. */
+ for (i = 0; i < buf_size; i++)
+ buf[i] = rect.color;
+
+ for (tiled_pos = 0; tiled_pos < fb->size; tiled_pos += bpp) {
+ tiled_pos_to_x_y_linear(fb, tiled_pos, swizzle, &x, &y);
+
+ if (x >= rect.x && x < rect.x + rect.w &&
+ y >= rect.y && y < rect.y + rect.h) {
+ if (buf_used == 0)
+ buf_start_pos = tiled_pos;
+ buf_used++;
+ } else {
+ flush_buf = true;
+ }
+
+ if (buf_used == buf_size || (flush_buf && buf_used > 0)) {
+ gem_write(drm_fd, fb->gem_handle, buf_start_pos, buf,
+ buf_used * sizeof(uint32_t));
+ flush_buf = false;
+ buf_used = 0;
+ }
+ }
+}
+
+static void draw_rect_blt(struct igt_fb *fb, int r)
+{
+ int i, reloc_pos;
+ uint32_t batch_handle;
+ int batch_size = 8 * sizeof(uint32_t);
+ uint32_t batch_buf[batch_size];
+ struct drm_i915_gem_execbuffer2 execbuf = {};
+ struct drm_i915_gem_exec_object2 objs[2] = {{}, {}};
+ struct drm_i915_gem_relocation_entry relocs[1] = {{}};
+ struct drm_i915_gem_wait gem_wait;
+ int blt_cmd_len;
+ uint32_t presumed_dst_offset = 0;
+ uint32_t dst_handle = fb->gem_handle;
+ struct rect rect = get_rect(r, fb);
+
+ blt_cmd_len = (gen >= 8) ? 0x5 : 0x4;
+
+ i = 0;
+ batch_buf[i++] = XY_COLOR_BLT_CMD_NOLEN | XY_COLOR_BLT_WRITE_ALPHA |
+ XY_COLOR_BLT_WRITE_RGB | XY_COLOR_BLT_TILED |
+ blt_cmd_len;
+ batch_buf[i++] = (3 << 24) | (0xF0 << 16) | (fb->stride / 4);
+ batch_buf[i++] = (rect.y << 16) | rect.x;
+ batch_buf[i++] = ((rect.y + rect.h) << 16) | (rect.x + rect.w);
+ reloc_pos = i;
+ batch_buf[i++] = presumed_dst_offset;
+ if (gen >= 8)
+ batch_buf[i++] = 0;
+ batch_buf[i++] = rect.color;
+
+ batch_buf[i++] = MI_BATCH_BUFFER_END;
+ if (gen < 8)
+ batch_buf[i++] = MI_NOOP;
+
+ igt_assert(i * sizeof(uint32_t) == batch_size);
+
+ batch_handle = gem_create(drm_fd, batch_size);
+ gem_write(drm_fd, batch_handle, 0, batch_buf, batch_size);
+
+ relocs[0].target_handle = dst_handle;
+ relocs[0].delta = 0;
+ relocs[0].offset = reloc_pos * sizeof(uint32_t);
+ relocs[0].presumed_offset = presumed_dst_offset;
+ relocs[0].read_domains = 0;
+ relocs[0].write_domain = I915_GEM_DOMAIN_RENDER;
+
+ objs[0].handle = dst_handle;
+ objs[0].alignment = 0;
+
+ objs[1].handle = batch_handle;
+ objs[1].relocation_count = 1;
+ objs[1].relocs_ptr = (uintptr_t)relocs;
+
+ execbuf.buffers_ptr = (uintptr_t)objs;
+ execbuf.buffer_count = 2;
+ execbuf.batch_len = batch_size;
+ execbuf.flags = I915_EXEC_BLT;
+ i915_execbuffer2_set_context_id(execbuf, 0);
+
+ do_ioctl(drm_fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
+
+ presumed_dst_offset = relocs[0].presumed_offset;
+
+ gem_wait.flags = 0;
+ gem_wait.timeout_ns = 10000000000LL; /* 10s */
+
+#if 0
+ gem_wait.bo_handle = batch_handle;
+ do_ioctl(drm_fd, DRM_IOCTL_I915_GEM_WAIT, &gem_wait);
+
+ gem_wait.bo_handle = dst_handle;
+ do_ioctl(drm_fd, DRM_IOCTL_I915_GEM_WAIT, &gem_wait);
+#endif
+ gem_sync(drm_fd, batch_handle);
+ gem_sync(drm_fd, dst_handle);
+
+ gem_close(drm_fd, batch_handle);
+}
+
+static void draw_rect_render(struct igt_fb *fb, int r)
+{
+ drm_intel_bufmgr *bufmgr;
+ drm_intel_bo *src, *dst;
+ igt_render_copyfunc_t rendercopy = igt_get_render_copyfunc(devid);
+ struct rect rect = get_rect(r, fb);
+ struct igt_buf src_buf, dst_buf;
+ struct intel_batchbuffer *batch;
+ struct drm_i915_gem_wait gem_wait;
+
+ bufmgr = drm_intel_bufmgr_gem_init(drm_fd, 4096);
+ igt_assert(bufmgr);
+
+ igt_skip_on(!rendercopy);
+
+ src = gem_handle_to_libdrm_bo(bufmgr, drm_fd, "",
+ rect_fbs[r].gem_handle);
+ igt_assert(src);
+ dst = gem_handle_to_libdrm_bo(bufmgr, drm_fd, "", fb->gem_handle);
+ igt_assert(dst);
+
+ src_buf.bo = src;
+ src_buf.stride = rect_fbs[r].stride;
+ src_buf.tiling = rect_fbs[r].tiling;
+ src_buf.size = rect_fbs[r].size;
+ dst_buf.bo = dst;
+ dst_buf.stride = fb->stride;
+ dst_buf.tiling = fb->tiling;
+ dst_buf.size = fb->size;
+
+ batch = intel_batchbuffer_alloc(bufmgr, devid);
+ igt_assert(batch);
+
+ rendercopy(batch, NULL, &src_buf, rect.x, rect.y, rect.w, rect.h,
+ &dst_buf, rect.x, rect.y);
+
+ gem_wait.flags = 0;
+ gem_wait.timeout_ns = 10000000000LL; /* 10s */
+ gem_wait.bo_handle = fb->gem_handle;
+ do_ioctl(drm_fd, DRM_IOCTL_I915_GEM_WAIT, &gem_wait);
+
+ intel_batchbuffer_free(batch);
+
+ drm_intel_bufmgr_destroy(bufmgr);
+}
+
+static void draw_rect(struct igt_fb *fb, enum draw_method method, int r)
+{
+ switch (method) {
+ case DRAW_MMAP_CPU:
+ draw_rect_mmap_cpu(fb, r);
+ break;
+ case DRAW_MMAP_GTT:
+ draw_rect_mmap_gtt(fb, r);
+ break;
+ case DRAW_PWRITE:
+ draw_rect_pwrite(fb, r);
+ break;
+ case DRAW_BLT:
+ draw_rect_blt(fb, r);
+ break;
+ case DRAW_RENDER:
+ draw_rect_render(fb, r);
+ break;
+ default:
+ igt_assert(false);
+ break;
+ }
+}
+
+static void get_base_crc_values(void)
+{
+ struct igt_fb blue;
+ int r, r_, rc;
+
+ kmstest_unset_all_crtcs(drm_fd, drm_res);
+
+ igt_create_fb(drm_fd, fbc_mode_params.mode->hdisplay,
+ fbc_mode_params.mode->vdisplay, DRM_FORMAT_XRGB8888,
+ I915_TILING_X, &blue);
+
+ for (r = 0; r < N_RECTS; r++)
+ igt_create_fb(drm_fd, fbc_mode_params.mode->hdisplay,
+ fbc_mode_params.mode->vdisplay,
+ DRM_FORMAT_XRGB8888, I915_TILING_X, &rect_fbs[r]);
+
+ /* Draw everything before setting the modes, in order to reduce the
+ * probability that some FBC bug will change the resulting CRC. */
+ fill_igt_fb(&blue, 0xFF);
+ for (r = 0; r < N_RECTS; r++)
+ fill_igt_fb(&rect_fbs[r], 0xFF);
+
+ /* We draw rect 0, then we draw rect 1 on top of the image that already
+ * contains rect 0, and so on. */
+ for (r = 0; r < N_RECTS; r++)
+ for (r_ = 0; r_ <= r; r_++)
+ draw_rect_pwrite(&rect_fbs[r], r_);
+
+ rc = drmModeSetCrtc(drm_fd, fbc_mode_params.crtc_id,
+ blue.fb_id, 0, 0, &fbc_mode_params.connector_id, 1,
+ fbc_mode_params.mode);
+ igt_assert(rc == 0);
+ igt_pipe_crc_collect_crc(pipe_crc, &blue_crc);
+
+ for (r = 0; r < N_RECTS; r++) {
+ rc = drmModeSetCrtc(drm_fd, fbc_mode_params.crtc_id,
+ rect_fbs[r].fb_id, 0, 0,
+ &fbc_mode_params.connector_id, 1,
+ fbc_mode_params.mode);
+ igt_assert(rc == 0);
+ igt_pipe_crc_collect_crc(pipe_crc, &rect_crc[r]);
+ }
+
+ igt_log(IGT_LOG_DEBUG, "Blue CRC: %s\n",
+ igt_crc_to_string(&blue_crc));
+ for (r = 0; r < N_RECTS; r++)
+ igt_log(IGT_LOG_DEBUG, "Rect %d CRC: %s\n", r,
+ igt_crc_to_string(&rect_crc[r]));
+
+ igt_remove_fb(drm_fd, &blue);
+}
+
+static void setup_environment(void)
+{
+ int i;
+
+ drm_fd = drm_open_any_master();
+ igt_require(drm_fd >= 0);
+
+ drm_res = drmModeGetResources(drm_fd);
+ igt_assert(drm_res->count_connectors <= MAX_CONNECTORS);
+
+ for (i = 0; i < drm_res->count_connectors; i++)
+ drm_connectors[i] = drmModeGetConnector(drm_fd,
+ drm_res->connectors[i]);
+
+ igt_require(init_modeset_cached_params());
+
+ kmstest_set_vt_graphics_mode();
+
+ fbc_fd = open("/sys/kernel/debug/dri/0/i915_fbc_status", O_RDONLY);
+ igt_assert(fbc_fd >= 0);
+
+ setup_last_action();
+ setup_compressing();
+
+ devid = intel_get_drm_devid(drm_fd);
+ gen = intel_gen(devid);
+
+ pipe_crc = igt_pipe_crc_new(0, INTEL_PIPE_CRC_SOURCE_AUTO);
+
+ get_base_crc_values();
+}
+
+static void teardown_environment(void)
+{
+ int i;
+
+ for (i = 0; i < N_RECTS; i++)
+ igt_remove_fb(drm_fd, &rect_fbs[i]);
+ igt_pipe_crc_free(pipe_crc);
+
+ close(fbc_fd);
+
+ if (non_fbc_mode_params.connector_id)
+ igt_remove_fb(drm_fd, &non_fbc_mode_params.fb);
+ igt_remove_fb(drm_fd, &fbc_mode_params.fb);
+
+ for (i = 0; i < drm_res->count_connectors; i++)
+ drmModeFreeConnector(drm_connectors[i]);
+ drmModeFreeResources(drm_res);
+ close(drm_fd);
+}
+
+static void wait_user(void)
+{
+ if (!manual)
+ return;
+
+ igt_log(IGT_LOG_INFO, "Press enter...\n");
+ while (getchar() != '\n')
+ ;
+}
+
+#define ASSERT_FBC_ENABLED (1 << 0)
+#define ASSERT_FBC_DISABLED (1 << 1)
+
+#define ASSERT_LAST_ACTION_CHANGED (1 << 2)
+#define ASSERT_NO_ACTION_CHANGE (1 << 3)
+
+#define ASSERT_CRC_SHIFT (5)
+#define ASSERT_CRC_BLUE (1 << 5)
+#define ASSERT_CRC_CUSTOM (1 << 6)
+#define ASSERT_CRC_RECT(r) (1 << (r + 7))
+
+#define do_crc_assertions(flags) do { \
+ int flags__ = (flags); \
+ int r_; \
+ \
+ igt_crc_t crc_, *wanted_crc_ = NULL; \
+ \
+ if (flags__ & ASSERT_CRC_BLUE) { \
+ wanted_crc_ = &blue_crc; \
+ } else if (flags__ & ASSERT_CRC_CUSTOM) { \
+ wanted_crc_ = custom_crc; \
+ } else { \
+ for (r_ = 0; r_ < N_RECTS; r_++) \
+ if (flags__ & ASSERT_CRC_RECT(r_)) \
+ wanted_crc_ = &rect_crc[r_]; \
+ } \
+ igt_assert(wanted_crc_); \
+ \
+ igt_pipe_crc_collect_crc(pipe_crc, &crc_); \
+ \
+ igt_log(IGT_LOG_DEBUG, "Calculated CRC: %s\n", \
+ igt_crc_to_string(&crc_)); \
+ \
+ if (check_crc) \
+ igt_assert(igt_crc_equal(&crc_, wanted_crc_)); \
+} while (0)
+
+#define do_assertions(flags) do { \
+ int flags_ = (flags); \
+ \
+ wait_user(); \
+ \
+ /* Check the CRC to make sure the response is immediate. */ \
+ if (flags_ >> ASSERT_CRC_SHIFT) \
+ do_crc_assertions(flags_); \
+ \
+ if (check_status) { \
+ if (flags_ & ASSERT_FBC_ENABLED) { \
+ igt_assert(wait_for_status(FBC_ENABLED)); \
+ \
+ if (supports_compressing && check_compression) \
+ igt_assert(wait_for_compression()); \
+ } else if (flags_ & ASSERT_FBC_DISABLED) { \
+ igt_assert(wait_for_status(FBC_DISABLED)); \
+ } \
+ } else { \
+ /* Make sure we settle before continuing. */ \
+ sleep(1); \
+ } \
+ \
+ /* Check CRC again to make sure the compressed screen is ok. */ \
+ if (flags_ >> ASSERT_CRC_SHIFT) \
+ do_crc_assertions(flags_); \
+ \
+ if (supports_last_action && check_last_action) { \
+ if (flags_ & ASSERT_LAST_ACTION_CHANGED) \
+ igt_assert(last_action_changed()); \
+ else if (flags_ & ASSERT_NO_ACTION_CHANGE) \
+ igt_assert(!last_action_changed()); \
+ } \
+ \
+ wait_user(); \
+} while (0)
+
+static void enable_fbc_screen_and_wait(void)
+{
+ /* Repaint it every time because the front-buffer rendering tests may
+ * have painted it in other tests. */
+ fill_igt_fb(&fbc_mode_params.fb, 0xFF);
+ set_mode_for_params(&fbc_mode_params);
+
+ do_assertions(ASSERT_FBC_ENABLED);
+ update_last_action();
+ do_assertions(ASSERT_CRC_BLUE);
+}
+
+static void enable_non_fbc_screen_and_wait(void)
+{
+ set_mode_for_params(&non_fbc_mode_params);
+ do_assertions(ASSERT_FBC_ENABLED);
+}
+
+static void rte_subtest(bool two_pipes)
+{
+ kmstest_unset_all_crtcs(drm_fd, drm_res);
+ do_assertions(ASSERT_FBC_DISABLED);
+
+ enable_fbc_screen_and_wait();
+
+ if (two_pipes) {
+ enable_non_fbc_screen_and_wait();
+ do_assertions(ASSERT_CRC_BLUE |
+ ASSERT_NO_ACTION_CHANGE);
+ }
+}
+
+static void frontbuffer_subtest(enum draw_method method)
+{
+ struct igt_fb *fb = &fbc_mode_params.fb;
+ int r;
+ int assertions;
+
+ kmstest_unset_all_crtcs(drm_fd, drm_res);
+ enable_fbc_screen_and_wait();
+
+ for (r = 0; r < N_RECTS; r++) {
+ draw_rect(fb, method, r);
+
+ assertions = ASSERT_FBC_ENABLED |
+ ASSERT_CRC_RECT(r);
+ if (method != DRAW_MMAP_GTT)
+ assertions |= ASSERT_LAST_ACTION_CHANGED;
+
+ do_assertions(assertions);
+ }
+}
+
+static void flip_subtest(void)
+{
+ int r, rc;
+
+ kmstest_unset_all_crtcs(drm_fd, drm_res);
+ enable_fbc_screen_and_wait();
+
+ for (r = 0; r < N_RECTS; r++) {
+ rc = drmModePageFlip(drm_fd, fbc_mode_params.crtc_id,
+ rect_fbs[r].fb_id, 0, NULL);
+ igt_assert(rc == 0);
+
+ do_assertions(ASSERT_FBC_ENABLED |
+ ASSERT_CRC_RECT(r) |
+ ASSERT_LAST_ACTION_CHANGED);
+ }
+}
+
+static void backbuffer_subtest(enum draw_method method)
+{
+ struct igt_fb back_fb;
+ int r;
+
+ igt_create_fb(drm_fd, 1024, 1024, DRM_FORMAT_XRGB8888, I915_TILING_X,
+ &back_fb);
+ fill_igt_fb(&back_fb, 0xFF0000);
+
+ kmstest_unset_all_crtcs(drm_fd, drm_res);
+ enable_fbc_screen_and_wait();
+
+ for (r = 0; r < N_RECTS; r++) {
+ draw_rect(&back_fb, method, r);
+
+ do_assertions(ASSERT_FBC_ENABLED |
+ ASSERT_CRC_BLUE |
+ ASSERT_NO_ACTION_CHANGE);
+ }
+
+ igt_remove_fb(drm_fd, &back_fb);
+}
+
+static void cursor_subtest(void)
+{
+ int i, rc;
+ int ntmp = 2;
+ struct igt_fb cursor, tmp[ntmp];
+ igt_crc_t tmp_crc[ntmp];
+
+ kmstest_unset_all_crtcs(drm_fd, drm_res);
+
+ /* First, draw the "cursors" using software and calculate the CRCs. */
+ for (i = 0; i < ntmp; i++) {
+ igt_create_fb(drm_fd, fbc_mode_params.mode->hdisplay,
+ fbc_mode_params.mode->vdisplay,
+ DRM_FORMAT_XRGB8888, I915_TILING_X, &tmp[i]);
+ fill_igt_fb(&tmp[i], 0xFF);
+ }
+
+ draw_rect_pwrite(&tmp[0], 4);
+ draw_rect_pwrite(&tmp[1], 5);
+
+ for (i = 0; i < ntmp; i++) {
+ rc = drmModeSetCrtc(drm_fd, fbc_mode_params.crtc_id,
+ tmp[i].fb_id,
+ 0, 0, &fbc_mode_params.connector_id, 1,
+ fbc_mode_params.mode);
+ igt_assert(rc == 0);
+ igt_pipe_crc_collect_crc(pipe_crc, &tmp_crc[i]);
+ }
+
+ for (i = 0; i < ntmp; i++)
+ igt_remove_fb(drm_fd, &tmp[i]);
+
+
+ /* Now that we have the CRCs, we can begin the test. */
+ igt_create_fb(drm_fd, 64, 64, DRM_FORMAT_ARGB8888, I915_TILING_NONE,
+ &cursor);
+ fill_igt_fb(&cursor, 0xFFFFFF00);
+
+ kmstest_unset_all_crtcs(drm_fd, drm_res);
+ enable_fbc_screen_and_wait();
+
+ rc = drmModeSetCursor(drm_fd, fbc_mode_params.crtc_id, 0, 0, 0);
+ igt_assert(rc == 0);
+ do_assertions(ASSERT_FBC_ENABLED |
+ ASSERT_CRC_BLUE |
+ ASSERT_NO_ACTION_CHANGE);
+
+ rc = drmModeMoveCursor(drm_fd, fbc_mode_params.crtc_id, 0, 0);
+ igt_assert(rc == 0);
+ do_assertions(ASSERT_FBC_ENABLED |
+ ASSERT_CRC_BLUE |
+ ASSERT_NO_ACTION_CHANGE);
+
+ rc = drmModeSetCursor(drm_fd, fbc_mode_params.crtc_id,
+ cursor.gem_handle, cursor.width, cursor.height);
+ igt_assert(rc == 0);
+ custom_crc = &tmp_crc[0];
+ do_assertions(ASSERT_FBC_ENABLED |
+ ASSERT_CRC_CUSTOM |
+ ASSERT_NO_ACTION_CHANGE);
+
+ rc = drmModeMoveCursor(drm_fd, fbc_mode_params.crtc_id, 0, 0);
+ igt_assert(rc == 0);
+ custom_crc = &tmp_crc[0];
+ do_assertions(ASSERT_FBC_ENABLED |
+ ASSERT_CRC_CUSTOM |
+ ASSERT_NO_ACTION_CHANGE);
+
+ rc = drmModeMoveCursor(drm_fd, fbc_mode_params.crtc_id, 64, 64);
+ igt_assert(rc == 0);
+ custom_crc = &tmp_crc[1];
+ do_assertions(ASSERT_FBC_ENABLED |
+ ASSERT_CRC_CUSTOM |
+ ASSERT_NO_ACTION_CHANGE);
+
+ rc = drmModeSetCursor(drm_fd, fbc_mode_params.crtc_id, 0, 0, 0);
+ igt_assert(rc == 0);
+ do_assertions(ASSERT_FBC_ENABLED |
+ ASSERT_CRC_BLUE |
+ ASSERT_NO_ACTION_CHANGE);
+
+ igt_remove_fb(drm_fd, &cursor);
+}
+
+static void plane_subtest(void)
+{
+ int i, rc;
+ drmModePlaneResPtr planes;
+ uint32_t plane_id = 0;
+ struct igt_fb plane_fb1, plane_fb2;
+ struct rect rect = get_rect(0, &fbc_mode_params.fb);
+
+ planes = drmModeGetPlaneResources(drm_fd);
+ for (i = 0; i < planes->count_planes && plane_id == 0; i++) {
+ drmModePlanePtr plane;
+
+ plane = drmModeGetPlane(drm_fd, planes->planes[i]);
+ igt_assert(plane);
+
+ /* We always use the first CRTC for FBC, so we can use 0x1 here
+ * as the index. */
+ if (plane->possible_crtcs & 0x1)
+ plane_id = plane->plane_id;
+
+ drmModeFreePlane(plane);
+ }
+ drmModeFreePlaneResources(planes);
+ igt_assert(plane_id);
+
+ igt_create_fb(drm_fd, rect.w, rect.h, DRM_FORMAT_XRGB8888,
+ I915_TILING_X, &plane_fb1);
+ fill_igt_fb(&plane_fb1, rect.color);
+
+ igt_create_fb(drm_fd, fbc_mode_params.fb.width,
+ fbc_mode_params.fb.height, DRM_FORMAT_XRGB8888,
+ I915_TILING_X, &plane_fb2);
+ fill_igt_fb(&plane_fb2, 0xFF);
+ draw_rect_pwrite(&plane_fb2, 0);
+
+ kmstest_unset_all_crtcs(drm_fd, drm_res);
+
+ enable_fbc_screen_and_wait();
+
+ rc = drmModeSetPlane(drm_fd, plane_id, fbc_mode_params.crtc_id,
+ plane_fb1.fb_id, 0, rect.x, rect.y,
+ plane_fb1.width, plane_fb1.height,
+ rect.x << 16, rect.y << 16,
+ plane_fb1.width << 16, plane_fb1.height << 16);
+ igt_assert(rc == 0);
+ do_assertions(ASSERT_FBC_ENABLED |
+ ASSERT_CRC_RECT(0) |
+ ASSERT_NO_ACTION_CHANGE);
+
+ rc = drmModeSetPlane(drm_fd, plane_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ igt_assert(rc == 0);
+ do_assertions(ASSERT_FBC_ENABLED |
+ ASSERT_CRC_BLUE |
+ ASSERT_NO_ACTION_CHANGE);
+
+ /* A full-screen plane: FBC is useless here. */
+ rc = drmModeSetPlane(drm_fd, plane_id, fbc_mode_params.crtc_id,
+ plane_fb2.fb_id, 0, 0, 0,
+ plane_fb2.width, plane_fb2.height,
+ 0 << 16, 0 << 16,
+ plane_fb2.width << 16, plane_fb2.height << 16);
+ igt_assert(rc == 0);
+ do_assertions(ASSERT_FBC_DISABLED |
+ ASSERT_CRC_RECT(0) |
+ ASSERT_LAST_ACTION_CHANGED);
+
+ rc = drmModeSetPlane(drm_fd, plane_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ igt_assert(rc == 0);
+ do_assertions(ASSERT_FBC_ENABLED |
+ ASSERT_CRC_BLUE |
+ ASSERT_LAST_ACTION_CHANGED);
+
+ igt_remove_fb(drm_fd, &plane_fb1);
+ igt_remove_fb(drm_fd, &plane_fb2);
+}
+
+static int opt_handler(int opt, int opt_index)
+{
+ switch (opt) {
+ case 's':
+ check_status = false;
+ break;
+ case 'c':
+ check_crc = false;
+ break;
+ case 'o':
+ check_compression = false;
+ break;
+ case 'a':
+ check_last_action = false;
+ break;
+ case 'm':
+ manual = true;
+ break;
+ default:
+ igt_assert(false);
+ }
+
+ return 0;
+}
+
+const char *help_str =
+" --no-status-check Don't check for enable/disable status\n"
+" --no-crc-check Don't check for CRC values\n"
+" --no-compression-check Don't check for the compression status\n"
+" --no-action-check Don't check for last action\n"
+" --manual Stop on each step so you can check the screen\n";
+
+int main(int argc, char *argv[])
+{
+ int i;
+ struct option long_options[] = {
+ { "no-status-check", 0, 0, 's'},
+ { "no-crc-check", 0, 0, 'c'},
+ { "no-compression-check", 0, 0, 'o'},
+ { "no-action-check", 0, 0, 'a'},
+ { "manual", 0, 0, 'm'},
+ { 0, 0, 0, 0 }
+ };
+
+ igt_subtest_init_parse_opts(argc, argv, "", long_options, help_str,
+ opt_handler);
+
+ igt_fixture
+ setup_environment();
+
+ igt_subtest("rte")
+ rte_subtest(false);
+
+ /* Draw on the frontbuffer: make sure contents are visible. */
+ for (i = 0; i < DRAW_METHOD_COUNT; i++)
+ igt_subtest_f("frontbuffer-%s", draw_method_names[i])
+ frontbuffer_subtest(i);
+
+ igt_subtest("flip")
+ flip_subtest();
+
+ /* Draw on a backbuffer: make sure FBC stays enabled. */
+ for (i = 0; i < DRAW_METHOD_COUNT; i++)
+ igt_subtest_f("backbuffer-%s", draw_method_names[i])
+ backbuffer_subtest(i);
+
+ igt_subtest("cursor")
+ cursor_subtest();
+
+ igt_subtest("plane")
+ plane_subtest();
+
+ /* TODO: improve the multi-pipe tests after this one stops failing on
+ * the upstream trees. */
+ igt_subtest("two-pipes-rte") {
+ igt_require(non_fbc_mode_params.connector_id);
+ rte_subtest(true);
+ }
+
+ igt_fixture
+ teardown_environment();
+
+ igt_exit();
+}