Upgrade igt-gpu-tools to 357dbe1869d88a2f08bcee4eebceff4ee9014424 am: 22248ce8b1
am: 141ed12fec

Change-Id: I2186dc70396f9ce334150239bb0a6df5d0b3e30c
diff --git a/METADATA b/METADATA
index aa97f6f..518c48c 100644
--- a/METADATA
+++ b/METADATA
@@ -5,11 +5,11 @@
     type: GIT
     value: "https://gitlab.freedesktop.org/drm/igt-gpu-tools"
   }
-  version: "1a5b48671e0863cb723e3d0239e54c828360dc99"
+  version: "357dbe1869d88a2f08bcee4eebceff4ee9014424"
   license_type: NOTICE
   last_upgrade_date {
     year: 2019
-    month: 7
-    day: 22
+    month: 8
+    day: 21
   }
 }
diff --git a/README.md b/README.md
index 865ed6e..b8c4c5a 100644
--- a/README.md
+++ b/README.md
@@ -139,13 +139,16 @@
 This is a non-exhaustive list of package dependencies required for building
 the default configuration (package names may vary):
 
+	bison
 	gtk-doc-tools
+	flex
 	libcairo2-dev
 	libdrm-dev
 	libkmod-dev
 	libpixman-1-dev
 	libpciaccess-dev
 	libprocps-dev
+	libudev-dev
 	libunwind-dev
 	liblzma-dev
 	libdw-dev
diff --git a/docs/chamelium.txt b/docs/chamelium.txt
index aaa0646..32c296f 100644
--- a/docs/chamelium.txt
+++ b/docs/chamelium.txt
@@ -77,47 +77,43 @@
 By default, this file is expected to exist in ~/.igtrc
 In order to run tests using the Chamelium, a valid configuration file must be
 present. It must contain Chamelium-specific keys as shown with the following
-example:
+example (only Chamelium.URL is mandatory):
 
-# The common configuration section follows.
-[Common]
-# The path to dump frames that fail comparison checks
-FrameDumpPath=/root/
+    # The common configuration section follows.
+    [Common]
+    # The path to dump frames that fail comparison checks
+    FrameDumpPath=/root/
 
-# The following section is used for configuring the Device Under Test.
-# It is not mandatory and allows overriding default values.
-[DUT]
-SuspendResumeDelay=15
+    # The following section is used for configuring the Device Under Test.
+    # It is not mandatory and allows overriding default values.
+    [DUT]
+    SuspendResumeDelay=15
 
-[Chamelium]
-# The URL used for connecting to the Chamelium's RPC server
-URL=http://192.168.1.2:9992
+    [Chamelium]
+    # The URL used for connecting to the Chamelium's RPC server
+    URL=http://192.168.1.2:9992
 
-# The rest of the sections are used for defining connector mappings.
-# This is required so any tests using the Chamelium know which connector
-# on the test machine should be connected to each Chamelium port.
-#
-# In the event that any of these mappings are specified incorrectly,
-# any hotplugging tests for the incorrect connector mapping will fail.
+    # The rest of the sections are used for defining connector mappings. This
+    # is optional, the mappings will be discovered automatically.
 
-# The name of the DRM connector
-# The DP-1 of [Chamelium:DP-1] and the HDMI-A-1 of [Chamelium:HDMI-A-1] indicate
-# "connector info type" of /sys/kernel/debug/dri/0/i915_display_info.
-[Chamelium:DP-1]
-# The ChameliumPortID indicates physical port (device) id of a Chamelium Board.
-# A Chamelium daemon program defines these port ids as
-# DP1 (located next to the HDMI port) = 1
-# DP2 (located next to the VGA connector) = 2
-# HDMI = 3 and VGA = 4
-# The port ids are defined at:
-# https://chromium.googlesource.com/chromiumos/platform/chameleon/+/master/chameleond/utils/ids.py
-ChameliumPortID=1
+    # The name of the DRM connector
+    # The DP-1 of [Chamelium:DP-1] and the HDMI-A-1 of [Chamelium:HDMI-A-1] indicate
+    # "connector info type" of /sys/kernel/debug/dri/0/i915_display_info.
+    [Chamelium:DP-1]
+    # The ChameliumPortID indicates physical port (device) id of a Chamelium Board.
+    # A Chamelium daemon program defines these port ids as
+    # DP1 (located next to the HDMI port) = 1
+    # DP2 (located next to the VGA connector) = 2
+    # HDMI = 3 and VGA = 4
+    # The port ids are defined at:
+    # https://chromium.googlesource.com/chromiumos/platform/chameleon/+/master/chameleond/utils/ids.py
+    ChameliumPortID=1
 
-[Chamelium:HDMI-A-2]
-ChameliumPortID=3
+    [Chamelium:HDMI-A-2]
+    ChameliumPortID=3
 
-[Chamelium:VGA-1]
-ChameliumPortID=4
+    [Chamelium:VGA-1]
+    ChameliumPortID=4
 
 Running the Chamelium With IGT
 ------------------------------
diff --git a/lib/Makefile.sources b/lib/Makefile.sources
index e16de86..cf094ab 100644
--- a/lib/Makefile.sources
+++ b/lib/Makefile.sources
@@ -41,6 +41,8 @@
 	igt_gvt.h		\
 	igt_halffloat.c		\
 	igt_halffloat.h		\
+	igt_infoframe.c		\
+	igt_infoframe.h		\
 	igt_matrix.c		\
 	igt_matrix.h		\
 	igt_primes.c		\
diff --git a/lib/i915/gem_ring.c b/lib/i915/gem_ring.c
index fdb9fc1..bf7f439 100644
--- a/lib/i915/gem_ring.c
+++ b/lib/i915/gem_ring.c
@@ -103,7 +103,7 @@
 	} while (1);
 
 	igt_assert_eq(__execbuf(fd, &execbuf), -EINTR);
-	igt_assert(count);
+	igt_assert(count > 1);
 
 	memset(&itv, 0, sizeof(itv));
 	setitimer(ITIMER_REAL, &itv, NULL);
@@ -118,7 +118,8 @@
 
 	gem_quiescent_gpu(fd);
 
-	return count;
+	/* Be conservative in case we must wrap later */
+	return count - 1;
 }
 
 /**
diff --git a/lib/i915/gem_submission.c b/lib/i915/gem_submission.c
index a8bb45c..7602d7f 100644
--- a/lib/i915/gem_submission.c
+++ b/lib/i915/gem_submission.c
@@ -74,7 +74,6 @@
 {
 	const int gen = intel_gen(intel_get_drm_devid(fd));
 	unsigned flags = 0;
-	int result;
 
 	int dir;
 
@@ -87,14 +86,7 @@
 		goto out;
 	}
 
-	if (igt_sysfs_get_boolean(dir, "enable_guc_submission")) {
-		flags |= GEM_SUBMISSION_GUC | GEM_SUBMISSION_EXECLISTS;
-		goto out;
-	}
-
-	if (igt_sysfs_scanf(dir, "enable_execlists", "%d", &result) != 1)
-		result = gen >= 8;
-	if (result) {
+	if (gen >= 8) {
 		flags |= GEM_SUBMISSION_EXECLISTS;
 		goto out;
 	}
diff --git a/lib/igt_chamelium.c b/lib/igt_chamelium.c
index 301e9d2..1b03a10 100644
--- a/lib/igt_chamelium.c
+++ b/lib/igt_chamelium.c
@@ -351,6 +351,32 @@
 	return res;
 }
 
+static bool __chamelium_is_reachable(struct chamelium *chamelium)
+{
+	xmlrpc_value *res;
+
+	/* GetSupportedInputs does not require a port and is harmless */
+	res = __chamelium_rpc(chamelium, NULL, "GetSupportedInputs", "()");
+
+	if (res != NULL)
+		xmlrpc_DECREF(res);
+
+	if (chamelium->env.fault_occurred)
+		igt_debug("Chamelium RPC call failed: %s\n",
+			  chamelium->env.fault_string);
+
+	return !chamelium->env.fault_occurred;
+}
+
+void chamelium_wait_reachable(struct chamelium *chamelium, int timeout)
+{
+	bool chamelium_online = igt_wait(__chamelium_is_reachable(chamelium),
+					 timeout * 1000, 100);
+
+	igt_assert_f(chamelium_online,
+		     "Couldn't connect to Chamelium for %ds", timeout);
+}
+
 /**
  * chamelium_plug:
  * @chamelium: The Chamelium instance to use
@@ -562,10 +588,9 @@
  * Returns: An opaque pointer to the Chamelium EDID
  */
 struct chamelium_edid *chamelium_new_edid(struct chamelium *chamelium,
-					  const unsigned char *raw_edid)
+					  const struct edid *edid)
 {
 	struct chamelium_edid *chamelium_edid;
-	const struct edid *edid = (struct edid *) raw_edid;
 	size_t edid_size = edid_get_size(edid);
 
 	chamelium_edid = calloc(1, sizeof(struct chamelium_edid));
@@ -1124,6 +1149,78 @@
 	return ret;
 }
 
+bool chamelium_supports_get_last_infoframe(struct chamelium *chamelium)
+{
+	return chamelium_supports_method(chamelium, "GetLastInfoFrame");
+}
+
+static const char *
+chamelium_infoframe_type_str(enum chamelium_infoframe_type type)
+{
+	switch (type) {
+	case CHAMELIUM_INFOFRAME_AVI:
+		return "avi";
+	case CHAMELIUM_INFOFRAME_AUDIO:
+		return "audio";
+	case CHAMELIUM_INFOFRAME_MPEG:
+		return "mpeg";
+	case CHAMELIUM_INFOFRAME_VENDOR:
+		return "vendor";
+	}
+	assert(0); /* unreachable */
+}
+
+struct chamelium_infoframe *
+chamelium_get_last_infoframe(struct chamelium *chamelium,
+			     struct chamelium_port *port,
+			     enum chamelium_infoframe_type type)
+{
+	xmlrpc_value *res, *res_version, *res_payload;
+	struct chamelium_infoframe *infoframe;
+	const unsigned char *payload;
+
+	res = chamelium_rpc(chamelium, NULL, "GetLastInfoFrame", "(is)",
+			    port->id, chamelium_infoframe_type_str(type));
+	xmlrpc_struct_find_value(&chamelium->env, res, "version", &res_version);
+	xmlrpc_struct_find_value(&chamelium->env, res, "payload", &res_payload);
+	infoframe = calloc(1, sizeof(*infoframe));
+	xmlrpc_read_int(&chamelium->env, res_version, &infoframe->version);
+	xmlrpc_read_base64(&chamelium->env, res_payload,
+			   &infoframe->payload_size, &payload);
+	/* xmlrpc-c's docs say payload is actually not constant */
+	infoframe->payload = (uint8_t *) payload;
+	xmlrpc_DECREF(res_version);
+	xmlrpc_DECREF(res_payload);
+	xmlrpc_DECREF(res);
+
+	if (infoframe->payload_size == 0) {
+		chamelium_infoframe_destroy(infoframe);
+		return NULL;
+	}
+	return infoframe;
+}
+
+void chamelium_infoframe_destroy(struct chamelium_infoframe *infoframe)
+{
+	free(infoframe->payload);
+	free(infoframe);
+}
+
+bool chamelium_supports_trigger_link_failure(struct chamelium *chamelium)
+{
+	return chamelium_supports_method(chamelium, "TriggerLinkFailure");
+}
+
+/**
+ * chamelium_trigger_link_failure: trigger a link failure on the provided port.
+ */
+void chamelium_trigger_link_failure(struct chamelium *chamelium,
+				    struct chamelium_port *port)
+{
+	xmlrpc_DECREF(chamelium_rpc(chamelium, port, "TriggerLinkFailure",
+				    "(i)", port->id));
+}
+
 bool chamelium_has_audio_support(struct chamelium *chamelium,
 				 struct chamelium_port *port)
 {
diff --git a/lib/igt_chamelium.h b/lib/igt_chamelium.h
index b6b7eb4..08705a9 100644
--- a/lib/igt_chamelium.h
+++ b/lib/igt_chamelium.h
@@ -34,6 +34,7 @@
 #include "igt_debugfs.h"
 
 struct igt_fb;
+struct edid;
 
 struct chamelium;
 struct chamelium_port;
@@ -65,6 +66,19 @@
 	int channels;
 };
 
+enum chamelium_infoframe_type {
+	CHAMELIUM_INFOFRAME_AVI,
+	CHAMELIUM_INFOFRAME_AUDIO,
+	CHAMELIUM_INFOFRAME_MPEG,
+	CHAMELIUM_INFOFRAME_VENDOR,
+};
+
+struct chamelium_infoframe {
+	int version;
+	size_t payload_size;
+	uint8_t *payload;
+};
+
 struct chamelium_edid;
 
 /**
@@ -98,6 +112,7 @@
 					       bool reprobe);
 const char *chamelium_port_get_name(struct chamelium_port *port);
 
+void chamelium_wait_reachable(struct chamelium *chamelium, int timeout);
 void chamelium_plug(struct chamelium *chamelium, struct chamelium_port *port);
 void chamelium_unplug(struct chamelium *chamelium, struct chamelium_port *port);
 bool chamelium_is_plugged(struct chamelium *chamelium,
@@ -114,7 +129,7 @@
 				   struct chamelium_port *port, int delay_ms,
 				   bool rising_edge);
 struct chamelium_edid *chamelium_new_edid(struct chamelium *chamelium,
-					  const unsigned char *edid);
+					  const struct edid *edid);
 const struct edid *chamelium_edid_get_raw(struct chamelium_edid *edid,
 					  struct chamelium_port *port);
 void chamelium_port_set_edid(struct chamelium *chamelium,
@@ -141,6 +156,14 @@
 void chamelium_stop_capture(struct chamelium *chamelium, int frame_count);
 void chamelium_capture(struct chamelium *chamelium, struct chamelium_port *port,
 		       int x, int y, int w, int h, int frame_count);
+bool chamelium_supports_get_last_infoframe(struct chamelium *chamelium);
+struct chamelium_infoframe *
+chamelium_get_last_infoframe(struct chamelium *chamelium,
+			     struct chamelium_port *port,
+			     enum chamelium_infoframe_type type);
+bool chamelium_supports_trigger_link_failure(struct chamelium *chamelium);
+void chamelium_trigger_link_failure(struct chamelium *chamelium,
+				    struct chamelium_port *port);
 bool chamelium_has_audio_support(struct chamelium *chamelium,
 				 struct chamelium_port *port);
 void chamelium_get_audio_channel_mapping(struct chamelium *chamelium,
@@ -185,5 +208,6 @@
 				 int height);
 void chamelium_destroy_frame_dump(struct chamelium_frame_dump *dump);
 void chamelium_destroy_audio_file(struct chamelium_audio_file *audio_file);
+void chamelium_infoframe_destroy(struct chamelium_infoframe *infoframe);
 
 #endif /* IGT_CHAMELIUM_H */
diff --git a/lib/igt_edid.c b/lib/igt_edid.c
index 1ad8897..1c85486 100644
--- a/lib/igt_edid.c
+++ b/lib/igt_edid.c
@@ -296,12 +296,24 @@
 }
 
 /**
- * edid_update_checksum: compute and update the EDID checksum
+ * edid_update_checksum: compute and update the checksums of the main EDID
+ * block and all extension blocks
  */
 void edid_update_checksum(struct edid *edid)
 {
+	size_t i;
+	struct edid_ext *ext;
+
 	edid->checksum = compute_checksum((uint8_t *) edid,
 					  sizeof(struct edid));
+
+	for (i = 0; i < edid->extensions_len; i++) {
+		ext = &edid->extensions[i];
+		if (ext->tag == EDID_EXT_CEA)
+			ext->data.cea.checksum =
+				compute_checksum((uint8_t *) ext,
+						 sizeof(struct edid_ext));
+	}
 }
 
 /**
@@ -465,9 +477,3 @@
 	cea->dtd_start = 4 + data_blocks_size;
 	cea->misc = flags | num_native_dtds;
 }
-
-void edid_ext_update_cea_checksum(struct edid_ext *ext)
-{
-	ext->data.cea.checksum = compute_checksum((uint8_t *) ext,
-						  sizeof(struct edid_ext));
-}
diff --git a/lib/igt_edid.h b/lib/igt_edid.h
index 606541a..59b47a9 100644
--- a/lib/igt_edid.h
+++ b/lib/igt_edid.h
@@ -32,6 +32,8 @@
 
 #include <xf86drmMode.h>
 
+#define EDID_BLOCK_SIZE 128
+
 /**
  * est_timings: set of established timings
  */
@@ -236,7 +238,7 @@
 	uint8_t flags1; /* enum hdmi_vsdb_flags1 */
 	uint8_t max_tdms_clock; /* multiply by 5MHz */
 	uint8_t flags2; /* enum hdmi_vsdb_flags2 */
-	char data[]; /* latency, misc, VIC, 3D */
+	uint8_t data[]; /* latency, misc, VIC, 3D */
 } __attribute__((packed));
 
 #define HDMI_VSDB_MIN_SIZE 2 /* just the source physical address */
@@ -366,7 +368,6 @@
 
 void cea_sad_init_pcm(struct cea_sad *sad, int channels,
 		      uint8_t sampling_rates, uint8_t sample_sizes);
-void edid_ext_update_cea_checksum(struct edid_ext *ext);
 const struct cea_vsdb *cea_vsdb_get_hdmi_default(size_t *size);
 size_t edid_cea_data_block_set_sad(struct edid_cea_data_block *block,
 				   const struct cea_sad *sads, size_t sads_len);
diff --git a/lib/igt_fb.c b/lib/igt_fb.c
index 5dc74a0..bad3eec 100644
--- a/lib/igt_fb.c
+++ b/lib/igt_fb.c
@@ -485,12 +485,10 @@
 		return format->num_planes;
 }
 
