Merge tag libdrm-2.4.75 into aosp/master

Below is a brief summary of patches pulled in:
0da99b8a (m/master, aosp/master) Move libdrm.so to vendor partition
d4b83443 (tag: libdrm-2.4.75) Bump version for 2.4.75 release
dae413e4 (tag: libdrm-2.4.74) Bump version for release
317bdff1 (tag: libdrm-2.4.73) Bump version for release
8cf43127 (tag: libdrm-2.4.72) Bump version for release
a44c9c31 (tag: libdrm-2.4.71) Bump version for release
20208455 (tag: android-o-preview-1, tag: android-n-mr2-preview-2, tag:
android-n-mr2-preview-1, aosp/sdk-release, aosp/o-preview) add a flag
control that private libdrm can be chosen

Bug: 35871718
Test: aosp_arm-eng compiles
Change-Id: I81985fd41d5c0d8a732705dc2a4bee8eb5d459bb
diff --git a/Android.mk b/Android.mk
index 5209059..102c9a3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -21,19 +21,28 @@
 # IN THE SOFTWARE.
 #
 
+
+ifneq ($(TARGET_USE_PRIVATE_LIBDRM),true)
+
 LIBDRM_COMMON_MK := $(call my-dir)/Android.common.mk
 
 LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
 
 # Import variables LIBDRM_{,H_,INCLUDE_H_,INCLUDE_VMWGFX_H_}FILES
 include $(LOCAL_PATH)/Makefile.sources
 
-#static library for the device (recovery)
+common_CFLAGS := \
+	-Wno-enum-conversion \
+	-Wno-pointer-arith \
+	-Wno-sign-compare \
+	-Wno-tautological-compare
+
+# Static library for the device (recovery)
 include $(CLEAR_VARS)
+
 LOCAL_MODULE := libdrm
 
-LOCAL_SRC_FILES := $(LIBDRM_FILES)
+LOCAL_SRC_FILES := $(filter-out %.h,$(LIBDRM_FILES))
 LOCAL_EXPORT_C_INCLUDE_DIRS := \
 	$(LOCAL_PATH) \
 	$(LOCAL_PATH)/include/drm
@@ -41,21 +50,31 @@
 LOCAL_C_INCLUDES := \
 	$(LOCAL_PATH)/include/drm
 
+LOCAL_CFLAGS := \
+	$(common_CFLAGS)
+
 include $(LIBDRM_COMMON_MK)
 include $(BUILD_STATIC_LIBRARY)
 
-# Shared library for the device
+# Dynamic library for the device
 include $(CLEAR_VARS)
-LOCAL_MODULE := libdrm
 
-LOCAL_SRC_FILES := $(LIBDRM_FILES)
+LOCAL_MODULE := libdrm
+LOCAL_VENDOR_MODULE := true
+
+LOCAL_SRC_FILES := $(filter-out %.h,$(LIBDRM_FILES))
 LOCAL_EXPORT_C_INCLUDE_DIRS := \
-        $(LOCAL_PATH)/include/drm
+	$(LOCAL_PATH) \
+	$(LOCAL_PATH)/include/drm
 
 LOCAL_C_INCLUDES := \
-        $(LOCAL_PATH)/include/drm
+	$(LOCAL_PATH)/include/drm
+
+LOCAL_CFLAGS := \
+	$(common_CFLAGS)
 
 include $(LIBDRM_COMMON_MK)
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 28a11db..aa1d752 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -2,3 +2,7 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/include/freedreno)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdrm_*intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libdrm_*intermediates)
+
+# libdrm is moved from /system to /vendor
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libdrm.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libdrm.so)
diff --git a/Makefile.am b/Makefile.am
index dfb8fcd..2bf644b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -96,6 +96,10 @@
 endif
 endif
 
+if HAVE_ROCKCHIP
+ROCKCHIP_SUBDIR = rockchip
+endif
+
 SUBDIRS = \
 	. \
 	$(LIBKMS_SUBDIR) \
@@ -110,7 +114,8 @@
 	$(VC4_SUBDIR) \
 	$(ETNAVIV_SUBDIR) \
 	tests \
-	$(MAN_SUBDIR)
+	$(MAN_SUBDIR) \
+	$(ROCKCHIP_SUBDIR)
 
 libdrm_la_LTLIBRARIES = libdrm.la
 libdrm_ladir = $(libdir)
diff --git a/configure.ac b/configure.ac
index 8e59332..1da9d86 100644
--- a/configure.ac
+++ b/configure.ac
@@ -127,6 +127,11 @@
 	      [Enable support for Tegra's experimental API (default: disabled)]),
 	      [TEGRA=$enableval], [TEGRA=no])
 
