diff mbox

[7/7] lib: add igt_draw

Message ID 1427928059-2129-1-git-send-email-przanoni@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Paulo Zanoni April 1, 2015, 10:40 p.m. UTC
From: Paulo Zanoni <paulo.r.zanoni@intel.com>

For all those IGT tests that need an easy way to draw rectangles on
buffers using different methods. Current planned users: FBC and PSR
CRC tests.

There is also a tests/kms_draw_crc program to check if the library is
sane.

v2: - Move the test from lib/tests to tests/ (Daniel).
    - Add igt_require() to filter out the swizzling/tiling methods we
      don't support (Daniel).
    - Simplify reloc handling on the BLT case (Daniel).
    - Document enum igt_draw_method (Daniel).
    - Document igt_draw_get_method_name() (Paulo).
v3: - Add IGT_DRAW_MMAP_WC (Chris).
    - Implement the other trivial swizzling methods (Chris).
    - Remove the gem_sync() calls (Chris).

Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
---
 lib/Makefile.sources   |   2 +
 lib/igt_draw.c         | 562 +++++++++++++++++++++++++++++++++++++++++++++++++
 lib/igt_draw.h         |  65 ++++++
 tests/.gitignore       |   1 +
 tests/Makefile.sources |   1 +
 tests/kms_draw_crc.c   | 247 ++++++++++++++++++++++
 6 files changed, 878 insertions(+)
 create mode 100644 lib/igt_draw.c
 create mode 100644 lib/igt_draw.h
 create mode 100644 tests/kms_draw_crc.c

Comments

Chris Wilson April 1, 2015, 11:15 p.m. UTC | #1
On Wed, Apr 01, 2015 at 07:40:59PM -0300, Paulo Zanoni wrote:
> +static void draw_rect_mmap_wc(int fd, struct buf_data *buf, struct rect *rect,
> +			      uint32_t color)
> +{
> +	uint32_t *ptr;
> +	uint32_t tiling, swizzle;
> +
> +	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
> +
> +	/* We didn't implement suport for the older tiling methods yet. */
> +	if (tiling != I915_TILING_NONE)
> +		igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);

But you now do! You need something like:

