2015-05-26 8:13 GMT-03:00 Thomas Wood <thomas.wood@xxxxxxxxx>: > On 25 May 2015 at 22:40, Paulo Zanoni <przanoni@xxxxxxxxx> wrote: >> From: Paulo Zanoni <paulo.r.zanoni@xxxxxxxxx> >> >> This is a new test that should exercise the frontbuffer tracking >> feature of the Kernel in a number of different ways. We use different >> drawing methods, we use the primary, cursor and sprite planes, we can >> test both on single and dual pipes, also on buffers not associated >> with any CRTCs, etc. >> >> We currently have assertions for both FBC and PSR, and we also have a >> "nop" test mode that should disable both FBC and PSR, and can be >> used for debugging. > > It would be good to have this information in the test as a comment > somewhere as well as adding a short description for the > IGT_TEST_DESCRIPTION macro. 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@xxxxxxxxx> >> --- >> tests/.gitignore | 1 + >> tests/Makefile.sources | 1 + >> tests/kms_frontbuffer_tracking.c | 1825 ++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 1827 insertions(+) >> create mode 100644 tests/kms_frontbuffer_tracking.c >> >> Some interesting details: >> >> On a tree from 2015, May 06, I get 18 failures and 357 successes. The >> only FBC failures I get are from the MMAP_WC draw method, which is >> maybe lacking proper frontbuffer tracking support on the Kernel. The >> other 16 failures are for PSR, most of them with GTT MMAPs. >> >> But if I use a tree from 2015, May 25, every single PSR test fails. We >> need to investigate that. Maybe if I had finished this earlier we >> would have an automated bisect. This also highlights the importance of >> testing stuff even when they are disabled by default! I plan to patch >> the other tests to do the same thing. >> >> I am also seeing some FBC failures that happen right after booting. It >> seems that the very first tests fail until I run a test that uses the >> render ring. I'll have to investigate this. >> >> I am also seeing some occasional corruptions on my eDP panel, but on >> these cases both the pipe and sink CRC tests succeed! Maybe this is >> some weird panel malfunction caused by the fact that we're doing tons >> and tons of modesets on the panel. >> >> diff --git a/tests/.gitignore b/tests/.gitignore >> index a3f3143..dcead2c 100644 >> --- a/tests/.gitignore >> +++ b/tests/.gitignore >> @@ -134,6 +134,7 @@ kms_flip >> kms_flip_event_leak >> kms_flip_tiling >> kms_force_connector >> +kms_frontbuffer_tracking >> kms_legacy_colorkey >> kms_mmio_vs_cs_flip >> kms_pipe_b_c_ivb >> diff --git a/tests/Makefile.sources b/tests/Makefile.sources >> index 994c31b..3c93337 100644 >> --- a/tests/Makefile.sources >> +++ b/tests/Makefile.sources >> @@ -66,6 +66,7 @@ TESTS_progs_M = \ >> kms_flip \ >> kms_flip_event_leak \ >> kms_flip_tiling \ >> + kms_frontbuffer_tracking \ >> kms_legacy_colorkey \ >> kms_mmio_vs_cs_flip \ >> kms_pipe_b_c_ivb \ >> diff --git a/tests/kms_frontbuffer_tracking.c b/tests/kms_frontbuffer_tracking.c >> new file mode 100644 >> index 0000000..f6554f9 >> --- /dev/null >> +++ b/tests/kms_frontbuffer_tracking.c >> @@ -0,0 +1,1825 @@ >> +/* >> + * Copyright © 2015 Intel Corporation >> + * >> + * Permission is hereby granted, free of charge, to any person obtaining a >> + * copy of this software and associated documentation files (the "Software"), >> + * to deal in the Software without restriction, including without limitation >> + * the rights to use, copy, modify, merge, publish, distribute, sublicense, >> + * and/or sell copies of the Software, and to permit persons to whom the >> + * Software is furnished to do so, subject to the following conditions: >> + * >> + * The above copyright notice and this permission notice (including the next >> + * paragraph) shall be included in all copies or substantial portions of the >> + * Software. >> + * >> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR >> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, >> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL >> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER >> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING >> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS >> + * IN THE SOFTWARE. >> + * >> + * Authors: Paulo Zanoni <paulo.r.zanoni@xxxxxxxxx> >> + * >> + */ >> + >> +#include <sys/types.h> >> +#include <sys/stat.h> >> +#include <fcntl.h> >> + >> +#include "drmtest.h" >> +#include "igt_aux.h" >> +#include "igt_draw.h" >> +#include "igt_kms.h" >> +#include "igt_debugfs.h" >> +#include "intel_chipset.h" >> +#include "ioctl_wrappers.h" >> + >> +#define FBC_PARAM_PATH "/sys/module/i915/parameters/enable_fbc" >> +#define PSR_PARAM_PATH "/sys/module/i915/parameters/enable_psr" >> + >> +struct test_mode { >> + enum { >> + PIPE_SINGLE = 0, >> + PIPE_DUAL, >> + PIPE_COUNT, >> + } pipes; >> + >> + enum { >> + SCREEN_PRIM = 0, >> + SCREEN_SCND, >> + SCREEN_OFFSCREEN, >> + SCREEN_COUNT, >> + } screen; >> + >> + enum { >> + PLANE_PRI = 0, >> + PLANE_CUR, >> + PLANE_SPR, >> + PLANE_COUNT, >> + } plane; >> + >> + enum { >> + FEATURE_NONE = 0, >> + FEATURE_FBC, >> + FEATURE_PSR, >> + FEATURE_COUNT, >> + } feature; >> + >> + enum igt_draw_method method; >> +}; >> + >> +enum feature_status { >> + ENABLED, >> + DISABLED, >> +}; >> + >> +struct rect { >> + int x; >> + int y; >> + int w; >> + int h; >> + uint32_t color; >> +}; >> + >> +#define MAX_CONNECTORS 32 >> +struct { >> + int fd; >> + drmModeResPtr res; >> + drmModeConnectorPtr connectors[MAX_CONNECTORS]; >> + drmModePlaneResPtr planes; >> + drm_intel_bufmgr *bufmgr; >> +} drm; >> + >> +struct { >> + int fd; >> + >> + char param_original_value[16]; >> + >> + bool supports_compressing; >> + bool supports_last_action; >> + >> + struct timespec last_action; >> +} fbc = { >> + .fd = -1, >> + .supports_last_action = false, >> + .supports_compressing = false, >> +}; >> + >> +struct { >> + int fd; >> + bool can_test; >> + >> + char param_original_value[16]; >> +} psr = { >> + .fd = -1, >> + .can_test = false, >> +}; >> + >> + >> +#define SINK_CRC_SIZE 12 >> +typedef struct { >> + char data[SINK_CRC_SIZE]; >> +} sink_crc_t; >> + >> +struct both_crcs { >> + igt_crc_t pipe; >> + sink_crc_t sink; >> +}; >> + >> +igt_pipe_crc_t *pipe_crc; >> +struct both_crcs blue_crc; >> +struct both_crcs *wanted_crc; >> + >> +struct { >> + int fd; >> +} sink_crc; >> + >> +struct draw_pattern_info { >> + bool initialized; >> + bool frames_stack; >> + int n_rects; >> + struct both_crcs *crcs; >> + struct rect (*get_rect)(struct igt_fb *fb, int r); >> +}; >> + >> +/* Draw big rectangles on the screen. */ >> +struct draw_pattern_info pattern1; >> +/* 64x64 rectangles at x:0,y:0, just so we can draw on the cursor and sprite. */ >> +struct draw_pattern_info pattern2; >> +/* 64x64 rectangles at different positions, same color, for the move test. */ >> +struct draw_pattern_info pattern3; >> +/* Just a fullscreen green square. */ >> +struct draw_pattern_info pattern4; >> + >> +/* Command line parameters. */ >> +struct { >> + bool check_status; >> + bool check_crc; >> + bool fbc_check_compression; >> + bool fbc_check_last_action; >> + bool no_edp; >> + bool small_modes; >> + int step; >> + int only_feature; >> + int only_pipes; >> +} opt = { >> + .check_status = true, >> + .check_crc = true, >> + .fbc_check_compression = true, >> + .fbc_check_last_action = true, >> + .no_edp = false, >> + .small_modes = false, >> + .step = 0, >> + .only_feature = FEATURE_COUNT, >> + .only_pipes = PIPE_COUNT, >> +}; >> + >> +struct modeset_params { >> + uint32_t crtc_id; >> + uint32_t connector_id; >> + uint32_t sprite_id; >> + drmModeModeInfoPtr mode; >> + struct igt_fb fb; >> + struct igt_fb cursor; >> + struct igt_fb sprite; >> +}; >> + >> +struct modeset_params prim_mode_params; >> +struct modeset_params scnd_mode_params; >> +struct igt_fb offscreen_fb; >> + >> +static drmModeModeInfoPtr get_connector_smallest_mode(drmModeConnectorPtr c) >> +{ >> + int i; >> + drmModeModeInfoPtr smallest = NULL; >> + >> + for (i = 0; i < c->count_modes; i++) { >> + drmModeModeInfoPtr mode = &c->modes[i]; >> + >> + if (!smallest) >> + smallest = mode; >> + >> + if (mode->hdisplay * mode->vdisplay < >> + smallest->hdisplay * smallest->vdisplay) >> + smallest = mode; >> + } >> + >> + return smallest; >> +} >> + >> +static drmModeConnectorPtr get_connector(uint32_t id) >> +{ >> + int i; >> + >> + for (i = 0; i < drm.res->count_connectors; i++) >> + if (drm.res->connectors[i] == id) >> + return drm.connectors[i]; >> + >> + igt_assert(false); > > igt_assert_f() could be used here to provide a more descriptive error message. 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@xxxxxxxxxxxxxxxxxxxxx >> http://lists.freedesktop.org/mailman/listinfo/intel-gfx -- Paulo Zanoni _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx