diff mbox

[i-g-t] tests/kms_plane_lowres: Plane visibility after atomic modesets

Message ID 1479213532-5217-1-git-send-email-mika.kahola@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Kahola, Mika Nov. 15, 2016, 12:38 p.m. UTC
Testcase for plane visibility after atomic modesets. The idea of the test
is the following:

 - draw a blue screen with high resolution
 - enable a yellow plane, visible, in lower-left corner
 - set a new lower resolution mode (1024x768) that makes plane invisible
 - check from debugfs 'i915_display_info' that the plane is invisible
 - switch back to higher resolution mode
 - check from debugfs 'i915_display_info' that the plane is visible again
 - repeat number of iterations, default 64

Signed-off-by: Mika Kahola <mika.kahola@intel.com>
---
 tests/Makefile.sources   |   1 +
 tests/kms_plane_lowres.c | 424 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 425 insertions(+)
 create mode 100644 tests/kms_plane_lowres.c

Comments

Daniel Stone Nov. 15, 2016, 1:20 p.m. UTC | #1
Hi Mika,

On 15 November 2016 at 12:38, Mika Kahola <mika.kahola@intel.com> wrote:
> Testcase for plane visibility after atomic modesets. The idea of the test
> is the following:
>
>  - draw a blue screen with high resolution
>  - enable a yellow plane, visible, in lower-left corner
>  - set a new lower resolution mode (1024x768) that makes plane invisible
>  - check from debugfs 'i915_display_info' that the plane is invisible
>  - switch back to higher resolution mode
>  - check from debugfs 'i915_display_info' that the plane is visible again
>  - repeat number of iterations, default 64

It would be nice to have this for non-Intel drivers as well, with
these suggestions:

> +static inline uint32_t pipe_select(int pipe)
> +{
> +       if (pipe > 1)
> +               return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
> +       else if (pipe > 0)
> +               return DRM_VBLANK_SECONDARY;
> +       else
> +               return 0;
> +}
> +
> +static unsigned int get_vblank(int fd, int pipe, unsigned int flags)
> +{
> +       union drm_wait_vblank vbl;
> +
> +       memset(&vbl, 0, sizeof(vbl));
> +       vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe) | flags;
> +       if (drmIoctl(fd, DRM_IOCTL_WAIT_VBLANK, &vbl))
> +               return 0;
> +
> +       return vbl.reply.sequence;
> +}
> +
> +static int parse_resolution(res_t *resolution, char *info)
> +{
> +       char size[32];
> +       int ret;
> +
> +       ret = sscanf(info + 4, "%d%*c %*s %*s %*s %s%*c",
> +                    &resolution->id, size);
> +
> +       if (ret != 2)
> +               return -1;
> +
> +       ret = sscanf(size + 6, "%d%*c%d%*c",
> +                    &resolution->width, &resolution->height);
> +
> +       if (ret != 2)
> +               return -1;
> +
> +       return ret + 1;
> +}

Could all these be helpers?

> +static bool get_visibility(FILE *fid, res_t *resolution)
> +{
> +       char tmp[256];
> +       res_t plane;
> +       int ret;
> +
> +       while (fgets(tmp, 256, fid) != NULL) {
> +               if (strstr(tmp, "type=OVL") != NULL) {
> +                       ret = sscanf(tmp + 12, "%d%*c %*s %*s %d%*c%d%*c",
> +                                    &plane.id, &plane.width, &plane.height);
> +
> +                       igt_assert_eq(ret, 3);
> +
> +                       if (plane.width > resolution->width)
> +                               return false;
> +                       else if (plane.height > resolution->height)
> +                               return false;
> +                       else
> +                               return true;
> +               }
> +       }
> +
> +       return false;
> +}
> +
> +static bool plane_visible(void)
> +{
> +       char tmp[256];
> +       FILE *fid;
> +       bool visible = false;
> +       res_t resolution;
> +       int ret;
> +
> +       fid  = fopen("/sys/kernel/debug/dri/0/i915_display_info", "r");
> +
> +       if (fid == NULL)
> +               return false;
> +
> +       while (fgets(tmp, 256, fid) != NULL) {
> +               if (strstr(tmp, "active=yes") != NULL) {
> +                       ret = parse_resolution(&resolution, tmp);
> +                       igt_assert_eq(ret, 3);
> +                       visible = get_visibility(fid, &resolution);
> +               }
> +       }
> +
> +       fclose(fid);
> +
> +       return visible;
> +}

