[RFC PATCH v3 3/4] ipvr: user mode helper for ipvr drm driver

Yao Cheng yao.cheng at intel.com
Fri Nov 21 11:09:08 PST 2014


add usermode helper for the ipvr kernel driver.
test_ioctl: test kernel driver by directly ioctl

v2:
take Emil's comments
	- correctly align ipvr_drm.h

v3:
take Daniel Vetter and Daniel Stone's comments, and implement PRIME
	- correctly align ipvr_drm.h
	- use __u32 family in ipvr_drm.h
	- rip out explicit fence from libdrm_ipvr
	- implemented PRIME support
	- add relocation fixup implementation

Signed-off-by: Yao Cheng <yao.cheng at intel.com>
---
 Makefile.am            |    6 +-
 Makefile.sources       |    1 +
 configure.ac           |   26 +-
 include/drm/ipvr_drm.h |  278 +++++++++++
 ipvr/Makefile.am       |   57 +++
 ipvr/Makefile.sources  |    5 +
 ipvr/ipvr_bufmgr.h     |  149 ++++++
 ipvr/ipvr_bufmgr_gem.c | 1200 ++++++++++++++++++++++++++++++++++++++++++++++++
 ipvr/libdrm_ipvr.pc.in |   11 +
 ipvr/test_ioctl.c      |  423 +++++++++++++++++
 10 files changed, 2154 insertions(+), 2 deletions(-)
 create mode 100644 include/drm/ipvr_drm.h
 create mode 100644 ipvr/Makefile.am
 create mode 100644 ipvr/Makefile.sources
 create mode 100644 ipvr/ipvr_bufmgr.h
 create mode 100644 ipvr/ipvr_bufmgr_gem.c
 create mode 100644 ipvr/libdrm_ipvr.pc.in
 create mode 100644 ipvr/test_ioctl.c

diff --git a/Makefile.am b/Makefile.am
index 3952a88..2227add 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -33,6 +33,10 @@ if HAVE_INTEL
 INTEL_SUBDIR = intel
 endif
 
+if HAVE_IPVR
+IPVR_SUBDIR = ipvr
+endif
+
 if HAVE_NOUVEAU
 NOUVEAU_SUBDIR = nouveau
 endif
@@ -53,7 +57,7 @@ if HAVE_FREEDRENO
 FREEDRENO_SUBDIR = freedreno
 endif
 
-SUBDIRS = . $(LIBKMS_SUBDIR) $(INTEL_SUBDIR) $(NOUVEAU_SUBDIR) $(RADEON_SUBDIR) $(OMAP_SUBDIR) $(EXYNOS_SUBDIR) $(FREEDRENO_SUBDIR) tests man
+SUBDIRS = . $(LIBKMS_SUBDIR) $(INTEL_SUBDIR) $(IPVR_SUBDIR) $(NOUVEAU_SUBDIR) $(RADEON_SUBDIR) $(OMAP_SUBDIR) $(EXYNOS_SUBDIR) $(FREEDRENO_SUBDIR) tests man
 
 libdrm_la_LTLIBRARIES = libdrm.la
 libdrm_ladir = $(libdir)
diff --git a/Makefile.sources b/Makefile.sources
index d86fb2a..96f8c60 100644
--- a/Makefile.sources
+++ b/Makefile.sources
@@ -18,6 +18,7 @@ LIBDRM_INCLUDE_H_FILES := \
 	include/drm/drm_mode.h \
 	include/drm/drm_sarea.h \
 	include/drm/i915_drm.h \
+	include/drm/ipvr_drm.h \
 	include/drm/mach64_drm.h \
 	include/drm/mga_drm.h \
 	include/drm/nouveau_drm.h \