+AC_ARG_ENABLE(rockchip-experimental-api,
+	      AS_HELP_STRING([--enable-rockchip-experimental-api],
+	      [Enable support for rockchip's experimental API (default: disabled)]),
+	      [ROCKCHIP=$enableval], [ROCKCHIP=no])
+
 AC_ARG_ENABLE(vc4,
 	      AS_HELP_STRING([--disable-vc4],
 	      [Enable support for vc4's API (default: auto, enabled on arm)]),
@@ -416,6 +421,10 @@
 	AC_DEFINE(HAVE_TEGRA, 1, [Have Tegra support])
 fi
 
+AM_CONDITIONAL(HAVE_ROCKCHIP, [test "x$ROCKCHIP" = xyes])
+if test "x$ROCKCHIP" = xyes; then
+	AC_DEFINE(HAVE_ROCKCHIP, 1, [Have ROCKCHIP support])
+
 AM_CONDITIONAL(HAVE_VC4, [test "x$VC4" = xyes])
 if test "x$VC4" = xyes; then
 	AC_DEFINE(HAVE_VC4, 1, [Have VC4 support])
@@ -528,6 +537,8 @@
 	freedreno/libdrm_freedreno.pc
 	tegra/Makefile
 	tegra/libdrm_tegra.pc
+	rockchip/Makefile
+	rockchip/libdrm_rockchip.pc
 	vc4/Makefile
 	vc4/libdrm_vc4.pc
 	etnaviv/Makefile
@@ -544,6 +555,7 @@
 	tests/exynos/Makefile
 	tests/tegra/Makefile
 	tests/nouveau/Makefile
+	tests/planetest/Makefile
 	tests/etnaviv/Makefile
 	tests/util/Makefile
 	man/Makefile
@@ -563,6 +575,7 @@
 echo "  EXYNOS API     $EXYNOS"
 echo "  Freedreno API  $FREEDRENO (kgsl: $FREEDRENO_KGSL)"
 echo "  Tegra API      $TEGRA"
+echo "  Rockchip API   $ROCKCHIP"
 echo "  VC4 API        $VC4"
 echo "  Etnaviv API    $ETNAVIV"
 echo ""
diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h
index df0e350..6708e2b 100644
--- a/include/drm/drm_mode.h
+++ b/include/drm/drm_mode.h
@@ -107,6 +107,14 @@
 #define DRM_MODE_DIRTY_ON       1
 #define DRM_MODE_DIRTY_ANNOTATE 2
 
+/* rotation property bits */
+#define DRM_ROTATE_0	0
+#define DRM_ROTATE_90	1
+#define DRM_ROTATE_180	2
+#define DRM_ROTATE_270	3
+#define DRM_REFLECT_X	4
+#define DRM_REFLECT_Y	5
+
 struct drm_mode_modeinfo {
 	__u32 clock;
 	__u16 hdisplay;
@@ -629,6 +637,13 @@
 		DRM_MODE_ATOMIC_NONBLOCK |\
 		DRM_MODE_ATOMIC_ALLOW_MODESET)
 
+#define DRM_MODE_ATOMIC_FLAGS (\
+		DRM_MODE_PAGE_FLIP_EVENT |\
+		DRM_MODE_PAGE_FLIP_ASYNC |\
+		DRM_MODE_ATOMIC_TEST_ONLY |\
+		DRM_MODE_ATOMIC_NONBLOCK |\
+		DRM_MODE_ATOMIC_ALLOW_MODESET)
+
 struct drm_mode_atomic {
 	__u32 flags;
 	__u32 count_objs;
diff --git a/include/drm/nouveau_class.h b/include/drm/nouveau_class.h
new file mode 100644
index 0000000..8d63877
--- /dev/null
+++ b/include/drm/nouveau_class.h
@@ -0,0 +1,651 @@
+#ifndef __NVIF_CLASS_H__
+#define __NVIF_CLASS_H__
+
+/*******************************************************************************
+ * class identifiers
+ ******************************************************************************/
+
+/* the below match nvidia-assigned (either in hw, or sw) class numbers */
+#define NV_DEVICE                                                    0x00000080
+
+#define NV_DMA_FROM_MEMORY                                           0x00000002
+#define NV_DMA_TO_MEMORY                                             0x00000003
+#define NV_DMA_IN_MEMORY                                             0x0000003d
+
+#define FERMI_TWOD_A                                                 0x0000902d
+
+#define FERMI_MEMORY_TO_MEMORY_FORMAT_A                              0x0000903d
+
+#define KEPLER_INLINE_TO_MEMORY_A                                    0x0000a040
+#define KEPLER_INLINE_TO_MEMORY_B                                    0x0000a140
+
+#define NV04_DISP                                                    0x00000046
+
+#define NV03_CHANNEL_DMA                                             0x0000006b
+#define NV10_CHANNEL_DMA                                             0x0000006e
+#define NV17_CHANNEL_DMA                                             0x0000176e
+#define NV40_CHANNEL_DMA                                             0x0000406e
+#define NV50_CHANNEL_DMA                                             0x0000506e
+#define G82_CHANNEL_DMA                                              0x0000826e
+
+#define NV50_CHANNEL_GPFIFO                                          0x0000506f
+#define G82_CHANNEL_GPFIFO                                           0x0000826f
+#define FERMI_CHANNEL_GPFIFO                                         0x0000906f
+#define KEPLER_CHANNEL_GPFIFO_A                                      0x0000a06f
+#define MAXWELL_CHANNEL_GPFIFO_A                                     0x0000b06f
+
+#define NV50_DISP                                                    0x00005070
+#define G82_DISP                                                     0x00008270
+#define GT200_DISP                                                   0x00008370
+#define GT214_DISP                                                   0x00008570
+#define GT206_DISP                                                   0x00008870
+#define GF110_DISP                                                   0x00009070
+#define GK104_DISP                                                   0x00009170
+#define GK110_DISP                                                   0x00009270
+#define GM107_DISP                                                   0x00009470
+#define GM204_DISP                                                   0x00009570
+
+#define NV50_DISP_CURSOR                                             0x0000507a
+#define G82_DISP_CURSOR                                              0x0000827a
+#define GT214_DISP_CURSOR                                            0x0000857a
+#define GF110_DISP_CURSOR                                            0x0000907a
+#define GK104_DISP_CURSOR                                            0x0000917a
+
+#define NV50_DISP_OVERLAY                                            0x0000507b
+#define G82_DISP_OVERLAY                                             0x0000827b
+#define GT214_DISP_OVERLAY                                           0x0000857b
+#define GF110_DISP_OVERLAY                                           0x0000907b
+#define GK104_DISP_OVERLAY                                           0x0000917b
+
+#define NV50_DISP_BASE_CHANNEL_DMA                                   0x0000507c
+#define G82_DISP_BASE_CHANNEL_DMA                                    0x0000827c
+#define GT200_DISP_BASE_CHANNEL_DMA                                  0x0000837c
+#define GT214_DISP_BASE_CHANNEL_DMA                                  0x0000857c
+#define GF110_DISP_BASE_CHANNEL_DMA                                  0x0000907c
+#define GK104_DISP_BASE_CHANNEL_DMA                                  0x0000917c
+#define GK110_DISP_BASE_CHANNEL_DMA                                  0x0000927c
+
+#define NV50_DISP_CORE_CHANNEL_DMA                                   0x0000507d
+#define G82_DISP_CORE_CHANNEL_DMA                                    0x0000827d
+#define GT200_DISP_CORE_CHANNEL_DMA                                  0x0000837d
+#define GT214_DISP_CORE_CHANNEL_DMA                                  0x0000857d
+#define GT206_DISP_CORE_CHANNEL_DMA                                  0x0000887d
+#define GF110_DISP_CORE_CHANNEL_DMA                                  0x0000907d
+#define GK104_DISP_CORE_CHANNEL_DMA                                  0x0000917d
+#define GK110_DISP_CORE_CHANNEL_DMA                                  0x0000927d
+#define GM107_DISP_CORE_CHANNEL_DMA                                  0x0000947d
+#define GM204_DISP_CORE_CHANNEL_DMA                                  0x0000957d
+
+#define NV50_DISP_OVERLAY_CHANNEL_DMA                                0x0000507e
+#define G82_DISP_OVERLAY_CHANNEL_DMA                                 0x0000827e
+#define GT200_DISP_OVERLAY_CHANNEL_DMA                               0x0000837e
+#define GT214_DISP_OVERLAY_CHANNEL_DMA                               0x0000857e
+#define GF110_DISP_OVERLAY_CONTROL_DMA                               0x0000907e
+#define GK104_DISP_OVERLAY_CONTROL_DMA                               0x0000917e
+
+#define FERMI_A                                                      0x00009097
+#define FERMI_B                                                      0x00009197
+#define FERMI_C                                                      0x00009297
+
+#define KEPLER_A                                                     0x0000a097
+#define KEPLER_B                                                     0x0000a197
+#define KEPLER_C                                                     0x0000a297
+
+#define MAXWELL_A                                                    0x0000b097
+#define MAXWELL_B                                                    0x0000b197
+
+#define FERMI_COMPUTE_A                                              0x000090c0
+#define FERMI_COMPUTE_B                                              0x000091c0
+
+#define KEPLER_COMPUTE_A                                             0x0000a0c0
+#define KEPLER_COMPUTE_B                                             0x0000a1c0
+
+#define MAXWELL_COMPUTE_A                                            0x0000b0c0
+#define MAXWELL_COMPUTE_B                                            0x0000b1c0
+
+#define MAXWELL_DMA_COPY_A                                           0x0000b0b5
+
+/*******************************************************************************
+ * client
+ ******************************************************************************/
+
+#define NV_CLIENT_DEVLIST                                                  0x00
+
+struct nv_client_devlist_v0 {
+	__u8  version;
+	__u8  count;
+	__u8  pad02[6];
+	__u64 device[];
+};
+
+
+/*******************************************************************************
+ * device
+ ******************************************************************************/
+
+struct nv_device_v0 {
+	__u8  version;
+	__u8  pad01[7];
+	__u64 device;	/* device identifier, ~0 for client default */
+#define NV_DEVICE_V0_DISABLE_IDENTIFY                     0x0000000000000001ULL
+#define NV_DEVICE_V0_DISABLE_MMIO                         0x0000000000000002ULL
+#define NV_DEVICE_V0_DISABLE_VBIOS                        0x0000000000000004ULL
+#define NV_DEVICE_V0_DISABLE_CORE                         0x0000000000000008ULL
+#define NV_DEVICE_V0_DISABLE_DISP                         0x0000000000010000ULL
+#define NV_DEVICE_V0_DISABLE_FIFO                         0x0000000000020000ULL
+#define NV_DEVICE_V0_DISABLE_GR                           0x0000000100000000ULL
+#define NV_DEVICE_V0_DISABLE_MPEG                         0x0000000200000000ULL
+#define NV_DEVICE_V0_DISABLE_ME                           0x0000000400000000ULL
+#define NV_DEVICE_V0_DISABLE_VP                           0x0000000800000000ULL
+#define NV_DEVICE_V0_DISABLE_CIPHER                       0x0000001000000000ULL
+#define NV_DEVICE_V0_DISABLE_BSP                          0x0000002000000000ULL
+#define NV_DEVICE_V0_DISABLE_MSPPP                        0x0000004000000000ULL
+#define NV_DEVICE_V0_DISABLE_CE0                          0x0000008000000000ULL
+#define NV_DEVICE_V0_DISABLE_CE1                          0x0000010000000000ULL
+#define NV_DEVICE_V0_DISABLE_VIC                          0x0000020000000000ULL
+#define NV_DEVICE_V0_DISABLE_MSENC                        0x0000040000000000ULL
+#define NV_DEVICE_V0_DISABLE_CE2                          0x0000080000000000ULL
+#define NV_DEVICE_V0_DISABLE_MSVLD                        0x0000100000000000ULL
+#define NV_DEVICE_V0_DISABLE_SEC                          0x0000200000000000ULL
+#define NV_DEVICE_V0_DISABLE_MSPDEC                       0x0000400000000000ULL
+	__u64 disable;	/* disable particular subsystems */
+	__u64 debug0;	/* as above, but *internal* ids, and *NOT* ABI */
+};
+
+#define NV_DEVICE_V0_INFO                                                  0x00
+#define NV_DEVICE_V0_ZCULL_INFO                                            0x01
+
+struct nv_device_info_v0 {
+	__u8  version;
+#define NV_DEVICE_INFO_V0_IGP                                              0x00
+#define NV_DEVICE_INFO_V0_PCI                                              0x01
+#define NV_DEVICE_INFO_V0_AGP                                              0x02
+#define NV_DEVICE_INFO_V0_PCIE                                             0x03
+#define NV_DEVICE_INFO_V0_SOC                                              0x04
+	__u8  platform;
+	__u16 chipset;	/* from NV_PMC_BOOT_0 */
+	__u8  revision;	/* from NV_PMC_BOOT_0 */
+#define NV_DEVICE_INFO_V0_TNT                                              0x01
+#define NV_DEVICE_INFO_V0_CELSIUS                                          0x02
+#define NV_DEVICE_INFO_V0_KELVIN                                           0x03
+#define NV_DEVICE_INFO_V0_RANKINE                                          0x04
+#define NV_DEVICE_INFO_V0_CURIE                                            0x05
+#define NV_DEVICE_INFO_V0_TESLA                                            0x06
+#define NV_DEVICE_INFO_V0_FERMI                                            0x07
+#define NV_DEVICE_INFO_V0_KEPLER                                           0x08
+#define NV_DEVICE_INFO_V0_MAXWELL                                          0x09
+	__u8  family;
+	__u8  pad06[2];
+	__u64 ram_size;
+	__u64 ram_user;
+};
+
+struct nv_device_zcull_info_v0 {
+	__u8  version;
+	__u8  pad03[3];
+	__u32 image_size;
+	__u32 width_align_pixels;
+	__u32 height_align_pixels;
+	__u32 pixel_squares_by_aliquots;
+	__u32 aliquot_total;
+	__u32 region_byte_multiplier;
+	__u32 region_header_size;
+	__u32 subregion_header_size;
+	__u32 subregion_width_align_pixels;
+	__u32 subregion_height_align_pixels;
+	__u32 subregion_count;
+};
+
+/*******************************************************************************
+ * context dma
+ ******************************************************************************/
+
+struct nv_dma_v0 {
+	__u8  version;
+#define NV_DMA_V0_TARGET_VM                                                0x00
+#define NV_DMA_V0_TARGET_VRAM                                              0x01
+#define NV_DMA_V0_TARGET_PCI                                               0x02
+#define NV_DMA_V0_TARGET_PCI_US                                            0x03
+#define NV_DMA_V0_TARGET_AGP                                               0x04
+	__u8  target;
+#define NV_DMA_V0_ACCESS_VM                                                0x00
+#define NV_DMA_V0_ACCESS_RD                                                0x01
+#define NV_DMA_V0_ACCESS_WR                                                0x02
+#define NV_DMA_V0_ACCESS_RDWR                 (NV_DMA_V0_ACCESS_RD | NV_DMA_V0_ACCESS_WR)
+	__u8  access;
+	__u8  pad03[5];
+	__u64 start;
+	__u64 limit;
+	/* ... chipset-specific class data */
+};
+
+struct nv50_dma_v0 {
+	__u8  version;
+#define NV50_DMA_V0_PRIV_VM                                                0x00
+#define NV50_DMA_V0_PRIV_US                                                0x01
+#define NV50_DMA_V0_PRIV__S                                                0x02
+	__u8  priv;
+#define NV50_DMA_V0_PART_VM                                                0x00
+#define NV50_DMA_V0_PART_256                                               0x01
+#define NV50_DMA_V0_PART_1KB                                               0x02
+	__u8  part;
+#define NV50_DMA_V0_COMP_NONE                                              0x00
+#define NV50_DMA_V0_COMP_1                                                 0x01
+#define NV50_DMA_V0_COMP_2                                                 0x02
+#define NV50_DMA_V0_COMP_VM                                                0x03
+	__u8  comp;
+#define NV50_DMA_V0_KIND_PITCH                                             0x00
+#define NV50_DMA_V0_KIND_VM                                                0x7f
+	__u8  kind;
+	__u8  pad05[3];
+};
+
+struct gf100_dma_v0 {
+	__u8  version;
+#define GF100_DMA_V0_PRIV_VM                                               0x00
+#define GF100_DMA_V0_PRIV_US                                               0x01
+#define GF100_DMA_V0_PRIV__S                                               0x02
+	__u8  priv;
+#define GF100_DMA_V0_KIND_PITCH                                            0x00
+#define GF100_DMA_V0_KIND_VM                                               0xff
+	__u8  kind;
+	__u8  pad03[5];
+};
+
+struct gf110_dma_v0 {
+	__u8  version;
+#define GF110_DMA_V0_PAGE_LP                                               0x00
+#define GF110_DMA_V0_PAGE_SP                                               0x01
+	__u8  page;
+#define GF110_DMA_V0_KIND_PITCH                                            0x00
+#define GF110_DMA_V0_KIND_VM                                               0xff
+	__u8  kind;
+	__u8  pad03[5];
+};
+
+
+/*******************************************************************************
+ * perfmon
+ ******************************************************************************/
+
+struct nvif_perfctr_v0 {
+	__u8  version;
+	__u8  pad01[1];
+	__u16 logic_op;
+	__u8  pad04[4];
+	char  name[4][64];
+};
+
+#define NVIF_PERFCTR_V0_QUERY                                              0x00
+#define NVIF_PERFCTR_V0_SAMPLE                                             0x01
+#define NVIF_PERFCTR_V0_READ                                               0x02
+
+struct nvif_perfctr_query_v0 {
+	__u8  version;
+	__u8  pad01[3];
+	__u32 iter;
+	char  name[64];
+};
+
+struct nvif_perfctr_sample {
+};
+
+struct nvif_perfctr_read_v0 {
+	__u8  version;
+	__u8  pad01[7];
+	__u32 ctr;
+	__u32 clk;
+};
+
+
+/*******************************************************************************
+ * device control
+ ******************************************************************************/
+
+#define NVIF_CONTROL_PSTATE_INFO                                           0x00
+#define NVIF_CONTROL_PSTATE_ATTR                                           0x01
+#define NVIF_CONTROL_PSTATE_USER                                           0x02
+
+struct nvif_ustate {
+	__s8 min;
+	__s8 max;
+};
+
+struct nvif_control_pstate_info_v0 {
+	__u8  version;
+	__u8  count; /* out: number of power states */
+#define NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE                         (-1)
+#define NVIF_CONTROL_PSTATE_INFO_V0_USTATE_PERFMON                         (-2)
+	struct {
+		struct nvif_ustate dc; // pwrsrc == 0
+		struct nvif_ustate ac; // pwrsrc == 1
+	}     ustate; /* out: target pstate index */
+	__s8  pwrsrc; /* out: current power source */
+#define NVIF_CONTROL_PSTATE_INFO_V0_PSTATE_UNKNOWN                         (-1)
+#define NVIF_CONTROL_PSTATE_INFO_V0_PSTATE_PERFMON                         (-2)
+	__s8  pstate; /* out: current pstate index */
+	__u8  pad06[2];
+};
+
+struct nvif_control_pstate_attr_v0 {
+	__u8  version;
+#define NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT                          (-1)
+	__s8  state; /*  in: index of pstate to query
+		      * out: pstate identifier
+		      */
+	__u8  index; /*  in: index of attribute to query
+		      * out: index of next attribute, or 0 if no more
+		      */
+	__u8  pad03[5];
+	__u32 min;
+	__u32 max;
+	char  name[32];
+	char  unit[16];
+};
+
+struct nvif_control_pstate_user_v0 {
+	__u8  version;
+#define NVIF_CONTROL_PSTATE_USER_V0_STATE_UNKNOWN                          (-1)
+#define NVIF_CONTROL_PSTATE_USER_V0_STATE_PERFMON                          (-2)
+	struct nvif_ustate ustate; /*  in: pstate identifier */
+	__s8  pwrsrc; /*  in: target power source */
+	__u8  pad03[5];
+};
+
+
+/*******************************************************************************
+ * DMA FIFO channels
+ ******************************************************************************/
+
+struct nv03_channel_dma_v0 {
+	__u8  version;
+	__u8  chid;
+	__u8  pad02[2];
+	__u32 pushbuf;
+	__u64 offset;
+};
+
+#define G82_CHANNEL_DMA_V0_NTFY_UEVENT                                     0x00
+
+/*******************************************************************************
+ * GPFIFO channels
+ ******************************************************************************/
+
+struct nv50_channel_gpfifo_v0 {
+	__u8  version;
+	__u8  chid;
+	__u8  pad01[6];
+	__u32 pushbuf;
+	__u32 ilength;
+	__u64 ioffset;
+};
+
+struct kepler_channel_gpfifo_a_v0 {
+	__u8  version;
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_GR                               0x01
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_MSPDEC                           0x02
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_MSPPP                            0x04
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_MSVLD                            0x08
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_CE0                              0x10
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_CE1                              0x20
+#define KEPLER_CHANNEL_GPFIFO_A_V0_ENGINE_ENC                              0x40
+	__u8  engine;
+	__u16 chid;
+	__u8  pad04[4];
+	__u32 pushbuf;
+	__u32 ilength;
+	__u64 ioffset;
+};
+
+#define CHANNEL_GPFIFO_ERROR_NOTIFIER_EEVENT                               0x01
+
+/*******************************************************************************
+ * legacy display
+ ******************************************************************************/
+
+#define NV04_DISP_NTFY_VBLANK                                              0x00
+#define NV04_DISP_NTFY_CONN                                                0x01
+
+struct nv04_disp_mthd_v0 {
+	__u8  version;
+#define NV04_DISP_SCANOUTPOS                                               0x00
+	__u8  method;
+	__u8  head;
+	__u8  pad03[5];
+};
+
+struct nv04_disp_scanoutpos_v0 {
+	__u8  version;
+	__u8  pad01[7];
+	__s64 time[2];
+	__u16 vblanks;
+	__u16 vblanke;
+	__u16 vtotal;
+	__u16 vline;
+	__u16 hblanks;
+	__u16 hblanke;
+	__u16 htotal;
+	__u16 hline;
+};
+
+/*******************************************************************************
+ * display
+ ******************************************************************************/
+
+#define NV50_DISP_MTHD                                                     0x00
+
+struct nv50_disp_mthd_v0 {
+	__u8  version;
+#define NV50_DISP_SCANOUTPOS                                               0x00
+	__u8  method;
+	__u8  head;
+	__u8  pad03[5];
+};
+
+struct nv50_disp_mthd_v1 {
+	__u8  version;
+#define NV50_DISP_MTHD_V1_DAC_PWR                                          0x10
+#define NV50_DISP_MTHD_V1_DAC_LOAD                                         0x11
+#define NV50_DISP_MTHD_V1_SOR_PWR                                          0x20
+#define NV50_DISP_MTHD_V1_SOR_HDA_ELD                                      0x21
+#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR                                     0x22
+#define NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT                                  0x23
+#define NV50_DISP_MTHD_V1_SOR_DP_PWR                                       0x24
+#define NV50_DISP_MTHD_V1_PIOR_PWR                                         0x30
+	__u8  method;
+	__u16 hasht;
+	__u16 hashm;
+	__u8  pad06[2];
+};
+
+struct nv50_disp_dac_pwr_v0 {
+	__u8  version;
+	__u8  state;
+	__u8  data;
+	__u8  vsync;
+	__u8  hsync;
+	__u8  pad05[3];
+};
+
+struct nv50_disp_dac_load_v0 {
+	__u8  version;
+	__u8  load;
+	__u8  pad02[2];
+	__u32 data;
+};
+
+struct nv50_disp_sor_pwr_v0 {
+	__u8  version;
+	__u8  state;
+	__u8  pad02[6];
+};
+
+struct nv50_disp_sor_hda_eld_v0 {
+	__u8  version;
+	__u8  pad01[7];
+	__u8  data[];
+};
+
+struct nv50_disp_sor_hdmi_pwr_v0 {
+	__u8  version;
+	__u8  state;
+	__u8  max_ac_packet;
+	__u8  rekey;
+	__u8  pad04[4];
+};
+
+struct nv50_disp_sor_lvds_script_v0 {
+	__u8  version;
+	__u8  pad01[1];
+	__u16 script;
+	__u8  pad04[4];
+};
+
+struct nv50_disp_sor_dp_pwr_v0 {
+	__u8  version;
+	__u8  state;
+	__u8  pad02[6];
+};
+
+struct nv50_disp_pior_pwr_v0 {
+	__u8  version;
+	__u8  state;
+	__u8  type;
+	__u8  pad03[5];
+};
+
+/* core */
+struct nv50_disp_core_channel_dma_v0 {
+	__u8  version;
+	__u8  pad01[3];
+	__u32 pushbuf;
+};
+
+#define NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT                          0x00
+
+/* cursor immediate */
+struct nv50_disp_cursor_v0 {
+	__u8  version;
+	__u8  head;
+	__u8  pad02[6];
+};
+
+#define NV50_DISP_CURSOR_V0_NTFY_UEVENT                                    0x00
+
+/* base */
+struct nv50_disp_base_channel_dma_v0 {
+	__u8  version;
+	__u8  pad01[2];
+	__u8  head;
+	__u32 pushbuf;
+};
+
+#define NV50_DISP_BASE_CHANNEL_DMA_V0_NTFY_UEVENT                          0x00
+
+/* overlay */
+struct nv50_disp_overlay_channel_dma_v0 {
+	__u8  version;
+	__u8  pad01[2];
+	__u8  head;
+	__u32 pushbuf;
+};
+
+#define NV50_DISP_OVERLAY_CHANNEL_DMA_V0_NTFY_UEVENT                       0x00
+
+/* overlay immediate */
+struct nv50_disp_overlay_v0 {
+	__u8  version;
+	__u8  head;
+	__u8  pad02[6];
+};
+
+#define NV50_DISP_OVERLAY_V0_NTFY_UEVENT                                   0x00
+
+/*******************************************************************************
+ * fermi
+ ******************************************************************************/
+
+#define FERMI_A_ZBC_COLOR                                                  0x00
+#define FERMI_A_ZBC_DEPTH                                                  0x01
+#define FERMI_A_ZCULL_BIND                                                 0x02
+#define FERMI_A_ZBC_QUERY_COLOR                                            0x03
+#define FERMI_A_ZBC_QUERY_DEPTH                                            0x04
+#define FERMI_A_ZBC_QUERY_TABLE_SIZE                                       0x05
+
+struct fermi_a_zbc_color_v0 {
+	__u8  version;
+#define FERMI_A_ZBC_COLOR_V0_FMT_ZERO                                      0x01
+#define FERMI_A_ZBC_COLOR_V0_FMT_UNORM_ONE                                 0x02
+#define FERMI_A_ZBC_COLOR_V0_FMT_RF32_GF32_BF32_AF32                       0x04
+#define FERMI_A_ZBC_COLOR_V0_FMT_R16_G16_B16_A16                           0x08
+#define FERMI_A_ZBC_COLOR_V0_FMT_RN16_GN16_BN16_AN16                       0x0c
+#define FERMI_A_ZBC_COLOR_V0_FMT_RS16_GS16_BS16_AS16                       0x10
+#define FERMI_A_ZBC_COLOR_V0_FMT_RU16_GU16_BU16_AU16                       0x14
+#define FERMI_A_ZBC_COLOR_V0_FMT_RF16_GF16_BF16_AF16                       0x16
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8R8G8B8                                  0x18
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8RL8GL8BL8                               0x1c
+#define FERMI_A_ZBC_COLOR_V0_FMT_A2B10G10R10                               0x20
+#define FERMI_A_ZBC_COLOR_V0_FMT_AU2BU10GU10RU10                           0x24
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8B8G8R8                                  0x28
+#define FERMI_A_ZBC_COLOR_V0_FMT_A8BL8GL8RL8                               0x2c
+#define FERMI_A_ZBC_COLOR_V0_FMT_AN8BN8GN8RN8                              0x30
+#define FERMI_A_ZBC_COLOR_V0_FMT_AS8BS8GS8RS8                              0x34
+#define FERMI_A_ZBC_COLOR_V0_FMT_AU8BU8GU8RU8                              0x38
+#define FERMI_A_ZBC_COLOR_V0_FMT_A2R10G10B10                               0x3c
+#define FERMI_A_ZBC_COLOR_V0_FMT_BF10GF11RF11                              0x40
+	__u8  format;
+	__u8  index;
+	__u8  pad03[5];
+	__u32 ds[4];
+	__u32 l2[4];
+};
+
+struct fermi_a_zbc_query_v0 {
+	__u8 version;
+	__u8 pad03[3];
+	__u32 ds[4];
+	__u32 l2[4];
+	__u32 format;
+	__u32 index;
+	__u32 table_size;
+};
+
+struct fermi_a_zbc_depth_v0 {
+	__u8  version;
+#define FERMI_A_ZBC_DEPTH_V0_FMT_FP32                                      0x01
+	__u8  format;
+	__u8  index;
+	__u8  pad03[5];
+	__u32 ds;
+	__u32 l2;
+};
+
+struct fermi_a_zcull_bind_v0 {
+	__u8  version;
+	__u8  pad03[3];
+#define FERMI_A_ZCULL_BIND_MODE_GLOBAL                                     0x00
+#define FERMI_A_ZCULL_BIND_MODE_NO_CTXSW                                   0x01
+#define FERMI_A_ZCULL_BIND_MODE_SEPARATE_BUFFER                            0x02
+	__u32 mode;
+	__u64 gpu_va;
+};
+
+#define KEPLER_SET_CHANNEL_PRIORITY                                        0x00
+#define KEPLER_SET_CHANNEL_TIMEOUT                                         0x01
+
+struct kepler_set_channel_priority_v0 {
+	__u8  version;
+#define KEPLER_SET_CHANNEL_PRIORITY_LOW                                    0x00
+#define KEPLER_SET_CHANNEL_PRIORITY_MEDIUM                                 0x01
+#define KEPLER_SET_CHANNEL_PRIORITY_HIGH                                   0x02
+	__u8 priority;
+	__u8  pad03[6];
+};
+
+struct kepler_set_channel_timeout_v0 {
+	__u8  version;
+	__u8  pad03[3];
+	__u32 timeout_ms;
+};
+
+#endif
diff --git a/include/drm/nouveau_drm.h b/include/drm/nouveau_drm.h
index e418f9f..1372f53 100644
--- a/include/drm/nouveau_drm.h
+++ b/include/drm/nouveau_drm.h
@@ -27,6 +27,14 @@
 
 #define NOUVEAU_DRM_HEADER_PATCHLEVEL 16
 
+/* reserved object handles when using deprecated object APIs - these
+ * are here so that libdrm can allow interoperability with the new
+ * object APIs
+ */
+#define NOUVEAU_ABI16_CLIENT   0xffffffff
+#define NOUVEAU_ABI16_DEVICE   0xdddddddd
+#define NOUVEAU_ABI16_CHAN(n) (0xcccc0000 | (n))
+
 struct drm_nouveau_channel_alloc {
 	uint32_t     fb_ctxdma_handle;
 	uint32_t     tt_ctxdma_handle;
@@ -114,6 +122,12 @@
 	uint32_t tile_flags;
 };
 
+struct drm_nouveau_gem_set_tiling {
+	uint32_t handle;
+	uint32_t tile_mode;
+	uint32_t tile_flags;
+};
+
 struct drm_nouveau_gem_new {
 	struct drm_nouveau_gem_info info;
 	uint32_t channel_hint;
@@ -172,6 +186,21 @@
 	uint64_t gart_available;
 };
 
+#define NOUVEAU_GEM_PUSHBUF_2_FENCE_WAIT                             0x00000001
+#define NOUVEAU_GEM_PUSHBUF_2_FENCE_EMIT                             0x00000002
+struct drm_nouveau_gem_pushbuf_2 {
+	uint32_t channel;
+	uint32_t flags;
+	uint32_t nr_push;
+	uint32_t nr_buffers;
+	int32_t  fence; /* in/out, depends on flags */
+	uint32_t pad;
+	uint64_t push; /* in raw hw format */
+	uint64_t buffers; /* ptr to drm_nouveau_gem_pushbuf_bo */
+	uint64_t vram_available;
+	uint64_t gart_available;
+};
+
 #define NOUVEAU_GEM_CPU_PREP_NOWAIT                                  0x00000001
 #define NOUVEAU_GEM_CPU_PREP_NOBLOCK                                 0x00000002
 #define NOUVEAU_GEM_CPU_PREP_WRITE                                   0x00000004
@@ -184,6 +213,19 @@
 	uint32_t handle;
 };
 
+#define NOUVEAU_GEM_AS_SPARSE	0x00000001
+struct drm_nouveau_gem_as_alloc {
+	uint64_t pages;     /* in, page length */
+	uint32_t page_size; /* in, byte page size */
+	uint32_t flags; /* in, flags of address space */
+	uint64_t align; /* in, requested alignment in bytes */
+	uint64_t address; /* in/out, non-zero for fixed address allocation */
+};
+
+struct drm_nouveau_gem_as_free {
+	uint64_t address;   /* in, byte address */
+};
+
 enum nouveau_bus_type {
 	NV_AGP     = 0,
 	NV_PCI     = 1,
@@ -193,6 +235,34 @@
 struct drm_nouveau_sarea {
 };
 
+#define NOUVEAU_GEM_CHANNEL_FIFO_ERROR_IDLE_TIMEOUT	8
+#define NOUVEAU_GEM_CHANNEL_GR_ERROR_SW_NOTIFY		13
+#define NOUVEAU_GEM_CHANNEL_FIFO_ERROR_MMU_ERR_FLT	31
+#define NOUVEAU_GEM_CHANNEL_PBDMA_ERROR			32
+struct drm_nouveau_gem_set_error_notifier {
+	uint32_t channel;
+	uint32_t buffer;
+	uint32_t offset; /* in bytes, u32-aligned */
+};
+
+struct drm_nouveau_gem_map {
+	uint32_t handle;
+	uint32_t domain;
+	uint64_t offset;
+	uint64_t delta;
+	uint64_t length;
+	uint32_t tile_mode;
+	uint32_t tile_flags;
+};
+
+struct drm_nouveau_gem_unmap {
+	uint32_t handle;
+	uint32_t pad;
+	uint64_t offset;
+	uint64_t delta;
+	uint64_t length;
+};
+
 #define DRM_NOUVEAU_GETPARAM           0x00
 #define DRM_NOUVEAU_SETPARAM           0x01
 #define DRM_NOUVEAU_CHANNEL_ALLOC      0x02
@@ -207,4 +277,14 @@
 #define DRM_NOUVEAU_GEM_CPU_FINI       0x43
 #define DRM_NOUVEAU_GEM_INFO           0x44
 
+/* The ioctls below are marked as staging */
+#define DRM_NOUVEAU_GEM_SET_TILING     0x50
+#define DRM_NOUVEAU_GEM_PUSHBUF_2      0x51
+#define DRM_NOUVEAU_GEM_SET_INFO       0x52
+#define DRM_NOUVEAU_GEM_AS_ALLOC       0x53
+#define DRM_NOUVEAU_GEM_AS_FREE        0x54
+#define DRM_NOUVEAU_GEM_SET_ERROR_NOTIFIER 0x55
+#define DRM_NOUVEAU_GEM_MAP            0x56
+#define DRM_NOUVEAU_GEM_UNMAP          0x57
+
 #endif /* __NOUVEAU_DRM_H__ */
diff --git a/include/drm/nouveau_ioctl.h b/include/drm/nouveau_ioctl.h
new file mode 100644
index 0000000..4cd8e32
--- /dev/null
+++ b/include/drm/nouveau_ioctl.h
@@ -0,0 +1,128 @@
+#ifndef __NVIF_IOCTL_H__
+#define __NVIF_IOCTL_H__
+
+struct nvif_ioctl_v0 {
+	__u8  version;
+#define NVIF_IOCTL_V0_OWNER_NVIF                                           0x00
+#define NVIF_IOCTL_V0_OWNER_ANY                                            0xff
+	__u8  owner;
+#define NVIF_IOCTL_V0_NOP                                                  0x00
+#define NVIF_IOCTL_V0_SCLASS                                               0x01
+#define NVIF_IOCTL_V0_NEW                                                  0x02
+#define NVIF_IOCTL_V0_DEL                                                  0x03
+#define NVIF_IOCTL_V0_MTHD                                                 0x04
+#define NVIF_IOCTL_V0_RD                                                   0x05
+#define NVIF_IOCTL_V0_WR                                                   0x06
+#define NVIF_IOCTL_V0_MAP                                                  0x07
+#define NVIF_IOCTL_V0_UNMAP                                                0x08
+#define NVIF_IOCTL_V0_NTFY_NEW                                             0x09
+#define NVIF_IOCTL_V0_NTFY_DEL                                             0x0a
+#define NVIF_IOCTL_V0_NTFY_GET                                             0x0b
+#define NVIF_IOCTL_V0_NTFY_PUT                                             0x0c
+	__u8  type;
+	__u8  path_nr;
+#define NVIF_IOCTL_V0_ROUTE_NVIF                                           0x00
+#define NVIF_IOCTL_V0_ROUTE_HIDDEN                                         0xff
+	__u8  pad04[3];
+	__u8  route;
+	__u64 token;
+	__u32 path[8];		/* in reverse */
+	__u8  data[];		/* ioctl data (below) */
+};
+
+struct nvif_ioctl_nop {
+};
+
+struct nvif_ioctl_sclass_v0 {
+	/* nvif_ioctl ... */
+	__u8  version;
+	__u8  count;
+	__u8  pad02[6];
+	__u32 oclass[];
+};
+
+struct nvif_ioctl_new_v0 {
+	/* nvif_ioctl ... */
+	__u8  version;
+	__u8  pad01[6];
+	__u8  route;
+	__u64 token;
+	__u32 handle;
+/* these class numbers are made up by us, and not nvidia-assigned */
+#define NVIF_IOCTL_NEW_V0_PERFCTR                                    0x0000ffff
+#define NVIF_IOCTL_NEW_V0_CONTROL                                    0x0000fffe
+	__u32 oclass;
+	__u8  data[];		/* class data (class.h) */
+};
+
+struct nvif_ioctl_del {
+};
+
+struct nvif_ioctl_rd_v0 {
+	/* nvif_ioctl ... */
+	__u8  version;
+	__u8  size;
+	__u8  pad02[2];
+	__u32 data;
+	__u64 addr;
+};
+
+struct nvif_ioctl_wr_v0 {
+	/* nvif_ioctl ... */
+	__u8  version;
+	__u8  size;
+	__u8  pad02[2];
+	__u32 data;
+	__u64 addr;
+};
+
+struct nvif_ioctl_map_v0 {
+	/* nvif_ioctl ... */
+	__u8  version;
+	__u8  pad01[3];
+	__u32 length;
+	__u64 handle;
+};
+
+struct nvif_ioctl_unmap {
+};
+
+struct nvif_ioctl_ntfy_new_v0 {
+	/* nvif_ioctl ... */
+	__u8  version;
+	__u8  event;
+	__u8  index;
+	__u8  pad03[5];
+	__u8  data[];		/* event request data (event.h) */
+};
+
+struct nvif_ioctl_ntfy_del_v0 {
+	/* nvif_ioctl ... */
+	__u8  version;
+	__u8  index;
+	__u8  pad02[6];
+};
+
+struct nvif_ioctl_ntfy_get_v0 {
+	/* nvif_ioctl ... */
+	__u8  version;
+	__u8  index;
+	__u8  pad02[6];
+};
+
+struct nvif_ioctl_ntfy_put_v0 {
+	/* nvif_ioctl ... */
+	__u8  version;
+	__u8  index;
+	__u8  pad02[6];
+};
+
+struct nvif_ioctl_mthd_v0 {
+	/* nvif_ioctl ... */
+	__u8  version;
+	__u8  method;
+	__u8  pad02[6];
+	__u8  data[];		/* method data (class.h) */
+};
+
+#endif
diff --git a/include/drm/tegra_drm.h b/include/drm/tegra_drm.h
index 7c0fe0e..1be09c4 100644
--- a/include/drm/tegra_drm.h
+++ b/include/drm/tegra_drm.h
@@ -1,29 +1,23 @@
 /*
  * Copyright (c) 2012-2013, NVIDIA CORPORATION.  All rights reserved.
  *
- * 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:
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
  *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
  *
- * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #ifndef _UAPI_TEGRA_DRM_H_
 #define _UAPI_TEGRA_DRM_H_
 
-#include <drm.h>
+#include <drm/drm.h>
 
 #define DRM_TEGRA_GEM_CREATE_TILED     (1 << 0)
 #define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1)
@@ -121,6 +115,7 @@
 	__u32 num_waitchks;
 	__u32 waitchk_mask;
 	__u32 timeout;
+	__u32 pad;
 	__u64 syncpts;
 	__u64 cmdbufs;
 	__u64 relocs;
@@ -168,6 +163,34 @@
 	__u32 flags;
 };
 
+enum request_type {
+	DRM_TEGRA_REQ_TYPE_CLK_KHZ = 0,
+	DRM_TEGRA_REQ_TYPE_BW_KBPS,
+};
+
+struct drm_tegra_get_clk_rate {
+	/* class ID*/
+	__u32 id;
+	/* request type: KBps or KHz */
+	__u32 type;
+	/* numeric value for type */
+	__u64 data;
+};
+
+struct drm_tegra_set_clk_rate {
+	/* class ID*/
+	__u32 id;
+	/* request type: KBps or KHz */
+	__u32 type;
+	/* numeric value for type */
+	__u64 data;
+};
+
+struct drm_tegra_keepon {
+	/* channel context (from opening a channel) */
+	__u64 context;
+};
+
 #define DRM_TEGRA_GEM_CREATE		0x00
 #define DRM_TEGRA_GEM_MMAP		0x01
 #define DRM_TEGRA_SYNCPT_READ		0x02
@@ -182,6 +205,10 @@
 #define DRM_TEGRA_GEM_GET_TILING	0x0b
 #define DRM_TEGRA_GEM_SET_FLAGS		0x0c
 #define DRM_TEGRA_GEM_GET_FLAGS		0x0d
+#define DRM_TEGRA_GET_CLK_RATE		0x0e
+#define DRM_TEGRA_SET_CLK_RATE		0x0f
+#define DRM_TEGRA_START_KEEPON		0x10
+#define DRM_TEGRA_STOP_KEEPON		0x11
 
 #define DRM_IOCTL_TEGRA_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_CREATE, struct drm_tegra_gem_create)
 #define DRM_IOCTL_TEGRA_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_MMAP, struct drm_tegra_gem_mmap)
@@ -197,5 +224,9 @@
 #define DRM_IOCTL_TEGRA_GEM_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_TILING, struct drm_tegra_gem_get_tiling)
 #define DRM_IOCTL_TEGRA_GEM_SET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_SET_FLAGS, struct drm_tegra_gem_set_flags)
 #define DRM_IOCTL_TEGRA_GEM_GET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_FLAGS, struct drm_tegra_gem_get_flags)
+#define DRM_IOCTL_TEGRA_GET_CLK_RATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_CLK_RATE, struct drm_tegra_get_clk_rate)
+#define DRM_IOCTL_TEGRA_SET_CLK_RATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SET_CLK_RATE, struct drm_tegra_set_clk_rate)
+#define DRM_IOCTL_TEGRA_START_KEEPON DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_START_KEEPON, struct drm_tegra_keepon)
+#define DRM_IOCTL_TEGRA_STOP_KEEPON DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_STOP_KEEPON, struct drm_tegra_keepon)
 
 #endif
diff --git a/intel/Android.mk b/intel/Android.mk
index 5407ff3..2a0dc4c 100644
--- a/intel/Android.mk
+++ b/intel/Android.mk
@@ -29,11 +29,10 @@
 
 LOCAL_MODULE := libdrm_intel
 
-LOCAL_SRC_FILES := $(LIBDRM_INTEL_FILES)
+# Removed dependency to libpciaccess: not used on Android
+LOCAL_SHARED_LIBRARIES := libdrm
 
-LOCAL_SHARED_LIBRARIES := \
-	libdrm \
-	libpciaccess
+LOCAL_SRC_FILES := $(LIBDRM_INTEL_FILES)
 
 include $(LIBDRM_COMMON_MK)
 include $(BUILD_SHARED_LIBRARY)
diff --git a/intel/intel_bufmgr.c b/intel/intel_bufmgr.c
index a285340..5bad93f 100644
--- a/intel/intel_bufmgr.c
+++ b/intel/intel_bufmgr.c
@@ -36,7 +36,9 @@
 #include <errno.h>
 #include <drm.h>
 #include <i915_drm.h>
+#ifndef __ANDROID__
 #include <pciaccess.h>
+#endif
 #include "libdrm_macros.h"
 #include "intel_bufmgr.h"
 #include "intel_bufmgr_priv.h"
@@ -326,6 +328,7 @@
 	return -1;
 }
 
+#ifndef __ANDROID__
 static size_t
 drm_intel_probe_agp_aperture_size(int fd)
 {
@@ -351,6 +354,15 @@
 	pci_system_cleanup ();
 	return size;
 }
+#else
+static size_t
+drm_intel_probe_agp_aperture_size(int fd)
+{
+	/* Nothing seems to rely on this value on Android anyway... */
+	fprintf(stderr, "%s: Mappable aperture size hardcoded to 64MiB\n", __func__);
+	return 64 * 1024 * 1024;
+}
+#endif
 
 int
 drm_intel_get_aperture_sizes(int fd, size_t *mappable, size_t *total)
diff --git a/libdrm_macros.h b/libdrm_macros.h
index 639d090..b88fdce 100644
--- a/libdrm_macros.h
+++ b/libdrm_macros.h
@@ -46,8 +46,6 @@
 #if defined(ANDROID) && !defined(__LP64__)
 #include <errno.h> /* for EINVAL */
 
-extern void *__mmap2(void *, size_t, int, int, int, size_t);
-
 static inline void *drm_mmap(void *addr, size_t length, int prot, int flags,
                              int fd, loff_t offset)
 {
@@ -57,7 +55,7 @@
       return MAP_FAILED;
    }
 
-   return __mmap2(addr, length, prot, flags, fd, (size_t) (offset >> 12));
+   return mmap64(addr, length, prot, flags, fd, offset);
 }
 
 #  define drm_munmap(addr, length) \
diff --git a/libkms/Android.mk b/libkms/Android.mk
index 0be7205..9f81d8e 100644
--- a/libkms/Android.mk
+++ b/libkms/Android.mk
@@ -2,16 +2,20 @@
 
 intel_drivers := i915 i965 i915g ilo
 radeon_drivers := r300g r600g radeonsi
+rockchip_drivers := rockchip
 nouveau_drivers := nouveau
 virgl_drivers := virgl
 vmwgfx_drivers := vmwgfx
+tegra_drivers := tegra
 
 valid_drivers := \
 	$(intel_drivers) \
 	$(radeon_drivers) \
+	$(rockchip_drivers) \
 	$(nouveau_drivers) \
 	$(virgl_drivers) \
-	$(vmwgfx_drivers)
+	$(vmwgfx_drivers) \
+	$(tegra_drivers)
 
 # warn about invalid drivers
 invalid_drivers := $(filter-out $(valid_drivers), $(DRM_GPU_DRIVERS))
diff --git a/rockchip/Android.mk b/rockchip/Android.mk
new file mode 100644
index 0000000..05d421e
--- /dev/null
+++ b/rockchip/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdrm_rockchip
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+LOCAL_SRC_FILES := rockchip_drm.c
+
+LOCAL_CFLAGS := \
+	-DHAVE_LIBDRM_ATOMIC_PRIMITIVES=1
+
+LOCAL_SHARED_LIBRARIES := \
+	libdrm
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/rockchip/Makefile.am b/rockchip/Makefile.am
new file mode 100644
index 0000000..2ebb82f
--- /dev/null
+++ b/rockchip/Makefile.am
@@ -0,0 +1,20 @@
+AM_CFLAGS = \
+	$(WARN_CFLAGS) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/rockchip \
+	$(PTHREADSTUBS_CFLAGS) \
+	-I$(top_srcdir)/include/drm
+
+libdrm_rockchip_la_LTLIBRARIES = libdrm_rockchip.la
+libdrm_rockchip_ladir = $(libdir)
+libdrm_rockchip_la_LDFLAGS = -version-number 1:0:0 -no-undefined
+libdrm_rockchip_la_LIBADD = ../libdrm.la @PTHREADSTUBS_LIBS@
+
+libdrm_rockchip_la_SOURCES = \
+	rockchip_drm.c
+
+libdrm_rockchipincludedir = ${includedir}/libdrm
+libdrm_rockchipinclude_HEADERS = rockchip_drmif.h rockchip_drm.h
+
+pkgconfigdir = @pkgconfigdir@
+pkgconfig_DATA = libdrm_rockchip.pc
diff --git a/rockchip/libdrm_rockchip.pc.in b/rockchip/libdrm_rockchip.pc.in
new file mode 100644
index 0000000..13f22ac
--- /dev/null
+++ b/rockchip/libdrm_rockchip.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_rockchip
+Description: Userspace interface to rockchip kernel DRM services
+Version: 0.1
+Libs: -L${libdir} -ldrm_rockchip
+Cflags: -I${includedir} -I${includedir}/libdrm
+Requires.private: libdrm
diff --git a/rockchip/rockchip_drm.c b/rockchip/rockchip_drm.c
new file mode 100644
index 0000000..44a78be
--- /dev/null
+++ b/rockchip/rockchip_drm.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ *
+ * based on exynos_drm.c
+ *
+ * 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+#include <linux/stddef.h>
+
+#include <xf86drm.h>
+
+#include "rockchip_drm.h"
+#include "rockchip_drmif.h"
+
+/*
+ * Create rockchip drm device object.
+ *
+ * @fd: file descriptor to rockchip drm driver opened.
+ *
+ * if true, return the device object else NULL.
+ */
+struct rockchip_device *rockchip_device_create(int fd)
+{
+	struct rockchip_device *dev;
+
+	dev = calloc(1, sizeof(*dev));
+	if (!dev) {
+		fprintf(stderr, "failed to create device[%s].\n",
+				strerror(errno));
+		return NULL;
+	}
+
+	dev->fd = fd;
+
+	return dev;
+}
+
+/*
+ * Destroy rockchip drm device object
+ *
+ * @dev: rockchip drm device object.
+ */
+void rockchip_device_destroy(struct rockchip_device *dev)
+{
+	free(dev);
+}
+
+/*
+ * Create a rockchip buffer object to rockchip drm device.
+ *
+ * @dev: rockchip drm device object.
+ * @size: user-desired size.
+ * flags: user-desired memory type.
+ *	user can set one or more types among several types to memory
+ *	allocation and cache attribute types. and as default,
+ *	ROCKCHIP_BO_NONCONTIG and ROCKCHIP-BO_NONCACHABLE types would
+ *	be used.
+ *
+ * if true, return a rockchip buffer object else NULL.
+ */
+struct rockchip_bo *rockchip_bo_create(struct rockchip_device *dev,
+					size_t size, uint32_t flags)
+{
+	struct rockchip_bo *bo;
+	struct drm_rockchip_gem_create req = {
+		.size = size,
+		.flags = flags,
+	};
+
+	if (size == 0) {
+		fprintf(stderr, "invalid size.\n");
+		return NULL;
+	}
+
+	bo = calloc(1, sizeof(*bo));
+	if (!bo) {
+		fprintf(stderr, "failed to create bo[%s].\n",
+				strerror(errno));
+		goto fail;
+	}
+
+	bo->dev = dev;
+
+	if (drmIoctl(dev->fd, DRM_IOCTL_ROCKCHIP_GEM_CREATE, &req)){
+		fprintf(stderr, "failed to create gem object[%s].\n",
+				strerror(errno));
+		goto err_free_bo;
+	}
+
+	bo->handle = req.handle;
+	bo->size = size;
+	bo->flags = flags;
+
+	return bo;
+
+err_free_bo:
+	free(bo);
+fail:
+	return NULL;
+}
+
+struct rockchip_bo *rockchip_bo_from_handle(struct rockchip_device *dev,
+			uint32_t handle, uint32_t flags, uint32_t size)
+{
+	struct rockchip_bo *bo;
+
+	if (size == 0) {
+		fprintf(stderr, "invalid size.\n");
+		return NULL;
+	}
+
+	bo = calloc(1, sizeof(*bo));
+	if (!bo) {
+		fprintf(stderr, "failed to create bo[%s].\n",
+				strerror(errno));
+		return NULL;
+	}
+
+	bo->dev = dev;
+	bo->handle = handle;
+	bo->size = size;
+	bo->flags = flags;
+
+	return bo;
+}
+
+/*
+ * Destroy a rockchip buffer object.
+ *
+ * @bo: a rockchip buffer object to be destroyed.
+ */
+void rockchip_bo_destroy(struct rockchip_bo *bo)
+{
+	if (!bo)
+		return;
+
+	if (bo->vaddr)
+		munmap(bo->vaddr, bo->size);
+
+	if (bo->handle) {
+		struct drm_gem_close req = {
+			.handle = bo->handle,
+		};
+
+		drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+	}
+
+	free(bo);
+}
+
+
+/*
+ * Get a rockchip buffer object from a gem global object name.
+ *
+ * @dev: a rockchip device object.
+ * @name: a gem global object name exported by another process.
+ *
+ * this interface is used to get a rockchip buffer object from a gem
+ * global object name sent by another process for buffer sharing.
+ *
+ * if true, return a rockchip buffer object else NULL.
+ *
+ */
+struct rockchip_bo *rockchip_bo_from_name(struct rockchip_device *dev,
+						uint32_t name)
+{
+	struct rockchip_bo *bo;
+	struct drm_gem_open req = {
+		.name = name,
+	};
+
+	bo = calloc(1, sizeof(*bo));
+	if (!bo) {
+		fprintf(stderr, "failed to allocate bo[%s].\n",
+				strerror(errno));
+		return NULL;
+	}
+
+	if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
+		fprintf(stderr, "failed to open gem object[%s].\n",
+				strerror(errno));
+		goto err_free_bo;
+	}
+
+	bo->dev = dev;
+	bo->name = name;
+	bo->handle = req.handle;
+
+	return bo;
+
+err_free_bo:
+	free(bo);
+	return NULL;
+}
+
+/*
+ * Get a gem global object name from a gem object handle.
+ *
+ * @bo: a rockchip buffer object including gem handle.
+ * @name: a gem global object name to be got by kernel driver.
+ *
+ * this interface is used to get a gem global object name from a gem object
+ * handle to a buffer that wants to share it with another process.
+ *
+ * if true, return 0 else negative.
+ */
+int rockchip_bo_get_name(struct rockchip_bo *bo, uint32_t *name)
+{
+	if (!bo->name) {
+		struct drm_gem_flink req = {
+			.handle = bo->handle,
+		};
+		int ret;
+
+		ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
+		if (ret) {
+			fprintf(stderr, "failed to get gem global name[%s].\n",
+					strerror(errno));
+			return ret;
+		}
+
+		bo->name = req.name;
+	}
+
+	*name = bo->name;
+
+	return 0;
+}
+
+uint32_t rockchip_bo_handle(struct rockchip_bo *bo)
+{
+	return bo->handle;
+}
+
+/*
+ * Mmap a buffer to user space.
+ *
+ * @bo: a rockchip buffer object including a gem object handle to be mmapped
+ *	to user space.
+ *
+ * if true, user pointer mmaped else NULL.
+ */
+void *rockchip_bo_map(struct rockchip_bo *bo)
+{
+	if (!bo->vaddr) {
+		struct rockchip_device *dev = bo->dev;
+		struct drm_rockchip_gem_map_off req = {
+			.handle = bo->handle,
+		};
+		int ret;
+
+		ret = drmIoctl(dev->fd, DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET, &req);
+		if (ret) {
+			fprintf(stderr, "failed to ioctl gem map offset[%s].\n",
+				strerror(errno));
+			return NULL;
+		}
+
+		bo->vaddr = mmap(0, bo->size, PROT_READ | PROT_WRITE,
+			   MAP_SHARED, dev->fd, req.offset);
+		if (bo->vaddr == MAP_FAILED) {
+			fprintf(stderr, "failed to mmap buffer[%s].\n",
+				strerror(errno));
+			return NULL;
+		}
+	}
+
+	return bo->vaddr;
+}
diff --git a/rockchip/rockchip_drm.h b/rockchip/rockchip_drm.h
new file mode 100644
index 0000000..13977d5
--- /dev/null
+++ b/rockchip/rockchip_drm.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) Fuzhou Rockchip Electronics Co.Ltd
+ * Authors:
+ *       Mark Yao <yzq@rock-chips.com>
+ *
+ * based on exynos_drm.h
+ *
+ * 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 _ROCKCHIP_DRM_H_
+#define _ROCKCHIP_DRM_H_
+
+#include <stdint.h>
+#include "drm.h"
+
+/**
+ * User-desired buffer creation information structure.
+ *
+ * @size: user-desired memory allocation size.
+ *	- this size value would be page-aligned internally.
+ * @flags: user request for setting memory type or cache attributes.
+ * @handle: returned a handle to created gem object.
+ *	- this handle will be set by gem module of kernel side.
+ */
+struct drm_rockchip_gem_create {
+	uint64_t size;
+	uint32_t flags;
+	uint32_t handle;
+};
+
+/**
+ * A structure for getting buffer offset.
+ *
+ * @handle: a pointer to gem object created.
+ * @pad: just padding to be 64-bit aligned.
+ * @offset: relatived offset value of the memory region allocated.
+ *	- this value should be set by user.
+ */
+struct drm_rockchip_gem_map_off {
+	uint32_t handle;
+	uint32_t pad;
+	uint64_t offset;
+};
+
+#define DRM_ROCKCHIP_GEM_CREATE	0x00
+#define DRM_ROCKCHIP_GEM_MAP_OFFSET	0x01
+
+#define DRM_IOCTL_ROCKCHIP_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
+
+#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
+
+#endif
diff --git a/rockchip/rockchip_drmif.h b/rockchip/rockchip_drmif.h
new file mode 100644
index 0000000..5c549a0
--- /dev/null
+++ b/rockchip/rockchip_drmif.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) ROCKCHIP, Inc.
+ * Author:yzq<yzq@rock-chips.com>
+ *
+ * based on exynos_drmif.h
+ *
+ * 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 ROCKCHIP_DRMIF_H_
+#define ROCKCHIP_DRMIF_H_
+
+#include <xf86drm.h>
+#include <stdint.h>
+#include "rockchip_drm.h"
+
+struct rockchip_device {
+	int fd;
+};
+
+/*
+ * Rockchip Buffer Object structure.
+ *
+ * @dev: rockchip device object allocated.
+ * @handle: a gem handle to gem object created.
+ * @flags: indicate memory allocation and cache attribute types.
+ * @size: size to the buffer created.
+ * @vaddr: user space address to a gem buffer mmaped.
+ * @name: a gem global handle from flink request.
+ */
+struct rockchip_bo {
+	struct rockchip_device	*dev;
+	uint32_t		handle;
+	uint32_t		flags;
+	size_t			size;
+	void			*vaddr;
+	uint32_t		name;
+};
+
+/*
+ * device related functions:
+ */
+struct rockchip_device *rockchip_device_create(int fd);
+void rockchip_device_destroy(struct rockchip_device *dev);
+
+/*
+ * buffer-object related functions:
+ */
+struct rockchip_bo *rockchip_bo_create(struct rockchip_device *dev,
+			size_t size, uint32_t flags);
+int rockchip_bo_get_info(struct rockchip_device *dev, uint32_t handle,
+			size_t *size, uint32_t *flags);
+void rockchip_bo_destroy(struct rockchip_bo *bo);
+struct rockchip_bo *rockchip_bo_from_name(struct rockchip_device *dev,
+			uint32_t name);
+int rockchip_bo_get_name(struct rockchip_bo *bo, uint32_t *name);
+uint32_t rockchip_bo_handle(struct rockchip_bo *bo);
+struct rockchip_bo *rockchip_bo_from_handle(struct rockchip_device *dev,
+			uint32_t handle, uint32_t flags, uint32_t size);
+void *rockchip_bo_map(struct rockchip_bo *bo);
+#endif /* ROCKCHIP_DRMIF_H_ */
diff --git a/tegra/Android.mk b/tegra/Android.mk
new file mode 100644
index 0000000..d2a1a59
--- /dev/null
+++ b/tegra/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdrm_tegra
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+LOCAL_SRC_FILES := tegra.c
+
+LOCAL_CFLAGS := \
+	-DHAVE_LIBDRM_ATOMIC_PRIMITIVES=1
+
+LOCAL_SHARED_LIBRARIES := \
+	libdrm
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tegra/private.h b/tegra/private.h
index bb6c1a5..571caa5 100644
--- a/tegra/private.h
+++ b/tegra/private.h
@@ -41,9 +41,10 @@
 struct drm_tegra_bo {
 	struct drm_tegra *drm;
 	uint32_t handle;
-	uint32_t offset;
+	uint64_t offset;
 	uint32_t flags;
 	uint32_t size;
+	uint32_t name;
 	atomic_t ref;
 	void *map;
 };
