On 25 May 2015 at 22:40, Paulo Zanoni <przanoni@xxxxxxxxx> wrote: > From: Paulo Zanoni <paulo.r.zanoni@xxxxxxxxx> > > This is a new test that should exercise the frontbuffer tracking > feature of the Kernel in a number of different ways. We use different > drawing methods, we use the primary, cursor and sprite planes, we can > test both on single and dual pipes, also on buffers not associated > with any CRTCs, etc. > > We currently have assertions for both FBC and PSR, and we also have a > "nop" test mode that should disable both FBC and PSR, and can be > used for debugging. It would be good to have this information in the test as a comment somewhere as well as adding a short description for the IGT_TEST_DESCRIPTION macro. Also, there are a few suggestions from lib/igt.cocci that might be worth implementing. > > This test is also capable of testing both FBC and PSR even if they are > disabled by default on the Kernel: the test knows how to change the > i915.ko parameters and then set them back after testing. > > I am getting a small number of failures when I run this test, which > means we have some work to do on the Kernel. > > I also still have a small list of additional subtests that I plan to > add to this test, and those tests are documented on the main function. > > Signed-off-by: Paulo Zanoni <paulo.r.zanoni@xxxxxxxxx> > --- > tests/.gitignore | 1 + > tests/Makefile.sources | 1 + > tests/kms_frontbuffer_tracking.c | 1825 ++++++++++++++++++++++++++++++++++++++ > 3 files changed, 1827 insertions(+) > create mode 100644 tests/kms_frontbuffer_tracking.c > > Some interesting details: > > On a tree from 2015, May 06, I get 18 failures and 357 successes. The > only FBC failures I get are from the MMAP_WC draw method, which is > maybe lacking proper frontbuffer tracking support on the Kernel. The > other 16 failures are for PSR, most of them with GTT MMAPs. > > But if I use a tree from 2015, May 25, every single PSR test fails. We > need to investigate that. Maybe if I had finished this earlier we > would have an automated bisect. This also highlights the importance of > testing stuff even when they are disabled by default! I plan to patch > the other tests to do the same thing. > > I am also seeing some FBC failures that happen right after booting. It > seems that the very first tests fail until I run a test that uses the > render ring. I'll have to investigate this. > > I am also seeing some occasional corruptions on my eDP panel, but on > these cases both the pipe and sink CRC tests succeed! Maybe this is > some weird panel malfunction caused by the fact that we're doing tons > and tons of modesets on the panel. > > diff --git a/tests/.gitignore b/tests/.gitignore > index a3f3143..dcead2c 100644 > --- a/tests/.gitignore > +++ b/tests/.gitignore > @@ -134,6 +134,7 @@ kms_flip > kms_flip_event_leak > kms_flip_tiling > kms_force_connector > +kms_frontbuffer_tracking > kms_legacy_colorkey > kms_mmio_vs_cs_flip > kms_pipe_b_c_ivb > diff --git a/tests/Makefile.sources b/tests/Makefile.sources > index 994c31b..3c93337 100644 > --- a/tests/Makefile.sources > +++ b/tests/Makefile.sources > @@ -66,6 +66,7 @@ TESTS_progs_M = \ > kms_flip \ > kms_flip_event_leak \ > kms_flip_tiling \ > + kms_frontbuffer_tracking \ > kms_legacy_colorkey \ > kms_mmio_vs_cs_flip \ > kms_pipe_b_c_ivb \ > diff --git a/tests/kms_frontbuffer_tracking.c b/tests/kms_frontbuffer_tracking.c > new file mode 100644 > index 0000000..f6554f9 > --- /dev/null > +++ b/tests/kms_frontbuffer_tracking.c > @@ -0,0 +1,1825 @@ > +/* > + * Copyright © 2015 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@xxxxxxxxx> > + * > + */ > + > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <fcntl.h> > + > +#include "drmtest.h" > +#include "igt_aux.h" > +#include "igt_draw.h" > +#include "igt_kms.h" > +#include "igt_debugfs.h" > +#include "intel_chipset.h" > +#include "ioctl_wrappers.h" > + > +#define FBC_PARAM_PATH "/sys/module/i915/parameters/enable_fbc" > +#define PSR_PARAM_PATH "/sys/module/i915/parameters/enable_psr" > + > +struct test_mode { > + enum { > + PIPE_SINGLE = 0, > + PIPE_DUAL, > + PIPE_COUNT, > + } pipes; > + > + enum { > + SCREEN_PRIM = 0, > + SCREEN_SCND, > + SCREEN_OFFSCREEN, > + SCREEN_COUNT, > + } screen; > + > + enum { > + PLANE_PRI = 0, > + PLANE_CUR, > + PLANE_SPR, > + PLANE_COUNT, > + } plane; > + > + enum { > + FEATURE_NONE = 0, > + FEATURE_FBC, > + FEATURE_PSR, > + FEATURE_COUNT, > + } feature; > + > + enum igt_draw_method method; > +}; > + > +enum feature_status { > + ENABLED, > + DISABLED, > +}; > + > +struct rect { > + int x; > + int y; > + int w; > + int h; > + uint32_t color; > +}; > + > +#define MAX_CONNECTORS 32 > +struct { > + int fd; > + drmModeResPtr res; > + drmModeConnectorPtr connectors[MAX_CONNECTORS]; > + drmModePlaneResPtr planes; > + drm_intel_bufmgr *bufmgr; > +} drm; > + > +struct { > + int fd; > + > + char param_original_value[16]; > + > + bool supports_compressing; > + bool supports_last_action; > + > + struct timespec last_action; > +} fbc = { > + .fd = -1, > + .supports_last_action = false, > + .supports_compressing = false, > +}; > + > +struct { > + int fd; > + bool can_test; > + > + char param_original_value[16]; > +} psr = { > + .fd = -1, > + .can_test = false, > +}; > + > + > +#define SINK_CRC_SIZE 12 > +typedef struct { > + char data[SINK_CRC_SIZE]; > +} sink_crc_t; > + > +struct both_crcs { > + igt_crc_t pipe; > + sink_crc_t sink; > +}; > + > +igt_pipe_crc_t *pipe_crc; > +struct both_crcs blue_crc; > +struct both_crcs *wanted_crc; > + > +struct { > + int fd; > +} sink_crc; > + > +struct draw_pattern_info { > + bool initialized; > + bool frames_stack; > + int n_rects; > + struct both_crcs *crcs; > + struct rect (*get_rect)(struct igt_fb *fb, int r); > +}; > + > +/* Draw big rectangles on the screen. */ > +struct draw_pattern_info pattern1; > +/* 64x64 rectangles at x:0,y:0, just so we can draw on the cursor and sprite. */ > +struct draw_pattern_info pattern2; > +/* 64x64 rectangles at different positions, same color, for the move test. */ > +struct draw_pattern_info pattern3; > +/* Just a fullscreen green square. */ > +struct draw_pattern_info pattern4; > + > +/* Command line parameters. */ > +struct { > + bool check_status; > + bool check_crc; > + bool fbc_check_compression; > + bool fbc_check_last_action; > + bool no_edp; > + bool small_modes; > + int step; > + int only_feature; > + int only_pipes; > +} opt = { > + .check_status = true, > + .check_crc = true, > + .fbc_check_compression = true, > + .fbc_check_last_action = true, > + .no_edp = false, > + .small_modes = false, > + .step = 0, > + .only_feature = FEATURE_COUNT, > + .only_pipes = PIPE_COUNT, > +}; > + > +struct modeset_params { > + uint32_t crtc_id; > + uint32_t connector_id; > + uint32_t sprite_id; > + drmModeModeInfoPtr mode; > + struct igt_fb fb; > + struct igt_fb cursor; > + struct igt_fb sprite; > +}; > + > +struct modeset_params prim_mode_params; > +struct modeset_params scnd_mode_params; > +struct igt_fb offscreen_fb; > + > +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; > +} > + > +static drmModeConnectorPtr get_connector(uint32_t id) > +{ > + int i; > + > + for (i = 0; i < drm.res->count_connectors; i++) > + if (drm.res->connectors[i] == id) > + return drm.connectors[i]; > + > + igt_assert(false); igt_assert_f() could be used here to provide a more descriptive error message. > +} > + > +static void print_mode_info(const char *screen, struct modeset_params *params) > +{ > + drmModeConnectorPtr c = get_connector(params->connector_id); > + > + igt_info("%s screen: %s %s\n", > + screen, > + kmstest_connector_type_str(c->connector_type), > + params->mode->name); > +} > + > +static void init_mode_params(struct modeset_params *params, uint32_t crtc_id, > + int crtc_index, uint32_t connector_id, > + drmModeModeInfoPtr mode) > +{ > + uint32_t plane_id = 0; > + int i; > + > + igt_create_fb(drm.fd, mode->hdisplay, mode->vdisplay, > + DRM_FORMAT_XRGB8888, LOCAL_I915_FORMAT_MOD_X_TILED, > + ¶ms->fb); > + igt_create_fb(drm.fd, 64, 64, DRM_FORMAT_ARGB8888, > + LOCAL_DRM_FORMAT_MOD_NONE, ¶ms->cursor); > + igt_create_fb(drm.fd, 64, 64, DRM_FORMAT_XRGB8888, > + LOCAL_I915_FORMAT_MOD_X_TILED, ¶ms->sprite); > + > + for (i = 0; i < drm.planes->count_planes && plane_id == 0; i++) { > + drmModePlanePtr plane; > + > + plane = drmModeGetPlane(drm.fd, drm.planes->planes[i]); > + igt_assert(plane); > + > + if (plane->possible_crtcs & (1 << crtc_index)) > + plane_id = plane->plane_id; > + > + drmModeFreePlane(plane); > + } > + igt_assert(plane_id); > + > + params->crtc_id = crtc_id; > + params->connector_id = connector_id; > + params->mode = mode; > + params->sprite_id = plane_id; > +} > + > +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", > +}; > + > +static bool connector_get_mode(drmModeConnectorPtr c, drmModeModeInfoPtr *mode) > +{ > + *mode = NULL; > + > + if (c->connection != DRM_MODE_CONNECTED || !c->count_modes) > + return false; > + > + if (c->connector_type == DRM_MODE_CONNECTOR_eDP && opt.no_edp) > + return false; > + > + if (opt.small_modes) > + *mode = get_connector_smallest_mode(c); > + else > + *mode = &c->modes[0]; > + > + /* 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; > + > + return true; > +} > + > +static bool init_modeset_cached_params(void) > +{ > + int i; > + uint32_t prim_connector_id = 0, scnd_connector_id = 0; > + drmModeModeInfoPtr prim_mode = NULL, scnd_mode = NULL; > + drmModeModeInfoPtr tmp_mode; > + > + /* First, try to find an eDP monitor since it's the only possible type > + * for PSR. */ > + for (i = 0; i < drm.res->count_connectors; i++) { > + if (drm.connectors[i]->connector_type != DRM_MODE_CONNECTOR_eDP) > + continue; > + > + if (connector_get_mode(drm.connectors[i], &tmp_mode)) { > + prim_connector_id = drm.res->connectors[i]; > + prim_mode = tmp_mode; > + } > + } > + for (i = 0; i < drm.res->count_connectors; i++) { > + /* Don't pick again what we just selected on the above loop. */ > + if (drm.res->connectors[i] == prim_connector_id) > + continue; > + > + if (connector_get_mode(drm.connectors[i], &tmp_mode)) { > + if (!prim_connector_id) { > + prim_connector_id = drm.res->connectors[i]; > + prim_mode = tmp_mode; > + } else if (!scnd_connector_id) { > + scnd_connector_id = drm.res->connectors[i]; > + scnd_mode = tmp_mode; > + break; > + } > + } > + } > + > + if (!prim_connector_id) > + return false; > + > + init_mode_params(&prim_mode_params, drm.res->crtcs[0], 0, > + prim_connector_id, prim_mode); > + print_mode_info("Primary", &prim_mode_params); > + > + if (!scnd_connector_id) { > + scnd_mode_params.connector_id = 0; > + return true; > + } > + > + igt_assert(drm.res->count_crtcs >= 2); > + init_mode_params(&scnd_mode_params, drm.res->crtcs[1], 1, > + scnd_connector_id, scnd_mode); > + print_mode_info("Secondary", &scnd_mode_params); > + > + 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, > + ¶ms->connector_id, 1, params->mode); > + return (rc == 0); > +} > + > +#define DEBUGFS_MSG_SIZE 256 > + > +static void get_debugfs_string(int fd, char *buf) > +{ > + ssize_t n_read; > + > + lseek(fd, 0, SEEK_SET); > + > + n_read = read(fd, buf, DEBUGFS_MSG_SIZE -1); > + igt_assert(n_read >= 0); > + buf[n_read] = '\0'; > +} > + > +static enum feature_status fbc_get_status(void) > +{ > + char buf[DEBUGFS_MSG_SIZE]; > + > + get_debugfs_string(fbc.fd, buf); > + > + if (strstr(buf, "FBC enabled\n")) > + return ENABLED; > + else > + return DISABLED; > +} > + > +static enum feature_status psr_get_status(void) > +{ > + char buf[DEBUGFS_MSG_SIZE]; > + > + get_debugfs_string(psr.fd, buf); > + > + if (strstr(buf, "\nActive: yes\n")) > + return ENABLED; > + else > + return DISABLED; > +} > + > +static struct timespec fbc_get_last_action(void) > +{ > + struct timespec ret = { 0, 0 }; > + char buf[DEBUGFS_MSG_SIZE]; > + char *action; > + ssize_t n_read; > + > + get_debugfs_string(fbc.fd, buf); > + > + action = strstr(buf, "\nLast 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 fbc_last_action_changed(void) > +{ > + struct timespec t_new, t_old; > + > + t_old = fbc.last_action; > + t_new = fbc_get_last_action(); > + > + fbc.last_action = t_new; > + > +#if 0 > + igt_info("old: %ld.%ld\n", t_old.tv_sec, t_old.tv_nsec); > + igt_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 fbc_update_last_action(void) > +{ > + if (!fbc.supports_last_action) > + return; > + > + fbc.last_action = fbc_get_last_action(); > + > +#if 0 > + igt_info("Last action: %ld.%ld\n", > + fbc.last_action.tv_sec, fbc.last_action.tv_nsec); > +#endif > +} > + > +static void fbc_setup_last_action(void) > +{ > + ssize_t n_read; > + char buf[DEBUGFS_MSG_SIZE]; > + char *action; > + > + get_debugfs_string(fbc.fd, buf); > + > + action = strstr(buf, "\nLast action:"); > + if (!action) { > + igt_info("FBC last action not supported\n"); > + return; > + } > + > + fbc.supports_last_action = true; > + > + n_read = sscanf(action, "Last action: %ld.%ld", > + &fbc.last_action.tv_sec, &fbc.last_action.tv_nsec); > + igt_assert(n_read == 2); > +} > + > +static bool fbc_is_compressing(void) > +{ > + char buf[DEBUGFS_MSG_SIZE]; > + > + get_debugfs_string(fbc.fd, buf); > + return strstr(buf, "\nCompressing: yes\n") != NULL; > +} > + > +static bool fbc_wait_for_compression(void) > +{ > + return igt_wait(fbc_is_compressing(), 5000, 1); > +} > + > +static void fbc_setup_compressing(void) > +{ > + char buf[DEBUGFS_MSG_SIZE]; > + > + get_debugfs_string(fbc.fd, buf); > + > + if (strstr(buf, "\nCompressing:")) > + fbc.supports_compressing = true; > + else > + igt_info("FBC compression information not supported\n"); > +} > + > +static bool fbc_wait_for_status(enum feature_status status) > +{ > + return igt_wait(fbc_get_status() == status, 5000, 1); > +} > + > +static bool psr_wait_for_status(enum feature_status status) > +{ > + return igt_wait(psr_get_status() == status, 5000, 1); > +} > + > +static void set_param(const char *path, bool enable) > +{ > + int fd; > + const char *str; > + > + fd = open(path, O_RDWR); > + igt_assert(fd >= 0); > + > + str = enable ? "1\n" : "0\n"; > + igt_assert(write(fd, str, 2) == 2); > + > + igt_assert(close(fd) == 0); > +} > +#define fbc_enable() set_param(FBC_PARAM_PATH, true) > +#define fbc_disable() set_param(FBC_PARAM_PATH, false) > +#define psr_enable() set_param(PSR_PARAM_PATH, true) > +#define psr_disable() set_param(PSR_PARAM_PATH, false) > + > +static void save_param(const char *file_path, char *param_value) > +{ > + int fd; > + ssize_t n; > + > + fd = open(file_path, O_RDWR); > + igt_assert(fd >= 0); > + > + n = read(fd, param_value, 15); > + igt_assert(n > 0); > + param_value[n] = '\0'; > + igt_assert(close(fd) == 0); > +} > + > +static void get_sink_crc(sink_crc_t *crc) > +{ > + lseek(sink_crc.fd, 0, SEEK_SET); > + > + igt_assert(read(sink_crc.fd, crc->data, SINK_CRC_SIZE) == > + SINK_CRC_SIZE); > +} > + > +static bool sink_crc_equal(sink_crc_t *a, sink_crc_t *b) > +{ > + return (memcmp(a->data, b->data, SINK_CRC_SIZE) == 0); > +} > + > +#define assert_sink_crc_equal(a, b) igt_assert(sink_crc_equal(a, b)) > + > +static struct rect pat1_get_rect(struct igt_fb *fb, int r) > +{ > + 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: > + rect.x = fb->width - 64; > + rect.y = fb->height - 64; > + rect.w = 64; > + rect.h = 64; > + rect.color = 0xFFFFFF; > + break; > + default: > + igt_assert(0); It might be worth adding an igt_assert_not_reached function (or similar) for these, which could print a better error message. > + } > + > + return rect; > +} > + > +static struct rect pat2_get_rect(struct igt_fb *fb, int r) > +{ > + struct rect rect; > + > + rect.x = 0; > + rect.y = 0; > + rect.w = 64; > + rect.h = 64; > + > + switch (r) { > + case 0: > + rect.color = 0xFF00FF00; > + break; > + case 1: > + rect.w = 32; > + rect.h = 32; > + rect.color = 0xFFFF0000; > + break; > + case 2: > + rect.x = 32; > + rect.y = 32; > + rect.w = 32; > + rect.h = 32; > + rect.color = 0xFFFF00FF; > + break; > + case 3: > + rect.x = 16; > + rect.y = 16; > + rect.w = 32; > + rect.h = 32; > + rect.color = 0xFF00FFFF; > + break; > + case 4: > + rect.color = 0xFFFFFF00; > + break; > + default: > + igt_assert(0); > + } > + > + return rect; > +} > + > +static struct rect pat3_get_rect(struct igt_fb *fb, int r) > +{ > + struct rect rect; > + > + rect.w = 64; > + rect.h = 64; > + rect.color = 0xFF00FF00; > + > + switch (r) { > + case 0: > + rect.x = 0; > + rect.y = 0; > + break; > + case 1: > + rect.x = 64; > + rect.y = 64; > + break; > + case 2: > + rect.x = 1; > + rect.y = 1; > + break; > + case 3: > + rect.x = fb->width - 64; > + rect.y = fb->height - 64; > + break; > + case 4: > + rect.x = fb->width / 2 - 32; > + rect.y = fb->height / 2 - 32; > + break; > + default: > + igt_assert(0); > + } > + > + return rect; > +} > + > +static struct rect pat4_get_rect(struct igt_fb *fb, int r) > +{ > + struct rect rect; > + > + igt_assert(r == 0); > + > + rect.x = 0; > + rect.y = 0; > + rect.w = fb->width; > + rect.h = fb->height; > + rect.color = 0xFF00FF00; > + > + return rect; > +} > + > +static void draw_rect(struct draw_pattern_info *pattern, struct igt_fb *fb, > + enum igt_draw_method method, int r) > +{ > + struct rect rect = pattern->get_rect(fb, r); > + > + igt_draw_rect_fb(drm.fd, drm.bufmgr, NULL, fb, method, rect.x, rect.y, > + rect.w, rect.h, rect.color); > +} > + > +static void unset_all_crtcs(void) > +{ > + int i, rc; > + > + for (i = 0; i < drm.res->count_crtcs; i++) { > + rc = drmModeSetCrtc(drm.fd, drm.res->crtcs[i], -1, 0, 0, NULL, > + 0, NULL); > + igt_assert(rc == 0); > + > + rc = drmModeSetCursor(drm.fd, drm.res->crtcs[i], 0, 0, 0); > + igt_assert(rc == 0); > + } > + > + for (i = 0; i < drm.planes->count_planes; i++) { > + rc = drmModeSetPlane(drm.fd, drm.planes->planes[i], 0, 0, 0, 0, > + 0, 0, 0, 0, 0, 0, 0); > + igt_assert(rc == 0); > + } > +} > + > +static void disable_features(void) > +{ > + fbc_disable(); > + psr_disable(); > +} > + > +static void print_crc(const char *str, struct both_crcs *crc) > +{ > + int i; > + char *pipe_str; > + > + pipe_str = igt_crc_to_string(&crc->pipe); > + > + igt_debug("%s pipe:[%s] sink:[", str, pipe_str); > + for (i = 0; i < SINK_CRC_SIZE; i++) > + igt_debug("%c", crc->sink.data[i]); > + igt_debug("]\n"); > + > + free(pipe_str); > +} > + > +static void collect_crcs(struct both_crcs *crcs) > +{ > + drmModeConnectorPtr c; > + > + igt_pipe_crc_collect_crc(pipe_crc, &crcs->pipe); > + > + c = get_connector(prim_mode_params.connector_id); > + if (c->connector_type == DRM_MODE_CONNECTOR_eDP) > + get_sink_crc(&crcs->sink); > + else > + memcpy(&crcs->sink, "unsupported!", SINK_CRC_SIZE); > +} > + > +static void init_blue_crc(void) > +{ > + struct igt_fb blue; > + int rc; > + > + disable_features(); > + unset_all_crtcs(); > + > + igt_create_fb(drm.fd, prim_mode_params.mode->hdisplay, > + prim_mode_params.mode->vdisplay, DRM_FORMAT_XRGB8888, > + LOCAL_I915_FORMAT_MOD_X_TILED, &blue); > + > + igt_draw_fill_fb(drm.fd, &blue, 0xFF); > + > + rc = drmModeSetCrtc(drm.fd, prim_mode_params.crtc_id, > + blue.fb_id, 0, 0, &prim_mode_params.connector_id, 1, > + prim_mode_params.mode); > + igt_assert(rc == 0); > + collect_crcs(&blue_crc); > + > + print_crc("Blue CRC: ", &blue_crc); > + > + igt_remove_fb(drm.fd, &blue); > +} > + > +static void init_crcs(struct draw_pattern_info *pattern) > +{ > + int r, r_, rc; > + struct igt_fb fbs[pattern->n_rects]; > + > + if (pattern->initialized) > + return; > + > + pattern->crcs = calloc(pattern->n_rects, sizeof(*(pattern->crcs))); > + > + for (r = 0; r < pattern->n_rects; r++) > + igt_create_fb(drm.fd, prim_mode_params.mode->hdisplay, > + prim_mode_params.mode->vdisplay, > + DRM_FORMAT_XRGB8888, > + LOCAL_I915_FORMAT_MOD_X_TILED, &fbs[r]); > + > + for (r = 0; r < pattern->n_rects; r++) > + igt_draw_fill_fb(drm.fd, &fbs[r], 0xFF); > + > + if (pattern->frames_stack) { > + for (r = 0; r < pattern->n_rects; r++) > + for (r_ = 0; r_ <= r; r_++) > + draw_rect(pattern, &fbs[r], IGT_DRAW_PWRITE, > + r_); > + } else { > + for (r = 0; r < pattern->n_rects; r++) > + draw_rect(pattern, &fbs[r], IGT_DRAW_PWRITE, r); > + } > + > + for (r = 0; r < pattern->n_rects; r++) { > + rc = drmModeSetCrtc(drm.fd, prim_mode_params.crtc_id, > + fbs[r].fb_id, 0, 0, > + &prim_mode_params.connector_id, 1, > + prim_mode_params.mode); > + igt_assert(rc == 0); > + collect_crcs(&pattern->crcs[r]); > + } > + > + for (r = 0; r < pattern->n_rects; r++) { > + igt_debug("Rect %d CRC:", r); > + print_crc("", &pattern->crcs[r]); > + } > + > + unset_all_crtcs(); > + > + for (r = 0; r < pattern->n_rects; r++) > + igt_remove_fb(drm.fd, &fbs[r]); > + > + pattern->initialized = true; > +} > + > +static void setup_drm(void) > +{ > + int i; > + > + drm.fd = drm_open_any_master(); > + igt_require(drm.fd >= 0); The file descriptor check is done inside drm_open_any_master. > + > + 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]); > + > + drm.planes = drmModeGetPlaneResources(drm.fd); > + > + drm.bufmgr = drm_intel_bufmgr_gem_init(drm.fd, 4096); > + igt_assert(drm.bufmgr); > + drm_intel_bufmgr_gem_enable_reuse(drm.bufmgr); > +} > + > +static void teardown_drm(void) > +{ > + int i; > + > + drm_intel_bufmgr_destroy(drm.bufmgr); > + > + drmModeFreePlaneResources(drm.planes); > + > + for (i = 0; i < drm.res->count_connectors; i++) > + drmModeFreeConnector(drm.connectors[i]); > + > + drmModeFreeResources(drm.res); > + close(drm.fd); > +} > + > +static void setup_modeset(void) > +{ > + igt_require(init_modeset_cached_params()); > + > + kmstest_set_vt_graphics_mode(); > + > + igt_create_fb(drm.fd, 1024, 1024, DRM_FORMAT_XRGB8888, > + LOCAL_I915_FORMAT_MOD_X_TILED, &offscreen_fb); > +} > + > +static void teardown_modeset(void) > +{ > + if (scnd_mode_params.connector_id) { > + igt_remove_fb(drm.fd, &scnd_mode_params.fb); > + igt_remove_fb(drm.fd, &scnd_mode_params.cursor); > + igt_remove_fb(drm.fd, &scnd_mode_params.sprite); > + } > + igt_remove_fb(drm.fd, &prim_mode_params.fb); > + igt_remove_fb(drm.fd, &prim_mode_params.cursor); > + igt_remove_fb(drm.fd, &prim_mode_params.sprite); > + igt_remove_fb(drm.fd, &offscreen_fb); > +} > + > +static void setup_crcs(void) > +{ > + pipe_crc = igt_pipe_crc_new(0, INTEL_PIPE_CRC_SOURCE_AUTO); > + > + sink_crc.fd = open("/sys/kernel/debug/dri/0/i915_sink_crc_eDP1", > + O_RDONLY); igt_debugfs_open would take care of ensuring debugfs is mounted and the path is correct. > + igt_assert(sink_crc.fd >= 0); > + > + init_blue_crc(); > + > + pattern1.initialized = false; > + pattern1.frames_stack = true; > + pattern1.n_rects = 5; > + pattern1.crcs = NULL; > + pattern1.get_rect = pat1_get_rect; > + > + pattern2.initialized = false; > + pattern2.frames_stack = true; > + pattern2.n_rects = 5; > + pattern2.crcs = NULL; > + pattern2.get_rect = pat2_get_rect; > + > + pattern3.initialized = false; > + pattern3.frames_stack = false; > + pattern3.n_rects = 5; > + pattern3.crcs = NULL; > + pattern3.get_rect = pat3_get_rect; > + > + pattern4.initialized = false; > + pattern4.frames_stack = false; > + pattern4.n_rects = 1; > + pattern4.crcs = NULL; > + pattern4.get_rect = pat4_get_rect; > +} > + > +static void teardown_crcs(void) > +{ > + if (pattern1.crcs) > + free(pattern1.crcs); > + if (pattern2.crcs) > + free(pattern2.crcs); > + if (pattern3.crcs) > + free(pattern3.crcs); > + if (pattern4.crcs) > + free(pattern4.crcs); > + > + close(sink_crc.fd); > + > + igt_pipe_crc_free(pipe_crc); > +} > + > +static void restore_param(const char *file_path, char *param_value) > +{ > + int fd; > + > + fd = open(file_path, O_RDWR); > + if (fd >= 0) { > + write(fd, param_value, strlen(param_value)); > + close(fd); > + } > +} > + > +static void exit_handler(int sig) > +{ > + restore_param(FBC_PARAM_PATH, fbc.param_original_value); > + restore_param(PSR_PARAM_PATH, psr.param_original_value); > +} > + > +static void setup_fbc(void) > +{ > + fbc.fd = open("/sys/kernel/debug/dri/0/i915_fbc_status", O_RDONLY); > + igt_assert(fbc.fd >= 0); > + > + save_param(FBC_PARAM_PATH, fbc.param_original_value); > + > + fbc_setup_last_action(); > + fbc_setup_compressing(); > +} > + > +static void teardown_fbc(void) > +{ > + if (fbc.fd != -1) > + close(fbc.fd); > +} > + > +static bool psr_sink_has_support(void) > +{ > + char buf[DEBUGFS_MSG_SIZE]; > + > + get_debugfs_string(psr.fd, buf); > + > + return strstr(buf, "Sink_Support: yes\n"); > +} > + > +static void setup_psr(void) > +{ > + if (get_connector(prim_mode_params.connector_id)->connector_type != > + DRM_MODE_CONNECTOR_eDP) { > + igt_info("Can't test PSR: no usable eDP screen.\n"); > + return; > + } > + > + psr.fd = open("/sys/kernel/debug/dri/0/i915_edp_psr_status", O_RDONLY); > + igt_assert(psr.fd >= 0); > + > + if (!psr_sink_has_support()) { > + igt_info("Can't test PSR: not supported by sink.\n"); > + return; > + } > + psr.can_test = true; > + > + save_param(PSR_PARAM_PATH, psr.param_original_value); > +} > + > +static void teardown_psr(void) > +{ > + if (psr.fd != -1) > + close(psr.fd); > +} > + > +static void setup_environment(void) > +{ > + setup_drm(); > + setup_modeset(); > + > + igt_install_exit_handler(exit_handler); > + setup_fbc(); > + setup_psr(); > + > + setup_crcs(); > +} > + > +static void teardown_environment(void) > +{ > + teardown_crcs(); > + teardown_psr(); > + teardown_fbc(); > + teardown_modeset(); > + teardown_drm(); > +} > + > +static void wait_user(void) > +{ > + igt_info("Press enter...\n"); > + while (getchar() != '\n') > + ; Could this be done with the interactive debug option? (i.e. using igt_debug_wait_for_keypress) > +} > + > +static struct modeset_params *pick_params(const struct test_mode *t) > +{ > + switch (t->screen) { > + case SCREEN_PRIM: > + return &prim_mode_params; > + case SCREEN_SCND: > + return &scnd_mode_params; > + case SCREEN_OFFSCREEN: > + return NULL; > + default: > + igt_assert(false); > + } > +} > + > +static struct igt_fb *pick_target(const struct test_mode *t, > + struct modeset_params *params) > +{ > + if (!params) > + return &offscreen_fb; > + > + switch (t->plane) { > + case PLANE_PRI: > + return ¶ms->fb; > + case PLANE_CUR: > + return ¶ms->cursor; > + case PLANE_SPR: > + return ¶ms->sprite; > + default: > + igt_assert(false); > + } > +} > + > +static void do_flush(const struct test_mode *t) > +{ > + struct modeset_params *params = pick_params(t); > + struct igt_fb *target = pick_target(t, params); > + > + gem_set_domain(drm.fd, target->gem_handle, I915_GEM_DOMAIN_GTT, 0); > +} > + > +#define DONT_ASSERT_CRC (1 << 0) > + > +#define FBC_ASSERT_FLAGS (0xF << 1) > +#define ASSERT_FBC_ENABLED (1 << 1) > +#define ASSERT_FBC_DISABLED (1 << 2) > +#define ASSERT_LAST_ACTION_CHANGED (1 << 3) > +#define ASSERT_NO_ACTION_CHANGE (1 << 4) > + > +#define PSR_ASSERT_FLAGS (3 << 5) > +#define ASSERT_PSR_ENABLED (1 << 5) > +#define ASSERT_PSR_DISABLED (1 << 6) > + > +static int adjust_assertion_flags(const struct test_mode *t, int flags) > +{ > + if (!(flags & ASSERT_FBC_DISABLED)) > + flags |= ASSERT_FBC_ENABLED; > + if (!(flags & ASSERT_PSR_DISABLED)) > + flags |= ASSERT_PSR_ENABLED; > + > + if (t->feature != FEATURE_FBC) > + flags &= ~FBC_ASSERT_FLAGS; > + if (t->feature != FEATURE_PSR) > + flags &= ~PSR_ASSERT_FLAGS; > + > + return flags; > +} > + > +#define do_crc_assertions(flags) do { \ > + int flags__ = (flags); \ > + struct both_crcs crc_; \ > + \ > + if (!opt.check_crc || (flags__ & DONT_ASSERT_CRC)) \ > + break; \ > + \ > + collect_crcs(&crc_); \ > + print_crc("Calculated CRC:", &crc_); \ > + \ > + igt_assert(wanted_crc); \ > + igt_assert_crc_equal(&crc_.pipe, &wanted_crc->pipe); \ > + assert_sink_crc_equal(&crc_.sink, &wanted_crc->sink); \ > +} while (0) > + > +#define do_assertions(flags) do { \ Could this be a function rather than a macro to make debugging easier? > + int flags_ = adjust_assertion_flags(t, (flags)); \ > + \ > + if (opt.step > 1) \ > + wait_user(); \ > + \ > + /* Check the CRC to make sure the drawing operations work \ > + * immediately, independently of the features being enabled */ \ > + do_crc_assertions(flags_); \ > + \ > + /* Now we can flush things to make the test faster. */ \ > + do_flush(t); \ > + \ > + if (opt.check_status) { \ > + if (flags_ & ASSERT_FBC_ENABLED) { \ > + igt_assert(fbc_wait_for_status(ENABLED)); \ > + \ > + if (fbc.supports_compressing && \ > + opt.fbc_check_compression) \ > + igt_assert(fbc_wait_for_compression()); \ > + } else if (flags_ & ASSERT_FBC_DISABLED) { \ > + igt_assert(fbc_wait_for_status(DISABLED)); \ > + } \ > + \ > + if (flags_ & ASSERT_PSR_ENABLED) \ > + igt_assert(psr_wait_for_status(ENABLED)); \ > + else if (flags_ & ASSERT_PSR_DISABLED) \ > + igt_assert(psr_wait_for_status(DISABLED)); \ > + } else { \ > + /* Make sure we settle before continuing. */ \ > + sleep(1); \ > + } \ > + \ > + /* Check CRC again to make sure the compressed screen is ok. */ \ > + do_crc_assertions(flags_); \ > + \ > + if (fbc.supports_last_action && opt.fbc_check_last_action) { \ > + if (flags_ & ASSERT_LAST_ACTION_CHANGED) \ > + igt_assert(fbc_last_action_changed()); \ > + else if (flags_ & ASSERT_NO_ACTION_CHANGE) \ > + igt_assert(!fbc_last_action_changed()); \ > + } \ > + \ > + if (opt.step) \ > + wait_user(); \ > +} while (0) > + > +static void enable_prim_screen_and_wait(const struct test_mode *t) > +{ > + igt_draw_fill_fb(drm.fd, &prim_mode_params.fb, 0xFF); > + set_mode_for_params(&prim_mode_params); > + > + wanted_crc = &blue_crc; > + fbc_update_last_action(); > + > + do_assertions(ASSERT_NO_ACTION_CHANGE); > +} > + > +static void enable_scnd_screen_and_wait(const struct test_mode *t) > +{ > + igt_draw_fill_fb(drm.fd, &scnd_mode_params.fb, 0x80); > + set_mode_for_params(&scnd_mode_params); > + do_assertions(ASSERT_NO_ACTION_CHANGE); > +} > + > +static void set_cursor_for_test(const struct test_mode *t, > + struct modeset_params *params) > +{ > + int rc; > + > + igt_draw_fill_fb(drm.fd, ¶ms->cursor, 0xFF0000FF); > + > + rc = drmModeMoveCursor(drm.fd, params->crtc_id, 0, 0); > + igt_assert(rc == 0); > + > + rc = drmModeSetCursor(drm.fd, params->crtc_id, > + params->cursor.gem_handle, params->cursor.width, > + params->cursor.height); > + igt_assert(rc == 0); > + > + do_assertions(ASSERT_NO_ACTION_CHANGE); > +} > + > +static void set_sprite_for_test(const struct test_mode *t, > + struct modeset_params *params) > +{ > + int rc; > + > + igt_draw_fill_fb(drm.fd, ¶ms->sprite, 0xFF0000FF); > + > + rc = drmModeSetPlane(drm.fd, params->sprite_id, params->crtc_id, > + params->sprite.fb_id, 0, 0, 0, > + params->sprite.width, params->sprite.height, > + 0, 0, params->sprite.width << 16, > + params->sprite.height << 16); > + igt_assert(rc == 0); > + > + do_assertions(ASSERT_NO_ACTION_CHANGE); > +} > + > +static void enable_features_for_test(const struct test_mode *t) > +{ > + switch (t->feature) { > + case FEATURE_NONE: > + break; > + case FEATURE_FBC: > + fbc_enable(); > + break; > + case FEATURE_PSR: > + psr_enable(); > + break; > + default: > + igt_assert(false); > + } > +} > + > +static void check_test_requirements(const struct test_mode *t) > +{ > + if (t->pipes == PIPE_DUAL) > + igt_require(scnd_mode_params.connector_id); It might be nice to use igt_require_f on these checks, so that a better message is printed if the test skips. > + > + if (t->feature == FEATURE_PSR) > + igt_require(psr.can_test); > + > + if (opt.only_feature != FEATURE_COUNT) > + igt_require(t->feature == opt.only_feature); > + > + if (opt.only_pipes != PIPE_COUNT) > + igt_require(t->pipes == opt.only_pipes); > +} > + > +static void set_screens_for_test(const struct test_mode *t, > + struct draw_pattern_info *pattern) > +{ > + check_test_requirements(t); > + > + if (t->screen == SCREEN_OFFSCREEN) > + igt_draw_fill_fb(drm.fd, &offscreen_fb, 0x80); > + > + disable_features(); > + unset_all_crtcs(); > + init_crcs(pattern); > + enable_features_for_test(t); > + > + enable_prim_screen_and_wait(t); > + if (t->screen == SCREEN_PRIM) { > + if (t->plane == PLANE_CUR) > + set_cursor_for_test(t, &prim_mode_params); > + if (t->plane == PLANE_SPR) > + set_sprite_for_test(t, &prim_mode_params); > + } > + > + if (t->pipes == PIPE_SINGLE) > + return; > + > + enable_scnd_screen_and_wait(t); > + if (t->screen == SCREEN_SCND) { > + if (t->plane == PLANE_CUR) > + set_cursor_for_test(t, &scnd_mode_params); > + if (t->plane == PLANE_SPR) > + set_sprite_for_test(t, &scnd_mode_params); > + } > +} > + > +static void rte_subtest(const struct test_mode *t) > +{ > + check_test_requirements(t); > + > + disable_features(); > + enable_features_for_test(t); > + unset_all_crtcs(); > + do_assertions(ASSERT_FBC_DISABLED | ASSERT_PSR_DISABLED | > + DONT_ASSERT_CRC); > + > + enable_prim_screen_and_wait(t); > + set_cursor_for_test(t, &prim_mode_params); > + set_sprite_for_test(t, &prim_mode_params); > + > + if (t->pipes == PIPE_SINGLE) > + return; > + > + enable_scnd_screen_and_wait(t); > + set_cursor_for_test(t, &scnd_mode_params); > + set_sprite_for_test(t, &scnd_mode_params); > +} > + > +static void update_wanted_crc(const struct test_mode *t, struct both_crcs *crc) > +{ > + if (t->screen == SCREEN_PRIM) > + wanted_crc = crc; > +} > + > +static void draw_subtest(const struct test_mode *t) > +{ > + int r; > + int assertions = 0; > + struct draw_pattern_info *pattern; > + struct modeset_params *params = pick_params(t); > + struct igt_fb *target = pick_target(t, params); > + > + switch (t->screen) { > + case SCREEN_PRIM: > + if (t->method != IGT_DRAW_MMAP_GTT && t->plane == PLANE_PRI) > + assertions |= ASSERT_LAST_ACTION_CHANGED; > + break; > + case SCREEN_SCND: > + case SCREEN_OFFSCREEN: > + assertions |= ASSERT_NO_ACTION_CHANGE; > + break; > + default: > + igt_assert(false); > + } > + > + switch (t->plane) { > + case PLANE_PRI: > + pattern = &pattern1; > + break; > + case PLANE_CUR: > + case PLANE_SPR: > + pattern = &pattern2; > + break; > + default: > + igt_assert(false); > + } > + > + set_screens_for_test(t, pattern); > + > + for (r = 0; r < pattern->n_rects; r++) { > + draw_rect(pattern, target, t->method, r); > + update_wanted_crc(t, &pattern->crcs[r]); > + do_assertions(assertions); > + } > +} > + > +static void flip_subtest(const struct test_mode *t) > +{ > + int r, rc; > + int assertions = 0; > + struct igt_fb fb2, *target; > + struct modeset_params *params = pick_params(t); > + struct draw_pattern_info *pattern = &pattern1; > + uint32_t bg_color; > + > + switch (t->screen) { > + case SCREEN_PRIM: > + assertions |= ASSERT_LAST_ACTION_CHANGED; > + bg_color = 0xFF; > + break; > + case SCREEN_SCND: > + assertions |= ASSERT_NO_ACTION_CHANGE; > + bg_color = 0x80; > + break; > + default: > + igt_assert(false); > + } > + > + set_screens_for_test(t, pattern); > + > + igt_create_fb(drm.fd, params->mode->hdisplay, params->mode->vdisplay, > + DRM_FORMAT_XRGB8888, LOCAL_I915_FORMAT_MOD_X_TILED, &fb2); > + igt_draw_fill_fb(drm.fd, &fb2, bg_color); > + > + for (r = 0; r < pattern->n_rects; r++) { > + target = (r % 2 == 0) ? &fb2 : ¶ms->fb; > + > + if (r != 0) > + draw_rect(pattern, target, t->method, r - 1); > + draw_rect(pattern, target, t->method, r); > + update_wanted_crc(t, &pattern->crcs[r]); > + > + rc = drmModePageFlip(drm.fd, params->crtc_id, target->fb_id, 0, > + NULL); > + igt_assert(rc == 0); > + > + do_assertions(assertions); > + } > + > + igt_remove_fb(drm.fd, &fb2); > +} > + > +static void move_subtest(const struct test_mode *t) > +{ > + int r, rc; > + int assertions = ASSERT_NO_ACTION_CHANGE; > + struct modeset_params *params = pick_params(t); > + struct draw_pattern_info *pattern = &pattern3; > + bool repeat = false; > + > + set_screens_for_test(t, pattern); > + > + /* Just paint the right color since we start at 0x0. */ > + draw_rect(pattern, pick_target(t, params), t->method, 0); > + update_wanted_crc(t, &pattern->crcs[0]); > + > + do_assertions(assertions); > + > + for (r = 1; r < pattern->n_rects; r++) { > + struct rect rect = pattern->get_rect(¶ms->fb, r); > + > + switch (t->plane) { > + case PLANE_CUR: > + rc = drmModeMoveCursor(drm.fd, params->crtc_id, rect.x, > + rect.y); > + igt_assert(rc == 0); > + break; > + case PLANE_SPR: > + rc = drmModeSetPlane(drm.fd, params->sprite_id, > + params->crtc_id, > + params->sprite.fb_id, 0, > + rect.x, rect.y, rect.w, > + rect.h, 0, 0, rect.w << 16, > + rect.h << 16); > + igt_assert(rc == 0); > + break; > + default: > + igt_assert(false); > + } > + update_wanted_crc(t, &pattern->crcs[r]); > + > + do_assertions(assertions); > + > + /* "Move" the last rect to the same position just to make sure > + * this works too. */ > + if (r+1 == pattern->n_rects && !repeat) { > + repeat = true; > + r--; > + } > + } > +} > + > +static void onoff_subtest(const struct test_mode *t) > +{ > + int r, rc; > + int assertions = ASSERT_NO_ACTION_CHANGE; > + struct modeset_params *params = pick_params(t); > + struct draw_pattern_info *pattern = &pattern3; > + > + set_screens_for_test(t, pattern); > + > + /* Just paint the right color since we start at 0x0. */ > + draw_rect(pattern, pick_target(t, params), t->method, 0); > + update_wanted_crc(t, &pattern->crcs[0]); > + do_assertions(assertions); > + > + for (r = 0; r < 4; r++) { > + if (r % 2 == 0) { > + switch (t->plane) { > + case PLANE_CUR: > + rc = drmModeSetCursor(drm.fd, params->crtc_id, > + 0, 0, 0); > + igt_assert(rc == 0); > + break; > + case PLANE_SPR: > + rc = drmModeSetPlane(drm.fd, params->sprite_id, > + 0, 0, 0, 0, 0, 0, 0, 0, 0, > + 0, 0); > + igt_assert(rc == 0); > + break; > + default: > + igt_assert(false); > + } > + update_wanted_crc(t, &blue_crc); > + > + } else { > + switch (t->plane) { > + case PLANE_CUR: > + rc = drmModeSetCursor(drm.fd, params->crtc_id, > + params->cursor.gem_handle, > + params->cursor.width, > + params->cursor.height); > + igt_assert(rc == 0); > + break; > + case PLANE_SPR: > + rc = drmModeSetPlane(drm.fd, params->sprite_id, > + params->crtc_id, > + params->sprite.fb_id, 0, > + 0, 0, params->sprite.width, > + params->sprite.height, 0, > + 0, > + params->sprite.width << 16, > + params->sprite.height << 16); > + igt_assert(rc == 0); > + break; > + default: > + igt_assert(false); > + } > + update_wanted_crc(t, &pattern->crcs[0]); > + > + } > + > + do_assertions(assertions); > + } > +} > + > +static void fullscreen_plane_subtest(const struct test_mode *t) > +{ > + struct draw_pattern_info *pattern = &pattern4; > + struct igt_fb fullscreen_fb; > + struct rect rect; > + struct modeset_params *params = pick_params(t); > + int assertions; > + int rc; > + > + set_screens_for_test(t, pattern); > + > + rect = pattern->get_rect(¶ms->fb, 0); > + igt_create_fb(drm.fd, rect.w, rect.h, DRM_FORMAT_XRGB8888, > + LOCAL_I915_FORMAT_MOD_X_TILED, &fullscreen_fb); > + igt_draw_fill_fb(drm.fd, &fullscreen_fb, rect.color); > + > + rc = drmModeSetPlane(drm.fd, params->sprite_id, params->crtc_id, > + fullscreen_fb.fb_id, 0, 0, 0, fullscreen_fb.width, > + fullscreen_fb.height, 0, 0, > + fullscreen_fb.width << 16, > + fullscreen_fb.height << 16); > + igt_assert(rc == 0); > + update_wanted_crc(t, &pattern->crcs[0]); > + > + switch (t->screen) { > + case SCREEN_PRIM: > + assertions = ASSERT_FBC_DISABLED | > + ASSERT_LAST_ACTION_CHANGED; > + break; > + case SCREEN_SCND: > + assertions = ASSERT_NO_ACTION_CHANGE; > + break; > + default: > + igt_assert(false); > + } > + do_assertions(assertions); > + > + rc = drmModeSetPlane(drm.fd, params->sprite_id, 0, 0, 0, 0, 0, 0, 0, 0, > + 0, 0, 0); > + igt_assert(rc == 0); > + > + if (t->screen == SCREEN_PRIM) > + assertions = ASSERT_LAST_ACTION_CHANGED; > + update_wanted_crc(t, &blue_crc); > + do_assertions(assertions); > + > + igt_remove_fb(drm.fd, &fullscreen_fb); > +} > + > +static int opt_handler(int option, int option_index) > +{ > + switch (option) { > + case 's': > + opt.check_status = false; > + break; > + case 'c': > + opt.check_crc = false; > + break; > + case 'o': > + opt.fbc_check_compression = false; > + break; > + case 'a': > + opt.fbc_check_last_action = false; > + break; > + case 'e': > + opt.no_edp = true; > + break; > + case 'm': > + opt.small_modes = true; > + break; > + case 't': > + opt.step++; > + break; > + case 'n': > + igt_assert(opt.only_feature == FEATURE_COUNT); > + opt.only_feature = FEATURE_NONE; > + break; > + case 'f': > + igt_assert(opt.only_feature == FEATURE_COUNT); > + opt.only_feature = FEATURE_FBC; > + break; > + case 'p': > + igt_assert(opt.only_feature == FEATURE_COUNT); > + opt.only_feature = FEATURE_PSR; > + break; > + case '1': > + igt_assert(opt.only_pipes == PIPE_COUNT); > + opt.only_pipes = PIPE_SINGLE; > + break; > + case '2': > + igt_assert(opt.only_pipes == PIPE_COUNT); > + opt.only_pipes = PIPE_DUAL; > + 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-fbc-compression-check Don't check for the FBC compression status\n" > +" --no-fbc-action-check Don't check for the FBC last action\n" > +" --no-edp Don't use eDP monitors\n" > +" --use-small-modes Use smaller resolutions for the modes\n" > +" --step Stop on each step so you can check the screen\n" > +" --nop-only Only run the \"nop\" feature subtests\n" > +" --fbc-only Only run the \"fbc\" feature subtests\n" > +" --psr-only Only run the \"psr\" feature subtests\n" > +" --1p-only Only run subtests that use 1 pipe\n" > +" --2p-only Only run subtests that use 2 pipes\n"; > + > +static const char *pipes_str(int pipes) > +{ > + switch (pipes) { > + case PIPE_SINGLE: > + return "1p"; > + case PIPE_DUAL: > + return "2p"; > + default: > + igt_assert(false); > + } > +} > + > +static const char *screen_str(int screen) > +{ > + switch (screen) { > + case SCREEN_PRIM: > + return "primscrn"; > + case SCREEN_SCND: > + return "scndscrn"; > + case SCREEN_OFFSCREEN: > + return "offscren"; > + default: > + igt_assert(false); > + } > +} > + > +static const char *plane_str(int plane) > +{ > + switch (plane) { > + case PLANE_PRI: > + return "pri"; > + case PLANE_CUR: > + return "cur"; > + case PLANE_SPR: > + return "spr"; > + default: > + igt_assert(false); > + } > +} > + > +static const char *feature_str(int feature) > +{ > + switch (feature) { > + case FEATURE_NONE: > + return "nop"; > + case FEATURE_FBC: > + return "fbc"; > + case FEATURE_PSR: > + return "psr"; > + default: > + igt_assert(false); > + } > +} > + > +#define TEST_MODE_ITER_BEGIN(t) \ > + for (t.pipes = 0; t.pipes < PIPE_COUNT; t.pipes++) { \ > + for (t.screen = 0; t.screen < SCREEN_COUNT; t.screen++) { \ > + for (t.plane = 0; t.plane < PLANE_COUNT; t.plane++) { \ > + for (t.method = 0; t.method < IGT_DRAW_METHOD_COUNT; t.method++) { \ > + for (t.feature = 0; t.feature < FEATURE_COUNT; t.feature++) { \ > + if (t.pipes == PIPE_SINGLE && t.screen == SCREEN_SCND) \ > + continue; > + > +#define TEST_MODE_ITER_END } } } } } > + > +int main(int argc, char *argv[]) > +{ > + struct test_mode t; > + struct option long_options[] = { > + { "no-status-check", 0, 0, 's'}, > + { "no-crc-check", 0, 0, 'c'}, > + { "no-fbc-compression-check", 0, 0, 'o'}, > + { "no-fbc-action-check", 0, 0, 'a'}, > + { "no-edp", 0, 0, 'e'}, > + { "use-small-modes", 0, 0, 'm'}, > + { "step", 0, 0, 't'}, > + { "nop-only", 0, 0, 'n'}, > + { "fbc-only", 0, 0, 'f'}, > + { "psr-only", 0, 0, 'p'}, > + { "1p-only", 0, 0, '1'}, > + { "2p-only", 0, 0, '2'}, > + { 0, 0, 0, 0 } > + }; > + > + igt_subtest_init_parse_opts(&argc, argv, "", long_options, help_str, > + opt_handler); This needs updating for the latest changes as this function and the option handler now have a user data pointer. > + > + igt_fixture > + setup_environment(); > + > + for (t.pipes = 0; t.pipes < PIPE_COUNT; t.pipes++) { > + for (t.feature = 0; t.feature < FEATURE_COUNT; t.feature++) { > + t.screen = SCREEN_PRIM; > + t.plane = PLANE_PRI; > + /* Make sure nothing is using this value. */ > + t.method = -1; > + > + igt_subtest_f("%s-rte-%s", > + pipes_str(t.pipes), > + feature_str(t.feature)) > + rte_subtest(&t); > + } > + } > + > + TEST_MODE_ITER_BEGIN(t) > + igt_subtest_f("%s-%s-%s-draw-%s-%s", > + pipes_str(t.pipes), > + screen_str(t.screen), > + plane_str(t.plane), > + igt_draw_get_method_name(t.method), > + feature_str(t.feature)) > + draw_subtest(&t); > + TEST_MODE_ITER_END > + > + > + TEST_MODE_ITER_BEGIN(t) > + if (t.plane != PLANE_PRI) > + continue; > + > + if (t.screen == SCREEN_OFFSCREEN) > + continue; > + > + igt_subtest_f("%s-%s-flip-%s-%s", > + pipes_str(t.pipes), > + screen_str(t.screen), > + igt_draw_get_method_name(t.method), > + feature_str(t.feature)) > + flip_subtest(&t); > + TEST_MODE_ITER_END > + > + TEST_MODE_ITER_BEGIN(t) > + if (t.screen == SCREEN_OFFSCREEN) > + continue; > + if (t.method != IGT_DRAW_BLT) > + continue; > + if (t.plane == PLANE_PRI) > + continue; > + > + igt_subtest_f("%s-%s-%s-move-%s", > + pipes_str(t.pipes), > + screen_str(t.screen), > + plane_str(t.plane), > + feature_str(t.feature)) > + move_subtest(&t); > + > + igt_subtest_f("%s-%s-%s-onoff-%s", > + pipes_str(t.pipes), > + screen_str(t.screen), > + plane_str(t.plane), > + feature_str(t.feature)) > + onoff_subtest(&t); > + TEST_MODE_ITER_END > + > + TEST_MODE_ITER_BEGIN(t) > + if (t.screen == SCREEN_OFFSCREEN) > + continue; > + if (t.method != IGT_DRAW_BLT) > + continue; > + if (t.plane != PLANE_SPR) > + continue; > + > + igt_subtest_f("%s-%s-%s-fullscreen-%s", > + pipes_str(t.pipes), > + screen_str(t.screen), > + plane_str(t.plane), > + feature_str(t.feature)) > + fullscreen_plane_subtest(&t); > + TEST_MODE_ITER_END > + > + /* > + * TODO: ideas for subtests: > + * - Add a new pipe configuration where both pipes can share a big > + * framebuffer (instead of each pipe having its own FB). This will > + * possibly require some wrapping of struct igt_fb to make the > + * implementation easier. > + * - Add a test that alternates between different writing methods. Don't > + * forget to add the proper domain handling. > + * - Add a new enum to struct test_mode that allows us to specify the > + * BPP/depth configuration. > + */ > + > + igt_fixture > + teardown_environment(); > + > + igt_exit(); > +} > -- > 2.1.4 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/intel-gfx _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx