Message ID | 1432590058-1747-1-git-send-email-przanoni@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 25 May 2015 at 22:40, Paulo Zanoni <przanoni@gmail.com> wrote: > From: Paulo Zanoni <paulo.r.zanoni@intel.com> > > 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@intel.com> > --- > 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@intel.com> > + * > + */ > + > +#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@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/intel-gfx
2015-05-26 8:13 GMT-03:00 Thomas Wood <thomas.wood@intel.com>: > On 25 May 2015 at 22:40, Paulo Zanoni <przanoni@gmail.com> wrote: >> From: Paulo Zanoni <paulo.r.zanoni@intel.com> >> >> 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. Will do. > > Also, there are a few suggestions from lib/igt.cocci that might be > worth implementing. I'll take a look. > > >> >> 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@intel.com> >> --- >> 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@intel.com> >> + * >> + */ >> + >> +#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. This would be a user of the igt_assert_not_reached() you mentioned below. We should really never reach this assertion. > > >> +} >> + >> +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. Agree. I'd totally use that for my tests. > > >> + } >> + >> + 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. Oh, I see. Will use it. > > >> + 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) What I don't like about igt_debug_wait_for_keypress() is that I usually ctrl+c the test after I get past the part I want, so I'm left with a terminal that just doesn't work, so I have to blindly "reset" the terminal. I'm willing to add 100 lines of code to each test I write in order to avoid that annoyance. > >> +} >> + >> +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? Nooooooo. Macros give me a correct line number when igt_assert() fails, while functions would always give me the same line number for every assertion in the code. I know we print the backtrace, but I don't get line numbers from the backtraces either, so they're not very useful. By the way, I have the same complaint about igt_assert_crc_equal(): can we please revert it back to a macro? > > >> + 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. Yeah, that one is going to happen a lot. > > >> + >> + 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. I'll take a look at the other users. Thanks for reviewing such a big file! > > >> + >> + 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@lists.freedesktop.org >> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
2015-05-26 8:13 GMT-03:00 Thomas Wood <thomas.wood@intel.com>: > On 25 May 2015 at 22:40, Paulo Zanoni <przanoni@gmail.com> wrote: >> From: Paulo Zanoni <paulo.r.zanoni@intel.com> >> >> 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. I just looked at that and I'm really not interested in any of the changes. Is igt.cocci optional or is someone, at some point just going to run it for everything and commit? My main complaint is about the igt assertion changes: igt_assert_lte is, IMHO, very hard to understand compared to what's currently at the code. I know there's the advantage that the value can be printed in case we fail the assertion, but I really think it doesn't outweigh the readability loss. I don't want to state for 30 seconds trying to remember what "lte" stands for, and if it checks for A < B, or B < A, or A <= B, or B <= A. Also, most of the comparisons are against constants, and in a lot of the assertions what we really want is the errno instead of the comparison values, which we already get. IMHO, if it turns out some assertion is getting hit too often and having the values would be useful, then I can replace it with the appropriate igt_assert_x macro or just use hardcode igt_assert_f() for that case. Also, I should remember the recent regressions that were caused by lib/igt.cocci macro changes. Just git log tests/kms_fbc_crc.c for _multiple_ examples. The cocci script is also requesting some changes at the exit handler, and I'm really not sure if those changes are safe for an exit handler, so I'd like to avoid them. Remember we also had hard-to-spot bugs on our exit handlers, so it's not like we're going to quickly notice these bugs and then fix them. > > >> >> 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@intel.com> >> --- >> 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@intel.com> >> + * >> + */ >> + >> +#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@lists.freedesktop.org >> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
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@intel.com> + * + */ + +#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); +} + +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); + } + + 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); + + 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_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') + ; +} + +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 { \ + 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); + + 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); + + 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(); +}