-static void fb_init(struct igt_fb *fb,
-		    int fd, int width, int height,
-		    uint32_t drm_format,
-		    uint64_t modifier,
-		    enum igt_color_encoding color_encoding,
-		    enum igt_color_range color_range)
+void igt_init_fb(struct igt_fb *fb, int fd, int width, int height,
+		 uint32_t drm_format, uint64_t modifier,
+		 enum igt_color_encoding color_encoding,
+		 enum igt_color_range color_range)
 {
 	const struct format_desc_struct *f = lookup_drm_format(drm_format);
 
@@ -627,8 +625,8 @@
 {
 	struct igt_fb fb;
 
-	fb_init(&fb, fd, width, height, drm_format, modifier,
-		IGT_COLOR_YCBCR_BT709, IGT_COLOR_YCBCR_LIMITED_RANGE);
+	igt_init_fb(&fb, fd, width, height, drm_format, modifier,
+		    IGT_COLOR_YCBCR_BT709, IGT_COLOR_YCBCR_LIMITED_RANGE);
 
 	fb.size = calc_fb_size(&fb);
 
@@ -855,8 +853,8 @@
 			  uint32_t format, uint64_t modifier,
 			  struct igt_fb *fb /* out */)
 {
-	fb_init(fb, fd, width, height, format, modifier,
-		IGT_COLOR_YCBCR_BT709, IGT_COLOR_YCBCR_LIMITED_RANGE);
+	igt_init_fb(fb, fd, width, height, format, modifier,
+		    IGT_COLOR_YCBCR_BT709, IGT_COLOR_YCBCR_LIMITED_RANGE);
 	create_bo_for_fb(fb);
 }
 
@@ -885,8 +883,8 @@
 {
 	struct igt_fb fb;
 
-	fb_init(&fb, fd, width, height, format, modifier,
-		IGT_COLOR_YCBCR_BT709, IGT_COLOR_YCBCR_LIMITED_RANGE);
+	igt_init_fb(&fb, fd, width, height, format, modifier,
+		    IGT_COLOR_YCBCR_BT709, IGT_COLOR_YCBCR_LIMITED_RANGE);
 
 	for (int i = 0; i < fb.num_planes; i++)
 		fb.strides[i] = stride;
@@ -1441,8 +1439,8 @@
 {
 	uint32_t flags = 0;
 
-	fb_init(fb, fd, width, height, format, modifier,
-		color_encoding, color_range);
+	igt_init_fb(fb, fd, width, height, format, modifier,
+		    color_encoding, color_range);
 
 	for (int i = 0; i < fb->num_planes; i++)
 		fb->strides[i] = bo_stride;
@@ -1974,9 +1972,9 @@
 	 * destination, tiling it at the same time.
 	 */
 
-	fb_init(&linear->fb, fb->fd, fb->width, fb->height,
-		fb->drm_format, LOCAL_DRM_FORMAT_MOD_NONE,
-		fb->color_encoding, fb->color_range);
+	igt_init_fb(&linear->fb, fb->fd, fb->width, fb->height,
+		    fb->drm_format, LOCAL_DRM_FORMAT_MOD_NONE,
+		    fb->color_encoding, fb->color_range);
 
 	create_bo_for_fb(&linear->fb);
 
@@ -2130,9 +2128,9 @@
 
 	igt_assert(shadow);
 
-	fb_init(shadow, fd, width, height,
-		drm_format, LOCAL_DRM_FORMAT_MOD_NONE,
-		IGT_COLOR_YCBCR_BT709, IGT_COLOR_YCBCR_LIMITED_RANGE);
+	igt_init_fb(shadow, fd, width, height,
+		    drm_format, LOCAL_DRM_FORMAT_MOD_NONE,
+		    IGT_COLOR_YCBCR_BT709, IGT_COLOR_YCBCR_LIMITED_RANGE);
 
 	shadow->strides[0] = ALIGN(width * (shadow->plane_bpp[0] / 8), 16);
 	shadow->size = ALIGN((uint64_t)shadow->strides[0] * height,
diff --git a/lib/igt_fb.h b/lib/igt_fb.h
index e19cc5d..69132b4 100644
--- a/lib/igt_fb.h
+++ b/lib/igt_fb.h
@@ -117,6 +117,10 @@
 			  unsigned *width_ret, unsigned *height_ret);
 void igt_calc_fb_size(int fd, int width, int height, uint32_t format, uint64_t modifier,
 		      uint64_t *size_ret, unsigned *stride_ret);
+void igt_init_fb(struct igt_fb *fb, int fd, int width, int height,
+		 uint32_t drm_format, uint64_t modifier,
+		 enum igt_color_encoding color_encoding,
+		 enum igt_color_range color_range);
 unsigned int
 igt_create_fb_with_bo_size(int fd, int width, int height,
 			   uint32_t format, uint64_t modifier,
diff --git a/lib/igt_infoframe.c b/lib/igt_infoframe.c
new file mode 100644
index 0000000..2e14fe7
--- /dev/null
+++ b/lib/igt_infoframe.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright © 2019 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: Simon Ser <simon.ser@intel.com>
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "igt_core.h"
+#include "igt_infoframe.h"
+
+/**
+ * SECTION:igt_infoframe
+ * @short_description: InfoFrame parsing library
+ * @title: InfoFrame
+ * @include: igt_infoframe.h
+ *
+ * This library provides helpers to parse InfoFrames as defined in CEA-861-D
+ * section 6.
+ */
+
+static const int sampling_freqs[] = {
+	-1, /* refer to stream header */
+	33000,
+	44100,
+	48000,
+	88200,
+	96000,
+	176400,
+	192000,
+};
+
+static const size_t sampling_freqs_len = sizeof(sampling_freqs) / sizeof(sampling_freqs[0]);
+
+static const int sample_sizes[] = {
+	-1, /* refer to stream header */
+	16,
+	20,
+	24,
+};
+
+static const size_t sample_sizes_len = sizeof(sample_sizes) / sizeof(sample_sizes[0]);
+
+bool infoframe_avi_parse(struct infoframe_avi *infoframe, int version,
+			 const uint8_t *buf, size_t buf_size)
+{
+	memset(infoframe, 0, sizeof(*infoframe));
+
+	switch (version) {
+	case 2:
+	case 3:
+	case 4:
+		break; /* supported */
+	default:
+		igt_debug("Unsuppported AVI InfoFrame version: %d\n", version);
+		return false;
+	}
+
+	if (buf_size < 13)
+		return false;
+
+	infoframe->rgb_ycbcr = buf[0] >> 5;
+	infoframe->scan = buf[0] & 0x3;
+
+	infoframe->colorimetry = buf[1] >> 6;
+	infoframe->picture_aspect_ratio = (buf[1] >> 4) & 0x3;
+	infoframe->active_aspect_ratio = buf[1] & 0xF;
+	infoframe->vic = buf[3];
+
+	return true;
+}
+
+bool infoframe_audio_parse(struct infoframe_audio *infoframe, int version,
+			   const uint8_t *buf, size_t buf_size)
+{
+	int channel_count;
+	size_t sampling_freq_idx, sample_size_idx;
+
+	memset(infoframe, 0, sizeof(*infoframe));
+
+	if (version != 1 || buf_size < 5)
+		return false;
+
+	infoframe->coding_type = buf[0] >> 4;
+
+	channel_count = buf[0] & 0x7;
+	if (channel_count == 0)
+		infoframe->channel_count = -1;
+	else
+		infoframe->channel_count = channel_count + 1;
+
+	sampling_freq_idx = (buf[1] >> 2) & 0x7;
+	if (sampling_freq_idx >= sampling_freqs_len)
+		return false;
+	infoframe->sampling_freq = sampling_freqs[sampling_freq_idx];
+
+	sample_size_idx = buf[1] & 0x3;
+	if (sample_size_idx >= sample_sizes_len)
+		return false;
+	infoframe->sample_size = sample_sizes[sample_size_idx];
+
+	return true;
+}
diff --git a/lib/igt_infoframe.h b/lib/igt_infoframe.h
new file mode 100644
index 0000000..eae251e
--- /dev/null
+++ b/lib/igt_infoframe.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2019 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: Simon Ser <simon.ser@intel.com>
+ */
+
+#ifndef IGT_INFOFRAME_H
+#define IGT_INFOFRAME_H
+
+#include "config.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+enum infoframe_avi_rgb_ycbcr {
+	INFOFRAME_AVI_RGB = 0,
+	INFOFRAME_AVI_YCBCR422 = 1,
+	INFOFRAME_AVI_YCBCR444 = 2,
+	INFOFRAME_AVI_YCBCR420 = 3,
+	INFOFRAME_AVI_IDO_DEFINED = 7,
+};
+
+enum infoframe_avi_scan {
+	INFOFRAME_AVI_SCAN_UNSPECIFIED = 0,
+	INFOFRAME_AVI_OVERSCAN = 1,
+	INFOFRAME_AVI_UNDERSCAN = 2,
+};
+
+enum infoframe_avi_colorimetry {
+	INFOFRAME_AVI_COLORIMETRY_UNSPECIFIED = 0,
+	INFOFRAME_AVI_SMPTE_170M = 1,
+	INFOFRAME_AVI_ITUR_BT709 = 2,
+	INFOFRAME_AVI_COLORIMETRY_EXTENDED = 3,
+};
+
+enum infoframe_avi_picture_aspect_ratio {
+	INFOFRAME_AVI_PIC_AR_UNSPECIFIED = 0,
+	INFOFRAME_AVI_PIC_AR_4_3 = 1,
+	INFOFRAME_AVI_PIC_AR_16_9 = 2,
+};
+
+enum infoframe_avi_active_aspect_ratio {
+	INFOFRAME_AVI_ACT_AR_PIC = 8, /* same as picture aspect ratio */
+	INFOFRAME_AVI_ACT_AR_4_3 = 9,
+	INFOFRAME_AVI_ACT_AR_16_9 = 10,
+	INFOFRAME_AVI_ACT_AR_14_9 = 11,
+};
+
+#define INFOFRAME_AVI_VIC_UNSPECIFIED 0
+
+struct infoframe_avi {
+	enum infoframe_avi_rgb_ycbcr rgb_ycbcr;
+	enum infoframe_avi_scan scan;
+	enum infoframe_avi_colorimetry colorimetry;
+	enum infoframe_avi_picture_aspect_ratio picture_aspect_ratio;
+	enum infoframe_avi_active_aspect_ratio active_aspect_ratio;
+	uint8_t vic; /* Video Identification Code */
+	/* TODO: remaining fields */
+};
+
+enum infoframe_audio_coding_type {
+	INFOFRAME_AUDIO_CT_UNSPECIFIED = 0, /* refer to stream header */
+	INFOFRAME_AUDIO_CT_PCM = 1, /* IEC 60958 PCM */
+	INFOFRAME_AUDIO_CT_AC3 = 2,
+	INFOFRAME_AUDIO_CT_MPEG1 = 3,
+	INFOFRAME_AUDIO_CT_MP3 = 4,
+	INFOFRAME_AUDIO_CT_MPEG2 = 5,
+	INFOFRAME_AUDIO_CT_AAC = 6,
+	INFOFRAME_AUDIO_CT_DTS = 7,
+	INFOFRAME_AUDIO_CT_ATRAC = 8,
+	INFOFRAME_AUDIO_CT_ONE_BIT = 9,
+	INFOFRAME_AUDIO_CT_DOLBY = 10, /* Dolby Digital + */
+	INFOFRAME_AUDIO_CT_DTS_HD = 11,
+	INFOFRAME_AUDIO_CT_MAT = 12,
+	INFOFRAME_AUDIO_CT_DST = 13,
+	INFOFRAME_AUDIO_CT_WMA_PRO = 14,
+};
+
+struct infoframe_audio {
+	enum infoframe_audio_coding_type coding_type;
+	int channel_count; /* -1 if unspecified */
+	int sampling_freq; /* in Hz, -1 if unspecified */
+	int sample_size; /* in bits, -1 if unspecified */
+	/* TODO: speaker allocation */
+};
+
+bool infoframe_avi_parse(struct infoframe_avi *infoframe, int version,
+			 const uint8_t *buf, size_t buf_size);
+bool infoframe_audio_parse(struct infoframe_audio *infoframe, int version,
+			   const uint8_t *buf, size_t buf_size);
+
+#endif
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 175e71c..17a7d2b 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -100,7 +100,7 @@
  *
  * Returns: a basic edid block
  */
-const unsigned char *igt_kms_get_base_edid(void)
+const struct edid *igt_kms_get_base_edid(void)
 {
 	static struct edid edid;
 	drmModeModeInfo mode = {};
@@ -119,7 +119,7 @@
 	edid_init_with_mode(&edid, &mode);
 	edid_update_checksum(&edid);
 
-	return (unsigned char *) &edid;
+	return &edid;
 }
 
 /**
@@ -136,7 +136,7 @@
  *
  * Returns: an alternate edid block
  */
-const unsigned char *igt_kms_get_alt_edid(void)
+const struct edid *igt_kms_get_alt_edid(void)
 {
 	static struct edid edid;
 	drmModeModeInfo mode = {};
@@ -155,13 +155,13 @@
 	edid_init_with_mode(&edid, &mode);
 	edid_update_checksum(&edid);
 
-	return (unsigned char *) &edid;
+	return &edid;
 }
 
-#define AUDIO_EDID_LENGTH (2 * EDID_LENGTH)
+#define AUDIO_EDID_SIZE (2 * EDID_BLOCK_SIZE)
 
-static void
-generate_audio_edid(unsigned char raw_edid[static AUDIO_EDID_LENGTH],
+static const struct edid *
+generate_audio_edid(unsigned char raw_edid[static AUDIO_EDID_SIZE],
 		    bool with_vsdb, struct cea_sad *sad,
 		    struct cea_speaker_alloc *speaker_alloc)
 {
@@ -205,14 +205,15 @@
 	edid_ext_set_cea(edid_ext, cea_data_size, 0, EDID_CEA_BASIC_AUDIO);
 
 	edid_update_checksum(edid);
-	edid_ext_update_cea_checksum(edid_ext);
+
+	return edid;
 }
 
-const unsigned char *igt_kms_get_hdmi_audio_edid(void)
+const struct edid *igt_kms_get_hdmi_audio_edid(void)
 {
 	int channels;
 	uint8_t sampling_rates, sample_sizes;
-	static unsigned char raw_edid[AUDIO_EDID_LENGTH] = {0};
+	static unsigned char raw_edid[AUDIO_EDID_SIZE] = {0};
 	struct cea_sad sad = {0};
 	struct cea_speaker_alloc speaker_alloc = {0};
 
@@ -229,16 +230,14 @@
 	/* Initialize the Speaker Allocation Data */
 	speaker_alloc.speakers = CEA_SPEAKER_FRONT_LEFT_RIGHT_CENTER;
 
-	generate_audio_edid(raw_edid, true, &sad, &speaker_alloc);
-
-	return raw_edid;
+	return generate_audio_edid(raw_edid, true, &sad, &speaker_alloc);
 }
 
-const unsigned char *igt_kms_get_dp_audio_edid(void)
+const struct edid *igt_kms_get_dp_audio_edid(void)
 {
 	int channels;
 	uint8_t sampling_rates, sample_sizes;
-	static unsigned char raw_edid[AUDIO_EDID_LENGTH] = {0};
+	static unsigned char raw_edid[AUDIO_EDID_SIZE] = {0};
 	struct cea_sad sad = {0};
 	struct cea_speaker_alloc speaker_alloc = {0};
 
@@ -255,9 +254,7 @@
 	/* Initialize the Speaker Allocation Data */
 	speaker_alloc.speakers = CEA_SPEAKER_FRONT_LEFT_RIGHT_CENTER;
 
-	generate_audio_edid(raw_edid, false, &sad, &speaker_alloc);
-
-	return raw_edid;
+	return generate_audio_edid(raw_edid, false, &sad, &speaker_alloc);
 }
 
 static const uint8_t edid_4k_svds[] = {
@@ -268,7 +265,7 @@
 	19,                  /* 720p @ 50Hz */
 };
 
-const unsigned char *igt_kms_get_4k_edid(void)
+const struct edid *igt_kms_get_4k_edid(void)
 {
 	static unsigned char raw_edid[256] = {0};
 	struct edid *edid;
@@ -316,11 +313,11 @@
 	edid_ext_set_cea(edid_ext, cea_data_size, 0, 0);
 
 	edid_update_checksum(edid);
-	edid_ext_update_cea_checksum(edid_ext);
-	return raw_edid;
+
+	return edid;
 }
 
-const unsigned char *igt_kms_get_3d_edid(void)
+const struct edid *igt_kms_get_3d_edid(void)
 {
 	static unsigned char raw_edid[256] = {0};
 	struct edid *edid;
@@ -367,8 +364,8 @@
 	edid_ext_set_cea(edid_ext, cea_data_size, 0, 0);
 
 	edid_update_checksum(edid);
-	edid_ext_update_cea_checksum(edid_ext);
-	return raw_edid;
+
+	return edid;
 }
 
 const char * const igt_plane_prop_names[IGT_NUM_PLANE_PROPS] = {
@@ -413,6 +410,8 @@
 	[IGT_CONNECTOR_BROADCAST_RGB] = "Broadcast RGB",
 	[IGT_CONNECTOR_CONTENT_PROTECTION] = "Content Protection",
 	[IGT_CONNECTOR_VRR_CAPABLE] = "vrr_capable",
+	[IGT_CONNECTOR_HDCP_CONTENT_TYPE] = "HDCP Content Type",
+	[IGT_CONNECTOR_LINK_STATUS] = "link-status",
 };
 
 /*
@@ -1084,7 +1083,7 @@
  * If @edid is NULL, the forced EDID will be removed.
  */
 void kmstest_force_edid(int drm_fd, drmModeConnector *connector,
-			const unsigned char *edid)
+			const struct edid *edid)
 {
 	char *path;
 	int debugfs_fd, ret;
@@ -1101,7 +1100,7 @@
 		ret = write(debugfs_fd, "reset", 5);
 	else
 		ret = write(debugfs_fd, edid,
-			    edid_get_size((struct edid *) edid));
+			    edid_get_size(edid));
 	close(debugfs_fd);
 
 	/* To allow callers to always use GetConnectorCurrent we need to force a
@@ -1858,6 +1857,10 @@
 	if (igt_output_has_prop(output, IGT_CONNECTOR_BROADCAST_RGB))
 		igt_output_set_prop_value(output, IGT_CONNECTOR_BROADCAST_RGB,
 					  BROADCAST_RGB_FULL);
+
+	if (igt_output_has_prop(output, IGT_CONNECTOR_CONTENT_PROTECTION))
+		igt_output_set_prop_enum(output, IGT_CONNECTOR_CONTENT_PROTECTION,
+					 "Undesired");
 }
 
 /**
@@ -1870,6 +1873,7 @@
  * For outputs:
  * - %IGT_CONNECTOR_CRTC_ID
  * - %IGT_CONNECTOR_BROADCAST_RGB (if applicable)
+ *   %IGT_CONNECTOR_CONTENT_PROTECTION (if applicable)
  * - igt_output_override_mode() to default.
  *
  * For pipes:
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index 0486737..56481fd 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -123,6 +123,8 @@
        IGT_CONNECTOR_BROADCAST_RGB,
        IGT_CONNECTOR_CONTENT_PROTECTION,
        IGT_CONNECTOR_VRR_CAPABLE,
+       IGT_CONNECTOR_HDCP_CONTENT_TYPE,
+       IGT_CONNECTOR_LINK_STATUS,
        IGT_NUM_CONNECTOR_PROPS
 };
 
@@ -191,11 +193,12 @@
 	BROADCAST_RGB_16_235
 };
 
+struct edid;
 
 bool kmstest_force_connector(int fd, drmModeConnector *connector,
 			     enum kmstest_force_connector_state state);
 void kmstest_force_edid(int drm_fd, drmModeConnector *connector,
-			const unsigned char *edid);
+			const struct edid *edid);
 
 bool kmstest_get_connector_default_mode(int drm_fd, drmModeConnector *connector,
 					drmModeModeInfo *mode);
@@ -753,16 +756,12 @@
 
 uint32_t kmstest_get_vbl_flag(uint32_t pipe_id);
 
-struct cea_sad;
-struct cea_speaker_alloc;
-
-#define EDID_LENGTH 128
-const unsigned char *igt_kms_get_base_edid(void);
-const unsigned char *igt_kms_get_alt_edid(void);
-const unsigned char *igt_kms_get_hdmi_audio_edid(void);
-const unsigned char *igt_kms_get_dp_audio_edid(void);
-const unsigned char *igt_kms_get_4k_edid(void);
-const unsigned char *igt_kms_get_3d_edid(void);
+const struct edid *igt_kms_get_base_edid(void);
+const struct edid *igt_kms_get_alt_edid(void);
+const struct edid *igt_kms_get_hdmi_audio_edid(void);
+const struct edid *igt_kms_get_dp_audio_edid(void);
+const struct edid *igt_kms_get_4k_edid(void);
+const struct edid *igt_kms_get_3d_edid(void);
 
 struct udev_monitor *igt_watch_hotplug(void);
 bool igt_hotplug_detected(struct udev_monitor *mon,
diff --git a/lib/intel_chipset.h b/lib/intel_chipset.h
index 781486d..2bd57f4 100644
--- a/lib/intel_chipset.h
+++ b/lib/intel_chipset.h
@@ -162,11 +162,12 @@
 #define IS_HASWELL(devid)	(intel_get_device_info(devid)->is_haswell)
 #define IS_BROADWELL(devid)	(intel_get_device_info(devid)->is_broadwell)
 #define IS_CHERRYVIEW(devid)	(intel_get_device_info(devid)->is_cherryview)
-#define IS_KABYLAKE(devid)	(intel_get_device_info(devid)->is_kabylake)
 #define IS_SKYLAKE(devid)	(intel_get_device_info(devid)->is_skylake)
 #define IS_BROXTON(devid)	(intel_get_device_info(devid)->is_broxton)
+#define IS_KABYLAKE(devid)	(intel_get_device_info(devid)->is_kabylake)
 #define IS_GEMINILAKE(devid)	(intel_get_device_info(devid)->is_geminilake)
 #define IS_COFFEELAKE(devid)	(intel_get_device_info(devid)->is_coffeelake)
+#define IS_COMETLAKE(devid)	(intel_get_device_info(devid)->is_cometlake)
 #define IS_CANNONLAKE(devid)	(intel_get_device_info(devid)->is_cannonlake)
 #define IS_ICELAKE(devid)	(intel_get_device_info(devid)->is_icelake)
 #define IS_TIGERLAKE(devid)	(intel_get_device_info(devid)->is_tigerlake)
diff --git a/lib/meson.build b/lib/meson.build
index 157624e..221ae28 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -61,6 +61,7 @@
 	'igt_amd.c',
 	'igt_edid.c',
 	'igt_eld.c',
+	'igt_infoframe.c',
 ]
 
 lib_deps = [
diff --git a/lib/sw_sync.c b/lib/sw_sync.c
index f208603..d671923 100644
--- a/lib/sw_sync.c
+++ b/lib/sw_sync.c
@@ -206,19 +206,12 @@
 
 int sync_fence_status(int fence)
 {
-	struct sync_fence_info fence_info;
-	struct sync_file_info file_info = {
-		.sync_fence_info = to_user_pointer(&fence_info),
-		.num_fences = 1,
-	};
+	struct sync_file_info info = { };
 
-	if (ioctl(fence, SYNC_IOC_FILE_INFO, &file_info))
+	if (ioctl(fence, SYNC_IOC_FILE_INFO, &info))
 		return -errno;
 
-	if (file_info.num_fences != 1)
-		return -EINVAL;
-
-	return fence_info.status;
+	return info.status;
 }
 
 static void modprobe(const char *driver)
diff --git a/lib/tests/igt_audio.c b/lib/tests/igt_audio.c
index a2d57fb..c072767 100644
--- a/lib/tests/igt_audio.c
+++ b/lib/tests/igt_audio.c
@@ -176,7 +176,7 @@
 
 igt_main
 {
-	struct audio_signal *signal;
+	struct audio_signal *signal = NULL;
 	int ret;
 	size_t i;
 
diff --git a/lib/tests/igt_edid.c b/lib/tests/igt_edid.c
index fc98f1b..8474d29 100644
--- a/lib/tests/igt_edid.c
+++ b/lib/tests/igt_edid.c
@@ -57,14 +57,14 @@
 	size_t i;
 	unsigned char csum = 0;
 
-	for (i = 0; i < EDID_LENGTH; i++) {
+	for (i = 0; i < EDID_BLOCK_SIZE; i++) {
 		csum += raw_edid[i];
 	}
 
 	return csum == 0;
 }
 
-typedef const unsigned char *(*get_edid_func)(void);
+typedef const struct edid *(*get_edid_func)(void);
 
 igt_simple_main
 {
@@ -80,23 +80,28 @@
 		{ "3d", igt_kms_get_3d_edid, 1 },
 		{0},
 	}, *f;
-	const unsigned char *edid;
+	const struct edid *edid;
+	const uint8_t *raw_edid, *raw_block;
 	size_t i;
 
 	for (f = funcs; f->f; f++) {
 		edid = f->f();
+		raw_edid = (uint8_t *) edid;
 
-		igt_assert_f(edid_header_is_valid(edid),
+		igt_assert_f(edid_header_is_valid(raw_edid),
 			     "invalid header on %s EDID", f->desc);
 		/* check base edid block */
-		igt_assert_f(edid_block_checksum(edid),
+		igt_assert_f(edid_block_checksum(raw_edid),
 			     "checksum failed on %s EDID", f->desc);
 		/* check extension blocks, if any */
-		igt_assert_f(edid[126] == f->exts,
+		igt_assert_f(raw_edid[126] == f->exts,
 			     "unexpected number of extensions on %s EDID",
 			     f->desc);
-		for (i = 0; i < f->exts; i++)
-			igt_assert_f(edid_block_checksum(edid + (i + 1) * EDID_LENGTH),
-				     "CEA block checksum failed on %s EDID", f->desc);
+		for (i = 0; i < f->exts; i++) {
+			raw_block = raw_edid + (i + 1) * EDID_BLOCK_SIZE;
+			igt_assert_f(edid_block_checksum(raw_block),
+				     "CEA block checksum failed on %s EDID",
+				     f->desc);
+		}
 	}
 }
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 250dbd3..c02e4d9 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -20,6 +20,7 @@
 	core_getversion \
 	core_setmaster_vs_auth \
 	debugfs_test \
+	dmabuf \
 	drm_import_export \
 	drm_mm \
 	drm_read \
@@ -63,6 +64,7 @@
 	kms_plane_lowres \
 	kms_plane_multiple \
 	kms_plane_scaling \
+	kms_prime \
 	kms_prop_blob \
 	kms_properties \
 	kms_psr \
diff --git a/tests/debugfs_test.c b/tests/debugfs_test.c
index 6a87d90..6d1757c 100644
--- a/tests/debugfs_test.c
+++ b/tests/debugfs_test.c
@@ -167,27 +167,6 @@
 	igt_subtest_group
 		kms_tests(fd, debugfs);
 
-	igt_subtest("emon_crash") {
-		int i;
-		/*
-		 * This check if we can crash the kernel with
-		 * segmentation-fault by reading
-		 * /sys/kernel/debug/dri/0/i915_emon_status too quickly
-		 */
-		for (i = 0; i < 1000; i++) {
-			char *buf = igt_sysfs_get(debugfs,
-						  "i915_emon_status");
-
-			igt_skip_on_f(!buf && !i, "i915_emon_status could not be read\n");
-
-			igt_assert(buf);
-			free(buf);
-		}
-
-		/* If we got here, we haven't crashed */
-		igt_success();
-	}
-
 	igt_fixture {
 		close(debugfs);
 		close(fd);
diff --git a/tests/dmabuf.c b/tests/dmabuf.c
new file mode 100644
index 0000000..a72cf3b
--- /dev/null
+++ b/tests/dmabuf.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright © 2019 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 "igt_kmod.h"
+
+IGT_TEST_DESCRIPTION("Kernel selftests for the dmabuf API");
+
+igt_main
+{
+	igt_kselftests("dmabuf_selftests", NULL, NULL, NULL);
+}
diff --git a/tests/i915/gem_concurrent_all.c b/tests/i915/gem_concurrent_all.c
index 3ddaab8..266995d 100644
--- a/tests/i915/gem_concurrent_all.c
+++ b/tests/i915/gem_concurrent_all.c
@@ -968,8 +968,6 @@
 		      do_copy do_copy_func,
 		      do_hang do_hang_func)
 {
-	gem_quiescent_gpu(fd);
-
 	buffers->mode->set_bo(buffers, buffers->src[0], 0xdeadbeef);
 	for (int i = 0; i < buffers->count; i++) {
 		igt_hang_t hang = do_hang_func();
@@ -985,8 +983,6 @@
 		      do_copy do_copy_func,
 		      do_hang do_hang_func)
 {
-	gem_quiescent_gpu(fd);
-
 	for (int i = 0; i < buffers->count; i++) {
 		igt_hang_t hang = do_hang_func();
 
@@ -1007,8 +1003,6 @@
 {
 	igt_hang_t hang;
 
-	gem_quiescent_gpu(fd);
-
 	for (int i = 0; i < buffers->count; i++) {
 		buffers->mode->set_bo(buffers, buffers->src[i], i);
 		buffers->mode->set_bo(buffers, buffers->dst[i], ~i);
@@ -1034,7 +1028,6 @@
 	igt_hang_t hang;
 	int i;
 
-	gem_quiescent_gpu(fd);
 	for (i = 0; i < buffers->count; i++) {
 		buffers->mode->set_bo(buffers, buffers->src[i], i);
 		buffers->mode->set_bo(buffers, buffers->dst[i], ~i);
@@ -1058,7 +1051,6 @@
 	igt_hang_t hang;
 	int i;
 
-	gem_quiescent_gpu(fd);
 	for (i = 0; i < half; i++) {
 		buffers->mode->set_bo(buffers, buffers->src[i], i);
 		buffers->mode->set_bo(buffers, buffers->dst[i], ~i);
@@ -1102,7 +1094,6 @@
 	igt_hang_t hang;
 	int i;
 
-	gem_quiescent_gpu(fd);
 	for (i = 0; i < buffers->count; i++) {
 		buffers->mode->set_bo(buffers, buffers->src[i], i);
 		buffers->mode->set_bo(buffers, buffers->dst[i], ~i);
@@ -1123,7 +1114,6 @@
 {
 	igt_hang_t hang;
 
-	gem_quiescent_gpu(fd);
 	buffers->mode->set_bo(buffers, buffers->src[0], 0);
 	buffers->mode->set_bo(buffers, buffers->dst[0], ~0);
 	do_copy_func(buffers, buffers->dst[0], buffers->src[0]);
@@ -1142,7 +1132,6 @@
 	igt_hang_t hang;
 	int i;
 
-	gem_quiescent_gpu(fd);
 	for (i = 0; i < buffers->count; i++) {
 		buffers->mode->set_bo(buffers, buffers->src[i], 0xdeadbeef^~i);
 		buffers->mode->set_bo(buffers, buffers->dst[i], i);
@@ -1196,7 +1185,6 @@
 	igt_hang_t hang;
 	int i;
 
-	gem_quiescent_gpu(fd);
 	for (i = buffers->count; i--; )
 		buffers->mode->set_bo(buffers, buffers->src[i], 0xdeadbeef);
 	for (i = 0; i < buffers->count; i++)
@@ -1214,7 +1202,6 @@
 	igt_hang_t hang;
 	int i;
 
-	gem_quiescent_gpu(fd);
 	for (i = buffers->count; i--; )
 		buffers->mode->set_bo(buffers, buffers->src[i], 0xdeadbeef ^ i);
 	for (i = 0; i < buffers->count; i++) {
@@ -1235,7 +1222,6 @@
 	igt_hang_t hang;
 	int i;
 
-	gem_quiescent_gpu(fd);
 	for (i = buffers->count; i--; )
 		buffers->mode->set_bo(buffers, buffers->src[i], 0xdeadbeef ^ i);
 	for (i = 0; i < buffers->count; i++) {
@@ -1255,7 +1241,6 @@
 	igt_hang_t hang;
 	int i;
 
-	gem_quiescent_gpu(fd);
 	for (i = buffers->count; i--; )
 		buffers->mode->set_bo(buffers, buffers->src[i], 0xdeadbeef ^ i);
 	for (i = 0; i < buffers->count; i++) {
@@ -1276,7 +1261,6 @@
 	igt_hang_t hang;
 	int i;
 
-	gem_quiescent_gpu(fd);
 	for (i = buffers->count; i--; )
 		buffers->mode->set_bo(buffers, buffers->src[i], 0xdeadbeef ^ i);
 	for (i = 0; i < buffers->count; i++) {
@@ -1296,7 +1280,6 @@
 	igt_hang_t hang;
 	int i;
 
-	gem_quiescent_gpu(fd);
 	for (i = buffers->count; i--; )
 		buffers->mode->set_bo(buffers, buffers->src[i], 0xabcdabcd);
 	for (i = 0; i < buffers->count; i++)
diff --git a/tests/i915/gem_ctx_engines.c b/tests/i915/gem_ctx_engines.c
index 8c66fb2..1e82e51 100644
--- a/tests/i915/gem_ctx_engines.c
+++ b/tests/i915/gem_ctx_engines.c
@@ -405,6 +405,14 @@
 	gem_context_destroy(i915, param.ctx_id);
 }
 
+static uint32_t read_result(int timeline, uint32_t *map, int idx)
+{
+	sw_sync_timeline_inc(timeline, 1);
+	while (!READ_ONCE(map[idx]))
+		;
+	return map[idx];
+}
+
 static void independent(int i915)
 {
 #define RCS_TIMESTAMP (0x2000 + 0x358)
@@ -423,6 +431,7 @@
 	uint32_t last, *map;
 
 	igt_require(gen >= 6); /* No per-engine TIMESTAMP on older gen */
+	igt_require(gem_scheduler_enabled(i915));
 
 	{
 		struct drm_i915_gem_execbuffer2 execbuf = {
@@ -438,6 +447,12 @@
 	memset(&engines, 0, sizeof(engines)); /* All rcs0 */
 	gem_context_set_param(i915, &param);
 
+	gem_set_caching(i915, results.handle, I915_CACHING_CACHED);
+	map = gem_mmap__cpu(i915, results.handle, 0, 4096, PROT_READ);
+	gem_set_domain(i915, results.handle,
+		       I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
+	memset(map, 0, 4096);
+
 	for (int i = 0; i < I915_EXEC_RING_MASK + 1; i++) {
 		struct drm_i915_gem_exec_object2 obj[2] = {
 			results, /* write hazard lies! */
@@ -472,21 +487,21 @@
 		gem_close(i915, obj[1].handle);
 		close(execbuf.rsvd2);
 	}
-	close(timeline);
-	gem_sync(i915, results.handle);
 
-	map = gem_mmap__cpu(i915, results.handle, 0, 4096, PROT_READ);
-	gem_set_domain(i915, results.handle, I915_GEM_DOMAIN_CPU, 0);
-	gem_close(i915, results.handle);
-
-	last = map[0];
+	last = read_result(timeline, map, 0);
 	for (int i = 1; i < I915_EXEC_RING_MASK + 1; i++) {
-		igt_assert_f((map[i] - last) > 0,
-			     "Engine instance [%d] executed too late\n", i);
-		last = map[i];
+		uint32_t t = read_result(timeline, map, i);
+		igt_assert_f(t - last > 0,
+			     "Engine instance [%d] executed too late, previous timestamp %08x, now %08x\n",
+			     i, last, t);
+		last = t;
 	}
 	munmap(map, 4096);
 
+	close(timeline);
+	gem_sync(i915, results.handle);
+	gem_close(i915, results.handle);
+
 	gem_context_destroy(i915, param.ctx_id);
 }
 
@@ -500,6 +515,8 @@
 
 		gem_require_contexts(i915);
 		igt_require(has_context_engines(i915));
+
+		igt_fork_hang_detector(i915);
 	}
 
 	igt_subtest("invalid-engines")
@@ -519,4 +536,7 @@
 
 	igt_subtest("independent")
 		independent(i915);
+
+	igt_fixture
+		igt_stop_hang_detector();
 }
diff --git a/tests/i915/gem_ctx_shared.c b/tests/i915/gem_ctx_shared.c
index 4b1020b..b073bdf 100644
--- a/tests/i915/gem_ctx_shared.c
+++ b/tests/i915/gem_ctx_shared.c
@@ -192,7 +192,8 @@
 		.flags = ring,
 	};
 	uint32_t scratch, *s;
-	uint32_t batch[16];
+	uint32_t batch, cs[16];
+	uint64_t offset;
 	int i;
 
 	gem_require_ring(i915, ring);
@@ -207,54 +208,62 @@
 	obj.flags |= EXEC_OBJECT_PINNED; /* reuse this address */
 
 	scratch = gem_create(i915, 4096);
-	s = gem_mmap__cpu(i915, scratch, 0, 4096, PROT_WRITE);
+	s = gem_mmap__wc(i915, scratch, 0, 4096, PROT_WRITE);
 
-	gem_set_domain(i915, scratch, I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
-	*s = bbe;
+	gem_set_domain(i915, scratch, I915_GEM_DOMAIN_WC, I915_GEM_DOMAIN_WC);
+	s[0] = bbe;
+	s[64] = bbe;
 
 	/* Load object into place in the GTT */
 	obj.handle = scratch;
 	gem_execbuf(i915, &execbuf);
+	offset = obj.offset;
 
 	/* Presume nothing causes an eviction in the meantime! */
 
-	obj.handle = gem_create(i915, 4096);
+	batch = gem_create(i915, 4096);
 
 	i = 0;
-	batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
+	cs[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
 	if (gen >= 8) {
-		batch[++i] = obj.offset;
-		batch[++i] = 0;
+		cs[++i] = obj.offset;
+		cs[++i] = obj.offset >> 32;
 	} else if (gen >= 4) {
-		batch[++i] = 0;
-		batch[++i] = obj.offset;
+		cs[++i] = 0;
+		cs[++i] = obj.offset;
 	} else {
-		batch[i]--;
-		batch[++i] = obj.offset;
+		cs[i]--;
+		cs[++i] = obj.offset;
 	}
-	batch[++i] = 0xc0ffee;
-	batch[++i] = bbe;
-	gem_write(i915, obj.handle, 0, batch, sizeof(batch));
+	cs[++i] = 0xc0ffee;
+	cs[++i] = bbe;
+	gem_write(i915, batch, 0, cs, sizeof(cs));
 
+	obj.handle = batch;
 	obj.offset += 8192; /* make sure we don't cause an eviction! */
 	execbuf.rsvd1 = gem_context_clone(i915, 0, I915_CONTEXT_CLONE_VM, 0);
 	if (gen > 3 && gen < 6)
 		execbuf.flags |= I915_EXEC_SECURE;
-
 	gem_execbuf(i915, &execbuf);
+
+	/* Check the scratch didn't move */
+	obj.handle = scratch;
+	obj.offset = -1;
+	obj.flags &= ~EXEC_OBJECT_PINNED;
+	execbuf.batch_start_offset = 64 * sizeof(s[0]);
+	gem_execbuf(i915, &execbuf);
+	igt_assert_eq_u64(obj.offset, offset);
 	gem_context_destroy(i915, execbuf.rsvd1);
-	gem_sync(i915, obj.handle); /* write hazard lies */
-	gem_close(i915, obj.handle);
+
+	gem_sync(i915, batch); /* write hazard lies */
+	gem_close(i915, batch);
 
 	/*
 	 * If we created the new context with the old GTT, the write
 	 * into the stale location of scratch will have landed in the right
 	 * object. Otherwise, it should read the previous value of
 	 * MI_BATCH_BUFFER_END.
-	 *
-	 * Setting .write = CPU to paper over our write hazard lies above.
 	 */
-	gem_set_domain(i915, scratch, I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
 	igt_assert_eq_u32(*s, 0xc0ffee);
 
 	munmap(s, 4096);
diff --git a/tests/i915/gem_eio.c b/tests/i915/gem_eio.c
index 4d7362d..9b086a0 100644
--- a/tests/i915/gem_eio.c
+++ b/tests/i915/gem_eio.c
@@ -559,6 +559,7 @@
 		const uint32_t bbe = MI_BATCH_BUFFER_END;
 		struct drm_i915_gem_exec_object2 obj[2];
 		struct drm_i915_gem_execbuffer2 execbuf;
+		unsigned int count;
 		igt_spin_t *hang;
 		uint32_t ctx[64];
 		int fence[64];
@@ -587,16 +588,19 @@
 		execbuf.buffer_count = 2;
 		execbuf.flags = engine | I915_EXEC_FENCE_OUT;
 
+		count = 0;
 		for (unsigned int n = 0; n < ARRAY_SIZE(fence); n++) {
 			execbuf.rsvd1 = ctx[n];
-			gem_execbuf_wr(fd, &execbuf);
+			if (__gem_execbuf_wr(fd, &execbuf))
+				break; /* small shared ring */
 			fence[n] = execbuf.rsvd2 >> 32;
 			igt_assert(fence[n] != -1);
+			count++;
 		}
 
 		check_wait(fd, obj[1].handle, wait, NULL);
 
-		for (unsigned int n = 0; n < ARRAY_SIZE(fence); n++) {
+		for (unsigned int n = 0; n < count; n++) {
 			igt_assert_eq(sync_fence_status(fence[n]), -EIO);
 			close(fence[n]);
 		}
@@ -730,6 +734,11 @@
 		.flags = engine,
 	};
 	igt_stats_t stats;
+	int max;
+
+	max = gem_measure_ring_inflight(fd, engine, 0);
+	max = max / 2 - 1; /* assume !execlists and a shared ring */
+	igt_require(max > 0);
 
 	gem_write(fd, obj.handle, 0, &bbe, sizeof(bbe));
 
@@ -751,11 +760,11 @@
 		hang = spin_sync(fd, ctx0, engine);
 
 		execbuf.rsvd1 = ctx;
-		for (i = 0; i < 10; i++)
+		for (i = 0; i < max; i++)
 			gem_execbuf(fd, &execbuf);
 
 		execbuf.rsvd1 = ctx0;
-		for (i = 0; i < 10; i++)
+		for (i = 0; i < max; i++)
 			gem_execbuf(fd, &execbuf);
 
 		/* Wedge after a small delay. */
@@ -773,11 +782,11 @@
 		 * both contexts.
 		 */
 		execbuf.rsvd1 = ctx;
-		for (i = 0; i < 5; i++)
+		for (i = 0; i < max; i++)
 			gem_execbuf(fd, &execbuf);
 
 		execbuf.rsvd1 = ctx0;
-		for (i = 0; i < 5; i++)
+		for (i = 0; i < max; i++)
 			gem_execbuf(fd, &execbuf);
 
 		gem_sync(fd, obj.handle);
diff --git a/tests/i915/gem_exec_fence.c b/tests/i915/gem_exec_fence.c
index 0befb54..2071829 100644
--- a/tests/i915/gem_exec_fence.c
+++ b/tests/i915/gem_exec_fence.c
@@ -274,7 +274,7 @@
 
 		if (all < 0) {
 			all = fence;
-			break;
+			continue;
 		}
 
 		new = sync_fence_merge(all, fence);
diff --git a/tests/i915/gem_exec_schedule.c b/tests/i915/gem_exec_schedule.c
index 7b41862..0581021 100644
--- a/tests/i915/gem_exec_schedule.c
+++ b/tests/i915/gem_exec_schedule.c
@@ -164,8 +164,13 @@
 static void unplug_show_queue(int fd, struct igt_cork *c, unsigned int engine)
 {
 	igt_spin_t *spin[MAX_ELSP_QLEN];
+	int max = MAX_ELSP_QLEN;
 
-	for (int n = 0; n < ARRAY_SIZE(spin); n++) {
+	/* If no scheduler, all batches are emitted in submission order */
+	if (!gem_scheduler_enabled(fd))
+		max = 1;
+
+	for (int n = 0; n < max; n++) {
 		const struct igt_spin_factory opts = {
 			.ctx = create_highest_priority(fd),
 			.engine = engine,
@@ -177,7 +182,7 @@
 	igt_cork_unplug(c); /* batches will now be queued on the engine */
 	igt_debugfs_dump(fd, "i915_engine_info");
 
-	for (int n = 0; n < ARRAY_SIZE(spin); n++)
+	for (int n = 0; n < max; n++)
 		igt_spin_free(fd, spin[n]);
 
 }
@@ -282,9 +287,11 @@
 	nengine = 0;
 	if (ring == ALL_ENGINES) {
 		for_each_physical_engine(fd, engine)
-			engines[nengine++] = engine;
+			if (gem_can_store_dword(fd, engine))
+				engines[nengine++] = engine;
 	} else {
-		engines[nengine++] = ring;
+		if (gem_can_store_dword(fd, ring))
+			engines[nengine++] = ring;
 	}
 	igt_require(nengine);
 
@@ -1774,9 +1781,6 @@
 		igt_fixture {
 			igt_require(gem_scheduler_enabled(fd));
 			igt_require(gem_scheduler_has_ctx_priority(fd));
-
-			/* need separate rings */
-			igt_require(gem_has_execlists(fd));
 		}
 
 		for (e = intel_execution_engines; e->name; e++) {
diff --git a/tests/i915/gem_mmap_gtt.c b/tests/i915/gem_mmap_gtt.c
index 6f3a9c3..8eff918 100644
--- a/tests/i915/gem_mmap_gtt.c
+++ b/tests/i915/gem_mmap_gtt.c
@@ -323,6 +323,44 @@
 }
 
 static void
+test_isolation(int i915)
+{
+	struct drm_i915_gem_mmap_gtt mmap_arg;
+	int A = gem_reopen_driver(i915);
+	int B = gem_reopen_driver(i915);
+	uint64_t offset_a, offset_b;
+	uint32_t a, b;
+	void *ptr;
+
+	a = gem_create(A, 4096);
+	b = gem_open(B, gem_flink(A, a));
+
+	mmap_arg.handle = a;
+	do_ioctl(A, DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg);
+	offset_a = mmap_arg.offset;
+
+	mmap_arg.handle = b;
+	do_ioctl(B, DRM_IOCTL_I915_GEM_MMAP_GTT, &mmap_arg);
+	offset_b = mmap_arg.offset;
+
+	igt_info("A: {fd:%d, handle:%d, offset:%"PRIx64"}\n",
+		 A, a, offset_a);
+	igt_info("B: {fd:%d, handle:%d, offset:%"PRIx64"}\n",
+		 B, b, offset_b);
+
+	close(B);
+
+	ptr = mmap64(0, 4096, PROT_READ, MAP_SHARED, A, offset_a);
+	igt_assert(ptr != MAP_FAILED);
+	munmap(ptr, 4096);
+
+	close(A);
+
+	ptr = mmap64(0, 4096, PROT_READ, MAP_SHARED, A, offset_a);
+	igt_assert(ptr == MAP_FAILED);
+}
+
+static void
 test_write_gtt(int fd)
 {
 	uint32_t dst;
@@ -945,6 +983,8 @@
 		test_write_cpu_read_gtt(fd);
 	igt_subtest("basic-wc")
 		test_wc(fd);
+	igt_subtest("isolation")
+		test_isolation(fd);
 	igt_subtest("pf-nonblock")
 		test_pf_nonblock(fd);
 
diff --git a/tests/i915/gem_mocs_settings.c b/tests/i915/gem_mocs_settings.c
index 1a311b8..3ad9414 100644
--- a/tests/i915/gem_mocs_settings.c
+++ b/tests/i915/gem_mocs_settings.c
@@ -63,6 +63,7 @@
 #define GEN9_MFX1_MOCS_0	(0xcA00)	/* Media 1 MOCS base register*/
 #define GEN9_VEBOX_MOCS_0	(0xcB00)	/* Video MOCS base register*/
 #define GEN9_BLT_MOCS_0		(0xcc00)	/* Blitter MOCS base register*/
+#define GEN12_GLOBAL_MOCS	(0x4000)
 #define ICELAKE_MOCS_PTE	{0x00000004, 0x0030, 0x1}
 #define MOCS_PTE		{0x00000038, 0x0030, 0x1}
 
@@ -78,6 +79,40 @@
 };
 
 /* The first entries in the MOCS tables are defined by uABI */
+
+static const struct mocs_entry tigerlake_mocs_table[GEN11_NUM_MOCS_ENTRIES] = {
+	[2]  = { 0x00000037, 0x0030, 0x1},
+	[3]  = { 0x00000005, 0x0010, 0x1},
+	[4]  = { 0x00000005, 0x0030, 0x1},
+	[5]  = { 0x00000037, 0x0010, 0x1},
+	[6]  = { 0x00000017, 0x0010, 0x1},
+	[7]  = { 0x00000017, 0x0030, 0x1},
+	[8]  = { 0x00000027, 0x0010, 0x1},
+	[9]  = { 0x00000027, 0x0030, 0x1},
+	[10] = { 0x00000077, 0x0010, 0x1},
+	[11] = { 0x00000077, 0x0030, 0x1},
+	[12] = { 0x00000057, 0x0010, 0x1},
+	[13] = { 0x00000057, 0x0030, 0x1},
+	[14] = { 0x00000067, 0x0010, 0x1},
+	[15] = { 0x00000067, 0x0030, 0x1},
+	[16] = { 0x00004005, 0x0010, 0x1},
+	[17] = { 0x00004005, 0x0030, 0x1},
+	[18] = { 0x00060037, 0x0030, 0x1},
+	[19] = { 0x00000737, 0x0030, 0x1},
+	[20] = { 0x00000337, 0x0030, 0x1},
+	[21] = { 0x00000137, 0x0030, 0x1},
+	[22] = { 0x000003b7, 0x0030, 0x1},
+	[23] = { 0x000007b7, 0x0030, 0x1},
+	[48] = { 0x00000037, 0x0030, 0x1},
+	[49] = { 0x00000005, 0x0030, 0x1},
+	[50] = { 0x00000037, 0x0010, 0x1},
+	[51] = { 0x00000005, 0x0010, 0x1},
+	[60] = { 0x00000037, 0x0010, 0x1},
+	[61] = { 0x00004005, 0x0030, 0x1},
+	[62] = { 0x00000037, 0x0010, 0x1},
+	[63] = { 0x00000037, 0x0010, 0x1},
+};
+
 static const struct mocs_entry icelake_mocs_table[GEN11_NUM_MOCS_ENTRIES] = {
 	[0]  = { 0x00000005, 0x0010, 0x1},
 	[1]  = ICELAKE_MOCS_PTE,
@@ -101,7 +136,6 @@
 	[21] = { 0x00000137, 0x0030, 0x1},
 	[22] = { 0x000003b7, 0x0030, 0x1},
 	[23] = { 0x000007b7, 0x0030, 0x1},
-	[24 ... 61] = ICELAKE_MOCS_PTE,
 	[62] = { 0x00000037, 0x0010, 0x1},
 	[63] = { 0x00000037, 0x0010, 0x1},
 };
@@ -132,12 +166,17 @@
 	[0 ... GEN9_NUM_MOCS_ENTRIES - 1] = 0xFFFFFFFF,
 };
 
+static bool has_global_mocs(int fd)
+{
+	return intel_gen(intel_get_drm_devid(fd)) >= 12;
+}
+
 static bool get_mocs_settings(int fd, struct mocs_table *table, bool dirty)
 {
 	uint32_t devid = intel_get_drm_devid(fd);
 	bool result = false;
 
-	if (IS_SKYLAKE(devid) || IS_KABYLAKE(devid)) {
+	if (IS_SKYLAKE(devid) || IS_KABYLAKE(devid) || IS_COMETLAKE(devid)) {
 		if (dirty) {
 			table->size  = ARRAY_SIZE(dirty_skylake_mocs_table);
 			table->table = dirty_skylake_mocs_table;
@@ -159,6 +198,10 @@
 		table->size  = ARRAY_SIZE(icelake_mocs_table);
 		table->table = icelake_mocs_table;
 		result = true;
+	} else if (IS_TIGERLAKE(devid)) {
+		table->size  = ARRAY_SIZE(tigerlake_mocs_table);
+		table->table = tigerlake_mocs_table;
+		result = true;
 	}
 
 	return result;
@@ -167,8 +210,11 @@
 #define LOCAL_I915_EXEC_BSD1 (I915_EXEC_BSD | (1<<13))
 #define LOCAL_I915_EXEC_BSD2 (I915_EXEC_BSD | (2<<13))
 
-static uint32_t get_engine_base(uint32_t engine)
+static uint32_t get_engine_base(int fd, uint32_t engine)
 {
+	if (has_global_mocs(fd))
+		return GEN12_GLOBAL_MOCS;
+
 	switch (engine) {
 	case LOCAL_I915_EXEC_BSD1:	return GEN9_MFX0_MOCS_0;
 	case LOCAL_I915_EXEC_BSD2:	return GEN9_MFX1_MOCS_0;
@@ -302,7 +348,7 @@
 				    uint32_t ctx_id,
 				    bool dirty)
 {
-	const uint32_t reg_base = get_engine_base(engine);
+	const uint32_t reg_base = get_engine_base(fd, engine);
 	uint32_t dst_handle = gem_create(fd, 4096);
 	uint32_t *read_regs;
 	struct mocs_table table;
@@ -320,10 +366,16 @@
 
 	gem_set_domain(fd, dst_handle, I915_GEM_DOMAIN_CPU, 0);
 	for (int index = 0; index < table.size; index++) {
+		uint32_t val, read_val;
+
 		if (!table.table[index].used)
 			continue;
-		igt_assert_eq_u32(read_regs[index],
-				  table.table[index].control_value);
+
+		read_val = read_regs[index];
+		val = table.table[index].control_value;
+		igt_assert_f(read_val == val,
+			     "engine=%u index=%u read_value=0x%08x value=0x%08x\n",
+			     engine, index, read_val, val);
 	}
 
 	munmap(read_regs, 4096);
@@ -428,7 +480,7 @@
 	else
 		num_of_mocs_entries = GEN9_NUM_MOCS_ENTRIES;
 
-	write_registers(fd, ctx_id, get_engine_base(engine),
+	write_registers(fd, ctx_id, get_engine_base(fd, engine),
 			write_values, num_of_mocs_entries,
 			engine, privileged);
 
@@ -454,7 +506,7 @@
 	gem_require_ring(fd, engine);
 
 	/* Skip if we don't know where the registers are for this engine */
-	igt_require(get_engine_base(engine));
+	igt_require(get_engine_base(fd, engine));
 
 	if (flags & MOCS_NON_DEFAULT_CTX)
 		ctx_id = gem_context_create(fd);
diff --git a/tests/i915/gem_persistent_relocs.c b/tests/i915/gem_persistent_relocs.c
index 452fe68..dff4e9a 100644
--- a/tests/i915/gem_persistent_relocs.c
+++ b/tests/i915/gem_persistent_relocs.c
@@ -281,10 +281,13 @@
 	struct igt_helper_process thrasher = {};
 
 	if (flags & (THRASH | THRASH_INACTIVE)) {
-		uint64_t val = (flags & THRASH_INACTIVE) ?
-				(DROP_RETIRE | DROP_BOUND | DROP_UNBOUND) : DROP_ALL;
-
 		igt_fork_helper(&thrasher) {
+			uint64_t val;
+
+			val = DROP_RETIRE | DROP_BOUND | DROP_UNBOUND;
+			if (!(flags & THRASH_INACTIVE))
+				val |= DROP_ACTIVE | DROP_SHRINK_ALL;
+
 			while (1) {
 				usleep(1000);
 				igt_drop_caches_set(fd, val);
diff --git a/tests/i915/gem_reloc_vs_gpu.c b/tests/i915/gem_reloc_vs_gpu.c
index d421e43..328730a 100644
--- a/tests/i915/gem_reloc_vs_gpu.c
+++ b/tests/i915/gem_reloc_vs_gpu.c
@@ -258,10 +258,13 @@
 		igt_require_hang_ring(fd, I915_EXEC_BLT);
 
 	if (flags & (THRASH | THRASH_INACTIVE)) {
-		uint64_t val = (flags & THRASH_INACTIVE) ?
-				(DROP_RETIRE | DROP_BOUND | DROP_UNBOUND) : DROP_ALL;
-
 		igt_fork_helper(&thrasher) {
+			uint64_t val;
+
+			val = DROP_RETIRE | DROP_BOUND | DROP_UNBOUND;
+			if (!(flags & THRASH_INACTIVE))
+				val |= DROP_ACTIVE | DROP_SHRINK_ALL;
+
 			while (1) {
 				usleep(1000);
 				igt_drop_caches_set(fd, val);
diff --git a/tests/i915/gem_shrink.c b/tests/i915/gem_shrink.c
index 037ff00..3db754f 100644
--- a/tests/i915/gem_shrink.c
+++ b/tests/i915/gem_shrink.c
@@ -45,6 +45,13 @@
 	gem_madvise(fd, handle, I915_MADV_DONTNEED);
 }
 
+static void get_pages_dirty(int fd, uint64_t alloc)
+{
+	uint32_t handle = gem_create(fd, alloc);
+	gem_set_domain(fd, handle, I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
+	gem_madvise(fd, handle, I915_MADV_DONTNEED);
+}
+
 static void pwrite_(int fd, uint64_t alloc)
 {
 	uint32_t tmp;
@@ -214,7 +221,8 @@
 	munmap(obj, obj_size);
 }
 
-static void userptr(int fd, uint64_t alloc)
+static void userptr(int fd, uint64_t alloc, unsigned int flags)
+#define UDIRTY (1 << 0)
 {
 	struct local_i915_gem_userptr userptr;
 	void *ptr;
@@ -231,7 +239,11 @@
 	userptr.user_ptr = to_user_pointer(ptr);
 	do_ioctl(fd, LOCAL_IOCTL_I915_GEM_USERPTR, &userptr);
 
-	gem_set_domain(fd, userptr.handle, I915_GEM_DOMAIN_GTT, 0);
+	if (flags & UDIRTY)
+		gem_set_domain(fd, userptr.handle,
+			       I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
+	else
+		gem_set_domain(fd, userptr.handle, I915_GEM_DOMAIN_GTT, 0);
 
 	madvise(ptr, alloc, MADV_FREE);
 }
@@ -273,7 +285,8 @@
 
 #define SOLO 1
 #define USERPTR 2
-#define OOM 4
+#define USERPTR_DIRTY 4
+#define OOM 8
 
 static void run_test(int nchildren, uint64_t alloc,
 		     void (*func)(int, uint64_t), unsigned flags)
@@ -309,7 +322,20 @@
 			igt_until_timeout(timeout) {
 				int fd = drm_open_driver(DRIVER_INTEL);
 				for (int pass = 0; pass < nchildren; pass++)
-					userptr(fd, alloc);
+					userptr(fd, alloc, 0);
+				close(fd);
+			}
+		}
+		nchildren = (nchildren + 1)/2;
+	}
+
+	if (flags & USERPTR_DIRTY) {
+		igt_require(has_userptr());
+		igt_fork(child, (nchildren + 1)/2) {
+			igt_until_timeout(timeout) {
+				int fd = drm_open_driver(DRIVER_INTEL);
+				for (int pass = 0; pass < nchildren; pass++)
+					userptr(fd, alloc, UDIRTY);
 				close(fd);
 			}
 		}
@@ -373,6 +399,7 @@
 		void (*func)(int, uint64_t);
 	} tests[] = {
 		{ "get-pages", get_pages },
+		{ "get-pages-dirty", get_pages_dirty },
 		{ "pwrite", pwrite_ },
 		{ "pread", pread_ },
 		{ "mmap-gtt", mmap_gtt },
@@ -390,6 +417,7 @@
 		{ "-sanitycheck", SOLO },
 		{ "", 0 },
 		{ "-userptr", USERPTR },
+		{ "-userptr-dirty", USERPTR | USERPTR_DIRTY },
 		{ "-oom", USERPTR | OOM },
 		{ NULL },
 	};
diff --git a/tests/i915/gem_userptr_blits.c b/tests/i915/gem_userptr_blits.c
index 1373f16..5f7770c 100644
--- a/tests/i915/gem_userptr_blits.c
+++ b/tests/i915/gem_userptr_blits.c
@@ -1662,20 +1662,24 @@
 static void *mm_stress_thread(void *data)
 {
 	struct stress_thread_data *stdata = (struct stress_thread_data *)data;
+	const size_t sz = 2 << 20;
 	void *ptr;
-	int ret;
 
 	while (!stdata->stop) {
-		ptr = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
-				MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+		ptr = mmap(NULL, sz, PROT_READ | PROT_WRITE,
+			   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
 		if (ptr == MAP_FAILED) {
 			stdata->exit_code = -EFAULT;
 			break;
 		}
-		ret = munmap(ptr, PAGE_SIZE);
-		if (ret) {
-		        stdata->exit_code = errno;
-		        break;
+
+		madvise(ptr, sz, MADV_HUGEPAGE);
+		for (size_t page = 0; page < sz; page += PAGE_SIZE)
+			*(volatile uint32_t *)((unsigned char *)ptr + page) = 0;
+
+		if (munmap(ptr, sz)) {
+			stdata->exit_code = errno;
+			break;
 		}
 	}
 
@@ -1713,6 +1717,35 @@
 	igt_assert_eq(stdata.exit_code, 0);
 }
 
+static void test_stress_purge(int fd)
+{
+	struct stress_thread_data stdata;
+	uint32_t handle;
+	pthread_t t;
+	void *ptr;
+
+	memset(&stdata, 0, sizeof(stdata));
+
+	igt_assert(posix_memalign(&ptr, PAGE_SIZE, PAGE_SIZE) == 0);
+	igt_assert(!pthread_create(&t, NULL, mm_stress_thread, &stdata));
+
+	igt_until_timeout(150) {
+		gem_userptr(fd, ptr, PAGE_SIZE, 0, userptr_flags, &handle);
+
+		gem_set_domain(fd, handle,
+			       I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
+		intel_purge_vm_caches(fd);
+
+		gem_close(fd, handle);
+	}
+
+	free(ptr);
+
+	stdata.stop = 1;
+	igt_assert(!pthread_join(t, NULL));
+	igt_assert_eq(stdata.exit_code, 0);
+}
+
 struct userptr_close_thread_data {
 	int fd;
 	void *ptr;
@@ -1975,6 +2008,8 @@
 
 		igt_subtest("stress-mm")
 			test_stress_mm(fd);
+		igt_subtest("stress-purge")
+			test_stress_purge(fd);
 
 		igt_subtest("stress-mm-invalidate-close")
 			test_invalidate_close_race(fd, false);
diff --git a/tests/i915/i915_pm_rpm.c b/tests/i915/i915_pm_rpm.c
index e2c7ba2..2168ff7 100644
--- a/tests/i915/i915_pm_rpm.c
+++ b/tests/i915/i915_pm_rpm.c
@@ -50,6 +50,7 @@
 #include "igt_sysfs.h"
 #include "igt_debugfs.h"
 #include "igt_device.h"
+#include "igt_edid.h"
 
 #define MSR_PKG_CST_CONFIG_CONTROL	0xE2
 /* HSW/BDW: */
@@ -655,10 +656,10 @@
 	return rc >= 0;
 }
 
-static void format_hex_string(const unsigned char edid[static EDID_LENGTH],
-			      char buf[static EDID_LENGTH * 5 + 1])
+static void format_hex_string(const unsigned char edid[static EDID_BLOCK_SIZE],
+			      char buf[static EDID_BLOCK_SIZE * 5 + 1])
 {
-	for (int i = 0; i < EDID_LENGTH; ++i)
+	for (int i = 0; i < EDID_BLOCK_SIZE; ++i)
 		sprintf(buf+i*5, "0x%02x ", edid[i]);
 }
 
@@ -670,7 +671,7 @@
 
 	for (int i = 0; i < data->res->count_connectors; i++) {
 		unsigned char *drm_edid = data->edids[i] ? data->edids[i]->data : NULL;
-		unsigned char i2c_edid[EDID_LENGTH] = {};
+		unsigned char i2c_edid[EDID_BLOCK_SIZE] = {};
 
 		igt_output_t *output = igt_output_from_connector(&display,
 								 data->connectors[i]);
@@ -694,13 +695,13 @@
 			continue;
 
 		if (got_i2c_edid && got_drm_edid)
-			edids_equal = (0 == memcmp(drm_edid, i2c_edid, EDID_LENGTH));
+			edids_equal = (0 == memcmp(drm_edid, i2c_edid, EDID_BLOCK_SIZE));
 		else
 			edids_equal = false;
 
 
 		if (!edids_equal) {
-			char buf[5 * EDID_LENGTH + 1];
+			char buf[5 * EDID_BLOCK_SIZE + 1];
 			igt_critical("Detected EDID mismatch on connector %s\n",
 				     connector_name);
 
diff --git a/tests/i915/i915_pm_sseu.c b/tests/i915/i915_pm_sseu.c
index 252df7d..0b93698 100644
--- a/tests/i915/i915_pm_sseu.c
+++ b/tests/i915/i915_pm_sseu.c
@@ -199,7 +199,7 @@
 	if (dbg_has_line(first, last, "Enabled Subslice Per Slice:")) {
 		stat->hw.subslice_per =
 			dbg_get_int(first, last, "Enabled Subslice Per Slice:");
-	} else {
+	} else if (dbg_has_line(first, last, "Enabled Slice0 subslices:")) {
 		stat->hw.subslice_per =
 			dbg_get_int(first, last, "Enabled Slice0 subslices:");
 	}
diff --git a/tests/igt_command_line.sh b/tests/igt_command_line.sh
index 92643c4..a019e3a 100755
--- a/tests/igt_command_line.sh
+++ b/tests/igt_command_line.sh
@@ -90,7 +90,7 @@
 		# Subtest enumeration of kernel selftest launchers depends
 		# on the running kernel. If selftests are not enabled,
 		# they will output nothing and exit with 0.
-		if [ "$testname" != "i915_selftest" -a "$testname" != "drm_mm" -a "$testname" != "kms_selftest" ]; then
+		if [ "$testname" != "i915_selftest" -a "$testname" != "drm_mm" -a "$testname" != "kms_selftest" -a "$testname" != "dmabuf" ]; then
 			fail $test
 		fi
 	fi
diff --git a/tests/intel-ci/fast-feedback.testlist b/tests/intel-ci/fast-feedback.testlist
index 29d09a6..e78e7fd 100644
--- a/tests/intel-ci/fast-feedback.testlist
+++ b/tests/intel-ci/fast-feedback.testlist
@@ -78,7 +78,6 @@
 igt@gem_mmap_gtt@basic-small-bo-tiledy
 igt@gem_mmap_gtt@basic-small-copy
 igt@gem_mmap_gtt@basic-small-copy-xy
-igt@gem_mmap_gtt@basic-wc
 igt@gem_mmap_gtt@basic-write
 igt@gem_mmap_gtt@basic-write-cpu-read-gtt
 igt@gem_mmap_gtt@basic-write-gtt
diff --git a/tests/kms_3d.c b/tests/kms_3d.c
index 8ade6d3..b970b3b 100644
--- a/tests/kms_3d.c
+++ b/tests/kms_3d.c
@@ -31,7 +31,7 @@
 	int drm_fd;
 	drmModeRes *res;
 	drmModeConnector *connector;
-	const unsigned char *edid;
+	const struct edid *edid;
 	int mode_count, connector_id;
 
 	drm_fd = drm_open_driver_master(DRIVER_INTEL);
@@ -46,8 +46,7 @@
 
 		connector = drmModeGetConnectorCurrent(drm_fd, res->connectors[i]);
 
-		if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA &&
-		    connector->connection == DRM_MODE_DISCONNECTED)
+		if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA)
 			break;
 
 		drmModeFreeConnector(connector);
@@ -56,6 +55,8 @@
 	}
 	igt_require(connector);
 
+	kmstest_unset_all_crtcs(drm_fd, res);
+
 	edid = igt_kms_get_3d_edid();
 
 	kmstest_force_edid(drm_fd, connector, edid);
diff --git a/tests/kms_addfb_basic.c b/tests/kms_addfb_basic.c
index 20dfd4f..666e716 100644
--- a/tests/kms_addfb_basic.c
+++ b/tests/kms_addfb_basic.c
@@ -131,6 +131,7 @@
 	}
 
 	igt_subtest("clobberred-modifier") {
+		igt_require_intel(fd);
 		f.flags = 0;
 		f.modifier[0] = 0;
 		gem_set_tiling(fd, gem_bo, I915_TILING_X, 512*4);
@@ -302,6 +303,7 @@
 
 	igt_subtest_group {
 		igt_fixture {
+			igt_require_intel(fd);
 			tiled_x_bo = igt_create_bo_with_dimensions(fd, 1024, 1024,
 				DRM_FORMAT_XRGB8888, LOCAL_I915_FORMAT_MOD_X_TILED,
 				1024*4, NULL, NULL, NULL);
@@ -447,6 +449,7 @@
 	}
 
 	igt_subtest("bo-too-small-due-to-tiling") {
+		igt_require_intel(fd);
 		gem_set_tiling(fd, gem_bo_small, I915_TILING_X, 1024*4);
 		igt_assert(drmIoctl(fd, DRM_IOCTL_MODE_ADDFB2, &f) == -1 &&
 			   errno == EINVAL);
@@ -498,6 +501,7 @@
 
 	igt_subtest_group {
 		igt_fixture {
+			igt_require_intel(fd);
 			gem_set_tiling(fd, gem_bo, I915_TILING_X, 1024*4);
 			igt_require_fb_modifiers(fd);
 		}
diff --git a/tests/kms_available_modes_crc.c b/tests/kms_available_modes_crc.c
index 0777276..786d50e 100644
--- a/tests/kms_available_modes_crc.c
+++ b/tests/kms_available_modes_crc.c
@@ -102,7 +102,6 @@
 	igt_plane_set_fb(primary, &data->primary_fb);
 	igt_display_commit2(&data->display, data->commit);
 
-	igt_pipe_crc_drain(data->pipe_crc);
 	igt_pipe_crc_get_current(data->gfx_fd, data->pipe_crc, &data->cursor_crc);
 	igt_plane_set_fb(primary, NULL);
 	igt_display_commit2(&data->display, data->commit);
@@ -114,7 +113,6 @@
 	igt_plane_set_fb(primary, &data->primary_fb);
 	igt_display_commit2(&data->display, data->commit);
 
-	igt_pipe_crc_drain(data->pipe_crc);
 	igt_pipe_crc_get_current(data->gfx_fd, data->pipe_crc, &data->fullscreen_crc);
 
 	igt_remove_fb(data->gfx_fd, &data->primary_fb);
@@ -313,7 +311,6 @@
 		igt_display_commit2(&data->display, data->commit);
 
 		igt_wait_for_vblank(data->gfx_fd, pipe);
-		igt_pipe_crc_drain(data->pipe_crc);
 		igt_pipe_crc_get_current(data->gfx_fd, data->pipe_crc, &current_crc);
 
 		if (plane->type != DRM_PLANE_TYPE_CURSOR) {
diff --git a/tests/kms_ccs.c b/tests/kms_ccs.c
index 2f08a83..1ce66cd 100644
--- a/tests/kms_ccs.c
+++ b/tests/kms_ccs.c
@@ -297,17 +297,13 @@
 	return valid_tests;
 }
 
-static int test_output(data_t *data)
+static int __test_output(data_t *data)
 {
 	igt_display_t *display = &data->display;
 	int i, valid_tests = 0;
 
-	igt_display_require_output_on_pipe(display, data->pipe);
-
-	/* Sets data->output with a valid output. */
-	for_each_valid_output_on_pipe(display, data->pipe, data->output) {
-		break;
-	}
+	data->output = igt_get_single_output_for_pipe(display, data->pipe);
+	igt_require(data->output);
 
 	igt_output_set_pipe(data->output, data->pipe);
 
@@ -322,6 +318,12 @@
 	return valid_tests;
 }
 
+static void test_output(data_t *data)
+{
+	int valid_tests = __test_output(data);
+	igt_require_f(valid_tests > 0, "CCS not supported, skipping\n");
+}
+
 static data_t data;
 
 igt_main
@@ -345,19 +347,19 @@
 
 		data.flags = TEST_BAD_PIXEL_FORMAT;
 		igt_subtest_f("pipe-%s-bad-pixel-format", pipe_name)
-			igt_require(test_output(&data));
+			test_output(&data);
 
 		data.flags = TEST_BAD_ROTATION_90;
 		igt_subtest_f("pipe-%s-bad-rotation-90", pipe_name)
-			igt_require(test_output(&data));
+			test_output(&data);
 
 		data.flags = TEST_CRC;
 		igt_subtest_f("pipe-%s-crc-primary-basic", pipe_name)
-			igt_require(test_output(&data));
+			test_output(&data);
 
 		data.flags = TEST_CRC | TEST_ROTATE_180;
 		igt_subtest_f("pipe-%s-crc-primary-rotation-180", pipe_name)
-			igt_require(test_output(&data));
+			test_output(&data);
 
 		data.flags = TEST_CRC;
 		igt_subtest_f("pipe-%s-crc-sprite-planes-basic", pipe_name) {
@@ -368,25 +370,26 @@
 			for_each_plane_on_pipe(&data.display, data.pipe, data.plane) {
 				if (data.plane->type == DRM_PLANE_TYPE_PRIMARY)
 					continue;
-				valid_tests += test_output(&data);
+				valid_tests += __test_output(&data);
 			}
 
-			igt_require(valid_tests);
+			igt_require_f(valid_tests > 0,
+				      "CCS not supported, skipping\n");
 		}
 
 		data.plane = NULL;
 
 		data.flags = TEST_NO_AUX_BUFFER;
 		igt_subtest_f("pipe-%s-missing-ccs-buffer", pipe_name)
-			igt_require(test_output(&data));
+			test_output(&data);
 
 		data.flags = TEST_BAD_CCS_HANDLE;
 		igt_subtest_f("pipe-%s-ccs-on-another-bo", pipe_name)
-			igt_require(test_output(&data));
+			test_output(&data);
 
 		data.flags = TEST_BAD_AUX_STRIDE;
 		igt_subtest_f("pipe-%s-bad-aux-stride", pipe_name)
-			igt_require(test_output(&data));
+			test_output(&data);
 	}
 
 	igt_fixture
diff --git a/tests/kms_chamelium.c b/tests/kms_chamelium.c
index b7d30a2..d6aec8b 100644
--- a/tests/kms_chamelium.c
+++ b/tests/kms_chamelium.c
@@ -29,6 +29,7 @@
 #include "igt_vc4.h"
 #include "igt_edid.h"
 #include "igt_eld.h"
+#include "igt_infoframe.h"
 
 #include <fcntl.h>
 #include <pthread.h>
@@ -40,8 +41,9 @@
 	TEST_EDID_ALT,
 	TEST_EDID_HDMI_AUDIO,
 	TEST_EDID_DP_AUDIO,
+	TEST_EDID_ASPECT_RATIO,
 };
-#define TEST_EDID_COUNT 4
+#define TEST_EDID_COUNT 5
 
 typedef struct {
 	struct chamelium *chamelium;
@@ -55,6 +57,7 @@
 } data_t;
 
 #define HOTPLUG_TIMEOUT 20 /* seconds */
+#define ONLINE_TIMEOUT 20 /* seconds */
 
 #define HPD_STORM_PULSE_INTERVAL_DP 100 /* ms */
 #define HPD_STORM_PULSE_INTERVAL_HDMI 200 /* ms */
@@ -118,15 +121,25 @@
 	return status;
 }
 
+static const char *connection_str(drmModeConnection c)
+{
+	switch (c) {
+	case DRM_MODE_CONNECTED:
+		return "connected";
+	case DRM_MODE_DISCONNECTED:
+		return "disconnected";
+	case DRM_MODE_UNKNOWNCONNECTION:
+		return "unknown";
+	}
+	assert(0); /* unreachable */
+}
+
 static void
 wait_for_connector(data_t *data, struct chamelium_port *port,
 		   drmModeConnection status)
 {
-	bool finished = false;
-
-	igt_debug("Waiting for %s to %sconnect...\n",
-		  chamelium_port_get_name(port),
-		  status == DRM_MODE_DISCONNECTED ? "dis" : "");
+	igt_debug("Waiting for %s to get %s...\n",
+		  chamelium_port_get_name(port), connection_str(status));
 
 	/*
 	 * Rely on simple reprobing so we don't fail tests that don't require
@@ -134,14 +147,14 @@
 	 */
 	igt_until_timeout(HOTPLUG_TIMEOUT) {
 		if (reprobe_connector(data, port) == status) {
-			finished = true;
 			return;
 		}
 
 		usleep(50000);
 	}
 
-	igt_assert(finished);
+	igt_assert_f(false, "Timed out waiting for %s to get %s\n",
+		  chamelium_port_get_name(port), connection_str(status));
 }
 
 static int chamelium_vga_modes[][2] = {
@@ -234,6 +247,7 @@
 {
 	struct udev_monitor *mon = igt_watch_hotplug();
 	int i;
+	drmModeConnection status;
 
 	reset_state(data, NULL);
 	igt_hpd_storm_set_threshold(data->drm_fd, 0);
@@ -243,24 +257,31 @@
 
 		/* Check if we get a sysfs hotplug event */
 		chamelium_plug(data->chamelium, port);
-		igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT));
-		igt_assert_eq(reprobe_connector(data, port),
-			      DRM_MODE_CONNECTED);
+		igt_assert_f(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT),
+			     "Timed out waiting for hotplug uevent\n");
+		status = reprobe_connector(data, port);
+		igt_assert_f(status == DRM_MODE_CONNECTED,
+			     "Invalid connector status after hotplug: "
+			     "got %s, expected connected\n",
+			     connection_str(status));
 
 		igt_flush_hotplugs(mon);
 
 		/* Now check if we get a hotplug from disconnection */
 		chamelium_unplug(data->chamelium, port);
-		igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT));
-		igt_assert_eq(reprobe_connector(data, port),
-			      DRM_MODE_DISCONNECTED);
+		igt_assert_f(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT),
+			     "Timed out waiting for unplug uevent\n");
+		igt_assert_f(status == DRM_MODE_DISCONNECTED,
+			     "Invalid connector status after hotplug: "
+			     "got %s, expected disconnected\n",
+			     connection_str(status));
 	}
 
 	igt_cleanup_hotplug(mon);
 	igt_hpd_storm_reset(data->drm_fd);
 }
 
-static const unsigned char *get_edid(enum test_edid edid);
+static const struct edid *get_edid(enum test_edid edid);
 
 static void set_edid(data_t *data, struct chamelium_port *port,
 		     enum test_edid edid)
@@ -289,6 +310,7 @@
 	igt_assert(kmstest_get_property(data->drm_fd, connector->connector_id,
 					DRM_MODE_OBJECT_CONNECTOR, "EDID", NULL,
 					&edid_blob_id, NULL));
+	igt_assert(edid_blob_id != 0);
 	igt_assert(edid_blob = drmModeGetPropertyBlob(data->drm_fd,
 						      edid_blob_id));
 
@@ -348,6 +370,7 @@
 
 	igt_system_suspend_autoresume(state, test);
 	igt_assert(wait_for_hotplug(mon, &timeout));
+	chamelium_wait_reachable(data->chamelium, ONLINE_TIMEOUT);
 
 	if (port) {
 		igt_assert_eq(reprobe_connector(data, port), target_state);
@@ -453,8 +476,8 @@
 	igt_flush_hotplugs(mon);
 
 	igt_system_suspend_autoresume(state, test);
-
 	igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT));
+	chamelium_wait_reachable(data->chamelium, ONLINE_TIMEOUT);
 
 	get_connectors_link_status_failed(data, link_status_failed[1]);
 
@@ -539,6 +562,145 @@
 	drmModeFreeConnector(connector);
 }
 
+static bool find_mode(const drmModeModeInfo *list, size_t list_len,
+		      const drmModeModeInfo *mode)
+{
+	size_t i;
+
+	for (i = 0; i < list_len; i++) {
+		if (memcmp(&list[i], mode, sizeof(*mode)) == 0) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static void check_modes_subset(const drmModeModeInfo *prev, size_t prev_len,
+			       const drmModeModeInfo *cur, size_t cur_len)
+{
+	size_t i;
+
+	for (i = 0; i < cur_len; i++) {
+		igt_assert_f(find_mode(prev, prev_len, &cur[i]),
+			     "Got new mode %s after link status failure\n",
+			     cur[i].name);
+	}
+
+	igt_assert(cur_len <= prev_len); /* safety net */
+	igt_debug("New mode list contains %zu less modes\n",
+		  prev_len - cur_len);
+}
+
+static bool are_fallback_modes(const drmModeModeInfo *modes, size_t modes_len)
+{
+	igt_assert(modes_len > 0);
+
+	return modes[0].hdisplay <= 1024 && modes[0].vdisplay <= 768;
+}
+
+static void
+test_link_status(data_t *data, struct chamelium_port *port)
+{
+	drmModeConnector *connector;
+	igt_output_t *output;
+	igt_plane_t *primary;
+	drmModeModeInfo *prev_modes;
+	size_t prev_modes_len;
+	drmModeModeInfo mode = {0};
+	uint32_t link_status_id;
+	uint64_t link_status;
+	bool has_prop;
+	unsigned int fb_id = 0;
+	struct igt_fb fb;
+	struct udev_monitor *mon;
+
+	igt_require(chamelium_supports_trigger_link_failure(data->chamelium));
+
+	reset_state(data, port);
+
+	output = prepare_output(data, port, TEST_EDID_BASE);
+	connector = chamelium_port_get_connector(data->chamelium, port, false);
+	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
+	igt_assert(primary);
+
+	has_prop = kmstest_get_property(data->drm_fd, connector->connector_id,
+					DRM_MODE_OBJECT_CONNECTOR,
+					"link-status", &link_status_id,
+					&link_status, NULL);
+	igt_require(has_prop);
+	igt_assert_f(link_status == DRM_MODE_LINK_STATUS_GOOD,
+		     "Expected link status to be %d initially, got %"PRIu64"\n",
+		     DRM_MODE_LINK_STATUS_GOOD, link_status);
+
+	igt_debug("Connector has %d modes\n", connector->count_modes);
+	prev_modes_len = connector->count_modes;
+	prev_modes = malloc(prev_modes_len * sizeof(drmModeModeInfo));
+	memcpy(prev_modes, connector->modes,
+	       prev_modes_len * sizeof(drmModeModeInfo));
+
+	mon = igt_watch_hotplug();
+
+	while (1) {
+		if (link_status == DRM_MODE_LINK_STATUS_BAD) {
+			igt_output_set_prop_value(output,
+						  IGT_CONNECTOR_LINK_STATUS,
+						  DRM_MODE_LINK_STATUS_GOOD);
+		}
+
+		if (memcmp(&connector->modes[0], &mode, sizeof(mode)) != 0) {
+			igt_assert(connector->count_modes > 0);
+			mode = connector->modes[0];
+			igt_debug("Modesetting with %s\n", mode.name);
+			if (fb_id > 0)
+				igt_remove_fb(data->drm_fd, &fb);
+			fb_id = igt_create_color_pattern_fb(data->drm_fd,
+							    mode.hdisplay,
+							    mode.vdisplay,
+							    DRM_FORMAT_XRGB8888,
+							    LOCAL_DRM_FORMAT_MOD_NONE,
+							    0, 0, 0, &fb);
+			igt_assert(fb_id > 0);
+			enable_output(data, port, output, &mode, &fb);
+		} else {
+			igt_display_commit2(&data->display, COMMIT_ATOMIC);
+		}
+
+		igt_debug("Triggering link failure\n");
+		chamelium_trigger_link_failure(data->chamelium, port);
+
+		igt_assert(igt_hotplug_detected(mon, HOTPLUG_TIMEOUT));
+		igt_assert_eq(reprobe_connector(data, port),
+			      DRM_MODE_CONNECTED);
+
+		igt_flush_hotplugs(mon);
+
+		drmModeFreeConnector(connector);
+		connector = chamelium_port_get_connector(data->chamelium, port,
+							 false);
+		link_status = igt_output_get_prop(output, IGT_CONNECTOR_LINK_STATUS);
+		igt_assert_f(link_status == DRM_MODE_LINK_STATUS_BAD,
+			     "Expected link status to be %d after link failure, "
+			     "got %"PRIu64"\n",
+			     DRM_MODE_LINK_STATUS_BAD, link_status);
+		check_modes_subset(prev_modes, prev_modes_len,
+				   connector->modes, connector->count_modes);
+		prev_modes_len = connector->count_modes;
+		memcpy(prev_modes, connector->modes,
+		       connector->count_modes * sizeof(drmModeModeInfo));
+
+		if (are_fallback_modes(connector->modes, connector->count_modes)) {
+			igt_debug("Reached fallback modes\n");
+			break;
+		}
+	}
+
+	igt_cleanup_hotplug(mon);
+	igt_remove_fb(data->drm_fd, &fb);
+	free(prev_modes);
+	drmModeFreeConnector(connector);
+}
+
 static void chamelium_paint_xr24_pattern(uint32_t *data,
 					 size_t width, size_t height,
 					 size_t stride, size_t block_size)
@@ -861,6 +1023,169 @@
 	drmModeFreeConnector(connector);
 }
 
+/* Set of Video Identification Codes advertised in the EDID */
+static const uint8_t edid_ar_svds[] = {
+	16, /* 1080p @ 60Hz, 16:9 */
+};
+
+struct vic_mode {
+	int hactive, vactive;
+	int vrefresh; /* Hz */
+	uint32_t picture_ar;
+};
+
+/* Maps Video Identification Codes to a mode */
+static const struct vic_mode vic_modes[] = {
+	[16] = {
+		.hactive = 1920,
+		.vactive = 1080,
+		.vrefresh = 60,
+		.picture_ar = DRM_MODE_PICTURE_ASPECT_16_9,
+	},
+};
+
+/* Maps aspect ratios to their mode flag */
+static const uint32_t mode_ar_flags[] = {
+	[DRM_MODE_PICTURE_ASPECT_16_9] = DRM_MODE_FLAG_PIC_AR_16_9,
+};
+
+static enum infoframe_avi_picture_aspect_ratio
+get_infoframe_avi_picture_ar(uint32_t aspect_ratio)
+{
+	/* The AVI picture aspect ratio field only supports 4:3 and 16:9 */
+	switch (aspect_ratio) {
+	case DRM_MODE_PICTURE_ASPECT_4_3:
+		return INFOFRAME_AVI_PIC_AR_4_3;
+	case DRM_MODE_PICTURE_ASPECT_16_9:
+		return INFOFRAME_AVI_PIC_AR_16_9;
+	default:
+		return INFOFRAME_AVI_PIC_AR_UNSPECIFIED;
+	}
+}
+
+static bool vic_mode_matches_drm(const struct vic_mode *vic_mode,
+				 drmModeModeInfo *drm_mode)
+{
+	uint32_t ar_flag = mode_ar_flags[vic_mode->picture_ar];
+
+	return vic_mode->hactive == drm_mode->hdisplay &&
+	       vic_mode->vactive == drm_mode->vdisplay &&
+	       vic_mode->vrefresh == drm_mode->vrefresh &&
+	       ar_flag == (drm_mode->flags & DRM_MODE_FLAG_PIC_AR_MASK);
+}
+
+static const struct edid *get_aspect_ratio_edid(void)
+{
+	static unsigned char raw_edid[2 * EDID_BLOCK_SIZE] = {0};
+	struct edid *edid;
+	struct edid_ext *edid_ext;
+	struct edid_cea *edid_cea;
+	char *cea_data;
+	struct edid_cea_data_block *block;
+	size_t cea_data_size = 0, vsdb_size;
+	const struct cea_vsdb *vsdb;
+
+	edid = (struct edid *) raw_edid;
+	memcpy(edid, igt_kms_get_base_edid(), sizeof(struct edid));
+	edid->extensions_len = 1;
+	edid_ext = &edid->extensions[0];
+	edid_cea = &edid_ext->data.cea;
+	cea_data = edid_cea->data;
+
+	/* The HDMI VSDB advertises support for InfoFrames */
+	block = (struct edid_cea_data_block *) &cea_data[cea_data_size];
+	vsdb = cea_vsdb_get_hdmi_default(&vsdb_size);
+	cea_data_size += edid_cea_data_block_set_vsdb(block, vsdb,
+						      vsdb_size);
+
+	/* Short Video Descriptor */
+	block = (struct edid_cea_data_block *) &cea_data[cea_data_size];
+	cea_data_size += edid_cea_data_block_set_svd(block, edid_ar_svds,
+						     sizeof(edid_ar_svds));
+
+	assert(cea_data_size <= sizeof(edid_cea->data));
+
+	edid_ext_set_cea(edid_ext, cea_data_size, 0, 0);
+
+	edid_update_checksum(edid);
+
+	return edid;
+}
+
+static void test_display_aspect_ratio(data_t *data, struct chamelium_port *port)
+{
+	igt_output_t *output;
+	igt_plane_t *primary;
+	drmModeConnector *connector;
+	drmModeModeInfo *mode;
+	int fb_id, i;
+	struct igt_fb fb;
+	bool found, ok;
+	struct chamelium_infoframe *infoframe;
+	struct infoframe_avi infoframe_avi;
+	uint8_t vic = 16; /* TODO: test more VICs */
+	const struct vic_mode *vic_mode;
+	uint32_t aspect_ratio;
+	enum infoframe_avi_picture_aspect_ratio frame_ar;
+
+	igt_require(chamelium_supports_get_last_infoframe(data->chamelium));
+
+	reset_state(data, port);
+
+	output = prepare_output(data, port, TEST_EDID_ASPECT_RATIO);
+	connector = chamelium_port_get_connector(data->chamelium, port, false);
+	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
+	igt_assert(primary);
+
+	vic_mode = &vic_modes[vic];
+	aspect_ratio = vic_mode->picture_ar;
+
+	found = false;
+	igt_assert(connector->count_modes > 0);
+	for (i = 0; i < connector->count_modes; i++) {
+		mode = &connector->modes[i];
+
+		if (vic_mode_matches_drm(vic_mode, mode)) {
+			found = true;
+			break;
+		}
+	}
+	igt_assert_f(found,
+		     "Failed to find mode with the correct aspect ratio\n");
+
+	fb_id = igt_create_color_pattern_fb(data->drm_fd,
+					    mode->hdisplay, mode->vdisplay,
+					    DRM_FORMAT_XRGB8888,
+					    LOCAL_DRM_FORMAT_MOD_NONE,
+					    0, 0, 0, &fb);
+	igt_assert(fb_id > 0);
+
+	enable_output(data, port, output, mode, &fb);
+
+	infoframe = chamelium_get_last_infoframe(data->chamelium, port,
+						 CHAMELIUM_INFOFRAME_AVI);
+	igt_assert_f(infoframe, "AVI InfoFrame not received\n");
+
+	ok = infoframe_avi_parse(&infoframe_avi, infoframe->version,
+				 infoframe->payload, infoframe->payload_size);
+	igt_assert_f(ok, "Failed to parse AVI InfoFrame\n");
+
+	frame_ar = get_infoframe_avi_picture_ar(aspect_ratio);
+
+	igt_debug("Checking AVI InfoFrame\n");
+	igt_debug("Picture aspect ratio: got %d, expected %d\n",
+		  infoframe_avi.picture_aspect_ratio, frame_ar);
+	igt_debug("Video Identification Code (VIC): got %d, expected %d\n",
+		  infoframe_avi.vic, vic);
+
+	igt_assert(infoframe_avi.picture_aspect_ratio == frame_ar);
+	igt_assert(infoframe_avi.vic == vic);
+
+	chamelium_infoframe_destroy(infoframe);
+	igt_remove_fb(data->drm_fd, &fb);
+	drmModeFreeConnector(connector);
+}
+
 
 /* Playback parameters control the audio signal we synthesize and send */
 #define PLAYBACK_CHANNELS 2
@@ -1121,6 +1446,59 @@
 		  success ? "ALL GREEN" : "FAILED");
 }
 
+static void check_audio_infoframe(struct audio_state *state)
+{
+	struct chamelium_infoframe *infoframe;
+	struct infoframe_audio infoframe_audio;
+	struct infoframe_audio expected = {0};
+	bool ok;
+
+	if (!chamelium_supports_get_last_infoframe(state->chamelium)) {
+		igt_debug("Skipping audio InfoFrame check: "
+			  "Chamelium board doesn't support GetLastInfoFrame\n");
+		return;
+	}
+
+	expected.coding_type = INFOFRAME_AUDIO_CT_PCM;
+	expected.channel_count = state->playback.channels;
+	expected.sampling_freq = state->playback.rate;
+	expected.sample_size = snd_pcm_format_width(state->playback.format);
+
+	infoframe = chamelium_get_last_infoframe(state->chamelium, state->port,
+						 CHAMELIUM_INFOFRAME_AUDIO);
+	if (infoframe == NULL && state->playback.channels <= 2) {
+		/* Audio InfoFrames are optional for mono and stereo audio */
+		igt_debug("Skipping audio InfoFrame check: "
+			  "no InfoFrame received\n");
+		return;
+	}
+	igt_assert_f(infoframe != NULL, "no audio InfoFrame received\n");
+
+	ok = infoframe_audio_parse(&infoframe_audio, infoframe->version,
+				   infoframe->payload, infoframe->payload_size);
+	chamelium_infoframe_destroy(infoframe);
+	igt_assert_f(ok, "failed to parse audio InfoFrame\n");
+
+	igt_debug("Checking audio InfoFrame:\n");
+	igt_debug("coding_type: got %d, expected %d\n",
+		  infoframe_audio.coding_type, expected.coding_type);
+	igt_debug("channel_count: got %d, expected %d\n",
+		  infoframe_audio.channel_count, expected.channel_count);
+	igt_debug("sampling_freq: got %d, expected %d\n",
+		  infoframe_audio.sampling_freq, expected.sampling_freq);
+	igt_debug("sample_size: got %d, expected %d\n",
+		  infoframe_audio.sample_size, expected.sample_size);
+
+	if (infoframe_audio.coding_type != INFOFRAME_AUDIO_CT_UNSPECIFIED)
+		igt_assert(infoframe_audio.coding_type == expected.coding_type);
+	if (infoframe_audio.channel_count >= 0)
+		igt_assert(infoframe_audio.channel_count == expected.channel_count);
+	if (infoframe_audio.sampling_freq >= 0)
+		igt_assert(infoframe_audio.sampling_freq == expected.sampling_freq);
+	if (infoframe_audio.sample_size >= 0)
+		igt_assert(infoframe_audio.sample_size == expected.sample_size);
+}
+
 static int
 audio_output_frequencies_callback(void *data, void *buffer, int samples)
 {
@@ -1246,6 +1624,8 @@
 	free(channel);
 	audio_signal_fini(state->signal);
 
+	check_audio_infoframe(state);
+
 	return success;
 }
 
@@ -2141,7 +2521,7 @@
 	igt_hpd_storm_reset(data->drm_fd);
 }
 
-static const unsigned char *get_edid(enum test_edid edid)
+static const struct edid *get_edid(enum test_edid edid)
 {
 	switch (edid) {
 	case TEST_EDID_BASE:
@@ -2152,6 +2532,8 @@
 		return igt_kms_get_hdmi_audio_edid();
 	case TEST_EDID_DP_AUDIO:
 		return igt_kms_get_dp_audio_edid();
+	case TEST_EDID_ASPECT_RATIO:
+		return get_aspect_ratio_edid();
 	}
 	assert(0); /* unreachable */
 }
@@ -2234,6 +2616,9 @@
 			test_hpd_storm_disable(&data, port,
 					       HPD_STORM_PULSE_INTERVAL_DP);
 
+		connector_subtest("dp-link-status", DisplayPort)
+			test_link_status(&data, port);
+
 		connector_subtest("dp-edid-change-during-suspend", DisplayPort)
 			test_suspend_resume_edid_change(&data, port,
 							SUSPEND_STATE_MEM,
@@ -2431,6 +2816,9 @@
 		connector_subtest("hdmi-audio-edid", HDMIA)
 			test_display_audio_edid(&data, port,
 						TEST_EDID_HDMI_AUDIO);
+
+		connector_subtest("hdmi-aspect-ratio", HDMIA)
+			test_display_aspect_ratio(&data, port);
 	}
 
 	igt_subtest_group {
diff --git a/tests/kms_content_protection.c b/tests/kms_content_protection.c
index ae6ab49..e676b60 100644
--- a/tests/kms_content_protection.c
+++ b/tests/kms_content_protection.c
@@ -24,9 +24,13 @@
 
 #include <poll.h>
 #include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/stat.h>
+#include <libudev.h>
 #include "igt.h"
 #include "igt_sysfs.h"
 #include "igt_kms.h"
+#include "igt_kmod.h"
 
 IGT_TEST_DESCRIPTION("Test content protection (HDCP)");
 
@@ -34,18 +38,39 @@
 	int drm_fd;
 	igt_display_t display;
 	struct igt_fb red, green;
+	unsigned int cp_tests;
 } data;
 
+/* Test flags */
+#define CP_DPMS					(1 << 0)
+#define CP_LIC					(1 << 1)
+#define CP_MEI_RELOAD				(1 << 2)
+#define CP_TYPE_CHANGE				(1 << 3)
+#define CP_UEVENT				(1 << 4)
+
 #define CP_UNDESIRED				0
 #define CP_DESIRED				1
 #define CP_ENABLED				2
 
+/*
+ * HDCP_CONTENT_TYPE_0 can be handled on both HDCP1.4 and HDCP2.2. Where as
+ * HDCP_CONTENT_TYPE_1 can be handled only through HDCP2.2.
+ */
+#define HDCP_CONTENT_TYPE_0				0
+#define HDCP_CONTENT_TYPE_1				1
+
 #define LIC_PERIOD_MSEC				(4 * 1000)
 /* Kernel retry count=3, Max time per authentication allowed = 6Sec */
 #define KERNEL_AUTH_TIME_ALLOWED_MSEC		(3 *  6 * 1000)
 #define KERNEL_DISABLE_TIME_ALLOWED_MSEC	(1 * 1000)
 #define FLIP_EVENT_POLLING_TIMEOUT_MSEC		1000
 
+__u8 facsimile_srm[] = {
+	0x80, 0x0, 0x0, 0x05, 0x01, 0x0, 0x0, 0x36, 0x02, 0x51, 0x1E, 0xF2,
+	0x1A, 0xCD, 0xE7, 0x26, 0x97, 0xF4, 0x01, 0x97, 0x10, 0x19, 0x92, 0x53,
+	0xE9, 0xF0, 0x59, 0x95, 0xA3, 0x7A, 0x3B, 0xFE, 0xE0, 0x9C, 0x76, 0xDD,
+	0x83, 0xAA, 0xC2, 0x5B, 0x24, 0xB3, 0x36, 0x84, 0x94, 0x75, 0x34, 0xDB,
+	0x10, 0x9E, 0x3B, 0x23, 0x13, 0xD8, 0x7A, 0xC2, 0x30, 0x79, 0x84};
 
 static void flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
 			 unsigned int tv_usec, void *_data)
@@ -87,6 +112,137 @@
 	return rc;
 }
 
+static bool hdcp_event(struct udev_monitor *uevent_monitor,
+		       struct udev *udev, uint32_t conn_id, uint32_t prop_id)
+{
+	struct udev_device *dev;
+	dev_t udev_devnum;
+	struct stat s;
+	const char *hotplug, *connector, *property;
+	bool ret = false;
+
+	dev = udev_monitor_receive_device(uevent_monitor);
+	if (!dev)
+		goto out;
+
+	udev_devnum = udev_device_get_devnum(dev);
+	fstat(data.display.drm_fd, &s);
+
+	hotplug = udev_device_get_property_value(dev, "HOTPLUG");
+	if (!(memcmp(&s.st_rdev, &udev_devnum, sizeof(dev_t)) == 0 &&
+	    hotplug && atoi(hotplug) == 1)) {
+		igt_debug("Not a Hotplug event\n");
+		goto out_dev;
+	}
+
+	connector = udev_device_get_property_value(dev, "CONNECTOR");
+	if (!(memcmp(&s.st_rdev, &udev_devnum, sizeof(dev_t)) == 0 &&
+	    connector && atoi(connector) == conn_id)) {
+		igt_debug("Not for connector id: %u\n", conn_id);
+		goto out_dev;
+	}
+
+	property = udev_device_get_property_value(dev, "PROPERTY");
+	if (!(memcmp(&s.st_rdev, &udev_devnum, sizeof(dev_t)) == 0 &&
+	    property && atoi(property) == prop_id)) {
+		igt_debug("Not for property id: %u\n", prop_id);
+		goto out_dev;
+	}
+	ret = true;
+
+out_dev:
+	udev_device_unref(dev);
+out:
+	return ret;
+}
+
+static void hdcp_udev_fini(struct udev_monitor *uevent_monitor,
+			   struct udev *udev)
+{
+	if (uevent_monitor)
+		udev_monitor_unref(uevent_monitor);
+	if (udev)
+		udev_unref(udev);
+}
+
+static int hdcp_udev_init(struct udev_monitor *uevent_monitor,
+			  struct udev *udev)
+{
+	int ret = -EINVAL;
+
+	udev = udev_new();
+	if (!udev) {
+		igt_info("failed to create udev object\n");
+		goto out;
+	}
+
+	uevent_monitor = udev_monitor_new_from_netlink(udev, "udev");
+	if (!uevent_monitor) {
+		igt_info("failed to create udev event monitor\n");
+		goto out;
+	}
+
+	ret = udev_monitor_filter_add_match_subsystem_devtype(uevent_monitor,
+							      "drm",
+							      "drm_minor");
+	if (ret < 0) {
+		igt_info("failed to filter for drm events\n");
+		goto out;
+	}
+
+	ret = udev_monitor_enable_receiving(uevent_monitor);
+	if (ret < 0) {
+		igt_info("failed to enable udev event reception\n");
+		goto out;
+	}
+
+	return udev_monitor_get_fd(uevent_monitor);
+
+out:
+	hdcp_udev_fini(uevent_monitor, udev);
+	return ret;
+}
+
+#define MAX_EVENTS	10
+static bool wait_for_hdcp_event(uint32_t conn_id, uint32_t prop_id,
+				uint32_t timeout_mSec)
+{
+
+	struct udev_monitor *uevent_monitor = NULL;
+	struct udev *udev = NULL;
+	int udev_fd, epoll_fd;
+	struct epoll_event event, events[MAX_EVENTS];
+	bool ret = false;
+
+	udev_fd = hdcp_udev_init(uevent_monitor, udev);
+	if (udev_fd < 0)
+		return false;
+
+	epoll_fd = epoll_create1(0);
+	if (epoll_fd == -1) {
+		igt_info("Failed to create epoll fd. %d\n", epoll_fd);
+		goto out_ep_create;
+	}
+
+	event.events = EPOLLIN | EPOLLERR;
+	event.data.fd = 0;
+
+	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, udev_fd, &event)) {
+		igt_info("failed to fd into epoll\n");
+		goto out_ep_ctl;
+	}
+
+	if (epoll_wait(epoll_fd, events, MAX_EVENTS, timeout_mSec))
+		ret = hdcp_event(uevent_monitor, udev, conn_id, prop_id);
+
+out_ep_ctl:
+	if (close(epoll_fd))
+		igt_info("failed to close the epoll fd\n");
+out_ep_create:
+	hdcp_udev_fini(uevent_monitor, udev);
+	return ret;
+}
+
 static bool
 wait_for_prop_value(igt_output_t *output, uint64_t expected,
 		    uint32_t timeout_mSec)
@@ -94,13 +250,25 @@
 	uint64_t val;
 	int i;
 
-	for (i = 0; i < timeout_mSec; i++) {
+	if (data.cp_tests & CP_UEVENT && expected != CP_UNDESIRED) {
+		igt_assert_f(wait_for_hdcp_event(output->id,
+			     output->props[IGT_CONNECTOR_CONTENT_PROTECTION],
+			     timeout_mSec), "uevent is not received");
+
 		val = igt_output_get_prop(output,
 					  IGT_CONNECTOR_CONTENT_PROTECTION);
 		if (val == expected)
 			return true;
-		usleep(1000);
+	} else {
+		for (i = 0; i < timeout_mSec; i++) {
+			val = igt_output_get_prop(output,
+						  IGT_CONNECTOR_CONTENT_PROTECTION);
+			if (val == expected)
+				return true;
+			usleep(1000);
+		}
 	}
+
 	igt_info("prop_value mismatch %" PRId64 " != %" PRId64 "\n",
 		 val, expected);
 
@@ -155,7 +323,8 @@
 	commit_display_and_wait_for_flip(s);
 }
 
-static bool test_cp_enable(igt_output_t *output, enum igt_commit_style s)
+static bool test_cp_enable(igt_output_t *output, enum igt_commit_style s,
+			   int content_type, bool type_change)
 {
 	igt_display_t *display = &data.display;
 	igt_plane_t *primary;
@@ -163,8 +332,15 @@
 
 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
 
-	igt_output_set_prop_value(output,
-				  IGT_CONNECTOR_CONTENT_PROTECTION, CP_DESIRED);
+	if (!type_change)
+		igt_output_set_prop_value(output,
+					  IGT_CONNECTOR_CONTENT_PROTECTION,
+					  CP_DESIRED);
+
+	if (output->props[IGT_CONNECTOR_HDCP_CONTENT_TYPE])
+		igt_output_set_prop_value(output,
+					  IGT_CONNECTOR_HDCP_CONTENT_TYPE,
+					  content_type);
 	igt_display_commit2(display, s);
 
 	ret = wait_for_prop_value(output, CP_ENABLED,
@@ -201,13 +377,18 @@
 }
 
 static void test_cp_enable_with_retry(igt_output_t *output,
-				      enum igt_commit_style s, int retry)
+				      enum igt_commit_style s, int retry,
+				      int content_type, bool expect_failure,
+				      bool type_change)
 {
+	int retry_orig = retry;
 	bool ret;
 
 	do {
-		test_cp_disable(output, s);
-		ret = test_cp_enable(output, s);
+		if (!type_change || retry_orig != retry)
+			test_cp_disable(output, s);
+
+		ret = test_cp_enable(output, s, content_type, type_change);
 
 		if (!ret && --retry)
 			igt_debug("Retry (%d/2) ...\n", 3 - retry);
@@ -216,7 +397,12 @@
 	if (!ret)
 		test_cp_disable(output, s);
 
-	igt_assert_f(ret, "Content Protection not enabled\n");
+	if (expect_failure)
+		igt_assert_f(!ret,
+			     "CP Enabled. Though it is expected to fail\n");
+	else
+		igt_assert_f(ret, "Content Protection not enabled\n");
+
 }
 
 static bool igt_pipe_is_free(igt_display_t *display, enum pipe pipe)
@@ -239,9 +425,30 @@
 	igt_assert_f(!ret, "Content Protection LIC Failed\n");
 }
 
+static bool write_srm_as_fw(const __u8 *srm, int len)
+{
+	int fd, ret, total = 0;
+
+	fd = open("/lib/firmware/display_hdcp_srm.bin",
+		  O_WRONLY | O_CREAT, S_IRWXU);
+	do {
+		ret = write(fd, srm + total, len - total);
+		if (ret < 0)
+			ret = -errno;
+		if (ret == -EINTR || ret == -EAGAIN)
+			continue;
+		if (ret <= 0)
+			break;
+		total += ret;
+	} while (total != len);
+	close(fd);
+
+	return total < len ? false : true;
+}
+
 static void test_content_protection_on_output(igt_output_t *output,
 					      enum igt_commit_style s,
-					      bool dpms_test)
+					      int content_type)
 {
 	igt_display_t *display = &data.display;
 	igt_plane_t *primary;
@@ -262,10 +469,40 @@
 			continue;
 
 		modeset_with_fb(pipe, output, s);
-		test_cp_enable_with_retry(output, s, 3);
-		test_cp_lic(output);
+		test_cp_enable_with_retry(output, s, 3, content_type, false,
+					  false);
 
-		if (dpms_test) {
+		if (data.cp_tests & CP_TYPE_CHANGE) {
+			/* Type 1 -> Type 0 */
+			test_cp_enable_with_retry(output, s, 3,
+						  HDCP_CONTENT_TYPE_0, false,
+						  true);
+			/* Type 0 -> Type 1 */
+			test_cp_enable_with_retry(output, s, 3,
+						  content_type, false,
+						  true);
+		}
+
+		if (data.cp_tests & CP_MEI_RELOAD) {
+			igt_assert_f(!igt_kmod_unload("mei_hdcp", 0),
+				     "mei_hdcp unload failed");
+
+			/* Expected to fail */
+			test_cp_enable_with_retry(output, s, 3,
+						  content_type, true, false);
+
+			igt_assert_f(!igt_kmod_load("mei_hdcp", NULL),
+				     "mei_hdcp load failed");
+
+			/* Expected to pass */
+			test_cp_enable_with_retry(output, s, 3,
+						  content_type, false, false);
+		}
+
+		if (data.cp_tests & CP_LIC)
+			test_cp_lic(output);
+
+		if (data.cp_tests & CP_DPMS) {
 			igt_pipe_set_prop_value(display, pipe,
 						IGT_CRTC_ACTIVE, 0);
 			igt_display_commit2(display, s);
@@ -277,7 +514,9 @@
 			ret = wait_for_prop_value(output, CP_ENABLED,
 						  KERNEL_AUTH_TIME_ALLOWED_MSEC);
 			if (!ret)
-				test_cp_enable_with_retry(output, s, 2);
+				test_cp_enable_with_retry(output, s, 2,
+							  content_type, false,
+							  false);
 		}
 
 		test_cp_disable(output, s);
@@ -304,7 +543,8 @@
 
 #define debugfs_read(fd, p, arr) __debugfs_read(fd, p, arr, sizeof(arr))
 
-#define MAX_SINK_HDCP_CAP_BUF_LEN	500
+#define MAX_SINK_HDCP_CAP_BUF_LEN	5000
+
 static bool sink_hdcp_capable(igt_output_t *output)
 {
 	char buf[MAX_SINK_HDCP_CAP_BUF_LEN];
@@ -322,26 +562,55 @@
 	return strstr(buf, "HDCP1.4");
 }
 
+static bool sink_hdcp2_capable(igt_output_t *output)
+{
+	char buf[MAX_SINK_HDCP_CAP_BUF_LEN];
+	int fd;
+
+	fd = igt_debugfs_connector_dir(data.drm_fd, output->name, O_RDONLY);
+	if (fd < 0)
+		return false;
+
+	debugfs_read(fd, "i915_hdcp_sink_capability", buf);
+	close(fd);
+
+	igt_debug("Sink capability: %s\n", buf);
+
+	return strstr(buf, "HDCP2.2");
+}
 
 static void
-test_content_protection(enum igt_commit_style s, bool dpms_test)
+test_content_protection(enum igt_commit_style s, int content_type)
 {
 	igt_display_t *display = &data.display;
 	igt_output_t *output;
 	int valid_tests = 0;
 
+	if (data.cp_tests & CP_MEI_RELOAD)
+		igt_require_f(igt_kmod_is_loaded("mei_hdcp"),
+			      "mei_hdcp module is not loaded\n");
+
 	for_each_connected_output(display, output) {
 		if (!output->props[IGT_CONNECTOR_CONTENT_PROTECTION])
 			continue;
 
+		if (!output->props[IGT_CONNECTOR_HDCP_CONTENT_TYPE] &&
+		    content_type)
+			continue;
+
 		igt_info("CP Test execution on %s\n", output->name);
-		if (!sink_hdcp_capable(output)) {
+
+		if (content_type && !sink_hdcp2_capable(output)) {
+			igt_info("\tSkip %s (Sink has no HDCP2.2 support)\n",
+				 output->name);
+			continue;
+		} else if (!sink_hdcp_capable(output)) {
 			igt_info("\tSkip %s (Sink has no HDCP support)\n",
 				 output->name);
 			continue;
 		}
 
-		test_content_protection_on_output(output, s, dpms_test);
+		test_content_protection_on_output(output, s, content_type);
 		valid_tests++;
 	}
 
@@ -358,17 +627,69 @@
 		igt_display_require(&data.display, data.drm_fd);
 	}
 
-	igt_subtest("legacy")
-		test_content_protection(COMMIT_LEGACY, false);
+	igt_subtest("legacy") {
+		data.cp_tests = 0;
+		test_content_protection(COMMIT_LEGACY, HDCP_CONTENT_TYPE_0);
+	}
 
 	igt_subtest("atomic") {
 		igt_require(data.display.is_atomic);
-		test_content_protection(COMMIT_ATOMIC, false);
+		data.cp_tests = 0;
+		test_content_protection(COMMIT_ATOMIC, HDCP_CONTENT_TYPE_0);
 	}
 
 	igt_subtest("atomic-dpms") {
 		igt_require(data.display.is_atomic);
-		test_content_protection(COMMIT_ATOMIC, true);
+		data.cp_tests = CP_DPMS;
+		test_content_protection(COMMIT_ATOMIC, HDCP_CONTENT_TYPE_0);
+	}
+
+	igt_subtest("LIC") {
+		igt_require(data.display.is_atomic);
+		data.cp_tests = CP_LIC;
+		test_content_protection(COMMIT_ATOMIC, HDCP_CONTENT_TYPE_0);
+	}
+
+	igt_subtest("type1") {
+		igt_require(data.display.is_atomic);
+		test_content_protection(COMMIT_ATOMIC, HDCP_CONTENT_TYPE_1);
+	}
+
+	igt_subtest("mei_interface") {
+		igt_require(data.display.is_atomic);
+		data.cp_tests = CP_MEI_RELOAD;
+		test_content_protection(COMMIT_ATOMIC, HDCP_CONTENT_TYPE_1);
+	}
+
+	igt_subtest("content_type_change") {
+		igt_require(data.display.is_atomic);
+		data.cp_tests = CP_TYPE_CHANGE;
+		test_content_protection(COMMIT_ATOMIC, HDCP_CONTENT_TYPE_1);
+	}
+
+	igt_subtest("uevent") {
+		igt_require(data.display.is_atomic);
+		data.cp_tests = CP_UEVENT;
+		test_content_protection(COMMIT_ATOMIC, HDCP_CONTENT_TYPE_0);
+	}
+
+	/*
+	 *  Testing the revocation check through SRM needs a HDCP sink with
+	 *  programmable Ksvs or we need a uAPI from kernel to read the
+	 *  connected HDCP sink's Ksv. With that we would be able to add that
+	 *  Ksv into a SRM and send in for revocation check. Since we dont have
+	 *  either of these options, we test SRM writing from userspace and
+	 *  validation of the same at kernel. Something is better than nothing.
+	 */
+	igt_subtest("srm") {
+		bool ret;
+
+		igt_require(data.display.is_atomic);
+		data.cp_tests = 0;
+		ret = write_srm_as_fw((const __u8 *)facsimile_srm,
+				      sizeof(facsimile_srm));
+		igt_assert_f(ret, "SRM update failed");
+		test_content_protection(COMMIT_ATOMIC, HDCP_CONTENT_TYPE_0);
 	}
 
 	igt_fixture
diff --git a/tests/kms_cursor_edge_walk.c b/tests/kms_cursor_edge_walk.c
index 809dca4..87d17e0 100644
--- a/tests/kms_cursor_edge_walk.c
+++ b/tests/kms_cursor_edge_walk.c
@@ -114,8 +114,6 @@
 
 	dx = (ex - sx)/XSTEP;
 
-	igt_pipe_crc_drain(data->pipe_crc);
-
 	i = 0;
 
 	for (x = sx; xdir * (x - ex) <= 0; x += dx) {
diff --git a/tests/kms_hdmi_inject.c b/tests/kms_hdmi_inject.c
index 7868424..5211050 100644
--- a/tests/kms_hdmi_inject.c
+++ b/tests/kms_hdmi_inject.c
@@ -65,8 +65,7 @@
 		connector =
 			drmModeGetConnectorCurrent(drm_fd, res->connectors[i]);
 
-		if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA &&
-		    connector->connection == DRM_MODE_DISCONNECTED)
+		if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA)
 			break;
 
 		drmModeFreeConnector(connector);
@@ -79,7 +78,7 @@
 static void
 hdmi_inject_4k(int drm_fd, drmModeConnector *connector)
 {
-	const unsigned char *edid;
+	const struct edid *edid;
 	struct kmstest_connector_config config;
 	int ret, cid, i, crtc_mask = -1;
 	int fb_id;
@@ -140,7 +139,7 @@
 static void
 hdmi_inject_audio(int drm_fd, drmModeConnector *connector)
 {
-	const unsigned char *edid;
+	const struct edid *edid;
 	int fb_id, cid, ret, crtc_mask = -1;
 	struct igt_fb fb;
 	struct kmstest_connector_config config;
@@ -203,6 +202,8 @@
 
 		connector = get_connector(drm_fd, res);
 		igt_require(connector);
+
+		kmstest_unset_all_crtcs(drm_fd, res);
 	}
 
 	igt_describe("Make sure that 4K modes exposed by DRM match the "
diff --git a/tests/kms_plane_multiple.c b/tests/kms_plane_multiple.c
index 81ed45d..255cd34 100644
--- a/tests/kms_plane_multiple.c
+++ b/tests/kms_plane_multiple.c
@@ -317,6 +317,8 @@
 		for_each_plane_on_pipe(&data->display, pipe, plane)
 			igt_plane_set_fb(plane, NULL);
 
+		igt_display_commit2(&data->display, COMMIT_ATOMIC);
+
 		for (int x = 0; x < c; x++)
 			igt_remove_fb(data->drm_fd, &data->fb[x]);
 	} while (!err && c < n_planes);
@@ -340,6 +342,8 @@
 		for_each_plane_on_pipe(&data->display, pipe, plane)
 			igt_plane_set_fb(plane, NULL);
 
+		igt_display_commit2(&data->display, COMMIT_ATOMIC);
+
 		for (int x = 0; x < c; x++)
 			igt_remove_fb(data->drm_fd, &data->fb[x]);
 
diff --git a/tests/kms_prime.c b/tests/kms_prime.c
new file mode 100644
index 0000000..1759957
--- /dev/null
+++ b/tests/kms_prime.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright © 2019 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 "igt_vgem.h"
+
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <time.h>
+
+struct crc_info {
+	igt_crc_t crc;
+	char *str;
+	const char *name;
+};
+
+static struct {
+	double r, g, b;
+	uint32_t color;
+	struct crc_info prime_crc, direct_crc;
+} colors[3] = {
+	{ .r = 0.0, .g = 0.0, .b = 0.0, .color = 0xff000000 },
+	{ .r = 1.0, .g = 1.0, .b = 1.0, .color = 0xffffffff },
+	{ .r = 1.0, .g = 0.0, .b = 0.0, .color = 0xffff0000 },
+};
+
+IGT_TEST_DESCRIPTION("Prime tests, focusing on KMS side");
+
+static bool has_prime_import(int fd)
+{
+	uint64_t value;
+
+	if (drmGetCap(fd, DRM_CAP_PRIME, &value))
+		return false;
+
+	return value & DRM_PRIME_CAP_IMPORT;
+}
+
+static bool has_prime_export(int fd)
+{
+	uint64_t value;
+
+	if (drmGetCap(fd, DRM_CAP_PRIME, &value))
+		return false;
+
+	return value & DRM_PRIME_CAP_EXPORT;
+}
+
+static igt_output_t *setup_display(int importer_fd, igt_display_t *display,
+				   enum pipe pipe)
+{
+	igt_display_require(display, importer_fd);
+	igt_skip_on(pipe >= display->n_pipes);
+	igt_output_t *output = igt_get_single_output_for_pipe(display, pipe);
+
+	igt_require_f(output, "No connector found for pipe %s\n",
+		      kmstest_pipe_name(pipe));
+
+	igt_display_reset(display);
+	igt_output_set_pipe(output, pipe);
+	return output;
+}
+
+static void prepare_scratch(int exporter_fd, struct vgem_bo *scratch,
+			    drmModeModeInfo *mode, uint32_t color)
+{
+	uint32_t *ptr;
+
+	scratch->width = mode->hdisplay;
+	scratch->height = mode->vdisplay;
+	scratch->bpp = 32;
+	vgem_create(exporter_fd, scratch);
+
+	ptr = vgem_mmap(exporter_fd, scratch, PROT_WRITE);
+	for (size_t idx = 0; idx < scratch->size / sizeof(*ptr); ++idx)
+		ptr[idx] = color;
+
+	munmap(ptr, scratch->size);
+}
+
+static void prepare_fb(int importer_fd, struct vgem_bo *scratch, struct igt_fb *fb)
+{
+	enum igt_color_encoding color_encoding = IGT_COLOR_YCBCR_BT709;
+	enum igt_color_range color_range = IGT_COLOR_YCBCR_LIMITED_RANGE;
+
+	igt_init_fb(fb, importer_fd, scratch->width, scratch->height,
+		    DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
+		    color_encoding, color_range);
+}
+
+static void import_fb(int importer_fd, struct igt_fb *fb,
+		      int dmabuf_fd, uint32_t pitch)
+{
+	uint32_t offsets[4] = {}, pitches[4] = {}, handles[4] = {};
+	int ret;
+
+	fb->gem_handle = prime_fd_to_handle(importer_fd, dmabuf_fd);
+
+	handles[0] = fb->gem_handle;
+	pitches[0] = pitch;
+	offsets[0] = 0;
+
+	ret = drmModeAddFB2(importer_fd, fb->width, fb->height,
+			    DRM_FORMAT_XRGB8888,
+			    handles, pitches, offsets,
+			    &fb->fb_id, 0);
+	igt_assert(ret == 0);
+}
+
+static void set_fb(struct igt_fb *fb,
+		   igt_display_t *display,
+		   igt_output_t *output)
+{
+	igt_plane_t *primary;
+	int ret;
+
+	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
+	igt_assert(primary);
+
+	igt_plane_set_fb(primary, fb);
+	ret = igt_display_commit(display);
+
+	igt_assert(ret == 0);
+}
+
+static void collect_crc_for_fb(int importer_fd, struct igt_fb *fb, igt_display_t *display,
+			       igt_output_t *output, igt_pipe_crc_t *pipe_crc,
+			       uint32_t color, struct crc_info *info)
+{
+	set_fb(fb, display, output);
+	igt_pipe_crc_collect_crc(pipe_crc, &info->crc);
+	info->str = igt_crc_to_string(&info->crc);
+	igt_debug("CRC through '%s' method for %#08x is %s\n",
+		  info->name, color, info->str);
+	igt_remove_fb(importer_fd, fb);
+}
+
+static void test_crc(int exporter_fd, int importer_fd)
+{
+	igt_display_t display;
+	igt_output_t *output;
+	igt_pipe_crc_t *pipe_crc;
+	enum pipe pipe = PIPE_A;
+	struct igt_fb fb;
+	int dmabuf_fd;
+	struct vgem_bo scratch = {}; /* despite the name, it suits for any
+				      * gem-compatible device
+				      * TODO: rename
+				      */
+	int i, j;
+	drmModeModeInfo *mode;
+
+	bool crc_equal = false;
+
+	output = setup_display(importer_fd, &display, pipe);
+
+	mode = igt_output_get_mode(output);
+	pipe_crc = igt_pipe_crc_new(importer_fd, pipe, INTEL_PIPE_CRC_SOURCE_AUTO);
+
+	for (i = 0; i < ARRAY_SIZE(colors); i++) {
+		prepare_scratch(exporter_fd, &scratch, mode, colors[i].color);
+		dmabuf_fd = prime_handle_to_fd(exporter_fd, scratch.handle);
+		gem_close(exporter_fd, scratch.handle);
+
+		prepare_fb(importer_fd, &scratch, &fb);
+		import_fb(importer_fd, &fb, dmabuf_fd, scratch.pitch);
+		close(dmabuf_fd);
+
+		colors[i].prime_crc.name = "prime";
+		collect_crc_for_fb(importer_fd, &fb, &display, output,
+				   pipe_crc, colors[i].color, &colors[i].prime_crc);
+
+		igt_create_color_fb(importer_fd,
+				    mode->hdisplay, mode->vdisplay,
+				    DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
+				    colors[i].r, colors[i].g, colors[i].b,
+				    &fb);
+
+		colors[i].direct_crc.name = "direct";
+		collect_crc_for_fb(importer_fd, &fb, &display, output,
+				   pipe_crc, colors[i].color, &colors[i].direct_crc);
+	}
+	igt_pipe_crc_free(pipe_crc);
+
+	igt_debug("CRC table:\n");
+	igt_debug("Color\t\tPrime\t\tDirect\n");
+	for (i = 0; i < ARRAY_SIZE(colors); i++) {
+		igt_debug("%#08x\t%.8s\t%.8s\n", colors[i].color,
+			  colors[i].prime_crc.str, colors[i].direct_crc.str);
+		free(colors[i].prime_crc.str);
+		free(colors[i].direct_crc.str);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(colors); i++) {
+		for (j = 0; j < ARRAY_SIZE(colors); j++) {
+			if (i == j) {
+				igt_assert_crc_equal(&colors[i].prime_crc.crc,
+						     &colors[j].direct_crc.crc);
+				continue;
+			}
+			crc_equal = igt_check_crc_equal(&colors[i].prime_crc.crc,
+							&colors[j].direct_crc.crc);
+			igt_assert_f(!crc_equal, "CRC should be different");
+		}
+	}
+	igt_display_fini(&display);
+}
+
+static void run_test_crc(int export_chipset, int import_chipset)
+{
+	int importer_fd = -1;
+	int exporter_fd = -1;
+
+	exporter_fd = drm_open_driver(export_chipset);
+	importer_fd = drm_open_driver_master(import_chipset);
+
+	igt_require(has_prime_export(exporter_fd));
+	igt_require(has_prime_import(importer_fd));
+	igt_require_pipe_crc(importer_fd);
+
+	test_crc(exporter_fd, importer_fd);
+	close(importer_fd);
+	close(exporter_fd);
+}
+
+igt_main
+{
+	igt_fixture {
+		kmstest_set_vt_graphics_mode();
+	}
+	igt_describe("Make a dumb buffer inside vgem, fill it, export to another device and compare the CRC");
+	igt_subtest("basic-crc")
+		run_test_crc(DRIVER_VGEM, DRIVER_ANY);
+}
diff --git a/tests/kms_properties.c b/tests/kms_properties.c
index eb9cbdd..18cf96e 100644
--- a/tests/kms_properties.c
+++ b/tests/kms_properties.c
@@ -91,7 +91,7 @@
 	if (atomic)
 		req = drmModeAtomicAlloc();
 
-	for ( i = prop->values[0]; i < prop->values[1] ; i++) {
+	for (i = prop->values[0]; i <= prop->values[1]; i++) {
 		if (!atomic) {
 			ret = drmModeObjectSetProperty(fd, id, type, prop_id, i);
 
diff --git a/tests/meson.build b/tests/meson.build
index 34a7402..a7b2b32 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -5,6 +5,7 @@
 	'core_getversion',
 	'core_setmaster_vs_auth',
 	'debugfs_test',
+	'dmabuf',
 	'drm_import_export',
 	'drm_mm',
 	'drm_read',
@@ -48,6 +49,7 @@
 	'kms_plane_lowres',
 	'kms_plane_multiple',
 	'kms_plane_scaling',
+	'kms_prime',
 	'kms_prop_blob',
 	'kms_properties',
 	'kms_psr',
diff --git a/tests/perf_pmu.c b/tests/perf_pmu.c
index 72b9166..d392a67 100644
--- a/tests/perf_pmu.c
+++ b/tests/perf_pmu.c
@@ -1712,116 +1712,110 @@
 		igt_subtest_f("init-sema-%s", e->name)
 			init(fd, e, I915_SAMPLE_SEMA);
 
-		igt_subtest_group {
-			igt_fixture {
-				gem_context_has_engine(fd, 0, e->flags);
-			}
+		/**
+		 * Test that engines show no load when idle.
+		 */
+		igt_subtest_f("idle-%s", e->name)
+			single(fd, e, 0);
 
-			/**
-			 * Test that engines show no load when idle.
-			 */
-			igt_subtest_f("idle-%s", e->name)
-				single(fd, e, 0);
+		/**
+		 * Test that a single engine reports load correctly.
+		 */
+		igt_subtest_f("busy-%s", e->name)
+			single(fd, e, TEST_BUSY);
+		igt_subtest_f("busy-idle-%s", e->name)
+			single(fd, e, TEST_BUSY | TEST_TRAILING_IDLE);
 
-			/**
-			 * Test that a single engine reports load correctly.
-			 */
-			igt_subtest_f("busy-%s", e->name)
-				single(fd, e, TEST_BUSY);
-			igt_subtest_f("busy-idle-%s", e->name)
-				single(fd, e, TEST_BUSY | TEST_TRAILING_IDLE);
+		/**
+		 * Test that when one engine is loaded other report no
+		 * load.
+		 */
+		igt_subtest_f("busy-check-all-%s", e->name)
+			busy_check_all(fd, e, num_engines, TEST_BUSY);
+		igt_subtest_f("busy-idle-check-all-%s", e->name)
+			busy_check_all(fd, e, num_engines,
+				       TEST_BUSY | TEST_TRAILING_IDLE);
 
-			/**
-			 * Test that when one engine is loaded other report no
-			 * load.
-			 */
-			igt_subtest_f("busy-check-all-%s", e->name)
-				busy_check_all(fd, e, num_engines, TEST_BUSY);
-			igt_subtest_f("busy-idle-check-all-%s", e->name)
-				busy_check_all(fd, e, num_engines,
-					       TEST_BUSY | TEST_TRAILING_IDLE);
+		/**
+		 * Test that when all except one engine are loaded all
+		 * loads are correctly reported.
+		 */
+		igt_subtest_f("most-busy-check-all-%s", e->name)
+			most_busy_check_all(fd, e, num_engines,
+					    TEST_BUSY);
+		igt_subtest_f("most-busy-idle-check-all-%s", e->name)
+			most_busy_check_all(fd, e, num_engines,
+					    TEST_BUSY |
+					    TEST_TRAILING_IDLE);
 
-			/**
-			 * Test that when all except one engine are loaded all
-			 * loads are correctly reported.
-			 */
-			igt_subtest_f("most-busy-check-all-%s", e->name)
-				most_busy_check_all(fd, e, num_engines,
-						    TEST_BUSY);
-			igt_subtest_f("most-busy-idle-check-all-%s", e->name)
-				most_busy_check_all(fd, e, num_engines,
-						    TEST_BUSY |
-						    TEST_TRAILING_IDLE);
+		/**
+		 * Test that semphore counters report no activity on
+		 * idle or busy engines.
+		 */
+		igt_subtest_f("idle-no-semaphores-%s", e->name)
+			no_sema(fd, e, 0);
 
-			/**
-			 * Test that semphore counters report no activity on
-			 * idle or busy engines.
-			 */
-			igt_subtest_f("idle-no-semaphores-%s", e->name)
-				no_sema(fd, e, 0);
+		igt_subtest_f("busy-no-semaphores-%s", e->name)
+			no_sema(fd, e, TEST_BUSY);
 
-			igt_subtest_f("busy-no-semaphores-%s", e->name)
-				no_sema(fd, e, TEST_BUSY);
+		igt_subtest_f("busy-idle-no-semaphores-%s", e->name)
+			no_sema(fd, e, TEST_BUSY | TEST_TRAILING_IDLE);
 
-			igt_subtest_f("busy-idle-no-semaphores-%s", e->name)
-				no_sema(fd, e, TEST_BUSY | TEST_TRAILING_IDLE);
+		/**
+		 * Test that semaphore waits are correctly reported.
+		 */
+		igt_subtest_f("semaphore-wait-%s", e->name)
+			sema_wait(fd, e, TEST_BUSY);
 
-			/**
-			 * Test that semaphore waits are correctly reported.
-			 */
-			igt_subtest_f("semaphore-wait-%s", e->name)
-				sema_wait(fd, e, TEST_BUSY);
+		igt_subtest_f("semaphore-wait-idle-%s", e->name)
+			sema_wait(fd, e,
+				  TEST_BUSY | TEST_TRAILING_IDLE);
 
-			igt_subtest_f("semaphore-wait-idle-%s", e->name)
-				sema_wait(fd, e,
-					  TEST_BUSY | TEST_TRAILING_IDLE);
+		/**
+		 * Check that two perf clients do not influence each
+		 * others observations.
+		 */
+		igt_subtest_f("multi-client-%s", e->name)
+			multi_client(fd, e);
 
-			/**
-			 * Check that two perf clients do not influence each
-			 * others observations.
-			 */
-			igt_subtest_f("multi-client-%s", e->name)
-				multi_client(fd, e);
+		/**
+		 * Check that reported usage is correct when PMU is
+		 * enabled after the batch is running.
+		 */
+		igt_subtest_f("busy-start-%s", e->name)
+			busy_start(fd, e);
 
-			/**
-			* Check that reported usage is correct when PMU is
-			* enabled after the batch is running.
-			*/
-			igt_subtest_f("busy-start-%s", e->name)
-				busy_start(fd, e);
+		/**
+		 * Check that reported usage is correct when PMU is
+		 * enabled after two batches are running.
+		 */
+		igt_subtest_f("busy-double-start-%s", e->name) {
+			gem_require_contexts(fd);
+			busy_double_start(fd, e);
+		}
 
-			/**
-			 * Check that reported usage is correct when PMU is
-			 * enabled after two batches are running.
-			 */
-			igt_subtest_f("busy-double-start-%s", e->name) {
-				gem_require_contexts(fd);
-				busy_double_start(fd, e);
-			}
+		/**
+		 * Check that the PMU can be safely enabled in face of
+		 * interrupt-heavy engine load.
+		 */
+		igt_subtest_f("enable-race-%s", e->name)
+			test_enable_race(fd, e);
 
-			/**
-			 * Check that the PMU can be safely enabled in face of
-			 * interrupt-heavy engine load.
-			 */
-			igt_subtest_f("enable-race-%s", e->name)
-				test_enable_race(fd, e);
+		/**
+		 * Check engine busyness accuracy is as expected.
+		 */
+		for (i = 0; i < ARRAY_SIZE(pct); i++) {
+			igt_subtest_f("busy-accuracy-%u-%s",
+				      pct[i], e->name)
+				accuracy(fd, e, pct[i], 10);
+		}
 
-			/**
-			 * Check engine busyness accuracy is as expected.
-			 */
-			for (i = 0; i < ARRAY_SIZE(pct); i++) {
-				igt_subtest_f("busy-accuracy-%u-%s",
-					      pct[i], e->name)
-					accuracy(fd, e, pct[i], 10);
-			}
+		igt_subtest_f("busy-hang-%s", e->name) {
+			igt_hang_t hang = igt_allow_hang(fd, 0, 0);
 
-			igt_subtest_f("busy-hang-%s", e->name) {
-				igt_hang_t hang = igt_allow_hang(fd, 0, 0);
+			single(fd, e, TEST_BUSY | FLAG_HANG);
 
-				single(fd, e, TEST_BUSY | FLAG_HANG);
-
-				igt_disallow_hang(fd, hang);
-			}
+			igt_disallow_hang(fd, hang);
 		}
 
 		/**
@@ -1901,19 +1895,12 @@
 		}
 
 		__for_each_physical_engine(render_fd, e) {
-			igt_subtest_group {
-				igt_fixture {
-					gem_context_has_engine(render_fd,
-							   0, e->flags);
-				}
-
-				igt_subtest_f("render-node-busy-%s", e->name)
-					single(render_fd, e, TEST_BUSY);
-				igt_subtest_f("render-node-busy-idle-%s",
-					      e->name)
-					single(render_fd, e,
-					       TEST_BUSY | TEST_TRAILING_IDLE);
-			}
+			igt_subtest_f("render-node-busy-%s", e->name)
+				single(render_fd, e, TEST_BUSY);
+			igt_subtest_f("render-node-busy-idle-%s",
+				      e->name)
+				single(render_fd, e,
+				       TEST_BUSY | TEST_TRAILING_IDLE);
 		}
 
 		igt_fixture {