diff --git a/tegra/tegra.c b/tegra/tegra.c
index f7dc89a..66f19e9 100644
--- a/tegra/tegra.c
+++ b/tegra/tegra.c
@@ -166,6 +166,61 @@
 	return 0;
 }
 
+int drm_tegra_bo_name_ref(struct drm_tegra *drm, uint32_t name, uint32_t size,
+			 struct drm_tegra_bo **bop)
+{
+	struct drm_tegra_bo *bo;
+	struct drm_gem_open open_args;
+	struct drm_gem_close close_args;
+	int ret;
+
+	memset(&open_args, 0, sizeof(open_args));
+
+	open_args.name = name;
+
+	ret = drmIoctl(drm->fd, DRM_IOCTL_GEM_OPEN, &open_args);
+	if (ret)
+		return ret;
+
+	ret = drm_tegra_bo_wrap(bop, drm, open_args.handle, 0, size);
+	if (ret)
+		goto err;
+
+	(*bop)->name = name;
+
+	return 0;
+
+err:
+	memset(&close_args, 0, sizeof(close_args));
+	close_args.handle = open_args.handle;
+	drmIoctl(drm->fd, DRM_IOCTL_GEM_CLOSE, &close_args);
+
+	return ret;
+}
+
+int drm_tegra_bo_name_get(struct drm_tegra_bo *bo, uint32_t *name)
+{
+	struct drm_gem_flink args;
+	int ret;
+
+	args.handle =  bo->handle;
+
+	*name = bo->name;
+	if (*name && *name != ~0U)
+		return 0;
+
+	ret = drmIoctl(bo->drm->fd, DRM_IOCTL_GEM_FLINK, &args);
+	if (ret) {
+		*name = 0;
+		return ret;
+	}
+
+	bo->name = args.name;
+	*name = bo->name;
+
+	return 0;
+}
+
 struct drm_tegra_bo *drm_tegra_bo_ref(struct drm_tegra_bo *bo)
 {
 	if (bo)
diff --git a/tegra/tegra.h b/tegra/tegra.h
index 31b0995..e10eab7 100644
--- a/tegra/tegra.h
+++ b/tegra/tegra.h
@@ -38,6 +38,11 @@
 		     uint32_t flags, uint32_t size);
 int drm_tegra_bo_wrap(struct drm_tegra_bo **bop, struct drm_tegra *drm,
 		      uint32_t handle, uint32_t flags, uint32_t size);
