diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index d594038..068a44e 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -64,6 +64,7 @@ TESTS_progs_M = \
 	gem_streaming_writes \
 	gem_tiled_blits \
 	gem_tiled_partial_pwrite_pread \
+	gem_trtt \
 	gem_userptr_blits \
 	gem_write_read_ring_switch \
 	kms_addfb_basic \
diff --git a/tests/gem_trtt.c b/tests/gem_trtt.c
new file mode 100644
index 0000000..f652b67
--- /dev/null
+++ b/tests/gem_trtt.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Akash Goel <akash.goel@intel.com>
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <malloc.h>
+#include "drm.h"
+#include "ioctl_wrappers.h"
+#include "drmtest.h"
+#include "intel_chipset.h"
+#include "intel_io.h"
+#include "i915_drm.h"
+#include <assert.h>
+#include <sys/wait.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include "igt_kms.h"
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define BO_SIZE 4096
+#define EXEC_OBJECT_PINNED	(1<<4)
+#define EXEC_OBJECT_SUPPORTS_48B_ADDRESS (1<<3)
+
+#define NO_PPGTT 0
+#define ALIASING_PPGTT 1
+#define FULL_32_BIT_PPGTT 2
+#define FULL_48_BIT_PPGTT 3
+/* uses_full_ppgtt
+ * Finds supported PPGTT details.
+ * @fd DRM fd
+ * @min can be
+ * 0 - No PPGTT
+ * 1 - Aliasing PPGTT
+ * 2 - Full PPGTT (32b)
+ * 3 - Full PPGTT (48b)
+ * RETURNS true/false if min support is present
+*/
+static bool uses_full_ppgtt(int fd, int min)
+{
+	struct drm_i915_getparam gp;
+	int val = 0;
+
+	memset(&gp, 0, sizeof(gp));
+	gp.param = 18; /* HAS_ALIASING_PPGTT */
+	gp.value = &val;
+
+	if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
+		return 0;
+
+	errno = 0;
+	return val >= min;
+}
+
+/* has_softpin_support
+ * Finds if softpin feature is supported
+ * @fd DRM fd
+*/
+static bool has_softpin_support(int fd)
+{
+	struct drm_i915_getparam gp;
+	int val = 0;
+
+	memset(&gp, 0, sizeof(gp));
+	gp.param = 37; /* I915_PARAM_HAS_EXEC_SOFTPIN */
+	gp.value = &val;
+
+	if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
+		return 0;
+
+	errno = 0;
+	return (val == 1);
+}
+
+/* has_trtt_support
+ * Finds if trtt hw is present
+ * @fd DRM fd
+*/
+static bool has_trtt_support(int fd)
+{
+	struct drm_i915_getparam gp;
+	int val = 0;
+
+	memset(&gp, 0, sizeof(gp));
+	gp.param = 38; /* I915_PARAM_HAS_TRTT */
+	gp.value = &val;
+
+	if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
+		return 0;
+
+	errno = 0;
+	return (val == 1);
+}
+
+/* mmap_bo
+ * helper for creating a CPU mmapping of the buffer
+ * @fd - drm fd
+ * @handle - handle of the buffer to mmap
+ * @size: size of the buffer
+*/
+static void* mmap_bo(int fd, uint32_t handle, uint64_t size)
+{
+	uint32_t *ptr = gem_mmap__cpu(fd, handle, 0, size, PROT_READ);
+	gem_set_domain(fd, handle, I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
+	return ptr;
+}
+
+/* setup_exec_obj
+ * populate exec object
+ * @exec - exec object
+ * @handle - handle to gem buffer
+ * @flags - any flags
+ * @offset - requested VMA
+*/
+static void setup_exec_obj(struct drm_i915_gem_exec_object2 *exec,
+			   uint32_t handle, uint32_t flags,
+			   uint64_t offset)
+{
+	memset(exec, 0, sizeof(struct drm_i915_gem_exec_object2));
+	exec->handle = handle;
+	exec->flags = flags;
+	exec->offset = offset;
+}
+
+/* gem_store_data_svm
+ * populate batch buffer with MI_STORE_DWORD_IMM command
+ * @fd: drm file descriptor
+ * @cmd_buf: batch buffer
+ * @dw_offset: write offset in batch buffer
+ * @vaddr: destination Virtual address
+ * @data: data to be store at destination
+ * @end: whether to end batch buffer or not
+*/
+static int gem_store_data_svm(int fd, uint32_t *cmd_buf, uint32_t dw_offset,
+			uint64_t vaddr, uint32_t data, bool end)
+{
+	cmd_buf[dw_offset++] = MI_STORE_DWORD_IMM;
+	cmd_buf[dw_offset++] = vaddr & 0xFFFFFFFC;
+	cmd_buf[dw_offset++] = (vaddr >> 32) & 0xFFFF; /* bits 32:47 */
+
+	cmd_buf[dw_offset++] = data;
+	if (end) {
+		cmd_buf[dw_offset++] = MI_BATCH_BUFFER_END;
+		cmd_buf[dw_offset++] = 0;
+	}
+
+	return dw_offset;
+}
+
+/* setup_execbuffer
+ * helper for buffer execution
+ * @execbuf - pointer to execbuffer
+ * @exec_object - pointer to exec object2 struct
+ * @ring - ring to be used
+ * @buffer_count - how manu buffers to submit
+ * @batch_length - length of batch buffer
+*/
+static void setup_execbuffer(struct drm_i915_gem_execbuffer2 *execbuf,
+			     struct drm_i915_gem_exec_object2 *exec_object,
+			     int ring, int buffer_count, int batch_length)
+{
+	execbuf->buffers_ptr = (unsigned long)exec_object;
+	execbuf->buffer_count = buffer_count;
+	execbuf->batch_start_offset = 0;
+	execbuf->batch_len = batch_length;
+	execbuf->cliprects_ptr = 0;
+	execbuf->num_cliprects = 0;
+	execbuf->DR1 = 0;
+	execbuf->DR4 = 0;
+	execbuf->flags = ring;
+	i915_execbuffer2_set_context_id(*execbuf, 0);
+	execbuf->rsvd2 = 0;
+}
+
+/* submit_and_sync
+ * Helper function for exec and sync functions
+ * @fd - drm fd
+ * @execbuf - pointer to execbuffer
+ * @batch_buf_handle - batch buffer handle
+*/
+static void submit_and_sync(int fd, struct drm_i915_gem_execbuffer2 *execbuf,
+			    uint32_t batch_buf_handle)
+{
+	gem_execbuf(fd, execbuf);
+	gem_sync(fd, batch_buf_handle);
+}
+
+struct local_i915_gem_context_trtt_param {
+	uint64_t l3_table_address;
+	uint32_t invd_tile_val;
+	uint32_t null_tile_val;
+};
+
+/* send_trtt_params
+ * Helper function to request KMD to enable TRTT
+ * @fd - drm fd
+ * @ctx_id - id of the context for which TRTT is to be enabled
+ * @l3_table_address - GFX address of the L3 table
+*/
+static void send_trtt_params(int fd, uint32_t ctx_id, uint64_t l3_table_address)
+{
+	struct local_i915_gem_context_param ctx_param;
+	struct local_i915_gem_context_trtt_param trtt_param;
+
+	memset(&ctx_param, 0, sizeof(ctx_param));
+
+	trtt_param.null_tile_val = 0xFFFFFFFF;
+	trtt_param.invd_tile_val = 0xFFFFFFFE;
+	trtt_param.l3_table_address = l3_table_address;
+
+	ctx_param.context = ctx_id;
+	ctx_param.size = sizeof(trtt_param);
+	ctx_param.param = 4; /* CONTEXT_PARAM_ENABLE_TRTT */
+	ctx_param.value = (uint64_t)&trtt_param;
+
+	gem_context_set_param(fd, &ctx_param);
+}
+
+#define TABLE_SIZE 0x1000
+#define TILE_SIZE 0x10000
+
+#define FIRST_TILE_ADDRESS 0xF00000000000
+#define LAST_TILE_ADDRESS  0xFFFFFFFF0000
+
+#define BO_ALLOC_AND_SETUP(fd, bo_size, bo_handle, bo_offset, idx) \
+	bo_handle = gem_create(fd, bo_size); \
+	bo_offset = current_ppgtt_offset; \
+	setup_exec_obj(&exec_object2[idx], bo_handle, EXEC_OBJECT_PINNED, bo_offset); \
+	current_ppgtt_offset += bo_size;
+
+/* basic test
+ * This test will add a series of MI_STORE_ commands, first to update the
+ * TR-TT table entries and then to update the data buffers using the TR-TT VA,
+ * exercising the programming the table programming done previously
+*/
+static void gem_basic_trtt_use(void)
+{
+	int fd;
+	int ring, len = 0;
+	uint32_t *ptr;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 exec_object2[8];
+	uint32_t batch_buffer[BO_SIZE];
+
+	uint32_t l3_tbl_handle, l2_tbl1_handle, l2_tbl2_handle;
+	uint32_t l1_tbl1_handle, l1_tbl2_handle, batch_buf_handle;
+	uint32_t buffer1_handle, buffer2_handle;
+
+	uint64_t l3_tbl_offset, l2_tbl1_offset, l2_tbl2_offset;
+	uint64_t l1_tbl1_offset, l1_tbl2_offset;
+	uint64_t buffer1_offset, buffer2_offset;
+
+	uint32_t data;
+	uint64_t address, current_ppgtt_offset = 0x10000;
+
+	fd = drm_open_driver(DRIVER_INTEL);
+	igt_require(uses_full_ppgtt(fd, FULL_48_BIT_PPGTT));
+	igt_require(has_softpin_support(fd));
+	igt_require(has_trtt_support(fd));
+
+	/* Allocate a L3 table BO */
+	BO_ALLOC_AND_SETUP(fd, TABLE_SIZE, l3_tbl_handle, l3_tbl_offset, 0);
+
+	/* Allocate two L2 table BOs */
+	BO_ALLOC_AND_SETUP(fd, TABLE_SIZE, l2_tbl1_handle, l2_tbl1_offset, 1);
+	BO_ALLOC_AND_SETUP(fd, TABLE_SIZE, l2_tbl2_handle, l2_tbl2_offset, 2);
+
+	/* Allocate two L1 table BOs */
+	BO_ALLOC_AND_SETUP(fd, TABLE_SIZE, l1_tbl1_handle, l1_tbl1_offset, 3);
+	BO_ALLOC_AND_SETUP(fd, TABLE_SIZE, l1_tbl2_handle, l1_tbl2_offset, 4);
+
+	/* Align the PPGTT offsets for the 2 data buffers to next 64 KB boundary */
+	current_ppgtt_offset = ALIGN(current_ppgtt_offset, TILE_SIZE);
+
+	/* Allocate two Data buffer BOs */
+	BO_ALLOC_AND_SETUP(fd, TILE_SIZE, buffer1_handle, buffer1_offset, 5);
+	BO_ALLOC_AND_SETUP(fd, TILE_SIZE, buffer2_handle, buffer2_offset, 6);
+
+	/* Finally allocate Batch buffer BO */
+	batch_buf_handle = gem_create(fd, BO_SIZE);
+	setup_exec_obj(&exec_object2[7], batch_buf_handle, 0, 0);
+
+	/* Add commands to update the two L3 table entries to point them to the L2 tables*/
+	address = l3_tbl_offset;
+	data = l2_tbl1_offset;
+	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
+
+	address = l3_tbl_offset + 511*sizeof(uint64_t);
+	data = l2_tbl2_offset;
+	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
+
+	/* Add commands to update an entry of 2 L2 tables to point them to the L1 tables*/
+	address = l2_tbl1_offset;
+	data = l1_tbl1_offset;
+	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
+
+	address = l2_tbl2_offset + 511*sizeof(uint64_t);
+	data = l1_tbl2_offset;
+	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
+
+	/* Add commands to update an entry of 2 L1 tables to point them to the data buffers*/
+	address = l1_tbl1_offset;
+	data = buffer1_offset >> 16;
+	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
+
+	address = l1_tbl2_offset + 1023*sizeof(uint32_t);
+	data = buffer2_offset >> 16;
+	len = gem_store_data_svm(fd, batch_buffer, len, address, data, false);
+
+	/* Add commands to update the 2 data buffers, using their TRTT VA */
+	data = 0x12345678;
+	len = gem_store_data_svm(fd, batch_buffer, len, FIRST_TILE_ADDRESS, data, false);
+	len = gem_store_data_svm(fd, batch_buffer, len, LAST_TILE_ADDRESS, data, true);
+
+	gem_write(fd, batch_buf_handle, 0, batch_buffer, len*4);
+
+	/* Request KMD to setup the TR-TT */
+	send_trtt_params(fd, 0, l3_tbl_offset);
+
+	ring = I915_EXEC_RENDER;
+	setup_execbuffer(&execbuf, exec_object2, ring, 8, len*4);
+
+	/* submit command buffer */
+	submit_and_sync(fd, &execbuf, batch_buf_handle);
+
+	/* read the 2 data buffers to check for the value written by the GPU */
+	ptr = mmap_bo(fd, buffer1_handle, TILE_SIZE);
+	igt_fail_on_f(ptr[0] != data,
+		"\nCPU read does not match GPU write,\
+		expected: 0x%x, got: 0x%x\n",
+		data, ptr[0]);
+
+	ptr = mmap_bo(fd, buffer2_handle, TILE_SIZE);
+	igt_fail_on_f(ptr[0] != data,
+		"\nCPU read does not match GPU write,\
+		expected: 0x%x, got: 0x%x\n",
+		data, ptr[0]);
+
+	gem_close(fd, l3_tbl_handle);
+	gem_close(fd, l2_tbl1_handle);
+	gem_close(fd, l2_tbl2_handle);
+	gem_close(fd, l1_tbl1_handle);
+	gem_close(fd, l1_tbl2_handle);
+	gem_close(fd, buffer1_handle);
+	gem_close(fd, buffer2_handle);
+	gem_close(fd, batch_buf_handle);
+	close(fd);
+}
+
+int main(int argc, char* argv[])
+{
+	igt_subtest_init(argc, argv);
+	igt_skip_on_simulation();
+
+	/* test needs 48 PPGTT & Soft Pin support */
+	igt_subtest("basic") {
+		gem_basic_trtt_use();
+	}
+
+	igt_exit();
+}
+