static void get_tiling_and_swizzle(int fd,
				   struct buf_data *buf,
				   int *tiling, int *swizzle)
{
	struct local_i915_gem_get_tiling_v2 {
		uint32_t handle;
		uint32_t tiling_mode;
		uint32_t swizzle_mode;
		uint32_t phys_swizzle_mode;
	} tiling;
#define LOCAL_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct local_i915_gem_get_tiling_v2)

	memset(&tiling, 0, sizeof(tiling));
	tiling.handle = buf->handle;
	do_ioctl(fd, LOCAL_IOCTL_I915_GEM_GET_TILING, &tiling);
	igt_require(tiling.phys_swizzle == tiling.swizzle_mode ||
		    intel_gen(intel_get_drm_devid(fd)) >= 5); /* old kernel? */

	*tiling = tiling.tilling_mode;
	*swizzle = tiling.swizzle_mode;
}
Chris Wilson April 1, 2015, 11:17 p.m. UTC | #2
On Thu, Apr 02, 2015 at 12:15:13AM +0100, Chris Wilson wrote:
> On Wed, Apr 01, 2015 at 07:40:59PM -0300, Paulo Zanoni wrote:
> > +static void draw_rect_mmap_wc(int fd, struct buf_data *buf, struct rect *rect,
> > +			      uint32_t color)
> > +{
> > +	uint32_t *ptr;
> > +	uint32_t tiling, swizzle;
> > +
> > +	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
> > +
> > +	/* We didn't implement suport for the older tiling methods yet. */
> > +	if (tiling != I915_TILING_NONE)
> > +		igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
> 
> But you now do!

Oh, with the exception of gen2. You need to adjust the tilesize
constants there.
-Chris
Daniel Vetter April 7, 2015, 8:10 a.m. UTC | #3
On Thu, Apr 02, 2015 at 12:15:13AM +0100, Chris Wilson wrote:
> On Wed, Apr 01, 2015 at 07:40:59PM -0300, Paulo Zanoni wrote:
> > +static void draw_rect_mmap_wc(int fd, struct buf_data *buf, struct rect *rect,
> > +			      uint32_t color)
> > +{
> > +	uint32_t *ptr;
> > +	uint32_t tiling, swizzle;
> > +
> > +	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
> > +
> > +	/* We didn't implement suport for the older tiling methods yet. */
> > +	if (tiling != I915_TILING_NONE)
> > +		igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
> 
> But you now do! You need something like:

The problem is that the kernel hides bit17 swizzling. I chatted with Paulo
on irc about this and we decided just ignore them all is the simplest
approach.
-Daniel

> 
> static void get_tiling_and_swizzle(int fd,
> 				   struct buf_data *buf,
> 				   int *tiling, int *swizzle)
> {
> 	struct local_i915_gem_get_tiling_v2 {
> 		uint32_t handle;
> 		uint32_t tiling_mode;
> 		uint32_t swizzle_mode;
> 		uint32_t phys_swizzle_mode;
> 	} tiling;
> #define LOCAL_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct local_i915_gem_get_tiling_v2)
> 
> 	memset(&tiling, 0, sizeof(tiling));
> 	tiling.handle = buf->handle;
> 	do_ioctl(fd, LOCAL_IOCTL_I915_GEM_GET_TILING, &tiling);
> 	igt_require(tiling.phys_swizzle == tiling.swizzle_mode ||
> 		    intel_gen(intel_get_drm_devid(fd)) >= 5); /* old kernel? */
> 
> 	*tiling = tiling.tilling_mode;
> 	*swizzle = tiling.swizzle_mode;
> }
> 
> -- 
> Chris Wilson, Intel Open Source Technology Centre
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
Chris Wilson April 7, 2015, 8:36 a.m. UTC | #4
On Tue, Apr 07, 2015 at 10:10:25AM +0200, Daniel Vetter wrote:
> On Thu, Apr 02, 2015 at 12:15:13AM +0100, Chris Wilson wrote:
> > On Wed, Apr 01, 2015 at 07:40:59PM -0300, Paulo Zanoni wrote:
> > > +static void draw_rect_mmap_wc(int fd, struct buf_data *buf, struct rect *rect,
> > > +			      uint32_t color)
> > > +{
> > > +	uint32_t *ptr;
> > > +	uint32_t tiling, swizzle;
> > > +
> > > +	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
> > > +
> > > +	/* We didn't implement suport for the older tiling methods yet. */
> > > +	if (tiling != I915_TILING_NONE)
> > > +		igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
> > 
> > But you now do! You need something like:
> 
> The problem is that the kernel hides bit17 swizzling. I chatted with Paulo
> on irc about this and we decided just ignore them all is the simplest
> approach.

Urm, that was the whole point of GET_TILING v2. That small function is
all you need to determine when bit17 is in effect and then you get to
reuse all the direct CPU methods (as they are also used by userspace)
for earlier gen.
-Chris
Daniel Vetter April 7, 2015, 9:07 a.m. UTC | #5
On Tue, Apr 07, 2015 at 09:36:37AM +0100, Chris Wilson wrote:
> On Tue, Apr 07, 2015 at 10:10:25AM +0200, Daniel Vetter wrote:
> > On Thu, Apr 02, 2015 at 12:15:13AM +0100, Chris Wilson wrote:
> > > On Wed, Apr 01, 2015 at 07:40:59PM -0300, Paulo Zanoni wrote:
> > > > +static void draw_rect_mmap_wc(int fd, struct buf_data *buf, struct rect *rect,
> > > > +			      uint32_t color)
> > > > +{
> > > > +	uint32_t *ptr;
> > > > +	uint32_t tiling, swizzle;
> > > > +
> > > > +	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
> > > > +
> > > > +	/* We didn't implement suport for the older tiling methods yet. */
> > > > +	if (tiling != I915_TILING_NONE)
> > > > +		igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
> > > 
> > > But you now do! You need something like:
> > 
> > The problem is that the kernel hides bit17 swizzling. I chatted with Paulo
> > on irc about this and we decided just ignore them all is the simplest
> > approach.
> 
> Urm, that was the whole point of GET_TILING v2. That small function is
> all you need to determine when bit17 is in effect and then you get to
> reuse all the direct CPU methods (as they are also used by userspace)
> for earlier gen.

Oh right completely forgot that we've added this. But imo can be added on
top once we need it (it's not just gen2 but also some gen3 which need
different tile dimensions).
-Daniel
Chris Wilson April 7, 2015, 10:12 a.m. UTC | #6
On Tue, Apr 07, 2015 at 11:07:07AM +0200, Daniel Vetter wrote:
> On Tue, Apr 07, 2015 at 09:36:37AM +0100, Chris Wilson wrote:
> > On Tue, Apr 07, 2015 at 10:10:25AM +0200, Daniel Vetter wrote:
> > > On Thu, Apr 02, 2015 at 12:15:13AM +0100, Chris Wilson wrote:
> > > > On Wed, Apr 01, 2015 at 07:40:59PM -0300, Paulo Zanoni wrote:
> > > > > +static void draw_rect_mmap_wc(int fd, struct buf_data *buf, struct rect *rect,
> > > > > +			      uint32_t color)
> > > > > +{
> > > > > +	uint32_t *ptr;
> > > > > +	uint32_t tiling, swizzle;
> > > > > +
> > > > > +	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
> > > > > +
> > > > > +	/* We didn't implement suport for the older tiling methods yet. */
> > > > > +	if (tiling != I915_TILING_NONE)
> > > > > +		igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
> > > > 
> > > > But you now do! You need something like:
> > > 
> > > The problem is that the kernel hides bit17 swizzling. I chatted with Paulo
> > > on irc about this and we decided just ignore them all is the simplest
> > > approach.
> > 
> > Urm, that was the whole point of GET_TILING v2. That small function is
> > all you need to determine when bit17 is in effect and then you get to
> > reuse all the direct CPU methods (as they are also used by userspace)
> > for earlier gen.
> 
> Oh right completely forgot that we've added this. But imo can be added on
> top once we need it (it's not just gen2 but also some gen3 which need
> different tile dimensions).

Tile size is 2048 for gen2 only right. Tile width is the same for all
gen3 for tiling X (and gen2), but tiling Y width depends on subgen.
Right?

Just need to check because I have code that depends on this...
-Chris
Daniel Vetter April 7, 2015, 1:44 p.m. UTC | #7
On Tue, Apr 07, 2015 at 11:12:09AM +0100, Chris Wilson wrote:
> On Tue, Apr 07, 2015 at 11:07:07AM +0200, Daniel Vetter wrote:
> > On Tue, Apr 07, 2015 at 09:36:37AM +0100, Chris Wilson wrote:
> > > On Tue, Apr 07, 2015 at 10:10:25AM +0200, Daniel Vetter wrote:
> > > > On Thu, Apr 02, 2015 at 12:15:13AM +0100, Chris Wilson wrote:
> > > > > On Wed, Apr 01, 2015 at 07:40:59PM -0300, Paulo Zanoni wrote:
> > > > > > +static void draw_rect_mmap_wc(int fd, struct buf_data *buf, struct rect *rect,
> > > > > > +			      uint32_t color)
> > > > > > +{
> > > > > > +	uint32_t *ptr;
> > > > > > +	uint32_t tiling, swizzle;
> > > > > > +
> > > > > > +	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
> > > > > > +
> > > > > > +	/* We didn't implement suport for the older tiling methods yet. */
> > > > > > +	if (tiling != I915_TILING_NONE)
> > > > > > +		igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
> > > > > 
> > > > > But you now do! You need something like:
> > > > 
> > > > The problem is that the kernel hides bit17 swizzling. I chatted with Paulo
> > > > on irc about this and we decided just ignore them all is the simplest
> > > > approach.
> > > 
> > > Urm, that was the whole point of GET_TILING v2. That small function is
> > > all you need to determine when bit17 is in effect and then you get to
> > > reuse all the direct CPU methods (as they are also used by userspace)
> > > for earlier gen.
> > 
> > Oh right completely forgot that we've added this. But imo can be added on
> > top once we need it (it's not just gen2 but also some gen3 which need
> > different tile dimensions).
> 
> Tile size is 2048 for gen2 only right. Tile width is the same for all
> gen3 for tiling X (and gen2), but tiling Y width depends on subgen.
> Right?
> 
> Just need to check because I have code that depends on this...

Oh right thought about Y tiling which changes on gen3. X tiling matches
your description afaik - I consider gem_tiled_pread the authoritative
source for this stuff.
-Daniel
Paulo Zanoni May 5, 2015, 9:30 p.m. UTC | #8
2015-04-07 10:44 GMT-03:00 Daniel Vetter <daniel@ffwll.ch>:
> On Tue, Apr 07, 2015 at 11:12:09AM +0100, Chris Wilson wrote:
>> On Tue, Apr 07, 2015 at 11:07:07AM +0200, Daniel Vetter wrote:
>> > On Tue, Apr 07, 2015 at 09:36:37AM +0100, Chris Wilson wrote:
>> > > On Tue, Apr 07, 2015 at 10:10:25AM +0200, Daniel Vetter wrote:
>> > > > On Thu, Apr 02, 2015 at 12:15:13AM +0100, Chris Wilson wrote:
>> > > > > On Wed, Apr 01, 2015 at 07:40:59PM -0300, Paulo Zanoni wrote:
>> > > > > > +static void draw_rect_mmap_wc(int fd, struct buf_data *buf, struct rect *rect,
>> > > > > > +                         uint32_t color)
>> > > > > > +{
>> > > > > > +   uint32_t *ptr;
>> > > > > > +   uint32_t tiling, swizzle;
>> > > > > > +
>> > > > > > +   gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
>> > > > > > +
>> > > > > > +   /* We didn't implement suport for the older tiling methods yet. */
>> > > > > > +   if (tiling != I915_TILING_NONE)
>> > > > > > +           igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
>> > > > >
>> > > > > But you now do! You need something like:
>> > > >
>> > > > The problem is that the kernel hides bit17 swizzling. I chatted with Paulo
>> > > > on irc about this and we decided just ignore them all is the simplest
>> > > > approach.
>> > >
>> > > Urm, that was the whole point of GET_TILING v2. That small function is
>> > > all you need to determine when bit17 is in effect and then you get to
>> > > reuse all the direct CPU methods (as they are also used by userspace)
>> > > for earlier gen.
>> >
>> > Oh right completely forgot that we've added this. But imo can be added on
>> > top once we need it (it's not just gen2 but also some gen3 which need
>> > different tile dimensions).
>>
>> Tile size is 2048 for gen2 only right. Tile width is the same for all
>> gen3 for tiling X (and gen2), but tiling Y width depends on subgen.
>> Right?
>>
>> Just need to check because I have code that depends on this...
>
> Oh right thought about Y tiling which changes on gen3. X tiling matches
> your description afaik - I consider gem_tiled_pread the authoritative
> source for this stuff.

Ok, so after all this discussion, what is the conclusion here? What is
required before we can merge the patch?

Notice that with this patch we still won't have anybody using the
library (except for the test added by the patch), and all the users I
plan to add are gen5+. Also, I created VIZ-5495 to make sure we create
intel_bo_fill() at some point (and, when we do it, I suggest we just
use the implementation from igt_draw.c).

I really think the improvements discussed here can be done after we
merge the patch because the it's not breaking anything, AFAICS. So,
can I push this? Also, after the code is on igt, it will probably be
easier to discuss the possible improvements since we'll be able to
send patches.

Thanks for the reviews,
Paulo

> -Daniel
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
Daniel Vetter May 6, 2015, 9 a.m. UTC | #9
On Tue, May 05, 2015 at 06:30:50PM -0300, Paulo Zanoni wrote:
> 2015-04-07 10:44 GMT-03:00 Daniel Vetter <daniel@ffwll.ch>:
> > On Tue, Apr 07, 2015 at 11:12:09AM +0100, Chris Wilson wrote:
> >> On Tue, Apr 07, 2015 at 11:07:07AM +0200, Daniel Vetter wrote:
> >> > On Tue, Apr 07, 2015 at 09:36:37AM +0100, Chris Wilson wrote:
> >> > > On Tue, Apr 07, 2015 at 10:10:25AM +0200, Daniel Vetter wrote:
> >> > > > On Thu, Apr 02, 2015 at 12:15:13AM +0100, Chris Wilson wrote:
> >> > > > > On Wed, Apr 01, 2015 at 07:40:59PM -0300, Paulo Zanoni wrote:
> >> > > > > > +static void draw_rect_mmap_wc(int fd, struct buf_data *buf, struct rect *rect,
> >> > > > > > +                         uint32_t color)
> >> > > > > > +{
> >> > > > > > +   uint32_t *ptr;
> >> > > > > > +   uint32_t tiling, swizzle;
> >> > > > > > +
> >> > > > > > +   gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
> >> > > > > > +
> >> > > > > > +   /* We didn't implement suport for the older tiling methods yet. */
> >> > > > > > +   if (tiling != I915_TILING_NONE)
> >> > > > > > +           igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
> >> > > > >
> >> > > > > But you now do! You need something like:
> >> > > >
> >> > > > The problem is that the kernel hides bit17 swizzling. I chatted with Paulo
> >> > > > on irc about this and we decided just ignore them all is the simplest
> >> > > > approach.
> >> > >
> >> > > Urm, that was the whole point of GET_TILING v2. That small function is
> >> > > all you need to determine when bit17 is in effect and then you get to
> >> > > reuse all the direct CPU methods (as they are also used by userspace)
> >> > > for earlier gen.
> >> >
> >> > Oh right completely forgot that we've added this. But imo can be added on
> >> > top once we need it (it's not just gen2 but also some gen3 which need
> >> > different tile dimensions).
> >>
> >> Tile size is 2048 for gen2 only right. Tile width is the same for all
> >> gen3 for tiling X (and gen2), but tiling Y width depends on subgen.
> >> Right?
> >>
> >> Just need to check because I have code that depends on this...
> >
> > Oh right thought about Y tiling which changes on gen3. X tiling matches
> > your description afaik - I consider gem_tiled_pread the authoritative
> > source for this stuff.
> 
> Ok, so after all this discussion, what is the conclusion here? What is
> required before we can merge the patch?
> 
> Notice that with this patch we still won't have anybody using the
> library (except for the test added by the patch), and all the users I
> plan to add are gen5+. Also, I created VIZ-5495 to make sure we create
> intel_bo_fill() at some point (and, when we do it, I suggest we just
> use the implementation from igt_draw.c).
> 
> I really think the improvements discussed here can be done after we
> merge the patch because the it's not breaking anything, AFAICS. So,
> can I push this? Also, after the code is on igt, it will probably be
> easier to discuss the possible improvements since we'll be able to
> send patches.

I guess you could capture the discussion here in a TODO comment in
igt_draw? But yeah I thought we've discussed this on irc and agreed that
moving ahead is ok, and that extensions can be done later on.
-Daniel
diff mbox

Patch

diff --git a/lib/Makefile.sources b/lib/Makefile.sources
index 3d93629..85dc321 100644
--- a/lib/Makefile.sources
+++ b/lib/Makefile.sources
@@ -52,6 +52,8 @@  libintel_tools_la_SOURCES = 	\
 	igt_fb.h		\
 	igt_core.c		\
 	igt_core.h		\
+	igt_draw.c		\
+	igt_draw.h		\
 	$(NULL)
 
 .PHONY: version.h.tmp
diff --git a/lib/igt_draw.c b/lib/igt_draw.c
new file mode 100644
index 0000000..14e470f
--- /dev/null
+++ b/lib/igt_draw.c
@@ -0,0 +1,562 @@ 
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include <sys/mman.h>
+
+#include "igt_draw.h"
+
+#include "drmtest.h"
+#include "intel_chipset.h"
+#include "igt_core.h"
+#include "igt_fb.h"
+#include "ioctl_wrappers.h"
+
+/**
+ * SECTION:igt_draw
+ * @short_description: drawing helpers for tests
+ * @title: i-g-t draw
+ * @include: igt_draw.h
+ *
+ * This library contains some functions for drawing rectangles on buffers using
+ * the many different drawing methods we have. It also contains some wrappers
+ * that make the process easier if you have the abstract objects in hand.
+ *
+ * All functions assume the buffers are in the XRGB 8:8:8 format.
+ *
+ */
+
+/* Some internal data structures to avoid having to pass tons of parameters
+ * around everything. */
+struct cmd_data {
+	drm_intel_bufmgr *bufmgr;
+	drm_intel_context *context;
+};
+
+struct buf_data {
+	uint32_t handle;
+	uint32_t size;
+	uint32_t stride;
+};
+
+struct rect {
+	int x;
+	int y;
+	int w;
+	int h;
+};
+
+/**
+ * igt_draw_get_method_name:
+ *
+ * Simple function to transform the enum into a string. Useful when naming
+ * subtests and printing debug messages.
+ */
+const char *igt_draw_get_method_name(enum igt_draw_method method)
+{
+	switch (method) {
+	case IGT_DRAW_MMAP_CPU:
+		return "mmap-cpu";
+	case IGT_DRAW_MMAP_GTT:
+		return "mmap-gtt";
+	case IGT_DRAW_MMAP_WC:
+		return "mmap-wc";
+	case IGT_DRAW_PWRITE:
+		return "pwrite";
+	case IGT_DRAW_BLT:
+		return "blt";
+	case IGT_DRAW_RENDER:
+		return "render";
+	default:
+		igt_assert(false);
+	}
+}
+
+#define BIT(num, bit) ((num >> bit) & 1)
+
+static int swizzle_addr(int addr, int swizzle)
+{
+	int bit6;
+
+	switch (swizzle) {
+	case I915_BIT_6_SWIZZLE_NONE:
+		bit6 = BIT(addr, 6);
+		break;
+	case I915_BIT_6_SWIZZLE_9:
+		bit6 = BIT(addr, 6) ^ BIT(addr, 9);
+		break;
+	case I915_BIT_6_SWIZZLE_9_10:
+		bit6 = BIT(addr, 6) ^ BIT(addr, 9) ^ BIT(addr, 10);
+		break;
+	case I915_BIT_6_SWIZZLE_9_11:
+		bit6 = BIT(addr, 6) ^ BIT(addr, 9) ^ BIT(addr, 11);
+		break;
+	case I915_BIT_6_SWIZZLE_9_10_11:
+		bit6 = BIT(addr, 6) ^ BIT(addr, 9) ^ BIT(addr, 10) ^
+		       BIT(addr, 11);
+		break;
+	case I915_BIT_6_SWIZZLE_UNKNOWN:
+	case I915_BIT_6_SWIZZLE_9_17:
+	case I915_BIT_6_SWIZZLE_9_10_17:
+	default:
+		/* If we hit this case, we need to implement support for the
+		 * appropriate swizzling method. */
+		igt_require(false);
+		break;
+	}
+
+	addr &= ~(1 << 6);
+	addr |= (bit6 << 6);
+	return addr;
+}
+
+/* It's all in "pixel coordinates", so make sure you multiply/divide by the bpp
+ * if you need to. */
+static int linear_x_y_to_tiled_pos(int x, int y, uint32_t stride, int swizzle)
+{
+	int x_tile_size, y_tile_size;
+	int x_tile_n, y_tile_n, x_tile_off, y_tile_off;
+	int line_size, tile_size;
+	int tile_n, tile_off;
+	int tiled_pos, tiles_per_line;
+	int bpp;
+
+	line_size = stride;
+	x_tile_size = 512;
+	y_tile_size = 8;
+	tile_size = x_tile_size * y_tile_size;
+	tiles_per_line = line_size / x_tile_size;
+	bpp = sizeof(uint32_t);
+
+	y_tile_n = y / y_tile_size;
+	y_tile_off = y % y_tile_size;
+
+	x_tile_n = (x * bpp) / x_tile_size;
+	x_tile_off = (x * bpp) % x_tile_size;
+
+	tile_n = y_tile_n * tiles_per_line + x_tile_n;
+	tile_off = y_tile_off * x_tile_size + x_tile_off;
+	tiled_pos = tile_n * tile_size + tile_off;
+
+	tiled_pos = swizzle_addr(tiled_pos, swizzle);
+
+	return tiled_pos / bpp;
+}
+
+/* It's all in "pixel coordinates", so make sure you multiply/divide by the bpp
+ * if you need to. */
+static void tiled_pos_to_x_y_linear(int tiled_pos, uint32_t stride,
+				    int swizzle, int *x, int *y)
+{
+	int tile_n, tile_off, tiles_per_line, line_size;
+	int x_tile_off, y_tile_off;
+	int x_tile_n, y_tile_n;
+	int x_tile_size, y_tile_size, tile_size;
+	int bpp;
+
+	tiled_pos = swizzle_addr(tiled_pos, swizzle);
+
+	line_size = stride;
+	x_tile_size = 512;
+	y_tile_size = 8;
+	tile_size = x_tile_size * y_tile_size;
+	tiles_per_line = line_size / x_tile_size;
+	bpp = sizeof(uint32_t);
+
+	tile_n = tiled_pos / tile_size;
+	tile_off = tiled_pos % tile_size;
+
+	y_tile_off = tile_off / x_tile_size;
+	x_tile_off = tile_off % x_tile_size;
+
+	x_tile_n = tile_n % tiles_per_line;
+	y_tile_n = tile_n / tiles_per_line;
+
+	*x = (x_tile_n * x_tile_size + x_tile_off) / bpp;
+	*y = y_tile_n * y_tile_size + y_tile_off;
+}
+
+static void draw_rect_ptr_linear(uint32_t *ptr, uint32_t stride,
+				  struct rect *rect, uint32_t color)
+{
+	int x, y, line_begin;
+
+	for (y = rect->y; y < rect->y + rect->h; y++) {
+		line_begin = y * stride / sizeof(uint32_t);
+		for (x = rect->x; x < rect->x + rect->w; x++)
+			ptr[line_begin + x] = color;
+	}
+
+}
+
+static void draw_rect_ptr_tiled(uint32_t *ptr, uint32_t stride, int swizzle,
+				 struct rect *rect, uint32_t color)
+{
+	int x, y, pos;
+
+	for (y = rect->y; y < rect->y + rect->h; y++) {
+		for (x = rect->x; x < rect->x + rect->w; x++) {
+			pos = linear_x_y_to_tiled_pos(x, y, stride, swizzle);
+			ptr[pos] = color;
+		}
+	}
+}
+
+static void draw_rect_mmap_cpu(int fd, struct buf_data *buf, struct rect *rect,
+			       uint32_t color)
+{
+	uint32_t *ptr;
+	uint32_t tiling, swizzle;
+
+	gem_set_domain(fd, buf->handle, I915_GEM_DOMAIN_CPU,
+		       I915_GEM_DOMAIN_CPU);
+	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
+
+	/* We didn't implement suport for the older tiling methods yet. */
+	if (tiling != I915_TILING_NONE)
+		igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
+
+	ptr = gem_mmap__cpu(fd, buf->handle, 0, buf->size, 0);
+	igt_assert(ptr);
+
+	switch (tiling) {
+	case I915_TILING_NONE:
+		draw_rect_ptr_linear(ptr, buf->stride, rect, color);
+		break;
+	case I915_TILING_X:
+		draw_rect_ptr_tiled(ptr, buf->stride, swizzle, rect, color);
+		break;
+	default:
+		igt_assert(false);
+		break;
+	}
+
+	gem_sw_finish(fd, buf->handle);
+
+	igt_assert(munmap(ptr, buf->size) == 0);
+}
+
+static void draw_rect_mmap_gtt(int fd, struct buf_data *buf, struct rect *rect,
+			       uint32_t color)
+{
+	uint32_t *ptr;
+
+	ptr = gem_mmap__gtt(fd, buf->handle, buf->size, PROT_READ | PROT_WRITE);
+	igt_assert(ptr);
+
+	draw_rect_ptr_linear(ptr, buf->stride, rect, color);
+
+	igt_assert(munmap(ptr, buf->size) == 0);
+}
+
+static void draw_rect_mmap_wc(int fd, struct buf_data *buf, struct rect *rect,
+			      uint32_t color)
+{
+	uint32_t *ptr;
+	uint32_t tiling, swizzle;
+
+	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
+
+	/* We didn't implement suport for the older tiling methods yet. */
+	if (tiling != I915_TILING_NONE)
+		igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
+
+	ptr = gem_mmap__wc(fd, buf->handle, 0, buf->size,
+			   PROT_READ | PROT_WRITE);
+	igt_assert(ptr);
+
+	switch (tiling) {
+	case I915_TILING_NONE:
+		draw_rect_ptr_linear(ptr, buf->stride, rect, color);
+		break;
+	case I915_TILING_X:
+		draw_rect_ptr_tiled(ptr, buf->stride, swizzle, rect, color);
+		break;
+	default:
+		igt_assert(false);
+		break;
+	}
+
+	igt_assert(munmap(ptr, buf->size) == 0);
+}
+
+static void draw_rect_pwrite_untiled(int fd, struct buf_data *buf,
+				     struct rect *rect, uint32_t color)
+{
+	uint32_t tmp[rect->w];
+	int i, y, offset, bpp;
+
+	bpp = sizeof(uint32_t);
+
+	for (i = 0; i < rect->w; i++)
+		tmp[i] = color;
+
+	for (y = rect->y; y < rect->y + rect->h; y++) {
+		offset = (y * buf->stride) + (rect->x * bpp);
+		gem_write(fd, buf->handle, offset, tmp, rect->w * bpp);
+	}
+}
+
+static void draw_rect_pwrite_tiled(int fd, struct buf_data *buf,
+				   struct rect *rect, uint32_t color,
+				   uint32_t swizzle)
+{
+	int i;
+	int tiled_pos, bpp, x, y;
+	uint32_t tmp[1024];
+	int tmp_used = 0, tmp_size = ARRAY_SIZE(tmp);
+	bool flush_tmp = false;
+	int tmp_start_pos = 0;
+
+	/* We didn't implement suport for the older tiling methods yet. */
+	igt_require(intel_gen(intel_get_drm_devid(fd)) >= 5);
+
+	bpp = sizeof(uint32_t);
+
+	/* Instead of doing one pwrite per pixel, we try to group the maximum
+	 * amount of consecutive pixels we can in a single pwrite: that's why we
+	 * use the "tmp" variables. */
+	for (i = 0; i < tmp_size; i++)
+		tmp[i] = color;
+
+	for (tiled_pos = 0; tiled_pos < buf->size; tiled_pos += bpp) {
+		tiled_pos_to_x_y_linear(tiled_pos, buf->stride, swizzle, &x, &y);
+
+		if (x >= rect->x && x < rect->x + rect->w &&
+		    y >= rect->y && y < rect->y + rect->h) {
+			if (tmp_used == 0)
+				tmp_start_pos = tiled_pos;
+			tmp_used++;
+		} else {
+			flush_tmp = true;
+		}
+
+		if (tmp_used == tmp_size || (flush_tmp && tmp_used > 0)) {
+			gem_write(fd, buf->handle, tmp_start_pos, tmp,
+				  tmp_used * bpp);
+			flush_tmp = false;
+			tmp_used = 0;
+		}
+	}
+}
+
+static void draw_rect_pwrite(int fd, struct buf_data *buf,
+			     struct rect *rect, uint32_t color)
+{
+	uint32_t tiling, swizzle;
+
+	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
+
+	switch (tiling) {
+	case I915_TILING_NONE:
+		draw_rect_pwrite_untiled(fd, buf, rect, color);
+		break;
+	case I915_TILING_X:
+		draw_rect_pwrite_tiled(fd, buf, rect, color, swizzle);
+		break;
+	default:
+		igt_assert(false);
+		break;
+	}
+}
+
+static void draw_rect_blt(int fd, struct cmd_data *cmd_data,
+			  struct buf_data *buf, struct rect *rect,
+			  uint32_t color)
+{
+	drm_intel_bo *dst;
+	struct intel_batchbuffer *batch;
+	int blt_cmd_len, blt_cmd_tiling;
+	uint32_t devid = intel_get_drm_devid(fd);
+	int gen = intel_gen(devid);
+	uint32_t tiling, swizzle;
+	int pitch;
+
+	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
+
+	dst = gem_handle_to_libdrm_bo(cmd_data->bufmgr, fd, "", buf->handle);
+	igt_assert(dst);
+
+	batch = intel_batchbuffer_alloc(cmd_data->bufmgr, devid);
+	igt_assert(batch);
+
+	blt_cmd_len = (gen >= 8) ?  0x5 : 0x4;
+	blt_cmd_tiling = (tiling) ? XY_COLOR_BLT_TILED : 0;
+	pitch = (tiling) ? buf->stride / 4 : buf->stride;
+
+	BEGIN_BATCH(6, 1);
+	OUT_BATCH(XY_COLOR_BLT_CMD_NOLEN | XY_COLOR_BLT_WRITE_ALPHA |
+		  XY_COLOR_BLT_WRITE_RGB | blt_cmd_tiling | blt_cmd_len);
+	OUT_BATCH((3 << 24) | (0xF0 << 16) | pitch);
+	OUT_BATCH((rect->y << 16) | rect->x);
+	OUT_BATCH(((rect->y + rect->h) << 16) | (rect->x + rect->w));
+	OUT_RELOC_FENCED(dst, 0, I915_GEM_DOMAIN_RENDER, 0);
+	OUT_BATCH(color);
+	ADVANCE_BATCH();
+
+	intel_batchbuffer_flush(batch);
+	intel_batchbuffer_free(batch);
+}
+
+static void draw_rect_render(int fd, struct cmd_data *cmd_data,
+			     struct buf_data *buf, struct rect *rect,
+			     uint32_t color)
+{
+	drm_intel_bo *src, *dst;
+	uint32_t devid = intel_get_drm_devid(fd);
+	igt_render_copyfunc_t rendercopy = igt_get_render_copyfunc(devid);
+	struct igt_buf src_buf, dst_buf;
+	struct intel_batchbuffer *batch;
+	uint32_t tiling, swizzle;
+	struct buf_data tmp;
+
+	igt_skip_on(!rendercopy);
+
+	gem_get_tiling(fd, buf->handle, &tiling, &swizzle);
+
+	/* We create a temporary buffer and copy from it using rendercopy. */
+	tmp.size = rect->w * rect->h * sizeof(uint32_t);
+	tmp.handle = gem_create(fd, tmp.size);
+	tmp.stride = rect->w * sizeof(uint32_t);
+	draw_rect_mmap_cpu(fd, &tmp, &(struct rect){0, 0, rect->w, rect->h},
+			   color);
+
+	src = gem_handle_to_libdrm_bo(cmd_data->bufmgr, fd, "", tmp.handle);
+	igt_assert(src);
+	dst = gem_handle_to_libdrm_bo(cmd_data->bufmgr, fd, "", buf->handle);
+	igt_assert(dst);
+
+	src_buf.bo = src;
+	src_buf.stride = tmp.stride;
+	src_buf.tiling = I915_TILING_NONE;
+	src_buf.size = tmp.size;
+	dst_buf.bo = dst;
+	dst_buf.stride = buf->stride;
+	dst_buf.tiling = tiling;
+	dst_buf.size = buf->size;
+
+	batch = intel_batchbuffer_alloc(cmd_data->bufmgr, devid);
+	igt_assert(batch);
+
+	rendercopy(batch, cmd_data->context, &src_buf, 0, 0, rect->w, rect->h,
+		   &dst_buf, rect->x, rect->y);
+
+	intel_batchbuffer_free(batch);
+	gem_close(fd, tmp.handle);
+}
+
+/**
+ * igt_draw_rect:
+ * @fd: the DRM file descriptor
+ * @bufmgr: the libdrm bufmgr, only required for IGT_DRAW_BLT and
+ *          IGT_DRAW_RENDER
+ * @context: the context, can be NULL if you don't want to think about it
+ * @buf_handle: the handle of the buffer where you're going to draw to
+ * @buf_size: the size of the buffer
+ * @buf_stride: the stride of the buffer
+ * @method: method you're going to use to write to the buffer
+ * @rect_x: horizontal position on the buffer where your rectangle starts
+ * @rect_y: vertical position on the buffer where your rectangle starts
+ * @rect_w: width of the rectangle
+ * @rect_h: height of the rectangle
+ * @color: color of the rectangle
+ *
+ * This function draws a colored rectangle on the destination buffer, allowing
+ * you to specify the method used to draw the rectangle. We assume 32 bit pixels
+ * with 8 bits per color.
+ */
+void igt_draw_rect(int fd, drm_intel_bufmgr *bufmgr, drm_intel_context *context,
+		   uint32_t buf_handle, uint32_t buf_size, uint32_t buf_stride,
+		   enum igt_draw_method method, int rect_x, int rect_y,
+		   int rect_w, int rect_h, uint32_t color)
+{
+	struct cmd_data cmd_data = {
+		.bufmgr = bufmgr,
+		.context = context,
+	};
+	struct buf_data buf = {
+		.handle = buf_handle,
+		.size = buf_size,
+		.stride = buf_stride,
+	};
+	struct rect rect = {
+		.x = rect_x,
+		.y = rect_y,
+		.w = rect_w,
+		.h = rect_h,
+	};
+
+	switch (method) {
+	case IGT_DRAW_MMAP_CPU:
+		draw_rect_mmap_cpu(fd, &buf, &rect, color);
+		break;
+	case IGT_DRAW_MMAP_GTT:
+		draw_rect_mmap_gtt(fd, &buf, &rect, color);
+		break;
+	case IGT_DRAW_MMAP_WC:
+		draw_rect_mmap_wc(fd, &buf, &rect, color);
+		break;
+	case IGT_DRAW_PWRITE:
+		draw_rect_pwrite(fd, &buf, &rect, color);
+		break;
+	case IGT_DRAW_BLT:
+		draw_rect_blt(fd, &cmd_data, &buf, &rect, color);
+		break;
+	case IGT_DRAW_RENDER:
+		draw_rect_render(fd, &cmd_data, &buf, &rect, color);
+		break;
+	default:
+		igt_assert(false);
+		break;
+	}
+}
+
+/**
+ * igt_draw_rect_fb:
+ *
+ * This is exactly the same as igt_draw_rect, but you can pass an igt_fb instead
+ * of manually providing its details. See igt_draw_rect.
+ */
+void igt_draw_rect_fb(int fd, drm_intel_bufmgr *bufmgr,
+		      drm_intel_context *context, struct igt_fb *fb,
+		      enum igt_draw_method method, int rect_x, int rect_y,
+		      int rect_w, int rect_h, uint32_t color)
+{
+	igt_draw_rect(fd, bufmgr, context, fb->gem_handle, fb->size, fb->stride,
+		      method, rect_x, rect_y, rect_w, rect_h, color);
+}
+
+/**
+ * igt_draw_fill_fb:
+ * @fd: the DRM file descriptor
+ * @fb: the FB that is going to be filled
+ * @color: the color you're going to paint it
+ *
+ * This function just paints an igt_fb using the provided color. It assumes 32
+ * bit pixels with 8 bit colors.
+ */
+void igt_draw_fill_fb(int fd, struct igt_fb *fb, uint32_t color)
+{
+	igt_draw_rect_fb(fd, NULL, NULL, fb, IGT_DRAW_MMAP_GTT,
+			 0, 0, fb->width, fb->height, color);
+}
diff --git a/lib/igt_draw.h b/lib/igt_draw.h
new file mode 100644
index 0000000..61ffad5
--- /dev/null
+++ b/lib/igt_draw.h
@@ -0,0 +1,65 @@ 
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __IGT_DRAW_H__
+#define __IGT_DRAW_H__
+
+#include <intel_bufmgr.h>
+#include "igt_fb.h"
+
+/**
+ *igt_draw_method:
+ * @IGT_DRAW_MMAP_CPU: draw using a CPU mmap.
+ * @IGT_DRAW_MMAP_GTT: draw using a GTT mmap.
+ * @IGT-DRAW_MMAP_WC: draw using the WC mmap.
+ * @IGT_DRAW_PWRITE: draw using the pwrite ioctl.
+ * @IGT_DRAW_BLT: draw using the BLT ring.
+ * @IGT_DRAW_RENDER: draw using the render ring.
+ * @IGT_DRAW_METHOD_COUNT: useful for iterating through everything.
+ */
+enum igt_draw_method {
+	IGT_DRAW_MMAP_CPU,
+	IGT_DRAW_MMAP_GTT,
+	IGT_DRAW_MMAP_WC,
+	IGT_DRAW_PWRITE,
+	IGT_DRAW_BLT,
+	IGT_DRAW_RENDER,
+	IGT_DRAW_METHOD_COUNT,
+};
+
+const char *igt_draw_get_method_name(enum igt_draw_method method);
+
+void igt_draw_rect(int fd, drm_intel_bufmgr *bufmgr, drm_intel_context *context,
+		   uint32_t buf_handle, uint32_t buf_size, uint32_t buf_stride,
+		   enum igt_draw_method method, int rect_x, int rect_y,
+		   int rect_w, int rect_h, uint32_t color);
+
+void igt_draw_rect_fb(int fd, drm_intel_bufmgr *bufmgr,
+		      drm_intel_context *context, struct igt_fb *fb,
+		      enum igt_draw_method method, int rect_x, int rect_y,
+		      int rect_w, int rect_h, uint32_t color);
+
+void igt_draw_fill_fb(int fd, struct igt_fb *fb, uint32_t color);
+
+#endif /* __IGT_DRAW_H__ */
diff --git a/tests/.gitignore b/tests/.gitignore
index 1f0e2d1..ae9de29 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -125,6 +125,7 @@  gen7_forcewake_mt
 kms_3d
 kms_addfb
 kms_cursor_crc
+kms_draw_crc
 kms_fbc_crc
 kms_fence_pin_leak
 kms_flip
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 93e05e4..b8941b0 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -69,6 +69,7 @@  TESTS_progs_M = \
 	gem_write_read_ring_switch \
 	kms_addfb \
 	kms_cursor_crc \
+	kms_draw_crc \
 	kms_fbc_crc \
 	kms_flip \
 	kms_flip_event_leak \
diff --git a/tests/kms_draw_crc.c b/tests/kms_draw_crc.c
new file mode 100644
index 0000000..1630cc2
--- /dev/null
+++ b/tests/kms_draw_crc.c
@@ -0,0 +1,247 @@ 
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+/* This program tests whether the igt_draw library actually works. */
+
+#include "drmtest.h"
+#include "igt_aux.h"
+#include "igt_draw.h"
+#include "igt_debugfs.h"
+#include "igt_fb.h"
+#include "igt_kms.h"
+
+#define MAX_CONNECTORS 32
+
+struct modeset_params {
+	uint32_t crtc_id;
+	uint32_t connector_id;
+	drmModeModeInfoPtr mode;
+};
+
+int drm_fd;
+drmModeResPtr drm_res;
+drmModeConnectorPtr drm_connectors[MAX_CONNECTORS];
+drm_intel_bufmgr *bufmgr;
+igt_pipe_crc_t *pipe_crc;
+
+bool has_method_base_crc = false;
+igt_crc_t method_base_crc;
+
+struct modeset_params ms;
+
+static void find_modeset_params(void)
+{
+	int i;
+	uint32_t connector_id = 0, crtc_id;
+	drmModeModeInfoPtr mode = NULL;
+
+	for (i = 0; i < drm_res->count_connectors; i++) {
+		drmModeConnectorPtr c = drm_connectors[i];
+
+		if (c->count_modes) {
+			connector_id = c->connector_id;
+			mode = &c->modes[0];
+			break;
+		}
+	}
+	igt_require(connector_id);
+
+	crtc_id = drm_res->crtcs[0];
+	igt_assert(crtc_id);
+	igt_assert(mode);
+
+	ms.connector_id = connector_id;
+	ms.crtc_id = crtc_id;
+	ms.mode = mode;
+
+}
+
+static void get_method_crc(enum igt_draw_method method, uint64_t tiling,
+			   igt_crc_t *crc)
+{
+	struct igt_fb fb;
+	int rc;
+
+	igt_create_fb(drm_fd, ms.mode->hdisplay, ms.mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, tiling, &fb);
+	igt_draw_rect_fb(drm_fd, bufmgr, NULL, &fb, method,
+			 0, 0, fb.width, fb.height, 0xFF);
+
+	igt_draw_rect_fb(drm_fd, bufmgr, NULL, &fb, method,
+			 fb.width / 4, fb.height / 4,
+			 fb.width / 2, fb.height / 2, 0xFF00);
+	igt_draw_rect_fb(drm_fd, bufmgr, NULL, &fb, method,
+			 fb.width / 8, fb.height / 8,
+			 fb.width / 4, fb.height / 4, 0xFF0000);
+	igt_draw_rect_fb(drm_fd, bufmgr, NULL, &fb, method,
+			 fb.width / 2, fb.height / 2,
+			 fb.width / 3, fb.height / 3, 0xFF00FF);
+
+	rc = drmModeSetCrtc(drm_fd, ms.crtc_id, fb.fb_id, 0, 0,
+			    &ms.connector_id, 1, ms.mode);
+	igt_assert(rc == 0);
+
+	igt_debug_wait_for_keypress("crc");
+	igt_pipe_crc_collect_crc(pipe_crc, crc);
+
+	kmstest_unset_all_crtcs(drm_fd, drm_res);
+	igt_remove_fb(drm_fd, &fb);
+}
+
+static void draw_method_subtest(enum igt_draw_method method, uint64_t tiling)
+{
+	igt_crc_t crc;
+
+	kmstest_unset_all_crtcs(drm_fd, drm_res);
+
+	find_modeset_params();
+
+	/* Use IGT_DRAW_MMAP_GTT on an untiled buffer as the parameter for
+	 * comparison. Cache the value so we don't recompute it for every single
+	 * subtest. */
+	if (!has_method_base_crc) {
+		get_method_crc(IGT_DRAW_MMAP_GTT, LOCAL_DRM_FORMAT_MOD_NONE,
+			       &method_base_crc);
+		has_method_base_crc = true;
+	}
+
+	get_method_crc(method, tiling, &crc);
+	igt_assert_crc_equal(&crc, &method_base_crc);
+}
+
+static void get_fill_crc(uint64_t tiling, igt_crc_t *crc)
+{
+	struct igt_fb fb;
+	int rc;
+
+	igt_create_fb(drm_fd, ms.mode->hdisplay, ms.mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, tiling, &fb);
+
+	igt_draw_fill_fb(drm_fd, &fb, 0xFF);
+
+	rc = drmModeSetCrtc(drm_fd, ms.crtc_id, fb.fb_id, 0, 0,
+			    &ms.connector_id, 1, ms.mode);
+	igt_assert(rc == 0);
+
+	igt_debug_wait_for_keypress("crc");
+	igt_pipe_crc_collect_crc(pipe_crc, crc);
+
+	kmstest_unset_all_crtcs(drm_fd, drm_res);
+	igt_remove_fb(drm_fd, &fb);
+}
+
+static void fill_fb_subtest(void)
+{
+	int rc;
+	struct igt_fb fb;
+	igt_crc_t base_crc, crc;
+
+	kmstest_unset_all_crtcs(drm_fd, drm_res);
+
+	find_modeset_params();
+
+	igt_create_fb(drm_fd, ms.mode->hdisplay, ms.mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE, &fb);
+
+	igt_draw_rect_fb(drm_fd, bufmgr, NULL, &fb, IGT_DRAW_MMAP_GTT,
+			 0, 0, fb.width, fb.height, 0xFF);
+
+	rc = drmModeSetCrtc(drm_fd, ms.crtc_id, fb.fb_id, 0, 0,
+			    &ms.connector_id, 1, ms.mode);
+	igt_assert(rc == 0);
+
+	igt_debug_wait_for_keypress("crc");
+	igt_pipe_crc_collect_crc(pipe_crc, &base_crc);
+
+	get_fill_crc(LOCAL_DRM_FORMAT_MOD_NONE, &crc);
+	igt_assert_crc_equal(&crc, &base_crc);
+
+	get_fill_crc(LOCAL_I915_FORMAT_MOD_X_TILED, &crc);
+	igt_assert_crc_equal(&crc, &base_crc);
+
+	kmstest_unset_all_crtcs(drm_fd, drm_res);
+	igt_remove_fb(drm_fd, &fb);
+}
+
+static void setup_environment(void)
+{
+	int i;
+
+	drm_fd = drm_open_any_master();
+	igt_require(drm_fd >= 0);
+
+	drm_res = drmModeGetResources(drm_fd);
+	igt_assert(drm_res->count_connectors <= MAX_CONNECTORS);
+
+	for (i = 0; i < drm_res->count_connectors; i++)
+		drm_connectors[i] = drmModeGetConnector(drm_fd,
+							drm_res->connectors[i]);
+
+	kmstest_set_vt_graphics_mode();
+
+	bufmgr = drm_intel_bufmgr_gem_init(drm_fd, 4096);
+	igt_assert(bufmgr);
+	drm_intel_bufmgr_gem_enable_reuse(bufmgr);
+
+	pipe_crc = igt_pipe_crc_new(0, INTEL_PIPE_CRC_SOURCE_AUTO);
+}
+
+static void teardown_environment(void)
+{
+	int i;
+
+	igt_pipe_crc_free(pipe_crc);
+
+	drm_intel_bufmgr_destroy(bufmgr);
+
+	for (i = 0; i < drm_res->count_connectors; i++)
+		drmModeFreeConnector(drm_connectors[i]);
+
+	drmModeFreeResources(drm_res);
+	close(drm_fd);
+}
+
+igt_main
+{
+	enum igt_draw_method method;
+
+	igt_fixture
+		setup_environment();
+
+	for (method = 0; method < IGT_DRAW_METHOD_COUNT; method++) {
+		igt_subtest_f("draw-method-%s-untiled",
+			      igt_draw_get_method_name(method))
+			draw_method_subtest(method, LOCAL_DRM_FORMAT_MOD_NONE);
+		igt_subtest_f("draw-method-%s-tiled",
+			      igt_draw_get_method_name(method))
+			draw_method_subtest(method,
+					    LOCAL_I915_FORMAT_MOD_X_TILED);
+	}
+
+	igt_subtest("fill-fb")
+		fill_fb_subtest();
+
+	igt_fixture
+		teardown_environment();
+}