+
+int drm_tegra_bo_name_ref(struct drm_tegra *drm, uint32_t name, uint32_t size,
+			 struct drm_tegra_bo **bop);
+int drm_tegra_bo_name_get(struct drm_tegra_bo *bo, uint32_t *name);
+
 struct drm_tegra_bo *drm_tegra_bo_ref(struct drm_tegra_bo *bo);
 void drm_tegra_bo_unref(struct drm_tegra_bo *bo);
 int drm_tegra_bo_get_handle(struct drm_tegra_bo *bo, uint32_t *handle);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0355a92..f2bb4d4 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,7 +1,7 @@
 SUBDIRS = util kms modeprint proptest modetest vbltest
 
 if HAVE_LIBKMS
-SUBDIRS += kmstest
+SUBDIRS += kmstest planetest
 endif
 
 if HAVE_RADEON
diff --git a/tests/modetest/Android.mk b/tests/modetest/Android.mk
index c1a71fd..ab40b80 100644
--- a/tests/modetest/Android.mk
+++ b/tests/modetest/Android.mk
@@ -3,12 +3,14 @@
 include $(CLEAR_VARS)
 include $(LOCAL_PATH)/Makefile.sources
 
-LOCAL_SRC_FILES := $(MODETEST_FILES)
+LOCAL_SRC_FILES := $(filter-out %.h,$(MODETEST_FILES))
 
 LOCAL_MODULE := modetest
 
 LOCAL_SHARED_LIBRARIES := libdrm
 LOCAL_STATIC_LIBRARIES := libdrm_util
 
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
+
 include $(LIBDRM_COMMON_MK)
 include $(BUILD_EXECUTABLE)
