[Intel-gfx] [PATCH igt] tests: add kms_frontbuffer_tracking

Thomas Wood thomas.wood at intel.com
Tue May 26 04:13:58 PDT 2015


On 25 May 2015 at 22:40, Paulo Zanoni <przanoni at gmail.com> wrote:
> From: Paulo Zanoni <paulo.r.zanoni at 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 at 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 at 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,
> +                     &params->fb);
> +       igt_create_fb(drm.fd, 64, 64, DRM_FORMAT_ARGB8888,
> +                     LOCAL_DRM_FORMAT_MOD_NONE, &params->cursor);
> +       igt_create_fb(drm.fd, 64, 64, DRM_FORMAT_XRGB8888,
> +                     LOCAL_I915_FORMAT_MOD_X_TILED, &params->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,
> +                           &params->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 &params->fb;
> +       case PLANE_CUR:
> +               return &params->cursor;
> +       case PLANE_SPR:
> +               return &params->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, &params->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, &params->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 : &params->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(&params->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(&params->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 at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx


More information about the Intel-gfx mailing list