Could this be made optional, determined from property state?

> +static drmModeModeInfo *
> +test_setup(data_t *data, enum pipe pipe, uint64_t tiling, int flags,
> +          igt_output_t *output)
> +{
> +       drmModeModeInfo *mode;
> +       int x, y;
> +
> +       igt_output_set_pipe(output, pipe);
> +
> +       data->plane[0] = igt_output_get_plane(output, IGT_PLANE_PRIMARY);
> +       data->plane[1] = igt_output_get_plane(output, IGT_PLANE_2);

Could the test skip if these were unavailable?

> +       mode = igt_output_get_mode(output);
> +
> +       x = 0;
> +       y = mode->vdisplay - SIZE;
> +
> +       igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
> +                           DRM_FORMAT_XRGB8888,
> +                           LOCAL_DRM_FORMAT_MOD_NONE,
> +                           0.0, 0.0, 1.0,
> +                           &data->fb[0]);
> +
> +       igt_plane_set_fb(data->plane[0], &data->fb[0]);
> +
> +       /* yellow sprite plane in lower left corner */
> +       igt_create_color_fb(data->drm_fd,
> +                           SIZE, SIZE,
> +                           DRM_FORMAT_XRGB8888,
> +                           tiling,

s/tiling/modifier/

> +static void
> +test_plane_position_with_output(data_t *data, enum pipe pipe,
> +                               igt_output_t *output, uint64_t tiling)
> +{
> +       igt_crc_t *crc_hires1, *crc_hires2;
> +       igt_crc_t *crc_lowres;
> +       drmModeModeInfo std_1024_mode = {
> +               .clock = 65000,
> +               .hdisplay = 1024,
> +               .hsync_start = 1048,
> +               .hsync_end = 1184,
> +               .htotal = 1344,
> +               .hskew = 0,
> +               .vdisplay = 768,
> +               .vsync_start = 771,
> +               .vsync_end = 777,
> +               .vtotal = 806,
> +               .vscan = 0,
> +               .vrefresh = 60,
> +               .flags = 0xA,
> +               .type = 0x40,
> +               .name = "Custom 1024x768",
> +       };

Could this mode only be used as a fallback, with the mode list first
being scanned to see if the output natively offers a mode small enough
to pass the test?

> +       i = 0;
> +       while (i < iterations || loop_forever) {
> +               /* switch to lower resolution */
> +               igt_output_override_mode(output, &std_1024_mode);
> +               igt_output_set_pipe(output, pipe);
> +
> +               mode2 = igt_output_get_mode(output);
> +
> +               igt_assert_eq(std_1024_mode.hdisplay, mode2->hdisplay);
> +               igt_assert_eq(std_1024_mode.vdisplay, mode2->vdisplay);
> +               igt_assert_eq(std_1024_mode.vrefresh, mode2->vrefresh);
> +
> +               display_commit_mode(data, pipe, flags, crc_lowres);

Could the test skip if this fails? Either because the output does not
support the mode, or because some hardware does not allow planes to be
marked as visible but offscreen.

> +static void
> +run_tests_for_pipe(data_t *data, enum pipe pipe)
> +{
> +       igt_subtest_f("pipe-%s-tiling-none",
> +                     kmstest_pipe_name(pipe))
> +               test_plane_position(data, pipe, LOCAL_I915_FORMAT_MOD_X_TILED);
> +
> +       igt_subtest_f("pipe-%s-tiling-x",
> +                     kmstest_pipe_name(pipe))
> +               test_plane_position(data, pipe, LOCAL_I915_FORMAT_MOD_X_TILED);
> +
> +       igt_subtest_f("pipe-%s-tiling-y",
> +                     kmstest_pipe_name(pipe))
> +               test_plane_position(data, pipe, LOCAL_I915_FORMAT_MOD_Y_TILED);
> +
> +       igt_subtest_f("pipe-%s-tiling-yf",
> +                     kmstest_pipe_name(pipe))
> +               test_plane_position(data, pipe, LOCAL_I915_FORMAT_MOD_Yf_TILED);
> +}

Is there any reason to not test MOD_NONE as well?

> +       igt_fixture {
> +               data.drm_fd = drm_open_driver_master(DRIVER_INTEL);

With all these, you should at least be very close to making this DRIVER_ANY.

Cheers,
Daniel
Kahola, Mika Nov. 16, 2016, 11:37 a.m. UTC | #2
> -----Original Message-----

> From: Daniel Stone [mailto:daniel@fooishbar.org]

> Sent: Tuesday, November 15, 2016 3:20 PM

> To: Kahola, Mika <mika.kahola@intel.com>

> Cc: intel-gfx <intel-gfx@lists.freedesktop.org>

> Subject: Re: [Intel-gfx] [PATCH i-g-t] tests/kms_plane_lowres: Plane visibility

> after atomic modesets

> 

> Hi Mika,

> 

> On 15 November 2016 at 12:38, Mika Kahola <mika.kahola@intel.com> wrote:

> > Testcase for plane visibility after atomic modesets. The idea of the

> > test is the following:

> >

> >  - draw a blue screen with high resolution

> >  - enable a yellow plane, visible, in lower-left corner

> >  - set a new lower resolution mode (1024x768) that makes plane

> > invisible

> >  - check from debugfs 'i915_display_info' that the plane is invisible

> >  - switch back to higher resolution mode

> >  - check from debugfs 'i915_display_info' that the plane is visible

> > again

> >  - repeat number of iterations, default 64

> 

> It would be nice to have this for non-Intel drivers as well, with these

> suggestions:

Thanks for the review and feedback! 

I think we can modify this to suite non-Intel drivers as well.

> 

> > +static inline uint32_t pipe_select(int pipe) {

> > +       if (pipe > 1)

> > +               return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;

> > +       else if (pipe > 0)

> > +               return DRM_VBLANK_SECONDARY;

> > +       else

> > +               return 0;

> > +}

> > +

> > +static unsigned int get_vblank(int fd, int pipe, unsigned int flags)

> > +{

> > +       union drm_wait_vblank vbl;

> > +

> > +       memset(&vbl, 0, sizeof(vbl));

> > +       vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe) | flags;

> > +       if (drmIoctl(fd, DRM_IOCTL_WAIT_VBLANK, &vbl))

> > +               return 0;

> > +

> > +       return vbl.reply.sequence;

> > +}

> > +

> > +static int parse_resolution(res_t *resolution, char *info) {

> > +       char size[32];

> > +       int ret;

> > +

> > +       ret = sscanf(info + 4, "%d%*c %*s %*s %*s %s%*c",

> > +                    &resolution->id, size);

> > +

> > +       if (ret != 2)

> > +               return -1;

> > +

> > +       ret = sscanf(size + 6, "%d%*c%d%*c",

> > +                    &resolution->width, &resolution->height);

> > +

> > +       if (ret != 2)

> > +               return -1;

> > +

> > +       return ret + 1;

> > +}

> 

> Could all these be helpers?

This crossed my mind too. I was thinking to do some cleanup after this one. For example the routine get_vblank() is called from other tests as well so it does make sense to move these as helpers.

> 

> > +static bool get_visibility(FILE *fid, res_t *resolution) {

> > +       char tmp[256];

> > +       res_t plane;

> > +       int ret;

> > +

> > +       while (fgets(tmp, 256, fid) != NULL) {

> > +               if (strstr(tmp, "type=OVL") != NULL) {

> > +                       ret = sscanf(tmp + 12, "%d%*c %*s %*s %d%*c%d%*c",

> > +                                    &plane.id, &plane.width,

> > + &plane.height);

> > +

> > +                       igt_assert_eq(ret, 3);

> > +

> > +                       if (plane.width > resolution->width)

> > +                               return false;

> > +                       else if (plane.height > resolution->height)

> > +                               return false;

> > +                       else

> > +                               return true;

> > +               }

> > +       }

> > +

> > +       return false;

> > +}

> > +

> > +static bool plane_visible(void)

> > +{

> > +       char tmp[256];

> > +       FILE *fid;

> > +       bool visible = false;

> > +       res_t resolution;

> > +       int ret;

> > +

> > +       fid  = fopen("/sys/kernel/debug/dri/0/i915_display_info",

> > + "r");

> > +

> > +       if (fid == NULL)

> > +               return false;

> > +

> > +       while (fgets(tmp, 256, fid) != NULL) {

> > +               if (strstr(tmp, "active=yes") != NULL) {

> > +                       ret = parse_resolution(&resolution, tmp);

> > +                       igt_assert_eq(ret, 3);

> > +                       visible = get_visibility(fid, &resolution);

> > +               }

> > +       }

> > +

> > +       fclose(fid);

> > +

> > +       return visible;

> > +}

> 

> Could this be made optional, determined from property state?

The idea of the test case is to test plane visibility when switching resolution. Therefore, I wouldn't make this optional.
> 

> > +static drmModeModeInfo *

> > +test_setup(data_t *data, enum pipe pipe, uint64_t tiling, int flags,

> > +          igt_output_t *output)

> > +{

> > +       drmModeModeInfo *mode;

> > +       int x, y;

> > +

> > +       igt_output_set_pipe(output, pipe);

> > +

> > +       data->plane[0] = igt_output_get_plane(output, IGT_PLANE_PRIMARY);

> > +       data->plane[1] = igt_output_get_plane(output, IGT_PLANE_2);

> 

> Could the test skip if these were unavailable?

You're right, we need to test that these planes are available.
> 

> > +       mode = igt_output_get_mode(output);

> > +

> > +       x = 0;

> > +       y = mode->vdisplay - SIZE;

> > +

> > +       igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,

> > +                           DRM_FORMAT_XRGB8888,

> > +                           LOCAL_DRM_FORMAT_MOD_NONE,

> > +                           0.0, 0.0, 1.0,

> > +                           &data->fb[0]);

> > +

> > +       igt_plane_set_fb(data->plane[0], &data->fb[0]);

> > +

> > +       /* yellow sprite plane in lower left corner */

> > +       igt_create_color_fb(data->drm_fd,

> > +                           SIZE, SIZE,

> > +                           DRM_FORMAT_XRGB8888,

> > +                           tiling,

> 

> s/tiling/modifier/

Ok

> 

> > +static void

> > +test_plane_position_with_output(data_t *data, enum pipe pipe,

> > +                               igt_output_t *output, uint64_t tiling)

> > +{

> > +       igt_crc_t *crc_hires1, *crc_hires2;

> > +       igt_crc_t *crc_lowres;

> > +       drmModeModeInfo std_1024_mode = {

> > +               .clock = 65000,

> > +               .hdisplay = 1024,

> > +               .hsync_start = 1048,

> > +               .hsync_end = 1184,

> > +               .htotal = 1344,

> > +               .hskew = 0,

> > +               .vdisplay = 768,

> > +               .vsync_start = 771,

> > +               .vsync_end = 777,

> > +               .vtotal = 806,

> > +               .vscan = 0,

> > +               .vrefresh = 60,

> > +               .flags = 0xA,

> > +               .type = 0x40,

> > +               .name = "Custom 1024x768",

> > +       };

> 

> Could this mode only be used as a fallback, with the mode list first being scanned

> to see if the output natively offers a mode small enough to pass the test?

That is an option. I chose to use this one as this mode was applied with other kms test, see for example 'kms_frontbuffer_tracking.c'.

It is true, that I should at least test that the original mode is big enough that with this mode the plane has theoretical possibility to be invisible.

> 

> > +       i = 0;

> > +       while (i < iterations || loop_forever) {

> > +               /* switch to lower resolution */

> > +               igt_output_override_mode(output, &std_1024_mode);

> > +               igt_output_set_pipe(output, pipe);

> > +

> > +               mode2 = igt_output_get_mode(output);

> > +

> > +               igt_assert_eq(std_1024_mode.hdisplay, mode2->hdisplay);

> > +               igt_assert_eq(std_1024_mode.vdisplay, mode2->vdisplay);

> > +               igt_assert_eq(std_1024_mode.vrefresh,

> > + mode2->vrefresh);

> > +

> > +               display_commit_mode(data, pipe, flags, crc_lowres);

> 

> Could the test skip if this fails? Either because the output does not support the

> mode, or because some hardware does not allow planes to be marked as visible

> but offscreen.

Yes, test could be skipped if mode commit fails. Or would it be better if the test would fail in this case? 

> 

> > +static void

> > +run_tests_for_pipe(data_t *data, enum pipe pipe) {

> > +       igt_subtest_f("pipe-%s-tiling-none",

> > +                     kmstest_pipe_name(pipe))

> > +               test_plane_position(data, pipe,

> > +LOCAL_I915_FORMAT_MOD_X_TILED);

> > +

> > +       igt_subtest_f("pipe-%s-tiling-x",

> > +                     kmstest_pipe_name(pipe))

> > +               test_plane_position(data, pipe,

> > + LOCAL_I915_FORMAT_MOD_X_TILED);

> > +

> > +       igt_subtest_f("pipe-%s-tiling-y",

> > +                     kmstest_pipe_name(pipe))

> > +               test_plane_position(data, pipe,

> > + LOCAL_I915_FORMAT_MOD_Y_TILED);

> > +

> > +       igt_subtest_f("pipe-%s-tiling-yf",

> > +                     kmstest_pipe_name(pipe))

> > +               test_plane_position(data, pipe,

> > +LOCAL_I915_FORMAT_MOD_Yf_TILED); }

> 

> Is there any reason to not test MOD_NONE as well?

This is my bad. MODE_NONE should be tested. I will add this test when spinning another round of this.

Cheers,
Mika
> 

> > +       igt_fixture {

> > +               data.drm_fd = drm_open_driver_master(DRIVER_INTEL);

> 

> With all these, you should at least be very close to making this DRIVER_ANY.

> 

> Cheers,

> Daniel
diff mbox

Patch

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index debe3df..9ebdc44 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -109,6 +109,7 @@  TESTS_progs_M = \
 	kms_pipe_crc_basic \
 	kms_plane \
 	kms_plane_multiple \
+	kms_plane_lowres \
 	kms_properties \
 	kms_psr_sink_crc \
 	kms_render \
diff --git a/tests/kms_plane_lowres.c b/tests/kms_plane_lowres.c
new file mode 100644
index 0000000..6735a3b
--- /dev/null
+++ b/tests/kms_plane_lowres.c
@@ -0,0 +1,424 @@ 
+/*
+ * Copyright © 2016 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.
+ *
+ */
+
+#include "igt.h"
+#include "drmtest.h"
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+IGT_TEST_DESCRIPTION("Test atomic mode setting with a plane by switching between high and low resolutions");
+
+#define MAX_CRCS          1
+#define SIZE            256
+#define LOOP_FOREVER     -1
+
+typedef struct {
+	int drm_fd;
+	igt_display_t display;
+	igt_pipe_crc_t *pipe_crc;
+	igt_plane_t *plane[2];
+	struct igt_fb fb[2];
+} data_t;
+
+typedef struct {
+	int id;
+	int width;
+	int height;
+} res_t;
+
+/* Command line parameters. */
+struct {
+	int iterations;
+} opt = {
+	.iterations = 64,
+};
+
+static inline uint32_t pipe_select(int pipe)
+{
+	if (pipe > 1)
+		return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
+	else if (pipe > 0)
+		return DRM_VBLANK_SECONDARY;
+	else
+		return 0;
+}
+
+static unsigned int get_vblank(int fd, int pipe, unsigned int flags)
+{
+	union drm_wait_vblank vbl;
+
+	memset(&vbl, 0, sizeof(vbl));
+	vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe) | flags;
+	if (drmIoctl(fd, DRM_IOCTL_WAIT_VBLANK, &vbl))
+		return 0;
+
+	return vbl.reply.sequence;
+}
+
+static int parse_resolution(res_t *resolution, char *info)
+{
+	char size[32];
+	int ret;
+
+	ret = sscanf(info + 4, "%d%*c %*s %*s %*s %s%*c",
+		     &resolution->id, size);
+
+	if (ret != 2)
+		return -1;
+
+	ret = sscanf(size + 6, "%d%*c%d%*c",
+		     &resolution->width, &resolution->height);
+
+	if (ret != 2)
+		return -1;
+
+	return ret + 1;
+}
+
+static bool get_visibility(FILE *fid, res_t *resolution)
+{
+	char tmp[256];
+	res_t plane;
+	int ret;
+
+	while (fgets(tmp, 256, fid) != NULL) {
+		if (strstr(tmp, "type=OVL") != NULL) {
+			ret = sscanf(tmp + 12, "%d%*c %*s %*s %d%*c%d%*c",
+				     &plane.id, &plane.width, &plane.height);
+
+			igt_assert_eq(ret, 3);
+
+			if (plane.width > resolution->width)
+				return false;
+			else if (plane.height > resolution->height)
+				return false;
+			else
+				return true;
+		}
+	}
+
+	return false;
+}
+
+static bool plane_visible(void)
+{
+	char tmp[256];
+	FILE *fid;
+	bool visible = false;
+	res_t resolution;
+	int ret;
+
+	fid  = fopen("/sys/kernel/debug/dri/0/i915_display_info", "r");
+
+	if (fid == NULL)
+		return false;
+
+	while (fgets(tmp, 256, fid) != NULL) {
+		if (strstr(tmp, "active=yes") != NULL) {
+			ret = parse_resolution(&resolution, tmp);
+			igt_assert_eq(ret, 3);
+			visible = get_visibility(fid, &resolution);
+		}
+	}
+
+	fclose(fid);
+
+	return visible;
+}
+
+static void test_init(data_t *data, enum pipe pipe)
+{
+	data->pipe_crc = igt_pipe_crc_new(pipe, INTEL_PIPE_CRC_SOURCE_AUTO);
+}
+
+static void test_fini(data_t *data, igt_output_t *output)
+{
+	/* restore original mode */
+	igt_output_override_mode(output, NULL);
+
+	for (int i = 0; i < 2; i++)
+		igt_plane_set_fb(data->plane[i], NULL);
+
+	/* reset the constraint on the pipe */
+	igt_output_set_pipe(output, PIPE_ANY);
+
+	igt_pipe_crc_free(data->pipe_crc);
+}
+
+static int
+display_commit_mode(data_t *data, enum pipe pipe, int flags, igt_crc_t *crc)
+{
+	char buf[256];
+	struct drm_event *e = (void *)buf;
+	unsigned int vblank_start, vblank_stop;
+	int n, ret;
+
+	vblank_start = get_vblank(data->display.drm_fd, pipe,
+				  DRM_VBLANK_NEXTONMISS);
+
+	ret = igt_display_try_commit_atomic(&data->display,
+					    flags,
+					    NULL);
+	igt_skip_on(ret != 0);
+
+	igt_set_timeout(1, "Stuck on page flip");
+	ret = read(data->display.drm_fd, buf, sizeof(buf));
+	igt_assert(ret >= 0);
+
+	vblank_stop = get_vblank(data->display.drm_fd, pipe, 0);
+	igt_assert_eq(e->type, DRM_EVENT_FLIP_COMPLETE);
+	igt_reset_timeout();
+
+	n = igt_pipe_crc_get_crcs(data->pipe_crc, vblank_stop - vblank_start,
+				  &crc);
+	igt_assert_eq(n, vblank_stop - vblank_start);
+
+	return n;
+}
+
+static drmModeModeInfo *
+test_setup(data_t *data, enum pipe pipe, uint64_t tiling, int flags,
+	   igt_output_t *output)
+{
+	drmModeModeInfo *mode;
+	int x, y;
+
+	igt_output_set_pipe(output, pipe);
+
+	data->plane[0] = igt_output_get_plane(output, IGT_PLANE_PRIMARY);
+	data->plane[1] = igt_output_get_plane(output, IGT_PLANE_2);
+
+	mode = igt_output_get_mode(output);
+
+	x = 0;
+	y = mode->vdisplay - SIZE;
+
+	igt_create_color_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
+			    DRM_FORMAT_XRGB8888,
+			    LOCAL_DRM_FORMAT_MOD_NONE,
+			    0.0, 0.0, 1.0,
+			    &data->fb[0]);
+
+	igt_plane_set_fb(data->plane[0], &data->fb[0]);
+
+	/* yellow sprite plane in lower left corner */
+	igt_create_color_fb(data->drm_fd,
+			    SIZE, SIZE,
+			    DRM_FORMAT_XRGB8888,
+			    tiling,
+			    1.0, 1.0, 0.0,
+			    &data->fb[1]);
+
+	igt_plane_set_position(data->plane[1], x, y);
+	igt_plane_set_fb(data->plane[1], &data->fb[1]);
+
+	return mode;
+}
+
+static void
+test_plane_position_with_output(data_t *data, enum pipe pipe,
+				igt_output_t *output, uint64_t tiling)
+{
+	igt_crc_t *crc_hires1, *crc_hires2;
+	igt_crc_t *crc_lowres;
+	drmModeModeInfo std_1024_mode = {
+		.clock = 65000,
+		.hdisplay = 1024,
+		.hsync_start = 1048,
+		.hsync_end = 1184,
+		.htotal = 1344,
+		.hskew = 0,
+		.vdisplay = 768,
+		.vsync_start = 771,
+		.vsync_end = 777,
+		.vtotal = 806,
+		.vscan = 0,
+		.vrefresh = 60,
+		.flags = 0xA,
+		.type = 0x40,
+		.name = "Custom 1024x768",
+	};
+	int flags = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_ALLOW_MODESET;
+	drmModeModeInfo *mode1, *mode2, *mode3;
+	int ret, i, n;
+	int iterations = opt.iterations < 1 ? 1 : opt.iterations;
+	bool loop_forever;
+	char info[256];
+
+	if (opt.iterations == LOOP_FOREVER) {
+		loop_forever = true;
+		sprintf(info, "forever");
+	} else {
+		loop_forever = false;
+		sprintf(info, "for %d %s",
+			iterations, iterations > 1 ? "iterations" : "iteration");
+	}
+
+	igt_info("Testing connector %s using pipe %s %s\n",
+		 igt_output_name(output), kmstest_pipe_name(pipe), info);
+
+	test_init(data, pipe);
+
+	mode1 = test_setup(data, pipe, tiling, flags, output);
+
+	igt_pipe_crc_start(data->pipe_crc);
+	ret = igt_display_try_commit2(&data->display, COMMIT_ATOMIC);
+	igt_skip_on(ret != 0);
+
+	n = igt_pipe_crc_get_crcs(data->pipe_crc, 1, &crc_hires1);
+	igt_assert_eq(1, n);
+
+	igt_assert_eq(plane_visible(), true);
+
+	i = 0;
+	while (i < iterations || loop_forever) {
+		/* switch to lower resolution */
+		igt_output_override_mode(output, &std_1024_mode);
+		igt_output_set_pipe(output, pipe);
+
+		mode2 = igt_output_get_mode(output);
+
+		igt_assert_eq(std_1024_mode.hdisplay, mode2->hdisplay);
+		igt_assert_eq(std_1024_mode.vdisplay, mode2->vdisplay);
+		igt_assert_eq(std_1024_mode.vrefresh, mode2->vrefresh);
+
+		display_commit_mode(data, pipe, flags, crc_lowres);
+		igt_assert_eq(plane_visible(), false);
+
+		/* switch back to higher resolution */
+		igt_output_override_mode(output, NULL);
+		igt_output_set_pipe(output, pipe);
+
+		mode3 = igt_output_get_mode(output);
+
+		igt_assert_eq(mode1->hdisplay, mode3->hdisplay);
+		igt_assert_eq(mode1->vdisplay, mode3->vdisplay);
+		igt_assert_eq(mode1->vrefresh, mode3->vrefresh);
+
+		display_commit_mode(data, pipe, flags, crc_hires2);
+		igt_assert_eq(plane_visible(), true);
+
+		i++;
+	}
+
+	igt_pipe_crc_stop(data->pipe_crc);
+
+	test_fini(data, output);
+}
+
+static void
+test_plane_position(data_t *data, enum pipe pipe, uint64_t tiling)
+{
+	igt_output_t *output;
+	int connected_outs;
+
+	igt_require(data->display.is_atomic);
+	igt_skip_on(pipe >= data->display.n_pipes);
+
+	connected_outs = 0;
+	for_each_connected_output(&data->display, output) {
+		test_plane_position_with_output(data, pipe, output, tiling);
+		connected_outs++;
+	}
+
+	igt_skip_on(connected_outs == 0);
+
+}
+
+static void
+run_tests_for_pipe(data_t *data, enum pipe pipe)
+{
+	igt_subtest_f("pipe-%s-tiling-none",
+		      kmstest_pipe_name(pipe))
+		test_plane_position(data, pipe, LOCAL_I915_FORMAT_MOD_X_TILED);
+
+	igt_subtest_f("pipe-%s-tiling-x",
+		      kmstest_pipe_name(pipe))
+		test_plane_position(data, pipe, LOCAL_I915_FORMAT_MOD_X_TILED);
+
+	igt_subtest_f("pipe-%s-tiling-y",
+		      kmstest_pipe_name(pipe))
+		test_plane_position(data, pipe, LOCAL_I915_FORMAT_MOD_Y_TILED);
+
+	igt_subtest_f("pipe-%s-tiling-yf",
+		      kmstest_pipe_name(pipe))
+		test_plane_position(data, pipe, LOCAL_I915_FORMAT_MOD_Yf_TILED);
+}
+
+static data_t data;
+
+static int opt_handler(int option, int option_index, void *input)
+{
+	switch (option) {
+	case 'i':
+		opt.iterations = strtol(optarg, NULL, 0);
+
+		if (opt.iterations < LOOP_FOREVER || opt.iterations == 0) {
+			igt_info("incorrect number of iterations\n");
+			igt_assert(false);
+		}
+		break;
+	default:
+		igt_assert(false);
+	}
+
+	return 0;
+}
+
+const char *help_str =
+	"  --iterations Number of iterations for test coverage. -1 loop forever, default 64 iterations\n";
+
+int main(int argc, char *argv[])
+{
+	struct option long_options[] = {
+		{ "iterations", required_argument, NULL, 'i'},
+		{ 0, 0, 0, 0 }
+	};
+
+	igt_subtest_init_parse_opts(&argc, argv, "", long_options, help_str,
+				    opt_handler, NULL);
+
+	igt_skip_on_simulation();
+
+	igt_fixture {
+		data.drm_fd = drm_open_driver_master(DRIVER_INTEL);
+
+		kmstest_set_vt_graphics_mode();
+
+		igt_require_pipe_crc();
+		igt_display_init(&data.display, data.drm_fd);
+	}
+
+	for (int pipe = 0; pipe < I915_MAX_PIPES; pipe++)
+		run_tests_for_pipe(&data, pipe);
+
+	igt_fixture {
+		igt_display_fini(&data.display);
+	}
+
+	igt_exit();
+}