diff --git a/tests/modetest/modetest.c b/tests/modetest/modetest.c
index c390d87..cd91119 100644
--- a/tests/modetest/modetest.c
+++ b/tests/modetest/modetest.c
@@ -524,6 +524,7 @@
 		return NULL;
 
 	drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+	drmSetClientCap(dev->fd, DRM_CLIENT_CAP_ATOMIC, 1);
 
 	res->res = drmModeGetResources(dev->fd);
 	if (!res->res) {
diff --git a/tests/planetest/Android.mk b/tests/planetest/Android.mk
new file mode 100644
index 0000000..3616e2b
--- /dev/null
+++ b/tests/planetest/Android.mk
@@ -0,0 +1,23 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_SRC_FILES := $(filter-out %.h,$(PLANETEST_COMMON_FILES) $(PLANETEST_FILES))
+
+LOCAL_MODULE := planetest
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_SRC_FILES := $(filter-out %.h,$(PLANETEST_COMMON_FILES) $(ATOMICTEST_FILES))
+
+LOCAL_MODULE := atomictest
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+include $(BUILD_EXECUTABLE)
diff --git a/tests/planetest/Makefile.am b/tests/planetest/Makefile.am
new file mode 100644
index 0000000..b82d05b
--- /dev/null
+++ b/tests/planetest/Makefile.am
@@ -0,0 +1,30 @@
+include Makefile.sources
+
+AM_CFLAGS = $(filter-out -Wpointer-arith, $(WARN_CFLAGS))
+
+AM_CFLAGS += \
+	-I$(top_srcdir)/include/drm \
+	-I$(top_srcdir)/libkms/ \
+	-I$(top_srcdir)
+
+PLANETEST_COMMON_LDADD = \
+	$(top_builddir)/libdrm.la \
+	$(top_builddir)/libkms/libkms.la \
+	-lpthread
+
+if HAVE_INSTALL_TESTS
+bin_PROGRAMS = \
+	atomictest \
+	planetest
+else
+noinst_PROGRAMS = \
+	atomictest \
+	planetest
+endif
+
+atomictest_CFLAGS=-DUSE_ATOMIC_API ${AM_CFLAGS}
+atomictest_SOURCES=${PLANETEST_COMMON_FILES} ${ATOMICTEST_FILES}
+planetest_SOURCES=${PLANETEST_COMMON_FILES} ${PLANETEST_FILES}
+
+atomictest_LDADD=${PLANETEST_COMMON_LDADD}
+planetest_LDADD=${PLANETEST_COMMON_LDADD}
diff --git a/tests/planetest/Makefile.sources b/tests/planetest/Makefile.sources
new file mode 100644
index 0000000..3cbeb2b
--- /dev/null
+++ b/tests/planetest/Makefile.sources
@@ -0,0 +1,13 @@
+PLANETEST_COMMON_FILES := \
+	bo.c \
+	bo.h \
+	dev.c \
+	dev.h \
+	modeset.c \
+	modeset.h
+
+ATOMICTEST_FILES := \
+	atomictest.c
+
+PLANETEST_FILES := \
+	planetest.c
diff --git a/tests/planetest/atomictest.c b/tests/planetest/atomictest.c
new file mode 100644
index 0000000..5fec911
--- /dev/null
+++ b/tests/planetest/atomictest.c
@@ -0,0 +1,151 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+
+#include <xf86drm.h>
+
+#include "dev.h"
+#include "bo.h"
+#include "modeset.h"
+
+static int terminate = 0;
+
+static void sigint_handler(int arg)
+{
+	terminate = 1;
+}
+
+static void
+page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
+		unsigned int tv_usec, void *user_data)
+{
+}
+
+static void incrementor(int *inc, int *val, int increment, int lower, int upper)
+{
+	if(*inc > 0)
+		*inc = *val + increment >= upper ? -1 : 1;
+	else
+		*inc = *val - increment <= lower ? 1 : -1;
+	*val += *inc * increment;
+}
+
+int main(int argc, char *argv[])
+{
+	int ret, i, j, num_test_planes;
+	int x_inc = 1, x = 0, y_inc = 1, y = 0;
+	uint32_t plane_w = 128, plane_h = 128;
+	struct sp_dev *dev;
+	struct sp_plane **plane = NULL;
+	struct sp_crtc *test_crtc;
+	fd_set fds;
+	drmModePropertySetPtr pset;
+	drmEventContext event_context = {
+		.version = DRM_EVENT_CONTEXT_VERSION,
+		.page_flip_handler = page_flip_handler,
+	};
+	int card = 0, crtc = 0;
+
+	signal(SIGINT, sigint_handler);
+
+	parse_arguments(argc, argv, &card, &crtc);
+
+	dev = create_sp_dev(card);
+	if (!dev) {
+		printf("Failed to create sp_dev\n");
+		return -1;
+	}
+
+	if (crtc >= dev->num_crtcs) {
+		printf("Invalid crtc %d (num=%d)\n", crtc, dev->num_crtcs);
+		return -1;
+	}
+
+	ret = initialize_screens(dev);
+	if (ret) {
+		printf("Failed to initialize screens\n");
+		goto out;
+	}
+	test_crtc = &dev->crtcs[crtc];
+
+	plane = calloc(dev->num_planes, sizeof(*plane));
+	if (!plane) {
+		printf("Failed to allocate plane array\n");
+		goto out;
+	}
+
+	/* Create our planes */
+	num_test_planes = test_crtc->num_planes;
+	for (i = 0; i < num_test_planes; i++) {
+		plane[i] = get_sp_plane(dev, test_crtc);
+		if (!plane[i]) {
+			printf("no unused planes available\n");
+			goto out;
+		}
+
+		plane[i]->bo = create_sp_bo(dev, plane_w, plane_h, 16, plane[i]->format, 0);
+		if (!plane[i]->bo) {
+			printf("failed to create plane bo\n");
+			goto out;
+		}
+
+		fill_bo(plane[i]->bo, 0xFF, 0xFF, 0xFF, 0xFF);
+	}
+
+	pset = drmModePropertySetAlloc();
+	if (!pset) {
+		printf("Failed to allocate the property set\n");
+		goto out;
+	}
+
+	while (!terminate) {
+		FD_ZERO(&fds);
+		FD_SET(dev->fd, &fds);
+
+		incrementor(&x_inc, &x, 5, 0,
+			test_crtc->crtc->mode.hdisplay - plane_w);
+		incrementor(&y_inc, &y, 5, 0, test_crtc->crtc->mode.vdisplay -
+						plane_h * num_test_planes);
+
+		for (j = 0; j < num_test_planes; j++) {
+			ret = set_sp_plane_pset(dev, plane[j], pset, test_crtc,
+					x, y + j * plane_h);
+			if (ret) {
+				printf("failed to move plane %d\n", ret);
+				goto out;
+			}
+		}
+
+		ret = drmModePropertySetCommit(dev->fd,
+				DRM_MODE_PAGE_FLIP_EVENT, NULL, pset);
+		if (ret) {
+			printf("failed to commit properties ret=%d\n", ret);
+			goto out;
+		}
+
+		do {
+			ret = select(dev->fd + 1, &fds, NULL, NULL, NULL);
+		} while (ret == -1 && errno == EINTR);
+
+		if (FD_ISSET(dev->fd, &fds))
+			drmHandleEvent(dev->fd, &event_context);
+	}
+
+	drmModePropertySetFree(pset);
+
+	for (i = 0; i < num_test_planes; i++)
+		put_sp_plane(plane[i]);
+
+out:
+	destroy_sp_dev(dev);
+	free(plane);
+	return ret;
+}
diff --git a/tests/planetest/bo.c b/tests/planetest/bo.c
new file mode 100644
index 0000000..d4b82c6
--- /dev/null
+++ b/tests/planetest/bo.c
@@ -0,0 +1,234 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+
+#include "bo.h"
+#include "dev.h"
+
+#define MAKE_YUV_601_Y(r, g, b) \
+	((( 66 * (r) + 129 * (g) +  25 * (b) + 128) >> 8) + 16)
+#define MAKE_YUV_601_U(r, g, b) \
+	(((-38 * (r) -  74 * (g) + 112 * (b) + 128) >> 8) + 128)
+#define MAKE_YUV_601_V(r, g, b) \
+	(((112 * (r) -  94 * (g) -  18 * (b) + 128) >> 8) + 128)
+
+static void draw_rect_yuv(struct sp_bo *bo, uint32_t x, uint32_t y, uint32_t width,
+		uint32_t height, uint8_t a, uint8_t r, uint8_t g, uint8_t b)
+{
+	uint32_t i, j, xmax = x + width, ymax = y + height;
+
+	if (xmax > bo->width)
+		xmax = bo->width;
+	if (ymax > bo->height)
+		ymax = bo->height;
+
+	for (i = y; i < ymax; i++) {
+		uint8_t *luma = bo->map_addr + i * bo->pitch;
+
+		for (j = x; j < xmax; j++)
+			luma[j] = MAKE_YUV_601_Y(r, g, b);
+	}
+
+	for (i = y; i < ymax / 2; i++) {
+		uint8_t *chroma = bo->map_addr + (i + height) * bo->pitch;
+
+		for (j = x; j < xmax / 2; j++) {
+			chroma[j*2] = MAKE_YUV_601_U(r, g, b);
+			chroma[j*2 + 1] = MAKE_YUV_601_V(r, g, b);
+		}
+	}
+}
+
+void fill_bo(struct sp_bo *bo, uint8_t a, uint8_t r, uint8_t g, uint8_t b)
+{
+	if (bo->format == DRM_FORMAT_NV12)
+		draw_rect_yuv(bo, 0, 0, bo->width, bo->height, a, r, g, b);
+	else
+		draw_rect(bo, 0, 0, bo->width, bo->height, a, r, g, b);
+}
+
+void draw_rect(struct sp_bo *bo, uint32_t x, uint32_t y, uint32_t width,
+		uint32_t height, uint8_t a, uint8_t r, uint8_t g, uint8_t b)
+{
+	uint32_t i, j, xmax = x + width, ymax = y + height;
+
+	if (xmax > bo->width)
+		xmax = bo->width;
+	if (ymax > bo->height)
+		ymax = bo->height;
+
+	for (i = y; i < ymax; i++) {
+		uint8_t *row = bo->map_addr + i * bo->pitch;
+
+		for (j = x; j < xmax; j++) {
+			uint8_t *pixel = row + j * 4;
+
+			if (bo->format == DRM_FORMAT_ARGB8888 ||
+			    bo->format == DRM_FORMAT_XRGB8888)
+			{
+				pixel[0] = b;
+				pixel[1] = g;
+				pixel[2] = r;
+				pixel[3] = a;
+			} else if (bo->format == DRM_FORMAT_RGBA8888) {
+				pixel[0] = r;
+				pixel[1] = g;
+				pixel[2] = b;
+				pixel[3] = a;
+			}
+		}
+	}
+}
+
+static int add_fb_sp_bo(struct sp_bo *bo, uint32_t format)
+{
+	int ret;
+	uint32_t handles[4], pitches[4], offsets[4];
+
+	handles[0] = bo->handle;
+	pitches[0] = bo->pitch;
+	offsets[0] = 0;
+	if (bo->format == DRM_FORMAT_NV12) {
+		handles[1] = bo->handle;
+		pitches[1] = pitches[0];
+		offsets[1] = pitches[0] * bo->height;
+	}
+
+	ret = drmModeAddFB2(bo->dev->fd, bo->width, bo->height,
+			format, handles, pitches, offsets,
+			&bo->fb_id, bo->flags);
+	if (ret) {
+		printf("failed to create fb ret=%d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int map_sp_bo(struct sp_bo *bo)
+{
+	int ret;
+	struct drm_mode_map_dumb md;
+
+	if (bo->map_addr)
+		return 0;
+
+	md.handle = bo->handle;
+	ret = drmIoctl(bo->dev->fd, DRM_IOCTL_MODE_MAP_DUMB, &md);
+	if (ret) {
+		printf("failed to map sp_bo ret=%d\n", ret);
+		return ret;
+	}
+
+	bo->map_addr = mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+				bo->dev->fd, md.offset);
+	if (bo->map_addr == MAP_FAILED) {
+		printf("failed to map bo ret=%d\n", -errno);
+		return -errno;
+	}
+	return 0;
+}
+
+static int format_to_bpp(uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV12:
+		return 8;
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_RGBA8888:
+	default:
+		return 32;
+	}
+}
+
+struct sp_bo *create_sp_bo(struct sp_dev *dev, uint32_t width, uint32_t height,
+		uint32_t depth, uint32_t format, uint32_t flags)
+{
+	int ret;
+	struct drm_mode_create_dumb cd;
+	struct sp_bo *bo;
+
+	bo = calloc(1, sizeof(*bo));
+	if (!bo)
+		return NULL;
+
+	if (format == DRM_FORMAT_NV12)
+		cd.height = height * 3 / 2;
+	else
+		cd.height = height;
+
+	cd.width = width;
+	cd.bpp = format_to_bpp(format);
+	cd.flags = flags;
+
+	ret = drmIoctl(dev->fd, DRM_IOCTL_MODE_CREATE_DUMB, &cd);
+	if (ret) {
+		printf("failed to create sp_bo %d\n", ret);
+		goto err;
+	}
+
+	bo->dev = dev;
+	bo->width = width;
+	bo->height = height;
+	bo->depth = depth;
+	bo->bpp = format_to_bpp(format);
+	bo->format = format;
+	bo->flags = flags;
+
+	bo->handle = cd.handle;
+	bo->pitch = cd.pitch;
+	bo->size = cd.size;
+
+	ret = add_fb_sp_bo(bo, format);
+	if (ret) {
+		printf("failed to add fb ret=%d\n", ret);
+		goto err;
+	}
+
+	ret = map_sp_bo(bo);
+	if (ret) {
+		printf("failed to map bo ret=%d\n", ret);
+		goto err;
+	}
+
+	return bo;
+
+err:
+	free_sp_bo(bo);
+	return NULL;
+}
+
+void free_sp_bo(struct sp_bo *bo)
+{
+	int ret;
+	struct drm_mode_destroy_dumb dd;
+
+	if (!bo)
+		return;
+
+	if (bo->map_addr)
+		munmap(bo->map_addr, bo->size);
+
+	if (bo->fb_id) {
+		ret = drmModeRmFB(bo->dev->fd, bo->fb_id);
+		if (ret)
+			printf("Failed to rmfb ret=%d!\n", ret);
+	}
+
+	if (bo->handle) {
+		dd.handle = bo->handle;
+		ret = drmIoctl(bo->dev->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dd);
+		if (ret)
+			printf("Failed to destroy buffer ret=%d\n", ret);
+	}
+
+	free(bo);
+}
diff --git a/tests/planetest/bo.h b/tests/planetest/bo.h
new file mode 100644
index 0000000..7471e12
--- /dev/null
+++ b/tests/planetest/bo.h
@@ -0,0 +1,34 @@
+#ifndef __BO_H_INCLUDED__
+#define __BO_H_INCLUDED__
+
+#include <stdint.h>
+
+struct sp_dev;
+
+struct sp_bo {
+	struct sp_dev *dev;
+
+	uint32_t width;
+	uint32_t height;
+	uint32_t depth;
+	uint32_t bpp;
+	uint32_t format;
+	uint32_t flags;
+
+	uint32_t fb_id;
+	uint32_t handle;
+	void *map_addr;
+	uint32_t pitch;
+	uint32_t size;
+};
+
+struct sp_bo *create_sp_bo(struct sp_dev *dev, uint32_t width, uint32_t height,
+		uint32_t depth, uint32_t format, uint32_t flags);
+
+void fill_bo(struct sp_bo *bo, uint8_t a, uint8_t r, uint8_t g, uint8_t b);
+void draw_rect(struct sp_bo *bo, uint32_t x, uint32_t y, uint32_t width,
+		uint32_t height, uint8_t a, uint8_t r, uint8_t g, uint8_t b);
+
+void free_sp_bo(struct sp_bo *bo);
+
+#endif /* __BO_H_INCLUDED__ */
diff --git a/tests/planetest/dev.c b/tests/planetest/dev.c
new file mode 100644
index 0000000..bd0968c
--- /dev/null
+++ b/tests/planetest/dev.c
@@ -0,0 +1,367 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <drm.h>
+#include <drm_fourcc.h>
+#include <errno.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "bo.h"
+#include "dev.h"
+#include "modeset.h"
+
+static void show_usage(char *name)
+{
+	printf("Usage: %s [OPTION]\n", name);
+	printf("   -c, --card      Index of dri card (ie: /dev/dri/cardN)\n");
+	printf("   -r, --crtc      Index of crtc to use for test\n");
+	printf("\n\n");
+}
+
+void parse_arguments(int argc, char *argv[], int *card, int *crtc)
+{
+	static struct option options[] = {
+		{ "card", required_argument, NULL, 'c' },
+		{ "crtc", required_argument, NULL, 'r' },
+		{ "help", no_argument, NULL, 'h' },
+	};
+	int option_index = 0;
+	int c;
+
+	*card = -1;
+	*crtc = -1;
+	do {
+		c = getopt_long(argc, argv, "c:r:h", options, &option_index);
+		switch (c) {
+		case 0:
+		case 'h':
+			show_usage(argv[0]);
+			exit(0);
+		case -1:
+			break;
+		case 'c':
+			if (optarg[0] < '0' || optarg[0] > '9') {
+				printf("Invalid card value '%s'!\n", optarg);
+				show_usage(argv[0]);
+				exit(-1);
+			}
+			*card = optarg[0] - '0';
+			break;
+		case 'r':
+			if (optarg[0] < '0' || optarg[0] > '9') {
+				printf("Invalid crtc value '%s'!\n", optarg);
+				show_usage(argv[0]);
+				exit(-1);
+			}
+			*crtc = optarg[0] - '0';
+			break;
+		}
+	} while (c != -1);
+
+	if (*card < 0 || *crtc < 0) {
+		show_usage(argv[0]);
+		exit(-1);
+	}
+}
+
+static uint32_t get_prop_id(struct sp_dev *dev,
+			drmModeObjectPropertiesPtr props, const char *name)
+{
+	drmModePropertyPtr p;
+	uint32_t i, prop_id = 0; /* Property ID should always be > 0 */
+
+	for (i = 0; !prop_id && i < props->count_props; i++) {
+		p = drmModeGetProperty(dev->fd, props->props[i]);
+		if (!strcmp(p->name, name))
+			prop_id = p->prop_id;
+		drmModeFreeProperty(p);
+	}
+	if (!prop_id)
+		printf("Could not find %s property\n", name);
+	return prop_id;
+}
+
+static int get_supported_format(struct sp_plane *plane, uint32_t *format)
+{
+	uint32_t i;
+
+	for (i = 0; i < plane->plane->count_formats; i++) {
+		if (plane->plane->formats[i] == DRM_FORMAT_XRGB8888 ||
+		    plane->plane->formats[i] == DRM_FORMAT_ARGB8888 ||
+		    plane->plane->formats[i] == DRM_FORMAT_RGBA8888 ||
+		    plane->plane->formats[i] == DRM_FORMAT_NV12) {
+			*format = plane->plane->formats[i];
+			return 0;
+		}
+	}
+	printf("No suitable formats found!\n");
+	return -ENOENT;
+}
+
+struct sp_dev *create_sp_dev(int card)
+{
+	struct sp_dev *dev;
+	int ret, fd, i, j;
+	drmModeRes *r = NULL;
+	drmModePlaneRes *pr = NULL;
+	char card_path[256];
+
+	snprintf(card_path, sizeof(card_path), "/dev/dri/card%d", card);
+
+	fd = open(card_path, O_RDWR);
+	if (fd < 0) {
+		printf("failed to open card0\n");
+		return NULL;
+	}
+
+	dev = calloc(1, sizeof(*dev));
+	if (!dev) {
+		printf("failed to allocate dev\n");
+		return NULL;
+	}
+
+	dev->fd = fd;
+
+	ret = drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+	if (ret) {
+		printf("failed to set client cap\n");
+		goto err;
+	}
+
+	ret = drmSetClientCap(dev->fd, DRM_CLIENT_CAP_ATOMIC, 1);
+	if (ret) {
+		printf("Failed to set atomic cap %d", ret);
+		goto err;
+	}
+
+	r = drmModeGetResources(dev->fd);
+	if (!r) {
+		printf("failed to get r\n");
+		goto err;
+	}
+
+	dev->num_connectors = r->count_connectors;
+	dev->connectors = calloc(dev->num_connectors,
+				sizeof(struct sp_connector));
+	if (!dev->connectors) {
+		printf("failed to allocate connectors\n");
+		goto err;
+	}
+	for (i = 0; i < dev->num_connectors; i++) {
+		drmModeObjectPropertiesPtr props;
+		dev->connectors[i].conn = drmModeGetConnector(dev->fd,
+					r->connectors[i]);
+		if (!dev->connectors[i].conn) {
+			printf("failed to get connector %d\n", i);
+			goto err;
+		}
+
+		props = drmModeObjectGetProperties(dev->fd, r->connectors[i],
+				DRM_MODE_OBJECT_CONNECTOR);
+		if (!props) {
+			printf("failed to get connector properties\n");
+			goto err;
+		}
+
+		dev->connectors[i].crtc_id_pid = get_prop_id(dev, props,
+								"CRTC_ID");
+		drmModeFreeObjectProperties(props);
+		if (!dev->connectors[i].crtc_id_pid)
+			goto err;
+	}
+
+	dev->num_encoders = r->count_encoders;
+	dev->encoders = calloc(dev->num_encoders, sizeof(*dev->encoders));
+	if (!dev->encoders) {
+		printf("failed to allocate encoders\n");
+		goto err;
+	}
+	for (i = 0; i < dev->num_encoders; i++) {
+		dev->encoders[i] = drmModeGetEncoder(dev->fd, r->encoders[i]);
+		if (!dev->encoders[i]) {
+			printf("failed to get encoder %d\n", i);
+			goto err;
+		}
+	}
+
+	dev->num_crtcs = r->count_crtcs;
+	dev->crtcs = calloc(dev->num_crtcs, sizeof(struct sp_crtc));
+	if (!dev->crtcs) {
+		printf("failed to allocate crtcs\n");
+		goto err;
+	}
+	for (i = 0; i < dev->num_crtcs; i++) {
+		drmModeObjectPropertiesPtr props;
+
+		dev->crtcs[i].crtc = drmModeGetCrtc(dev->fd, r->crtcs[i]);
+		if (!dev->crtcs[i].crtc) {
+			printf("failed to get crtc %d\n", i);
+			goto err;
+		}
+		dev->crtcs[i].pipe = i;
+		dev->crtcs[i].num_planes = 0;
+
+		props = drmModeObjectGetProperties(dev->fd, r->crtcs[i],
+				DRM_MODE_OBJECT_CRTC);
+		if (!props) {
+			printf("failed to get crtc properties\n");
+			goto err;
+		}
+
+		dev->crtcs[i].mode_pid = get_prop_id(dev, props, "MODE_ID");
+		dev->crtcs[i].active_pid = get_prop_id(dev, props, "ACTIVE");
+		drmModeFreeObjectProperties(props);
+		if (!dev->crtcs[i].mode_pid || !dev->crtcs[i].active_pid)
+			goto err;
+	}
+
+	pr = drmModeGetPlaneResources(dev->fd);
+	if (!pr) {
+		printf("failed to get plane resources\n");
+		goto err;
+	}
+	dev->num_planes = pr->count_planes;
+	dev->planes = calloc(dev->num_planes, sizeof(struct sp_plane));
+	for(i = 0; i < dev->num_planes; i++) {
+		drmModeObjectPropertiesPtr props;
+		struct sp_plane *plane = &dev->planes[i];
+
+		plane->dev = dev;
+		plane->plane = drmModeGetPlane(dev->fd, pr->planes[i]);
+		if (!plane->plane) {
+			printf("failed to get plane %d\n", i);
+			goto err;
+		}
+		plane->bo = NULL;
+		plane->in_use = 0;
+
+		ret = get_supported_format(plane, &plane->format);
+		if (ret) {
+			printf("failed to get supported format: %d\n", ret);
+			goto err;
+		}
+
+		for (j = 0; j < dev->num_crtcs; j++) {
+			if (plane->plane->possible_crtcs & (1 << j))
+				dev->crtcs[j].num_planes++;
+		}
+
+		props = drmModeObjectGetProperties(dev->fd, pr->planes[i],
+				DRM_MODE_OBJECT_PLANE);
+		if (!props) {
+			printf("failed to get plane properties\n");
+			goto err;
+		}
+		plane->crtc_pid = get_prop_id(dev, props, "CRTC_ID");
+		if (!plane->crtc_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->fb_pid = get_prop_id(dev, props, "FB_ID");
+		if (!plane->fb_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->crtc_x_pid = get_prop_id(dev, props, "CRTC_X");
+		if (!plane->crtc_x_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->crtc_y_pid = get_prop_id(dev, props, "CRTC_Y");
+		if (!plane->crtc_y_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->crtc_w_pid = get_prop_id(dev, props, "CRTC_W");
+		if (!plane->crtc_w_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->crtc_h_pid = get_prop_id(dev, props, "CRTC_H");
+		if (!plane->crtc_h_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->src_x_pid = get_prop_id(dev, props, "SRC_X");
+		if (!plane->src_x_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->src_y_pid = get_prop_id(dev, props, "SRC_Y");
+		if (!plane->src_y_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->src_w_pid = get_prop_id(dev, props, "SRC_W");
+		if (!plane->src_w_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		plane->src_h_pid = get_prop_id(dev, props, "SRC_H");
+		if (!plane->src_h_pid) {
+			drmModeFreeObjectProperties(props);
+			goto err;
+		}
+		drmModeFreeObjectProperties(props);
+	}
+
+	if (pr)
+		drmModeFreePlaneResources(pr);
+	if (r)
+		drmModeFreeResources(r);
+
+	return dev;
+err:
+	if (pr)
+		drmModeFreePlaneResources(pr);
+	if (r)
+		drmModeFreeResources(r);
+	destroy_sp_dev(dev);
+	return NULL;
+}
+
+void destroy_sp_dev(struct sp_dev *dev)
+{
+	int i;
+
+	if (dev->planes) {
+		for (i = 0; i< dev->num_planes; i++) {
+			if (dev->planes[i].in_use)
+				put_sp_plane(&dev->planes[i]);
+			if (dev->planes[i].plane)
+				drmModeFreePlane(dev->planes[i].plane);
+			if (dev->planes[i].bo)
+				free_sp_bo(dev->planes[i].bo);
+		}
+		free(dev->planes);
+	}
+	if (dev->crtcs) {
+		for (i = 0; i< dev->num_crtcs; i++) {
+			if (dev->crtcs[i].crtc)
+				drmModeFreeCrtc(dev->crtcs[i].crtc);
+		}
+		free(dev->crtcs);
+	}
+	if (dev->encoders) {
+		for (i = 0; i< dev->num_encoders; i++) {
+			if (dev->encoders[i])
+				drmModeFreeEncoder(dev->encoders[i]);
+		}
+		free(dev->encoders);
+	}
+	if (dev->connectors) {
+		for (i = 0; i< dev->num_connectors; i++) {
+			if (dev->connectors[i].conn)
+				drmModeFreeConnector(dev->connectors[i].conn);
+		}
+		free(dev->connectors);
+	}
+
+	close(dev->fd);
+	free(dev);
+}
diff --git a/tests/planetest/dev.h b/tests/planetest/dev.h
new file mode 100644
index 0000000..04dec79
--- /dev/null
+++ b/tests/planetest/dev.h
@@ -0,0 +1,65 @@
+#ifndef __DEV_H_INCLUDED__
+#define __DEV_H_INCLUDED__
+
+#include <stdint.h>
+#include <xf86drmMode.h>
+
+struct sp_bo;
+struct sp_dev;
+
+struct sp_plane {
+	struct sp_dev *dev;
+	drmModePlanePtr plane;
+	struct sp_bo *bo;
+	int in_use;
+	uint32_t format;
+
+	/* Property ID's */
+	uint32_t crtc_pid;
+	uint32_t fb_pid;
+	uint32_t zpos_pid;
+	uint32_t crtc_x_pid;
+	uint32_t crtc_y_pid;
+	uint32_t crtc_w_pid;
+	uint32_t crtc_h_pid;
+	uint32_t src_x_pid;
+	uint32_t src_y_pid;
+	uint32_t src_w_pid;
+	uint32_t src_h_pid;
+};
+
+struct sp_connector {
+	drmModeConnectorPtr conn;
+	uint32_t crtc_id_pid;
+};
+
+struct sp_crtc {
+	drmModeCrtcPtr crtc;
+	int pipe;
+	int num_planes;
+	uint32_t mode_pid;
+	uint32_t active_pid;
+};
+
+struct sp_dev {
+	int fd;
+
+	int num_connectors;
+	struct sp_connector *connectors;
+
+	int num_encoders;
+	drmModeEncoderPtr *encoders;
+
+	int num_crtcs;
+	struct sp_crtc *crtcs;
+
+	int num_planes;
+	struct sp_plane *planes;
+};
+
+void parse_arguments(int argc, char *argv[], int *card, int *crtc);
+
+struct sp_dev *create_sp_dev(int card);
+void destroy_sp_dev(struct sp_dev *dev);
+
+#endif /* __DEV_H_INCLUDED__ */
diff --git a/tests/planetest/modeset.c b/tests/planetest/modeset.c
new file mode 100644
index 0000000..037814e
--- /dev/null
+++ b/tests/planetest/modeset.c
@@ -0,0 +1,232 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <drm_fourcc.h>
+
+#include "modeset.h"
+#include "bo.h"
+#include "dev.h"
+
+static int set_crtc_mode(struct sp_dev *dev, struct sp_crtc *crtc,
+			struct sp_connector *conn, drmModeModeInfoPtr mode)
+{
+	int ret;
+	struct drm_mode_create_blob create_blob;
+	drmModePropertySetPtr pset;
+
+	memset(&create_blob, 0, sizeof(create_blob));
+	create_blob.length = sizeof(struct drm_mode_modeinfo);
+	create_blob.data = (__u64)(uintptr_t)mode;
+
+	ret = drmIoctl(dev->fd, DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
+	if (ret) {
+		printf("Failed to create mode property blob %d", ret);
+		return ret;
+	}
+
+	pset = drmModePropertySetAlloc();
+	if (!pset) {
+		printf("Failed to allocate property set");
+		return -1;
+	}
+
+	ret = drmModePropertySetAdd(pset, crtc->crtc->crtc_id,
+				    crtc->mode_pid, create_blob.blob_id) ||
+	      drmModePropertySetAdd(pset, crtc->crtc->crtc_id,
+				    crtc->active_pid, 1) ||
+		drmModePropertySetAdd(pset, conn->conn->connector_id,
+				conn->crtc_id_pid, crtc->crtc->crtc_id);
+	if (ret) {
+		printf("Failed to add blob %d to pset", create_blob.blob_id);
+		drmModePropertySetFree(pset);
+		return ret;
+	}
+
+	ret = drmModePropertySetCommit(dev->fd, DRM_MODE_ATOMIC_ALLOW_MODESET,
+					NULL, pset);
+
+	drmModePropertySetFree(pset);
+
+	if (ret) {
+		printf("Failed to commit pset ret=%d\n", ret);
+		return ret;
+	}
+
+	memcpy(&crtc->crtc->mode, mode, sizeof(struct drm_mode_modeinfo));
+	crtc->crtc->mode_valid = 1;
+	return 0;
+}
+
+int initialize_screens(struct sp_dev *dev)
+{
+	int ret, i, j;
+	unsigned crtc_mask = 0;
+
+	for (i = 0; i < dev->num_connectors; i++) {
+		struct sp_connector *c = &dev->connectors[i];
+		drmModeModeInfoPtr m = NULL;
+		drmModeEncoderPtr e = NULL;
+		struct sp_crtc *cr = NULL;
+
+		if (c->conn->connection != DRM_MODE_CONNECTED)
+			continue;
+
+		if (!c->conn->count_modes) {
+			printf("connector has no modes, skipping\n");
+			continue;
+		}
+
+		/* Take the first unless there's a preferred mode */
+		m = &c->conn->modes[0];
+		for (j = 0; j < c->conn->count_modes; j++) {
+			drmModeModeInfoPtr tmp_m = &c->conn->modes[j];
+
+			if (!(tmp_m->type & DRM_MODE_TYPE_PREFERRED))
+				continue;
+
+			m = tmp_m;
+			break;
+		}
+
+		if (!c->conn->count_encoders) {
+			printf("no possible encoders for connector\n");
+			continue;
+		}
+
+		for (j = 0; j < dev->num_encoders; j++) {
+			e = dev->encoders[j];
+			if (e->encoder_id == c->conn->encoders[0])
+				break;
+		}
+		if (j == dev->num_encoders) {
+			printf("could not find encoder for the connector\n");
+			continue;
+		}
+
+		for (j = 0; j < dev->num_crtcs; j++) {
+			if ((1 << j) & crtc_mask)
+				continue;
+
+			cr = &dev->crtcs[j];
+
+			if ((1 << j) & e->possible_crtcs)
+				break;
+		}
+		if (j == dev->num_crtcs) {
+			printf("could not find crtc for the encoder\n");
+			continue;
+		}
+
+		ret = set_crtc_mode(dev, cr, c, m);
+		if (ret) {
+			printf("failed to set mode!\n");
+			continue;
+		}
+		crtc_mask |= 1 << j;
+	}
+	return 0;
+}
+
+struct sp_plane *get_sp_plane(struct sp_dev *dev, struct sp_crtc *crtc)
+{
+	int i;
+
+	for(i = 0; i < dev->num_planes; i++) {
+		struct sp_plane *p = &dev->planes[i];
+
+		if (p->in_use)
+			continue;
+
+		if (!(p->plane->possible_crtcs & (1 << crtc->pipe)))
+			continue;
+
+		p->in_use = 1;
+		return p;
+	}
+	return NULL;
+}
+
+void put_sp_plane(struct sp_plane *plane)
+{
+	drmModePlanePtr p;
+
+	/* Get the latest plane information (most notably the crtc_id) */
+	p = drmModeGetPlane(plane->dev->fd, plane->plane->plane_id);
+	if (p)
+		plane->plane = p;
+
+	if (plane->bo) {
+		free_sp_bo(plane->bo);
+		plane->bo = NULL;
+	}
+	plane->in_use = 0;
+}
+
+int set_sp_plane(struct sp_dev *dev, struct sp_plane *plane,
+		struct sp_crtc *crtc, int x, int y)
+{
+	int ret;
+	uint32_t w, h;
+
+	w = plane->bo->width;
+	h = plane->bo->height;
+
+	if ((w + x) > crtc->crtc->mode.hdisplay)
+		w = crtc->crtc->mode.hdisplay - x;
+	if ((h + y) > crtc->crtc->mode.vdisplay)
+		h = crtc->crtc->mode.vdisplay - y;
+
+	ret = drmModeSetPlane(dev->fd, plane->plane->plane_id,
+			crtc->crtc->crtc_id, plane->bo->fb_id, 0, x, y, w, h,
+			0, 0, w << 16, h << 16);
+	if (ret) {
+		printf("failed to set plane to crtc ret=%d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+int set_sp_plane_pset(struct sp_dev *dev, struct sp_plane *plane,
+		drmModePropertySetPtr pset, struct sp_crtc *crtc, int x, int y)
+{
+	int ret;
+	uint32_t w, h;
+
+	w = plane->bo->width;
+	h = plane->bo->height;
+
+	if ((w + x) > crtc->crtc->mode.hdisplay)
+		w = crtc->crtc->mode.hdisplay - x;
+	if ((h + y) > crtc->crtc->mode.vdisplay)
+		h = crtc->crtc->mode.vdisplay - y;
+
+	ret = drmModePropertySetAdd(pset, plane->plane->plane_id,
+			plane->crtc_pid, crtc->crtc->crtc_id)
+		|| drmModePropertySetAdd(pset, plane->plane->plane_id,
+			plane->fb_pid, plane->bo->fb_id)
+		|| drmModePropertySetAdd(pset, plane->plane->plane_id,
+			plane->crtc_x_pid, x)
+		|| drmModePropertySetAdd(pset, plane->plane->plane_id,
+			plane->crtc_y_pid, y)
+		|| drmModePropertySetAdd(pset, plane->plane->plane_id,
+			plane->crtc_w_pid, w)
+		|| drmModePropertySetAdd(pset, plane->plane->plane_id,
+			plane->crtc_h_pid, h)
+		|| drmModePropertySetAdd(pset, plane->plane->plane_id,
+			plane->src_x_pid, 0)
+		|| drmModePropertySetAdd(pset, plane->plane->plane_id,
+			plane->src_y_pid, 0)
+		|| drmModePropertySetAdd(pset, plane->plane->plane_id,
+			plane->src_w_pid, w << 16)
+		|| drmModePropertySetAdd(pset, plane->plane->plane_id,
+			plane->src_h_pid, h << 16);
+	if (ret) {
+		printf("failed to add properties to the set\n");
+		return -1;
+	}
+
+	return ret;
+}
diff --git a/tests/planetest/modeset.h b/tests/planetest/modeset.h
new file mode 100644
index 0000000..7e96574
--- /dev/null
+++ b/tests/planetest/modeset.h
@@ -0,0 +1,19 @@
+#ifndef __MODESET_H_INCLUDED__
+#define __MODESET_H_INCLUDED__
+
+struct sp_dev;
+struct sp_crtc;
+
+int initialize_screens(struct sp_dev *dev);
+
+
+struct sp_plane *get_sp_plane(struct sp_dev *dev, struct sp_crtc *crtc);
+void put_sp_plane(struct sp_plane *plane);
+
+int set_sp_plane(struct sp_dev *dev, struct sp_plane *plane,
+		struct sp_crtc *crtc, int x, int y);
+
+int set_sp_plane_pset(struct sp_dev *dev, struct sp_plane *plane,
+		drmModePropertySetPtr pset, struct sp_crtc *crtc, int x, int y);
+
+#endif /* __MODESET_H_INCLUDED__ */
diff --git a/tests/planetest/planetest.c b/tests/planetest/planetest.c
new file mode 100644
index 0000000..5e187c9
--- /dev/null
+++ b/tests/planetest/planetest.c
@@ -0,0 +1,116 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+
+#include <xf86drm.h>
+
+#include "dev.h"
+#include "bo.h"
+#include "modeset.h"
+
+static int terminate = 0;
+
+static void sigint_handler(int arg)
+{
+	terminate = 1;
+}
+
+static void incrementor(int *inc, int *val, int increment, int lower, int upper)
+{
+	if(*inc > 0)
+		*inc = *val + increment >= upper ? -1 : 1;
+	else
+		*inc = *val - increment <= lower ? 1 : -1;
+	*val += *inc * increment;
+}
+
+int main(int argc, char *argv[])
+{
+	int ret, i, j, num_test_planes;
+	int x_inc = 1, x = 0, y_inc = 1, y = 0;
+	uint32_t plane_w = 128, plane_h = 128;
+	struct sp_dev *dev;
+	struct sp_plane **plane = NULL;
+	struct sp_crtc *test_crtc;
+	int card = 0, crtc = 0;
+
+	signal(SIGINT, sigint_handler);
+
+	parse_arguments(argc, argv, &card, &crtc);
+
+	dev = create_sp_dev(card);
+	if (!dev) {
+		printf("Failed to create sp_dev\n");
+		return -1;
+	}
+
+	if (crtc >= dev->num_crtcs) {
+		printf("Invalid crtc %d (num=%d)\n", crtc, dev->num_crtcs);
+		return -1;
+	}
+
+	ret = initialize_screens(dev);
+	if (ret) {
+		printf("Failed to initialize screens\n");
+		goto out;
+	}
+	test_crtc = &dev->crtcs[crtc];
+
+	plane = calloc(dev->num_planes, sizeof(*plane));
+	if (!plane) {
+		printf("Failed to allocate plane array\n");
+		goto out;
+	}
+
+	/* Create our planes */
+	num_test_planes = test_crtc->num_planes;
+	for (i = 0; i < num_test_planes; i++) {
+		plane[i] = get_sp_plane(dev, test_crtc);
+		if (!plane[i]) {
+			printf("no unused planes available\n");
+			goto out;
+		}
+
+		plane[i]->bo = create_sp_bo(dev, plane_w, plane_h, 16,
+				plane[i]->format, 0);
+		if (!plane[i]->bo) {
+			printf("failed to create plane bo\n");
+			goto out;
+		}
+
+		fill_bo(plane[i]->bo, 0xFF, 0xFF, 0xFF, 0xFF);
+	}
+
+	while (!terminate) {
+		incrementor(&x_inc, &x, 5, 0,
+			test_crtc->crtc->mode.hdisplay - plane_w);
+		incrementor(&y_inc, &y, 5, 0, test_crtc->crtc->mode.vdisplay -
+						plane_h * num_test_planes);
+
+		for (j = 0; j < num_test_planes; j++) {
+			ret = set_sp_plane(dev, plane[j], test_crtc,
+					x, y + j * plane_h);
+			if (ret) {
+				printf("failed to set plane %d %d\n", j, ret);
+				goto out;
+			}
+		}
+		usleep(15 * 1000);
+	}
+
+	for (i = 0; i < num_test_planes; i++)
+		put_sp_plane(plane[i]);
+
+out:
+	destroy_sp_dev(dev);
+	free(plane);
+	return ret;
+}
diff --git a/tests/proptest/Android.mk b/tests/proptest/Android.mk
index 91a590f..588fbed 100644
--- a/tests/proptest/Android.mk
+++ b/tests/proptest/Android.mk
@@ -10,5 +10,7 @@
 LOCAL_SHARED_LIBRARIES := libdrm
 LOCAL_STATIC_LIBRARIES := libdrm_util
 
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
+
 include $(LIBDRM_COMMON_MK)
 include $(BUILD_EXECUTABLE)
diff --git a/xf86drmMode.c b/xf86drmMode.c
index 0266bc1..e1c9974 100644
--- a/xf86drmMode.c
+++ b/xf86drmMode.c
@@ -1188,6 +1188,275 @@
 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_OBJ_SETPROPERTY, &prop);
 }
 
+typedef struct _drmModePropertySetItem drmModePropertySetItem, *drmModePropertySetItemPtr;
+
+struct _drmModePropertySetItem {
+	uint32_t object_id;
+	uint32_t property_id;
+	bool is_blob;
+	uint64_t value;
+	void *blob;
+	drmModePropertySetItemPtr next;
+};
+
+struct _drmModePropertySet {
+	unsigned int count_objs;
+	unsigned int count_props;
+	unsigned int count_blobs;
+	drmModePropertySetItem list;
+};
+
+drmModePropertySetPtr drmModePropertySetAlloc(void)
+{
+	drmModePropertySetPtr set;
+
+	set = drmMalloc(sizeof *set);
+	if (!set)
+		return NULL;
+
+	set->list.next = NULL;
+	set->count_props = 0;
+	set->count_objs = 0;
+
+	return set;
+}
+
+int drmModePropertySetAdd(drmModePropertySetPtr set,
+			  uint32_t object_id,
+			  uint32_t property_id,
+			  uint64_t value)
+{
+	drmModePropertySetItemPtr prev = &set->list;
+	bool new_obj = false;
+
+	/* keep it sorted by object_id and property_id */
+	while (prev->next) {
+		if (prev->next->object_id > object_id)
+			break;
+
+		if (prev->next->object_id == object_id &&
+		    prev->next->property_id >= property_id)
+			break;
+
+		prev = prev->next;
+	}
+
+	if ((prev == &set->list || prev->object_id != object_id) &&
+	    (!prev->next || prev->next->object_id != object_id))
+		new_obj = true;
+
+	/* replace or add? */
+	if (prev->next &&
+	    prev->next->object_id == object_id &&
+	    prev->next->property_id == property_id) {
+		drmModePropertySetItemPtr item = prev->next;
+
+		if (item->is_blob)
+			return -EINVAL;
+
+		item->value = value;
+	} else {
+		drmModePropertySetItemPtr item;
+
+		item = drmMalloc(sizeof *item);
+		if (!item)
+			return -1;
+
+		item->object_id = object_id;
+		item->property_id = property_id;
+		item->value = value;
+		item->is_blob = false;
+		item->blob = NULL;
+
+		item->next = prev->next;
+		prev->next = item;
+
+		set->count_props++;
+	}
+
+	if (new_obj)
+		set->count_objs++;
+
+	return 0;
+}
+
+int drmModePropertySetAddBlob(drmModePropertySetPtr set,
+			      uint32_t object_id,
+			      uint32_t property_id,
+			      uint64_t length,
+			      void *data)
+{
+	drmModePropertySetItemPtr prev = &set->list;
+	bool new_obj = false;
+
+	/* keep it sorted by object_id and property_id */
+	while (prev->next) {
+		if (prev->next->object_id > object_id)
+			break;
+
+		if (prev->next->object_id == object_id &&
+		    prev->next->property_id >= property_id)
+			break;
+
+		prev = prev->next;
+	}
+
+	if ((prev == &set->list || prev->object_id != object_id) &&
+	    (!prev->next || prev->next->object_id != object_id))
+		new_obj = true;
+
+	/* replace or add? */
+	if (prev->next &&
+	    prev->next->object_id == object_id &&
+	    prev->next->property_id == property_id) {
+		drmModePropertySetItemPtr item = prev->next;
+
+		if (!item->is_blob)
+			return -EINVAL;
+
+		item->value = length;
+		item->blob = data;
+	} else {
+		drmModePropertySetItemPtr item;
+
+		item = drmMalloc(sizeof *item);
+		if (!item)
+			return -1;
+
+		item->object_id = object_id;
+		item->property_id = property_id;
+		item->is_blob = true;
+		item->value = length;
+		item->blob = data;
+
+		item->next = prev->next;
+		prev->next = item;
+
+		set->count_props++;
+		set->count_blobs++;
+	}
+
+	if (new_obj)
+		set->count_objs++;
+
+	return 0;
+}
+
+void drmModePropertySetFree(drmModePropertySetPtr set)
+{
+	drmModePropertySetItemPtr item;
+
+	if (!set)
+		return;
+
+	item = set->list.next;
+
+	while (item) {
+		drmModePropertySetItemPtr next = item->next;
+
+		drmFree(item);
+
+		item = next;
+	}
+
+	drmFree(set);
+}
+
+int drmModePropertySetCommit(int fd, uint32_t flags, void *user_data,
+			     drmModePropertySetPtr set)
+{
+	drmModePropertySetItemPtr item;
+	uint32_t *objs_ptr = NULL;
+	uint32_t *count_props_ptr = NULL;
+	uint32_t *props_ptr = NULL;
+	uint64_t *prop_values_ptr = NULL;
+	uint64_t *blob_values_ptr = NULL;
+	struct drm_mode_atomic atomic = { 0 };
+	unsigned int obj_idx = 0;
+	unsigned int prop_idx = 0;
+	unsigned int blob_idx = 0;
+	int ret = -1;
+
+	if (!set)
+		return -1;
+
+	objs_ptr = drmMalloc(set->count_objs * sizeof objs_ptr[0]);
+	if (!objs_ptr) {
+		errno = ENOMEM;
+		goto out;
+	}
+
+	count_props_ptr = drmMalloc(set->count_objs * sizeof count_props_ptr[0]);
+	if (!count_props_ptr) {
+		errno = ENOMEM;
+		goto out;
+	}
+
+	props_ptr = drmMalloc(set->count_props * sizeof props_ptr[0]);
+	if (!props_ptr) {
+		errno = ENOMEM;
+		goto out;
+	}
+
+	prop_values_ptr = drmMalloc(set->count_props * sizeof prop_values_ptr[0]);
+	if (!prop_values_ptr) {
+		errno = ENOMEM;
+		goto out;
+	}
+
+	blob_values_ptr = drmMalloc(set->count_blobs * sizeof blob_values_ptr[0]);
+	if (!blob_values_ptr) {
+		errno = ENOMEM;
+		goto out;
+	}
+
+	item = set->list.next;
+
+	while (item) {
+		int count_props = 0;
+		drmModePropertySetItemPtr next = item;
+
+		objs_ptr[obj_idx] = item->object_id;
+
+		while (next && next->object_id == item->object_id) {
+			props_ptr[prop_idx] = next->property_id;
+			prop_values_ptr[prop_idx] = next->value;
+			prop_idx++;
+
+			if (next->is_blob)
+				blob_values_ptr[blob_idx++] = VOID2U64(next->blob);
+
+			count_props++;
+
+			next = next->next;
+		}
+
+		count_props_ptr[obj_idx++] = count_props;
+
+		item = next;
+	}
+
+	atomic.count_objs = set->count_objs;
+	atomic.flags = flags;
+	atomic.objs_ptr = VOID2U64(objs_ptr);
+	atomic.count_props_ptr = VOID2U64(count_props_ptr);
+	atomic.props_ptr = VOID2U64(props_ptr);
+	atomic.prop_values_ptr = VOID2U64(prop_values_ptr);
+// TODO:
+//	atomic.blob_values_ptr = VOID2U64(blob_values_ptr);
+	atomic.user_data = VOID2U64(user_data);
+
+	ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ATOMIC, &atomic);
+
+out:
+	drmFree(objs_ptr);
+	drmFree(count_props_ptr);
+	drmFree(props_ptr);
+	drmFree(prop_values_ptr);
+
+	return ret;
+}
+
 typedef struct _drmModeAtomicReqItem drmModeAtomicReqItem, *drmModeAtomicReqItemPtr;
 
 struct _drmModeAtomicReqItem {
diff --git a/xf86drmMode.h b/xf86drmMode.h
index 5b390d9..9d73be9 100644
--- a/xf86drmMode.h
+++ b/xf86drmMode.h
@@ -498,6 +498,25 @@
 				    uint64_t value);
 
 
+typedef struct _drmModePropertySet drmModePropertySet, *drmModePropertySetPtr;
+
+extern drmModePropertySetPtr drmModePropertySetAlloc(void);
+
+extern int drmModePropertySetAdd(drmModePropertySetPtr set,
+				 uint32_t object_id,
+				 uint32_t property_id,
+				 uint64_t value);
+extern int drmModePropertySetAddBlob(drmModePropertySetPtr set,
+				     uint32_t object_id,
+				     uint32_t property_id,
+				     uint64_t length,
+				     void *blob);
+
+extern int drmModePropertySetCommit(int fd, uint32_t flags,
+				    void *user_data, drmModePropertySetPtr set);
+
+extern void drmModePropertySetFree(drmModePropertySetPtr set);
+
 typedef struct _drmModeAtomicReq drmModeAtomicReq, *drmModeAtomicReqPtr;
 
 extern drmModeAtomicReqPtr drmModeAtomicAlloc(void);