diff --git a/configure.ac b/configure.ac
index ee59b03..6dcf1b2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -68,6 +68,11 @@ AC_ARG_ENABLE(intel,
 	      [Enable support for intel's KMS API (default: auto)]),
 	      [INTEL=$enableval], [INTEL=auto])
 
+AC_ARG_ENABLE(ipvr,
+	      AS_HELP_STRING([--disable-ipvr],
+	      [Enable support for baytrail's hardware VP8 decode (default: auto)]),
+	      [IPVR=$enableval], [IPVR=auto])
+
 AC_ARG_ENABLE(radeon,
 	      AS_HELP_STRING([--disable-radeon],
 	      [Enable support for radeon's KMS API (default: auto)]),
@@ -204,7 +209,7 @@ if test "x$drm_cv_atomic_primitives" = "xlibatomic-ops"; then
 	AC_DEFINE(HAVE_LIB_ATOMIC_OPS, 1, [Enable if you have libatomic-ops-dev installed])
 fi
 
-if test "x$INTEL" != "xno" -o "x$RADEON" != "xno" -o "x$NOUVEAU" != "xno"; then
+if test "x$INTEL" != "xno" -o "x$IPVR" != "xno" -o "x$RADEON" != "xno" -o "x$NOUVEAU" != "xno"; then
 	if test "x$drm_cv_atomic_primitives" = "xnone"; then
 		if test "x$INTEL" != "xauto"; then
 			if test "x$INTEL" != "xno"; then
@@ -214,6 +219,14 @@ if test "x$INTEL" != "xno" -o "x$RADEON" != "xno" -o "x$NOUVEAU" != "xno"; then
 			AC_MSG_WARN([Disabling libdrm_intel. It depends on atomic operations, which were not found for your compiler/cpu. Try compiling with -march=native, or install the libatomics-op-dev package.])
 			INTEL=no
 		fi
+		if test "x$IPVR" != "xauto"; then
+			if test "x$IPVR" != "xno"; then
+				AC_MSG_ERROR([libdrm_ipvr depends upon atomic operations, which were not found for your compiler/cpu. Try compiling with -march=native, or install the libatomics-op-dev package, or, failing both of those, disable support for Baytrail VP8 by passing --disable-ipvr to ./configure])
+			fi
+		else
+			AC_MSG_WARN([Disabling libdrm_ipvr. It depends on atomic operations, which were not found for your compiler/cpu. Try compiling with -march=native, or install the libatomics-op-dev package.])
+			INTEL=no
+		fi
 		if test "x$RADEON" != "xauto"; then
 			if test "x$RADEON" != "xno"; then
 				AC_MSG_ERROR([libdrm_radeon depends upon atomic operations, which were not found for your compiler/cpu. Try compiling with -march=native, or install the libatomics-op-dev package, or, failing both of those, disable support for Radeon GPUs by passing --disable-radeon to ./configure])
@@ -237,6 +250,9 @@ if test "x$INTEL" != "xno" -o "x$RADEON" != "xno" -o "x$NOUVEAU" != "xno"; then
 				*) INTEL=no ;;
 			esac
 		fi
+		if test "x$IPVR" != "xno"; then
+			IPVR=yes
+		fi
 		if test "x$RADEON" != "xno"; then
 			RADEON=yes
 		fi
@@ -274,6 +290,11 @@ if test "x$INTEL" = xyes; then
 	AC_DEFINE(HAVE_INTEL, 1, [Have intel support])
 fi
 
+AM_CONDITIONAL(HAVE_IPVR, [test "x$IPVR" = xyes])
+if test "x$IPVR" = xyes; then
+	AC_DEFINE(HAVE_PVR, 1, [Have ipvr support])
+fi
+
 AM_CONDITIONAL(HAVE_VMWGFX, [test "x$VMWGFX" = xyes])
 if test "x$VMWGFX" = xyes; then
 	AC_DEFINE(HAVE_VMWGFX, 1, [Have vmwgfx kernel headers])
@@ -393,6 +414,8 @@ AC_CONFIG_FILES([
 	libkms/libkms.pc
 	intel/Makefile
 	intel/libdrm_intel.pc
+	ipvr/Makefile
+	ipvr/libdrm_ipvr.pc
 	radeon/Makefile
 	radeon/libdrm_radeon.pc
 	nouveau/Makefile
@@ -419,6 +442,7 @@ echo "$PACKAGE_STRING will be compiled with:"
 echo ""
 echo "  libkms         $LIBKMS"
 echo "  Intel API      $INTEL"
+echo "  Ipvr API       $IPVR"
 echo "  vmwgfx API     $VMWGFX"
 echo "  Radeon API     $RADEON"
 echo "  Nouveau API    $NOUVEAU"
diff --git a/include/drm/ipvr_drm.h b/include/drm/ipvr_drm.h
new file mode 100644
index 0000000..d1916fa
--- /dev/null
+++ b/include/drm/ipvr_drm.h
@@ -0,0 +1,278 @@
+/**************************************************************************
+ * ipvr_drm.h: IPVR header file exported to user space
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ *    Fei Jiang <fei.jiang at intel.com>
+ *    Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+
+
+/* this file only define structs and macro which need export to user space */
+#ifndef _IPVR_DRM_H_
+#define _IPVR_DRM_H_
+
+#include <drm/drm.h>
+struct drm_ipvr_context_create {
+	/* passed ctx_info, including codec, profile info */
+#define IPVR_CONTEXT_TYPE_VED   (0x1)
+	__u32 ctx_type;
+	/* returned back ctx_id */
+	__u32 ctx_id;
+	/*
+	 * following tiling strides for VED are supported:
+	 * stride 0: 512 for scheme 0, 1024 for scheme 1
+	 * stride 1: 1024 for scheme 0, 2048 for scheme 1
+	 * stride 2: 2048 for scheme 0, 4096 for scheme 1
+	 * stride 3: 4096 for scheme 0
+	 */
+	__u32 tiling_stride;
+	/*
+	 * scheme 0: tile is 256x16, while minimal tile stride is 512
+	 * scheme 1: tile is 512x8, while minimal tile stride is 1024
+	 */
+	__u32 tiling_scheme;
+};
+
+struct drm_ipvr_context_destroy {
+	__u32 ctx_id;
+	__u32 pad64;
+};
+
+enum drm_ipvr_misc_key {
+	IPVR_DEVICE_INFO,
+	IPVR_UPDATE_TILING,
+	IPVR_SET_DISPLAYING_FRAME,
+	IPVR_GET_DISPLAYING_FRAME,
+	IPVR_QUERY_ENTRY
+};
+
+/*
+ * different context maybe has different tiling stride,
+ * then tiling info need be bound with ctx
+ */
+struct drm_ipvr_update_tiling {
+	__u32 ctx_id;
+	__u32 tiling_stride;
+	__u32 tiling_scheme;
+	__u32 pad64;
+};
+
+/* Ioctl to set/get misc params:
+ */
+struct drm_ipvr_misc {
+	__u64 key;
+	__u64 arg;		/* argument pointer */
+	__u64 value;	/* feed back pointer */
+};
+
+struct drm_ipvr_gem_relocation_entry {
+	/**
+	 * Handle of the buffer being pointed to by this relocation entry.
+	 *
+	 * It's appealing to make this be an index into the mm_validate_entry
+	 * list to refer to the buffer, but this allows the driver to create
+	 * a relocation list for state buffers and not re-write it per
+	 * exec using the buffer.
+	 */
+	__u32 target_handle;
+
+	/**
+	 * Value to be added to the offset of the target buffer to make up
+	 * the relocation entry.
+	 */
+	__u32 delta;
+
+	/** Offset in the buffer the relocation entry will be written into */
+	__u64 offset;
+
+	/**
+	 * Offset value of the target buffer that the relocation entry was last
+	 * written as.
+	 *
+	 * If the buffer has the same offset as last time, we can skip syncing
+	 * and writing the relocation.  This value is written back out by
+	 * the execbuffer ioctl when the relocation is written.
+	 */
+	__u64 presumed_offset;
+
+	/**
+	 * Target memory domains read by this operation.
+	 */
+	__u32 read_domains;
+
+	/**
+	 * Target memory domains written by this operation.
+	 *
+	 * Note that only one domain may be written by the whole
+	 * execbuffer operation, so that where there are conflicts,
+	 * the application will get -EINVAL back.
+	 */
+	__u32 write_domain;
+};
+
+struct drm_ipvr_gem_exec_object {
+	/**
+	 * User's handle for a buffer to be bound into the MMU for this
+	 * operation.
+	 */
+	__u32 handle;
+
+	/** Number of relocations to be performed on this buffer */
+	__u32 relocation_count;
+	/**
+	 * Pointer to array of struct drm_i915_gem_relocation_entry containing
+	 * the relocations to be performed in this buffer.
+	 */
+	__u64 relocs_ptr;
+
+	/** Required alignment in graphics aperture */
+	__u64 alignment;
+
+	/**
+	 * Returned value of the updated offset of the object, for future
+	 * presumed_offset writes.
+	 */
+	__u64 offset;
+
+#define IPVR_EXEC_OBJECT_NEED_FENCE (1 << 0)
+#define IPVR_EXEC_OBJECT_SUBMIT     (1 << 1)
+	__u64 flags;
+
+	__u64 rsvd1;
+	__u64 rsvd2;
+};
+
+struct drm_ipvr_gem_execbuffer {
+	/**
+	 * List of gem_exec_object2 structs
+	 */
+	__u64 buffers_ptr;
+	__u32 buffer_count;
+
+	/** Offset in the batchbuffer to start execution from. */
+	__u32 exec_start_offset;
+	/** Bytes used in batchbuffer from batch_start_offset */
+	__u32 exec_len;
+
+	/**
+	 * ID of hardware context.
+	 */
+	__u32 ctx_id;
+
+	__u64 flags;
+	__u64 rsvd1;
+	__u64 rsvd2;
+};
+
+enum ipvr_cache_level
+{
+	IPVR_CACHE_NOACCESS,
+	IPVR_CACHE_UNCACHED,
+	IPVR_CACHE_WRITEBACK,
+	IPVR_CACHE_WRITECOMBINE,
+	IPVR_CACHE_MAX,
+};
+
+struct drm_ipvr_gem_create {
+	/*
+	 * Requested size for the object.
+	 * The (page-aligned) allocated size for the object will be returned.
+	 */
+	__u64 size;
+	__u64 rounded_size;
+	__u64 mmu_offset;
+	/*
+	 * Returned handle for the object.
+	 * Object handles are nonzero.
+	 */
+	__u32 handle;
+	__u32 tiling;
+
+	__u32 cache_level;
+	__u32 pad64;
+	/*
+	 * Handle used for user to mmap BO
+	 */
+	__u64 map_offset;
+};
+
+struct drm_ipvr_gem_busy {
+	/* Handle of the buffer to check for busy */
+	__u32 handle;
+
+	/*
+	 * Return busy status (1 if busy, 0 if idle).
+	 * The high word is used to indicate on which rings the object
+	 * currently resides:
+	 *  16:31 - busy (r or r/w) rings (16 render, 17 bsd, 18 blt, etc)
+	 */
+	__u32 busy;
+};
+
+struct drm_ipvr_gem_mmap_offset {
+	/** Handle for the object being mapped. */
+	__u32 handle;
+	__u32 pad64;
+	/**
+	 * Fake offset to use for subsequent mmap call
+	 *
+	 * This is a fixed-size type for 32/64 compatibility.
+	 */
+	__u64 offset;
+};
+
+struct drm_ipvr_gem_wait {
+	/* Handle of BO we shall wait on */
+	__u32 handle;
+	__u32 flags;
+	/** Number of nanoseconds to wait, Returns time remaining. */
+	__s64 timeout_ns;
+};
+
+/*
+ * IPVR GEM specific ioctls
+ */
+#define DRM_IPVR_CONTEXT_CREATE     0x00
+#define DRM_IPVR_CONTEXT_DESTROY    0x01
+#define DRM_IPVR_MISC               0x02
+#define DRM_IPVR_GEM_EXECBUFFER     0x03
+#define DRM_IPVR_GEM_BUSY           0x04
+#define DRM_IPVR_GEM_CREATE         0x05
+#define DRM_IPVR_GEM_WAIT           0x06
+#define DRM_IPVR_GEM_MMAP_OFFSET    0x07
+
+#define DRM_IOCTL_IPVR_CONTEXT_CREATE	\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_CONTEXT_CREATE, struct drm_ipvr_context_create)
+#define DRM_IOCTL_IPVR_CONTEXT_DESTROY	\
+	DRM_IOW(DRM_COMMAND_BASE + DRM_IPVR_CONTEXT_DESTROY, struct drm_ipvr_context_destroy)
+#define DRM_IOCTL_IPVR_MISC		\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_MISC, struct drm_ipvr_misc)
+#define DRM_IOCTL_IPVR_GEM_EXECBUFFER	\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_GEM_EXECBUFFER, struct drm_ipvr_gem_execbuffer)
+#define DRM_IOCTL_IPVR_GEM_BUSY		\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_GEM_BUSY, struct drm_ipvr_gem_busy)
+#define DRM_IOCTL_IPVR_GEM_CREATE	\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_GEM_CREATE, struct drm_ipvr_gem_create)
+#define DRM_IOCTL_IPVR_GEM_WAIT		\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_GEM_WAIT, struct drm_ipvr_gem_wait)
+#define DRM_IOCTL_IPVR_GEM_MMAP_OFFSET	\
+	DRM_IOWR(DRM_COMMAND_BASE + DRM_IPVR_GEM_MMAP_OFFSET, struct drm_ipvr_gem_mmap_offset)
+
+#endif
diff --git a/ipvr/Makefile.am b/ipvr/Makefile.am
new file mode 100644
index 0000000..60cbb36
--- /dev/null
+++ b/ipvr/Makefile.am
@@ -0,0 +1,57 @@
+# Copyright © 2014 Intel Corporation
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+#
+# Authors:
+#    Yao Cheng <yao.cheng at intel.com>
+
+include Makefile.sources
+
+AM_CFLAGS = \
+	$(WARN_CFLAGS) \
+	$(VISIBILITY_CFLAGS) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/ipvr \
+	$(PTHREADSTUBS_CFLAGS) \
+	$(PCIACCESS_CFLAGS) \
+	$(VALGRIND_CFLAGS) \
+	-I$(top_srcdir)/include/drm
+
+libdrm_ipvr_la_LTLIBRARIES = libdrm_ipvr.la
+libdrm_ipvr_ladir = $(libdir)
+libdrm_ipvr_la_LDFLAGS = -version-number 0:1:0 -no-undefined
+libdrm_ipvr_la_LIBADD = ../libdrm.la \
+	@PTHREADSTUBS_LIBS@ \
+	@PCIACCESS_LIBS@ \
+	@CLOCK_LIB@
+
+libdrm_ipvr_la_SOURCES = $(LIBDRM_IPVR_FILES)
+
+ipvr_bufmgr_gem_o_CFLAGS = $(AM_CFLAGS) -c99
+
+libdrm_ipvrincludedir = ${includedir}/libdrm
+libdrm_ipvrinclude_HEADERS = $(LIBDRM_IPVR_H_FILES)
+
+# This may be interesting even outside of "make check", due to the -dump option.
+noinst_PROGRAMS = test_ioctl
+
+test_ioctl_LDADD = libdrm_ipvr.la ../libdrm.la ../intel/libdrm_intel.la
+
+pkgconfig_DATA = libdrm_ipvr.pc
diff --git a/ipvr/Makefile.sources b/ipvr/Makefile.sources
new file mode 100644
index 0000000..5103a02
--- /dev/null
+++ b/ipvr/Makefile.sources
@@ -0,0 +1,5 @@
+LIBDRM_IPVR_FILES := \
+	ipvr_bufmgr_gem.c
+
+LIBDRM_IPVR_H_FILES := \
+	ipvr_bufmgr.h
diff --git a/ipvr/ipvr_bufmgr.h b/ipvr/ipvr_bufmgr.h
new file mode 100644
index 0000000..31df82f
--- /dev/null
+++ b/ipvr/ipvr_bufmgr.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2014 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Yao Cheng <yao.cheng at intel.com>
+ *
+ */
+#ifndef IPVR_BUFMGR_H
+#define IPVR_BUFMGR_H
+#include <stdio.h>
+#include <stdint.h>
+#include <stdio.h>
+
+typedef struct _drm_ipvr_bo drm_ipvr_bo;
+typedef struct _drm_ipvr_bufmgr drm_ipvr_bufmgr;
+typedef struct _drm_ipvr_context drm_ipvr_context;
+
+struct _drm_ipvr_bo {
+    /**
+     * Size in bytes of the buffer object.
+     *
+     * The size may be larger than the size originally requested for the
+     * allocation, such as being aligned to page size.
+     */
+    size_t size;
+
+    /** rounded up size of actual allocation */
+    size_t alloc_size;
+
+    /** GEM handle of the BO */
+    uint32_t handle;
+
+    /**
+     * several buffers may share one BO (camera/RAR), and use offset to distinguish it
+     */
+    uint64_t buffer_ofs;
+
+    /**
+     * Last seen card virtual address (offset from the beginning of the
+     * aperture) for the object.  This should be used to fill relocation
+     * entries when calling drm_ipvr_gem_bo_emit_reloc()
+     */
+    uint64_t offset;
+
+    /**
+     * Virtual address for accessing the buffer data.  Only valid while
+     * mapped.
+     */
+    void *virt;
+
+    /**
+     * Generic external buffer info e.g. gfx handle on Android
+     */
+    uint64_t ext_handle;
+
+    /** Indicates the BO can be re-used or not after being recycled */
+    int reusable;
+
+    /** Buffer manager context associated with this buffer object */
+    drm_ipvr_bufmgr *bufmgr;
+};
+
+struct _drm_ipvr_bufmgr {
+    int debug;
+};
+
+struct _drm_ipvr_context
+{
+    uint32_t ctx_id;
+    uint32_t ctx_type;
+    drm_ipvr_bufmgr *bufmgr;
+};
+
+drm_ipvr_bufmgr *
+drm_ipvr_gem_bufmgr_init(int fd, int batch_size);
+
+void drm_ipvr_gem_bufmgr_destroy(drm_ipvr_bufmgr *bufmgr);
+
+int drm_ipvr_gem_bufmgr_get_device_info(drm_ipvr_bufmgr *bufmgr, uint16_t *dev_id, uint16_t *caps);
+
+/*void drm_ipvr_gem_bufmgr_trace_all_bos(drm_ipvr_bufmgr *bufmgr);*/
+
+
+drm_ipvr_bo *
+drm_ipvr_gem_bo_alloc(drm_ipvr_bufmgr *bufmgr, drm_ipvr_context *ctx,
+               const char *name, size_t size,
+               uint8_t tiling, uint8_t cache_level, uint8_t reusable);
+
+void drm_ipvr_gem_bo_reference(drm_ipvr_bo *bo);
+
+void drm_ipvr_gem_bo_unreference(drm_ipvr_bo *bo);
+
+int drm_ipvr_gem_bo_map(drm_ipvr_bo *bo, unsigned long offset, size_t size, int write_enable);
+
+int drm_ipvr_gem_bo_unmap(drm_ipvr_bo *bo);
+
+void drm_ipvr_gem_bo_wait(drm_ipvr_bo *bo);
+
+int drm_ipvr_gem_bo_emit_reloc(drm_ipvr_bo *bo, unsigned long offset,
+              drm_ipvr_bo *target_bo, unsigned long target_offset, uint32_t skip_fence);
+
+int drm_ipvr_gem_bo_exec(drm_ipvr_bo *bo, unsigned long offset, size_t len,
+        int fence_in, int *fence_out);
+
+drm_ipvr_bo *
+drm_ipvr_gem_bo_create_from_name(drm_ipvr_bufmgr *bufmgr, drm_ipvr_context *ctx,
+                  const char *name, uint32_t handle);
+
+int drm_ipvr_gem_bo_flink(drm_ipvr_bo *bo, uint32_t * name);
+
+int drm_ipvr_gem_bo_export_to_prime(drm_ipvr_bo *bo, int *prime_fd);
+
+drm_ipvr_bo *drm_ipvr_gem_bo_create_from_prime(drm_ipvr_bufmgr *bufmgr,
+                        int prime_fd, int size);
+
+int drm_ipvr_gem_bo_busy(drm_ipvr_bo *bo);
+
+int drm_ipvr_gem_bo_references(drm_ipvr_bo *bo, drm_ipvr_bo *target_bo);
+
+void drm_ipvr_gem_bo_remove_relocs(drm_ipvr_bo *bo, uint8_t recursive);
+
+/*int drm_ipvr_gem_bo_get_MB_error(drm_ipvr_bo *bo);*/
+
+drm_ipvr_context *
+drm_ipvr_gem_context_create(drm_ipvr_bufmgr *bufmgr, uint32_t ctx_type, uint32_t tiling_stride, uint32_t tiling_mode);
+
+void
+drm_ipvr_gem_context_destroy(drm_ipvr_context *ctx);
+
+#endif
diff --git a/ipvr/ipvr_bufmgr_gem.c b/ipvr/ipvr_bufmgr_gem.c
new file mode 100644
index 0000000..1f6e7c5
--- /dev/null
+++ b/ipvr/ipvr_bufmgr_gem.c
@@ -0,0 +1,1200 @@
+/**************************************************************************
+ *
+ * Copyright 2014 Intel 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ *
+ **************************************************************************/
+/*
+ * Authors: Yao Cheng <yao.cheng at intel.com>
+ *
+ */
+
+#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <xf86drm.h>
+#include <xf86atomic.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <sys/time.h>
+#include "errno.h"
+#include "libdrm.h"
+#include "libdrm_lists.h"
+#include "ipvr_bufmgr.h"
+#include "string.h"
+#include "ipvr_drm.h"
+#ifdef ANDROID
+#include <utils/Timers.h>
+#endif
+
+static int debug_level = 3;
+
+typedef uint64_t nsecs_t;
+#define VERB(fmt, ...)    _DBGPRINT(0, fmt, ##__VA_ARGS__)
+#define DBG(fmt, ...)     _DBGPRINT(1, fmt, ##__VA_ARGS__)
+#define INFO(fmt, ...)    _DBGPRINT(2, fmt, ##__VA_ARGS__)
+#define WARN(fmt, ...)    _DBGPRINT(3, fmt, ##__VA_ARGS__)
+#define ERR(fmt, ...)     _DBGPRINT(4, fmt, ##__VA_ARGS__)
+#define _DBGPRINT(level, fmt, ...) \
+    do { \
+        if (debug_level <= level) { \
+            fprintf(stderr, fmt, ##__VA_ARGS__); \
+        } \
+    } while (false);
+
+#define MAX_SELECT_TIMEOUT (10 * 1000) // 10 msec for 1MB BO
+#define COUNT_BEFORE_SELECT (5)
+
+#define IPVR_TIMEOUT_USEC 990000LL
+
+typedef struct _drm_ipvr_bo_gem drm_ipvr_bo_gem;
+
+typedef struct _drm_ipvr_reloc_target_info {
+    drm_ipvr_bo *bo;
+    uint64_t flags;
+} drm_ipvr_reloc_target;
+
+struct drm_ipvr_cache_bucket {
+    unsigned long size;
+    drmMMListHead head;
+    size_t count;
+    size_t  limit;
+};
+
+typedef struct _drm_ipvr_bufmgr_gem
+{
+    drm_ipvr_bufmgr base;
+    int fd;
+    pthread_mutex_t lock;
+    pthread_mutex_t list_lock;
+
+    struct drm_ipvr_gem_exec_object *exec_objs;
+    drm_ipvr_bo **exec_bos;
+    int exec_size;
+    int exec_count;
+    time_t time;
+    int max_relocs;
+
+    /* cache bucket of power-of-two */
+    int cache_bucket_size;
+
+    /* only cache linear BOs */
+    struct drm_ipvr_cache_bucket *cache_buckets;
+
+    /* seqno used to check BO's last operation oldness */
+    int exec_seq;
+} drm_ipvr_bufmgr_gem;
+
+typedef struct _drm_ipvr_bo_gem
+{
+    drm_ipvr_bo base;
+    const char *name;
+    /**
+     * Kenel-assigned global name for this object
+     */
+    unsigned int global_name;
+    atomic_t refcount;
+    uint32_t tiling;
+    uint32_t caching;
+    unsigned long stride;
+    atomic_t mapcount;
+    struct drm_ipvr_gem_relocation_entry *relocs;
+    /*
+     * Handle for mmap
+     */
+    uint64_t map_offset;
+    /**
+     * Array of info structs corresponding to relocs[i].target_handle etc
+     */
+    drm_ipvr_reloc_target *reloc_target_info;
+    /** Number of entries in relocs */
+    int reloc_count;
+    /**
+     * Index of the buffer within the validation list while preparing a
+     * batchbuffer execution.
+     */
+    int validate_index;
+    /**
+     * Boolean of whether this buffer has been used as a relocation
+     * target and had its size accounted for, and thus can't have any
+     * further relocations added to it.
+     */
+    bool used_as_reloc_target;
+    /*bool                        is_vmap;*/
+    bool is_prime_imported;
+    /*void                        *user_virt;*/
+
+    /** BO cache list */
+    drmMMListHead cache_head;
+
+    drm_ipvr_context *ctx;
+    int exec_seq;
+} drm_ipvr_bo_gem;
+
+static nsecs_t get_time_ns(void)
+{
+#ifdef ANDROID
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+#else
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    return tv.tv_sec * 1000000000LL + tv.tv_usec * 1000LL;
+#endif
+}
+
+static struct drm_ipvr_cache_bucket *
+drm__ipvr_cache_bucket_for_size(drm_ipvr_bufmgr *bufmgr,
+                 size_t size, uint8_t tiling)
+{
+    int i;
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem*)bufmgr;
+
+    assert(tiling == 0);
+    for (i = 0; i < bufmgr_gem->cache_bucket_size; i++) {
+        struct drm_ipvr_cache_bucket *bucket =
+            &bufmgr_gem->cache_buckets[i];
+        if (bucket->size >= size) {
+            return bucket;
+        }
+    }
+    return NULL;
+}
+
+static int
+drm__ipvr_gem_bo_free(drm_ipvr_bo *bo)
+{
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem*) bo->bufmgr;
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem *) bo;
+    struct drm_gem_close arg;
+    int ret;
+    bool fence_destroyed = false;
+
+    /* Close this object */
+    arg.handle = bo->handle;
+
+    ret = drmIoctl(bufmgr_gem->fd, DRM_IOCTL_GEM_CLOSE, &arg);
+    if (ret != 0) {
+        ERR("%s: DRM_IOCTL_GEM_CLOSE \"%s\" handle 0x%x offset 0x%lx failed: %s\n",
+            __FUNCTION__, bo_gem->name,
+            bo->handle, bo->offset, strerror(errno));
+    }
+
+    if (bo_gem->relocs)
+        free(bo_gem->relocs);
+    if (bo_gem->reloc_target_info)
+        free(bo_gem->reloc_target_info);
+
+    DBG("%s: freed buf khandle \"%s\" hnd %x, offset 0x%lx, %s\n",
+        __FUNCTION__, bo_gem->name, bo->handle, bo->offset,
+        (fence_destroyed? "fence is destroyed": "no fence to destroy"));
+
+    memset(bo_gem, 0, sizeof(*bo_gem));
+    free(bo_gem);
+    return 0;
+}
+
+drm_public int
+drm_ipvr_gem_bo_map(drm_ipvr_bo *bo, unsigned long offset, size_t size, int write_enable);
+drm_public int
+drm_ipvr_gem_bo_unmap(drm_ipvr_bo *bo);
+
+drm_public drm_ipvr_bo *
+drm_ipvr_gem_bo_alloc(drm_ipvr_bufmgr *bufmgr, drm_ipvr_context *ctx,
+               const char *name, size_t size,
+               uint8_t tiling, uint8_t cache_level, uint8_t reusable)
+{
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem*) bufmgr;
+    drm_ipvr_bo_gem *allocated_bo_gem = NULL;
+    int ret;
+    struct drm_ipvr_gem_create arg;
+    bool alloc_from_cache = false;
+    size_t alloc_size = size;
+    struct drm_ipvr_cache_bucket *bucket = NULL;
+    int bo_movings = 0;
+    int bucket_free_before, bucket_free_after;
+    nsecs_t begin_ns, end_ns;
+    nsecs_t time_wait, time_bo_moving, time_fence_destroy, time_kernel_alloc;
+    nsecs_t now;
+    time_wait = time_bo_moving = time_fence_destroy = time_kernel_alloc = 0;
+    bucket_free_before = bucket_free_after = 0;
+
+    begin_ns = get_time_ns();
+    if (reusable)
+        bucket = drm__ipvr_cache_bucket_for_size(&bufmgr_gem->base, size, tiling);
+
+    if (reusable && !bucket) {
+        reusable = false;
+        WARN("%s size %zu cannot fit into any cache bucket, set as non-reusable\n",
+            __FUNCTION__, size);
+    }
+    if (reusable && bucket) {
+        /**
+         * only expand size when it's reusable
+         */
+        alloc_size = bucket->size;
+    }
+
+    if (reusable && bucket && !DRMLISTEMPTY(&bucket->head)) {
+        VERB("%s searching cached BOs in fast mode: free_list\n", __FUNCTION__);
+        now = get_time_ns();
+        pthread_mutex_lock(&bufmgr_gem->list_lock);
+        allocated_bo_gem = DRMLISTENTRY(drm_ipvr_bo_gem,
+                      bucket->head.next, cache_head);
+        if (bufmgr_gem->exec_seq - allocated_bo_gem->exec_seq < 5) {
+            alloc_from_cache = false;
+            allocated_bo_gem = NULL;
+        }
+        else if (!drm_ipvr_gem_bo_busy(&allocated_bo_gem->base)) {
+            alloc_from_cache = true;
+            DRMLISTDELINIT(&allocated_bo_gem->cache_head);
+            bucket->count--;
+        }
+        else {
+            drm_ipvr_gem_bo_wait(&allocated_bo_gem->base);
+            alloc_from_cache = true;
+            DRMLISTDELINIT(&allocated_bo_gem->cache_head);
+            bucket->count--;
+        }
+        pthread_mutex_unlock(&bufmgr_gem->list_lock);
+        time_wait += get_time_ns() - now;
+        VERB("%s got free BO from cache\n", __FUNCTION__);
+    }
+    else {
+        VERB("%s empty free_list, alloc_from kernel\n", __FUNCTION__);
+        alloc_from_cache = false;
+    }
+
+    if (!alloc_from_cache) {
+        allocated_bo_gem = calloc(1, sizeof(*allocated_bo_gem));
+        if (!allocated_bo_gem) {
+            ERR("%s: calloc failed: %s\n", __FUNCTION__, strerror(errno));
+            return NULL;
+        }
+
+        if (reusable)
+            arg.size = alloc_size;
+        else
+            arg.size = size;
+        arg.tiling = tiling;
+        arg.cache_level = cache_level;
+
+        now = get_time_ns();
+        ret = drmCommandWriteRead(bufmgr_gem->fd,
+                   DRM_IPVR_GEM_CREATE,
+                   &arg, sizeof(arg));
+
+        if (ret != 0) {
+            ERR("%s: IOCTL GEM_CREATE failed: %d\n",
+                __FUNCTION__, ret);
+            free(allocated_bo_gem);
+            allocated_bo_gem = NULL;
+            return NULL;
+        }
+        time_kernel_alloc = get_time_ns() - now;
+
+        allocated_bo_gem->base.bufmgr = bufmgr;
+        allocated_bo_gem->base.handle = arg.handle;
+        allocated_bo_gem->tiling = arg.tiling;
+        allocated_bo_gem->caching = arg.cache_level;
+        allocated_bo_gem->stride = 0; /* FIXME: does ipvr_drv_video maintain stride/sliceHeight itself? */
+        allocated_bo_gem->base.offset = arg.mmu_offset;
+        allocated_bo_gem->base.alloc_size = arg.rounded_size;
+        allocated_bo_gem->base.size = size;
+        allocated_bo_gem->name = name;
+        allocated_bo_gem->base.virt = NULL;
+        allocated_bo_gem->map_offset = arg.map_offset;
+        allocated_bo_gem->is_prime_imported = false;
+        atomic_set(&allocated_bo_gem->mapcount, 0);
+
+        DBG("%s: allocated buf handle 0x%x from kernel, "
+            "offset 0x%lx, name \"%s\" size %lu (%lu), tiling %u, cache_level %u, bufmgr %p\n",
+            __FUNCTION__,
+            allocated_bo_gem->base.handle, allocated_bo_gem->base.offset, allocated_bo_gem->name,
+            allocated_bo_gem->base.size, allocated_bo_gem->base.alloc_size, allocated_bo_gem->tiling, allocated_bo_gem->caching, bufmgr);
+    }
+    else {
+        assert(allocated_bo_gem);
+        DBG("%s: allocated buf handle 0x%x from userspace cache, "
+            "offset 0x%lx, name \"%s\" size %lu (%lu), tiling %u, cache_level %u, bufmgr %p\n",
+            __FUNCTION__,
+            allocated_bo_gem->base.handle, allocated_bo_gem->base.offset, allocated_bo_gem->name,
+            allocated_bo_gem->base.size, allocated_bo_gem->base.alloc_size, allocated_bo_gem->tiling, allocated_bo_gem->caching, bufmgr);
+    }
+
+    allocated_bo_gem->name = name;
+    allocated_bo_gem->base.size = size;
+    allocated_bo_gem->validate_index = -1;
+    allocated_bo_gem->ctx = ctx;
+    allocated_bo_gem->base.reusable = reusable;
+    allocated_bo_gem->exec_seq = -1;
+
+    atomic_set(&allocated_bo_gem->refcount, 1);
+    end_ns = get_time_ns();
+    if (bucket) {
+        bucket_free_after = bucket->count;
+    }
+    assert(atomic_read(&allocated_bo_gem->mapcount) == 0);
+
+    if (end_ns - begin_ns > 2 * 1000 * 1000) {
+        DBG("%s got from %s: \"%s\" hnd %u offset 0x%lx size %lu took %.2f ms, "
+            "(wait %.2f, "
+            "bo_moving %.2f, kernel_alloc %.2f), "
+            "bucket (%d/%zu)=>(%d/%zu)\n",
+            __FUNCTION__, (alloc_from_cache? "CACHE": "KERNEL"),
+            allocated_bo_gem->name, allocated_bo_gem->base.handle, allocated_bo_gem->base.offset,
+            allocated_bo_gem->base.alloc_size, (end_ns - begin_ns)/1000000.0,
+            time_wait/1000000.0,
+            bo_movings/1000000.0, time_kernel_alloc/1000000.0,
+            bucket_free_before, (bucket? bucket->limit: 0),
+            bucket_free_after, (bucket? bucket->limit: 0));
+    }
+    return &allocated_bo_gem->base;
+}
+
+drm_public void drm_ipvr_gem_bo_unreference(drm_ipvr_bo *bo);
+
+static void
+drm__ipvr_gem_bo_remove_relocs(drm_ipvr_bo *bo, int depth, bool recursive)
+{
+    int i;
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem*)bo;
+    drm_ipvr_bo_gem *target_bo_gem;
+    for (i = 0; i < bo_gem->reloc_count; i++) {
+        if (bo_gem->reloc_target_info[i].bo != bo) {
+            target_bo_gem = (drm_ipvr_bo_gem*)bo_gem->reloc_target_info[i].bo;
+            VERB("%s (depth: %d) remove \"%s\" hnd %x off 0x%lx => \"%s\" hnd %x off 0x%lx\n",
+                __FUNCTION__, depth, bo_gem->name, bo_gem->base.handle, bo->offset,
+                target_bo_gem->name, target_bo_gem->base.handle, target_bo_gem->base.offset);
+            if (recursive)
+                drm__ipvr_gem_bo_remove_relocs(bo_gem->reloc_target_info[i].bo, depth + 1, true);
+            drm_ipvr_gem_bo_unreference(bo_gem->reloc_target_info[i].bo);
+        }
+        bo_gem->reloc_target_info[i].bo = NULL;
+        memset(&bo_gem->relocs[i], 0, sizeof(bo_gem->relocs[i]));
+    }
+    bo_gem->reloc_count = 0;
+}
+
+
+drm_public void
+drm_ipvr_gem_bo_remove_relocs(drm_ipvr_bo *bo, uint8_t recursive)
+{
+    drm__ipvr_gem_bo_remove_relocs(bo, 0, recursive);
+}
+
+drm_public void
+drm_ipvr_gem_bo_reference(drm_ipvr_bo *bo)
+{
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem *)bo;
+    atomic_inc(&bo_gem->refcount);
+    VERB("%s \"%s\" hnd 0x%x offset 0x%lx, refcount became %d\n",
+        __FUNCTION__, bo_gem->name, bo->handle, bo->offset, atomic_read(&bo_gem->refcount));
+}
+
+static void
+drm__ipvr_gem_bo_finalize(drm_ipvr_bo *bo)
+{
+    struct drm_ipvr_cache_bucket *bucket;
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem*)bo;
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem*)bo->bufmgr;
+    int mapcount;
+    /* add lock? */
+    /* Automatically unreference all the target buffers */
+    drm_ipvr_gem_bo_remove_relocs(bo, false);
+    mapcount = atomic_read(&bo_gem->mapcount);
+    if (mapcount != 0)
+        WARN("%s BO is finalized with mapcount %d\n", __FUNCTION__, mapcount);
+
+    assert(atomic_read(&bo_gem->mapcount) == 0);
+
+    bo_gem->validate_index = -1;
+
+    bucket = drm__ipvr_cache_bucket_for_size(bo->bufmgr, bo->size, bo_gem->tiling);
+
+    /* Put the buffer into our internal cache for reuse if we can. */
+    if (bo->reusable && bucket != NULL) {
+        bo_gem->validate_index = -1;
+        DBG("%s adding bo \"%s\" hnd %x (offset 0x%lx) to internal cache\n",
+            __FUNCTION__, bo_gem->name, bo->handle, bo->offset);
+
+        pthread_mutex_lock(&bufmgr_gem->list_lock);
+        DRMLISTADDTAIL(&bo_gem->cache_head, &bucket->head);
+        bucket->count++;
+
+        pthread_mutex_unlock(&bufmgr_gem->list_lock);
+    } else {
+        VERB("%s freeing bo \"%s\" hnd %x (offset 0x%lx)\n",
+            __FUNCTION__, bo_gem->name, bo->handle, bo->offset);
+        drm__ipvr_gem_bo_free(bo);
+    }
+}
+
+drm_public void
+drm_ipvr_gem_bo_unreference(drm_ipvr_bo *bo)
+{
+    drm_ipvr_bo_gem * bo_gem = (drm_ipvr_bo_gem*)bo;
+    assert(atomic_read(&bo_gem->refcount) > 0);
+    if (atomic_dec_and_test(&bo_gem->refcount)) {
+        drm__ipvr_gem_bo_finalize(bo);
+    } else {
+        VERB("%s \"%s\" handle 0x%x offset 0x%lx, refcount became %d\n",
+            __FUNCTION__, bo_gem->name, bo->handle, bo->offset, atomic_read(&bo_gem->refcount));
+    }
+}
+
+drm_public int
+drm_ipvr_gem_bo_map(drm_ipvr_bo *bo, unsigned long offset,
+            size_t size, int write_enable)
+{
+    int ret;
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem *)bo->bufmgr;
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem*)bo;
+    struct drm_ipvr_gem_mmap_offset arg;
+
+    assert(atomic_read(&bo_gem->mapcount) >= 0);
+    /* maintain userspace mapping cache, to avoid unnecessary IOCTLs */
+    if (atomic_read(&bo_gem->mapcount) == 0 && !bo->virt) {
+        /* no need to call synccpu-grab, kernel do it automatically */
+        VERB("Calling GEM_MMAP with handle %u offset 0x%"PRIx64", size %"PRIu64"\n",
+            bo->handle, offset, bo->size);
+
+        if (!bo_gem->map_offset) {
+            memset(&arg, 0, sizeof(arg));
+            arg.handle = bo->handle;
+            ret = drmCommandWriteRead(bufmgr_gem->fd, DRM_IPVR_GEM_MMAP_OFFSET,
+                &arg, sizeof(arg));
+            if (ret < 0) {
+                ERR("%s: failed to get mmap offset at bo \"%s\" 0x%x: %d (%s)\n",
+                    __FUNCTION__, bo_gem->name, bo->handle, ret, strerror(ret));
+                return ret;
+            }
+        }
+        bo->virt = drm_mmap(0, bo->alloc_size, PROT_READ | PROT_WRITE,
+                   MAP_SHARED, bufmgr_gem->fd, bo_gem->map_offset);
+        if (bo->virt == MAP_FAILED) {
+            bo->virt = NULL;
+            return -errno;
+        }
+
+        VERB("%s: map for first time, \"%s\" hnd 0x%x (offset 0x%lx) (%s) -> %p\n",
+            __FUNCTION__, bo_gem->name, bo->handle, bo->offset, bo_gem->name, bo->virt);
+    }
+    else {
+        VERB("%s: already mapped, \"%s\" hnd 0x%x (offset 0x%lx) (%s) -> %p\n",
+            __FUNCTION__, bo_gem->name, bo->handle, bo->offset, bo_gem->name, bo->virt);
+    }
+    atomic_inc(&bo_gem->mapcount);
+    assert(bo->virt);
+    VERB("%s: map_count became %d\n",
+        __FUNCTION__, atomic_read(&bo_gem->mapcount));
+
+    return 0;
+}
+
+drm_public int
+drm_ipvr_gem_bo_unmap(drm_ipvr_bo *bo)
+{
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem*)bo;
+    int mapcount;
+
+    mapcount = atomic_read(&bo_gem->mapcount);
+    if (mapcount <= 0) {
+        VERB("%s: unexpected unmap() when mapcount is %d, ignore it\n", __FUNCTION__, mapcount);
+    }
+    else if (atomic_dec_and_test(&bo_gem->mapcount)) {
+        if (bo->virt)
+            drm_munmap(bo->virt, bo->size);
+        bo->virt = NULL;
+    }
+    VERB("%s: \"%s\" hnd 0x%x offset 0x%lx map_count became %d\n",
+        __FUNCTION__, bo_gem->name, bo->handle, bo->offset, atomic_read(&bo_gem->mapcount));
+    return 0;
+}
+
+drm_public void
+drm_ipvr_gem_bo_wait(drm_ipvr_bo *bo)
+{
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem*) bo->bufmgr;
+    drm_ipvr_bo_gem * bo_gem = (drm_ipvr_bo_gem*)bo;
+    struct drm_ipvr_gem_wait arg;
+    int ret;
+    VERB("%s: wait \"%s\" hnd 0x%x offset 0x%lx\n",
+        __FUNCTION__, bo_gem->name, bo->handle, bo->offset);
+
+    arg.handle = bo->handle;
+    arg.flags = 0;
+    ret = drmCommandWriteRead(bufmgr_gem->fd, DRM_IPVR_GEM_WAIT, &arg, sizeof(arg));
+    if (ret < 0) {
+        ERR("%s \"%s\" hnd %u offset 0x%lx failed, %d (%s)\n",
+            __FUNCTION__, bo_gem->name, bo->handle, bo->offset, ret, strerror(ret));
+    }
+}
+
+static unsigned
+drm__ipvr_time_diff(struct timeval *now, struct timeval *then)
+{
+    long long val;
+
+    val = now->tv_sec - then->tv_sec;
+    val *= 1000000LL;
+    val += now->tv_usec;
+    val -= then->tv_usec;
+    if (val < 1LL)
+        val = 1LL;
+
+    return (unsigned) val;
+}
+
+static int
+drm__ipvr_setup_reloc_list(drm_ipvr_bo *bo)
+{
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem *) bo;
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem *) bo->bufmgr;
+    unsigned int max_relocs = bufmgr_gem->max_relocs;
+
+    if (bo->size / 4 < max_relocs)
+        max_relocs = bo->size / 4;
+
+    bo_gem->relocs = malloc(max_relocs *
+                sizeof(struct drm_ipvr_gem_relocation_entry));
+    bo_gem->reloc_target_info = malloc(max_relocs *
+                       sizeof(drm_ipvr_reloc_target));
+    if (bo_gem->relocs == NULL || bo_gem->reloc_target_info == NULL) {
+        ERR("%s: failed to alloc relocs and reloc_target_info\n", __FUNCTION__);
+        free (bo_gem->relocs);
+        bo_gem->relocs = NULL;
+
+        free (bo_gem->reloc_target_info);
+        bo_gem->reloc_target_info = NULL;
+
+        return 1;
+    }
+
+    return 0;
+}
+
+drm_public int
+drm_ipvr_gem_bo_emit_reloc(drm_ipvr_bo *bo, unsigned long offset,
+                drm_ipvr_bo *target_bo, unsigned long target_offset, uint32_t skip_fence)
+{
+    int ret;
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem *) bo->bufmgr;
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem *) bo;
+    drm_ipvr_bo_gem *target_bo_gem = (drm_ipvr_bo_gem *) target_bo;
+    VERB("%s::%d\n", __func__, __LINE__);
+    /* Create a new relocation list if needed */
+    if (bo_gem->relocs == NULL) {
+        ret = drm__ipvr_setup_reloc_list(bo);
+        if (ret) {
+            ERR("%s failed: relocs = %p, setup_reloc_list returns %d (%s)\n",
+                __FUNCTION__, bo_gem->relocs, ret, strerror(ret));
+            return -ENOMEM;
+        }
+    }
+
+    /* Check overflow */
+    assert(bo_gem->reloc_count < bufmgr_gem->max_relocs);
+
+    /* Check args */
+    assert(offset <= bo->size - 4);
+
+    /* Make sure that we're not adding a reloc to something whose size has
+     * already been accounted for.
+     */
+    if (target_bo_gem != bo_gem) {
+         /* todo: check it */
+    }
+    /* An object needing a fence is a tiled buffer, so it won't have
+     * relocs to other buffers.
+     */
+    bo_gem->relocs[bo_gem->reloc_count].offset = offset;
+    bo_gem->relocs[bo_gem->reloc_count].delta = target_offset;
+    bo_gem->relocs[bo_gem->reloc_count].target_handle =
+        target_bo_gem->base.handle;
+    bo_gem->relocs[bo_gem->reloc_count].presumed_offset = target_bo->offset;
+    bo_gem->reloc_target_info[bo_gem->reloc_count].bo = target_bo;
+
+    if (target_bo != bo)
+        drm_ipvr_gem_bo_reference(target_bo);
+    bo_gem->reloc_target_info[bo_gem->reloc_count].flags = skip_fence?
+        0: IPVR_EXEC_OBJECT_NEED_FENCE;
+    
+    bo_gem->reloc_count++;
+
+    VERB("%s emitted reloc: \"%s\" hnd %x (0x%lx) => \"%s\" hnd %x (0x%lx = 0x%lx+0x%lx)\n",
+        __FUNCTION__, bo_gem->name, bo->handle, bo->offset,
+        target_bo_gem->name, target_bo->handle,
+        target_bo->offset + target_offset, target_bo->offset, target_offset);
+    return 0;
+}
+
+drm_public drm_ipvr_bo *
+drm_ipvr_gem_bo_create_from_name(drm_ipvr_bufmgr *bufmgr, drm_ipvr_context *ctx,
+                  const char *name, uint32_t handle)
+{
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem *) bufmgr;
+    drm_ipvr_bo_gem *bo_gem;
+    int ret;
+    struct drm_gem_open open_arg;
+
+    open_arg.name = handle;
+    ret = drmIoctl(bufmgr_gem->fd,
+               DRM_IOCTL_GEM_OPEN,
+               &open_arg);
+    if (ret) {
+        ERR("Couldn't reference %s handle 0x%08x: %s\n",
+            name, handle, strerror(errno));
+        return NULL;
+    }
+    bo_gem = calloc(1, sizeof(*bo_gem));
+    if (!bo_gem)
+        return NULL;
+
+    bo_gem->base.size = open_arg.size;
+    bo_gem->base.offset = 0;
+    bo_gem->base.bufmgr = bufmgr;
+    bo_gem->name = name;
+    atomic_set(&bo_gem->refcount, 1);
+    atomic_set(&bo_gem->mapcount, 0);
+    bo_gem->validate_index = -1;
+    bo_gem->base.handle = open_arg.handle;
+    bo_gem->global_name = handle;
+    bo_gem->base.reusable = false;
+    DBG("bo_create_from_handle: %d (%s)\n", handle, bo_gem->name);
+
+    return &bo_gem->base;
+}
+
+drm_public int
+drm_ipvr_gem_bo_flink(drm_ipvr_bo *bo, uint32_t * name)
+{
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem *) bo->bufmgr;
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem *) bo;
+    int ret;
+    struct drm_gem_flink flink;
+    if (!bo_gem->global_name) {
+        flink.handle = bo_gem->base.handle;
+
+        ret = drmIoctl(bufmgr_gem->fd, DRM_IOCTL_GEM_FLINK, &flink);
+        if (ret != 0)
+            return -errno;
+
+        bo_gem->global_name = flink.name;
+        bo_gem->base.reusable = false;
+    }
+
+    *name = bo_gem->global_name;
+    return 0;
+}
+
+drm_public int
+drm_ipvr_gem_bo_export_to_prime(drm_ipvr_bo *bo, int *prime_fd)
+{
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem *) bo->bufmgr;
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem *) bo;
+    int ret;
+    ret = drmPrimeHandleToFD(bufmgr_gem->fd, bo_gem->base.handle,
+                   DRM_CLOEXEC, prime_fd);
+    if (ret) {
+        ERR("%s: failed calling drmPrimeHandleToFD: %d (%s)\n",
+            __FUNCTION__, ret, strerror(ret))
+        return ret;
+    }
+
+    bo_gem->base.reusable = false;
+
+    return 0;
+}
+
+drm_public drm_ipvr_bo*
+drm_ipvr_gem_bo_create_from_prime(drm_ipvr_bufmgr *bufmgr,
+                        int prime_fd, int size)
+{
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem *) bufmgr;
+    int ret;
+    uint32_t handle;
+    drm_ipvr_bo_gem *bo_gem;
+
+    ret = drmPrimeFDToHandle(bufmgr_gem->fd, prime_fd, &handle);
+    if (ret) {
+        ERR("%s: failed calling drmPrimeFDToHandle: %d (%s)\n",
+            __FUNCTION__, ret, strerror(ret))
+        return NULL;
+    }
+
+    /*
+     * See if the kernel has already returned this buffer to us. Just as
+     * for named buffers, we must not create two bo's pointing at the same
+     * kernel object
+     */
+    pthread_mutex_lock(&bufmgr_gem->lock);
+
+    bo_gem = calloc(1, sizeof(*bo_gem));
+    if (!bo_gem) {
+        pthread_mutex_unlock(&bufmgr_gem->lock);
+        return NULL;
+    }
+    /* Determine size of bo.  The fd-to-handle ioctl really should
+     * return the size, but it doesn't.  If we have kernel 3.12 or
+     * later, we can lseek on the prime fd to get the size.  Older
+     * kernels will just fail, in which case we fall back to the
+     * provided (estimated or guess size). */
+    ret = lseek(prime_fd, 0, SEEK_END);
+    if (ret != -1) {
+        bo_gem->base.size = ret;
+        bo_gem->base.alloc_size = ret;
+    }
+    else {
+        bo_gem->base.size = size;
+        bo_gem->base.alloc_size = size;
+    }
+
+    bo_gem->base.bufmgr = bufmgr;
+    bo_gem->base.handle = handle;
+    bo_gem->tiling = 0;
+    bo_gem->caching = 0;
+    bo_gem->stride = 0; /* FIXME: does ipvr_drv_video maintain stride/sliceHeight itself? */
+    bo_gem->name = "prime";
+    bo_gem->base.virt = NULL;
+    bo_gem->map_offset = 0;
+    atomic_set(&bo_gem->mapcount, 0);
+    atomic_set(&bo_gem->refcount, 1);
+    bo_gem->validate_index = -1;
+    bo_gem->ctx = NULL;
+    bo_gem->base.reusable = false;
+    bo_gem->exec_seq = -1;
+    bo_gem->is_prime_imported = true;
+
+    pthread_mutex_unlock(&bufmgr_gem->lock);
+
+    return &bo_gem->base;
+}
+
+static int
+drm__ipvr_add_validate_buffer(drm_ipvr_bo *bo, uint64_t flags)
+{
+    drm_ipvr_bufmgr_gem *bufmgr_gem = NULL;
+    drm_ipvr_bo_gem *bo_gem = NULL;
+    int index;
+
+    if (!bo) {
+        ERR("%s, invalid bo %p\n", __FUNCTION__, bo);
+        return -EINVAL;
+    }
+    bo_gem = (drm_ipvr_bo_gem *) bo;
+    if (!(bo->bufmgr)) {
+        ERR("%s, invalid bo bufmgr %p for bo \"%s\" hnd %x (off 0x%lx)\n",
+            __FUNCTION__, bo->bufmgr, bo_gem->name, bo->handle, bo->offset);
+        return -EINVAL;
+    }
+
+    bufmgr_gem = (drm_ipvr_bufmgr_gem *)bo->bufmgr;
+
+    if (bo_gem->validate_index != -1) {
+        if (!(bufmgr_gem->exec_objs[bo_gem->validate_index].flags & IPVR_EXEC_OBJECT_NEED_FENCE)
+            && (flags & IPVR_EXEC_OBJECT_NEED_FENCE)) {
+            VERB("%s: already has validate_index %d, update flags: 0x%llx->0x%llx\n",
+                __FUNCTION__, bo_gem->validate_index,
+                bufmgr_gem->exec_objs[bo_gem->validate_index].flags,
+                IPVR_EXEC_OBJECT_NEED_FENCE | bufmgr_gem->exec_objs[bo_gem->validate_index].flags);
+            bufmgr_gem->exec_objs[bo_gem->validate_index].flags |= IPVR_EXEC_OBJECT_NEED_FENCE;
+        }
+        return 0;
+    }
+
+    /* Extend the array of validation entries as necessary. */
+    if (bufmgr_gem->exec_count == bufmgr_gem->exec_size) {
+        int new_size = bufmgr_gem->exec_size * 2;
+
+        if (new_size == 0)
+            new_size = 10;
+
+        bufmgr_gem->exec_objs =
+            realloc(bufmgr_gem->exec_objs,
+                sizeof(*bufmgr_gem->exec_objs) * new_size);
+        bufmgr_gem->exec_bos =
+            realloc(bufmgr_gem->exec_bos,
+                sizeof(*bufmgr_gem->exec_bos) * new_size);
+        bufmgr_gem->exec_size = new_size;
+    }
+
+    if (!bufmgr_gem->exec_objs || !bufmgr_gem->exec_bos) {
+        ERR("%s val_args = %p and exec_bos = %p\n",
+            __FUNCTION__, bufmgr_gem->exec_objs, bufmgr_gem->exec_bos);
+        return -ENOMEM;
+    }
+
+    index = bufmgr_gem->exec_count;
+    bo_gem->validate_index = index;
+    /* Fill in array entry */
+    if (bufmgr_gem->exec_objs && bufmgr_gem->exec_bos) {
+        bufmgr_gem->exec_objs[index].offset = bo->offset;
+        bufmgr_gem->exec_objs[index].handle = bo_gem->base.handle;
+        bufmgr_gem->exec_objs[index].flags = flags;
+        bufmgr_gem->exec_objs[index].relocs_ptr = (uintptr_t)bo_gem->relocs;
+        bufmgr_gem->exec_objs[index].relocation_count = bo_gem->reloc_count;
+        bufmgr_gem->exec_bos[index] = bo;
+
+        VERB("%s added validate buffer \"%s\" hnd %x (off 0x%lx) at [%d]\n",
+            __FUNCTION__, bo_gem->name, bo->handle, bo->offset, index);
+        bufmgr_gem->exec_count++;
+    }
+    return 0;
+}
+
+static int
+drm__ipvr_bo_process_reloc(drm_ipvr_bo *bo, uint64_t flags)
+{
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem *)bo;
+    int i;
+    int ret = 0;
+
+    /* Add the target to the validate list */
+    ret = drm__ipvr_add_validate_buffer(bo, flags);
+    if (ret != 0)
+        return ret;
+
+    if (bo_gem->relocs == NULL)
+        return 0;
+
+    for (i = 0; i < bo_gem->reloc_count; i++) {
+        drm_ipvr_bo *target_bo = bo_gem->reloc_target_info[i].bo;
+        uint64_t target_flags = bo_gem->reloc_target_info[i].flags;
+        if (target_bo == bo)
+            continue;
+
+        /* Continue walking the tree depth-first. */
+        ret = drm__ipvr_bo_process_reloc(target_bo, target_flags);
+        if (ret != 0)
+            return ret;
+    }
+
+    return ret;
+}
+
+drm_public int
+drm_ipvr_gem_bo_exec(drm_ipvr_bo *bo, unsigned long offset, size_t len,
+        int fence_in, int *fence_out)
+{
+    int ret;
+    int i;
+    struct timeval then, now;
+    bool have_then = false;
+    struct drm_ipvr_gem_execbuffer exec_arg;
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem *)bo;
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem*)bo->bufmgr;
+
+    pthread_mutex_lock(&bufmgr_gem->lock);
+
+    ret = drm__ipvr_bo_process_reloc(bo,
+        IPVR_EXEC_OBJECT_NEED_FENCE | IPVR_EXEC_OBJECT_SUBMIT);
+    if (ret != 0) {
+        ERR("%s bo_process_reloc2 failed: %d (%s)\n",
+            __FUNCTION__, ret, strerror(ret));
+        pthread_mutex_unlock(&bufmgr_gem->lock);
+        return ret;
+    }
+
+    VERB("%s finished the reloc/validate processing. exec_count=%d\n",
+        __FUNCTION__, bufmgr_gem->exec_count);
+    exec_arg.exec_start_offset = offset;
+    exec_arg.exec_len = len;
+    exec_arg.buffers_ptr = (uintptr_t)bufmgr_gem->exec_objs;
+    exec_arg.buffer_count = bufmgr_gem->exec_count;
+    exec_arg.ctx_id = bo_gem->ctx->ctx_id;
+    VERB("%s sending EXEC IOCTL to kernel with: cmdbuf_handle %x (0x%lx), size %zu, "
+        "buffer_list_count %u, ctx_id 0x%08x\n", __FUNCTION__, bo->handle,
+        bo->offset, len,
+        exec_arg.buffer_count, exec_arg.ctx_id);
+
+    bufmgr_gem->exec_seq ++;
+    do {
+        ret = drmCommandWriteRead(bufmgr_gem->fd,
+                   DRM_IPVR_GEM_EXECBUFFER,
+                   &exec_arg, sizeof(exec_arg));
+        if (ret == EAGAIN) {
+            if (!have_then) {
+                if (gettimeofday(&then, NULL)) {
+                    ERR("%s have no then, gettimeofday error.\n", __FUNCTION__);
+                    break;
+                }
+
+                have_then = true;
+            }
+            if (gettimeofday(&now, NULL)) {
+                ERR("%s: Gettimeofday error.\n", __FUNCTION__);
+                break;
+            }
+
+        }
+    } while ((ret == EAGAIN) && (drm__ipvr_time_diff(&now, &then) < IPVR_TIMEOUT_USEC));
+
+    if (ret) {
+        WARN("%s: command write return is %d\n", __FUNCTION__, ret);
+        goto out;
+    }
+
+    /* update the presumed_mmu_offsets
+     * in case we support eviction in IPVR driver later */
+    for (i = 0; i < bufmgr_gem->exec_count; i++) {
+        drm_ipvr_bo *bo = bufmgr_gem->exec_bos[i];
+        drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem *)bo;
+
+        /* Update the buffer offset to accelerate future exec */
+        if (bufmgr_gem->exec_objs[i].offset != bo->offset) {
+            VERB("BO \"%s\" hnd %x migrated: 0x%"PRIx64" -> 0x%llx\n",
+                bo_gem->name, bo->handle, bo->offset,
+                bufmgr_gem->exec_objs[i].offset);
+            bo->offset = bufmgr_gem->exec_objs[i].offset;
+        }
+        bo_gem->exec_seq = bufmgr_gem->exec_seq;
+    }
+
+    VERB("%s EXECBUFFER IOCTL succeeded\n", __FUNCTION__);
+out:
+    for (i = 0; i < bufmgr_gem->exec_count; i++) {
+        drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem*)bufmgr_gem->exec_bos[i];
+
+        /* Disconnect the buffer from the validate list */
+        bo_gem->validate_index = -1;
+        bufmgr_gem->exec_bos[i] = NULL;
+    }
+
+    /* autmatically decrease all bo's refcount in the reloc tree.
+     * VA created bo will be cached if unreferencing in vaRenderPicture.
+     * execbuf/mtxmsg/surface/colocated_buffer's refcount will become 1.
+     */
+    drm_ipvr_gem_bo_remove_relocs(bo, 1);
+
+    bufmgr_gem->exec_count = 0;
+    pthread_mutex_unlock(&bufmgr_gem->lock);
+    return 0;
+}
+
+drm_public int
+drm_ipvr_gem_bo_busy(drm_ipvr_bo *bo)
+{
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem*)bo->bufmgr;
+    drm_ipvr_bo_gem *bo_gem = (drm_ipvr_bo_gem *) bo;
+    struct drm_ipvr_gem_busy arg;
+    int ret;
+
+    arg.handle = bo->handle;
+
+    ret = drmCommandWriteRead(bufmgr_gem->fd, DRM_IPVR_GEM_BUSY, &arg, sizeof(arg));
+    if (ret == -EBUSY || (ret == 0 && arg.busy)) {
+        VERB("%s returns %d: buffer \"%s\" hnd %x (offset 0x%lx) param is busy\".\n",
+            __FUNCTION__, ret, bo_gem->name, bo->handle, bo->offset);
+        return 1;
+    }
+    else if (!ret && !arg.busy){
+        VERB("%s: buffer \"%s\" hnd %x (offset 0x%lx) param is free\".\n",
+            __FUNCTION__, bo_gem->name, bo->handle, bo->offset);
+        return 0;
+    }
+    else {
+        WARN("%s: checking buffer busy \"%s\" hnd %u (offset 0x%lx) "
+            "got unexpected result: %d (%s), busy state=%d, view it as free.\n",
+            __FUNCTION__, bo_gem->name, bo->handle, bo->offset, ret, strerror(ret), arg.busy);
+
+        return 0;
+    }
+}
+
+drm_public void
+drm_ipvr_gem_bufmgr_destroy(drm_ipvr_bufmgr *bufmgr)
+{
+    int j;
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem*)bufmgr;
+
+    if (bufmgr_gem->exec_bos)
+        free(bufmgr_gem->exec_bos);
+    if (bufmgr_gem->exec_objs)
+        free(bufmgr_gem->exec_objs);
+
+    if (bufmgr_gem->cache_buckets) {
+        for (j = 0; j < bufmgr_gem->cache_bucket_size; j++) {
+            struct drm_ipvr_cache_bucket *bucket =
+                &bufmgr_gem->cache_buckets[j];
+
+            while (!DRMLISTEMPTY(&bucket->head)) {
+                drm_ipvr_bo_gem *bo_gem;
+
+                bo_gem = DRMLISTENTRY(drm_ipvr_bo_gem,
+                              bucket->head.next, cache_head);
+
+                DRMLISTDELINIT(&bo_gem->cache_head);
+                bucket->count--;
+                assert(bo_gem->reloc_count == 0);
+                drm__ipvr_gem_bo_free(&bo_gem->base);
+            }
+        }
+
+        free(bufmgr_gem->cache_buckets);
+    }
+
+    pthread_mutex_destroy(&bufmgr_gem->lock);
+    free(bufmgr_gem);
+}
+
+drm_public int
+drm_ipvr_gem_bo_references(drm_ipvr_bo *bo, drm_ipvr_bo *target_bo)
+{
+    return -1;
+}
+
+drm_public void
+drm_ipvr_gem_context_destroy(drm_ipvr_context *ctx)
+{
+    int ret;
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem*)ctx->bufmgr;
+    struct drm_ipvr_context_destroy arg;
+    arg.ctx_id = ctx->ctx_id;
+
+    ret = drmCommandWriteRead(bufmgr_gem->fd, DRM_IPVR_CONTEXT_DESTROY, &arg, sizeof(arg));
+    if (ret != 0) {
+        ERR("%s: Error destroying context %u: %s .\n",
+            __FUNCTION__, ctx->ctx_id, strerror(ret));
+    }
+
+    pthread_mutex_destroy(&bufmgr_gem->lock);
+    pthread_mutex_destroy(&bufmgr_gem->list_lock);
+    free(ctx);
+}
+
+drm_public drm_ipvr_context*
+drm_ipvr_gem_context_create(drm_ipvr_bufmgr *bufmgr, uint32_t ctx_type,
+                    uint32_t tiling_stride, uint32_t tiling_mode)
+{
+    int ret;
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem*)bufmgr;
+    struct drm_ipvr_context_create arg;
+    drm_ipvr_context *ctx = calloc(1, sizeof(drm_ipvr_context));
+    arg.ctx_type = ctx_type;
+    arg.tiling_stride = tiling_stride;
+    arg.tiling_scheme = tiling_mode;
+    ret = drmCommandWriteRead(bufmgr_gem->fd, DRM_IPVR_CONTEXT_CREATE, &arg, sizeof(arg));
+    if (ret != 0) {
+        ERR("%s: Error creating context %d: %s. tiling stride=%u, scheme=%u\n",
+            __FUNCTION__, ctx_type, strerror(ret),
+            arg.tiling_stride, arg.tiling_scheme);
+        free(ctx);
+        return NULL;
+    }
+    ctx->ctx_id = arg.ctx_id;
+    ctx->ctx_type = ctx_type;
+    ctx->bufmgr = bufmgr;
+
+    DBG("%s: successfully create drm context: id=%x, tiling stride=%u, scheme=%u\n",
+        __FUNCTION__, ctx->ctx_id, arg.tiling_stride, arg.tiling_scheme);
+    return ctx;
+}
+
+drm_public int
+drm_ipvr_gem_bufmgr_get_device_info(drm_ipvr_bufmgr *bufmgr, uint16_t *dev_id, uint16_t *caps)
+{
+    struct drm_ipvr_misc arg;
+    int ret;
+    drm_ipvr_bufmgr_gem *bufmgr_gem = (drm_ipvr_bufmgr_gem*)bufmgr;
+    unsigned long info;
+    arg.key = IPVR_DEVICE_INFO;
+    arg.value = (uint64_t)((unsigned long)&info);
+    ret = drmCommandWriteRead(bufmgr_gem->fd, DRM_IPVR_MISC, &arg, sizeof(arg));
+    if (ret != 0) {
+        ERR("%s: Error getting device info: %s .\n",
+            __FUNCTION__, strerror(ret));
+        return ret;
+    }
+    *dev_id = (info >> 16) & 0xffff;
+    *caps = info & 0xffff;
+    return 0;
+}
+
+drm_public drm_ipvr_bufmgr *
+drm_ipvr_gem_bufmgr_init(int fd, int batch_size)
+{
+    drm_ipvr_bufmgr_gem * bufmgr_gem = NULL;
+    int j;
+
+    bufmgr_gem = calloc(1, sizeof(*bufmgr_gem));
+    if (bufmgr_gem == NULL) {
+        ERR("bufmgr init: calloc failed: %s\n", strerror(errno));
+        return NULL;
+    }
+
+    bufmgr_gem->fd = fd;
+
+    if (pthread_mutex_init(&bufmgr_gem->lock, NULL) != 0) {
+        ERR("bufmgr init: mutex init failed: %s\n", strerror(errno));
+        free(bufmgr_gem);
+        bufmgr_gem = NULL;
+        return NULL;
+    }
+    if (pthread_mutex_init(&bufmgr_gem->list_lock, NULL) != 0) {
+        ERR("bufmgr init: mutex init failed: %s\n", strerror(errno));
+        free(bufmgr_gem);
+        bufmgr_gem = NULL;
+        return NULL;
+    }
+
+    /* Hard code one.
+     */
+    bufmgr_gem->max_relocs = 32;
+
+    bufmgr_gem->exec_seq = -1;
+
+    /* init cache buckets */
+    /* 4KB, 8KB, 16KB, 32KB, 64KB, 128KB, 256KB, 512KB, 1MB, 2MB, 4MB, 8MB, 16MB, 32MB */
+    bufmgr_gem->cache_bucket_size = 14;
+    bufmgr_gem->cache_buckets = calloc(bufmgr_gem->cache_bucket_size,
+        sizeof(struct drm_ipvr_cache_bucket));
+    if (!bufmgr_gem->cache_buckets) {
+        ERR("%s failed to allocate cache buckets\n", __FUNCTION__);
+        return NULL;
+    }
+
+    for (j = 0; j < bufmgr_gem->cache_bucket_size; ++j) {
+        DRMINITLISTHEAD(&bufmgr_gem->cache_buckets[j].head);
+        bufmgr_gem->cache_buckets[j].size = 0x1000 << j;
+        bufmgr_gem->cache_buckets[j].limit = 2 << ((14 - j)/2);
+        bufmgr_gem->cache_buckets[j].count = 0;
+    }
+
+    DBG("%s created bufmgr %p with cache_bucket_size %u\n",
+        __FUNCTION__, &bufmgr_gem->base, bufmgr_gem->cache_bucket_size);
+
+    return &bufmgr_gem->base;
+}
diff --git a/ipvr/libdrm_ipvr.pc.in b/ipvr/libdrm_ipvr.pc.in
new file mode 100644
index 0000000..2fe9ab6
--- /dev/null
+++ b/ipvr/libdrm_ipvr.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_ipvr
+Description: Userspace interface to VP8 hardware decoder on baytrail
+Version: @PACKAGE_VERSION@
+Requires: libdrm
+Libs: -L${libdir} -ldrm_ipvr
+Cflags: -I${includedir} -I${includedir}/libdrm
diff --git a/ipvr/test_ioctl.c b/ipvr/test_ioctl.c
new file mode 100644
index 0000000..976c244
--- /dev/null
+++ b/ipvr/test_ioctl.c
@@ -0,0 +1,423 @@
+/**************************************************************************
+ * test_ioctl.c: it is gem ioctl unit test
+ *
+ * Copyright (c) 2014 Intel Corporation, Hillsboro, OR, USA
+ * All Rights Reserved.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ *    Yao Cheng <yao.cheng at intel.com>
+ *
+ **************************************************************************/
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include "ipvr_drm.h"
+#include "i915_drm.h"
+#include "../intel/intel_bufmgr.h"
+#include <xf86drm.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+#include <libdrm.h>
+
+#define exiterr(ret, fmt, ...) \
+    do { \
+        fprintf(stderr, fmt, ##__VA_ARGS__); \
+        exit(ret); \
+    } while (0);
+
+#define check_ret(ret) \
+    do { \
+        if (ret) { \
+            exiterr(ret, "failed at %s::%d: %d (%s)\n", __FILE__, __LINE__, ret, strerror(ret)); \
+        } \
+    } while (0);
+#define check_addr(addr) \
+    do { \
+        if (!addr) { \
+            exiterr(-1, "failed at %s::%d: NULL address\n", __FILE__, __LINE__); \
+        } \
+    } while (0);
+
+static unsigned char msg_data[] = {
+    0x14,0x81,0x80,0x01,0x11,0x49,0x85,0x00,0x00,0xd0,0x5b,0x00,0x95,0x70,0xee,0x31,
+    0x00,0x10,0x21,0x04
+};
+
+static void basic_test(int fd)
+{
+    struct drm_ipvr_gem_create create_arg;
+    struct drm_gem_close close_arg;
+    struct drm_gem_flink flink_arg;
+    struct drm_gem_open open_arg;
+    struct drm_ipvr_gem_mmap_offset map_offset_arg;
+    int i915_fd, export_fd, i915_export_fd;
+    dri_bufmgr *i915_bufmgr;
+    dri_bo *i915_bo;
+    uint32_t global_name;
+    uint32_t *vaddr;
+    uint32_t import_handle;
+    int ret;
+
+    printf("\nTesting gem create.\n");
+
+    /* create bo iotcl*/
+    memset(&create_arg, 0, sizeof(create_arg));
+    create_arg.size = 16 * 1024;
+    ret = drmCommandWriteRead(fd, DRM_IPVR_GEM_CREATE, &create_arg, sizeof(create_arg));
+    check_ret(ret);
+
+    printf("\nTest mmap.\n");
+
+    /* mmap bo ioctl*/
+    if (!create_arg.map_offset) {
+        printf("didn't get valid map_offset during BO creation. call MMAP_OFFSET\n");
+        map_offset_arg.handle = create_arg.handle;
+        ret = drmCommandWriteRead(fd, DRM_IPVR_GEM_MMAP_OFFSET, &map_offset_arg, sizeof(map_offset_arg));
+        check_ret(ret);
+    }
+    vaddr = drm_mmap(NULL, create_arg.rounded_size, PROT_READ | PROT_WRITE,
+               MAP_SHARED, fd, create_arg.map_offset);
+    if (vaddr) {
+        printf("succeed to call drm_mmap, addr_ptr is %p, writting"
+            " 0xa5a5a5a5 0xb6b6b6b6 0xc7c7c7c7 0xd8d8d8d8 into it.\n", vaddr);
+        *vaddr++ = 0xa5a5a5a5;
+        *vaddr++ = 0xb6b6b6b6;
+        *vaddr++ = 0xc7c7c7c7;
+        *vaddr++ = 0xd8d8d8d8;
+    } else
+        exiterr(-1, "failed to call DRM_IOCTL_IPVR_GEM_MMAP ioctl: %d (%s).\n", ret, strerror(ret));
+    drm_munmap(vaddr, create_arg.rounded_size);
+
+    printf("\nTest opening flinked BOs.\n");
+
+    {
+        /* flink the bo */
+        flink_arg.handle = create_arg.handle;
+        flink_arg.name = 0;
+        ret = drmIoctl(fd, DRM_IOCTL_GEM_FLINK, &flink_arg);
+        check_ret(ret);
+        global_name = flink_arg.name;
+
+        /* open from flink name */
+        open_arg.name = global_name;
+        ret = drmIoctl(fd, DRM_IOCTL_GEM_OPEN, &open_arg);
+        check_ret(ret);
+
+        if (open_arg.size != create_arg.rounded_size)
+            exiterr(-1, "opened size %llu not consistent with created size %llu\n",
+                open_arg.size, create_arg.rounded_size);
+
+        /* mmap and verify the bo content */
+        map_offset_arg.handle = open_arg.handle;
+        ret = drmCommandWriteRead(fd, DRM_IPVR_GEM_MMAP_OFFSET, &map_offset_arg, sizeof(map_offset_arg));
+        check_ret(ret);
+        /* mmap bo ioctl*/
+        vaddr = drm_mmap(NULL, open_arg.size, PROT_READ | PROT_WRITE,
+                   MAP_SHARED, fd, map_offset_arg.offset);
+        check_addr(vaddr);
+        printf("succeed to call drm_mmap, addr_ptr is %p, readding got "
+            "0x%08x 0x%08x 0x%08x 0x%08x.\n", vaddr,
+            *vaddr, *(vaddr+1), *(vaddr+2), *(vaddr+3));
+        if (*vaddr == 0xa5a5a5a5 &&
+            *(vaddr+1) == 0xb6b6b6b6 &&
+            *(vaddr+2) == 0xc7c7c7c7 &&
+            *(vaddr+3) == 0xd8d8d8d8) {
+            printf("succeed to verify opened BO content.\n");
+        }
+        else {
+            drm_munmap(vaddr, create_arg.rounded_size);
+            exiterr(-1, "failed to call DRM_IOCTL_IPVR_GEM_MMAP ioctl: %d (%s).\n", ret, strerror(ret));
+        }
+        drm_munmap(vaddr, create_arg.rounded_size);
+
+        /* close opened bo ioctl*/
+        close_arg.handle = open_arg.handle;
+        ret = drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close_arg);
+        check_ret(ret);
+    }
+
+    printf("\nTest importing i915 prime BOs.\n");
+
+    i915_fd = open("/dev/dri/card0", O_RDWR);
+    i915_bufmgr = drm_intel_bufmgr_gem_init(i915_fd, 4096);
+
+    /* test prime import */
+    {
+        i915_bo = dri_bo_alloc(i915_bufmgr, "to-be-exported", 4096, 0x1000);
+        check_addr(i915_bo);
+        ret = dri_bo_map(i915_bo, 1);
+        check_ret(ret);
+        check_addr(i915_bo->virtual);
+        vaddr = i915_bo->virtual;
+        printf("mapped i915 BO to %p and write"
+            " 0xd8d8d8d8 0xc7c7c7c7 0xb6b6b6b6 0xa5a5a5a5 into it.\n", vaddr);
+        *vaddr++ = 0xd8d8d8d8;
+        *vaddr++ = 0xc7c7c7c7;
+        *vaddr++ = 0xb6b6b6b6;
+        *vaddr++ = 0xa5a5a5a5;
+        ret = dri_bo_unmap(i915_bo);
+        check_ret(ret);
+        ret = drm_intel_bo_gem_export_to_prime(i915_bo, &i915_export_fd);
+        check_ret(ret);
+        ret = drmPrimeFDToHandle(fd, i915_export_fd, &import_handle);
+        map_offset_arg.handle = import_handle;
+        ret = drmCommandWriteRead(fd, DRM_IPVR_GEM_MMAP_OFFSET, &map_offset_arg, sizeof(map_offset_arg));
+        check_ret(ret);
+        vaddr = drm_mmap(NULL, 4096, PROT_READ | PROT_WRITE,
+                   MAP_SHARED, fd, map_offset_arg.offset);
+        check_addr(vaddr);
+        printf("succeed mapping the imported BO to %p, content is "
+            "0x%08x 0x%08x 0x%08x 0x%08x.\n", vaddr,
+            *vaddr, *(vaddr+1), *(vaddr+2), *(vaddr+3));
+        if (*vaddr == 0xd8d8d8d8 &&
+            *(vaddr+1) == 0xc7c7c7c7 &&
+            *(vaddr+2) == 0xb6b6b6b6 &&
+            *(vaddr+3) == 0xa5a5a5a5) {
+            printf("succeeded verifying imported i915 prime BO content.\n");
+        }
+        else
+            exiterr(-1, "failed to verify imported i915 prime BO content\n");
+        ret = drm_munmap(vaddr, 4096);
+        check_ret(ret);
+        close_arg.handle = import_handle;
+        ret = drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close_arg);
+        check_ret(ret);
+        close(i915_export_fd);
+        dri_bo_unreference(i915_bo);
+    }
+
+    printf("\nTest exporting prime BO and import with i915.\n");
+
+    /* test prime export */
+    {
+        ret = drmPrimeHandleToFD(fd, create_arg.handle, 0, &export_fd);
+        check_ret(ret);
+        i915_bo = drm_intel_bo_gem_create_from_prime(i915_bufmgr, export_fd, create_arg.rounded_size);
+        check_addr(i915_bo);
+        ret = drm_intel_gem_bo_map_gtt(i915_bo);
+        check_ret(ret);
+        check_addr (i915_bo->virtual);
+        vaddr = i915_bo->virtual;
+        printf("succeed to import with i915 and mapping to %p, content is "
+            "0x%08x 0x%08x 0x%08x 0x%08x.\n", vaddr,
+            *vaddr, *(vaddr+1), *(vaddr+2), *(vaddr+3));
+        if (*vaddr == 0xa5a5a5a5 &&
+            *(vaddr+1) == 0xb6b6b6b6 &&
+            *(vaddr+2) == 0xc7c7c7c7 &&
+            *(vaddr+3) == 0xd8d8d8d8) {
+            printf("succeed to verify exported prime BO content.\n");
+        }
+        else
+            exiterr(-1, "failed to verify exported prime BO content.\n");
+        ret = dri_bo_unmap(i915_bo);
+        check_ret(ret);
+        dri_bo_unreference(i915_bo);
+        close(export_fd);
+    }
+    dri_bufmgr_destroy(i915_bufmgr);
+    close(i915_fd);
+
+    /* close bo ioctl*/
+    close_arg.handle = create_arg.handle;
+    ret = drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close_arg);
+    check_ret(ret);
+
+    printf("\nExit gem basic test.\n");
+}
+
+static void exec_test(int fd)
+{
+    struct drm_ipvr_context_create ctx_create_arg;
+    struct drm_ipvr_context_destroy ctx_destroy_arg;
+    struct drm_ipvr_gem_create create_arg;
+    struct drm_ipvr_gem_execbuffer exec_arg;
+    struct drm_ipvr_gem_wait wait_arg;
+    struct drm_ipvr_gem_busy busy_arg;
+    struct drm_gem_close close_arg;
+    struct drm_ipvr_gem_relocation_entry reloc_entries[3];
+    struct drm_ipvr_gem_exec_object *exec_objs;
+    struct drm_ipvr_gem_exec_object *arg;
+    int i915_fd, i915_export_fd;
+    dri_bufmgr *i915_bufmgr;
+    dri_bo *i915_bo;
+    uint32_t import_handle;
+    uint32_t *dword;
+    uint32_t *vaddr;
+    int i, ret, buffer_count, cmdbuf_size, ctx_type;
+
+    printf("\nTest simple exec ioctl.\n");
+    i915_fd = open("/dev/dri/card0", O_RDWR);
+    i915_bufmgr = drm_intel_bufmgr_gem_init(i915_fd, 4096);
+
+    /* create a ctx_context, which is needed for exec ioctl */
+    memset(&ctx_create_arg, 0, sizeof(ctx_create_arg));
+    ctx_type = IPVR_CONTEXT_TYPE_VED;
+    ctx_create_arg.ctx_type = ctx_type;
+    ctx_create_arg.tiling_scheme = 0;
+    ctx_create_arg.tiling_stride = 0;
+    ret = drmCommandWriteRead(fd, DRM_IPVR_CONTEXT_CREATE, &ctx_create_arg, sizeof(ctx_create_arg));
+    check_ret(ret);
+
+    buffer_count = 4;
+    dword = msg_data;
+    exec_objs = calloc(1, sizeof(struct drm_ipvr_gem_exec_object) * buffer_count);
+
+    /* create 1 bo with correct mmu offset */
+    arg = &exec_objs[0];
+    memset(&create_arg, 0, sizeof(create_arg));
+    create_arg.size = 16 * 1024;
+    ret = drmCommandWriteRead(fd, DRM_IPVR_GEM_CREATE, &create_arg, sizeof(create_arg));
+    check_ret(ret);
+    arg->handle = create_arg.handle;
+    arg->offset = create_arg.mmu_offset;
+    arg->flags |= IPVR_EXEC_OBJECT_NEED_FENCE;
+    arg->relocation_count = 0;
+    arg->relocs_ptr = (uintptr_t)NULL;
+    reloc_entries[0].delta = 0;
+    reloc_entries[0].offset = 8;
+    reloc_entries[0].presumed_offset = create_arg.mmu_offset;
+    reloc_entries[0].target_handle = create_arg.handle;
+
+    /* create 1 bo with wrong mmu offset */
+    arg = &exec_objs[1];
+    memset(&create_arg, 0, sizeof(create_arg));
+    create_arg.size = 16 * 1024;
+    ret = drmCommandWriteRead(fd, DRM_IPVR_GEM_CREATE, &create_arg, sizeof(create_arg));
+    check_ret(ret);
+    arg->handle = create_arg.handle;
+    arg->offset = create_arg.mmu_offset;
+    arg->flags |= IPVR_EXEC_OBJECT_NEED_FENCE;
+    arg->relocation_count = 0;
+    arg->relocs_ptr = (uintptr_t)NULL;
+    reloc_entries[1].delta = 0;
+    reloc_entries[1].offset = 12;
+    reloc_entries[1].presumed_offset = dword[reloc_entries[1].offset/4];
+    arg->offset = reloc_entries[1].presumed_offset;
+    reloc_entries[1].target_handle = create_arg.handle;
+
+    /* import a i915 bo */
+    i915_bo = dri_bo_alloc(i915_bufmgr, "to-be-exported", 16 * 1024, 0x1000);
+    check_addr(i915_bo);
+    ret = drm_intel_bo_gem_export_to_prime(i915_bo, &i915_export_fd);
+    check_ret(ret);
+    ret = drmPrimeFDToHandle(fd, i915_export_fd, &import_handle);
+    check_ret(ret);
+    arg = &exec_objs[2];
+    arg->handle = import_handle;
+    arg->offset = 0; /* we don't know the mmu offset */
+    arg->flags |= IPVR_EXEC_OBJECT_NEED_FENCE;
+    arg->relocation_count = 0;
+    arg->relocs_ptr = (uintptr_t)NULL;
+    reloc_entries[2].delta = 0;
+    reloc_entries[2].offset = 16;
+    reloc_entries[2].presumed_offset = 0;
+    reloc_entries[2].target_handle = import_handle;
+
+    /* create a cmd bo with 1 page */
+    memset(&create_arg, 0, sizeof(create_arg));
+    cmdbuf_size = 4 * 1024;
+    create_arg.size = cmdbuf_size;
+    ret = drmCommandWriteRead(fd, DRM_IPVR_GEM_CREATE, &create_arg, sizeof(create_arg));
+    check_ret(ret);
+
+    vaddr = drm_mmap(NULL, create_arg.rounded_size, PROT_READ | PROT_WRITE,
+               MAP_SHARED, fd, create_arg.map_offset);
+    if (vaddr) {
+        printf("succeed to map CMD BO, addr_ptr is %p.\n", vaddr);
+        memcpy((void *)vaddr, msg_data, sizeof(msg_data));
+    } else
+        exiterr(-1, "failed to map CMD BO.\n");
+    drm_munmap(vaddr, create_arg.rounded_size);
+    arg = &exec_objs[3];
+    arg->handle = create_arg.handle;
+    arg->offset = create_arg.mmu_offset;
+    arg->flags |= IPVR_EXEC_OBJECT_NEED_FENCE | IPVR_EXEC_OBJECT_SUBMIT;
+    arg->relocation_count = 3;
+    arg->relocs_ptr = (uintptr_t)reloc_entries;
+
+    /* run execbuffer ioctl */
+    memset(&exec_arg, 0, sizeof(exec_arg));
+    exec_arg.buffer_count = buffer_count;
+    exec_arg.buffers_ptr = (uintptr_t)exec_objs;
+    exec_arg.exec_start_offset = 0;
+    exec_arg.exec_len = 20;
+    exec_arg.ctx_id = ctx_create_arg.ctx_id;
+
+    ret = drmCommandWriteRead(fd, DRM_IPVR_GEM_EXECBUFFER, &exec_arg, sizeof(exec_arg));
+    check_ret(ret);
+
+    /*check busy ioctl*/
+    memset(&busy_arg, 0, sizeof(busy_arg));
+    busy_arg.handle = create_arg.handle;
+    ret = drmCommandWriteRead(fd, DRM_IPVR_GEM_BUSY, &busy_arg, sizeof(busy_arg));
+    if (ret != 0 && ret != -EBUSY)
+        exiterr(ret, "failed calling DRM_IPVR_GEM_BUSY: %d (%s)\n", ret, strerror(ret));
+
+    /*wait for finish*/
+    memset(&wait_arg, 0, sizeof(wait_arg));
+    wait_arg.handle = create_arg.handle;
+    ret = drmCommandWriteRead(fd, DRM_IPVR_GEM_WAIT, &wait_arg, sizeof(wait_arg));
+    /* the random command causes PANIC */
+    check_ret(!(ret == 0) && !(ret == -EDEADLK));
+
+    /* close val bo and cmd bo */
+    memset(&close_arg, 0, sizeof(close_arg));
+    for (i = 0; i < buffer_count; i++) {
+        close_arg.handle = exec_objs[i].handle;
+        ret = drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close_arg);
+        if (ret == 0)
+            printf("succeed to call DRM_IOCTL_GEM_CLOSE ioctl.\n");
+        else
+            exiterr(-1, "failed to call DRM_IOCTL_GEM_CLOSE ioctl.\n");
+    }
+
+    /* clean up i915 */
+    close(i915_export_fd);
+    dri_bo_unreference(i915_bo);
+    dri_bufmgr_destroy(i915_bufmgr);
+    close(i915_fd);
+
+    /* destroy the ctx_context */
+    memset(&ctx_destroy_arg, 0, sizeof(ctx_destroy_arg));
+    ctx_destroy_arg.ctx_id = ctx_create_arg.ctx_id;
+    ret = drmCommandWriteRead(fd, DRM_IPVR_CONTEXT_DESTROY, &ctx_destroy_arg, sizeof(ctx_destroy_arg));
+    check_ret(ret);
+    return;
+}
+
+int main(int argc, char **argv)
+{
+    int fd;
+
+    fd = open("/dev/dri/card1", O_RDWR);
+    /* test bo create, mmap, close */
+    basic_test(fd);
+    /* simulate exec ioctl */
+    exec_test(fd);
+    close(fd);
+    return 0;
+}
-- 
2.1.0



More information about the dri-devel mailing list