Merge branch 'libdrm-imx-2.4.84' into master
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..893b7be
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,19 @@
+# To use this config with your editor, follow the instructions at:
+# http://editorconfig.org
+
+root = true
+
+[*]
+charset = utf-8
+insert_final_newline = true
+
+[*.{c,h}]
+indent_style = space
+indent_size = 4
+
+[{Makefile.*,*.mk}]
+indent_style = tab
+
+[*.m4]
+indent_style = space
+indent_size = 2
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d51e619
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,108 @@
+bsd-core/*/@
+bsd-core/*/machine
+*~
+*.1
+*.3
+*.5
+*.7
+*.flags
+*.ko
+*.ko.cmd
+*.la
+*.lo
+*.log
+*.mod.c
+*.mod.o
+*.o
+*.o.cmd
+*.sw?
+*.trs
+.depend
+.deps
+.libs
+.tmp_versions
+.*check*
+.*install*
+Makefile
+Makefile.in
+TAGS
+aclocal.m4
+autom4te.cache
+build-aux
+bus_if.h
+compile
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+configure.lineno
+cscope.*
+depcomp
+device_if.h
+drm.kld
+drm_pciids.h
+export_syms
+i915.kld
+install-sh
+libdrm/config.h.in
+libdrm.pc
+libdrm_intel.pc
+libdrm_nouveau.pc
+libdrm_radeon.pc
+libdrm_omap.pc
+libdrm_exynos.pc
+libdrm_freedreno.pc
+libdrm_amdgpu.pc
+libdrm_vc4.pc
+libdrm_etnaviv.pc
+libkms.pc
+libtool
+ltmain.sh
+mach64.kld
+man/.man_fixup
+mga.kld
+missing
+mkinstalldirs
+opt_drm.h
+pci_if.h
+r128.kld
+radeon.kld
+savage.kld
+sis.kld
+stamp-h1
+tdfx.kld
+via.kld
+tests/auth
+tests/amdgpu/amdgpu_test
+tests/dristat
+tests/drmdevice
+tests/drmsl
+tests/drmstat
+tests/getclient
+tests/getstats
+tests/getversion
+tests/hash
+tests/lock
+tests/openclose
+tests/random
+tests/setversion
+tests/updatedraw
+tests/modeprint/modeprint
+tests/modetest/modetest
+tests/name_from_fd
+tests/proptest/proptest
+tests/kms/kms-steal-crtc
+tests/kms/kms-universal-planes
+tests/kmstest/kmstest
+tests/vbltest/vbltest
+tests/radeon/radeon_ttm
+tests/exynos/exynos_fimg2d_event
+tests/exynos/exynos_fimg2d_perf
+tests/exynos/exynos_fimg2d_test
+tests/etnaviv/etnaviv_2d_test
+tests/etnaviv/etnaviv_cmd_stream_test
+tests/etnaviv/etnaviv_bo_cache_test
+man/*.3
diff --git a/Android.common.mk b/Android.common.mk
new file mode 100644
index 0000000..d487acb
--- /dev/null
+++ b/Android.common.mk
@@ -0,0 +1,17 @@
+# XXX: Consider moving these to config.h analogous to autoconf.
+LOCAL_CFLAGS += \
+	-DMAJOR_IN_SYSMACROS=1 \
+	-DHAVE_VISIBILITY=1 \
+	-DHAVE_LIBDRM_ATOMIC_PRIMITIVES=1
+
+LOCAL_CFLAGS += \
+	-Wno-unused-parameter \
+	-Wno-missing-field-initializers \
+	-Wno-pointer-arith \
+	-Wno-enum-conversion
+
+# Quiet down the build system and remove any .h files from the sources
+LOCAL_SRC_FILES := $(patsubst %.h, , $(LOCAL_SRC_FILES))
+LOCAL_EXPORT_C_INCLUDE_DIRS += $(LOCAL_PATH)
+
+LOCAL_PROPRIETARY_MODULE := true
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..e97a28a
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,64 @@
+#
+# Copyright © 2011-2012 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.
+#
+
+LIBDRM_COMMON_MK := $(call my-dir)/Android.common.mk
+
+LOCAL_PATH := $(call my-dir)
+LIBDRM_TOP := $(LOCAL_PATH)
+
+include $(CLEAR_VARS)
+
+# Import variables LIBDRM_{,H_,INCLUDE_H_,INCLUDE_VMWGFX_H_}FILES
+include $(LOCAL_PATH)/Makefile.sources
+
+#static library for the device (recovery)
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdrm_android
+
+LOCAL_SRC_FILES := $(LIBDRM_FILES)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+	$(LOCAL_PATH) \
+	$(LOCAL_PATH)/include/drm
+
+LOCAL_C_INCLUDES := \
+	$(LOCAL_PATH)/include/drm
+
+include $(LIBDRM_COMMON_MK)
+include $(BUILD_STATIC_LIBRARY)
+
+# Shared library for the device
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdrm_android
+
+LOCAL_SRC_FILES := $(LIBDRM_FILES)
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+        $(LOCAL_PATH)/include/drm
+
+LOCAL_C_INCLUDES := \
+        $(LOCAL_PATH)/include/drm
+
+LOCAL_VENDOR_MODULE := true
+include $(LIBDRM_COMMON_MK)
+include $(BUILD_SHARED_LIBRARY)
+
+include $(LOCAL_PATH)/vivante/Android.mk
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..912378f
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,15 @@
+ 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.
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..28a11db
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,4 @@
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/include/libdrm)
+$(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)
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..649160d
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,150 @@
+#  Copyright 2005 Adam Jackson.
+#
+#  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
+#  on 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 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 NON-INFRINGEMENT.  IN NO EVENT SHALL
+#  ADAM JACKSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+#  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+#  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+include Makefile.sources
+
+ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
+
+AM_MAKEFLAGS = -s
+AM_DISTCHECK_CONFIGURE_FLAGS = \
+	--enable-udev \
+	--enable-libkms \
+	--enable-intel \
+	--enable-radeon \
+	--enable-amdgpu \
+	--enable-nouveau \
+	--enable-vc4 \
+	--enable-vmwgfx \
+	--enable-omap-experimental-api \
+	--enable-exynos-experimental-api \
+	--enable-freedreno \
+	--enable-freedreno-kgsl\
+	--enable-tegra-experimental-api \
+	--enable-etnaviv-experimental-api \
+	--enable-vivante-experimental-api \
+	--enable-install-test-programs \
+	--enable-cairo-tests \
+	--enable-manpages \
+	--enable-valgrind
+
+pkgconfigdir = @pkgconfigdir@
+pkgconfig_DATA = libdrm.pc
+
+if HAVE_LIBKMS
+LIBKMS_SUBDIR = libkms
+endif
+
+if HAVE_INTEL
+INTEL_SUBDIR = intel
+endif
+
+if HAVE_NOUVEAU
+NOUVEAU_SUBDIR = nouveau
+endif
+
+if HAVE_RADEON
+RADEON_SUBDIR = radeon
+endif
+
+if HAVE_AMDGPU
+AMDGPU_SUBDIR = amdgpu
+endif
+
+if HAVE_OMAP
+OMAP_SUBDIR = omap
+endif
+
+if HAVE_EXYNOS
+EXYNOS_SUBDIR = exynos
+endif
+
+if HAVE_FREEDRENO
+FREEDRENO_SUBDIR = freedreno
+endif
+
+if HAVE_TEGRA
+TEGRA_SUBDIR = tegra
+endif
+
+if HAVE_VC4
+VC4_SUBDIR = vc4
+endif
+
+if HAVE_ETNAVIV
+ETNAVIV_SUBDIR = etnaviv
+endif
+if HAVE_VIVANTE
+VIVANTE_SUBDIR = vivante
+endif
+
+if BUILD_MANPAGES
+if HAVE_MANPAGES_STYLESHEET
+MAN_SUBDIR = man
+endif
+endif
+
+SUBDIRS = \
+	. \
+	$(LIBKMS_SUBDIR) \
+	$(INTEL_SUBDIR) \
+	$(NOUVEAU_SUBDIR) \
+	$(RADEON_SUBDIR) \
+	$(AMDGPU_SUBDIR) \
+	$(OMAP_SUBDIR) \
+	$(EXYNOS_SUBDIR) \
+	$(FREEDRENO_SUBDIR) \
+	$(TEGRA_SUBDIR) \
+	$(VC4_SUBDIR) \
+	$(VIVANTE_SUBDIR) \
+	$(ETNAVIV_SUBDIR) \
+	data \
+	tests \
+	$(MAN_SUBDIR)
+
+libdrm_la_LTLIBRARIES = libdrm.la
+libdrm_ladir = $(libdir)
+libdrm_la_LDFLAGS = -version-number 2:4:0 -no-undefined
+libdrm_la_LIBADD = @CLOCK_LIB@ -lm
+
+libdrm_la_CPPFLAGS = -I$(top_srcdir)/include/drm
+AM_CFLAGS = \
+	$(WARN_CFLAGS) \
+	$(VALGRIND_CFLAGS)
+
+libdrm_la_SOURCES = $(LIBDRM_FILES)
+
+libdrmincludedir = ${includedir}
+libdrminclude_HEADERS = $(LIBDRM_H_FILES)
+
+klibdrmincludedir = ${includedir}/libdrm
+klibdrminclude_HEADERS = $(LIBDRM_INCLUDE_H_FILES)
+
+if HAVE_VMWGFX
+klibdrminclude_HEADERS += $(LIBDRM_INCLUDE_VMWGFX_H_FILES)
+endif
+
+EXTRA_DIST = include/drm/README
+
+copy-headers :
+	cp -r $(kernel_source)/include/uapi/drm/*.h $(top_srcdir)/include/drm/
+
+commit-headers : copy-headers
+	git add include/drm/*.h
+	git commit -am "Copy headers from kernel $$(GIT_DIR=$(kernel_source)/.git git describe)"
diff --git a/Makefile.sources b/Makefile.sources
new file mode 100644
index 0000000..88d4f86
--- /dev/null
+++ b/Makefile.sources
@@ -0,0 +1,42 @@
+LIBDRM_FILES := \
+	xf86drm.c \
+	xf86drmHash.c \
+	xf86drmHash.h \
+	xf86drmRandom.c \
+	xf86drmRandom.h \
+	xf86drmSL.c \
+	xf86drmMode.c \
+	xf86atomic.h \
+	libdrm_macros.h \
+	libdrm_lists.h \
+	util_double_list.h \
+	util_math.h
+
+LIBDRM_H_FILES := \
+	libsync.h \
+	xf86drm.h \
+	xf86drmMode.h
+
+LIBDRM_INCLUDE_H_FILES := \
+	include/drm/drm.h \
+	include/drm/drm_fourcc.h \
+	include/drm/drm_mode.h \
+	include/drm/drm_sarea.h \
+	include/drm/i915_drm.h \
+	include/drm/mach64_drm.h \
+	include/drm/mga_drm.h \
+	include/drm/nouveau_drm.h \
+	include/drm/qxl_drm.h \
+	include/drm/r128_drm.h \
+	include/drm/radeon_drm.h \
+	include/drm/amdgpu_drm.h \
+	include/drm/savage_drm.h \
+	include/drm/sis_drm.h \
+	include/drm/tegra_drm.h \
+	include/drm/vc4_drm.h \
+	include/drm/vivante_drm.h \
+	include/drm/via_drm.h \
+	include/drm/virtgpu_drm.h
+
+LIBDRM_INCLUDE_VMWGFX_H_FILES := \
+	include/drm/vmwgfx_drm.h
diff --git a/README b/README
new file mode 100644
index 0000000..26cab9d
--- /dev/null
+++ b/README
@@ -0,0 +1,41 @@
+libdrm - userspace library for drm
+
+This  is libdrm,  a userspace  library for  accessing the  DRM, direct
+rendering  manager, on  Linux,  BSD and  other  operating systems that
+support the  ioctl interface.  The library  provides wrapper functions
+for the  ioctls to avoid  exposing the kernel interface  directly, and
+for chipsets with drm memory manager, support for tracking relocations
+and  buffers.   libdrm  is  a  low-level library,  typically  used  by
+graphics drivers  such as the Mesa  DRI drivers, the  X drivers, libva
+and  similar projects.  New  functionality in  the kernel  DRM drivers
+typically requires  a new  libdrm, but a  new libdrm will  always work
+with an older kernel.
+
+
+Compiling
+---------
+
+libdrm  is  a  standard  autotools  package and  follows  the  normal
+configure, build  and install steps.   The first step is  to configure
+the package, which is done by running the configure shell script:
+
+	./configure
+
+By default, libdrm  will install into the /usr/local/  prefix.  If you
+want  to  install   this  DRM  to  replace  your   system  copy,  pass
+--prefix=/usr and  --exec-prefix=/ to configure.  If  you are building
+libdrm  from a  git checkout,  you first  need to  run  the autogen.sh
+script.  You can  pass any options to autogen.sh  that you would other
+wise  pass to configure,  or you  can just  re-run configure  with the
+options you need once autogen.sh finishes.
+
+Next step is to build libdrm:
+
+	make
+
+and once make finishes successfully, install the package using
+
+	make install
+
+If you are installing into a system location, you will need to be root
+to perform the install step.
diff --git a/RELEASING b/RELEASING
new file mode 100644
index 0000000..262ca08
--- /dev/null
+++ b/RELEASING
@@ -0,0 +1,51 @@
+The release criteria for libdrm is essentially "if you need a release,
+make one".  There is no designated release engineer or maintainer.
+Anybody is free to make a release if there's a certain feature or bug
+fix they need in a released version of libdrm.
+
+When new ioctl definitions are merged into drm-next, we will add
+support to libdrm, at which point we typically create a new release.
+However, this is up to whoever is driving the feature in question.
+
+Follow these steps to release a new version of libdrm:
+
+  1) Bump the version number in configure.ac. We seem to have settled
+     for 2.4.x as the versioning scheme for libdrm, so just bump the
+     micro version.
+
+  2) Run autoconf and then re-run ./configure so the build system
+     picks up the new version number.
+
+  3) Verify that the code passes "make distcheck".  Running "make
+     distcheck" should result in no warnings or errors and end with a
+     message of the form:
+
+	=============================================
+	libdrm-X.Y.Z archives ready for distribution:
+	libdrm-X.Y.Z.tar.gz
+	libdrm-X.Y.Z.tar.bz2
+	=============================================
+
+     Make sure that the version number reported by distcheck and in
+     the tarball names matches the number you bumped to in configure.ac.
+
+  4) Push the updated master branch with the bumped version number:
+
+	git push origin master
+
+     assuming the remote for the upstream libdrm repo is called origin.
+
+  5) Use the release.sh script from the xorg/util/modular repo to
+     upload the tarballs to the freedesktop.org download area and
+     create an announce email template.  The script takes one argument:
+     the path to the libdrm checkout. So, if a checkout of modular is
+     at the same level than the libdrm repo:
+
+	./modular/release.sh libdrm
+
+     This copies the two tarballs to freedesktop.org and creates
+     libdrm-2.4.16.announce which has a detailed summary of the
+     changes, links to the tarballs, MD5 and SHA1 sums and pre-filled
+     out email headers.  Fill out the blank between the email headers
+     and the list of changes with a brief message of what changed or
+     what prompted this release.  Send out the email and you're done!
diff --git a/amdgpu/.editorconfig b/amdgpu/.editorconfig
new file mode 100644
index 0000000..2528d67
--- /dev/null
+++ b/amdgpu/.editorconfig
@@ -0,0 +1,9 @@
+# To use this config with your editor, follow the instructions at:
+# http://editorconfig.org
+
+[*]
+charset = utf-8
+indent_style = tab
+indent_size = 8
+tab_width = 8
+insert_final_newline = true
diff --git a/amdgpu/Android.mk b/amdgpu/Android.mk
new file mode 100644
index 0000000..ce27301
--- /dev/null
+++ b/amdgpu/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Import variables LIBDRM_AMDGPU_FILES, LIBDRM_AMDGPU_H_FILES
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_MODULE := libdrm_amdgpu
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+LOCAL_SRC_FILES := $(LIBDRM_AMDGPU_FILES)
+
+LOCAL_CFLAGS := \
+	-DAMDGPU_ASIC_ID_TABLE=\"/vendor/etc/hwdata/amdgpu.ids\" \
+	-DAMDGPU_ASIC_ID_TABLE_NUM_ENTRIES=$(shell egrep -ci '^[0-9a-f]{4},.*[0-9a-f]+,' $(LIBDRM_TOP)/data/amdgpu.ids)
+
+LOCAL_REQUIRED_MODULES := amdgpu.ids
+
+include $(LIBDRM_COMMON_MK)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/amdgpu/Makefile.am b/amdgpu/Makefile.am
new file mode 100644
index 0000000..66f6f67
--- /dev/null
+++ b/amdgpu/Makefile.am
@@ -0,0 +1,54 @@
+# Copyright © 2008 Jérôme Glisse
+#
+# 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:
+#    Jérôme Glisse <glisse@freedesktop.org>
+
+include Makefile.sources
+
+AM_CFLAGS = \
+	$(WARN_CFLAGS) \
+	-I$(top_srcdir) \
+	$(PTHREADSTUBS_CFLAGS) \
+	-I$(top_srcdir)/include/drm
+
+libdrmdatadir = @libdrmdatadir@
+ASIC_ID_TABLE_NUM_ENTRIES := $(shell egrep -ci '^[0-9a-f]{4},.*[0-9a-f]+,' \
+	$(top_srcdir)/data/amdgpu.ids)
+AM_CPPFLAGS = -DAMDGPU_ASIC_ID_TABLE=\"${libdrmdatadir}/amdgpu.ids\" \
+	-DAMDGPU_ASIC_ID_TABLE_NUM_ENTRIES=$(ASIC_ID_TABLE_NUM_ENTRIES)
+
+libdrm_amdgpu_la_LTLIBRARIES = libdrm_amdgpu.la
+libdrm_amdgpu_ladir = $(libdir)
+libdrm_amdgpu_la_LDFLAGS = -version-number 1:0:0 -no-undefined
+libdrm_amdgpu_la_LIBADD = ../libdrm.la @PTHREADSTUBS_LIBS@
+
+libdrm_amdgpu_la_SOURCES = $(LIBDRM_AMDGPU_FILES)
+amdgpu_asic_id.lo: $(top_srcdir)/data/amdgpu.ids
+
+libdrm_amdgpuincludedir = ${includedir}/libdrm
+libdrm_amdgpuinclude_HEADERS = $(LIBDRM_AMDGPU_H_FILES)
+
+pkgconfigdir = @pkgconfigdir@
+pkgconfig_DATA = libdrm_amdgpu.pc
+
+TESTS = amdgpu-symbol-check
+EXTRA_DIST = $(TESTS)
diff --git a/amdgpu/Makefile.sources b/amdgpu/Makefile.sources
new file mode 100644
index 0000000..bc3abaa
--- /dev/null
+++ b/amdgpu/Makefile.sources
@@ -0,0 +1,15 @@
+LIBDRM_AMDGPU_FILES := \
+	amdgpu_asic_id.c \
+	amdgpu_bo.c \
+	amdgpu_cs.c \
+	amdgpu_device.c \
+	amdgpu_gpu_info.c \
+	amdgpu_internal.h \
+	amdgpu_vamgr.c \
+	util_hash.c \
+	util_hash.h \
+	util_hash_table.c \
+	util_hash_table.h
+
+LIBDRM_AMDGPU_H_FILES := \
+	amdgpu.h
diff --git a/amdgpu/amdgpu-symbol-check b/amdgpu/amdgpu-symbol-check
new file mode 100755
index 0000000..d9f89ef
--- /dev/null
+++ b/amdgpu/amdgpu-symbol-check
@@ -0,0 +1,70 @@
+#!/bin/bash
+
+# The following symbols (past the first five) are taken from the public headers.
+# A list of the latter should be available Makefile.am/libdrm_amdgpuinclude_HEADERS
+
+FUNCS=$(nm -D --format=bsd --defined-only ${1-.libs/libdrm_amdgpu.so} | awk '{print $3}' | while read func; do
+( grep -q "^$func$" || echo $func )  <<EOF
+__bss_start
+_edata
+_end
+_fini
+_init
+amdgpu_bo_alloc
+amdgpu_bo_cpu_map
+amdgpu_bo_cpu_unmap
+amdgpu_bo_export
+amdgpu_bo_free
+amdgpu_bo_import
+amdgpu_bo_list_create
+amdgpu_bo_list_destroy
+amdgpu_bo_list_update
+amdgpu_bo_query_info
+amdgpu_bo_set_metadata
+amdgpu_bo_va_op
+amdgpu_bo_va_op_raw
+amdgpu_bo_wait_for_idle
+amdgpu_create_bo_from_user_mem
+amdgpu_cs_chunk_fence_info_to_data
+amdgpu_cs_chunk_fence_to_dep
+amdgpu_cs_create_semaphore
+amdgpu_cs_create_syncobj
+amdgpu_cs_ctx_create
+amdgpu_cs_ctx_free
+amdgpu_cs_destroy_semaphore
+amdgpu_cs_destroy_syncobj
+amdgpu_cs_export_syncobj
+amdgpu_cs_fence_to_handle
+amdgpu_cs_import_syncobj
+amdgpu_cs_query_fence_status
+amdgpu_cs_query_reset_state
+amdgpu_cs_signal_semaphore
+amdgpu_cs_submit
+amdgpu_cs_submit_raw
+amdgpu_cs_syncobj_export_sync_file
+amdgpu_cs_syncobj_import_sync_file
+amdgpu_cs_syncobj_wait
+amdgpu_cs_wait_fences
+amdgpu_cs_wait_semaphore
+amdgpu_device_deinitialize
+amdgpu_device_initialize
+amdgpu_get_marketing_name
+amdgpu_query_buffer_size_alignment
+amdgpu_query_crtc_from_id
+amdgpu_query_firmware_version
+amdgpu_query_gds_info
+amdgpu_query_gpu_info
+amdgpu_query_heap_info
+amdgpu_query_hw_ip_count
+amdgpu_query_hw_ip_info
+amdgpu_query_info
+amdgpu_query_sensor_info
+amdgpu_read_mm_registers
+amdgpu_va_range_alloc
+amdgpu_va_range_free
+amdgpu_va_range_query
+EOF
+done)
+
+test ! -n "$FUNCS" || echo $FUNCS
+test ! -n "$FUNCS"
diff --git a/amdgpu/amdgpu.h b/amdgpu/amdgpu.h
new file mode 100644
index 0000000..23cde10
--- /dev/null
+++ b/amdgpu/amdgpu.h
@@ -0,0 +1,1482 @@
+/*
+ * Copyright 2014 Advanced Micro Devices, Inc.
+ *
+ * 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 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 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.
+ *
+ */
+
+/**
+ * \file amdgpu.h
+ *
+ * Declare public libdrm_amdgpu API
+ *
+ * This file define API exposed by libdrm_amdgpu library.
+ * User wanted to use libdrm_amdgpu functionality must include
+ * this file.
+ *
+ */
+#ifndef _AMDGPU_H_
+#define _AMDGPU_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct drm_amdgpu_info_hw_ip;
+
+/*--------------------------------------------------------------------------*/
+/* --------------------------- Defines ------------------------------------ */
+/*--------------------------------------------------------------------------*/
+
+/**
+ * Define max. number of Command Buffers (IB) which could be sent to the single
+ * hardware IP to accommodate CE/DE requirements
+ *
+ * \sa amdgpu_cs_ib_info
+*/
+#define AMDGPU_CS_MAX_IBS_PER_SUBMIT		4
+
+/**
+ * Special timeout value meaning that the timeout is infinite.
+ */
+#define AMDGPU_TIMEOUT_INFINITE			0xffffffffffffffffull
+
+/**
+ * Used in amdgpu_cs_query_fence_status(), meaning that the given timeout
+ * is absolute.
+ */
+#define AMDGPU_QUERY_FENCE_TIMEOUT_IS_ABSOLUTE     (1 << 0)
+
+/*--------------------------------------------------------------------------*/
+/* ----------------------------- Enums ------------------------------------ */
+/*--------------------------------------------------------------------------*/
+
+/**
+ * Enum describing possible handle types
+ *
+ * \sa amdgpu_bo_import, amdgpu_bo_export
+ *
+*/
+enum amdgpu_bo_handle_type {
+	/** GEM flink name (needs DRM authentication, used by DRI2) */
+	amdgpu_bo_handle_type_gem_flink_name = 0,
+
+	/** KMS handle which is used by all driver ioctls */
+	amdgpu_bo_handle_type_kms = 1,
+
+	/** DMA-buf fd handle */
+	amdgpu_bo_handle_type_dma_buf_fd = 2
+};
+
+/** Define known types of GPU VM VA ranges */
+enum amdgpu_gpu_va_range
+{
+	/** Allocate from "normal"/general range */
+	amdgpu_gpu_va_range_general = 0
+};
+
+/*--------------------------------------------------------------------------*/
+/* -------------------------- Datatypes ----------------------------------- */
+/*--------------------------------------------------------------------------*/
+
+/**
+ * Define opaque pointer to context associated with fd.
+ * This context will be returned as the result of
+ * "initialize" function and should be pass as the first
+ * parameter to any API call
+ */
+typedef struct amdgpu_device *amdgpu_device_handle;
+
+/**
+ * Define GPU Context type as pointer to opaque structure
+ * Example of GPU Context is the "rendering" context associated
+ * with OpenGL context (glCreateContext)
+ */
+typedef struct amdgpu_context *amdgpu_context_handle;
+
+/**
+ * Define handle for amdgpu resources: buffer, GDS, etc.
+ */
+typedef struct amdgpu_bo *amdgpu_bo_handle;
+
+/**
+ * Define handle for list of BOs
+ */
+typedef struct amdgpu_bo_list *amdgpu_bo_list_handle;
+
+/**
+ * Define handle to be used to work with VA allocated ranges
+ */
+typedef struct amdgpu_va *amdgpu_va_handle;
+
+/**
+ * Define handle for semaphore
+ */
+typedef struct amdgpu_semaphore *amdgpu_semaphore_handle;
+
+/*--------------------------------------------------------------------------*/
+/* -------------------------- Structures ---------------------------------- */
+/*--------------------------------------------------------------------------*/
+
+/**
+ * Structure describing memory allocation request
+ *
+ * \sa amdgpu_bo_alloc()
+ *
+*/
+struct amdgpu_bo_alloc_request {
+	/** Allocation request. It must be aligned correctly. */
+	uint64_t alloc_size;
+
+	/**
+	 * It may be required to have some specific alignment requirements
+	 * for physical back-up storage (e.g. for displayable surface).
+	 * If 0 there is no special alignment requirement
+	 */
+	uint64_t phys_alignment;
+
+	/**
+	 * UMD should specify where to allocate memory and how it
+	 * will be accessed by the CPU.
+	 */
+	uint32_t preferred_heap;
+
+	/** Additional flags passed on allocation */
+	uint64_t flags;
+};
+
+/**
+ * Special UMD specific information associated with buffer.
+ *
+ * It may be need to pass some buffer charactersitic as part
+ * of buffer sharing. Such information are defined UMD and
+ * opaque for libdrm_amdgpu as well for kernel driver.
+ *
+ * \sa amdgpu_bo_set_metadata(), amdgpu_bo_query_info,
+ *     amdgpu_bo_import(), amdgpu_bo_export
+ *
+*/
+struct amdgpu_bo_metadata {
+	/** Special flag associated with surface */
+	uint64_t flags;
+
+	/**
+	 * ASIC-specific tiling information (also used by DCE).
+	 * The encoding is defined by the AMDGPU_TILING_* definitions.
+	 */
+	uint64_t tiling_info;
+
+	/** Size of metadata associated with the buffer, in bytes. */
+	uint32_t size_metadata;
+
+	/** UMD specific metadata. Opaque for kernel */
+	uint32_t umd_metadata[64];
+};
+
+/**
+ * Structure describing allocated buffer. Client may need
+ * to query such information as part of 'sharing' buffers mechanism
+ *
+ * \sa amdgpu_bo_set_metadata(), amdgpu_bo_query_info(),
+ *     amdgpu_bo_import(), amdgpu_bo_export()
+*/
+struct amdgpu_bo_info {
+	/** Allocated memory size */
+	uint64_t alloc_size;
+
+	/**
+	 * It may be required to have some specific alignment requirements
+	 * for physical back-up storage.
+	 */
+	uint64_t phys_alignment;
+
+	/** Heap where to allocate memory. */
+	uint32_t preferred_heap;
+
+	/** Additional allocation flags. */
+	uint64_t alloc_flags;
+
+	/** Metadata associated with buffer if any. */
+	struct amdgpu_bo_metadata metadata;
+};
+
+/**
+ * Structure with information about "imported" buffer
+ *
+ * \sa amdgpu_bo_import()
+ *
+ */
+struct amdgpu_bo_import_result {
+	/** Handle of memory/buffer to use */
+	amdgpu_bo_handle buf_handle;
+
+	 /** Buffer size */
+	uint64_t alloc_size;
+};
+
+/**
+ *
+ * Structure to describe GDS partitioning information.
+ * \note OA and GWS resources are asscoiated with GDS partition
+ *
+ * \sa amdgpu_gpu_resource_query_gds_info
+ *
+*/
+struct amdgpu_gds_resource_info {
+	uint32_t gds_gfx_partition_size;
+	uint32_t compute_partition_size;
+	uint32_t gds_total_size;
+	uint32_t gws_per_gfx_partition;
+	uint32_t gws_per_compute_partition;
+	uint32_t oa_per_gfx_partition;
+	uint32_t oa_per_compute_partition;
+};
+
+/**
+ * Structure describing CS fence
+ *
+ * \sa amdgpu_cs_query_fence_status(), amdgpu_cs_request, amdgpu_cs_submit()
+ *
+*/
+struct amdgpu_cs_fence {
+
+	/** In which context IB was sent to execution */
+	amdgpu_context_handle context;
+
+	/** To which HW IP type the fence belongs */
+	uint32_t ip_type;
+
+	/** IP instance index if there are several IPs of the same type. */
+	uint32_t ip_instance;
+
+	/** Ring index of the HW IP */
+	uint32_t ring;
+
+	/** Specify fence for which we need to check submission status.*/
+	uint64_t fence;
+};
+
+/**
+ * Structure describing IB
+ *
+ * \sa amdgpu_cs_request, amdgpu_cs_submit()
+ *
+*/
+struct amdgpu_cs_ib_info {
+	/** Special flags */
+	uint64_t flags;
+
+	/** Virtual MC address of the command buffer */
+	uint64_t ib_mc_address;
+
+	/**
+	 * Size of Command Buffer to be submitted.
+	 *   - The size is in units of dwords (4 bytes).
+	 *   - Could be 0
+	 */
+	uint32_t size;
+};
+
+/**
+ * Structure describing fence information
+ *
+ * \sa amdgpu_cs_request, amdgpu_cs_query_fence,
+ *     amdgpu_cs_submit(), amdgpu_cs_query_fence_status()
+*/
+struct amdgpu_cs_fence_info {
+	/** buffer object for the fence */
+	amdgpu_bo_handle handle;
+
+	/** fence offset in the unit of sizeof(uint64_t) */
+	uint64_t offset;
+};
+
+/**
+ * Structure describing submission request
+ *
+ * \note We could have several IBs as packet. e.g. CE, CE, DE case for gfx
+ *
+ * \sa amdgpu_cs_submit()
+*/
+struct amdgpu_cs_request {
+	/** Specify flags with additional information */
+	uint64_t flags;
+
+	/** Specify HW IP block type to which to send the IB. */
+	unsigned ip_type;
+
+	/** IP instance index if there are several IPs of the same type. */
+	unsigned ip_instance;
+
+	/**
+	 * Specify ring index of the IP. We could have several rings
+	 * in the same IP. E.g. 0 for SDMA0 and 1 for SDMA1.
+	 */
+	uint32_t ring;
+
+	/**
+	 * List handle with resources used by this request.
+	 */
+	amdgpu_bo_list_handle resources;
+
+	/**
+	 * Number of dependencies this Command submission needs to
+	 * wait for before starting execution.
+	 */
+	uint32_t number_of_dependencies;
+
+	/**
+	 * Array of dependencies which need to be met before
+	 * execution can start.
+	 */
+	struct amdgpu_cs_fence *dependencies;
+
+	/** Number of IBs to submit in the field ibs. */
+	uint32_t number_of_ibs;
+
+	/**
+	 * IBs to submit. Those IBs will be submit together as single entity
+	 */
+	struct amdgpu_cs_ib_info *ibs;
+
+	/**
+	 * The returned sequence number for the command submission 
+	 */
+	uint64_t seq_no;
+
+	/**
+	 * The fence information
+	 */
+	struct amdgpu_cs_fence_info fence_info;
+};
+
+/**
+ * Structure which provide information about GPU VM MC Address space
+ * alignments requirements
+ *
+ * \sa amdgpu_query_buffer_size_alignment
+ */
+struct amdgpu_buffer_size_alignments {
+	/** Size alignment requirement for allocation in
+	 * local memory */
+	uint64_t size_local;
+
+	/**
+	 * Size alignment requirement for allocation in remote memory
+	 */
+	uint64_t size_remote;
+};
+
+/**
+ * Structure which provide information about heap
+ *
+ * \sa amdgpu_query_heap_info()
+ *
+ */
+struct amdgpu_heap_info {
+	/** Theoretical max. available memory in the given heap */
+	uint64_t heap_size;
+
+	/**
+	 * Number of bytes allocated in the heap. This includes all processes
+	 * and private allocations in the kernel. It changes when new buffers
+	 * are allocated, freed, and moved. It cannot be larger than
+	 * heap_size.
+	 */
+	uint64_t heap_usage;
+
+	/**
+	 * Theoretical possible max. size of buffer which
+	 * could be allocated in the given heap
+	 */
+	uint64_t max_allocation;
+};
+
+/**
+ * Describe GPU h/w info needed for UMD correct initialization
+ *
+ * \sa amdgpu_query_gpu_info()
+*/
+struct amdgpu_gpu_info {
+	/** Asic id */
+	uint32_t asic_id;
+	/** Chip revision */
+	uint32_t chip_rev;
+	/** Chip external revision */
+	uint32_t chip_external_rev;
+	/** Family ID */
+	uint32_t family_id;
+	/** Special flags */
+	uint64_t ids_flags;
+	/** max engine clock*/
+	uint64_t max_engine_clk;
+	/** max memory clock */
+	uint64_t max_memory_clk;
+	/** number of shader engines */
+	uint32_t num_shader_engines;
+	/** number of shader arrays per engine */
+	uint32_t num_shader_arrays_per_engine;
+	/**  Number of available good shader pipes */
+	uint32_t avail_quad_shader_pipes;
+	/**  Max. number of shader pipes.(including good and bad pipes  */
+	uint32_t max_quad_shader_pipes;
+	/** Number of parameter cache entries per shader quad pipe */
+	uint32_t cache_entries_per_quad_pipe;
+	/**  Number of available graphics context */
+	uint32_t num_hw_gfx_contexts;
+	/** Number of render backend pipes */
+	uint32_t rb_pipes;
+	/**  Enabled render backend pipe mask */
+	uint32_t enabled_rb_pipes_mask;
+	/** Frequency of GPU Counter */
+	uint32_t gpu_counter_freq;
+	/** CC_RB_BACKEND_DISABLE.BACKEND_DISABLE per SE */
+	uint32_t backend_disable[4];
+	/** Value of MC_ARB_RAMCFG register*/
+	uint32_t mc_arb_ramcfg;
+	/** Value of GB_ADDR_CONFIG */
+	uint32_t gb_addr_cfg;
+	/** Values of the GB_TILE_MODE0..31 registers */
+	uint32_t gb_tile_mode[32];
+	/** Values of GB_MACROTILE_MODE0..15 registers */
+	uint32_t gb_macro_tile_mode[16];
+	/** Value of PA_SC_RASTER_CONFIG register per SE */
+	uint32_t pa_sc_raster_cfg[4];
+	/** Value of PA_SC_RASTER_CONFIG_1 register per SE */
+	uint32_t pa_sc_raster_cfg1[4];
+	/* CU info */
+	uint32_t cu_active_number;
+	uint32_t cu_ao_mask;
+	uint32_t cu_bitmap[4][4];
+	/* video memory type info*/
+	uint32_t vram_type;
+	/* video memory bit width*/
+	uint32_t vram_bit_width;
+	/** constant engine ram size*/
+	uint32_t ce_ram_size;
+	/* vce harvesting instance */
+	uint32_t vce_harvest_config;
+	/* PCI revision ID */
+	uint32_t pci_rev_id;
+};
+
+
+/*--------------------------------------------------------------------------*/
+/*------------------------- Functions --------------------------------------*/
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Initialization / Cleanup
+ *
+*/
+
+/**
+ *
+ * \param   fd            - \c [in]  File descriptor for AMD GPU device
+ *                                   received previously as the result of
+ *                                   e.g. drmOpen() call.
+ *                                   For legacy fd type, the DRI2/DRI3
+ *                                   authentication should be done before
+ *                                   calling this function.
+ * \param   major_version - \c [out] Major version of library. It is assumed
+ *                                   that adding new functionality will cause
+ *                                   increase in major version
+ * \param   minor_version - \c [out] Minor version of library
+ * \param   device_handle - \c [out] Pointer to opaque context which should
+ *                                   be passed as the first parameter on each
+ *                                   API call
+ *
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ *
+ * \sa amdgpu_device_deinitialize()
+*/
+int amdgpu_device_initialize(int fd,
+			     uint32_t *major_version,
+			     uint32_t *minor_version,
+			     amdgpu_device_handle *device_handle);
+
+/**
+ *
+ * When access to such library does not needed any more the special
+ * function must be call giving opportunity to clean up any
+ * resources if needed.
+ *
+ * \param   device_handle - \c [in]  Context associated with file
+ *                                   descriptor for AMD GPU device
+ *                                   received previously as the
+ *                                   result e.g. of drmOpen() call.
+ *
+ * \return  0 on success\n
+ *         <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_device_initialize()
+ *
+*/
+int amdgpu_device_deinitialize(amdgpu_device_handle device_handle);
+
+/*
+ * Memory Management
+ *
+*/
+
+/**
+ * Allocate memory to be used by UMD for GPU related operations
+ *
+ * \param   dev		 - \c [in] Device handle.
+ *				   See #amdgpu_device_initialize()
+ * \param   alloc_buffer - \c [in] Pointer to the structure describing an
+ *				   allocation request
+ * \param   buf_handle	- \c [out] Allocated buffer handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_free()
+*/
+int amdgpu_bo_alloc(amdgpu_device_handle dev,
+		    struct amdgpu_bo_alloc_request *alloc_buffer,
+		    amdgpu_bo_handle *buf_handle);
+
+/**
+ * Associate opaque data with buffer to be queried by another UMD
+ *
+ * \param   dev	       - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   buf_handle - \c [in] Buffer handle
+ * \param   info       - \c [in] Metadata to associated with buffer
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+*/
+int amdgpu_bo_set_metadata(amdgpu_bo_handle buf_handle,
+			   struct amdgpu_bo_metadata *info);
+
+/**
+ * Query buffer information including metadata previusly associated with
+ * buffer.
+ *
+ * \param   dev	       - \c [in] Device handle.
+ *				 See #amdgpu_device_initialize()
+ * \param   buf_handle - \c [in]   Buffer handle
+ * \param   info       - \c [out]  Structure describing buffer
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_set_metadata(), amdgpu_bo_alloc()
+*/
+int amdgpu_bo_query_info(amdgpu_bo_handle buf_handle,
+			 struct amdgpu_bo_info *info);
+
+/**
+ * Allow others to get access to buffer
+ *
+ * \param   dev		  - \c [in] Device handle.
+ *				    See #amdgpu_device_initialize()
+ * \param   buf_handle    - \c [in] Buffer handle
+ * \param   type          - \c [in] Type of handle requested
+ * \param   shared_handle - \c [out] Special "shared" handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_import()
+ *
+*/
+int amdgpu_bo_export(amdgpu_bo_handle buf_handle,
+		     enum amdgpu_bo_handle_type type,
+		     uint32_t *shared_handle);
+
+/**
+ * Request access to "shared" buffer
+ *
+ * \param   dev		  - \c [in] Device handle.
+ *				    See #amdgpu_device_initialize()
+ * \param   type	  - \c [in] Type of handle requested
+ * \param   shared_handle - \c [in] Shared handle received as result "import"
+ *				     operation
+ * \param   output        - \c [out] Pointer to structure with information
+ *				     about imported buffer
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \note  Buffer must be "imported" only using new "fd" (different from
+ *	  one used by "exporter").
+ *
+ * \sa amdgpu_bo_export()
+ *
+*/
+int amdgpu_bo_import(amdgpu_device_handle dev,
+		     enum amdgpu_bo_handle_type type,
+		     uint32_t shared_handle,
+		     struct amdgpu_bo_import_result *output);
+
+/**
+ * Request GPU access to user allocated memory e.g. via "malloc"
+ *
+ * \param dev - [in] Device handle. See #amdgpu_device_initialize()
+ * \param cpu - [in] CPU address of user allocated memory which we
+ * want to map to GPU address space (make GPU accessible)
+ * (This address must be correctly aligned).
+ * \param size - [in] Size of allocation (must be correctly aligned)
+ * \param buf_handle - [out] Buffer handle for the userptr memory
+ * resource on submission and be used in other operations.
+ *
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \note
+ * This call doesn't guarantee that such memory will be persistently
+ * "locked" / make non-pageable. The purpose of this call is to provide
+ * opportunity for GPU get access to this resource during submission.
+ *
+ * The maximum amount of memory which could be mapped in this call depends
+ * if overcommit is disabled or not. If overcommit is disabled than the max.
+ * amount of memory to be pinned will be limited by left "free" size in total
+ * amount of memory which could be locked simultaneously ("GART" size).
+ *
+ * Supported (theoretical) max. size of mapping is restricted only by
+ * "GART" size.
+ *
+ * It is responsibility of caller to correctly specify access rights
+ * on VA assignment.
+*/
+int amdgpu_create_bo_from_user_mem(amdgpu_device_handle dev,
+				    void *cpu, uint64_t size,
+				    amdgpu_bo_handle *buf_handle);
+
+/**
+ * Free previosuly allocated memory
+ *
+ * \param   dev	       - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   buf_handle - \c [in]  Buffer handle to free
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \note In the case of memory shared between different applications all
+ *	 resources will be “physically” freed only all such applications
+ *	 will be terminated
+ * \note If is UMD responsibility to ‘free’ buffer only when there is no
+ *	 more GPU access
+ *
+ * \sa amdgpu_bo_set_metadata(), amdgpu_bo_alloc()
+ *
+*/
+int amdgpu_bo_free(amdgpu_bo_handle buf_handle);
+
+/**
+ * Request CPU access to GPU accessible memory
+ *
+ * \param   buf_handle - \c [in] Buffer handle
+ * \param   cpu        - \c [out] CPU address to be used for access
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_cpu_unmap()
+ *
+*/
+int amdgpu_bo_cpu_map(amdgpu_bo_handle buf_handle, void **cpu);
+
+/**
+ * Release CPU access to GPU memory
+ *
+ * \param   buf_handle  - \c [in] Buffer handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_cpu_map()
+ *
+*/
+int amdgpu_bo_cpu_unmap(amdgpu_bo_handle buf_handle);
+
+/**
+ * Wait until a buffer is not used by the device.
+ *
+ * \param   dev           - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   buf_handle    - \c [in] Buffer handle.
+ * \param   timeout_ns    - Timeout in nanoseconds.
+ * \param   buffer_busy   - 0 if buffer is idle, all GPU access was completed
+ *                            and no GPU access is scheduled.
+ *                          1 GPU access is in fly or scheduled
+ *
+ * \return   0 - on success
+ *          <0 - Negative POSIX Error code
+ */
+int amdgpu_bo_wait_for_idle(amdgpu_bo_handle buf_handle,
+			    uint64_t timeout_ns,
+			    bool *buffer_busy);
+
+/**
+ * Creates a BO list handle for command submission.
+ *
+ * \param   dev			- \c [in] Device handle.
+ *				   See #amdgpu_device_initialize()
+ * \param   number_of_resources	- \c [in] Number of BOs in the list
+ * \param   resources		- \c [in] List of BO handles
+ * \param   resource_prios	- \c [in] Optional priority for each handle
+ * \param   result		- \c [out] Created BO list handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_list_destroy()
+*/
+int amdgpu_bo_list_create(amdgpu_device_handle dev,
+			  uint32_t number_of_resources,
+			  amdgpu_bo_handle *resources,
+			  uint8_t *resource_prios,
+			  amdgpu_bo_list_handle *result);
+
+/**
+ * Destroys a BO list handle.
+ *
+ * \param   handle	- \c [in] BO list handle.
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_list_create()
+*/
+int amdgpu_bo_list_destroy(amdgpu_bo_list_handle handle);
+
+/**
+ * Update resources for existing BO list
+ *
+ * \param   handle              - \c [in] BO list handle
+ * \param   number_of_resources - \c [in] Number of BOs in the list
+ * \param   resources           - \c [in] List of BO handles
+ * \param   resource_prios      - \c [in] Optional priority for each handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_bo_list_update()
+*/
+int amdgpu_bo_list_update(amdgpu_bo_list_handle handle,
+			  uint32_t number_of_resources,
+			  amdgpu_bo_handle *resources,
+			  uint8_t *resource_prios);
+
+/*
+ * GPU Execution context
+ *
+*/
+
+/**
+ * Create GPU execution Context
+ *
+ * For the purpose of GPU Scheduler and GPU Robustness extensions it is
+ * necessary to have information/identify rendering/compute contexts.
+ * It also may be needed to associate some specific requirements with such
+ * contexts.  Kernel driver will guarantee that submission from the same
+ * context will always be executed in order (first come, first serve).
+ *
+ *
+ * \param   dev	    - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   context - \c [out] GPU Context handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_cs_ctx_free()
+ *
+*/
+int amdgpu_cs_ctx_create(amdgpu_device_handle dev,
+			 amdgpu_context_handle *context);
+
+/**
+ *
+ * Destroy GPU execution context when not needed any more
+ *
+ * \param   context - \c [in] GPU Context handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_cs_ctx_create()
+ *
+*/
+int amdgpu_cs_ctx_free(amdgpu_context_handle context);
+
+/**
+ * Query reset state for the specific GPU Context
+ *
+ * \param   context - \c [in]  GPU Context handle
+ * \param   state   - \c [out] One of AMDGPU_CTX_*_RESET
+ * \param   hangs   - \c [out] Number of hangs caused by the context.
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_cs_ctx_create()
+ *
+*/
+int amdgpu_cs_query_reset_state(amdgpu_context_handle context,
+				uint32_t *state, uint32_t *hangs);
+
+/*
+ * Command Buffers Management
+ *
+*/
+
+/**
+ * Send request to submit command buffers to hardware.
+ *
+ * Kernel driver could use GPU Scheduler to make decision when physically
+ * sent this request to the hardware. Accordingly this request could be put
+ * in queue and sent for execution later. The only guarantee is that request
+ * from the same GPU context to the same ip:ip_instance:ring will be executed in
+ * order.
+ *
+ * The caller can specify the user fence buffer/location with the fence_info in the
+ * cs_request.The sequence number is returned via the 'seq_no' parameter
+ * in ibs_request structure.
+ *
+ *
+ * \param   dev		       - \c [in]  Device handle.
+ *					  See #amdgpu_device_initialize()
+ * \param   context            - \c [in]  GPU Context
+ * \param   flags              - \c [in]  Global submission flags
+ * \param   ibs_request        - \c [in/out] Pointer to submission requests.
+ *					  We could submit to the several
+ *					  engines/rings simulteniously as
+ *					  'atomic' operation
+ * \param   number_of_requests - \c [in]  Number of submission requests
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \note It is required to pass correct resource list with buffer handles
+ *	 which will be accessible by command buffers from submission
+ *	 This will allow kernel driver to correctly implement "paging".
+ *	 Failure to do so will have unpredictable results.
+ *
+ * \sa amdgpu_command_buffer_alloc(), amdgpu_command_buffer_free(),
+ *     amdgpu_cs_query_fence_status()
+ *
+*/
+int amdgpu_cs_submit(amdgpu_context_handle context,
+		     uint64_t flags,
+		     struct amdgpu_cs_request *ibs_request,
+		     uint32_t number_of_requests);
+
+/**
+ *  Query status of Command Buffer Submission
+ *
+ * \param   fence   - \c [in] Structure describing fence to query
+ * \param   timeout_ns - \c [in] Timeout value to wait
+ * \param   flags   - \c [in] Flags for the query
+ * \param   expired - \c [out] If fence expired or not.\n
+ *				0  – if fence is not expired\n
+ *				!0 - otherwise
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \note If UMD wants only to check operation status and returned immediately
+ *	 then timeout value as 0 must be passed. In this case success will be
+ *	 returned in the case if submission was completed or timeout error
+ *	 code.
+ *
+ * \sa amdgpu_cs_submit()
+*/
+int amdgpu_cs_query_fence_status(struct amdgpu_cs_fence *fence,
+				 uint64_t timeout_ns,
+				 uint64_t flags,
+				 uint32_t *expired);
+
+/**
+ *  Wait for multiple fences
+ *
+ * \param   fences      - \c [in] The fence array to wait
+ * \param   fence_count - \c [in] The fence count
+ * \param   wait_all    - \c [in] If true, wait all fences to be signaled,
+ *                                otherwise, wait at least one fence
+ * \param   timeout_ns  - \c [in] The timeout to wait, in nanoseconds
+ * \param   status      - \c [out] '1' for signaled, '0' for timeout
+ * \param   first       - \c [out] the index of the first signaled fence from @fences
+ *
+ * \return  0 on success
+ *          <0 - Negative POSIX Error code
+ *
+ * \note    Currently it supports only one amdgpu_device. All fences come from
+ *          the same amdgpu_device with the same fd.
+*/
+int amdgpu_cs_wait_fences(struct amdgpu_cs_fence *fences,
+			  uint32_t fence_count,
+			  bool wait_all,
+			  uint64_t timeout_ns,
+			  uint32_t *status, uint32_t *first);
+
+/*
+ * Query / Info API
+ *
+*/
+
+/**
+ * Query allocation size alignments
+ *
+ * UMD should query information about GPU VM MC size alignments requirements
+ * to be able correctly choose required allocation size and implement
+ * internal optimization if needed.
+ *
+ * \param   dev  - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   info - \c [out] Pointer to structure to get size alignment
+ *			  requirements
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_buffer_size_alignment(amdgpu_device_handle dev,
+				       struct amdgpu_buffer_size_alignments
+						*info);
+
+/**
+ * Query firmware versions
+ *
+ * \param   dev	        - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   fw_type     - \c [in] AMDGPU_INFO_FW_*
+ * \param   ip_instance - \c [in] Index of the IP block of the same type.
+ * \param   index       - \c [in] Index of the engine. (for SDMA and MEC)
+ * \param   version     - \c [out] Pointer to to the "version" return value
+ * \param   feature     - \c [out] Pointer to to the "feature" return value
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_firmware_version(amdgpu_device_handle dev, unsigned fw_type,
+				  unsigned ip_instance, unsigned index,
+				  uint32_t *version, uint32_t *feature);
+
+/**
+ * Query the number of HW IP instances of a certain type.
+ *
+ * \param   dev      - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   type     - \c [in] Hardware IP block type = AMDGPU_HW_IP_*
+ * \param   count    - \c [out] Pointer to structure to get information
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+*/
+int amdgpu_query_hw_ip_count(amdgpu_device_handle dev, unsigned type,
+			     uint32_t *count);
+
+/**
+ * Query engine information
+ *
+ * This query allows UMD to query information different engines and their
+ * capabilities.
+ *
+ * \param   dev         - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   type        - \c [in] Hardware IP block type = AMDGPU_HW_IP_*
+ * \param   ip_instance - \c [in] Index of the IP block of the same type.
+ * \param   info        - \c [out] Pointer to structure to get information
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+*/
+int amdgpu_query_hw_ip_info(amdgpu_device_handle dev, unsigned type,
+			    unsigned ip_instance,
+			    struct drm_amdgpu_info_hw_ip *info);
+
+/**
+ * Query heap information
+ *
+ * This query allows UMD to query potentially available memory resources and
+ * adjust their logic if necessary.
+ *
+ * \param   dev  - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   heap - \c [in] Heap type
+ * \param   info - \c [in] Pointer to structure to get needed information
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_heap_info(amdgpu_device_handle dev, uint32_t heap,
+			   uint32_t flags, struct amdgpu_heap_info *info);
+
+/**
+ * Get the CRTC ID from the mode object ID
+ *
+ * \param   dev    - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   id     - \c [in] Mode object ID
+ * \param   result - \c [in] Pointer to the CRTC ID
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_crtc_from_id(amdgpu_device_handle dev, unsigned id,
+			      int32_t *result);
+
+/**
+ * Query GPU H/w Info
+ *
+ * Query hardware specific information
+ *
+ * \param   dev  - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   heap - \c [in] Heap type
+ * \param   info - \c [in] Pointer to structure to get needed information
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_gpu_info(amdgpu_device_handle dev,
+			   struct amdgpu_gpu_info *info);
+
+/**
+ * Query hardware or driver information.
+ *
+ * The return size is query-specific and depends on the "info_id" parameter.
+ * No more than "size" bytes is returned.
+ *
+ * \param   dev     - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   info_id - \c [in] AMDGPU_INFO_*
+ * \param   size    - \c [in] Size of the returned value.
+ * \param   value   - \c [out] Pointer to the return value.
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX error code
+ *
+*/
+int amdgpu_query_info(amdgpu_device_handle dev, unsigned info_id,
+		      unsigned size, void *value);
+
+/**
+ * Query information about GDS
+ *
+ * \param   dev	     - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   gds_info - \c [out] Pointer to structure to get GDS information
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_gds_info(amdgpu_device_handle dev,
+			struct amdgpu_gds_resource_info *gds_info);
+
+/**
+ * Query information about sensor.
+ *
+ * The return size is query-specific and depends on the "sensor_type"
+ * parameter. No more than "size" bytes is returned.
+ *
+ * \param   dev         - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   sensor_type - \c [in] AMDGPU_INFO_SENSOR_*
+ * \param   size        - \c [in] Size of the returned value.
+ * \param   value       - \c [out] Pointer to the return value.
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_query_sensor_info(amdgpu_device_handle dev, unsigned sensor_type,
+			     unsigned size, void *value);
+
+/**
+ * Read a set of consecutive memory-mapped registers.
+ * Not all registers are allowed to be read by userspace.
+ *
+ * \param   dev          - \c [in] Device handle. See #amdgpu_device_initialize(
+ * \param   dword_offset - \c [in] Register offset in dwords
+ * \param   count        - \c [in] The number of registers to read starting
+ *                                 from the offset
+ * \param   instance     - \c [in] GRBM_GFX_INDEX selector. It may have other
+ *                                 uses. Set it to 0xffffffff if unsure.
+ * \param   flags        - \c [in] Flags with additional information.
+ * \param   values       - \c [out] The pointer to return values.
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX error code
+ *
+*/
+int amdgpu_read_mm_registers(amdgpu_device_handle dev, unsigned dword_offset,
+			     unsigned count, uint32_t instance, uint32_t flags,
+			     uint32_t *values);
+
+/**
+ * Flag to request VA address range in the 32bit address space
+*/
+#define AMDGPU_VA_RANGE_32_BIT		0x1
+
+/**
+ * Allocate virtual address range
+ *
+ * \param dev - [in] Device handle. See #amdgpu_device_initialize()
+ * \param va_range_type - \c [in] Type of MC va range from which to allocate
+ * \param size - \c [in] Size of range. Size must be correctly* aligned.
+ * It is client responsibility to correctly aligned size based on the future
+ * usage of allocated range.
+ * \param va_base_alignment - \c [in] Overwrite base address alignment
+ * requirement for GPU VM MC virtual
+ * address assignment. Must be multiple of size alignments received as
+ * 'amdgpu_buffer_size_alignments'.
+ * If 0 use the default one.
+ * \param va_base_required - \c [in] Specified required va base address.
+ * If 0 then library choose available one.
+ * If !0 value will be passed and those value already "in use" then
+ * corresponding error status will be returned.
+ * \param va_base_allocated - \c [out] On return: Allocated VA base to be used
+ * by client.
+ * \param va_range_handle - \c [out] On return: Handle assigned to allocation
+ * \param flags - \c [in] flags for special VA range
+ *
+ * \return 0 on success\n
+ * >0 - AMD specific error code\n
+ * <0 - Negative POSIX Error code
+ *
+ * \notes \n
+ * It is client responsibility to correctly handle VA assignments and usage.
+ * Neither kernel driver nor libdrm_amdpgu are able to prevent and
+ * detect wrong va assignemnt.
+ *
+ * It is client responsibility to correctly handle multi-GPU cases and to pass
+ * the corresponding arrays of all devices handles where corresponding VA will
+ * be used.
+ *
+*/
+int amdgpu_va_range_alloc(amdgpu_device_handle dev,
+			   enum amdgpu_gpu_va_range va_range_type,
+			   uint64_t size,
+			   uint64_t va_base_alignment,
+			   uint64_t va_base_required,
+			   uint64_t *va_base_allocated,
+			   amdgpu_va_handle *va_range_handle,
+			   uint64_t flags);
+
+/**
+ * Free previously allocated virtual address range
+ *
+ *
+ * \param va_range_handle - \c [in] Handle assigned to VA allocation
+ *
+ * \return 0 on success\n
+ * >0 - AMD specific error code\n
+ * <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_va_range_free(amdgpu_va_handle va_range_handle);
+
+/**
+* Query virtual address range
+*
+* UMD can query GPU VM range supported by each device
+* to initialize its own VAM accordingly.
+*
+* \param   dev    - [in] Device handle. See #amdgpu_device_initialize()
+* \param   type   - \c [in] Type of virtual address range
+* \param   offset - \c [out] Start offset of virtual address range
+* \param   size   - \c [out] Size of virtual address range
+*
+* \return   0 on success\n
+*          <0 - Negative POSIX Error code
+*
+*/
+
+int amdgpu_va_range_query(amdgpu_device_handle dev,
+			  enum amdgpu_gpu_va_range type,
+			  uint64_t *start,
+			  uint64_t *end);
+
+/**
+ *  VA mapping/unmapping for the buffer object
+ *
+ * \param  bo		- \c [in] BO handle
+ * \param  offset	- \c [in] Start offset to map
+ * \param  size		- \c [in] Size to map
+ * \param  addr		- \c [in] Start virtual address.
+ * \param  flags	- \c [in] Supported flags for mapping/unmapping
+ * \param  ops		- \c [in] AMDGPU_VA_OP_MAP or AMDGPU_VA_OP_UNMAP
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+
+int amdgpu_bo_va_op(amdgpu_bo_handle bo,
+		    uint64_t offset,
+		    uint64_t size,
+		    uint64_t addr,
+		    uint64_t flags,
+		    uint32_t ops);
+
+/**
+ *  VA mapping/unmapping for a buffer object or PRT region.
+ *
+ * This is not a simple drop-in extension for amdgpu_bo_va_op; instead, all
+ * parameters are treated "raw", i.e. size is not automatically aligned, and
+ * all flags must be specified explicitly.
+ *
+ * \param  dev		- \c [in] device handle
+ * \param  bo		- \c [in] BO handle (may be NULL)
+ * \param  offset	- \c [in] Start offset to map
+ * \param  size		- \c [in] Size to map
+ * \param  addr		- \c [in] Start virtual address.
+ * \param  flags	- \c [in] Supported flags for mapping/unmapping
+ * \param  ops		- \c [in] AMDGPU_VA_OP_MAP or AMDGPU_VA_OP_UNMAP
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+
+int amdgpu_bo_va_op_raw(amdgpu_device_handle dev,
+			amdgpu_bo_handle bo,
+			uint64_t offset,
+			uint64_t size,
+			uint64_t addr,
+			uint64_t flags,
+			uint32_t ops);
+
+/**
+ *  create semaphore
+ *
+ * \param   sem	   - \c [out] semaphore handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_create_semaphore(amdgpu_semaphore_handle *sem);
+
+/**
+ *  signal semaphore
+ *
+ * \param   context        - \c [in] GPU Context
+ * \param   ip_type        - \c [in] Hardware IP block type = AMDGPU_HW_IP_*
+ * \param   ip_instance    - \c [in] Index of the IP block of the same type
+ * \param   ring           - \c [in] Specify ring index of the IP
+ * \param   sem	           - \c [in] semaphore handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_signal_semaphore(amdgpu_context_handle ctx,
+			       uint32_t ip_type,
+			       uint32_t ip_instance,
+			       uint32_t ring,
+			       amdgpu_semaphore_handle sem);
+
+/**
+ *  wait semaphore
+ *
+ * \param   context        - \c [in] GPU Context
+ * \param   ip_type        - \c [in] Hardware IP block type = AMDGPU_HW_IP_*
+ * \param   ip_instance    - \c [in] Index of the IP block of the same type
+ * \param   ring           - \c [in] Specify ring index of the IP
+ * \param   sem	           - \c [in] semaphore handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_wait_semaphore(amdgpu_context_handle ctx,
+			     uint32_t ip_type,
+			     uint32_t ip_instance,
+			     uint32_t ring,
+			     amdgpu_semaphore_handle sem);
+
+/**
+ *  destroy semaphore
+ *
+ * \param   sem	    - \c [in] semaphore handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_destroy_semaphore(amdgpu_semaphore_handle sem);
+
+/**
+ *  Get the ASIC marketing name
+ *
+ * \param   dev         - \c [in] Device handle. See #amdgpu_device_initialize()
+ *
+ * \return  the constant string of the marketing name
+ *          "NULL" means the ASIC is not found
+*/
+const char *amdgpu_get_marketing_name(amdgpu_device_handle dev);
+
+/**
+ *  Create kernel sync object
+ *
+ * \param   dev	      - \c [in]  device handle
+ * \param   syncobj   - \c [out] sync object handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_create_syncobj(amdgpu_device_handle dev,
+			     uint32_t *syncobj);
+/**
+ *  Destroy kernel sync object
+ *
+ * \param   dev	    - \c [in] device handle
+ * \param   syncobj - \c [in] sync object handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_destroy_syncobj(amdgpu_device_handle dev,
+			      uint32_t syncobj);
+
+/**
+ *  Wait for one or all sync objects to signal.
+ *
+ * \param   dev	    - \c [in] self-explanatory
+ * \param   handles - \c [in] array of sync object handles
+ * \param   num_handles - \c [in] self-explanatory
+ * \param   timeout_nsec - \c [in] self-explanatory
+ * \param   flags   - \c [in] a bitmask of DRM_SYNCOBJ_WAIT_FLAGS_*
+ * \param   first_signaled - \c [in] self-explanatory
+ *
+ * \return   0 on success\n
+ *          -ETIME - Timeout
+ *          <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_wait(amdgpu_device_handle dev,
+			   uint32_t *handles, unsigned num_handles,
+			   int64_t timeout_nsec, unsigned flags,
+			   uint32_t *first_signaled);
+
+/**
+ *  Export kernel sync object to shareable fd.
+ *
+ * \param   dev	       - \c [in] device handle
+ * \param   syncobj    - \c [in] sync object handle
+ * \param   shared_fd  - \c [out] shared file descriptor.
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_export_syncobj(amdgpu_device_handle dev,
+			     uint32_t syncobj,
+			     int *shared_fd);
+/**
+ *  Import kernel sync object from shareable fd.
+ *
+ * \param   dev	       - \c [in] device handle
+ * \param   shared_fd  - \c [in] shared file descriptor.
+ * \param   syncobj    - \c [out] sync object handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_cs_import_syncobj(amdgpu_device_handle dev,
+			     int shared_fd,
+			     uint32_t *syncobj);
+
+/**
+ *  Export kernel sync object to a sync_file.
+ *
+ * \param   dev	       - \c [in] device handle
+ * \param   syncobj    - \c [in] sync object handle
+ * \param   sync_file_fd - \c [out] sync_file file descriptor.
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_export_sync_file(amdgpu_device_handle dev,
+				       uint32_t syncobj,
+				       int *sync_file_fd);
+
+/**
+ *  Import kernel sync object from a sync_file.
+ *
+ * \param   dev	       - \c [in] device handle
+ * \param   syncobj    - \c [in] sync object handle
+ * \param   sync_file_fd - \c [in] sync_file file descriptor.
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ */
+int amdgpu_cs_syncobj_import_sync_file(amdgpu_device_handle dev,
+				       uint32_t syncobj,
+				       int sync_file_fd);
+
+/**
+ * Export an amdgpu fence as a handle (syncobj or fd).
+ *
+ * \param what		AMDGPU_FENCE_TO_HANDLE_GET_{SYNCOBJ, FD}
+ * \param out_handle	returned handle
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ */
+int amdgpu_cs_fence_to_handle(amdgpu_device_handle dev,
+			      struct amdgpu_cs_fence *fence,
+			      uint32_t what,
+			      uint32_t *out_handle);
+
+/**
+ *  Submit raw command submission to kernel
+ *
+ * \param   dev	       - \c [in] device handle
+ * \param   context    - \c [in] context handle for context id
+ * \param   bo_list_handle - \c [in] request bo list handle (0 for none)
+ * \param   num_chunks - \c [in] number of CS chunks to submit
+ * \param   chunks     - \c [in] array of CS chunks
+ * \param   seq_no     - \c [out] output sequence number for submission.
+ *
+ * \return   0 on success\n
+ *          <0 - Negative POSIX Error code
+ *
+ */
+struct drm_amdgpu_cs_chunk;
+struct drm_amdgpu_cs_chunk_dep;
+struct drm_amdgpu_cs_chunk_data;
+
+int amdgpu_cs_submit_raw(amdgpu_device_handle dev,
+			 amdgpu_context_handle context,
+			 amdgpu_bo_list_handle bo_list_handle,
+			 int num_chunks,
+			 struct drm_amdgpu_cs_chunk *chunks,
+			 uint64_t *seq_no);
+
+void amdgpu_cs_chunk_fence_to_dep(struct amdgpu_cs_fence *fence,
+				  struct drm_amdgpu_cs_chunk_dep *dep);
+void amdgpu_cs_chunk_fence_info_to_data(struct amdgpu_cs_fence_info *fence_info,
+					struct drm_amdgpu_cs_chunk_data *data);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* #ifdef _AMDGPU_H_ */
diff --git a/amdgpu/amdgpu_asic_id.c b/amdgpu/amdgpu_asic_id.c
new file mode 100644
index 0000000..e821897
--- /dev/null
+++ b/amdgpu/amdgpu_asic_id.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright © 2017 Advanced Micro Devices, Inc.
+ * 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:
+ *
+ * The above copyright notice and this permission notice 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 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "xf86drm.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+
+static int parse_one_line(const char *line, struct amdgpu_asic_id *id)
+{
+	char *buf, *saveptr;
+	char *s_did;
+	char *s_rid;
+	char *s_name;
+	char *endptr;
+	int r = 0;
+
+	buf = strdup(line);
+	if (!buf)
+		return -ENOMEM;
+
+	/* ignore empty line and commented line */
+	if (strlen(line) == 0 || line[0] == '#') {
+		r = -EAGAIN;
+		goto out;
+	}
+
+	/* device id */
+	s_did = strtok_r(buf, ",", &saveptr);
+	if (!s_did) {
+		r = -EINVAL;
+		goto out;
+	}
+
+	id->did = strtol(s_did, &endptr, 16);
+	if (*endptr) {
+		r = -EINVAL;
+		goto out;
+	}
+
+	/* revision id */
+	s_rid = strtok_r(NULL, ",", &saveptr);
+	if (!s_rid) {
+		r = -EINVAL;
+		goto out;
+	}
+
+	id->rid = strtol(s_rid, &endptr, 16);
+	if (*endptr) {
+		r = -EINVAL;
+		goto out;
+	}
+
+	/* marketing name */
+	s_name = strtok_r(NULL, ",", &saveptr);
+	if (!s_name) {
+		r = -EINVAL;
+		goto out;
+	}
+	/* trim leading whitespaces or tabs */
+	while (isblank(*s_name))
+		s_name++;
+	if (strlen(s_name) == 0) {
+		r = -EINVAL;
+		goto out;
+	}
+
+	id->marketing_name = strdup(s_name);
+	if (id->marketing_name == NULL) {
+		r = -EINVAL;
+		goto out;
+	}
+
+out:
+	free(buf);
+
+	return r;
+}
+
+int amdgpu_parse_asic_ids(struct amdgpu_asic_id **p_asic_id_table)
+{
+	struct amdgpu_asic_id *asic_id_table;
+	struct amdgpu_asic_id *id;
+	FILE *fp;
+	char *line = NULL;
+	size_t len = 0;
+	ssize_t n;
+	int line_num = 1;
+	size_t table_size = 0;
+	size_t table_max_size = AMDGPU_ASIC_ID_TABLE_NUM_ENTRIES;
+	int r = 0;
+
+	fp = fopen(AMDGPU_ASIC_ID_TABLE, "r");
+	if (!fp) {
+		fprintf(stderr, "%s: %s\n", AMDGPU_ASIC_ID_TABLE,
+			strerror(errno));
+		return -EINVAL;
+	}
+
+	asic_id_table = calloc(table_max_size + 1,
+			       sizeof(struct amdgpu_asic_id));
+	if (!asic_id_table) {
+		r = -ENOMEM;
+		goto close;
+	}
+
+	/* 1st valid line is file version */
+	while ((n = getline(&line, &len, fp)) != -1) {
+		/* trim trailing newline */
+		if (line[n - 1] == '\n')
+			line[n - 1] = '\0';
+
+		/* ignore empty line and commented line */
+		if (strlen(line) == 0 || line[0] == '#') {
+			line_num++;
+			continue;
+		}
+
+		drmMsg("%s version: %s\n", AMDGPU_ASIC_ID_TABLE, line);
+		break;
+	}
+
+	while ((n = getline(&line, &len, fp)) != -1) {
+		if (table_size > table_max_size) {
+			/* double table size */
+			table_max_size *= 2;
+			id = realloc(asic_id_table, (table_max_size + 1) *
+				     sizeof(struct amdgpu_asic_id));
+			if (!id) {
+				r = -ENOMEM;
+				goto free;
+			}
+                        asic_id_table = id;
+		}
+
+		id = asic_id_table + table_size;
+
+		/* trim trailing newline */
+		if (line[n - 1] == '\n')
+			line[n - 1] = '\0';
+
+		r = parse_one_line(line, id);
+		if (r) {
+			if (r == -EAGAIN) {
+				line_num++;
+				continue;
+			}
+			fprintf(stderr, "Invalid format: %s: line %d: %s\n",
+				AMDGPU_ASIC_ID_TABLE, line_num, line);
+			goto free;
+		}
+
+		line_num++;
+		table_size++;
+	}
+
+	if (table_size != table_max_size) {
+		id = realloc(asic_id_table, (table_size + 1) *
+			     sizeof(struct amdgpu_asic_id));
+		if (!id) {
+			r = -ENOMEM;
+			goto free;
+		}
+		asic_id_table = id;
+        }
+
+	/* end of table */
+	id = asic_id_table + table_size;
+	memset(id, 0, sizeof(struct amdgpu_asic_id));
+
+free:
+	free(line);
+
+	if (r && asic_id_table) {
+		while (table_size--) {
+			id = asic_id_table + table_size;
+			free(id->marketing_name);
+		}
+		free(asic_id_table);
+		asic_id_table = NULL;
+	}
+close:
+	fclose(fp);
+
+	*p_asic_id_table = asic_id_table;
+
+	return r;
+}
diff --git a/amdgpu/amdgpu_bo.c b/amdgpu/amdgpu_bo.c
new file mode 100644
index 0000000..3853fd0
--- /dev/null
+++ b/amdgpu/amdgpu_bo.c
@@ -0,0 +1,725 @@
+/*
+ * Copyright © 2014 Advanced Micro Devices, Inc.
+ * 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:
+ *
+ * The above copyright notice and this permission notice 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 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include "libdrm_macros.h"
+#include "xf86drm.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+#include "util_hash_table.h"
+#include "util_math.h"
+
+static void amdgpu_close_kms_handle(amdgpu_device_handle dev,
+				     uint32_t handle)
+{
+	struct drm_gem_close args = {};
+
+	args.handle = handle;
+	drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &args);
+}
+
+int amdgpu_bo_alloc(amdgpu_device_handle dev,
+		    struct amdgpu_bo_alloc_request *alloc_buffer,
+		    amdgpu_bo_handle *buf_handle)
+{
+	struct amdgpu_bo *bo;
+	union drm_amdgpu_gem_create args;
+	unsigned heap = alloc_buffer->preferred_heap;
+	int r = 0;
+
+	/* It's an error if the heap is not specified */
+	if (!(heap & (AMDGPU_GEM_DOMAIN_GTT | AMDGPU_GEM_DOMAIN_VRAM)))
+		return -EINVAL;
+
+	bo = calloc(1, sizeof(struct amdgpu_bo));
+	if (!bo)
+		return -ENOMEM;
+
+	atomic_set(&bo->refcount, 1);
+	bo->dev = dev;
+	bo->alloc_size = alloc_buffer->alloc_size;
+
+	memset(&args, 0, sizeof(args));
+	args.in.bo_size = alloc_buffer->alloc_size;
+	args.in.alignment = alloc_buffer->phys_alignment;
+
+	/* Set the placement. */
+	args.in.domains = heap;
+	args.in.domain_flags = alloc_buffer->flags;
+
+	/* Allocate the buffer with the preferred heap. */
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_CREATE,
+				&args, sizeof(args));
+	if (r) {
+		free(bo);
+		return r;
+	}
+
+	bo->handle = args.out.handle;
+
+	pthread_mutex_init(&bo->cpu_access_mutex, NULL);
+
+	*buf_handle = bo;
+	return 0;
+}
+
+int amdgpu_bo_set_metadata(amdgpu_bo_handle bo,
+			   struct amdgpu_bo_metadata *info)
+{
+	struct drm_amdgpu_gem_metadata args = {};
+
+	args.handle = bo->handle;
+	args.op = AMDGPU_GEM_METADATA_OP_SET_METADATA;
+	args.data.flags = info->flags;
+	args.data.tiling_info = info->tiling_info;
+
+	if (info->size_metadata > sizeof(args.data.data))
+		return -EINVAL;
+
+	if (info->size_metadata) {
+		args.data.data_size_bytes = info->size_metadata;
+		memcpy(args.data.data, info->umd_metadata, info->size_metadata);
+	}
+
+	return drmCommandWriteRead(bo->dev->fd,
+				   DRM_AMDGPU_GEM_METADATA,
+				   &args, sizeof(args));
+}
+
+int amdgpu_bo_query_info(amdgpu_bo_handle bo,
+			 struct amdgpu_bo_info *info)
+{
+	struct drm_amdgpu_gem_metadata metadata = {};
+	struct drm_amdgpu_gem_create_in bo_info = {};
+	struct drm_amdgpu_gem_op gem_op = {};
+	int r;
+
+	/* Validate the BO passed in */
+	if (!bo->handle)
+		return -EINVAL;
+
+	/* Query metadata. */
+	metadata.handle = bo->handle;
+	metadata.op = AMDGPU_GEM_METADATA_OP_GET_METADATA;
+
+	r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_METADATA,
+				&metadata, sizeof(metadata));
+	if (r)
+		return r;
+
+	if (metadata.data.data_size_bytes >
+	    sizeof(info->metadata.umd_metadata))
+		return -EINVAL;
+
+	/* Query buffer info. */
+	gem_op.handle = bo->handle;
+	gem_op.op = AMDGPU_GEM_OP_GET_GEM_CREATE_INFO;
+	gem_op.value = (uintptr_t)&bo_info;
+
+	r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_OP,
+				&gem_op, sizeof(gem_op));
+	if (r)
+		return r;
+
+	memset(info, 0, sizeof(*info));
+	info->alloc_size = bo_info.bo_size;
+	info->phys_alignment = bo_info.alignment;
+	info->preferred_heap = bo_info.domains;
+	info->alloc_flags = bo_info.domain_flags;
+	info->metadata.flags = metadata.data.flags;
+	info->metadata.tiling_info = metadata.data.tiling_info;
+
+	info->metadata.size_metadata = metadata.data.data_size_bytes;
+	if (metadata.data.data_size_bytes > 0)
+		memcpy(info->metadata.umd_metadata, metadata.data.data,
+		       metadata.data.data_size_bytes);
+
+	return 0;
+}
+
+static void amdgpu_add_handle_to_table(amdgpu_bo_handle bo)
+{
+	pthread_mutex_lock(&bo->dev->bo_table_mutex);
+	util_hash_table_set(bo->dev->bo_handles,
+			    (void*)(uintptr_t)bo->handle, bo);
+	pthread_mutex_unlock(&bo->dev->bo_table_mutex);
+}
+
+static int amdgpu_bo_export_flink(amdgpu_bo_handle bo)
+{
+	struct drm_gem_flink flink;
+	int fd, dma_fd;
+	uint32_t handle;
+	int r;
+
+	fd = bo->dev->fd;
+	handle = bo->handle;
+	if (bo->flink_name)
+		return 0;
+
+
+	if (bo->dev->flink_fd != bo->dev->fd) {
+		r = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
+				       &dma_fd);
+		if (!r) {
+			r = drmPrimeFDToHandle(bo->dev->flink_fd, dma_fd, &handle);
+			close(dma_fd);
+		}
+		if (r)
+			return r;
+		fd = bo->dev->flink_fd;
+	}
+	memset(&flink, 0, sizeof(flink));
+	flink.handle = handle;
+
+	r = drmIoctl(fd, DRM_IOCTL_GEM_FLINK, &flink);
+	if (r)
+		return r;
+
+	bo->flink_name = flink.name;
+
+	if (bo->dev->flink_fd != bo->dev->fd) {
+		struct drm_gem_close args = {};
+		args.handle = handle;
+		drmIoctl(bo->dev->flink_fd, DRM_IOCTL_GEM_CLOSE, &args);
+	}
+
+	pthread_mutex_lock(&bo->dev->bo_table_mutex);
+	util_hash_table_set(bo->dev->bo_flink_names,
+			    (void*)(uintptr_t)bo->flink_name,
+			    bo);
+	pthread_mutex_unlock(&bo->dev->bo_table_mutex);
+
+	return 0;
+}
+
+int amdgpu_bo_export(amdgpu_bo_handle bo,
+		     enum amdgpu_bo_handle_type type,
+		     uint32_t *shared_handle)
+{
+	int r;
+
+	switch (type) {
+	case amdgpu_bo_handle_type_gem_flink_name:
+		r = amdgpu_bo_export_flink(bo);
+		if (r)
+			return r;
+
+		*shared_handle = bo->flink_name;
+		return 0;
+
+	case amdgpu_bo_handle_type_kms:
+		amdgpu_add_handle_to_table(bo);
+		*shared_handle = bo->handle;
+		return 0;
+
+	case amdgpu_bo_handle_type_dma_buf_fd:
+		amdgpu_add_handle_to_table(bo);
+		return drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
+				       (int*)shared_handle);
+	}
+	return -EINVAL;
+}
+
+int amdgpu_bo_import(amdgpu_device_handle dev,
+		     enum amdgpu_bo_handle_type type,
+		     uint32_t shared_handle,
+		     struct amdgpu_bo_import_result *output)
+{
+	struct drm_gem_open open_arg = {};
+	struct amdgpu_bo *bo = NULL;
+	int r;
+	int dma_fd;
+	uint64_t dma_buf_size = 0;
+
+	/* We must maintain a list of pairs <handle, bo>, so that we always
+	 * return the same amdgpu_bo instance for the same handle. */
+	pthread_mutex_lock(&dev->bo_table_mutex);
+
+	/* Convert a DMA buf handle to a KMS handle now. */
+	if (type == amdgpu_bo_handle_type_dma_buf_fd) {
+		uint32_t handle;
+		off_t size;
+
+		/* Get a KMS handle. */
+		r = drmPrimeFDToHandle(dev->fd, shared_handle, &handle);
+		if (r) {
+			pthread_mutex_unlock(&dev->bo_table_mutex);
+			return r;
+		}
+
+		/* Query the buffer size. */
+		size = lseek(shared_handle, 0, SEEK_END);
+		if (size == (off_t)-1) {
+			pthread_mutex_unlock(&dev->bo_table_mutex);
+			amdgpu_close_kms_handle(dev, handle);
+			return -errno;
+		}
+		lseek(shared_handle, 0, SEEK_SET);
+
+		dma_buf_size = size;
+		shared_handle = handle;
+	}
+
+	/* If we have already created a buffer with this handle, find it. */
+	switch (type) {
+	case amdgpu_bo_handle_type_gem_flink_name:
+		bo = util_hash_table_get(dev->bo_flink_names,
+					 (void*)(uintptr_t)shared_handle);
+		break;
+
+	case amdgpu_bo_handle_type_dma_buf_fd:
+		bo = util_hash_table_get(dev->bo_handles,
+					 (void*)(uintptr_t)shared_handle);
+		break;
+
+	case amdgpu_bo_handle_type_kms:
+		/* Importing a KMS handle in not allowed. */
+		pthread_mutex_unlock(&dev->bo_table_mutex);
+		return -EPERM;
+
+	default:
+		pthread_mutex_unlock(&dev->bo_table_mutex);
+		return -EINVAL;
+	}
+
+	if (bo) {
+		/* The buffer already exists, just bump the refcount. */
+		atomic_inc(&bo->refcount);
+		pthread_mutex_unlock(&dev->bo_table_mutex);
+
+		output->buf_handle = bo;
+		output->alloc_size = bo->alloc_size;
+		return 0;
+	}
+
+	bo = calloc(1, sizeof(struct amdgpu_bo));
+	if (!bo) {
+		pthread_mutex_unlock(&dev->bo_table_mutex);
+		if (type == amdgpu_bo_handle_type_dma_buf_fd) {
+			amdgpu_close_kms_handle(dev, shared_handle);
+		}
+		return -ENOMEM;
+	}
+
+	/* Open the handle. */
+	switch (type) {
+	case amdgpu_bo_handle_type_gem_flink_name:
+		open_arg.name = shared_handle;
+		r = drmIoctl(dev->flink_fd, DRM_IOCTL_GEM_OPEN, &open_arg);
+		if (r) {
+			free(bo);
+			pthread_mutex_unlock(&dev->bo_table_mutex);
+			return r;
+		}
+
+		bo->handle = open_arg.handle;
+		if (dev->flink_fd != dev->fd) {
+			r = drmPrimeHandleToFD(dev->flink_fd, bo->handle, DRM_CLOEXEC, &dma_fd);
+			if (r) {
+				free(bo);
+				pthread_mutex_unlock(&dev->bo_table_mutex);
+				return r;
+			}
+			r = drmPrimeFDToHandle(dev->fd, dma_fd, &bo->handle );
+
+			close(dma_fd);
+
+			if (r) {
+				free(bo);
+				pthread_mutex_unlock(&dev->bo_table_mutex);
+				return r;
+			}
+		}
+		bo->flink_name = shared_handle;
+		bo->alloc_size = open_arg.size;
+		util_hash_table_set(dev->bo_flink_names,
+				    (void*)(uintptr_t)bo->flink_name, bo);
+		break;
+
+	case amdgpu_bo_handle_type_dma_buf_fd:
+		bo->handle = shared_handle;
+		bo->alloc_size = dma_buf_size;
+		break;
+
+	case amdgpu_bo_handle_type_kms:
+		assert(0); /* unreachable */
+	}
+
+	/* Initialize it. */
+	atomic_set(&bo->refcount, 1);
+	bo->dev = dev;
+	pthread_mutex_init(&bo->cpu_access_mutex, NULL);
+
+	util_hash_table_set(dev->bo_handles, (void*)(uintptr_t)bo->handle, bo);
+	pthread_mutex_unlock(&dev->bo_table_mutex);
+
+	output->buf_handle = bo;
+	output->alloc_size = bo->alloc_size;
+	return 0;
+}
+
+int amdgpu_bo_free(amdgpu_bo_handle buf_handle)
+{
+	struct amdgpu_device *dev;
+	struct amdgpu_bo *bo = buf_handle;
+
+	assert(bo != NULL);
+	dev = bo->dev;
+	pthread_mutex_lock(&dev->bo_table_mutex);
+
+	if (update_references(&bo->refcount, NULL)) {
+		/* Remove the buffer from the hash tables. */
+		util_hash_table_remove(dev->bo_handles,
+					(void*)(uintptr_t)bo->handle);
+
+		if (bo->flink_name) {
+			util_hash_table_remove(dev->bo_flink_names,
+						(void*)(uintptr_t)bo->flink_name);
+		}
+
+		/* Release CPU access. */
+		if (bo->cpu_map_count > 0) {
+			bo->cpu_map_count = 1;
+			amdgpu_bo_cpu_unmap(bo);
+		}
+
+		amdgpu_close_kms_handle(dev, bo->handle);
+		pthread_mutex_destroy(&bo->cpu_access_mutex);
+		free(bo);
+	}
+
+	pthread_mutex_unlock(&dev->bo_table_mutex);
+	return 0;
+}
+
+int amdgpu_bo_cpu_map(amdgpu_bo_handle bo, void **cpu)
+{
+	union drm_amdgpu_gem_mmap args;
+	void *ptr;
+	int r;
+
+	pthread_mutex_lock(&bo->cpu_access_mutex);
+
+	if (bo->cpu_ptr) {
+		/* already mapped */
+		assert(bo->cpu_map_count > 0);
+		bo->cpu_map_count++;
+		*cpu = bo->cpu_ptr;
+		pthread_mutex_unlock(&bo->cpu_access_mutex);
+		return 0;
+	}
+
+	assert(bo->cpu_map_count == 0);
+
+	memset(&args, 0, sizeof(args));
+
+	/* Query the buffer address (args.addr_ptr).
+	 * The kernel driver ignores the offset and size parameters. */
+	args.in.handle = bo->handle;
+
+	r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_MMAP, &args,
+				sizeof(args));
+	if (r) {
+		pthread_mutex_unlock(&bo->cpu_access_mutex);
+		return r;
+	}
+
+	/* Map the buffer. */
+	ptr = drm_mmap(NULL, bo->alloc_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+		       bo->dev->fd, args.out.addr_ptr);
+	if (ptr == MAP_FAILED) {
+		pthread_mutex_unlock(&bo->cpu_access_mutex);
+		return -errno;
+	}
+
+	bo->cpu_ptr = ptr;
+	bo->cpu_map_count = 1;
+	pthread_mutex_unlock(&bo->cpu_access_mutex);
+
+	*cpu = ptr;
+	return 0;
+}
+
+int amdgpu_bo_cpu_unmap(amdgpu_bo_handle bo)
+{
+	int r;
+
+	pthread_mutex_lock(&bo->cpu_access_mutex);
+	assert(bo->cpu_map_count >= 0);
+
+	if (bo->cpu_map_count == 0) {
+		/* not mapped */
+		pthread_mutex_unlock(&bo->cpu_access_mutex);
+		return -EINVAL;
+	}
+
+	bo->cpu_map_count--;
+	if (bo->cpu_map_count > 0) {
+		/* mapped multiple times */
+		pthread_mutex_unlock(&bo->cpu_access_mutex);
+		return 0;
+	}
+
+	r = drm_munmap(bo->cpu_ptr, bo->alloc_size) == 0 ? 0 : -errno;
+	bo->cpu_ptr = NULL;
+	pthread_mutex_unlock(&bo->cpu_access_mutex);
+	return r;
+}
+
+int amdgpu_query_buffer_size_alignment(amdgpu_device_handle dev,
+				struct amdgpu_buffer_size_alignments *info)
+{
+	info->size_local = dev->dev_info.pte_fragment_size;
+	info->size_remote = dev->dev_info.gart_page_size;
+	return 0;
+}
+
+int amdgpu_bo_wait_for_idle(amdgpu_bo_handle bo,
+			    uint64_t timeout_ns,
+			    bool *busy)
+{
+	union drm_amdgpu_gem_wait_idle args;
+	int r;
+
+	memset(&args, 0, sizeof(args));
+	args.in.handle = bo->handle;
+	args.in.timeout = amdgpu_cs_calculate_timeout(timeout_ns);
+
+	r = drmCommandWriteRead(bo->dev->fd, DRM_AMDGPU_GEM_WAIT_IDLE,
+				&args, sizeof(args));
+
+	if (r == 0) {
+		*busy = args.out.status;
+		return 0;
+	} else {
+		fprintf(stderr, "amdgpu: GEM_WAIT_IDLE failed with %i\n", r);
+		return r;
+	}
+}
+
+int amdgpu_create_bo_from_user_mem(amdgpu_device_handle dev,
+				    void *cpu,
+				    uint64_t size,
+				    amdgpu_bo_handle *buf_handle)
+{
+	int r;
+	struct amdgpu_bo *bo;
+	struct drm_amdgpu_gem_userptr args;
+
+	args.addr = (uintptr_t)cpu;
+	args.flags = AMDGPU_GEM_USERPTR_ANONONLY | AMDGPU_GEM_USERPTR_REGISTER |
+		AMDGPU_GEM_USERPTR_VALIDATE;
+	args.size = size;
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_USERPTR,
+				&args, sizeof(args));
+	if (r)
+		return r;
+
+	bo = calloc(1, sizeof(struct amdgpu_bo));
+	if (!bo)
+		return -ENOMEM;
+
+	atomic_set(&bo->refcount, 1);
+	bo->dev = dev;
+	bo->alloc_size = size;
+	bo->handle = args.handle;
+
+	*buf_handle = bo;
+
+	return r;
+}
+
+int amdgpu_bo_list_create(amdgpu_device_handle dev,
+			  uint32_t number_of_resources,
+			  amdgpu_bo_handle *resources,
+			  uint8_t *resource_prios,
+			  amdgpu_bo_list_handle *result)
+{
+	struct drm_amdgpu_bo_list_entry *list;
+	union drm_amdgpu_bo_list args;
+	unsigned i;
+	int r;
+
+	if (!number_of_resources)
+		return -EINVAL;
+
+	/* overflow check for multiplication */
+	if (number_of_resources > UINT32_MAX / sizeof(struct drm_amdgpu_bo_list_entry))
+		return -EINVAL;
+
+	list = malloc(number_of_resources * sizeof(struct drm_amdgpu_bo_list_entry));
+	if (!list)
+		return -ENOMEM;
+
+	*result = malloc(sizeof(struct amdgpu_bo_list));
+	if (!*result) {
+		free(list);
+		return -ENOMEM;
+	}
+
+	memset(&args, 0, sizeof(args));
+	args.in.operation = AMDGPU_BO_LIST_OP_CREATE;
+	args.in.bo_number = number_of_resources;
+	args.in.bo_info_size = sizeof(struct drm_amdgpu_bo_list_entry);
+	args.in.bo_info_ptr = (uint64_t)(uintptr_t)list;
+
+	for (i = 0; i < number_of_resources; i++) {
+		list[i].bo_handle = resources[i]->handle;
+		if (resource_prios)
+			list[i].bo_priority = resource_prios[i];
+		else
+			list[i].bo_priority = 0;
+	}
+
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_BO_LIST,
+				&args, sizeof(args));
+	free(list);
+	if (r) {
+		free(*result);
+		return r;
+	}
+
+	(*result)->dev = dev;
+	(*result)->handle = args.out.list_handle;
+	return 0;
+}
+
+int amdgpu_bo_list_destroy(amdgpu_bo_list_handle list)
+{
+	union drm_amdgpu_bo_list args;
+	int r;
+
+	memset(&args, 0, sizeof(args));
+	args.in.operation = AMDGPU_BO_LIST_OP_DESTROY;
+	args.in.list_handle = list->handle;
+
+	r = drmCommandWriteRead(list->dev->fd, DRM_AMDGPU_BO_LIST,
+				&args, sizeof(args));
+
+	if (!r)
+		free(list);
+
+	return r;
+}
+
+int amdgpu_bo_list_update(amdgpu_bo_list_handle handle,
+			  uint32_t number_of_resources,
+			  amdgpu_bo_handle *resources,
+			  uint8_t *resource_prios)
+{
+	struct drm_amdgpu_bo_list_entry *list;
+	union drm_amdgpu_bo_list args;
+	unsigned i;
+	int r;
+
+	if (!number_of_resources)
+		return -EINVAL;
+
+	/* overflow check for multiplication */
+	if (number_of_resources > UINT32_MAX / sizeof(struct drm_amdgpu_bo_list_entry))
+		return -EINVAL;
+
+	list = malloc(number_of_resources * sizeof(struct drm_amdgpu_bo_list_entry));
+	if (!list)
+		return -ENOMEM;
+
+	args.in.operation = AMDGPU_BO_LIST_OP_UPDATE;
+	args.in.list_handle = handle->handle;
+	args.in.bo_number = number_of_resources;
+	args.in.bo_info_size = sizeof(struct drm_amdgpu_bo_list_entry);
+	args.in.bo_info_ptr = (uintptr_t)list;
+
+	for (i = 0; i < number_of_resources; i++) {
+		list[i].bo_handle = resources[i]->handle;
+		if (resource_prios)
+			list[i].bo_priority = resource_prios[i];
+		else
+			list[i].bo_priority = 0;
+	}
+
+	r = drmCommandWriteRead(handle->dev->fd, DRM_AMDGPU_BO_LIST,
+				&args, sizeof(args));
+	free(list);
+	return r;
+}
+
+int amdgpu_bo_va_op(amdgpu_bo_handle bo,
+		     uint64_t offset,
+		     uint64_t size,
+		     uint64_t addr,
+		     uint64_t flags,
+		     uint32_t ops)
+{
+	amdgpu_device_handle dev = bo->dev;
+
+	size = ALIGN(size, getpagesize());
+
+	return amdgpu_bo_va_op_raw(dev, bo, offset, size, addr,
+				   AMDGPU_VM_PAGE_READABLE |
+				   AMDGPU_VM_PAGE_WRITEABLE |
+				   AMDGPU_VM_PAGE_EXECUTABLE, ops);
+}
+
+int amdgpu_bo_va_op_raw(amdgpu_device_handle dev,
+			amdgpu_bo_handle bo,
+			uint64_t offset,
+			uint64_t size,
+			uint64_t addr,
+			uint64_t flags,
+			uint32_t ops)
+{
+	struct drm_amdgpu_gem_va va;
+	int r;
+
+	if (ops != AMDGPU_VA_OP_MAP && ops != AMDGPU_VA_OP_UNMAP &&
+	    ops != AMDGPU_VA_OP_REPLACE && ops != AMDGPU_VA_OP_CLEAR)
+		return -EINVAL;
+
+	memset(&va, 0, sizeof(va));
+	va.handle = bo ? bo->handle : 0;
+	va.operation = ops;
+	va.flags = flags;
+	va.va_address = addr;
+	va.offset_in_bo = offset;
+	va.map_size = size;
+
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_VA, &va, sizeof(va));
+
+	return r;
+}
diff --git a/amdgpu/amdgpu_cs.c b/amdgpu/amdgpu_cs.c
new file mode 100644
index 0000000..9577d5c
--- /dev/null
+++ b/amdgpu/amdgpu_cs.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright 2014 Advanced Micro Devices, Inc.
+ *
+ * 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 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 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#endif
+
+#include "xf86drm.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+
+static int amdgpu_cs_unreference_sem(amdgpu_semaphore_handle sem);
+static int amdgpu_cs_reset_sem(amdgpu_semaphore_handle sem);
+
+/**
+ * Create command submission context
+ *
+ * \param   dev - \c [in] amdgpu device handle
+ * \param   context - \c [out] amdgpu context handle
+ *
+ * \return  0 on success otherwise POSIX Error code
+*/
+int amdgpu_cs_ctx_create(amdgpu_device_handle dev,
+			 amdgpu_context_handle *context)
+{
+	struct amdgpu_context *gpu_context;
+	union drm_amdgpu_ctx args;
+	int i, j, k;
+	int r;
+
+	if (!dev || !context)
+		return -EINVAL;
+
+	gpu_context = calloc(1, sizeof(struct amdgpu_context));
+	if (!gpu_context)
+		return -ENOMEM;
+
+	gpu_context->dev = dev;
+
+	r = pthread_mutex_init(&gpu_context->sequence_mutex, NULL);
+	if (r)
+		goto error;
+
+	/* Create the context */
+	memset(&args, 0, sizeof(args));
+	args.in.op = AMDGPU_CTX_OP_ALLOC_CTX;
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_CTX, &args, sizeof(args));
+	if (r)
+		goto error;
+
+	gpu_context->id = args.out.alloc.ctx_id;
+	for (i = 0; i < AMDGPU_HW_IP_NUM; i++)
+		for (j = 0; j < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; j++)
+			for (k = 0; k < AMDGPU_CS_MAX_RINGS; k++)
+				list_inithead(&gpu_context->sem_list[i][j][k]);
+	*context = (amdgpu_context_handle)gpu_context;
+
+	return 0;
+
+error:
+	pthread_mutex_destroy(&gpu_context->sequence_mutex);
+	free(gpu_context);
+	return r;
+}
+
+/**
+ * Release command submission context
+ *
+ * \param   dev - \c [in] amdgpu device handle
+ * \param   context - \c [in] amdgpu context handle
+ *
+ * \return  0 on success otherwise POSIX Error code
+*/
+int amdgpu_cs_ctx_free(amdgpu_context_handle context)
+{
+	union drm_amdgpu_ctx args;
+	int i, j, k;
+	int r;
+
+	if (!context)
+		return -EINVAL;
+
+	pthread_mutex_destroy(&context->sequence_mutex);
+
+	/* now deal with kernel side */
+	memset(&args, 0, sizeof(args));
+	args.in.op = AMDGPU_CTX_OP_FREE_CTX;
+	args.in.ctx_id = context->id;
+	r = drmCommandWriteRead(context->dev->fd, DRM_AMDGPU_CTX,
+				&args, sizeof(args));
+	for (i = 0; i < AMDGPU_HW_IP_NUM; i++) {
+		for (j = 0; j < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; j++) {
+			for (k = 0; k < AMDGPU_CS_MAX_RINGS; k++) {
+				amdgpu_semaphore_handle sem;
+				LIST_FOR_EACH_ENTRY(sem, &context->sem_list[i][j][k], list) {
+					list_del(&sem->list);
+					amdgpu_cs_reset_sem(sem);
+					amdgpu_cs_unreference_sem(sem);
+				}
+			}
+		}
+	}
+	free(context);
+
+	return r;
+}
+
+int amdgpu_cs_query_reset_state(amdgpu_context_handle context,
+				uint32_t *state, uint32_t *hangs)
+{
+	union drm_amdgpu_ctx args;
+	int r;
+
+	if (!context)
+		return -EINVAL;
+
+	memset(&args, 0, sizeof(args));
+	args.in.op = AMDGPU_CTX_OP_QUERY_STATE;
+	args.in.ctx_id = context->id;
+	r = drmCommandWriteRead(context->dev->fd, DRM_AMDGPU_CTX,
+				&args, sizeof(args));
+	if (!r) {
+		*state = args.out.state.reset_status;
+		*hangs = args.out.state.hangs;
+	}
+	return r;
+}
+
+/**
+ * Submit command to kernel DRM
+ * \param   dev - \c [in]  Device handle
+ * \param   context - \c [in]  GPU Context
+ * \param   ibs_request - \c [in]  Pointer to submission requests
+ * \param   fence - \c [out] return fence for this submission
+ *
+ * \return  0 on success otherwise POSIX Error code
+ * \sa amdgpu_cs_submit()
+*/
+static int amdgpu_cs_submit_one(amdgpu_context_handle context,
+				struct amdgpu_cs_request *ibs_request)
+{
+	union drm_amdgpu_cs cs;
+	uint64_t *chunk_array;
+	struct drm_amdgpu_cs_chunk *chunks;
+	struct drm_amdgpu_cs_chunk_data *chunk_data;
+	struct drm_amdgpu_cs_chunk_dep *dependencies = NULL;
+	struct drm_amdgpu_cs_chunk_dep *sem_dependencies = NULL;
+	struct list_head *sem_list;
+	amdgpu_semaphore_handle sem, tmp;
+	uint32_t i, size, sem_count = 0;
+	bool user_fence;
+	int r = 0;
+
+	if (ibs_request->ip_type >= AMDGPU_HW_IP_NUM)
+		return -EINVAL;
+	if (ibs_request->ring >= AMDGPU_CS_MAX_RINGS)
+		return -EINVAL;
+	if (ibs_request->number_of_ibs > AMDGPU_CS_MAX_IBS_PER_SUBMIT)
+		return -EINVAL;
+	if (ibs_request->number_of_ibs == 0) {
+		ibs_request->seq_no = AMDGPU_NULL_SUBMIT_SEQ;
+		return 0;
+	}
+	user_fence = (ibs_request->fence_info.handle != NULL);
+
+	size = ibs_request->number_of_ibs + (user_fence ? 2 : 1) + 1;
+
+	chunk_array = alloca(sizeof(uint64_t) * size);
+	chunks = alloca(sizeof(struct drm_amdgpu_cs_chunk) * size);
+
+	size = ibs_request->number_of_ibs + (user_fence ? 1 : 0);
+
+	chunk_data = alloca(sizeof(struct drm_amdgpu_cs_chunk_data) * size);
+
+	memset(&cs, 0, sizeof(cs));
+	cs.in.chunks = (uint64_t)(uintptr_t)chunk_array;
+	cs.in.ctx_id = context->id;
+	if (ibs_request->resources)
+		cs.in.bo_list_handle = ibs_request->resources->handle;
+	cs.in.num_chunks = ibs_request->number_of_ibs;
+	/* IB chunks */
+	for (i = 0; i < ibs_request->number_of_ibs; i++) {
+		struct amdgpu_cs_ib_info *ib;
+		chunk_array[i] = (uint64_t)(uintptr_t)&chunks[i];
+		chunks[i].chunk_id = AMDGPU_CHUNK_ID_IB;
+		chunks[i].length_dw = sizeof(struct drm_amdgpu_cs_chunk_ib) / 4;
+		chunks[i].chunk_data = (uint64_t)(uintptr_t)&chunk_data[i];
+
+		ib = &ibs_request->ibs[i];
+
+		chunk_data[i].ib_data._pad = 0;
+		chunk_data[i].ib_data.va_start = ib->ib_mc_address;
+		chunk_data[i].ib_data.ib_bytes = ib->size * 4;
+		chunk_data[i].ib_data.ip_type = ibs_request->ip_type;
+		chunk_data[i].ib_data.ip_instance = ibs_request->ip_instance;
+		chunk_data[i].ib_data.ring = ibs_request->ring;
+		chunk_data[i].ib_data.flags = ib->flags;
+	}
+
+	pthread_mutex_lock(&context->sequence_mutex);
+
+	if (user_fence) {
+		i = cs.in.num_chunks++;
+
+		/* fence chunk */
+		chunk_array[i] = (uint64_t)(uintptr_t)&chunks[i];
+		chunks[i].chunk_id = AMDGPU_CHUNK_ID_FENCE;
+		chunks[i].length_dw = sizeof(struct drm_amdgpu_cs_chunk_fence) / 4;
+		chunks[i].chunk_data = (uint64_t)(uintptr_t)&chunk_data[i];
+
+		/* fence bo handle */
+		chunk_data[i].fence_data.handle = ibs_request->fence_info.handle->handle;
+		/* offset */
+		chunk_data[i].fence_data.offset = 
+			ibs_request->fence_info.offset * sizeof(uint64_t);
+	}
+
+	if (ibs_request->number_of_dependencies) {
+		dependencies = malloc(sizeof(struct drm_amdgpu_cs_chunk_dep) *
+			ibs_request->number_of_dependencies);
+		if (!dependencies) {
+			r = -ENOMEM;
+			goto error_unlock;
+		}
+
+		for (i = 0; i < ibs_request->number_of_dependencies; ++i) {
+			struct amdgpu_cs_fence *info = &ibs_request->dependencies[i];
+			struct drm_amdgpu_cs_chunk_dep *dep = &dependencies[i];
+			dep->ip_type = info->ip_type;
+			dep->ip_instance = info->ip_instance;
+			dep->ring = info->ring;
+			dep->ctx_id = info->context->id;
+			dep->handle = info->fence;
+		}
+
+		i = cs.in.num_chunks++;
+
+		/* dependencies chunk */
+		chunk_array[i] = (uint64_t)(uintptr_t)&chunks[i];
+		chunks[i].chunk_id = AMDGPU_CHUNK_ID_DEPENDENCIES;
+		chunks[i].length_dw = sizeof(struct drm_amdgpu_cs_chunk_dep) / 4
+			* ibs_request->number_of_dependencies;
+		chunks[i].chunk_data = (uint64_t)(uintptr_t)dependencies;
+	}
+
+	sem_list = &context->sem_list[ibs_request->ip_type][ibs_request->ip_instance][ibs_request->ring];
+	LIST_FOR_EACH_ENTRY(sem, sem_list, list)
+		sem_count++;
+	if (sem_count) {
+		sem_dependencies = malloc(sizeof(struct drm_amdgpu_cs_chunk_dep) * sem_count);
+		if (!sem_dependencies) {
+			r = -ENOMEM;
+			goto error_unlock;
+		}
+		sem_count = 0;
+		LIST_FOR_EACH_ENTRY_SAFE(sem, tmp, sem_list, list) {
+			struct amdgpu_cs_fence *info = &sem->signal_fence;
+			struct drm_amdgpu_cs_chunk_dep *dep = &sem_dependencies[sem_count++];
+			dep->ip_type = info->ip_type;
+			dep->ip_instance = info->ip_instance;
+			dep->ring = info->ring;
+			dep->ctx_id = info->context->id;
+			dep->handle = info->fence;
+
+			list_del(&sem->list);
+			amdgpu_cs_reset_sem(sem);
+			amdgpu_cs_unreference_sem(sem);
+		}
+		i = cs.in.num_chunks++;
+
+		/* dependencies chunk */
+		chunk_array[i] = (uint64_t)(uintptr_t)&chunks[i];
+		chunks[i].chunk_id = AMDGPU_CHUNK_ID_DEPENDENCIES;
+		chunks[i].length_dw = sizeof(struct drm_amdgpu_cs_chunk_dep) / 4 * sem_count;
+		chunks[i].chunk_data = (uint64_t)(uintptr_t)sem_dependencies;
+	}
+
+	r = drmCommandWriteRead(context->dev->fd, DRM_AMDGPU_CS,
+				&cs, sizeof(cs));
+	if (r)
+		goto error_unlock;
+
+	ibs_request->seq_no = cs.out.handle;
+	context->last_seq[ibs_request->ip_type][ibs_request->ip_instance][ibs_request->ring] = ibs_request->seq_no;
+error_unlock:
+	pthread_mutex_unlock(&context->sequence_mutex);
+	free(dependencies);
+	free(sem_dependencies);
+	return r;
+}
+
+int amdgpu_cs_submit(amdgpu_context_handle context,
+		     uint64_t flags,
+		     struct amdgpu_cs_request *ibs_request,
+		     uint32_t number_of_requests)
+{
+	uint32_t i;
+	int r;
+
+	if (!context || !ibs_request)
+		return -EINVAL;
+
+	r = 0;
+	for (i = 0; i < number_of_requests; i++) {
+		r = amdgpu_cs_submit_one(context, ibs_request);
+		if (r)
+			break;
+		ibs_request++;
+	}
+
+	return r;
+}
+
+/**
+ * Calculate absolute timeout.
+ *
+ * \param   timeout - \c [in] timeout in nanoseconds.
+ *
+ * \return  absolute timeout in nanoseconds
+*/
+drm_private uint64_t amdgpu_cs_calculate_timeout(uint64_t timeout)
+{
+	int r;
+
+	if (timeout != AMDGPU_TIMEOUT_INFINITE) {
+		struct timespec current;
+		uint64_t current_ns;
+		r = clock_gettime(CLOCK_MONOTONIC, &current);
+		if (r) {
+			fprintf(stderr, "clock_gettime() returned error (%d)!", errno);
+			return AMDGPU_TIMEOUT_INFINITE;
+		}
+
+		current_ns = ((uint64_t)current.tv_sec) * 1000000000ull;
+		current_ns += current.tv_nsec;
+		timeout += current_ns;
+		if (timeout < current_ns)
+			timeout = AMDGPU_TIMEOUT_INFINITE;
+	}
+	return timeout;
+}
+
+static int amdgpu_ioctl_wait_cs(amdgpu_context_handle context,
+				unsigned ip,
+				unsigned ip_instance,
+				uint32_t ring,
+				uint64_t handle,
+				uint64_t timeout_ns,
+				uint64_t flags,
+				bool *busy)
+{
+	amdgpu_device_handle dev = context->dev;
+	union drm_amdgpu_wait_cs args;
+	int r;
+
+	memset(&args, 0, sizeof(args));
+	args.in.handle = handle;
+	args.in.ip_type = ip;
+	args.in.ip_instance = ip_instance;
+	args.in.ring = ring;
+	args.in.ctx_id = context->id;
+
+	if (flags & AMDGPU_QUERY_FENCE_TIMEOUT_IS_ABSOLUTE)
+		args.in.timeout = timeout_ns;
+	else
+		args.in.timeout = amdgpu_cs_calculate_timeout(timeout_ns);
+
+	r = drmIoctl(dev->fd, DRM_IOCTL_AMDGPU_WAIT_CS, &args);
+	if (r)
+		return -errno;
+
+	*busy = args.out.status;
+	return 0;
+}
+
+int amdgpu_cs_query_fence_status(struct amdgpu_cs_fence *fence,
+				 uint64_t timeout_ns,
+				 uint64_t flags,
+				 uint32_t *expired)
+{
+	bool busy = true;
+	int r;
+
+	if (!fence || !expired || !fence->context)
+		return -EINVAL;
+	if (fence->ip_type >= AMDGPU_HW_IP_NUM)
+		return -EINVAL;
+	if (fence->ring >= AMDGPU_CS_MAX_RINGS)
+		return -EINVAL;
+	if (fence->fence == AMDGPU_NULL_SUBMIT_SEQ) {
+		*expired = true;
+		return 0;
+	}
+
+	*expired = false;
+
+	r = amdgpu_ioctl_wait_cs(fence->context, fence->ip_type,
+				fence->ip_instance, fence->ring,
+			       	fence->fence, timeout_ns, flags, &busy);
+
+	if (!r && !busy)
+		*expired = true;
+
+	return r;
+}
+
+static int amdgpu_ioctl_wait_fences(struct amdgpu_cs_fence *fences,
+				    uint32_t fence_count,
+				    bool wait_all,
+				    uint64_t timeout_ns,
+				    uint32_t *status,
+				    uint32_t *first)
+{
+	struct drm_amdgpu_fence *drm_fences;
+	amdgpu_device_handle dev = fences[0].context->dev;
+	union drm_amdgpu_wait_fences args;
+	int r;
+	uint32_t i;
+
+	drm_fences = alloca(sizeof(struct drm_amdgpu_fence) * fence_count);
+	for (i = 0; i < fence_count; i++) {
+		drm_fences[i].ctx_id = fences[i].context->id;
+		drm_fences[i].ip_type = fences[i].ip_type;
+		drm_fences[i].ip_instance = fences[i].ip_instance;
+		drm_fences[i].ring = fences[i].ring;
+		drm_fences[i].seq_no = fences[i].fence;
+	}
+
+	memset(&args, 0, sizeof(args));
+	args.in.fences = (uint64_t)(uintptr_t)drm_fences;
+	args.in.fence_count = fence_count;
+	args.in.wait_all = wait_all;
+	args.in.timeout_ns = amdgpu_cs_calculate_timeout(timeout_ns);
+
+	r = drmIoctl(dev->fd, DRM_IOCTL_AMDGPU_WAIT_FENCES, &args);
+	if (r)
+		return -errno;
+
+	*status = args.out.status;
+
+	if (first)
+		*first = args.out.first_signaled;
+
+	return 0;
+}
+
+int amdgpu_cs_wait_fences(struct amdgpu_cs_fence *fences,
+			  uint32_t fence_count,
+			  bool wait_all,
+			  uint64_t timeout_ns,
+			  uint32_t *status,
+			  uint32_t *first)
+{
+	uint32_t i;
+
+	/* Sanity check */
+	if (!fences || !status || !fence_count)
+		return -EINVAL;
+
+	for (i = 0; i < fence_count; i++) {
+		if (NULL == fences[i].context)
+			return -EINVAL;
+		if (fences[i].ip_type >= AMDGPU_HW_IP_NUM)
+			return -EINVAL;
+		if (fences[i].ring >= AMDGPU_CS_MAX_RINGS)
+			return -EINVAL;
+	}
+
+	*status = 0;
+
+	return amdgpu_ioctl_wait_fences(fences, fence_count, wait_all,
+					timeout_ns, status, first);
+}
+
+int amdgpu_cs_create_semaphore(amdgpu_semaphore_handle *sem)
+{
+	struct amdgpu_semaphore *gpu_semaphore;
+
+	if (!sem)
+		return -EINVAL;
+
+	gpu_semaphore = calloc(1, sizeof(struct amdgpu_semaphore));
+	if (!gpu_semaphore)
+		return -ENOMEM;
+
+	atomic_set(&gpu_semaphore->refcount, 1);
+	*sem = gpu_semaphore;
+
+	return 0;
+}
+
+int amdgpu_cs_signal_semaphore(amdgpu_context_handle ctx,
+			       uint32_t ip_type,
+			       uint32_t ip_instance,
+			       uint32_t ring,
+			       amdgpu_semaphore_handle sem)
+{
+	if (!ctx || !sem)
+		return -EINVAL;
+	if (ip_type >= AMDGPU_HW_IP_NUM)
+		return -EINVAL;
+	if (ring >= AMDGPU_CS_MAX_RINGS)
+		return -EINVAL;
+	/* sem has been signaled */
+	if (sem->signal_fence.context)
+		return -EINVAL;
+	pthread_mutex_lock(&ctx->sequence_mutex);
+	sem->signal_fence.context = ctx;
+	sem->signal_fence.ip_type = ip_type;
+	sem->signal_fence.ip_instance = ip_instance;
+	sem->signal_fence.ring = ring;
+	sem->signal_fence.fence = ctx->last_seq[ip_type][ip_instance][ring];
+	update_references(NULL, &sem->refcount);
+	pthread_mutex_unlock(&ctx->sequence_mutex);
+	return 0;
+}
+
+int amdgpu_cs_wait_semaphore(amdgpu_context_handle ctx,
+			     uint32_t ip_type,
+			     uint32_t ip_instance,
+			     uint32_t ring,
+			     amdgpu_semaphore_handle sem)
+{
+	if (!ctx || !sem)
+		return -EINVAL;
+	if (ip_type >= AMDGPU_HW_IP_NUM)
+		return -EINVAL;
+	if (ring >= AMDGPU_CS_MAX_RINGS)
+		return -EINVAL;
+	/* must signal first */
+	if (!sem->signal_fence.context)
+		return -EINVAL;
+
+	pthread_mutex_lock(&ctx->sequence_mutex);
+	list_add(&sem->list, &ctx->sem_list[ip_type][ip_instance][ring]);
+	pthread_mutex_unlock(&ctx->sequence_mutex);
+	return 0;
+}
+
+static int amdgpu_cs_reset_sem(amdgpu_semaphore_handle sem)
+{
+	if (!sem || !sem->signal_fence.context)
+		return -EINVAL;
+
+	sem->signal_fence.context = NULL;;
+	sem->signal_fence.ip_type = 0;
+	sem->signal_fence.ip_instance = 0;
+	sem->signal_fence.ring = 0;
+	sem->signal_fence.fence = 0;
+
+	return 0;
+}
+
+static int amdgpu_cs_unreference_sem(amdgpu_semaphore_handle sem)
+{
+	if (!sem)
+		return -EINVAL;
+
+	if (update_references(&sem->refcount, NULL))
+		free(sem);
+	return 0;
+}
+
+int amdgpu_cs_destroy_semaphore(amdgpu_semaphore_handle sem)
+{
+	return amdgpu_cs_unreference_sem(sem);
+}
+
+int amdgpu_cs_create_syncobj(amdgpu_device_handle dev,
+			     uint32_t *handle)
+{
+	if (NULL == dev)
+		return -EINVAL;
+
+	return drmSyncobjCreate(dev->fd, 0, handle);
+}
+
+int amdgpu_cs_destroy_syncobj(amdgpu_device_handle dev,
+			      uint32_t handle)
+{
+	if (NULL == dev)
+		return -EINVAL;
+
+	return drmSyncobjDestroy(dev->fd, handle);
+}
+
+int amdgpu_cs_syncobj_wait(amdgpu_device_handle dev,
+			   uint32_t *handles, unsigned num_handles,
+			   int64_t timeout_nsec, unsigned flags,
+			   uint32_t *first_signaled)
+{
+	if (NULL == dev)
+		return -EINVAL;
+
+	return drmSyncobjWait(dev->fd, handles, num_handles, timeout_nsec,
+			      flags, first_signaled);
+}
+
+int amdgpu_cs_export_syncobj(amdgpu_device_handle dev,
+			     uint32_t handle,
+			     int *shared_fd)
+{
+	if (NULL == dev)
+		return -EINVAL;
+
+	return drmSyncobjHandleToFD(dev->fd, handle, shared_fd);
+}
+
+int amdgpu_cs_import_syncobj(amdgpu_device_handle dev,
+			     int shared_fd,
+			     uint32_t *handle)
+{
+	if (NULL == dev)
+		return -EINVAL;
+
+	return drmSyncobjFDToHandle(dev->fd, shared_fd, handle);
+}
+
+int amdgpu_cs_syncobj_export_sync_file(amdgpu_device_handle dev,
+				       uint32_t syncobj,
+				       int *sync_file_fd)
+{
+	if (NULL == dev)
+		return -EINVAL;
+
+	return drmSyncobjExportSyncFile(dev->fd, syncobj, sync_file_fd);
+}
+
+int amdgpu_cs_syncobj_import_sync_file(amdgpu_device_handle dev,
+				       uint32_t syncobj,
+				       int sync_file_fd)
+{
+	if (NULL == dev)
+		return -EINVAL;
+
+	return drmSyncobjImportSyncFile(dev->fd, syncobj, sync_file_fd);
+}
+
+int amdgpu_cs_submit_raw(amdgpu_device_handle dev,
+			 amdgpu_context_handle context,
+			 amdgpu_bo_list_handle bo_list_handle,
+			 int num_chunks,
+			 struct drm_amdgpu_cs_chunk *chunks,
+			 uint64_t *seq_no)
+{
+	union drm_amdgpu_cs cs = {0};
+	uint64_t *chunk_array;
+	int i, r;
+	if (num_chunks == 0)
+		return -EINVAL;
+
+	chunk_array = alloca(sizeof(uint64_t) * num_chunks);
+	for (i = 0; i < num_chunks; i++)
+		chunk_array[i] = (uint64_t)(uintptr_t)&chunks[i];
+	cs.in.chunks = (uint64_t)(uintptr_t)chunk_array;
+	cs.in.ctx_id = context->id;
+	cs.in.bo_list_handle = bo_list_handle ? bo_list_handle->handle : 0;
+	cs.in.num_chunks = num_chunks;
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_CS,
+				&cs, sizeof(cs));
+	if (r)
+		return r;
+
+	if (seq_no)
+		*seq_no = cs.out.handle;
+	return 0;
+}
+
+void amdgpu_cs_chunk_fence_info_to_data(struct amdgpu_cs_fence_info *fence_info,
+					struct drm_amdgpu_cs_chunk_data *data)
+{
+	data->fence_data.handle = fence_info->handle->handle;
+	data->fence_data.offset = fence_info->offset * sizeof(uint64_t);
+}
+
+void amdgpu_cs_chunk_fence_to_dep(struct amdgpu_cs_fence *fence,
+				  struct drm_amdgpu_cs_chunk_dep *dep)
+{
+	dep->ip_type = fence->ip_type;
+	dep->ip_instance = fence->ip_instance;
+	dep->ring = fence->ring;
+	dep->ctx_id = fence->context->id;
+	dep->handle = fence->fence;
+}
+
+int amdgpu_cs_fence_to_handle(amdgpu_device_handle dev,
+			      struct amdgpu_cs_fence *fence,
+			      uint32_t what,
+			      uint32_t *out_handle)
+{
+	union drm_amdgpu_fence_to_handle fth = {0};
+	int r;
+
+	fth.in.fence.ctx_id = fence->context->id;
+	fth.in.fence.ip_type = fence->ip_type;
+	fth.in.fence.ip_instance = fence->ip_instance;
+	fth.in.fence.ring = fence->ring;
+	fth.in.fence.seq_no = fence->fence;
+	fth.in.what = what;
+
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_FENCE_TO_HANDLE,
+				&fth, sizeof(fth));
+	if (r == 0)
+		*out_handle = fth.out.handle;
+	return r;
+}
diff --git a/amdgpu/amdgpu_device.c b/amdgpu/amdgpu_device.c
new file mode 100644
index 0000000..2b31c45
--- /dev/null
+++ b/amdgpu/amdgpu_device.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2014 Advanced Micro Devices, Inc.
+ *
+ * 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 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 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.
+ *
+ */
+
+/**
+ * \file amdgpu_device.c
+ *
+ *  Implementation of functions for AMD GPU device
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "xf86drm.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+#include "util_hash_table.h"
+#include "util_math.h"
+
+#define PTR_TO_UINT(x) ((unsigned)((intptr_t)(x)))
+#define UINT_TO_PTR(x) ((void *)((intptr_t)(x)))
+
+static pthread_mutex_t fd_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct util_hash_table *fd_tab;
+
+static unsigned handle_hash(void *key)
+{
+	return PTR_TO_UINT(key);
+}
+
+static int handle_compare(void *key1, void *key2)
+{
+	return PTR_TO_UINT(key1) != PTR_TO_UINT(key2);
+}
+
+static unsigned fd_hash(void *key)
+{
+	int fd = PTR_TO_UINT(key);
+	char *name = drmGetPrimaryDeviceNameFromFd(fd);
+	unsigned result = 0;
+	char *c;
+
+	if (name == NULL)
+		return 0;
+
+	for (c = name; *c; ++c)
+		result += *c;
+
+	free(name);
+
+	return result;
+}
+
+static int fd_compare(void *key1, void *key2)
+{
+	int fd1 = PTR_TO_UINT(key1);
+	int fd2 = PTR_TO_UINT(key2);
+	char *name1 = drmGetPrimaryDeviceNameFromFd(fd1);
+	char *name2 = drmGetPrimaryDeviceNameFromFd(fd2);
+	int result;
+
+	if (name1 == NULL || name2 == NULL) {
+		free(name1);
+		free(name2);
+		return 0;
+	}
+
+	result = strcmp(name1, name2);
+	free(name1);
+	free(name2);
+
+	return result;
+}
+
+/**
+* Get the authenticated form fd,
+*
+* \param   fd   - \c [in]  File descriptor for AMD GPU device
+* \param   auth - \c [out] Pointer to output the fd is authenticated or not
+*                          A render node fd, output auth = 0
+*                          A legacy fd, get the authenticated for compatibility root
+*
+* \return   0 on success\n
+*          >0 - AMD specific error code\n
+*          <0 - Negative POSIX Error code
+*/
+static int amdgpu_get_auth(int fd, int *auth)
+{
+	int r = 0;
+	drm_client_t client = {};
+
+	if (drmGetNodeTypeFromFd(fd) == DRM_NODE_RENDER)
+		*auth = 0;
+	else {
+		client.idx = 0;
+		r = drmIoctl(fd, DRM_IOCTL_GET_CLIENT, &client);
+		if (!r)
+			*auth = client.auth;
+	}
+	return r;
+}
+
+static void amdgpu_device_free_internal(amdgpu_device_handle dev)
+{
+	const struct amdgpu_asic_id *id;
+	amdgpu_vamgr_deinit(&dev->vamgr_32);
+	amdgpu_vamgr_deinit(&dev->vamgr);
+	util_hash_table_destroy(dev->bo_flink_names);
+	util_hash_table_destroy(dev->bo_handles);
+	pthread_mutex_destroy(&dev->bo_table_mutex);
+	util_hash_table_remove(fd_tab, UINT_TO_PTR(dev->fd));
+	close(dev->fd);
+	if ((dev->flink_fd >= 0) && (dev->fd != dev->flink_fd))
+		close(dev->flink_fd);
+	if (dev->asic_ids) {
+		for (id = dev->asic_ids; id->did; id++)
+			free(id->marketing_name);
+
+		free(dev->asic_ids);
+	}
+	free(dev);
+}
+
+/**
+ * Assignment between two amdgpu_device pointers with reference counting.
+ *
+ * Usage:
+ *    struct amdgpu_device *dst = ... , *src = ...;
+ *
+ *    dst = src;
+ *    // No reference counting. Only use this when you need to move
+ *    // a reference from one pointer to another.
+ *
+ *    amdgpu_device_reference(&dst, src);
+ *    // Reference counters are updated. dst is decremented and src is
+ *    // incremented. dst is freed if its reference counter is 0.
+ */
+static void amdgpu_device_reference(struct amdgpu_device **dst,
+			     struct amdgpu_device *src)
+{
+	if (update_references(&(*dst)->refcount, &src->refcount))
+		amdgpu_device_free_internal(*dst);
+	*dst = src;
+}
+
+int amdgpu_device_initialize(int fd,
+			     uint32_t *major_version,
+			     uint32_t *minor_version,
+			     amdgpu_device_handle *device_handle)
+{
+	struct amdgpu_device *dev;
+	drmVersionPtr version;
+	int r;
+	int flag_auth = 0;
+	int flag_authexist=0;
+	uint32_t accel_working = 0;
+	uint64_t start, max;
+
+	*device_handle = NULL;
+
+	pthread_mutex_lock(&fd_mutex);
+	if (!fd_tab)
+		fd_tab = util_hash_table_create(fd_hash, fd_compare);
+	r = amdgpu_get_auth(fd, &flag_auth);
+	if (r) {
+		fprintf(stderr, "%s: amdgpu_get_auth (1) failed (%i)\n",
+			__func__, r);
+		pthread_mutex_unlock(&fd_mutex);
+		return r;
+	}
+	dev = util_hash_table_get(fd_tab, UINT_TO_PTR(fd));
+	if (dev) {
+		r = amdgpu_get_auth(dev->fd, &flag_authexist);
+		if (r) {
+			fprintf(stderr, "%s: amdgpu_get_auth (2) failed (%i)\n",
+				__func__, r);
+			pthread_mutex_unlock(&fd_mutex);
+			return r;
+		}
+		if ((flag_auth) && (!flag_authexist)) {
+			dev->flink_fd = dup(fd);
+		}
+		*major_version = dev->major_version;
+		*minor_version = dev->minor_version;
+		amdgpu_device_reference(device_handle, dev);
+		pthread_mutex_unlock(&fd_mutex);
+		return 0;
+	}
+
+	dev = calloc(1, sizeof(struct amdgpu_device));
+	if (!dev) {
+		fprintf(stderr, "%s: calloc failed\n", __func__);
+		pthread_mutex_unlock(&fd_mutex);
+		return -ENOMEM;
+	}
+
+	dev->fd = -1;
+	dev->flink_fd = -1;
+
+	atomic_set(&dev->refcount, 1);
+
+	version = drmGetVersion(fd);
+	if (version->version_major != 3) {
+		fprintf(stderr, "%s: DRM version is %d.%d.%d but this driver is "
+			"only compatible with 3.x.x.\n",
+			__func__,
+			version->version_major,
+			version->version_minor,
+			version->version_patchlevel);
+		drmFreeVersion(version);
+		r = -EBADF;
+		goto cleanup;
+	}
+
+	dev->fd = dup(fd);
+	dev->flink_fd = dev->fd;
+	dev->major_version = version->version_major;
+	dev->minor_version = version->version_minor;
+	drmFreeVersion(version);
+
+	dev->bo_flink_names = util_hash_table_create(handle_hash,
+						     handle_compare);
+	dev->bo_handles = util_hash_table_create(handle_hash, handle_compare);
+	pthread_mutex_init(&dev->bo_table_mutex, NULL);
+
+	/* Check if acceleration is working. */
+	r = amdgpu_query_info(dev, AMDGPU_INFO_ACCEL_WORKING, 4, &accel_working);
+	if (r) {
+		fprintf(stderr, "%s: amdgpu_query_info(ACCEL_WORKING) failed (%i)\n",
+			__func__, r);
+		goto cleanup;
+	}
+	if (!accel_working) {
+		fprintf(stderr, "%s: AMDGPU_INFO_ACCEL_WORKING = 0\n", __func__);
+		r = -EBADF;
+		goto cleanup;
+	}
+
+	r = amdgpu_query_gpu_info_init(dev);
+	if (r) {
+		fprintf(stderr, "%s: amdgpu_query_gpu_info_init failed\n", __func__);
+		goto cleanup;
+	}
+
+	amdgpu_vamgr_init(&dev->vamgr, dev->dev_info.virtual_address_offset,
+			  dev->dev_info.virtual_address_max,
+			  dev->dev_info.virtual_address_alignment);
+
+	max = MIN2(dev->dev_info.virtual_address_max, 0xffffffff);
+	start = amdgpu_vamgr_find_va(&dev->vamgr,
+				     max - dev->dev_info.virtual_address_offset,
+				     dev->dev_info.virtual_address_alignment, 0);
+	if (start > 0xffffffff) {
+		fprintf(stderr, "%s: amdgpu_vamgr_find_va failed\n", __func__);
+		goto free_va; /* shouldn't get here */
+	}
+
+	amdgpu_vamgr_init(&dev->vamgr_32, start, max,
+			  dev->dev_info.virtual_address_alignment);
+
+	r = amdgpu_parse_asic_ids(&dev->asic_ids);
+	if (r) {
+		fprintf(stderr, "%s: Cannot parse ASIC IDs, 0x%x.",
+			__func__, r);
+	}
+
+	*major_version = dev->major_version;
+	*minor_version = dev->minor_version;
+	*device_handle = dev;
+	util_hash_table_set(fd_tab, UINT_TO_PTR(dev->fd), dev);
+	pthread_mutex_unlock(&fd_mutex);
+
+	return 0;
+
+free_va:
+	r = -ENOMEM;
+	amdgpu_vamgr_free_va(&dev->vamgr, start,
+			     max - dev->dev_info.virtual_address_offset);
+	amdgpu_vamgr_deinit(&dev->vamgr);
+
+cleanup:
+	if (dev->fd >= 0)
+		close(dev->fd);
+	free(dev);
+	pthread_mutex_unlock(&fd_mutex);
+	return r;
+}
+
+int amdgpu_device_deinitialize(amdgpu_device_handle dev)
+{
+	amdgpu_device_reference(&dev, NULL);
+	return 0;
+}
+
+const char *amdgpu_get_marketing_name(amdgpu_device_handle dev)
+{
+	const struct amdgpu_asic_id *id;
+
+	if (!dev->asic_ids)
+		return NULL;
+
+	for (id = dev->asic_ids; id->did; id++) {
+		if ((id->did == dev->info.asic_id) &&
+		    (id->rid == dev->info.pci_rev_id))
+			return id->marketing_name;
+	}
+
+	return NULL;
+}
diff --git a/amdgpu/amdgpu_gpu_info.c b/amdgpu/amdgpu_gpu_info.c
new file mode 100644
index 0000000..1efffc6
--- /dev/null
+++ b/amdgpu/amdgpu_gpu_info.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright © 2014 Advanced Micro Devices, Inc.
+ * 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:
+ *
+ * The above copyright notice and this permission notice 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 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include "amdgpu.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+#include "xf86drm.h"
+
+int amdgpu_query_info(amdgpu_device_handle dev, unsigned info_id,
+		      unsigned size, void *value)
+{
+	struct drm_amdgpu_info request;
+
+	memset(&request, 0, sizeof(request));
+	request.return_pointer = (uintptr_t)value;
+	request.return_size = size;
+	request.query = info_id;
+
+	return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+			       sizeof(struct drm_amdgpu_info));
+}
+
+int amdgpu_query_crtc_from_id(amdgpu_device_handle dev, unsigned id,
+			      int32_t *result)
+{
+	struct drm_amdgpu_info request;
+
+	memset(&request, 0, sizeof(request));
+	request.return_pointer = (uintptr_t)result;
+	request.return_size = sizeof(*result);
+	request.query = AMDGPU_INFO_CRTC_FROM_ID;
+	request.mode_crtc.id = id;
+
+	return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+			       sizeof(struct drm_amdgpu_info));
+}
+
+int amdgpu_read_mm_registers(amdgpu_device_handle dev, unsigned dword_offset,
+			     unsigned count, uint32_t instance, uint32_t flags,
+			     uint32_t *values)
+{
+	struct drm_amdgpu_info request;
+
+	memset(&request, 0, sizeof(request));
+	request.return_pointer = (uintptr_t)values;
+	request.return_size = count * sizeof(uint32_t);
+	request.query = AMDGPU_INFO_READ_MMR_REG;
+	request.read_mmr_reg.dword_offset = dword_offset;
+	request.read_mmr_reg.count = count;
+	request.read_mmr_reg.instance = instance;
+	request.read_mmr_reg.flags = flags;
+
+	return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+			       sizeof(struct drm_amdgpu_info));
+}
+
+int amdgpu_query_hw_ip_count(amdgpu_device_handle dev, unsigned type,
+			     uint32_t *count)
+{
+	struct drm_amdgpu_info request;
+
+	memset(&request, 0, sizeof(request));
+	request.return_pointer = (uintptr_t)count;
+	request.return_size = sizeof(*count);
+	request.query = AMDGPU_INFO_HW_IP_COUNT;
+	request.query_hw_ip.type = type;
+
+	return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+			       sizeof(struct drm_amdgpu_info));
+}
+
+int amdgpu_query_hw_ip_info(amdgpu_device_handle dev, unsigned type,
+			    unsigned ip_instance,
+			    struct drm_amdgpu_info_hw_ip *info)
+{
+	struct drm_amdgpu_info request;
+
+	memset(&request, 0, sizeof(request));
+	request.return_pointer = (uintptr_t)info;
+	request.return_size = sizeof(*info);
+	request.query = AMDGPU_INFO_HW_IP_INFO;
+	request.query_hw_ip.type = type;
+	request.query_hw_ip.ip_instance = ip_instance;
+
+	return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+			       sizeof(struct drm_amdgpu_info));
+}
+
+int amdgpu_query_firmware_version(amdgpu_device_handle dev, unsigned fw_type,
+				  unsigned ip_instance, unsigned index,
+				  uint32_t *version, uint32_t *feature)
+{
+	struct drm_amdgpu_info request;
+	struct drm_amdgpu_info_firmware firmware = {};
+	int r;
+
+	memset(&request, 0, sizeof(request));
+	request.return_pointer = (uintptr_t)&firmware;
+	request.return_size = sizeof(firmware);
+	request.query = AMDGPU_INFO_FW_VERSION;
+	request.query_fw.fw_type = fw_type;
+	request.query_fw.ip_instance = ip_instance;
+	request.query_fw.index = index;
+
+	r = drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+			    sizeof(struct drm_amdgpu_info));
+	if (r)
+		return r;
+
+	*version = firmware.ver;
+	*feature = firmware.feature;
+	return 0;
+}
+
+drm_private int amdgpu_query_gpu_info_init(amdgpu_device_handle dev)
+{
+	int r, i;
+
+	r = amdgpu_query_info(dev, AMDGPU_INFO_DEV_INFO, sizeof(dev->dev_info),
+			      &dev->dev_info);
+	if (r)
+		return r;
+
+	dev->info.asic_id = dev->dev_info.device_id;
+	dev->info.chip_rev = dev->dev_info.chip_rev;
+	dev->info.chip_external_rev = dev->dev_info.external_rev;
+	dev->info.family_id = dev->dev_info.family;
+	dev->info.max_engine_clk = dev->dev_info.max_engine_clock;
+	dev->info.max_memory_clk = dev->dev_info.max_memory_clock;
+	dev->info.gpu_counter_freq = dev->dev_info.gpu_counter_freq;
+	dev->info.enabled_rb_pipes_mask = dev->dev_info.enabled_rb_pipes_mask;
+	dev->info.rb_pipes = dev->dev_info.num_rb_pipes;
+	dev->info.ids_flags = dev->dev_info.ids_flags;
+	dev->info.num_hw_gfx_contexts = dev->dev_info.num_hw_gfx_contexts;
+	dev->info.num_shader_engines = dev->dev_info.num_shader_engines;
+	dev->info.num_shader_arrays_per_engine =
+		dev->dev_info.num_shader_arrays_per_engine;
+	dev->info.vram_type = dev->dev_info.vram_type;
+	dev->info.vram_bit_width = dev->dev_info.vram_bit_width;
+	dev->info.ce_ram_size = dev->dev_info.ce_ram_size;
+	dev->info.vce_harvest_config = dev->dev_info.vce_harvest_config;
+	dev->info.pci_rev_id = dev->dev_info.pci_rev;
+
+	if (dev->info.family_id < AMDGPU_FAMILY_AI) {
+		for (i = 0; i < (int)dev->info.num_shader_engines; i++) {
+			unsigned instance = (i << AMDGPU_INFO_MMR_SE_INDEX_SHIFT) |
+					    (AMDGPU_INFO_MMR_SH_INDEX_MASK <<
+					     AMDGPU_INFO_MMR_SH_INDEX_SHIFT);
+
+			r = amdgpu_read_mm_registers(dev, 0x263d, 1, instance, 0,
+						     &dev->info.backend_disable[i]);
+			if (r)
+				return r;
+			/* extract bitfield CC_RB_BACKEND_DISABLE.BACKEND_DISABLE */
+			dev->info.backend_disable[i] =
+				(dev->info.backend_disable[i] >> 16) & 0xff;
+
+			r = amdgpu_read_mm_registers(dev, 0xa0d4, 1, instance, 0,
+						     &dev->info.pa_sc_raster_cfg[i]);
+			if (r)
+				return r;
+
+			if (dev->info.family_id >= AMDGPU_FAMILY_CI) {
+				r = amdgpu_read_mm_registers(dev, 0xa0d5, 1, instance, 0,
+						     &dev->info.pa_sc_raster_cfg1[i]);
+				if (r)
+					return r;
+			}
+		}
+	}
+
+	r = amdgpu_read_mm_registers(dev, 0x263e, 1, 0xffffffff, 0,
+					     &dev->info.gb_addr_cfg);
+	if (r)
+		return r;
+
+	if (dev->info.family_id < AMDGPU_FAMILY_AI) {
+		r = amdgpu_read_mm_registers(dev, 0x2644, 32, 0xffffffff, 0,
+					     dev->info.gb_tile_mode);
+		if (r)
+			return r;
+
+		if (dev->info.family_id >= AMDGPU_FAMILY_CI) {
+			r = amdgpu_read_mm_registers(dev, 0x2664, 16, 0xffffffff, 0,
+						     dev->info.gb_macro_tile_mode);
+			if (r)
+				return r;
+		}
+
+		r = amdgpu_read_mm_registers(dev, 0x9d8, 1, 0xffffffff, 0,
+					     &dev->info.mc_arb_ramcfg);
+		if (r)
+			return r;
+	}
+
+	dev->info.cu_active_number = dev->dev_info.cu_active_number;
+	dev->info.cu_ao_mask = dev->dev_info.cu_ao_mask;
+	memcpy(&dev->info.cu_bitmap[0][0], &dev->dev_info.cu_bitmap[0][0], sizeof(dev->info.cu_bitmap));
+
+	/* TODO: info->max_quad_shader_pipes is not set */
+	/* TODO: info->avail_quad_shader_pipes is not set */
+	/* TODO: info->cache_entries_per_quad_pipe is not set */
+	return 0;
+}
+
+int amdgpu_query_gpu_info(amdgpu_device_handle dev,
+			struct amdgpu_gpu_info *info)
+{
+	if (!dev || !info)
+		return -EINVAL;
+
+	/* Get ASIC info*/
+	*info = dev->info;
+
+	return 0;
+}
+
+int amdgpu_query_heap_info(amdgpu_device_handle dev,
+			uint32_t heap,
+			uint32_t flags,
+			struct amdgpu_heap_info *info)
+{
+	struct drm_amdgpu_info_vram_gtt vram_gtt_info = {};
+	int r;
+
+	r = amdgpu_query_info(dev, AMDGPU_INFO_VRAM_GTT,
+			      sizeof(vram_gtt_info), &vram_gtt_info);
+	if (r)
+		return r;
+
+	/* Get heap information */
+	switch (heap) {
+	case AMDGPU_GEM_DOMAIN_VRAM:
+		/* query visible only vram heap */
+		if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED)
+			info->heap_size = vram_gtt_info.vram_cpu_accessible_size;
+		else /* query total vram heap */
+			info->heap_size = vram_gtt_info.vram_size;
+
+		info->max_allocation = vram_gtt_info.vram_cpu_accessible_size;
+
+		if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED)
+			r = amdgpu_query_info(dev, AMDGPU_INFO_VIS_VRAM_USAGE,
+					      sizeof(info->heap_usage),
+					      &info->heap_usage);
+		else
+			r = amdgpu_query_info(dev, AMDGPU_INFO_VRAM_USAGE,
+					      sizeof(info->heap_usage),
+					      &info->heap_usage);
+		if (r)
+			return r;
+		break;
+	case AMDGPU_GEM_DOMAIN_GTT:
+		info->heap_size = vram_gtt_info.gtt_size;
+		info->max_allocation = vram_gtt_info.vram_cpu_accessible_size;
+
+		r = amdgpu_query_info(dev, AMDGPU_INFO_GTT_USAGE,
+				      sizeof(info->heap_usage),
+				      &info->heap_usage);
+		if (r)
+			return r;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int amdgpu_query_gds_info(amdgpu_device_handle dev,
+			struct amdgpu_gds_resource_info *gds_info)
+{
+	struct drm_amdgpu_info_gds gds_config = {};
+        int r;
+
+	if (!gds_info)
+		return -EINVAL;
+
+        r = amdgpu_query_info(dev, AMDGPU_INFO_GDS_CONFIG,
+                              sizeof(gds_config), &gds_config);
+        if (r)
+                return r;
+
+	gds_info->gds_gfx_partition_size = gds_config.gds_gfx_partition_size;
+	gds_info->compute_partition_size = gds_config.compute_partition_size;
+	gds_info->gds_total_size = gds_config.gds_total_size;
+	gds_info->gws_per_gfx_partition = gds_config.gws_per_gfx_partition;
+	gds_info->gws_per_compute_partition = gds_config.gws_per_compute_partition;
+	gds_info->oa_per_gfx_partition = gds_config.oa_per_gfx_partition;
+	gds_info->oa_per_compute_partition = gds_config.oa_per_compute_partition;
+
+	return 0;
+}
+
+int amdgpu_query_sensor_info(amdgpu_device_handle dev, unsigned sensor_type,
+			     unsigned size, void *value)
+{
+	struct drm_amdgpu_info request;
+
+	memset(&request, 0, sizeof(request));
+	request.return_pointer = (uintptr_t)value;
+	request.return_size = size;
+	request.query = AMDGPU_INFO_SENSOR;
+	request.sensor_info.type = sensor_type;
+
+	return drmCommandWrite(dev->fd, DRM_AMDGPU_INFO, &request,
+			       sizeof(struct drm_amdgpu_info));
+}
diff --git a/amdgpu/amdgpu_internal.h b/amdgpu/amdgpu_internal.h
new file mode 100644
index 0000000..56d1511
--- /dev/null
+++ b/amdgpu/amdgpu_internal.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright © 2014 Advanced Micro Devices, Inc.
+ * 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:
+ *
+ * The above copyright notice and this permission notice 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 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.
+ *
+ */
+
+#ifndef _AMDGPU_INTERNAL_H_
+#define _AMDGPU_INTERNAL_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <pthread.h>
+
+#include "libdrm_macros.h"
+#include "xf86atomic.h"
+#include "amdgpu.h"
+#include "util_double_list.h"
+
+#define AMDGPU_CS_MAX_RINGS 8
+/* do not use below macro if b is not power of 2 aligned value */
+#define __round_mask(x, y) ((__typeof__(x))((y)-1))
+#define ROUND_UP(x, y) ((((x)-1) | __round_mask(x, y))+1)
+#define ROUND_DOWN(x, y) ((x) & ~__round_mask(x, y))
+
+#define AMDGPU_INVALID_VA_ADDRESS	0xffffffffffffffff
+#define AMDGPU_NULL_SUBMIT_SEQ		0
+
+struct amdgpu_bo_va_hole {
+	struct list_head list;
+	uint64_t offset;
+	uint64_t size;
+};
+
+struct amdgpu_bo_va_mgr {
+	/* the start virtual address */
+	uint64_t va_offset;
+	uint64_t va_max;
+	struct list_head va_holes;
+	pthread_mutex_t bo_va_mutex;
+	uint32_t va_alignment;
+};
+
+struct amdgpu_va {
+	amdgpu_device_handle dev;
+	uint64_t address;
+	uint64_t size;
+	enum amdgpu_gpu_va_range range;
+	struct amdgpu_bo_va_mgr *vamgr;
+};
+
+struct amdgpu_asic_id {
+	uint32_t did;
+	uint32_t rid;
+	char *marketing_name;
+};
+
+struct amdgpu_device {
+	atomic_t refcount;
+	int fd;
+	int flink_fd;
+	unsigned major_version;
+	unsigned minor_version;
+
+	/** Lookup table of asic device id, revision id and marketing name */
+	struct amdgpu_asic_id *asic_ids;
+	/** List of buffer handles. Protected by bo_table_mutex. */
+	struct util_hash_table *bo_handles;
+	/** List of buffer GEM flink names. Protected by bo_table_mutex. */
+	struct util_hash_table *bo_flink_names;
+	/** This protects all hash tables. */
+	pthread_mutex_t bo_table_mutex;
+	struct drm_amdgpu_info_device dev_info;
+	struct amdgpu_gpu_info info;
+	/** The global VA manager for the whole virtual address space */
+	struct amdgpu_bo_va_mgr vamgr;
+	/** The VA manager for the 32bit address space */
+	struct amdgpu_bo_va_mgr vamgr_32;
+};
+
+struct amdgpu_bo {
+	atomic_t refcount;
+	struct amdgpu_device *dev;
+
+	uint64_t alloc_size;
+
+	uint32_t handle;
+	uint32_t flink_name;
+
+	pthread_mutex_t cpu_access_mutex;
+	void *cpu_ptr;
+	int cpu_map_count;
+};
+
+struct amdgpu_bo_list {
+	struct amdgpu_device *dev;
+
+	uint32_t handle;
+};
+
+struct amdgpu_context {
+	struct amdgpu_device *dev;
+	/** Mutex for accessing fences and to maintain command submissions
+	    in good sequence. */
+	pthread_mutex_t sequence_mutex;
+	/* context id*/
+	uint32_t id;
+	uint64_t last_seq[AMDGPU_HW_IP_NUM][AMDGPU_HW_IP_INSTANCE_MAX_COUNT][AMDGPU_CS_MAX_RINGS];
+	struct list_head sem_list[AMDGPU_HW_IP_NUM][AMDGPU_HW_IP_INSTANCE_MAX_COUNT][AMDGPU_CS_MAX_RINGS];
+};
+
+/**
+ * Structure describing sw semaphore based on scheduler
+ *
+ */
+struct amdgpu_semaphore {
+	atomic_t refcount;
+	struct list_head list;
+	struct amdgpu_cs_fence signal_fence;
+};
+
+/**
+ * Functions.
+ */
+
+drm_private void amdgpu_vamgr_init(struct amdgpu_bo_va_mgr *mgr, uint64_t start,
+		       uint64_t max, uint64_t alignment);
+
+drm_private void amdgpu_vamgr_deinit(struct amdgpu_bo_va_mgr *mgr);
+
+drm_private uint64_t
+amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr, uint64_t size,
+		     uint64_t alignment, uint64_t base_required);
+
+drm_private void
+amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr, uint64_t va, uint64_t size);
+
+drm_private int amdgpu_parse_asic_ids(struct amdgpu_asic_id **asic_ids);
+
+drm_private int amdgpu_query_gpu_info_init(amdgpu_device_handle dev);
+
+drm_private uint64_t amdgpu_cs_calculate_timeout(uint64_t timeout);
+
+/**
+ * Inline functions.
+ */
+
+/**
+ * Increment src and decrement dst as if we were updating references
+ * for an assignment between 2 pointers of some objects.
+ *
+ * \return  true if dst is 0
+ */
+static inline bool update_references(atomic_t *dst, atomic_t *src)
+{
+	if (dst != src) {
+		/* bump src first */
+		if (src) {
+			assert(atomic_read(src) > 0);
+			atomic_inc(src);
+		}
+		if (dst) {
+			assert(atomic_read(dst) > 0);
+			return atomic_dec_and_test(dst);
+		}
+	}
+	return false;
+}
+
+#endif
diff --git a/amdgpu/amdgpu_vamgr.c b/amdgpu/amdgpu_vamgr.c
new file mode 100644
index 0000000..2b1388e
--- /dev/null
+++ b/amdgpu/amdgpu_vamgr.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2014 Advanced Micro Devices, Inc.
+ *
+ * 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 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 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "amdgpu.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+#include "util_math.h"
+
+int amdgpu_va_range_query(amdgpu_device_handle dev,
+			  enum amdgpu_gpu_va_range type, uint64_t *start, uint64_t *end)
+{
+	if (type == amdgpu_gpu_va_range_general) {
+		*start = dev->dev_info.virtual_address_offset;
+		*end = dev->dev_info.virtual_address_max;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+drm_private void amdgpu_vamgr_init(struct amdgpu_bo_va_mgr *mgr, uint64_t start,
+			      uint64_t max, uint64_t alignment)
+{
+	mgr->va_offset = start;
+	mgr->va_max = max;
+	mgr->va_alignment = alignment;
+
+	list_inithead(&mgr->va_holes);
+	pthread_mutex_init(&mgr->bo_va_mutex, NULL);
+}
+
+drm_private void amdgpu_vamgr_deinit(struct amdgpu_bo_va_mgr *mgr)
+{
+	struct amdgpu_bo_va_hole *hole, *tmp;
+	LIST_FOR_EACH_ENTRY_SAFE(hole, tmp, &mgr->va_holes, list) {
+		list_del(&hole->list);
+		free(hole);
+	}
+	pthread_mutex_destroy(&mgr->bo_va_mutex);
+}
+
+drm_private uint64_t
+amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr, uint64_t size,
+		     uint64_t alignment, uint64_t base_required)
+{
+	struct amdgpu_bo_va_hole *hole, *n;
+	uint64_t offset = 0, waste = 0;
+
+	alignment = MAX2(alignment, mgr->va_alignment);
+	size = ALIGN(size, mgr->va_alignment);
+
+	if (base_required % alignment)
+		return AMDGPU_INVALID_VA_ADDRESS;
+
+	pthread_mutex_lock(&mgr->bo_va_mutex);
+	/* TODO: using more appropriate way to track the holes */
+	/* first look for a hole */
+	LIST_FOR_EACH_ENTRY_SAFE(hole, n, &mgr->va_holes, list) {
+		if (base_required) {
+			if(hole->offset > base_required ||
+				(hole->offset + hole->size) < (base_required + size))
+				continue;
+			waste = base_required - hole->offset;
+			offset = base_required;
+		} else {
+			offset = hole->offset;
+			waste = offset % alignment;
+			waste = waste ? alignment - waste : 0;
+			offset += waste;
+			if (offset >= (hole->offset + hole->size)) {
+				continue;
+			}
+		}
+		if (!waste && hole->size == size) {
+			offset = hole->offset;
+			list_del(&hole->list);
+			free(hole);
+			pthread_mutex_unlock(&mgr->bo_va_mutex);
+			return offset;
+		}
+		if ((hole->size - waste) > size) {
+			if (waste) {
+				n = calloc(1, sizeof(struct amdgpu_bo_va_hole));
+				n->size = waste;
+				n->offset = hole->offset;
+				list_add(&n->list, &hole->list);
+			}
+			hole->size -= (size + waste);
+			hole->offset += size + waste;
+			pthread_mutex_unlock(&mgr->bo_va_mutex);
+			return offset;
+		}
+		if ((hole->size - waste) == size) {
+			hole->size = waste;
+			pthread_mutex_unlock(&mgr->bo_va_mutex);
+			return offset;
+		}
+	}
+
+	if (base_required) {
+		if (base_required < mgr->va_offset) {
+			pthread_mutex_unlock(&mgr->bo_va_mutex);
+			return AMDGPU_INVALID_VA_ADDRESS;
+		}
+		offset = mgr->va_offset;
+		waste = base_required - mgr->va_offset;
+	} else {
+		offset = mgr->va_offset;
+		waste = offset % alignment;
+		waste = waste ? alignment - waste : 0;
+	}
+
+	if (offset + waste + size > mgr->va_max) {
+		pthread_mutex_unlock(&mgr->bo_va_mutex);
+		return AMDGPU_INVALID_VA_ADDRESS;
+	}
+
+	if (waste) {
+		n = calloc(1, sizeof(struct amdgpu_bo_va_hole));
+		n->size = waste;
+		n->offset = offset;
+		list_add(&n->list, &mgr->va_holes);
+	}
+
+	offset += waste;
+	mgr->va_offset += size + waste;
+	pthread_mutex_unlock(&mgr->bo_va_mutex);
+	return offset;
+}
+
+drm_private void
+amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr, uint64_t va, uint64_t size)
+{
+	struct amdgpu_bo_va_hole *hole;
+
+	if (va == AMDGPU_INVALID_VA_ADDRESS)
+		return;
+
+	size = ALIGN(size, mgr->va_alignment);
+
+	pthread_mutex_lock(&mgr->bo_va_mutex);
+	if ((va + size) == mgr->va_offset) {
+		mgr->va_offset = va;
+		/* Delete uppermost hole if it reaches the new top */
+		if (!LIST_IS_EMPTY(&mgr->va_holes)) {
+			hole = container_of(mgr->va_holes.next, hole, list);
+			if ((hole->offset + hole->size) == va) {
+				mgr->va_offset = hole->offset;
+				list_del(&hole->list);
+				free(hole);
+			}
+		}
+	} else {
+		struct amdgpu_bo_va_hole *next;
+
+		hole = container_of(&mgr->va_holes, hole, list);
+		LIST_FOR_EACH_ENTRY(next, &mgr->va_holes, list) {
+			if (next->offset < va)
+				break;
+			hole = next;
+		}
+
+		if (&hole->list != &mgr->va_holes) {
+			/* Grow upper hole if it's adjacent */
+			if (hole->offset == (va + size)) {
+				hole->offset = va;
+				hole->size += size;
+				/* Merge lower hole if it's adjacent */
+				if (next != hole
+						&& &next->list != &mgr->va_holes
+						&& (next->offset + next->size) == va) {
+					next->size += hole->size;
+					list_del(&hole->list);
+					free(hole);
+				}
+				goto out;
+			}
+		}
+
+		/* Grow lower hole if it's adjacent */
+		if (next != hole && &next->list != &mgr->va_holes &&
+				(next->offset + next->size) == va) {
+			next->size += size;
+			goto out;
+		}
+
+		/* FIXME on allocation failure we just lose virtual address space
+		 * maybe print a warning
+		 */
+		next = calloc(1, sizeof(struct amdgpu_bo_va_hole));
+		if (next) {
+			next->size = size;
+			next->offset = va;
+			list_add(&next->list, &hole->list);
+		}
+	}
+out:
+	pthread_mutex_unlock(&mgr->bo_va_mutex);
+}
+
+int amdgpu_va_range_alloc(amdgpu_device_handle dev,
+			  enum amdgpu_gpu_va_range va_range_type,
+			  uint64_t size,
+			  uint64_t va_base_alignment,
+			  uint64_t va_base_required,
+			  uint64_t *va_base_allocated,
+			  amdgpu_va_handle *va_range_handle,
+			  uint64_t flags)
+{
+	struct amdgpu_bo_va_mgr *vamgr;
+
+	if (flags & AMDGPU_VA_RANGE_32_BIT)
+		vamgr = &dev->vamgr_32;
+	else
+		vamgr = &dev->vamgr;
+
+	va_base_alignment = MAX2(va_base_alignment, vamgr->va_alignment);
+	size = ALIGN(size, vamgr->va_alignment);
+
+	*va_base_allocated = amdgpu_vamgr_find_va(vamgr, size,
+					va_base_alignment, va_base_required);
+
+	if (!(flags & AMDGPU_VA_RANGE_32_BIT) &&
+	    (*va_base_allocated == AMDGPU_INVALID_VA_ADDRESS)) {
+		/* fallback to 32bit address */
+		vamgr = &dev->vamgr_32;
+		*va_base_allocated = amdgpu_vamgr_find_va(vamgr, size,
+					va_base_alignment, va_base_required);
+	}
+
+	if (*va_base_allocated != AMDGPU_INVALID_VA_ADDRESS) {
+		struct amdgpu_va* va;
+		va = calloc(1, sizeof(struct amdgpu_va));
+		if(!va){
+			amdgpu_vamgr_free_va(vamgr, *va_base_allocated, size);
+			return -ENOMEM;
+		}
+		va->dev = dev;
+		va->address = *va_base_allocated;
+		va->size = size;
+		va->range = va_range_type;
+		va->vamgr = vamgr;
+		*va_range_handle = va;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int amdgpu_va_range_free(amdgpu_va_handle va_range_handle)
+{
+	if(!va_range_handle || !va_range_handle->address)
+		return 0;
+
+	amdgpu_vamgr_free_va(va_range_handle->vamgr,
+			va_range_handle->address,
+			va_range_handle->size);
+	free(va_range_handle);
+	return 0;
+}
diff --git a/amdgpu/libdrm_amdgpu.pc.in b/amdgpu/libdrm_amdgpu.pc.in
new file mode 100644
index 0000000..f1c552a
--- /dev/null
+++ b/amdgpu/libdrm_amdgpu.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_amdgpu
+Description: Userspace interface to kernel DRM services for amdgpu
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -ldrm_amdgpu
+Cflags: -I${includedir} -I${includedir}/libdrm
+Requires.private: libdrm
diff --git a/amdgpu/util_hash.c b/amdgpu/util_hash.c
new file mode 100644
index 0000000..87cb671
--- /dev/null
+++ b/amdgpu/util_hash.c
@@ -0,0 +1,387 @@
+/**************************************************************************
+ *
+ * Copyright 2007 VMware, Inc.
+ * 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 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL VMWARE 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.
+ *
+ **************************************************************************/
+
+ /*
+  * Authors:
+  *   Zack Rusin <zackr@vmware.com>
+  */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "util_hash.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+#define MAX(a, b) ((a > b) ? (a) : (b))
+
+static const int MinNumBits = 4;
+
+static const unsigned char prime_deltas[] = {
+	0,  0,  1,  3,  1,  5,  3,  3,  1,  9,  7,  5,  3,  9, 25,  3,
+	1, 21,  3, 21,  7, 15,  9,  5,  3, 29, 15,  0,  0,  0,  0,  0
+};
+
+static int primeForNumBits(int numBits)
+{
+	return (1 << numBits) + prime_deltas[numBits];
+}
+
+/* Returns the smallest integer n such that
+   primeForNumBits(n) >= hint.
+*/
+static int countBits(int hint)
+{
+	int numBits = 0;
+	int bits = hint;
+
+	while (bits > 1) {
+		bits >>= 1;
+		numBits++;
+	}
+
+	if (numBits >= (int)sizeof(prime_deltas)) {
+		numBits = sizeof(prime_deltas) - 1;
+	} else if (primeForNumBits(numBits) < hint) {
+		++numBits;
+	}
+	return numBits;
+}
+
+struct util_node {
+   struct util_node *next;
+   unsigned key;
+   void *value;
+};
+
+struct util_hash_data {
+   struct util_node *fakeNext;
+   struct util_node **buckets;
+   int size;
+   int nodeSize;
+   short userNumBits;
+   short numBits;
+   int numBuckets;
+};
+
+struct util_hash {
+   union {
+      struct util_hash_data *d;
+      struct util_node      *e;
+   } data;
+};
+
+static void *util_data_allocate_node(struct util_hash_data *hash)
+{
+   return malloc(hash->nodeSize);
+}
+
+static void util_free_node(struct util_node *node)
+{
+   free(node);
+}
+
+static struct util_node *
+util_hash_create_node(struct util_hash *hash,
+                      unsigned akey, void *avalue,
+                      struct util_node **anextNode)
+{
+   struct util_node *node = util_data_allocate_node(hash->data.d);
+
+   if (!node)
+      return NULL;
+
+   node->key = akey;
+   node->value = avalue;
+
+   node->next = (struct util_node*)(*anextNode);
+   *anextNode = node;
+   ++hash->data.d->size;
+   return node;
+}
+
+static void util_data_rehash(struct util_hash_data *hash, int hint)
+{
+   if (hint < 0) {
+      hint = countBits(-hint);
+      if (hint < MinNumBits)
+         hint = MinNumBits;
+      hash->userNumBits = (short)hint;
+      while (primeForNumBits(hint) < (hash->size >> 1))
+         ++hint;
+   } else if (hint < MinNumBits) {
+      hint = MinNumBits;
+   }
+
+   if (hash->numBits != hint) {
+      struct util_node *e = (struct util_node *)(hash);
+      struct util_node **oldBuckets = hash->buckets;
+      int oldNumBuckets = hash->numBuckets;
+      int  i = 0;
+
+      hash->numBits = (short)hint;
+      hash->numBuckets = primeForNumBits(hint);
+      hash->buckets = malloc(sizeof(struct util_node*) * hash->numBuckets);
+      for (i = 0; i < hash->numBuckets; ++i)
+         hash->buckets[i] = e;
+
+      for (i = 0; i < oldNumBuckets; ++i) {
+         struct util_node *firstNode = oldBuckets[i];
+         while (firstNode != e) {
+            unsigned h = firstNode->key;
+            struct util_node *lastNode = firstNode;
+            struct util_node *afterLastNode;
+            struct util_node **beforeFirstNode;
+            
+            while (lastNode->next != e && lastNode->next->key == h)
+               lastNode = lastNode->next;
+
+            afterLastNode = lastNode->next;
+            beforeFirstNode = &hash->buckets[h % hash->numBuckets];
+            while (*beforeFirstNode != e)
+               beforeFirstNode = &(*beforeFirstNode)->next;
+            lastNode->next = *beforeFirstNode;
+            *beforeFirstNode = firstNode;
+            firstNode = afterLastNode;
+         }
+      }
+      free(oldBuckets);
+   }
+}
+
+static void util_data_might_grow(struct util_hash_data *hash)
+{
+   if (hash->size >= hash->numBuckets)
+      util_data_rehash(hash, hash->numBits + 1);
+}
+
+static void util_data_has_shrunk(struct util_hash_data *hash)
+{
+   if (hash->size <= (hash->numBuckets >> 3) &&
+       hash->numBits > hash->userNumBits) {
+      int max = MAX(hash->numBits-2, hash->userNumBits);
+      util_data_rehash(hash,  max);
+   }
+}
+
+static struct util_node *util_data_first_node(struct util_hash_data *hash)
+{
+   struct util_node *e = (struct util_node *)(hash);
+   struct util_node **bucket = hash->buckets;
+   int n = hash->numBuckets;
+   while (n--) {
+      if (*bucket != e)
+         return *bucket;
+      ++bucket;
+   }
+   return e;
+}
+
+static struct util_node **util_hash_find_node(struct util_hash *hash, unsigned akey)
+{
+   struct util_node **node;
+
+   if (hash->data.d->numBuckets) {
+      node = (struct util_node **)(&hash->data.d->buckets[akey % hash->data.d->numBuckets]);
+      assert(*node == hash->data.e || (*node)->next);
+      while (*node != hash->data.e && (*node)->key != akey)
+         node = &(*node)->next;
+   } else {
+      node = (struct util_node **)((const struct util_node * const *)(&hash->data.e));
+   }
+   return node;
+}
+
+drm_private struct util_hash_iter
+util_hash_insert(struct util_hash *hash, unsigned key, void *data)
+{
+   util_data_might_grow(hash->data.d);
+
+   {
+      struct util_node **nextNode = util_hash_find_node(hash, key);
+      struct util_node *node = util_hash_create_node(hash, key, data, nextNode);
+      if (!node) {
+         struct util_hash_iter null_iter = {hash, 0};
+         return null_iter;
+      }
+
+      {
+         struct util_hash_iter iter = {hash, node};
+         return iter;
+      }
+   }
+}
+
+drm_private struct util_hash *util_hash_create(void)
+{
+   struct util_hash *hash = malloc(sizeof(struct util_hash));
+   if (!hash)
+      return NULL;
+
+   hash->data.d = malloc(sizeof(struct util_hash_data));
+   if (!hash->data.d) {
+      free(hash);
+      return NULL;
+   }
+
+   hash->data.d->fakeNext = 0;
+   hash->data.d->buckets = 0;
+   hash->data.d->size = 0;
+   hash->data.d->nodeSize = sizeof(struct util_node);
+   hash->data.d->userNumBits = (short)MinNumBits;
+   hash->data.d->numBits = 0;
+   hash->data.d->numBuckets = 0;
+
+   return hash;
+}
+
+drm_private void util_hash_delete(struct util_hash *hash)
+{
+   struct util_node *e_for_x = (struct util_node *)(hash->data.d);
+   struct util_node **bucket = (struct util_node **)(hash->data.d->buckets);
+   int n = hash->data.d->numBuckets;
+   while (n--) {
+      struct util_node *cur = *bucket++;
+      while (cur != e_for_x) {
+         struct util_node *next = cur->next;
+         util_free_node(cur);
+         cur = next;
+      }
+   }
+   free(hash->data.d->buckets);
+   free(hash->data.d);
+   free(hash);
+}
+
+drm_private struct util_hash_iter
+util_hash_find(struct util_hash *hash, unsigned key)
+{
+   struct util_node **nextNode = util_hash_find_node(hash, key);
+   struct util_hash_iter iter = {hash, *nextNode};
+   return iter;
+}
+
+drm_private unsigned util_hash_iter_key(struct util_hash_iter iter)
+{
+   if (!iter.node || iter.hash->data.e == iter.node)
+      return 0;
+   return iter.node->key;
+}
+
+drm_private void *util_hash_iter_data(struct util_hash_iter iter)
+{
+   if (!iter.node || iter.hash->data.e == iter.node)
+      return 0;
+   return iter.node->value;
+}
+
+static struct util_node *util_hash_data_next(struct util_node *node)
+{
+   union {
+      struct util_node *next;
+      struct util_node *e;
+      struct util_hash_data *d;
+   } a;
+   int start;
+   struct util_node **bucket;
+   int n;
+
+   a.next = node->next;
+   if (!a.next) {
+      /* iterating beyond the last element */
+      return 0;
+   }
+   if (a.next->next)
+      return a.next;
+
+   start = (node->key % a.d->numBuckets) + 1;
+   bucket = a.d->buckets + start;
+   n = a.d->numBuckets - start;
+   while (n--) {
+      if (*bucket != a.e)
+         return *bucket;
+      ++bucket;
+   }
+   return a.e;
+}
+
+drm_private struct util_hash_iter
+util_hash_iter_next(struct util_hash_iter iter)
+{
+   struct util_hash_iter next = {iter.hash, util_hash_data_next(iter.node)};
+   return next;
+}
+
+drm_private int util_hash_iter_is_null(struct util_hash_iter iter)
+{
+   if (!iter.node || iter.node == iter.hash->data.e)
+      return 1;
+   return 0;
+}
+
+drm_private void *util_hash_take(struct util_hash *hash, unsigned akey)
+{
+   struct util_node **node = util_hash_find_node(hash, akey);
+   if (*node != hash->data.e) {
+      void *t = (*node)->value;
+      struct util_node *next = (*node)->next;
+      util_free_node(*node);
+      *node = next;
+      --hash->data.d->size;
+      util_data_has_shrunk(hash->data.d);
+      return t;
+   }
+   return 0;
+}
+
+drm_private struct util_hash_iter util_hash_first_node(struct util_hash *hash)
+{
+   struct util_hash_iter iter = {hash, util_data_first_node(hash->data.d)};
+   return iter;
+}
+
+drm_private struct util_hash_iter
+util_hash_erase(struct util_hash *hash, struct util_hash_iter iter)
+{
+   struct util_hash_iter ret = iter;
+   struct util_node *node = iter.node;
+   struct util_node **node_ptr;
+
+   if (node == hash->data.e)
+      return iter;
+
+   ret = util_hash_iter_next(ret);
+   node_ptr = (struct util_node**)(&hash->data.d->buckets[node->key % hash->data.d->numBuckets]);
+   while (*node_ptr != node)
+      node_ptr = &(*node_ptr)->next;
+   *node_ptr = node->next;
+   util_free_node(node);
+   --hash->data.d->size;
+   return ret;
+}
diff --git a/amdgpu/util_hash.h b/amdgpu/util_hash.h
new file mode 100644
index 0000000..01a4779
--- /dev/null
+++ b/amdgpu/util_hash.h
@@ -0,0 +1,107 @@
+/**************************************************************************
+ *
+ * Copyright 2007 VMware, Inc.
+ * 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 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL VMWARE 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.
+ *
+ **************************************************************************/
+
+/**
+ * @file
+ * Hash implementation.
+ * 
+ * This file provides a hash implementation that is capable of dealing
+ * with collisions. It stores colliding entries in linked list. All
+ * functions operating on the hash return an iterator. The iterator
+ * itself points to the collision list. If there wasn't any collision
+ * the list will have just one entry, otherwise client code should
+ * iterate over the entries to find the exact entry among ones that
+ * had the same key (e.g. memcmp could be used on the data to check
+ * that)
+ * 
+ * @author Zack Rusin <zackr@vmware.com>
+ */
+
+#ifndef UTIL_HASH_H
+#define UTIL_HASH_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdbool.h>
+
+#include "libdrm_macros.h"
+
+struct util_hash;
+struct util_node;
+
+struct util_hash_iter {
+	struct util_hash *hash;
+	struct util_node *node;
+};
+
+
+drm_private struct util_hash *util_hash_create(void);
+drm_private void util_hash_delete(struct util_hash *hash);
+
+
+/**
+ * Adds a data with the given key to the hash. If entry with the given
+ * key is already in the hash, this current entry is instered before it
+ * in the collision list.
+ * Function returns iterator pointing to the inserted item in the hash.
+ */
+drm_private struct util_hash_iter
+util_hash_insert(struct util_hash *hash, unsigned key, void *data);
+
+/**
+ * Removes the item pointed to by the current iterator from the hash.
+ * Note that the data itself is not erased and if it was a malloc'ed pointer
+ * it will have to be freed after calling this function by the callee.
+ * Function returns iterator pointing to the item after the removed one in
+ * the hash.
+ */
+drm_private struct util_hash_iter
+util_hash_erase(struct util_hash *hash, struct util_hash_iter iter);
+
+drm_private void *util_hash_take(struct util_hash *hash, unsigned key);
+
+
+drm_private struct util_hash_iter util_hash_first_node(struct util_hash *hash);
+
+/**
+ * Return an iterator pointing to the first entry in the collision list.
+ */
+drm_private struct util_hash_iter
+util_hash_find(struct util_hash *hash, unsigned key);
+
+
+drm_private int util_hash_iter_is_null(struct util_hash_iter iter);
+drm_private unsigned util_hash_iter_key(struct util_hash_iter iter);
+drm_private void *util_hash_iter_data(struct util_hash_iter iter);
+
+
+drm_private struct util_hash_iter
+util_hash_iter_next(struct util_hash_iter iter);
+
+#endif
diff --git a/amdgpu/util_hash_table.c b/amdgpu/util_hash_table.c
new file mode 100644
index 0000000..fa7f6ea
--- /dev/null
+++ b/amdgpu/util_hash_table.c
@@ -0,0 +1,262 @@
+/**************************************************************************
+ *
+ * Copyright 2008 VMware, Inc.
+ * 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 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL VMWARE 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.
+ *
+ **************************************************************************/
+
+/**
+ * @file
+ * General purpose hash table implementation.
+ * 
+ * Just uses the util_hash for now, but it might be better switch to a linear
+ * probing hash table implementation at some point -- as it is said they have 
+ * better lookup and cache performance and it appears to be possible to write 
+ * a lock-free implementation of such hash tables . 
+ * 
+ * @author José Fonseca <jfonseca@vmware.com>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "util_hash_table.h"
+#include "util_hash.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+struct util_hash_table
+{
+	struct util_hash *head;
+
+	/** Hash function */
+	unsigned (*make_hash)(void *key);
+
+	/** Compare two keys */
+	int (*compare)(void *key1, void *key2);
+};
+
+struct util_hash_table_item
+{
+	void *key;
+	void *value;
+};
+
+
+static struct util_hash_table_item *
+util_hash_table_item(struct util_hash_iter iter)
+{
+	return (struct util_hash_table_item *)util_hash_iter_data(iter);
+}
+
+drm_private struct util_hash_table *
+util_hash_table_create(unsigned (*hash)(void *key),
+		       int (*compare)(void *key1, void *key2))
+{
+	struct util_hash_table *ht;
+
+	ht = malloc(sizeof(struct util_hash_table));
+	if(!ht)
+		return NULL;
+
+	ht->head = util_hash_create();
+	if(!ht->head) {
+		free(ht);
+		return NULL;
+	}
+
+	ht->make_hash = hash;
+	ht->compare = compare;
+
+	return ht;
+}
+
+static struct util_hash_iter
+util_hash_table_find_iter(struct util_hash_table *ht,
+			  void *key, unsigned key_hash)
+{
+	struct util_hash_iter iter;
+	struct util_hash_table_item *item;
+
+	iter = util_hash_find(ht->head, key_hash);
+	while (!util_hash_iter_is_null(iter)) {
+		item = (struct util_hash_table_item *)util_hash_iter_data(iter);
+		if (!ht->compare(item->key, key))
+			break;
+		iter = util_hash_iter_next(iter);
+	}
+
+	return iter;
+}
+
+static struct util_hash_table_item *
+util_hash_table_find_item(struct util_hash_table *ht,
+                          void *key, unsigned key_hash)
+{
+	struct util_hash_iter iter;
+	struct util_hash_table_item *item;
+
+	iter = util_hash_find(ht->head, key_hash);
+	while (!util_hash_iter_is_null(iter)) {
+		item = (struct util_hash_table_item *)util_hash_iter_data(iter);
+		if (!ht->compare(item->key, key))
+			return item;
+		iter = util_hash_iter_next(iter);
+	}
+
+	return NULL;
+}
+
+drm_private void
+util_hash_table_set(struct util_hash_table *ht, void *key, void *value)
+{
+	unsigned key_hash;
+	struct util_hash_table_item *item;
+	struct util_hash_iter iter;
+
+	assert(ht);
+	if (!ht)
+		return;
+
+	key_hash = ht->make_hash(key);
+
+	item = util_hash_table_find_item(ht, key, key_hash);
+	if(item) {
+		/* TODO: key/value destruction? */
+		item->value = value;
+		return;
+	}
+
+	item = malloc(sizeof(struct util_hash_table_item));
+	if(!item)
+		return;
+
+	item->key = key;
+	item->value = value;
+
+	iter = util_hash_insert(ht->head, key_hash, item);
+	if(util_hash_iter_is_null(iter)) {
+		free(item);
+		return;
+	}
+}
+
+drm_private void *util_hash_table_get(struct util_hash_table *ht, void *key)
+{
+	unsigned key_hash;
+	struct util_hash_table_item *item;
+
+	assert(ht);
+	if (!ht)
+		return NULL;
+
+	key_hash = ht->make_hash(key);
+
+	item = util_hash_table_find_item(ht, key, key_hash);
+	if(!item)
+		return NULL;
+
+	return item->value;
+}
+
+drm_private void util_hash_table_remove(struct util_hash_table *ht, void *key)
+{
+	unsigned key_hash;
+	struct util_hash_iter iter;
+	struct util_hash_table_item *item;
+
+	assert(ht);
+	if (!ht)
+		return;
+
+	key_hash = ht->make_hash(key);
+
+	iter = util_hash_table_find_iter(ht, key, key_hash);
+	if(util_hash_iter_is_null(iter))
+		return;
+
+	item = util_hash_table_item(iter);
+	assert(item);
+	free(item);
+
+	util_hash_erase(ht->head, iter);
+}
+
+drm_private void util_hash_table_clear(struct util_hash_table *ht)
+{
+	struct util_hash_iter iter;
+	struct util_hash_table_item *item;
+
+	assert(ht);
+	if (!ht)
+		return;
+
+	iter = util_hash_first_node(ht->head);
+	while (!util_hash_iter_is_null(iter)) {
+		item = (struct util_hash_table_item *)util_hash_take(ht->head, util_hash_iter_key(iter));
+		free(item);
+		iter = util_hash_first_node(ht->head);
+	}
+}
+
+drm_private void util_hash_table_foreach(struct util_hash_table *ht,
+			void (*callback)(void *key, void *value, void *data),
+			void *data)
+{
+	struct util_hash_iter iter;
+	struct util_hash_table_item *item;
+
+	assert(ht);
+	if (!ht)
+		return;
+
+	iter = util_hash_first_node(ht->head);
+	while (!util_hash_iter_is_null(iter)) {
+		item = (struct util_hash_table_item *)util_hash_iter_data(iter);
+		callback(item->key, item->value, data);
+		iter = util_hash_iter_next(iter);
+	}
+}
+
+drm_private void util_hash_table_destroy(struct util_hash_table *ht)
+{
+	struct util_hash_iter iter;
+	struct util_hash_table_item *item;
+
+	assert(ht);
+	if (!ht)
+		return;
+
+	iter = util_hash_first_node(ht->head);
+	while (!util_hash_iter_is_null(iter)) {
+		item = (struct util_hash_table_item *)util_hash_iter_data(iter);
+		free(item);
+		iter = util_hash_iter_next(iter);
+	}
+
+	util_hash_delete(ht->head);
+	free(ht);
+}
diff --git a/amdgpu/util_hash_table.h b/amdgpu/util_hash_table.h
new file mode 100644
index 0000000..e000128
--- /dev/null
+++ b/amdgpu/util_hash_table.h
@@ -0,0 +1,73 @@
+/**************************************************************************
+ *
+ * Copyright 2008 VMware, Inc.
+ * 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 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL VMWARE 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.
+ *
+ **************************************************************************/
+
+/**
+ * General purpose hash table.
+ *  
+ * @author José Fonseca <jfonseca@vmware.com>
+ */
+
+#ifndef U_HASH_TABLE_H_
+#define U_HASH_TABLE_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "libdrm_macros.h"
+
+/**
+ * Generic purpose hash table.
+ */
+struct util_hash_table;
+
+/**
+ * Create an hash table.
+ * 
+ * @param hash hash function
+ * @param compare should return 0 for two equal keys.
+ */
+drm_private struct util_hash_table *
+util_hash_table_create(unsigned (*hash)(void *key),
+		       int (*compare)(void *key1, void *key2));
+
+drm_private void
+util_hash_table_set(struct util_hash_table *ht, void *key, void *value);
+
+drm_private void *util_hash_table_get(struct util_hash_table *ht, void *key);
+
+drm_private void util_hash_table_remove(struct util_hash_table *ht, void *key);
+
+drm_private void util_hash_table_clear(struct util_hash_table *ht);
+
+drm_private void util_hash_table_foreach(struct util_hash_table *ht,
+			void (*callback)(void *key, void *value, void *data),
+			void *data);
+
+drm_private void util_hash_table_destroy(struct util_hash_table *ht);
+
+#endif /* U_HASH_TABLE_H_ */
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..13d6991
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,20 @@
+#! /bin/sh
+
+srcdir=`dirname "$0"`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd "$srcdir"
+
+git config --local --get format.subjectPrefix >/dev/null ||
+    git config --local format.subjectPrefix "PATCH libdrm" 2>/dev/null
+
+git config --local --get sendemail.to >/dev/null ||
+    git config --local sendemail.to "dri-devel@lists.freedesktop.org" 2>/dev/null
+
+autoreconf --force --verbose --install || exit 1
+cd "$ORIGDIR" || exit $?
+
+if test -z "$NOCONFIGURE"; then
+    "$srcdir"/configure "$@"
+fi
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..1259a8f
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,603 @@
+#  Copyright 2005 Adam Jackson.
+#
+#  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
+#  on 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 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 NON-INFRINGEMENT.  IN NO EVENT SHALL
+#  ADAM JACKSON 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.
+
+AC_PREREQ([2.63])
+AC_INIT([libdrm],
+        [2.4.84],
+        [https://bugs.freedesktop.org/enter_bug.cgi?product=DRI],
+        [libdrm])
+
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_SRCDIR([Makefile.am])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_AUX_DIR([build-aux])
+PKG_PROG_PKG_CONFIG
+
+# Require xorg-macros minimum of 1.12 for XORG_WITH_XSLTPROC
+m4_ifndef([XORG_MACROS_VERSION],
+          [m4_fatal([must install xorg-macros 1.12 or later before running autoconf/autogen])])
+XORG_MACROS_VERSION(1.12)
+XORG_WITH_XSLTPROC
+XORG_MANPAGE_SECTIONS
+
+AM_INIT_AUTOMAKE([1.10 foreign dist-bzip2])
+
+# Enable quiet compiles on automake 1.11.
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+# Check for programs
+AC_PROG_CC
+AC_PROG_CC_C99
+
+if test "x$ac_cv_prog_cc_c99" = xno; then
+	AC_MSG_ERROR([Building libdrm requires C99 enabled compiler])
+fi
+
+AC_USE_SYSTEM_EXTENSIONS
+AC_SYS_LARGEFILE
+AC_FUNC_ALLOCA
+
+save_CFLAGS="$CFLAGS"
+export CFLAGS="$CFLAGS -Werror"
+AC_HEADER_MAJOR
+CFLAGS="$save_CFLAGS"
+
+AC_CHECK_HEADERS([sys/sysctl.h sys/select.h])
+
+# Initialize libtool
+LT_PREREQ([2.2])
+LT_INIT([disable-static])
+
+dnl pthread-stubs is mandatory on BSD platforms, due to the nature of the
+dnl project. Even then there's a notable issue as described in the project README
+case "$host_os" in
+linux* | cygwin* | darwin* | solaris* | *-gnu* | gnu*)
+    pthread_stubs_possible="no"
+    ;;
+* )
+    pthread_stubs_possible="yes"
+    ;;
+esac
+
+if test "x$pthread_stubs_possible" = xyes; then
+    PKG_CHECK_MODULES(PTHREADSTUBS, pthread-stubs >= 0.4)
+    AC_SUBST(PTHREADSTUBS_CFLAGS)
+    AC_SUBST(PTHREADSTUBS_LIBS)
+fi
+
+pkgconfigdir=${libdir}/pkgconfig
+AC_SUBST(pkgconfigdir)
+libdrmdatadir=${datadir}/libdrm
+AC_SUBST(libdrmdatadir)
+
+AC_ARG_ENABLE([udev],
+              [AS_HELP_STRING([--enable-udev],
+                              [Enable support for using udev instead of mknod (default: disabled)])],
+              [UDEV=$enableval], [UDEV=no])
+
+AC_ARG_ENABLE(libkms,
+	      AS_HELP_STRING([--disable-libkms],
+	      [Disable KMS mm abstraction library (default: auto, enabled on supported platforms)]),
+	      [LIBKMS=$enableval], [LIBKMS=auto])
+
+AC_ARG_ENABLE(intel,
+	      AS_HELP_STRING([--disable-intel],
+	      [Enable support for intel's KMS API (default: auto, enabled on x86)]),
+	      [INTEL=$enableval], [INTEL=auto])
+
+AC_ARG_ENABLE(radeon,
+	      AS_HELP_STRING([--disable-radeon],
+	      [Enable support for radeon's KMS API (default: auto)]),
+	      [RADEON=$enableval], [RADEON=auto])
+
+AC_ARG_ENABLE(amdgpu,
+	      AS_HELP_STRING([--disable-amdgpu],
+	      [Enable support for amdgpu's KMS API (default: auto)]),
+	      [AMDGPU=$enableval], [AMDGPU=auto])
+
+AC_ARG_ENABLE(nouveau,
+	      AS_HELP_STRING([--disable-nouveau],
+	      [Enable support for nouveau's KMS API (default: auto)]),
+	      [NOUVEAU=$enableval], [NOUVEAU=auto])
+
+AC_ARG_ENABLE(vmwgfx,
+	      AS_HELP_STRING([--disable-vmwgfx],
+	      [Enable support for vmwgfx's KMS API (default: yes)]),
+	      [VMWGFX=$enableval], [VMWGFX=yes])
+
+AC_ARG_ENABLE(omap-experimental-api,
+	      AS_HELP_STRING([--enable-omap-experimental-api],
+	      [Enable support for OMAP's experimental API (default: disabled)]),
+	      [OMAP=$enableval], [OMAP=no])
+
+AC_ARG_ENABLE(exynos-experimental-api,
+	      AS_HELP_STRING([--enable-exynos-experimental-api],
+	      [Enable support for EXYNOS's experimental API (default: disabled)]),
+	      [EXYNOS=$enableval], [EXYNOS=no])
+
+AC_ARG_ENABLE(freedreno,
+	      AS_HELP_STRING([--disable-freedreno],
+	      [Enable support for freedreno's KMS API (default: auto, enabled on arm)]),
+	      [FREEDRENO=$enableval], [FREEDRENO=auto])
+
+AC_ARG_ENABLE(freedreno-kgsl,
+	      AS_HELP_STRING([--enable-freedreno-kgsl],
+	      [Enable support for freedreno's to use downstream android kernel API (default: disabled)]),
+	      [FREEDRENO_KGSL=$enableval], [FREEDRENO_KGSL=no])
+
+AC_ARG_ENABLE(tegra-experimental-api,
+	      AS_HELP_STRING([--enable-tegra-experimental-api],
+	      [Enable support for Tegra's experimental API (default: disabled)]),
+	      [TEGRA=$enableval], [TEGRA=no])
+
+AC_ARG_ENABLE(vivante-experimental-api,
+	      AS_HELP_STRING([--enable-vivante-experimental-api],
+	      [Enable support for vivante's experimental API (default: disabled)]),
+	      [VIVANTE=$enableval], [VIVANTE=no])
+
+AC_ARG_ENABLE(vc4,
+	      AS_HELP_STRING([--disable-vc4],
+	      [Enable support for vc4's API (default: auto, enabled on arm)]),
+	      [VC4=$enableval], [VC4=auto])
+
+AC_ARG_ENABLE(etnaviv-experimental-api,
+	      AS_HELP_STRING([--enable-etnaviv-experimental-api],
+	      [Enable support for etnaviv's experimental API (default: disabled)]),
+	      [ETNAVIV=$enableval], [ETNAVIV=no])
+
+AC_ARG_ENABLE(install-test-programs,
+		  AS_HELP_STRING([--enable-install-test-programs],
+		  [Install test programs (default: no)]),
+		  [INSTALL_TESTS=$enableval], [INSTALL_TESTS=no])
+
+dnl ===========================================================================
+dnl check compiler flags
+AC_DEFUN([LIBDRM_CC_TRY_FLAG], [
+  AC_MSG_CHECKING([whether $CC supports $1])
+
+  libdrm_save_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS $1"
+
+  AC_COMPILE_IFELSE([AC_LANG_SOURCE([ ])], [libdrm_cc_flag=yes], [libdrm_cc_flag=no])
+  CFLAGS="$libdrm_save_CFLAGS"
+
+  if test "x$libdrm_cc_flag" = "xyes"; then
+    ifelse([$2], , :, [$2])
+  else
+    ifelse([$3], , :, [$3])
+  fi
+  AC_MSG_RESULT([$libdrm_cc_flag])
+])
+
+dnl We use clock_gettime to check for timeouts in drmWaitVBlank
+
+AC_CHECK_FUNCS([clock_gettime], [CLOCK_LIB=],
+               [AC_CHECK_LIB([rt], [clock_gettime], [CLOCK_LIB=-lrt],
+                             [AC_MSG_ERROR([Couldn't find clock_gettime])])])
+AC_SUBST([CLOCK_LIB])
+
+AC_CHECK_FUNCS([open_memstream], [HAVE_OPEN_MEMSTREAM=yes])
+
+dnl Use lots of warning flags with with gcc and compatible compilers
+
+dnl Note: if you change the following variable, the cache is automatically
+dnl skipped and all flags rechecked.  So there's no need to do anything
+dnl else.  If for any reason you need to force a recheck, just change
+dnl MAYBE_WARN in an ignorable way (like adding whitespace)
+
+MAYBE_WARN="-Wall -Wextra \
+-Wsign-compare -Werror-implicit-function-declaration \
+-Wpointer-arith -Wwrite-strings -Wstrict-prototypes \
+-Wmissing-prototypes -Wmissing-declarations -Wnested-externs \
+-Wpacked -Wswitch-enum -Wmissing-format-attribute \
+-Wstrict-aliasing=2 -Winit-self \
+-Wdeclaration-after-statement -Wold-style-definition \
+-Wno-unused-parameter \
+-Wno-attributes -Wno-long-long -Winline -Wshadow \
+-Wno-missing-field-initializers"
+
+# invalidate cached value if MAYBE_WARN has changed
+if test "x$libdrm_cv_warn_maybe" != "x$MAYBE_WARN"; then
+	unset libdrm_cv_warn_cflags
+fi
+AC_CACHE_CHECK([for supported warning flags], libdrm_cv_warn_cflags, [
+	echo
+	WARN_CFLAGS=""
+
+	# Some warning options are not supported by all versions of
+	# gcc, so test all desired options against the current
+	# compiler.
+	#
+	# Note that there are some order dependencies
+	# here. Specifically, an option that disables a warning will
+	# have no net effect if a later option then enables that
+	# warnings, (perhaps implicitly). So we put some grouped
+	# options (-Wall and -Wextra) up front and the -Wno options
+	# last.
+
+	for W in $MAYBE_WARN; do
+		LIBDRM_CC_TRY_FLAG([$W], [WARN_CFLAGS="$WARN_CFLAGS $W"])
+	done
+
+	libdrm_cv_warn_cflags=$WARN_CFLAGS
+	libdrm_cv_warn_maybe=$MAYBE_WARN
+
+	AC_MSG_CHECKING([which warning flags were supported])])
+WARN_CFLAGS="$libdrm_cv_warn_cflags"
+
+# Check for atomic intrinsics
+AC_CACHE_CHECK([for native atomic primitives], drm_cv_atomic_primitives, [
+	drm_cv_atomic_primitives="none"
+
+	AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+	int atomic_add(int *i) { return __sync_add_and_fetch (i, 1); }
+	int atomic_cmpxchg(int *i, int j, int k) { return __sync_val_compare_and_swap (i, j, k); }
+					  ]],[[]])], [drm_cv_atomic_primitives="Intel"],[])
+
+	if test "x$drm_cv_atomic_primitives" = "xnone"; then
+		AC_CHECK_HEADER([atomic_ops.h], drm_cv_atomic_primitives="libatomic-ops")
+	fi
+
+	# atomic functions defined in <atomic.h> & libc on Solaris
+	if test "x$drm_cv_atomic_primitives" = "xnone"; then
+		AC_CHECK_FUNC([atomic_cas_uint], drm_cv_atomic_primitives="Solaris")
+	fi
+])
+
+if test "x$drm_cv_atomic_primitives" = xIntel; then
+	AC_DEFINE(HAVE_LIBDRM_ATOMIC_PRIMITIVES, 1,
+		[Enable if your compiler supports the Intel __sync_* atomic primitives])
+fi
+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
+
+dnl Print out the approapriate message considering the value set be the
+dnl respective in $1.
+dnl $1 - value to be evaluated. Eg. $INTEL, $NOUVEAU, ...
+dnl $2 - libdrm shortname. Eg. intel, freedreno, ...
+dnl $3 - GPU name/brand. Eg. Intel, NVIDIA Tegra, ...
+dnl $4 - Configure switch. Eg. intel, omap-experimental-api, ...
+AC_DEFUN([LIBDRM_ATOMICS_NOT_FOUND_MSG], [
+	case "x$1" in
+		xyes)	AC_MSG_ERROR([libdrm_$2 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 $3 GPUs by passing --disable-$4 to ./configure]) ;;
+		xauto)	AC_MSG_WARN([Disabling $2. 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.]) ;;
+		*)	;;
+	esac
+])
+
+if test "x$drm_cv_atomic_primitives" = "xnone"; then
+	LIBDRM_ATOMICS_NOT_FOUND_MSG($INTEL, intel, Intel, intel)
+	INTEL=no
+
+	LIBDRM_ATOMICS_NOT_FOUND_MSG($RADEON, radeon, Radeon, radeon)
+	RADEON=no
+
+	LIBDRM_ATOMICS_NOT_FOUND_MSG($AMDGPU, amdgpu, AMD, amdgpu)
+	AMDGPU=no
+
+	LIBDRM_ATOMICS_NOT_FOUND_MSG($NOUVEAU, nouveau, NVIDIA, nouveau)
+	NOUVEAU=no
+
+	LIBDRM_ATOMICS_NOT_FOUND_MSG($OMAP, omap, OMAP, omap-experimental-api)
+	OMAP=no
+
+	LIBDRM_ATOMICS_NOT_FOUND_MSG($FREEDRENO, freedreno, Qualcomm Adreno, freedreno)
+	FREEDRENO=no
+
+	LIBDRM_ATOMICS_NOT_FOUND_MSG($TEGRA, tegra, NVIDIA Tegra, tegra-experimental-api)
+	TEGRA=no
+
+	LIBDRM_ATOMICS_NOT_FOUND_MSG($ETNAVIV, etnaviv, Vivante, etnaviv-experimental-api)
+	ETNAVIV=no
+else
+	if test "x$INTEL" = xauto; then
+		case $host_cpu in
+			i?86|x86_64)	INTEL=yes ;;
+			*)		INTEL=no ;;
+		esac
+	fi
+	if test "x$RADEON" = xauto; then
+		RADEON=yes
+	fi
+	if test "x$AMDGPU" = xauto; then
+		AMDGPU=yes
+	fi
+	if test "x$NOUVEAU" = xauto; then
+		NOUVEAU=yes
+	fi
+	if test "x$FREEDRENO" = xauto; then
+		case $host_cpu in
+			arm*|aarch64)	FREEDRENO=yes ;;
+			*)		FREEDRENO=no ;;
+		esac
+	fi
+	if test "x$VC4" = xauto; then
+		case $host_cpu in
+			arm*|aarch64)	VC4=yes ;;
+			*)		VC4=no ;;
+		esac
+	fi
+fi
+
+if test "x$INTEL" != "xno"; then
+	PKG_CHECK_MODULES(PCIACCESS, [pciaccess >= 0.10])
+fi
+AC_SUBST(PCIACCESS_CFLAGS)
+AC_SUBST(PCIACCESS_LIBS)
+
+if test "x$UDEV" = xyes; then
+	AC_DEFINE(UDEV, 1, [Have UDEV support])
+fi
+
+AC_CANONICAL_HOST
+if test "x$LIBKMS" = xauto ; then
+	case $host_os in
+		linux*)		LIBKMS="yes" ;;
+		freebsd* | kfreebsd*-gnu)
+				LIBKMS="yes" ;;
+		dragonfly*)	LIBKMS="yes" ;;
+		*)		LIBKMS="no" ;;
+	esac
+fi
+
+AM_CONDITIONAL(HAVE_LIBKMS, [test "x$LIBKMS" = xyes])
+
+AM_CONDITIONAL(HAVE_INTEL, [test "x$INTEL" = xyes])
+if test "x$INTEL" = xyes; then
+	AC_DEFINE(HAVE_INTEL, 1, [Have intel 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])
+fi
+
+AM_CONDITIONAL(HAVE_NOUVEAU, [test "x$NOUVEAU" = xyes])
+if test "x$NOUVEAU" = xyes; then
+	AC_DEFINE(HAVE_NOUVEAU, 1, [Have nouveau (nvidia) support])
+fi
+
+AM_CONDITIONAL(HAVE_OMAP, [test "x$OMAP" = xyes])
+if test "x$OMAP" = xyes; then
+	AC_DEFINE(HAVE_OMAP, 1, [Have OMAP support])
+fi
+
+AM_CONDITIONAL(HAVE_EXYNOS, [test "x$EXYNOS" = xyes])
+if test "x$EXYNOS" = xyes; then
+	AC_DEFINE(HAVE_EXYNOS, 1, [Have EXYNOS support])
+fi
+
+AM_CONDITIONAL(HAVE_FREEDRENO, [test "x$FREEDRENO" = xyes])
+if test "x$FREEDRENO" = xyes; then
+	AC_DEFINE(HAVE_FREEDRENO, 1, [Have freedreno support])
+fi
+
+if test "x$FREEDRENO_KGSL" = xyes; then
+	if test "x$FREEDRENO" != xyes; then
+		AC_MSG_ERROR([Cannot enable freedreno KGSL interface if freedreno is disabled])
+	fi
+fi
+AM_CONDITIONAL(HAVE_FREEDRENO_KGSL, [test "x$FREEDRENO_KGSL" = xyes])
+if test "x$FREEDRENO_KGSL" = xyes; then
+	AC_DEFINE(HAVE_FREEDRENO_KGSL, 1, [Have freedreno support for KGSL kernel interface])
+fi
+
+AM_CONDITIONAL(HAVE_RADEON, [test "x$RADEON" = xyes])
+if test "x$RADEON" = xyes; then
+	AC_DEFINE(HAVE_RADEON, 1, [Have radeon support])
+fi
+
+if test "x$AMDGPU" != xno; then
+	# Detect cunit library
+	PKG_CHECK_MODULES([CUNIT], [cunit >= 2.1], [have_cunit=yes], [have_cunit=no])
+	# If pkg-config does not find cunit, check it using AC_CHECK_LIB.  We
+	# do this because Debian (Ubuntu) lacks pkg-config file for cunit.
+	# fixed in 2.1-2.dfsg-3: http://anonscm.debian.org/cgit/collab-maint/cunit.git/commit/?h=debian
+	if test "x${have_cunit}" = "xno"; then
+		AC_CHECK_LIB([cunit], [CU_initialize_registry], [have_cunit=yes], [have_cunit=no])
+		if test "x${have_cunit}" = "xyes"; then
+			CUNIT_LIBS="-lcunit"
+			CUNIT_CFLAGS=""
+			AC_SUBST([CUNIT_LIBS])
+			AC_SUBST([CUNIT_CFLAGS])
+		fi
+	fi
+else
+	have_cunit=no
+fi
+AM_CONDITIONAL(HAVE_CUNIT, [test "x$have_cunit" != "xno"])
+
+AM_CONDITIONAL(HAVE_AMDGPU, [test "x$AMDGPU" = xyes])
+if test "x$AMDGPU" = xyes; then
+	AC_DEFINE(HAVE_AMDGPU, 1, [Have amdgpu support])
+
+	AC_DEFINE(HAVE_CUNIT, [test "x$have_cunit" != "xno"], [Enable CUNIT Have amdgpu support])
+
+	if test "x$have_cunit" = "xno"; then
+		AC_MSG_WARN([Could not find cunit library. Disabling amdgpu tests])
+	fi
+fi
+
+AM_CONDITIONAL(HAVE_TEGRA, [test "x$TEGRA" = xyes])
+if test "x$TEGRA" = xyes; then
+	AC_DEFINE(HAVE_TEGRA, 1, [Have Tegra support])
+fi
+
+AM_CONDITIONAL(HAVE_VIVANTE, [test "x$VIVANTE" = xyes])
+if test "x$VIVANTE" = xyes; then
+	AC_DEFINE(HAVE_VIVANTE, 1, [Have vivante support])
+fi
+
+AM_CONDITIONAL(HAVE_VC4, [test "x$VC4" = xyes])
+if test "x$VC4" = xyes; then
+	AC_DEFINE(HAVE_VC4, 1, [Have VC4 support])
+fi
+
+AM_CONDITIONAL(HAVE_ETNAVIV, [test "x$ETNAVIV" = xyes])
+if test "x$ETNAVIV" = xyes; then
+	AC_DEFINE(HAVE_ETNAVIV, 1, [Have etnaviv support])
+fi
+
+AM_CONDITIONAL(HAVE_INSTALL_TESTS, [test "x$INSTALL_TESTS" = xyes])
+if test "x$INSTALL_TESTS" = xyes; then
+	AC_DEFINE(HAVE_INSTALL_TESTS, 1, [Install test programs])
+fi
+
+AC_ARG_ENABLE([cairo-tests],
+              [AS_HELP_STRING([--enable-cairo-tests],
+                              [Enable support for Cairo rendering in tests (default: auto)])],
+              [CAIRO=$enableval], [CAIRO=auto])
+if test "x$CAIRO" != xno; then
+	PKG_CHECK_MODULES(CAIRO, cairo, [HAVE_CAIRO=yes], [HAVE_CAIRO=no])
+fi
+AC_MSG_CHECKING([whether to enable Cairo tests])
+if test "x$CAIRO" = xauto; then
+	CAIRO="$HAVE_CAIRO"
+fi
+if test "x$CAIRO" = xyes; then
+	if ! test "x$HAVE_CAIRO" = xyes; then
+		AC_MSG_ERROR([Cairo support required but not present])
+	fi
+	AC_DEFINE(HAVE_CAIRO, 1, [Have Cairo support])
+fi
+AC_MSG_RESULT([$CAIRO])
+AM_CONDITIONAL(HAVE_CAIRO, [test "x$CAIRO" = xyes])
+
+# xsltproc for docbook manpages
+AC_ARG_ENABLE([manpages],
+              AS_HELP_STRING([--enable-manpages], [enable manpages @<:@default=auto@:>@]),
+              [MANS=$enableval], [MANS=auto])
+AM_CONDITIONAL([BUILD_MANPAGES], [test "x$XSLTPROC" != "x" -a "x$MANS" != "xno"])
+
+# check for offline man-pages stylesheet
+AC_MSG_CHECKING([for docbook manpages stylesheet])
+MANPAGES_STYLESHEET="http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl"
+AC_PATH_PROGS_FEATURE_CHECK([XSLTPROC_TMP], [xsltproc],
+                            AS_IF([`"$ac_path_XSLTPROC_TMP" --nonet "$MANPAGES_STYLESHEET" > /dev/null 2>&1`],
+                                  [HAVE_MANPAGES_STYLESHEET=yes]))
+if test "x$HAVE_MANPAGES_STYLESHEET" = "xyes"; then
+        AC_SUBST(MANPAGES_STYLESHEET)
+        AC_MSG_RESULT([yes])
+else
+        AC_MSG_RESULT([no])
+fi
+AM_CONDITIONAL([HAVE_MANPAGES_STYLESHEET], [test "x$HAVE_MANPAGES_STYLESHEET" = "xyes"])
+
+AC_ARG_ENABLE(valgrind,
+              [AS_HELP_STRING([--enable-valgrind],
+                             [Build libdrm with  valgrind support (default: auto)])],
+                             [VALGRIND=$enableval], [VALGRIND=auto])
+if test "x$VALGRIND" != xno; then
+	PKG_CHECK_MODULES(VALGRIND, [valgrind], [have_valgrind=yes], [have_valgrind=no])
+fi
+AC_MSG_CHECKING([whether to enable Valgrind support])
+if test "x$VALGRIND" = xauto; then
+	VALGRIND="$have_valgrind"
+fi
+
+if test "x$VALGRIND" = "xyes"; then
+	if ! test "x$have_valgrind" = xyes; then
+		AC_MSG_ERROR([Valgrind support required but not present])
+	fi
+	AC_DEFINE([HAVE_VALGRIND], 1, [Use valgrind intrinsics to suppress false warnings])
+fi
+
+AC_MSG_RESULT([$VALGRIND])
+
+AC_ARG_WITH([kernel-source],
+            [AS_HELP_STRING([--with-kernel-source],
+              [specify path to linux kernel source])],
+	    [kernel_source="$with_kernel_source"])
+AC_SUBST(kernel_source)
+
+AC_MSG_CHECKING([whether $CC supports __attribute__(("hidden"))])
+AC_LINK_IFELSE([AC_LANG_PROGRAM([
+    int foo_hidden( void ) __attribute__((visibility("hidden")));
+])], HAVE_ATTRIBUTE_VISIBILITY="yes"; AC_MSG_RESULT([yes]), AC_MSG_RESULT([no]));
+
+if test "x$HAVE_ATTRIBUTE_VISIBILITY" = xyes; then
+    AC_DEFINE(HAVE_VISIBILITY, 1, [Compiler supports __attribute__(("hidden"))])
+fi
+
+AC_SUBST(WARN_CFLAGS)
+AC_CONFIG_FILES([
+	Makefile
+	data/Makefile
+	libkms/Makefile
+	libkms/libkms.pc
+	intel/Makefile
+	intel/libdrm_intel.pc
+	radeon/Makefile
+	radeon/libdrm_radeon.pc
+	amdgpu/Makefile
+	amdgpu/libdrm_amdgpu.pc
+	nouveau/Makefile
+	nouveau/libdrm_nouveau.pc
+	omap/Makefile
+	omap/libdrm_omap.pc
+	exynos/Makefile
+	exynos/libdrm_exynos.pc
+	freedreno/Makefile
+	freedreno/libdrm_freedreno.pc
+	tegra/Makefile
+	tegra/libdrm_tegra.pc
+	vivante/Makefile
+	vivante/libdrm_vivante.pc
+	vc4/Makefile
+	vc4/libdrm_vc4.pc
+	etnaviv/Makefile
+	etnaviv/libdrm_etnaviv.pc
+	tests/Makefile
+	tests/modeprint/Makefile
+	tests/modetest/Makefile
+	tests/kms/Makefile
+	tests/kmstest/Makefile
+	tests/proptest/Makefile
+	tests/radeon/Makefile
+	tests/amdgpu/Makefile
+	tests/vbltest/Makefile
+	tests/exynos/Makefile
+	tests/tegra/Makefile
+	tests/vivante/Makefile
+	tests/nouveau/Makefile
+	tests/etnaviv/Makefile
+	tests/util/Makefile
+	man/Makefile
+	libdrm.pc])
+AC_OUTPUT
+
+echo ""
+echo "$PACKAGE_STRING will be compiled with:"
+echo ""
+echo "  libkms         $LIBKMS"
+echo "  Intel API      $INTEL"
+echo "  vmwgfx API     $VMWGFX"
+echo "  Radeon API     $RADEON"
+echo "  AMDGPU API     $AMDGPU"
+echo "  Nouveau API    $NOUVEAU"
+echo "  OMAP API       $OMAP"
+echo "  EXYNOS API     $EXYNOS"
+echo "  Freedreno API  $FREEDRENO (kgsl: $FREEDRENO_KGSL)"
+echo "  Tegra API      $TEGRA"
+echo "  Vivante API    $VIVANTE"
+echo "  VC4 API        $VC4"
+echo "  Etnaviv API    $ETNAVIV"
+echo ""
diff --git a/data/Android.mk b/data/Android.mk
new file mode 100644
index 0000000..62013f0
--- /dev/null
+++ b/data/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := amdgpu.ids
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := hwdata
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/data/Makefile.am b/data/Makefile.am
new file mode 100644
index 0000000..eba915d
--- /dev/null
+++ b/data/Makefile.am
@@ -0,0 +1,23 @@
+#  Copyright © 2017 Advanced Micro Devices, Inc.
+#  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
+#  on 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 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 NON-INFRINGEMENT.  IN NO EVENT SHALL
+#  ADAM JACKSON 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.
+
+libdrmdatadir = @libdrmdatadir@
+dist_libdrmdata_DATA = amdgpu.ids
diff --git a/data/amdgpu.ids b/data/amdgpu.ids
new file mode 100644
index 0000000..f6c65dd
--- /dev/null
+++ b/data/amdgpu.ids
@@ -0,0 +1,160 @@
+# List of AMDGPU IDs
+#
+# Syntax:
+# device_id,	revision_id,	product_name        <-- single tab after comma
+
+1.0.0
+6600,	0,	AMD Radeon HD 8600/8700M
+6600,	81,	AMD Radeon (TM) R7 M370
+6601,	0,	AMD Radeon (TM) HD 8500M/8700M
+6604,	0,	AMD Radeon R7 M265 Series
+6604,	81,	AMD Radeon (TM) R7 M350
+6605,	0,	AMD Radeon R7 M260 Series
+6605,	81,	AMD Radeon (TM) R7 M340
+6606,	0,	AMD Radeon HD 8790M
+6607,	0,	AMD Radeon (TM) HD8530M
+6608,	0,	AMD FirePro W2100
+6610,	0,	AMD Radeon HD 8600 Series
+6610,	81,	AMD Radeon (TM) R7 350
+6610,	83,	AMD Radeon (TM) R5 340
+6611,	0,	AMD Radeon HD 8500 Series
+6613,	0,	AMD Radeon HD 8500 series
+6617,	C7,	AMD Radeon R7 240 Series
+6640,	0,	AMD Radeon HD 8950
+6640,	80,	AMD Radeon (TM) R9 M380
+6646,	0,	AMD Radeon R9 M280X
+6646,	80,	AMD Radeon (TM) R9 M470X
+6647,	0,	AMD Radeon R9 M270X
+6647,	80,	AMD Radeon (TM) R9 M380
+6649,	0,	AMD FirePro W5100
+6658,	0,	AMD Radeon R7 200 Series
+665C,	0,	AMD Radeon HD 7700 Series
+665D,	0,	AMD Radeon R7 200 Series
+665F,	81,	AMD Radeon (TM) R7 300 Series
+6660,	0,	AMD Radeon HD 8600M Series
+6660,	81,	AMD Radeon (TM) R5 M335
+6660,	83,	AMD Radeon (TM) R5 M330
+6663,	0,	AMD Radeon HD 8500M Series
+6663,	83,	AMD Radeon (TM) R5 M320
+6664,	0,	AMD Radeon R5 M200 Series
+6665,	0,	AMD Radeon R5 M200 Series
+6665,	83,	AMD Radeon (TM) R5 M320
+6667,	0,	AMD Radeon R5 M200 Series
+666F,	0,	AMD Radeon HD 8500M
+6780,	0,	ATI FirePro V (FireGL V) Graphics Adapter
+678A,	0,	ATI FirePro V (FireGL V) Graphics Adapter
+6798,	0,	AMD Radeon HD 7900 Series
+679A,	0,	AMD Radeon HD 7900 Series
+679B,	0,	AMD Radeon HD 7900 Series
+679E,	0,	AMD Radeon HD 7800 Series
+67A0,	0,	AMD Radeon FirePro W9100
+67A1,	0,	AMD Radeon FirePro W8100
+67B0,	0,	AMD Radeon R9 200 Series
+67B0,	80,	AMD Radeon (TM) R9 390 Series
+67B1,	0,	AMD Radeon R9 200 Series
+67B1,	80,	AMD Radeon (TM) R9 390 Series
+67B9,	0,	AMD Radeon R9 200 Series
+67DF,	C1,	Radeon RX 580 Series
+67DF,	C2,	Radeon RX 570 Series
+67DF,	C3,	Radeon RX 580 Series
+67DF,	C4,	AMD Radeon (TM) RX 480 Graphics
+67DF,	C5,	AMD Radeon (TM) RX 470 Graphics
+67DF,	C6,	Radeon RX 570 Series
+67DF,	C7,	AMD Radeon (TM) RX 480 Graphics
+67DF,	CF,	AMD Radeon (TM) RX 470 Graphics
+67DF,	E3,	Radeon RX Series
+67DF,	E7,	Radeon RX 580 Series
+67DF,	EF,	Radeon RX 570 Series
+67C2,	01,	AMD Radeon (TM) Pro V7350x2
+67C2,	02,	AMD Radeon (TM) Pro V7300X
+67C4,	00,	AMD Radeon (TM) Pro WX 7100 Graphics
+67C7,	00,	AMD Radeon (TM) Pro WX 5100 Graphics
+67C0,	00,	AMD Radeon (TM) Pro WX 7100 Graphics
+67D0,	01,	AMD Radeon (TM) Pro V7350x2
+67D0,	02,	AMD Radeon (TM) Pro V7300X
+67E0,	00,	AMD Radeon (TM) Pro WX Series
+67E3,	00,	AMD Radeon (TM) Pro WX 4100
+67E8,	00,	AMD Radeon (TM) Pro WX Series
+67E8,	01,	AMD Radeon (TM) Pro WX Series
+67E8,	80,	AMD Radeon (TM) E9260 Graphics
+67EB,	00,	AMD Radeon (TM) Pro V5300X
+67EF,	C0,	AMD Radeon (TM) RX Graphics
+67EF,	C1,	AMD Radeon (TM) RX 460 Graphics
+67EF,	C3,	Radeon RX Series
+67EF,	C5,	AMD Radeon (TM) RX 460 Graphics
+67EF,	C7,	AMD Radeon (TM) RX Graphics
+67EF,	CF,	AMD Radeon (TM) RX 460 Graphics
+67EF,	E1,	Radeon RX Series
+67EF,	E3,	Radeon RX Series
+67EF,	E7,	Radeon RX Series
+67EF,	EF,	AMD Radeon (TM) RX Graphics
+67EF,	FF,	Radeon RX Series
+67FF,	C0,	AMD Radeon (TM) RX Graphics
+67FF,	C1,	AMD Radeon (TM) RX Graphics
+67FF,	FF,	Radeon RX 550 Series
+6800,	0,	AMD Radeon HD 7970M
+6801,	0,	AMD Radeon(TM) HD8970M
+6808,	0,	ATI FirePro V(FireGL V) Graphics Adapter
+6809,	0,	ATI FirePro V(FireGL V) Graphics Adapter
+6810,	0,	AMD Radeon(TM) HD 8800 Series
+6810,	81,	AMD Radeon (TM) R7 370 Series
+6811,	0,	AMD Radeon(TM) HD8800 Series
+6811,	81,	AMD Radeon (TM) R7 300 Series
+6818,	0,	AMD Radeon HD 7800 Series
+6819,	0,	AMD Radeon HD 7800 Series
+6820,	0,	AMD Radeon HD 8800M Series
+6820,	81,	AMD Radeon (TM) R9 M375
+6820,	83,	AMD Radeon (TM) R9 M375X
+6821,	0,	AMD Radeon HD 8800M Series
+6821,	87,	AMD Radeon (TM) R7 M380
+6821,	83,	AMD Radeon R9 (TM) M370X
+6822,	0,	AMD Radeon E8860
+6823,	0,	AMD Radeon HD 8800M Series
+6825,	0,	AMD Radeon HD 7800M Series
+6827,	0,	AMD Radeon HD 7800M Series
+6828,	0,	ATI FirePro V(FireGL V) Graphics Adapter
+682B,	0,	AMD Radeon HD 8800M Series
+682B,	87,	AMD Radeon (TM) R9 M360
+682C,	0,	AMD FirePro W4100
+682D,	0,	AMD Radeon HD 7700M Series
+682F,	0,	AMD Radeon HD 7700M Series
+6835,	0,	AMD Radeon R7 Series / HD 9000 Series
+6837,	0,	AMD Radeon HD7700 Series
+683D,	0,	AMD Radeon HD 7700 Series
+683F,	0,	AMD Radeon HD 7700 Series
+6900,	0,	AMD Radeon R7 M260
+6900,	81,	AMD Radeon (TM) R7 M360
+6900,	83,	AMD Radeon (TM) R7 M340
+6901,	0,	AMD Radeon R5 M255
+6907,	0,	AMD Radeon R5 M255
+6907,	87,	AMD Radeon (TM) R5 M315
+6920,	0,	AMD RADEON R9 M395X
+6920,	1,	AMD RADEON R9 M390X
+6921,	0,	AMD Radeon R9 M295X
+6929,	0,	AMD FirePro S7150
+692B,	0,	AMD FirePro W7100
+6938,	0,	AMD Radeon R9 200 Series
+6938,	F0,	AMD Radeon R9 200 Series
+6938,	F1,	AMD Radeon (TM) R9 380 Series
+6939,	F0,	AMD Radeon R9 200 Series
+6939,	0,	AMD Radeon R9 200 Series
+6939,	F1,	AMD Radeon (TM) R9 380 Series
+6985,	00,	AMD Radeon Pro WX3100
+6995,	00,	AMD Radeon Pro WX2100
+699F,	C0,	Radeon 500 Series
+699F,	C3,	Radeon 500 Series
+699F,	C7,	Radeon RX 550 Series
+7300,	C1,	AMD FirePro (TM) S9300 x2
+7300,	C8,	AMD Radeon (TM) R9 Fury Series
+7300,	C9,	Radeon (TM) Pro Duo
+7300,	CB,	AMD Radeon (TM) R9 Fury Series
+7300,	CA,	AMD Radeon (TM) R9 Fury Series
+9874,	C4,	AMD Radeon R7 Graphics
+9874,	C5,	AMD Radeon R6 Graphics
+9874,	C6,	AMD Radeon R6 Graphics
+9874,	C7,	AMD Radeon R5 Graphics
+9874,	C8,	AMD Radeon R7 Graphics
+9874,	81,	AMD Radeon R6 Graphics
+9874,	87,	AMD Radeon R5 Graphics
+9874,	85,	AMD Radeon R6 Graphics
+9874,	84,	AMD Radeon R7 Graphics
diff --git a/etnaviv/Android.mk b/etnaviv/Android.mk
new file mode 100644
index 0000000..390f9a9
--- /dev/null
+++ b/etnaviv/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Import variables LIBDRM_ETNAVIV_FILES, LIBDRM_ETNAVIV_H_FILES
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_MODULE := libdrm_etnaviv
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+LOCAL_SRC_FILES := $(LIBDRM_ETNAVIV_FILES)
+
+include $(LIBDRM_COMMON_MK)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/etnaviv/Makefile.am b/etnaviv/Makefile.am
new file mode 100644
index 0000000..be96ba8
--- /dev/null
+++ b/etnaviv/Makefile.am
@@ -0,0 +1,26 @@
+include Makefile.sources
+
+AM_CFLAGS = \
+	$(WARN_CFLAGS) \
+	-I$(top_srcdir) \
+	$(PTHREADSTUBS_CFLAGS) \
+	-I$(top_srcdir)/include/drm
+
+libdrm_etnaviv_ladir = $(libdir)
+libdrm_etnaviv_la_LTLIBRARIES = libdrm_etnaviv.la
+libdrm_etnaviv_la_LDFLAGS = -version-number 1:0:0 -no-undefined
+libdrm_etnaviv_la_LIBADD = \
+	../libdrm.la \
+	@PTHREADSTUBS_LIBS@ \
+	@CLOCK_LIB@
+
+libdrm_etnaviv_la_SOURCES = $(LIBDRM_ETNAVIV_FILES)
+
+libdrm_etnavivincludedir = ${includedir}/libdrm
+libdrm_etnavivinclude_HEADERS = $(LIBDRM_ETNAVIV_H_FILES)
+
+pkgconfigdir = @pkgconfigdir@
+pkgconfig_DATA = libdrm_etnaviv.pc
+
+TESTS = etnaviv-symbol-check
+EXTRA_DIST = $(TESTS)
diff --git a/etnaviv/Makefile.sources b/etnaviv/Makefile.sources
new file mode 100644
index 0000000..5258056
--- /dev/null
+++ b/etnaviv/Makefile.sources
@@ -0,0 +1,12 @@
+LIBDRM_ETNAVIV_FILES := \
+	etnaviv_device.c \
+	etnaviv_gpu.c \
+	etnaviv_bo.c \
+	etnaviv_bo_cache.c \
+	etnaviv_pipe.c \
+	etnaviv_cmd_stream.c \
+	etnaviv_drm.h \
+	etnaviv_priv.h
+
+LIBDRM_ETNAVIV_H_FILES := \
+	etnaviv_drmif.h
diff --git a/etnaviv/etnaviv-symbol-check b/etnaviv/etnaviv-symbol-check
new file mode 100755
index 0000000..0e2030e
--- /dev/null
+++ b/etnaviv/etnaviv-symbol-check
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# The following symbols (past the first five) are taken from the public headers.
+# A list of the latter should be available Makefile.sources/LIBDRM_ETNAVIV_H_FILES
+
+FUNCS=$(nm -D --format=bsd --defined-only ${1-.libs/libdrm_etnaviv.so} | awk '{print $3}'| while read func; do
+( grep -q "^$func$" || echo $func )  <<EOF
+__bss_start
+_edata
+_end
+_fini
+_init
+etna_device_new
+etna_device_new_dup
+etna_device_ref
+etna_device_del
+etna_device_fd
+etna_gpu_new
+etna_gpu_del
+etna_gpu_get_param
+etna_pipe_new
+etna_pipe_del
+etna_pipe_wait
+etna_pipe_wait_ns
+etna_bo_new
+etna_bo_from_handle
+etna_bo_from_name
+etna_bo_from_dmabuf
+etna_bo_ref
+etna_bo_del
+etna_bo_get_name
+etna_bo_handle
+etna_bo_dmabuf
+etna_bo_size
+etna_bo_map
+etna_bo_cpu_prep
+etna_bo_cpu_fini
+etna_cmd_stream_new
+etna_cmd_stream_del
+etna_cmd_stream_timestamp
+etna_cmd_stream_flush
+etna_cmd_stream_flush2
+etna_cmd_stream_finish
+etna_cmd_stream_reloc
+EOF
+done)
+
+test ! -n "$FUNCS" || echo $FUNCS
+test ! -n "$FUNCS"
diff --git a/etnaviv/etnaviv_bo.c b/etnaviv/etnaviv_bo.c
new file mode 100644
index 0000000..78b9cd2
--- /dev/null
+++ b/etnaviv/etnaviv_bo.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2014 Etnaviv Project
+ *
+ * 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:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
+drm_private void bo_del(struct etna_bo *bo);
+
+/* set buffer name, and add to table, call w/ table_lock held: */
+static void set_name(struct etna_bo *bo, uint32_t name)
+{
+	bo->name = name;
+	/* add ourself into the name table: */
+	drmHashInsert(bo->dev->name_table, name, bo);
+}
+
+/* Called under table_lock */
+drm_private void bo_del(struct etna_bo *bo)
+{
+	if (bo->map)
+		drm_munmap(bo->map, bo->size);
+
+	if (bo->name)
+		drmHashDelete(bo->dev->name_table, bo->name);
+
+	if (bo->handle) {
+		struct drm_gem_close req = {
+			.handle = bo->handle,
+		};
+
+		drmHashDelete(bo->dev->handle_table, bo->handle);
+		drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+	}
+
+	free(bo);
+}
+
+/* lookup a buffer from it's handle, call w/ table_lock held: */
+static struct etna_bo *lookup_bo(void *tbl, uint32_t handle)
+{
+	struct etna_bo *bo = NULL;
+
+	if (!drmHashLookup(tbl, handle, (void **)&bo)) {
+		/* found, incr refcnt and return: */
+		bo = etna_bo_ref(bo);
+
+		/* don't break the bucket if this bo was found in one */
+		list_delinit(&bo->list);
+	}
+
+	return bo;
+}
+
+/* allocate a new buffer object, call w/ table_lock held */
+static struct etna_bo *bo_from_handle(struct etna_device *dev,
+		uint32_t size, uint32_t handle, uint32_t flags)
+{
+	struct etna_bo *bo = calloc(sizeof(*bo), 1);
+
+	if (!bo) {
+		struct drm_gem_close req = {
+			.handle = handle,
+		};
+
+		drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+
+		return NULL;
+	}
+
+	bo->dev = etna_device_ref(dev);
+	bo->size = size;
+	bo->handle = handle;
+	bo->flags = flags;
+	atomic_set(&bo->refcnt, 1);
+	list_inithead(&bo->list);
+	/* add ourselves to the handle table: */
+	drmHashInsert(dev->handle_table, handle, bo);
+
+	return bo;
+}
+
+/* allocate a new (un-tiled) buffer object */
+struct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size,
+		uint32_t flags)
+{
+	struct etna_bo *bo;
+	int ret;
+	struct drm_etnaviv_gem_new req = {
+			.flags = flags,
+	};
+
+	bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags);
+	if (bo)
+		return bo;
+
+	req.size = size;
+	ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW,
+			&req, sizeof(req));
+	if (ret)
+		return NULL;
+
+	pthread_mutex_lock(&table_lock);
+	bo = bo_from_handle(dev, size, req.handle, flags);
+	bo->reuse = 1;
+	pthread_mutex_unlock(&table_lock);
+
+	return bo;
+}
+
+struct etna_bo *etna_bo_ref(struct etna_bo *bo)
+{
+	atomic_inc(&bo->refcnt);
+
+	return bo;
+}
+
+/* get buffer info */
+static int get_buffer_info(struct etna_bo *bo)
+{
+	int ret;
+	struct drm_etnaviv_gem_info req = {
+		.handle = bo->handle,
+	};
+
+	ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO,
+			&req, sizeof(req));
+	if (ret) {
+		return ret;
+	}
+
+	/* really all we need for now is mmap offset */
+	bo->offset = req.offset;
+
+	return 0;
+}
+
+/* import a buffer object from DRI2 name */
+struct etna_bo *etna_bo_from_name(struct etna_device *dev, uint32_t name)
+{
+	struct etna_bo *bo;
+	struct drm_gem_open req = {
+		.name = name,
+	};
+
+	pthread_mutex_lock(&table_lock);
+
+	/* check name table first, to see if bo is already open: */
+	bo = lookup_bo(dev->name_table, name);
+	if (bo)
+		goto out_unlock;
+
+	if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
+		ERROR_MSG("gem-open failed: %s", strerror(errno));
+		goto out_unlock;
+	}
+
+	bo = lookup_bo(dev->handle_table, req.handle);
+	if (bo)
+		goto out_unlock;
+
+	bo = bo_from_handle(dev, req.size, req.handle, 0);
+	if (bo)
+		set_name(bo, name);
+
+out_unlock:
+	pthread_mutex_unlock(&table_lock);
+
+	return bo;
+}
+
+/* import a buffer from dmabuf fd, does not take ownership of the
+ * fd so caller should close() the fd when it is otherwise done
+ * with it (even if it is still using the 'struct etna_bo *')
+ */
+struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd)
+{
+	struct etna_bo *bo;
+	int ret, size;
+	uint32_t handle;
+
+	/* take the lock before calling drmPrimeFDToHandle to avoid
+	 * racing against etna_bo_del, which might invalidate the
+	 * returned handle.
+	 */
+	pthread_mutex_lock(&table_lock);
+
+	ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
+	if (ret) {
+		pthread_mutex_unlock(&table_lock);
+		return NULL;
+	}
+
+	bo = lookup_bo(dev->handle_table, handle);
+	if (bo)
+		goto out_unlock;
+
+	/* lseek() to get bo size */
+	size = lseek(fd, 0, SEEK_END);
+	lseek(fd, 0, SEEK_CUR);
+
+	bo = bo_from_handle(dev, size, handle, 0);
+
+out_unlock:
+	pthread_mutex_unlock(&table_lock);
+
+	return bo;
+}
+
+/* destroy a buffer object */
+void etna_bo_del(struct etna_bo *bo)
+{
+	struct etna_device *dev = bo->dev;
+
+	if (!bo)
+		return;
+
+	if (!atomic_dec_and_test(&bo->refcnt))
+		return;
+
+	pthread_mutex_lock(&table_lock);
+
+	if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0))
+		goto out;
+
+	bo_del(bo);
+	etna_device_del_locked(dev);
+out:
+	pthread_mutex_unlock(&table_lock);
+}
+
+/* get the global flink/DRI2 buffer name */
+int etna_bo_get_name(struct etna_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) {
+			return ret;
+		}
+
+		pthread_mutex_lock(&table_lock);
+		set_name(bo, req.name);
+		pthread_mutex_unlock(&table_lock);
+		bo->reuse = 0;
+	}
+
+	*name = bo->name;
+
+	return 0;
+}
+
+uint32_t etna_bo_handle(struct etna_bo *bo)
+{
+	return bo->handle;
+}
+
+/* caller owns the dmabuf fd that is returned and is responsible
+ * to close() it when done
+ */
+int etna_bo_dmabuf(struct etna_bo *bo)
+{
+	int ret, prime_fd;
+
+	ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
+				&prime_fd);
+	if (ret) {
+		ERROR_MSG("failed to get dmabuf fd: %d", ret);
+		return ret;
+	}
+
+	bo->reuse = 0;
+
+	return prime_fd;
+}
+
+uint32_t etna_bo_size(struct etna_bo *bo)
+{
+	return bo->size;
+}
+
+void *etna_bo_map(struct etna_bo *bo)
+{
+	if (!bo->map) {
+		if (!bo->offset) {
+			get_buffer_info(bo);
+		}
+
+		bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE,
+				MAP_SHARED, bo->dev->fd, bo->offset);
+		if (bo->map == MAP_FAILED) {
+			ERROR_MSG("mmap failed: %s", strerror(errno));
+			bo->map = NULL;
+		}
+	}
+
+	return bo->map;
+}
+
+int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op)
+{
+	struct drm_etnaviv_gem_cpu_prep req = {
+		.handle = bo->handle,
+		.op = op,
+	};
+
+	get_abs_timeout(&req.timeout, 5000000000);
+
+	return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP,
+			&req, sizeof(req));
+}
+
+void etna_bo_cpu_fini(struct etna_bo *bo)
+{
+	struct drm_etnaviv_gem_cpu_fini req = {
+		.handle = bo->handle,
+	};
+
+	drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI,
+			&req, sizeof(req));
+}
diff --git a/etnaviv/etnaviv_bo_cache.c b/etnaviv/etnaviv_bo_cache.c
new file mode 100644
index 0000000..8924651
--- /dev/null
+++ b/etnaviv/etnaviv_bo_cache.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 Etnaviv Project
+ *
+ * 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:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+drm_private void bo_del(struct etna_bo *bo);
+drm_private extern pthread_mutex_t table_lock;
+
+static void add_bucket(struct etna_bo_cache *cache, int size)
+{
+	unsigned i = cache->num_buckets;
+
+	assert(i < ARRAY_SIZE(cache->cache_bucket));
+
+	list_inithead(&cache->cache_bucket[i].list);
+	cache->cache_bucket[i].size = size;
+	cache->num_buckets++;
+}
+
+drm_private void etna_bo_cache_init(struct etna_bo_cache *cache)
+{
+	unsigned long size, cache_max_size = 64 * 1024 * 1024;
+
+	/* OK, so power of two buckets was too wasteful of memory.
+	 * Give 3 other sizes between each power of two, to hopefully
+	 * cover things accurately enough.  (The alternative is
+	 * probably to just go for exact matching of sizes, and assume
+	 * that for things like composited window resize the tiled
+	 * width/height alignment and rounding of sizes to pages will
+	 * get us useful cache hit rates anyway)
+	 */
+	add_bucket(cache, 4096);
+	add_bucket(cache, 4096 * 2);
+	add_bucket(cache, 4096 * 3);
+
+	/* Initialize the linked lists for BO reuse cache. */
+	for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
+		add_bucket(cache, size);
+		add_bucket(cache, size + size * 1 / 4);
+		add_bucket(cache, size + size * 2 / 4);
+		add_bucket(cache, size + size * 3 / 4);
+	}
+}
+
+/* Frees older cached buffers.  Called under table_lock */
+drm_private void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time)
+{
+	unsigned i;
+
+	if (cache->time == time)
+		return;
+
+	for (i = 0; i < cache->num_buckets; i++) {
+		struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
+		struct etna_bo *bo;
+
+		while (!LIST_IS_EMPTY(&bucket->list)) {
+			bo = LIST_ENTRY(struct etna_bo, bucket->list.next, list);
+
+			/* keep things in cache for at least 1 second: */
+			if (time && ((time - bo->free_time) <= 1))
+				break;
+
+			list_del(&bo->list);
+			bo_del(bo);
+		}
+	}
+
+	cache->time = time;
+}
+
+static struct etna_bo_bucket *get_bucket(struct etna_bo_cache *cache, uint32_t size)
+{
+	unsigned i;
+
+	/* hmm, this is what intel does, but I suppose we could calculate our
+	 * way to the correct bucket size rather than looping..
+	 */
+	for (i = 0; i < cache->num_buckets; i++) {
+		struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
+		if (bucket->size >= size) {
+			return bucket;
+		}
+	}
+
+	return NULL;
+}
+
+static int is_idle(struct etna_bo *bo)
+{
+	return etna_bo_cpu_prep(bo,
+			DRM_ETNA_PREP_READ |
+			DRM_ETNA_PREP_WRITE |
+			DRM_ETNA_PREP_NOSYNC) == 0;
+}
+
+static struct etna_bo *find_in_bucket(struct etna_bo_bucket *bucket, uint32_t flags)
+{
+	struct etna_bo *bo = NULL;
+
+	pthread_mutex_lock(&table_lock);
+	while (!LIST_IS_EMPTY(&bucket->list)) {
+		bo = LIST_ENTRY(struct etna_bo, bucket->list.next, list);
+
+		if (bo->flags == flags && is_idle(bo)) {
+			list_del(&bo->list);
+			break;
+		}
+
+		bo = NULL;
+		break;
+	}
+	pthread_mutex_unlock(&table_lock);
+
+	return bo;
+}
+
+/* allocate a new (un-tiled) buffer object
+ *
+ * NOTE: size is potentially rounded up to bucket size
+ */
+drm_private struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache, uint32_t *size,
+    uint32_t flags)
+{
+	struct etna_bo *bo;
+	struct etna_bo_bucket *bucket;
+
+	*size = ALIGN(*size, 4096);
+	bucket = get_bucket(cache, *size);
+
+	/* see if we can be green and recycle: */
+	if (bucket) {
+		*size = bucket->size;
+		bo = find_in_bucket(bucket, flags);
+		if (bo) {
+			atomic_set(&bo->refcnt, 1);
+			etna_device_ref(bo->dev);
+			return bo;
+		}
+	}
+
+	return NULL;
+}
+
+drm_private int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo)
+{
+	struct etna_bo_bucket *bucket = get_bucket(cache, bo->size);
+
+	/* see if we can be green and recycle: */
+	if (bucket) {
+		struct timespec time;
+
+		clock_gettime(CLOCK_MONOTONIC, &time);
+
+		bo->free_time = time.tv_sec;
+		list_addtail(&bo->list, &bucket->list);
+		etna_bo_cache_cleanup(cache, time.tv_sec);
+
+		/* bo's in the bucket cache don't have a ref and
+		 * don't hold a ref to the dev:
+		 */
+		etna_device_del_locked(bo->dev);
+
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/etnaviv/etnaviv_cmd_stream.c b/etnaviv/etnaviv_cmd_stream.c
new file mode 100644
index 0000000..8d0e813
--- /dev/null
+++ b/etnaviv/etnaviv_cmd_stream.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * 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:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+
+#include "etnaviv_drmif.h"
+#include "etnaviv_priv.h"
+
+static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz)
+{
+	if ((nr + 1) > *max) {
+		if ((*max * 2) < (nr + 1))
+			*max = nr + 5;
+		else
+			*max = *max * 2;
+		ptr = realloc(ptr, *max * sz);
+	}
+
+	return ptr;
+}
+
+#define APPEND(x, name) ({ \
+	(x)->name = grow((x)->name, (x)->nr_ ## name, &(x)->max_ ## name, sizeof((x)->name[0])); \
+	(x)->nr_ ## name ++; \
+})
+
+static inline struct etna_cmd_stream_priv *
+etna_cmd_stream_priv(struct etna_cmd_stream *stream)
+{
+    return (struct etna_cmd_stream_priv *)stream;
+}
+
+struct etna_cmd_stream *etna_cmd_stream_new(struct etna_pipe *pipe, uint32_t size,
+		void (*reset_notify)(struct etna_cmd_stream *stream, void *priv),
+		void *priv)
+{
+	struct etna_cmd_stream_priv *stream = NULL;
+
+	if (size == 0) {
+		ERROR_MSG("invalid size of 0");
+		goto fail;
+	}
+
+	stream = calloc(1, sizeof(*stream));
+	if (!stream) {
+		ERROR_MSG("allocation failed");
+		goto fail;
+	}
+
+	/* allocate even number of 32-bit words */
+	size = ALIGN(size, 2);
+
+	stream->base.buffer = malloc(size * sizeof(uint32_t));
+	if (!stream->base.buffer) {
+		ERROR_MSG("allocation failed");
+		goto fail;
+	}
+
+	stream->base.size = size;
+	stream->pipe = pipe;
+	stream->reset_notify = reset_notify;
+	stream->reset_notify_priv = priv;
+
+	return &stream->base;
+
+fail:
+	if (stream)
+		etna_cmd_stream_del(&stream->base);
+
+	return NULL;
+}
+
+void etna_cmd_stream_del(struct etna_cmd_stream *stream)
+{
+	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+
+	free(stream->buffer);
+	free(priv->submit.relocs);
+	free(priv);
+}
+
+static void reset_buffer(struct etna_cmd_stream *stream)
+{
+	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+
+	stream->offset = 0;
+	priv->submit.nr_bos = 0;
+	priv->submit.nr_relocs = 0;
+	priv->nr_bos = 0;
+
+	if (priv->reset_notify)
+		priv->reset_notify(stream, priv->reset_notify_priv);
+}
+
+uint32_t etna_cmd_stream_timestamp(struct etna_cmd_stream *stream)
+{
+	return etna_cmd_stream_priv(stream)->last_timestamp;
+}
+
+static uint32_t append_bo(struct etna_cmd_stream *stream, struct etna_bo *bo)
+{
+	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+	uint32_t idx;
+
+	idx = APPEND(&priv->submit, bos);
+	idx = APPEND(priv, bos);
+
+	priv->submit.bos[idx].flags = 0;
+	priv->submit.bos[idx].handle = bo->handle;
+
+	priv->bos[idx] = etna_bo_ref(bo);
+
+	return idx;
+}
+
+/* add (if needed) bo, return idx: */
+static uint32_t bo2idx(struct etna_cmd_stream *stream, struct etna_bo *bo,
+		uint32_t flags)
+{
+	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+	uint32_t idx;
+
+	pthread_mutex_lock(&idx_lock);
+
+	if (!bo->current_stream) {
+		idx = append_bo(stream, bo);
+		bo->current_stream = stream;
+		bo->idx = idx;
+	} else if (bo->current_stream == stream) {
+		idx = bo->idx;
+	} else {
+		/* slow-path: */
+		for (idx = 0; idx < priv->nr_bos; idx++)
+			if (priv->bos[idx] == bo)
+				break;
+		if (idx == priv->nr_bos) {
+			/* not found */
+			idx = append_bo(stream, bo);
+		}
+	}
+	pthread_mutex_unlock(&idx_lock);
+
+	if (flags & ETNA_RELOC_READ)
+		priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_READ;
+	if (flags & ETNA_RELOC_WRITE)
+		priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_WRITE;
+
+	return idx;
+}
+
+static void flush(struct etna_cmd_stream *stream, int in_fence_fd,
+		  int *out_fence_fd)
+{
+	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+	int ret, id = priv->pipe->id;
+	struct etna_gpu *gpu = priv->pipe->gpu;
+
+	struct drm_etnaviv_gem_submit req = {
+		.pipe = gpu->core,
+		.exec_state = id,
+		.bos = VOID2U64(priv->submit.bos),
+		.nr_bos = priv->submit.nr_bos,
+		.relocs = VOID2U64(priv->submit.relocs),
+		.nr_relocs = priv->submit.nr_relocs,
+		.stream = VOID2U64(stream->buffer),
+		.stream_size = stream->offset * 4, /* in bytes */
+	};
+
+	if (in_fence_fd != -1) {
+		req.flags |= ETNA_SUBMIT_FENCE_FD_IN | ETNA_SUBMIT_NO_IMPLICIT;
+		req.fence_fd = in_fence_fd;
+	}
+
+	if (out_fence_fd)
+		req.flags |= ETNA_SUBMIT_FENCE_FD_OUT;
+
+	ret = drmCommandWriteRead(gpu->dev->fd, DRM_ETNAVIV_GEM_SUBMIT,
+			&req, sizeof(req));
+
+	if (ret)
+		ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
+	else
+		priv->last_timestamp = req.fence;
+
+	for (uint32_t i = 0; i < priv->nr_bos; i++) {
+		struct etna_bo *bo = priv->bos[i];
+
+		bo->current_stream = NULL;
+		etna_bo_del(bo);
+	}
+
+	if (out_fence_fd)
+		*out_fence_fd = req.fence_fd;
+}
+
+void etna_cmd_stream_flush(struct etna_cmd_stream *stream)
+{
+	flush(stream, -1, NULL);
+	reset_buffer(stream);
+}
+
+void etna_cmd_stream_flush2(struct etna_cmd_stream *stream, int in_fence_fd,
+			    int *out_fence_fd)
+{
+	flush(stream, in_fence_fd, out_fence_fd);
+	reset_buffer(stream);
+}
+
+void etna_cmd_stream_finish(struct etna_cmd_stream *stream)
+{
+	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+
+	flush(stream, -1, NULL);
+	etna_pipe_wait(priv->pipe, priv->last_timestamp, 5000);
+	reset_buffer(stream);
+}
+
+void etna_cmd_stream_reloc(struct etna_cmd_stream *stream, const struct etna_reloc *r)
+{
+	struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+	struct drm_etnaviv_gem_submit_reloc *reloc;
+	uint32_t idx = APPEND(&priv->submit, relocs);
+	uint32_t addr = 0;
+
+	reloc = &priv->submit.relocs[idx];
+
+	reloc->reloc_idx = bo2idx(stream, r->bo, r->flags);
+	reloc->reloc_offset = r->offset;
+	reloc->submit_offset = stream->offset * 4; /* in bytes */
+	reloc->flags = 0;
+
+	etna_cmd_stream_emit(stream, addr);
+}
diff --git a/etnaviv/etnaviv_device.c b/etnaviv/etnaviv_device.c
new file mode 100644
index 0000000..3ce9203
--- /dev/null
+++ b/etnaviv/etnaviv_device.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 Etnaviv Project
+ *
+ * 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:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <xf86drm.h>
+#include <xf86atomic.h>
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
+
+struct etna_device *etna_device_new(int fd)
+{
+	struct etna_device *dev = calloc(sizeof(*dev), 1);
+
+	if (!dev)
+		return NULL;
+
+	atomic_set(&dev->refcnt, 1);
+	dev->fd = fd;
+	dev->handle_table = drmHashCreate();
+	dev->name_table = drmHashCreate();
+	etna_bo_cache_init(&dev->bo_cache);
+
+	return dev;
+}
+
+/* like etna_device_new() but creates it's own private dup() of the fd
+ * which is close()d when the device is finalized. */
+struct etna_device *etna_device_new_dup(int fd)
+{
+	int dup_fd = dup(fd);
+	struct etna_device *dev = etna_device_new(dup_fd);
+
+	if (dev)
+		dev->closefd = 1;
+	else
+		close(dup_fd);
+
+	return dev;
+}
+
+struct etna_device *etna_device_ref(struct etna_device *dev)
+{
+	atomic_inc(&dev->refcnt);
+
+	return dev;
+}
+
+static void etna_device_del_impl(struct etna_device *dev)
+{
+	etna_bo_cache_cleanup(&dev->bo_cache, 0);
+	drmHashDestroy(dev->handle_table);
+	drmHashDestroy(dev->name_table);
+
+	if (dev->closefd)
+		close(dev->fd);
+
+	free(dev);
+}
+
+drm_private void etna_device_del_locked(struct etna_device *dev)
+{
+	if (!atomic_dec_and_test(&dev->refcnt))
+		return;
+
+	etna_device_del_impl(dev);
+}
+
+void etna_device_del(struct etna_device *dev)
+{
+	if (!atomic_dec_and_test(&dev->refcnt))
+		return;
+
+	pthread_mutex_lock(&table_lock);
+	etna_device_del_impl(dev);
+	pthread_mutex_unlock(&table_lock);
+}
+
+int etna_device_fd(struct etna_device *dev)
+{
+   return dev->fd;
+}
diff --git a/etnaviv/etnaviv_drm.h b/etnaviv/etnaviv_drm.h
new file mode 100644
index 0000000..76f6f78
--- /dev/null
+++ b/etnaviv/etnaviv_drm.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2015 Etnaviv Project
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ETNAVIV_DRM_H__
+#define __ETNAVIV_DRM_H__
+
+#include "drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Please note that modifications to all structs defined here are
+ * subject to backwards-compatibility constraints:
+ *  1) Do not use pointers, use __u64 instead for 32 bit / 64 bit
+ *     user/kernel compatibility
+ *  2) Keep fields aligned to their size
+ *  3) Because of how drm_ioctl() works, we can add new fields at
+ *     the end of an ioctl if some care is taken: drm_ioctl() will
+ *     zero out the new fields at the tail of the ioctl, so a zero
+ *     value should have a backwards compatible meaning.  And for
+ *     output params, userspace won't see the newly added output
+ *     fields.. so that has to be somehow ok.
+ */
+
+/* timeouts are specified in clock-monotonic absolute times (to simplify
+ * restarting interrupted ioctls).  The following struct is logically the
+ * same as 'struct timespec' but 32/64b ABI safe.
+ */
+struct drm_etnaviv_timespec {
+	__s64 tv_sec;          /* seconds */
+	__s64 tv_nsec;         /* nanoseconds */
+};
+
+#define ETNAVIV_PARAM_GPU_MODEL                     0x01
+#define ETNAVIV_PARAM_GPU_REVISION                  0x02
+#define ETNAVIV_PARAM_GPU_FEATURES_0                0x03
+#define ETNAVIV_PARAM_GPU_FEATURES_1                0x04
+#define ETNAVIV_PARAM_GPU_FEATURES_2                0x05
+#define ETNAVIV_PARAM_GPU_FEATURES_3                0x06
+#define ETNAVIV_PARAM_GPU_FEATURES_4                0x07
+#define ETNAVIV_PARAM_GPU_FEATURES_5                0x08
+#define ETNAVIV_PARAM_GPU_FEATURES_6                0x09
+
+#define ETNAVIV_PARAM_GPU_STREAM_COUNT              0x10
+#define ETNAVIV_PARAM_GPU_REGISTER_MAX              0x11
+#define ETNAVIV_PARAM_GPU_THREAD_COUNT              0x12
+#define ETNAVIV_PARAM_GPU_VERTEX_CACHE_SIZE         0x13
+#define ETNAVIV_PARAM_GPU_SHADER_CORE_COUNT         0x14
+#define ETNAVIV_PARAM_GPU_PIXEL_PIPES               0x15
+#define ETNAVIV_PARAM_GPU_VERTEX_OUTPUT_BUFFER_SIZE 0x16
+#define ETNAVIV_PARAM_GPU_BUFFER_SIZE               0x17
+#define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT         0x18
+#define ETNAVIV_PARAM_GPU_NUM_CONSTANTS             0x19
+#define ETNAVIV_PARAM_GPU_NUM_VARYINGS              0x1a
+
+#define ETNA_MAX_PIPES 4
+
+struct drm_etnaviv_param {
+	__u32 pipe;           /* in */
+	__u32 param;          /* in, ETNAVIV_PARAM_x */
+	__u64 value;          /* out (get_param) or in (set_param) */
+};
+
+/*
+ * GEM buffers:
+ */
+
+#define ETNA_BO_CACHE_MASK   0x000f0000
+/* cache modes */
+#define ETNA_BO_CACHED       0x00010000
+#define ETNA_BO_WC           0x00020000
+#define ETNA_BO_UNCACHED     0x00040000
+/* map flags */
+#define ETNA_BO_FORCE_MMU    0x00100000
+
+struct drm_etnaviv_gem_new {
+	__u64 size;           /* in */
+	__u32 flags;          /* in, mask of ETNA_BO_x */
+	__u32 handle;         /* out */
+};
+
+struct drm_etnaviv_gem_info {
+	__u32 handle;         /* in */
+	__u32 pad;
+	__u64 offset;         /* out, offset to pass to mmap() */
+};
+
+#define ETNA_PREP_READ        0x01
+#define ETNA_PREP_WRITE       0x02
+#define ETNA_PREP_NOSYNC      0x04
+
+struct drm_etnaviv_gem_cpu_prep {
+	__u32 handle;         /* in */
+	__u32 op;             /* in, mask of ETNA_PREP_x */
+	struct drm_etnaviv_timespec timeout;   /* in */
+};
+
+struct drm_etnaviv_gem_cpu_fini {
+	__u32 handle;         /* in */
+	__u32 flags;          /* in, placeholder for now, no defined values */
+};
+
+/*
+ * Cmdstream Submission:
+ */
+
+/* The value written into the cmdstream is logically:
+ * relocbuf->gpuaddr + reloc_offset
+ *
+ * NOTE that reloc's must be sorted by order of increasing submit_offset,
+ * otherwise EINVAL.
+ */
+struct drm_etnaviv_gem_submit_reloc {
+	__u32 submit_offset;  /* in, offset from submit_bo */
+	__u32 reloc_idx;      /* in, index of reloc_bo buffer */
+	__u64 reloc_offset;   /* in, offset from start of reloc_bo */
+	__u32 flags;          /* in, placeholder for now, no defined values */
+};
+
+/* Each buffer referenced elsewhere in the cmdstream submit (ie. the
+ * cmdstream buffer(s) themselves or reloc entries) has one (and only
+ * one) entry in the submit->bos[] table.
+ *
+ * As a optimization, the current buffer (gpu virtual address) can be
+ * passed back through the 'presumed' field.  If on a subsequent reloc,
+ * userspace passes back a 'presumed' address that is still valid,
+ * then patching the cmdstream for this entry is skipped.  This can
+ * avoid kernel needing to map/access the cmdstream bo in the common
+ * case.
+ */
+#define ETNA_SUBMIT_BO_READ             0x0001
+#define ETNA_SUBMIT_BO_WRITE            0x0002
+struct drm_etnaviv_gem_submit_bo {
+	__u32 flags;          /* in, mask of ETNA_SUBMIT_BO_x */
+	__u32 handle;         /* in, GEM handle */
+	__u64 presumed;       /* in/out, presumed buffer address */
+};
+
+/* Each cmdstream submit consists of a table of buffers involved, and
+ * one or more cmdstream buffers.  This allows for conditional execution
+ * (context-restore), and IB buffers needed for per tile/bin draw cmds.
+ */
+#define ETNA_SUBMIT_NO_IMPLICIT         0x0001
+#define ETNA_SUBMIT_FENCE_FD_IN         0x0002
+#define ETNA_SUBMIT_FENCE_FD_OUT        0x0004
+#define ETNA_SUBMIT_FLAGS		(ETNA_SUBMIT_NO_IMPLICIT | \
+					 ETNA_SUBMIT_FENCE_FD_IN | \
+					 ETNA_SUBMIT_FENCE_FD_OUT)
+#define ETNA_PIPE_3D      0x00
+#define ETNA_PIPE_2D      0x01
+#define ETNA_PIPE_VG      0x02
+struct drm_etnaviv_gem_submit {
+	__u32 fence;          /* out */
+	__u32 pipe;           /* in */
+	__u32 exec_state;     /* in, initial execution state (ETNA_PIPE_x) */
+	__u32 nr_bos;         /* in, number of submit_bo's */
+	__u32 nr_relocs;      /* in, number of submit_reloc's */
+	__u32 stream_size;    /* in, cmdstream size */
+	__u64 bos;            /* in, ptr to array of submit_bo's */
+	__u64 relocs;         /* in, ptr to array of submit_reloc's */
+	__u64 stream;         /* in, ptr to cmdstream */
+	__u32 flags;          /* in, mask of ETNA_SUBMIT_x */
+	__s32 fence_fd;       /* in/out, fence fd (see ETNA_SUBMIT_FENCE_FD_x) */
+};
+
+/* The normal way to synchronize with the GPU is just to CPU_PREP on
+ * a buffer if you need to access it from the CPU (other cmdstream
+ * submission from same or other contexts, PAGE_FLIP ioctl, etc, all
+ * handle the required synchronization under the hood).  This ioctl
+ * mainly just exists as a way to implement the gallium pipe_fence
+ * APIs without requiring a dummy bo to synchronize on.
+ */
+#define ETNA_WAIT_NONBLOCK      0x01
+struct drm_etnaviv_wait_fence {
+	__u32 pipe;           /* in */
+	__u32 fence;          /* in */
+	__u32 flags;          /* in, mask of ETNA_WAIT_x */
+	__u32 pad;
+	struct drm_etnaviv_timespec timeout;   /* in */
+};
+
+#define ETNA_USERPTR_READ	0x01
+#define ETNA_USERPTR_WRITE	0x02
+struct drm_etnaviv_gem_userptr {
+	__u64 user_ptr;	/* in, page aligned user pointer */
+	__u64 user_size;	/* in, page aligned user size */
+	__u32 flags;		/* in, flags */
+	__u32 handle;	/* out, non-zero handle */
+};
+
+struct drm_etnaviv_gem_wait {
+	__u32 pipe;				/* in */
+	__u32 handle;				/* in, bo to be waited for */
+	__u32 flags;				/* in, mask of ETNA_WAIT_x  */
+	__u32 pad;
+	struct drm_etnaviv_timespec timeout;	/* in */
+};
+
+#define DRM_ETNAVIV_GET_PARAM          0x00
+/* placeholder:
+#define DRM_ETNAVIV_SET_PARAM          0x01
+ */
+#define DRM_ETNAVIV_GEM_NEW            0x02
+#define DRM_ETNAVIV_GEM_INFO           0x03
+#define DRM_ETNAVIV_GEM_CPU_PREP       0x04
+#define DRM_ETNAVIV_GEM_CPU_FINI       0x05
+#define DRM_ETNAVIV_GEM_SUBMIT         0x06
+#define DRM_ETNAVIV_WAIT_FENCE         0x07
+#define DRM_ETNAVIV_GEM_USERPTR        0x08
+#define DRM_ETNAVIV_GEM_WAIT           0x09
+#define DRM_ETNAVIV_NUM_IOCTLS         0x0a
+
+#define DRM_IOCTL_ETNAVIV_GET_PARAM    DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GET_PARAM, struct drm_etnaviv_param)
+#define DRM_IOCTL_ETNAVIV_GEM_NEW      DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_NEW, struct drm_etnaviv_gem_new)
+#define DRM_IOCTL_ETNAVIV_GEM_INFO     DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_INFO, struct drm_etnaviv_gem_info)
+#define DRM_IOCTL_ETNAVIV_GEM_CPU_PREP DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_PREP, struct drm_etnaviv_gem_cpu_prep)
+#define DRM_IOCTL_ETNAVIV_GEM_CPU_FINI DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_CPU_FINI, struct drm_etnaviv_gem_cpu_fini)
+#define DRM_IOCTL_ETNAVIV_GEM_SUBMIT   DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_SUBMIT, struct drm_etnaviv_gem_submit)
+#define DRM_IOCTL_ETNAVIV_WAIT_FENCE   DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence)
+#define DRM_IOCTL_ETNAVIV_GEM_USERPTR  DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_USERPTR, struct drm_etnaviv_gem_userptr)
+#define DRM_IOCTL_ETNAVIV_GEM_WAIT     DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_WAIT, struct drm_etnaviv_gem_wait)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ETNAVIV_DRM_H__ */
diff --git a/etnaviv/etnaviv_drmif.h b/etnaviv/etnaviv_drmif.h
new file mode 100644
index 0000000..87704ac
--- /dev/null
+++ b/etnaviv/etnaviv_drmif.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * 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:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifndef ETNAVIV_DRMIF_H_
+#define ETNAVIV_DRMIF_H_
+
+#include <xf86drm.h>
+#include <stdint.h>
+
+struct etna_bo;
+struct etna_pipe;
+struct etna_gpu;
+struct etna_device;
+struct etna_cmd_stream;
+
+enum etna_pipe_id {
+	ETNA_PIPE_3D = 0,
+	ETNA_PIPE_2D = 1,
+	ETNA_PIPE_VG = 2,
+	ETNA_PIPE_MAX
+};
+
+enum etna_param_id {
+	ETNA_GPU_MODEL                     = 0x1,
+	ETNA_GPU_REVISION                  = 0x2,
+	ETNA_GPU_FEATURES_0                = 0x3,
+	ETNA_GPU_FEATURES_1                = 0x4,
+	ETNA_GPU_FEATURES_2                = 0x5,
+	ETNA_GPU_FEATURES_3                = 0x6,
+	ETNA_GPU_FEATURES_4                = 0x7,
+	ETNA_GPU_FEATURES_5                = 0x8,
+	ETNA_GPU_FEATURES_6                = 0x9,
+
+	ETNA_GPU_STREAM_COUNT              = 0x10,
+	ETNA_GPU_REGISTER_MAX              = 0x11,
+	ETNA_GPU_THREAD_COUNT              = 0x12,
+	ETNA_GPU_VERTEX_CACHE_SIZE         = 0x13,
+	ETNA_GPU_SHADER_CORE_COUNT         = 0x14,
+	ETNA_GPU_PIXEL_PIPES               = 0x15,
+	ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE = 0x16,
+	ETNA_GPU_BUFFER_SIZE               = 0x17,
+	ETNA_GPU_INSTRUCTION_COUNT         = 0x18,
+	ETNA_GPU_NUM_CONSTANTS             = 0x19,
+	ETNA_GPU_NUM_VARYINGS              = 0x1a
+};
+
+/* bo flags: */
+#define DRM_ETNA_GEM_CACHE_CACHED       0x00010000
+#define DRM_ETNA_GEM_CACHE_WC           0x00020000
+#define DRM_ETNA_GEM_CACHE_UNCACHED     0x00040000
+#define DRM_ETNA_GEM_CACHE_MASK         0x000f0000
+/* map flags */
+#define DRM_ETNA_GEM_FORCE_MMU          0x00100000
+
+/* bo access flags: (keep aligned to ETNA_PREP_x) */
+#define DRM_ETNA_PREP_READ              0x01
+#define DRM_ETNA_PREP_WRITE             0x02
+#define DRM_ETNA_PREP_NOSYNC            0x04
+
+/* device functions:
+ */
+
+struct etna_device *etna_device_new(int fd);
+struct etna_device *etna_device_new_dup(int fd);
+struct etna_device *etna_device_ref(struct etna_device *dev);
+void etna_device_del(struct etna_device *dev);
+int etna_device_fd(struct etna_device *dev);
+
+/* gpu functions:
+ */
+
+struct etna_gpu *etna_gpu_new(struct etna_device *dev, unsigned int core);
+void etna_gpu_del(struct etna_gpu *gpu);
+int etna_gpu_get_param(struct etna_gpu *gpu, enum etna_param_id param,
+		uint64_t *value);
+
+
+/* pipe functions:
+ */
+
+struct etna_pipe *etna_pipe_new(struct etna_gpu *gpu, enum etna_pipe_id id);
+void etna_pipe_del(struct etna_pipe *pipe);
+int etna_pipe_wait(struct etna_pipe *pipe, uint32_t timestamp, uint32_t ms);
+int etna_pipe_wait_ns(struct etna_pipe *pipe, uint32_t timestamp, uint64_t ns);
+
+
+/* buffer-object functions:
+ */
+
+struct etna_bo *etna_bo_new(struct etna_device *dev,
+		uint32_t size, uint32_t flags);
+struct etna_bo *etna_bo_from_handle(struct etna_device *dev,
+		uint32_t handle, uint32_t size);
+struct etna_bo *etna_bo_from_name(struct etna_device *dev, uint32_t name);
+struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd);
+struct etna_bo *etna_bo_ref(struct etna_bo *bo);
+void etna_bo_del(struct etna_bo *bo);
+int etna_bo_get_name(struct etna_bo *bo, uint32_t *name);
+uint32_t etna_bo_handle(struct etna_bo *bo);
+int etna_bo_dmabuf(struct etna_bo *bo);
+uint32_t etna_bo_size(struct etna_bo *bo);
+void * etna_bo_map(struct etna_bo *bo);
+int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op);
+void etna_bo_cpu_fini(struct etna_bo *bo);
+
+
+/* cmd stream functions:
+ */
+
+struct etna_cmd_stream {
+	uint32_t *buffer;
+	uint32_t offset;	/* in 32-bit words */
+	uint32_t size;		/* in 32-bit words */
+};
+
+struct etna_cmd_stream *etna_cmd_stream_new(struct etna_pipe *pipe, uint32_t size,
+		void (*reset_notify)(struct etna_cmd_stream *stream, void *priv),
+		void *priv);
+void etna_cmd_stream_del(struct etna_cmd_stream *stream);
+uint32_t etna_cmd_stream_timestamp(struct etna_cmd_stream *stream);
+void etna_cmd_stream_flush(struct etna_cmd_stream *stream);
+void etna_cmd_stream_flush2(struct etna_cmd_stream *stream, int in_fence_fd,
+			    int *out_fence_fd);
+void etna_cmd_stream_finish(struct etna_cmd_stream *stream);
+
+static inline uint32_t etna_cmd_stream_avail(struct etna_cmd_stream *stream)
+{
+	static const uint32_t END_CLEARANCE = 2; /* LINK op code */
+
+	return stream->size - stream->offset - END_CLEARANCE;
+}
+
+static inline void etna_cmd_stream_reserve(struct etna_cmd_stream *stream, size_t n)
+{
+	if (etna_cmd_stream_avail(stream) < n)
+		etna_cmd_stream_flush(stream);
+}
+
+static inline void etna_cmd_stream_emit(struct etna_cmd_stream *stream, uint32_t data)
+{
+	stream->buffer[stream->offset++] = data;
+}
+
+static inline uint32_t etna_cmd_stream_get(struct etna_cmd_stream *stream, uint32_t offset)
+{
+	return stream->buffer[offset];
+}
+
+static inline void etna_cmd_stream_set(struct etna_cmd_stream *stream, uint32_t offset,
+		uint32_t data)
+{
+	stream->buffer[offset] = data;
+}
+
+static inline uint32_t etna_cmd_stream_offset(struct etna_cmd_stream *stream)
+{
+	return stream->offset;
+}
+
+struct etna_reloc {
+	struct etna_bo *bo;
+#define ETNA_RELOC_READ             0x0001
+#define ETNA_RELOC_WRITE            0x0002
+	uint32_t flags;
+	uint32_t offset;
+};
+
+void etna_cmd_stream_reloc(struct etna_cmd_stream *stream, const struct etna_reloc *r);
+
+#endif /* ETNAVIV_DRMIF_H_ */
diff --git a/etnaviv/etnaviv_gpu.c b/etnaviv/etnaviv_gpu.c
new file mode 100644
index 0000000..bc355e8
--- /dev/null
+++ b/etnaviv/etnaviv_gpu.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 Etnaviv Project
+ *
+ * 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:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+static uint64_t get_param(struct etna_device *dev, uint32_t core, uint32_t param)
+{
+	struct drm_etnaviv_param req = {
+		.pipe = core,
+		.param = param,
+	};
+	int ret;
+
+	ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GET_PARAM, &req, sizeof(req));
+	if (ret) {
+		ERROR_MSG("get-param (%x) failed! %d (%s)", param, ret, strerror(errno));
+		return 0;
+	}
+
+	return req.value;
+}
+
+struct etna_gpu *etna_gpu_new(struct etna_device *dev, unsigned int core)
+{
+	struct etna_gpu *gpu;
+
+	gpu = calloc(1, sizeof(*gpu));
+	if (!gpu) {
+		ERROR_MSG("allocation failed");
+		goto fail;
+	}
+
+	gpu->dev = dev;
+	gpu->core = core;
+
+	gpu->model    	= get_param(dev, core, ETNAVIV_PARAM_GPU_MODEL);
+	gpu->revision 	= get_param(dev, core, ETNAVIV_PARAM_GPU_REVISION);
+
+	if (!gpu->model)
+		goto fail;
+
+	INFO_MSG(" GPU model:          0x%x (rev %x)", gpu->model, gpu->revision);
+
+	return gpu;
+fail:
+	if (gpu)
+		etna_gpu_del(gpu);
+
+	return NULL;
+}
+
+void etna_gpu_del(struct etna_gpu *gpu)
+{
+	free(gpu);
+}
+
+int etna_gpu_get_param(struct etna_gpu *gpu, enum etna_param_id param,
+		uint64_t *value)
+{
+	struct etna_device *dev = gpu->dev;
+	unsigned int core = gpu->core;
+
+	switch(param) {
+	case ETNA_GPU_MODEL:
+		*value = gpu->model;
+		return 0;
+	case ETNA_GPU_REVISION:
+		*value = gpu->revision;
+		return 0;
+	case ETNA_GPU_FEATURES_0:
+		*value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_0);
+		return 0;
+	case ETNA_GPU_FEATURES_1:
+		*value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_1);
+		return 0;
+	case ETNA_GPU_FEATURES_2:
+		*value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_2);
+		return 0;
+	case ETNA_GPU_FEATURES_3:
+		*value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_3);
+		return 0;
+	case ETNA_GPU_FEATURES_4:
+		*value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_4);
+		return 0;
+	case ETNA_GPU_FEATURES_5:
+		*value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_5);
+		return 0;
+	case ETNA_GPU_FEATURES_6:
+		*value = get_param(dev, core, ETNAVIV_PARAM_GPU_FEATURES_6);
+		return 0;
+	case ETNA_GPU_STREAM_COUNT:
+		*value = get_param(dev, core, ETNA_GPU_STREAM_COUNT);
+		return 0;
+	case ETNA_GPU_REGISTER_MAX:
+		*value = get_param(dev, core, ETNA_GPU_REGISTER_MAX);
+		return 0;
+	case ETNA_GPU_THREAD_COUNT:
+		*value = get_param(dev, core, ETNA_GPU_THREAD_COUNT);
+		return 0;
+	case ETNA_GPU_VERTEX_CACHE_SIZE:
+		*value = get_param(dev, core, ETNA_GPU_VERTEX_CACHE_SIZE);
+		return 0;
+	case ETNA_GPU_SHADER_CORE_COUNT:
+		*value = get_param(dev, core, ETNA_GPU_SHADER_CORE_COUNT);
+		return 0;
+	case ETNA_GPU_PIXEL_PIPES:
+		*value = get_param(dev, core, ETNA_GPU_PIXEL_PIPES);
+		return 0;
+	case ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE:
+		*value = get_param(dev, core, ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE);
+		return 0;
+	case ETNA_GPU_BUFFER_SIZE:
+		*value = get_param(dev, core, ETNA_GPU_BUFFER_SIZE);
+		return 0;
+	case ETNA_GPU_INSTRUCTION_COUNT:
+		*value = get_param(dev, core, ETNA_GPU_INSTRUCTION_COUNT);
+		return 0;
+	case ETNA_GPU_NUM_CONSTANTS:
+		*value = get_param(dev, core, ETNA_GPU_NUM_CONSTANTS);
+		return 0;
+	case ETNA_GPU_NUM_VARYINGS:
+		*value = get_param(dev, core, ETNA_GPU_NUM_VARYINGS);
+		return 0;
+
+	default:
+		ERROR_MSG("invalid param id: %d", param);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/etnaviv/etnaviv_pipe.c b/etnaviv/etnaviv_pipe.c
new file mode 100644
index 0000000..94c5d37
--- /dev/null
+++ b/etnaviv/etnaviv_pipe.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * 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:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "etnaviv_priv.h"
+
+int etna_pipe_wait(struct etna_pipe *pipe, uint32_t timestamp, uint32_t ms)
+{
+	return etna_pipe_wait_ns(pipe, timestamp, ms * 1000000);
+}
+
+int etna_pipe_wait_ns(struct etna_pipe *pipe, uint32_t timestamp, uint64_t ns)
+{
+	struct etna_device *dev = pipe->gpu->dev;
+	int ret;
+
+	struct drm_etnaviv_wait_fence req = {
+		.pipe = pipe->gpu->core,
+		.fence = timestamp,
+	};
+
+	if (ns == 0)
+		req.flags |= ETNA_WAIT_NONBLOCK;
+
+	get_abs_timeout(&req.timeout, ns);
+
+	ret = drmCommandWrite(dev->fd, DRM_ETNAVIV_WAIT_FENCE, &req, sizeof(req));
+	if (ret) {
+		ERROR_MSG("wait-fence failed! %d (%s)", ret, strerror(errno));
+		return ret;
+	}
+
+	return 0;
+}
+
+void etna_pipe_del(struct etna_pipe *pipe)
+{
+	free(pipe);
+}
+
+struct etna_pipe *etna_pipe_new(struct etna_gpu *gpu, enum etna_pipe_id id)
+{
+	struct etna_pipe *pipe;
+
+	pipe = calloc(1, sizeof(*pipe));
+	if (!pipe) {
+		ERROR_MSG("allocation failed");
+		goto fail;
+	}
+
+	pipe->id = id;
+	pipe->gpu = gpu;
+
+	return pipe;
+fail:
+	return NULL;
+}
diff --git a/etnaviv/etnaviv_priv.h b/etnaviv/etnaviv_priv.h
new file mode 100644
index 0000000..1334ba3
--- /dev/null
+++ b/etnaviv/etnaviv_priv.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * 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:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifndef ETNAVIV_PRIV_H_
+#define ETNAVIV_PRIV_H_
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "libdrm_macros.h"
+#include "xf86drm.h"
+#include "xf86atomic.h"
+
+#include "util_double_list.h"
+
+#include "etnaviv_drmif.h"
+#include "etnaviv_drm.h"
+
+struct etna_bo_bucket {
+	uint32_t size;
+	struct list_head list;
+};
+
+struct etna_bo_cache {
+	struct etna_bo_bucket cache_bucket[14 * 4];
+	unsigned num_buckets;
+	time_t time;
+};
+
+struct etna_device {
+	int fd;
+	atomic_t refcnt;
+
+	/* tables to keep track of bo's, to avoid "evil-twin" etna_bo objects:
+	 *
+	 *   handle_table: maps handle to etna_bo
+	 *   name_table: maps flink name to etna_bo
+	 *
+	 * We end up needing two tables, because DRM_IOCTL_GEM_OPEN always
+	 * returns a new handle.  So we need to figure out if the bo is already
+	 * open in the process first, before calling gem-open.
+	 */
+	void *handle_table, *name_table;
+
+	struct etna_bo_cache bo_cache;
+
+	int closefd;        /* call close(fd) upon destruction */
+};
+
+drm_private void etna_bo_cache_init(struct etna_bo_cache *cache);
+drm_private void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time);
+drm_private struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache,
+		uint32_t *size, uint32_t flags);
+drm_private int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo);
+
+/* for where @table_lock is already held: */
+drm_private void etna_device_del_locked(struct etna_device *dev);
+
+/* a GEM buffer object allocated from the DRM device */
+struct etna_bo {
+	struct etna_device      *dev;
+	void            *map;           /* userspace mmap'ing (if there is one) */
+	uint32_t        size;
+	uint32_t        handle;
+	uint32_t        flags;
+	uint32_t        name;           /* flink global handle (DRI2 name) */
+	uint64_t        offset;         /* offset to mmap() */
+	atomic_t        refcnt;
+
+	/* in the common case, a bo won't be referenced by more than a single
+	 * command stream.  So to avoid looping over all the bo's in the
+	 * reloc table to find the idx of a bo that might already be in the
+	 * table, we cache the idx in the bo.  But in order to detect the
+	 * slow-path where bo is ref'd in multiple streams, we also must track
+	 * the current_stream for which the idx is valid.  See bo2idx().
+	 */
+	struct etna_cmd_stream *current_stream;
+	uint32_t idx;
+
+	int reuse;
+	struct list_head list;   /* bucket-list entry */
+	time_t free_time;        /* time when added to bucket-list */
+};
+
+struct etna_gpu {
+	struct etna_device *dev;
+	uint32_t core;
+	uint32_t model;
+	uint32_t revision;
+};
+
+struct etna_pipe {
+	enum etna_pipe_id id;
+	struct etna_gpu *gpu;
+};
+
+struct etna_cmd_stream_priv {
+	struct etna_cmd_stream base;
+	struct etna_pipe *pipe;
+
+	uint32_t last_timestamp;
+
+	/* submit ioctl related tables: */
+	struct {
+		/* bo's table: */
+		struct drm_etnaviv_gem_submit_bo *bos;
+		uint32_t nr_bos, max_bos;
+
+		/* reloc's table: */
+		struct drm_etnaviv_gem_submit_reloc *relocs;
+		uint32_t nr_relocs, max_relocs;
+	} submit;
+
+	/* should have matching entries in submit.bos: */
+	struct etna_bo **bos;
+	uint32_t nr_bos, max_bos;
+
+	/* notify callback if buffer reset happend */
+	void (*reset_notify)(struct etna_cmd_stream *stream, void *priv);
+	void *reset_notify_priv;
+};
+
+#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1))
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define enable_debug 1  /* TODO make dynamic */
+
+#define INFO_MSG(fmt, ...) \
+		do { drmMsg("[I] "fmt " (%s:%d)\n", \
+				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define DEBUG_MSG(fmt, ...) \
+		do if (enable_debug) { drmMsg("[D] "fmt " (%s:%d)\n", \
+				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define WARN_MSG(fmt, ...) \
+		do { drmMsg("[W] "fmt " (%s:%d)\n", \
+				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define ERROR_MSG(fmt, ...) \
+		do { drmMsg("[E] " fmt " (%s:%d)\n", \
+				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+
+#define VOID2U64(x) ((uint64_t)(unsigned long)(x))
+
+static inline void get_abs_timeout(struct drm_etnaviv_timespec *tv, uint64_t ns)
+{
+	struct timespec t;
+	uint32_t s = ns / 1000000000;
+	clock_gettime(CLOCK_MONOTONIC, &t);
+	tv->tv_sec = t.tv_sec + s;
+	tv->tv_nsec = t.tv_nsec + ns - (s * 1000000000);
+}
+
+#endif /* ETNAVIV_PRIV_H_ */
diff --git a/etnaviv/libdrm_etnaviv.pc.in b/etnaviv/libdrm_etnaviv.pc.in
new file mode 100644
index 0000000..13fed01
--- /dev/null
+++ b/etnaviv/libdrm_etnaviv.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_etnaviv
+Description: Userspace interface to etnaviv kernel DRM services
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -ldrm_etnaviv
+Cflags: -I${includedir} -I${includedir}/libdrm
+Requires.private: libdrm
diff --git a/exynos/Makefile.am b/exynos/Makefile.am
new file mode 100644
index 0000000..f99f898
--- /dev/null
+++ b/exynos/Makefile.am
@@ -0,0 +1,27 @@
+AM_CFLAGS = \
+	$(WARN_CFLAGS) \
+	-I$(top_srcdir) \
+	$(PTHREADSTUBS_CFLAGS) \
+	-I$(top_srcdir)/include/drm
+
+libdrm_exynos_la_LTLIBRARIES = libdrm_exynos.la
+libdrm_exynos_ladir = $(libdir)
+libdrm_exynos_la_LDFLAGS = -version-number 1:0:0 -no-undefined
+libdrm_exynos_la_LIBADD = ../libdrm.la @PTHREADSTUBS_LIBS@
+
+libdrm_exynos_la_SOURCES = \
+	exynos_drm.c \
+	exynos_fimg2d.c \
+	fimg2d_reg.h
+
+libdrm_exynoscommonincludedir = ${includedir}/exynos
+libdrm_exynoscommoninclude_HEADERS = exynos_drm.h exynos_fimg2d.h
+
+libdrm_exynosincludedir = ${includedir}/libdrm
+libdrm_exynosinclude_HEADERS = exynos_drmif.h
+
+pkgconfigdir = @pkgconfigdir@
+pkgconfig_DATA = libdrm_exynos.pc
+
+TESTS = exynos-symbol-check
+EXTRA_DIST = $(TESTS)
diff --git a/exynos/exynos-symbol-check b/exynos/exynos-symbol-check
new file mode 100755
index 0000000..9692caa
--- /dev/null
+++ b/exynos/exynos-symbol-check
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# The following symbols (past the first five) are taken from the public headers.
+# A list of the latter should be available Makefile.am/libdrm_exynos*_HEADERS
+
+FUNCS=$(nm -D --format=bsd --defined-only ${1-.libs/libdrm_exynos.so} | awk '{print $3}'| while read func; do
+( grep -q "^$func$" || echo $func )  <<EOF
+__bss_start
+_edata
+_end
+_fini
+_init
+exynos_bo_create
+exynos_bo_destroy
+exynos_bo_from_name
+exynos_bo_get_info
+exynos_bo_get_name
+exynos_bo_handle
+exynos_bo_map
+exynos_device_create
+exynos_device_destroy
+exynos_prime_fd_to_handle
+exynos_prime_handle_to_fd
+exynos_vidi_connection
+exynos_handle_event
+g2d_blend
+g2d_copy
+g2d_copy_with_scale
+g2d_exec
+g2d_config_event
+g2d_fini
+g2d_init
+g2d_move
+g2d_scale_and_blend
+g2d_solid_fill
+EOF
+done)
+
+test ! -n "$FUNCS" || echo $FUNCS
+test ! -n "$FUNCS"
diff --git a/exynos/exynos_drm.c b/exynos/exynos_drm.c
new file mode 100644
index 0000000..f6204f1
--- /dev/null
+++ b/exynos/exynos_drm.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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:
+ *    Inki Dae <inki.dae@samsung.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <linux/stddef.h>
+
+#include <xf86drm.h>
+
+#include "libdrm_macros.h"
+#include "exynos_drm.h"
+#include "exynos_drmif.h"
+
+#define U642VOID(x) ((void *)(unsigned long)(x))
+
+/*
+ * Create exynos drm device object.
+ *
+ * @fd: file descriptor to exynos drm driver opened.
+ *
+ * if true, return the device object else NULL.
+ */
+struct exynos_device * exynos_device_create(int fd)
+{
+	struct exynos_device *dev;
+
+	dev = calloc(sizeof(*dev), 1);
+	if (!dev) {
+		fprintf(stderr, "failed to create device[%s].\n",
+				strerror(errno));
+		return NULL;
+	}
+
+	dev->fd = fd;
+
+	return dev;
+}
+
+/*
+ * Destroy exynos drm device object
+ *
+ * @dev: exynos drm device object.
+ */
+void exynos_device_destroy(struct exynos_device *dev)
+{
+	free(dev);
+}
+
+/*
+ * Create a exynos buffer object to exynos drm device.
+ *
+ * @dev: exynos 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,
+ *	EXYNOS_BO_NONCONTIG and EXYNOS-BO_NONCACHABLE types would
+ *	be used.
+ *
+ * if true, return a exynos buffer object else NULL.
+ */
+struct exynos_bo * exynos_bo_create(struct exynos_device *dev,
+					       size_t size, uint32_t flags)
+{
+	struct exynos_bo *bo;
+	struct drm_exynos_gem_create req = {
+		.size = size,
+		.flags = flags,
+	};
+
+	if (size == 0) {
+		fprintf(stderr, "invalid size.\n");
+		goto fail;
+	}
+
+	bo = calloc(sizeof(*bo), 1);
+	if (!bo) {
+		fprintf(stderr, "failed to create bo[%s].\n",
+				strerror(errno));
+		goto err_free_bo;
+	}
+
+	bo->dev = dev;
+
+	if (drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_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;
+}
+
+/*
+ * Get information to gem region allocated.
+ *
+ * @dev: exynos drm device object.
+ * @handle: gem handle to request gem info.
+ * @size: size to gem object and returned by kernel side.
+ * @flags: gem flags to gem object and returned by kernel side.
+ *
+ * with this function call, you can get flags and size to gem handle
+ * through bo object.
+ *
+ * if true, return 0 else negative.
+ */
+int exynos_bo_get_info(struct exynos_device *dev, uint32_t handle,
+				  size_t *size, uint32_t *flags)
+{
+	int ret;
+	struct drm_exynos_gem_info req = {
+		.handle = handle,
+	};
+
+	ret = drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_GEM_GET, &req);
+	if (ret < 0) {
+		fprintf(stderr, "failed to get gem object information[%s].\n",
+				strerror(errno));
+		return ret;
+	}
+
+	*size = req.size;
+	*flags = req.flags;
+
+	return 0;
+}
+
+/*
+ * Destroy a exynos buffer object.
+ *
+ * @bo: a exynos buffer object to be destroyed.
+ */
+void exynos_bo_destroy(struct exynos_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 exynos buffer object from a gem global object name.
+ *
+ * @dev: a exynos device object.
+ * @name: a gem global object name exported by another process.
+ *
+ * this interface is used to get a exynos buffer object from a gem
+ * global object name sent by another process for buffer sharing.
+ *
+ * if true, return a exynos buffer object else NULL.
+ *
+ */
+struct exynos_bo *
+exynos_bo_from_name(struct exynos_device *dev, uint32_t name)
+{
+	struct exynos_bo *bo;
+	struct drm_gem_open req = {
+		.name = name,
+	};
+
+	bo = calloc(sizeof(*bo), 1);
+	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 exynos 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 exynos_bo_get_name(struct exynos_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 exynos_bo_handle(struct exynos_bo *bo)
+{
+	return bo->handle;
+}
+
+/*
+ * Mmap a buffer to user space.
+ *
+ * @bo: a exynos buffer object including a gem object handle to be mmapped
+ *	to user space.
+ *
+ * if true, user pointer mmaped else NULL.
+ */
+void *exynos_bo_map(struct exynos_bo *bo)
+{
+	if (!bo->vaddr) {
+		struct exynos_device *dev = bo->dev;
+		struct drm_mode_map_dumb arg;
+		void *map = NULL;
+		int ret;
+
+		memset(&arg, 0, sizeof(arg));
+		arg.handle = bo->handle;
+
+		ret = drmIoctl(dev->fd, DRM_IOCTL_MODE_MAP_DUMB, &arg);
+		if (ret) {
+			fprintf(stderr, "failed to map dumb buffer[%s].\n",
+				strerror(errno));
+			return NULL;
+		}
+
+		map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+				dev->fd, arg.offset);
+
+		if (map != MAP_FAILED)
+			bo->vaddr = map;
+	}
+
+	return bo->vaddr;
+}
+
+/*
+ * Export gem object to dmabuf as file descriptor.
+ *
+ * @dev: exynos device object
+ * @handle: gem handle to export as file descriptor of dmabuf
+ * @fd: file descriptor returned from kernel
+ *
+ * @return: 0 on success, -1 on error, and errno will be set
+ */
+int
+exynos_prime_handle_to_fd(struct exynos_device *dev, uint32_t handle, int *fd)
+{
+	return drmPrimeHandleToFD(dev->fd, handle, 0, fd);
+}
+
+/*
+ * Import file descriptor into gem handle.
+ *
+ * @dev: exynos device object
+ * @fd: file descriptor of dmabuf to import
+ * @handle: gem handle returned from kernel
+ *
+ * @return: 0 on success, -1 on error, and errno will be set
+ */
+int
+exynos_prime_fd_to_handle(struct exynos_device *dev, int fd, uint32_t *handle)
+{
+	return drmPrimeFDToHandle(dev->fd, fd, handle);
+}
+
+
+
+/*
+ * Request Wireless Display connection or disconnection.
+ *
+ * @dev: a exynos device object.
+ * @connect: indicate whether connectoin or disconnection request.
+ * @ext: indicate whether edid data includes extensions data or not.
+ * @edid: a pointer to edid data from Wireless Display device.
+ *
+ * this interface is used to request Virtual Display driver connection or
+ * disconnection. for this, user should get a edid data from the Wireless
+ * Display device and then send that data to kernel driver with connection
+ * request
+ *
+ * if true, return 0 else negative.
+ */
+int
+exynos_vidi_connection(struct exynos_device *dev, uint32_t connect,
+		       uint32_t ext, void *edid)
+{
+	struct drm_exynos_vidi_connection req = {
+		.connection	= connect,
+		.extensions	= ext,
+		.edid		= (uint64_t)(uintptr_t)edid,
+	};
+	int ret;
+
+	ret = drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_VIDI_CONNECTION, &req);
+	if (ret) {
+		fprintf(stderr, "failed to request vidi connection[%s].\n",
+				strerror(errno));
+		return ret;
+	}
+
+	return 0;
+}
+
+static void
+exynos_handle_vendor(int fd, struct drm_event *e, void *ctx)
+{
+	struct drm_exynos_g2d_event *g2d;
+	struct exynos_event_context *ectx = ctx;
+
+	switch (e->type) {
+		case DRM_EXYNOS_G2D_EVENT:
+			if (ectx->version < 1 || ectx->g2d_event_handler == NULL)
+				break;
+			g2d = (struct drm_exynos_g2d_event *)e;
+			ectx->g2d_event_handler(fd, g2d->cmdlist_no, g2d->tv_sec,
+						g2d->tv_usec, U642VOID(g2d->user_data));
+			break;
+
+		default:
+			break;
+	}
+}
+
+int
+exynos_handle_event(struct exynos_device *dev, struct exynos_event_context *ctx)
+{
+	char buffer[1024];
+	int len, i;
+	struct drm_event *e;
+	struct drm_event_vblank *vblank;
+	drmEventContextPtr evctx = &ctx->base;
+
+	/* The DRM read semantics guarantees that we always get only
+	 * complete events. */
+	len = read(dev->fd, buffer, sizeof buffer);
+	if (len == 0)
+		return 0;
+	if (len < (int)sizeof *e)
+		return -1;
+
+	i = 0;
+	while (i < len) {
+		e = (struct drm_event *)(buffer + i);
+		switch (e->type) {
+		case DRM_EVENT_VBLANK:
+			if (evctx->version < 1 ||
+			    evctx->vblank_handler == NULL)
+				break;
+			vblank = (struct drm_event_vblank *) e;
+			evctx->vblank_handler(dev->fd,
+					      vblank->sequence,
+					      vblank->tv_sec,
+					      vblank->tv_usec,
+					      U642VOID (vblank->user_data));
+			break;
+		case DRM_EVENT_FLIP_COMPLETE:
+			if (evctx->version < 2 ||
+			    evctx->page_flip_handler == NULL)
+				break;
+			vblank = (struct drm_event_vblank *) e;
+			evctx->page_flip_handler(dev->fd,
+						 vblank->sequence,
+						 vblank->tv_sec,
+						 vblank->tv_usec,
+						 U642VOID (vblank->user_data));
+			break;
+		default:
+			exynos_handle_vendor(dev->fd, e, evctx);
+			break;
+		}
+		i += e->length;
+	}
+
+	return 0;
+}
diff --git a/exynos/exynos_drm.h b/exynos/exynos_drm.h
new file mode 100644
index 0000000..c3af0ac
--- /dev/null
+++ b/exynos/exynos_drm.h
@@ -0,0 +1,172 @@
+/* exynos_drm.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Authors:
+ *	Inki Dae <inki.dae@samsung.com>
+ *	Joonyoung Shim <jy0922.shim@samsung.com>
+ *	Seung-Woo Kim <sw0312.kim@samsung.com>
+ *
+ * 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
+ * VA LINUX SYSTEMS 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.
+ */
+
+#ifndef _EXYNOS_DRM_H_
+#define _EXYNOS_DRM_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_exynos_gem_create {
+	uint64_t size;
+	unsigned int flags;
+	unsigned int handle;
+};
+
+/**
+ * A structure to gem information.
+ *
+ * @handle: a handle to gem object created.
+ * @flags: flag value including memory type and cache attribute and
+ *	this value would be set by driver.
+ * @size: size to memory region allocated by gem and this size would
+ *	be set by driver.
+ */
+struct drm_exynos_gem_info {
+	unsigned int handle;
+	unsigned int flags;
+	uint64_t size;
+};
+
+/**
+ * A structure for user connection request of virtual display.
+ *
+ * @connection: indicate whether doing connetion or not by user.
+ * @extensions: if this value is 1 then the vidi driver would need additional
+ *	128bytes edid data.
+ * @edid: the edid data pointer from user side.
+ */
+struct drm_exynos_vidi_connection {
+	unsigned int connection;
+	unsigned int extensions;
+	uint64_t edid;
+};
+
+/* memory type definitions. */
+enum e_drm_exynos_gem_mem_type {
+	/* Physically Continuous memory and used as default. */
+	EXYNOS_BO_CONTIG	= 0 << 0,
+	/* Physically Non-Continuous memory. */
+	EXYNOS_BO_NONCONTIG	= 1 << 0,
+	/* non-cachable mapping and used as default. */
+	EXYNOS_BO_NONCACHABLE	= 0 << 1,
+	/* cachable mapping. */
+	EXYNOS_BO_CACHABLE	= 1 << 1,
+	/* write-combine mapping. */
+	EXYNOS_BO_WC		= 1 << 2,
+	EXYNOS_BO_MASK		= EXYNOS_BO_NONCONTIG | EXYNOS_BO_CACHABLE |
+					EXYNOS_BO_WC
+};
+
+struct drm_exynos_g2d_get_ver {
+	__u32	major;
+	__u32	minor;
+};
+
+struct drm_exynos_g2d_cmd {
+	__u32	offset;
+	__u32	data;
+};
+
+enum drm_exynos_g2d_buf_type {
+	G2D_BUF_USERPTR = 1 << 31,
+};
+
+enum drm_exynos_g2d_event_type {
+	G2D_EVENT_NOT,
+	G2D_EVENT_NONSTOP,
+	G2D_EVENT_STOP,		/* not yet */
+};
+
+struct drm_exynos_g2d_userptr {
+	unsigned long userptr;
+	unsigned long size;
+};
+
+struct drm_exynos_g2d_set_cmdlist {
+	__u64					cmd;
+	__u64					cmd_buf;
+	__u32					cmd_nr;
+	__u32					cmd_buf_nr;
+
+	/* for g2d event */
+	__u64					event_type;
+	__u64					user_data;
+};
+
+struct drm_exynos_g2d_exec {
+	__u64					async;
+};
+
+#define DRM_EXYNOS_GEM_CREATE		0x00
+/* Reserved 0x04 ~ 0x05 for exynos specific gem ioctl */
+#define DRM_EXYNOS_GEM_GET		0x04
+#define DRM_EXYNOS_VIDI_CONNECTION	0x07
+
+/* G2D */
+#define DRM_EXYNOS_G2D_GET_VER		0x20
+#define DRM_EXYNOS_G2D_SET_CMDLIST	0x21
+#define DRM_EXYNOS_G2D_EXEC		0x22
+
+#define DRM_IOCTL_EXYNOS_GEM_CREATE		DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
+
+#define DRM_IOCTL_EXYNOS_GEM_GET	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_GEM_GET,	struct drm_exynos_gem_info)
+
+#define DRM_IOCTL_EXYNOS_VIDI_CONNECTION	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_VIDI_CONNECTION, struct drm_exynos_vidi_connection)
+
+#define DRM_IOCTL_EXYNOS_G2D_GET_VER		DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_G2D_GET_VER, struct drm_exynos_g2d_get_ver)
+#define DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST	DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_G2D_SET_CMDLIST, struct drm_exynos_g2d_set_cmdlist)
+#define DRM_IOCTL_EXYNOS_G2D_EXEC		DRM_IOWR(DRM_COMMAND_BASE + \
+		DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec)
+
+/* EXYNOS specific events */
+#define DRM_EXYNOS_G2D_EVENT		0x80000000
+
+struct drm_exynos_g2d_event {
+	struct drm_event	base;
+	__u64				user_data;
+	__u32				tv_sec;
+	__u32				tv_usec;
+	__u32				cmdlist_no;
+	__u32				reserved;
+};
+
+#endif
diff --git a/exynos/exynos_drmif.h b/exynos/exynos_drmif.h
new file mode 100644
index 0000000..154439b
--- /dev/null
+++ b/exynos/exynos_drmif.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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:
+ *    Inki Dae <inki.dae@samsung.com>
+ */
+
+#ifndef EXYNOS_DRMIF_H_
+#define EXYNOS_DRMIF_H_
+
+#include <xf86drm.h>
+#include <stdint.h>
+#include "exynos_drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct exynos_device {
+	int fd;
+};
+
+/*
+ * Exynos Buffer Object structure.
+ *
+ * @dev: exynos 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 exynos_bo {
+	struct exynos_device	*dev;
+	uint32_t		handle;
+	uint32_t		flags;
+	size_t			size;
+	void			*vaddr;
+	uint32_t		name;
+};
+
+#define EXYNOS_EVENT_CONTEXT_VERSION 1
+
+/*
+ * Exynos Event Context structure.
+ *
+ * @base: base context (for core events).
+ * @version: version info similar to the one in 'drmEventContext'.
+ * @g2d_event_handler: handler for G2D events.
+ */
+struct exynos_event_context {
+	drmEventContext base;
+
+	int version;
+
+	void (*g2d_event_handler)(int fd, unsigned int cmdlist_no,
+							  unsigned int tv_sec, unsigned int tv_usec,
+							  void *user_data);
+};
+
+/*
+ * device related functions:
+ */
+struct exynos_device * exynos_device_create(int fd);
+void exynos_device_destroy(struct exynos_device *dev);
+
+/*
+ * buffer-object related functions:
+ */
+struct exynos_bo * exynos_bo_create(struct exynos_device *dev,
+		size_t size, uint32_t flags);
+int exynos_bo_get_info(struct exynos_device *dev, uint32_t handle,
+			size_t *size, uint32_t *flags);
+void exynos_bo_destroy(struct exynos_bo *bo);
+struct exynos_bo * exynos_bo_from_name(struct exynos_device *dev, uint32_t name);
+int exynos_bo_get_name(struct exynos_bo *bo, uint32_t *name);
+uint32_t exynos_bo_handle(struct exynos_bo *bo);
+void * exynos_bo_map(struct exynos_bo *bo);
+int exynos_prime_handle_to_fd(struct exynos_device *dev, uint32_t handle,
+					int *fd);
+int exynos_prime_fd_to_handle(struct exynos_device *dev, int fd,
+					uint32_t *handle);
+
+/*
+ * Virtual Display related functions:
+ */
+int exynos_vidi_connection(struct exynos_device *dev, uint32_t connect,
+				uint32_t ext, void *edid);
+
+/*
+ * event handling related functions:
+ */
+int exynos_handle_event(struct exynos_device *dev,
+				struct exynos_event_context *ctx);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* EXYNOS_DRMIF_H_ */
diff --git a/exynos/exynos_fimg2d.c b/exynos/exynos_fimg2d.c
new file mode 100644
index 0000000..61340c3
--- /dev/null
+++ b/exynos/exynos_fimg2d.c
@@ -0,0 +1,1001 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co.Ltd
+ * Authors:
+ *	Inki Dae <inki.dae@samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <sys/mman.h>
+#include <linux/stddef.h>
+
+#include <xf86drm.h>
+
+#include "libdrm_macros.h"
+#include "exynos_drm.h"
+#include "fimg2d_reg.h"
+#include "exynos_fimg2d.h"
+
+#define		SET_BF(val, sc, si, scsa, scda, dc, di, dcsa, dcda) \
+			val.data.src_coeff = sc;		\
+			val.data.inv_src_color_coeff = si;	\
+			val.data.src_coeff_src_a = scsa;	\
+			val.data.src_coeff_dst_a = scda;	\
+			val.data.dst_coeff = dc;		\
+			val.data.inv_dst_color_coeff = di;	\
+			val.data.dst_coeff_src_a = dcsa;	\
+			val.data.dst_coeff_dst_a = dcda;
+
+#define MIN(a, b)	((a) < (b) ? (a) : (b))
+
+#define MSG_PREFIX "exynos/fimg2d: "
+
+#define G2D_MAX_CMD_NR		64
+#define G2D_MAX_GEM_CMD_NR	64
+#define G2D_MAX_CMD_LIST_NR	64
+
+struct g2d_context {
+	int				fd;
+	unsigned int			major;
+	unsigned int			minor;
+	struct drm_exynos_g2d_cmd	cmd[G2D_MAX_CMD_NR];
+	struct drm_exynos_g2d_cmd	cmd_buf[G2D_MAX_GEM_CMD_NR];
+	unsigned int			cmd_nr;
+	unsigned int			cmd_buf_nr;
+	unsigned int			cmdlist_nr;
+	void				*event_userdata;
+};
+
+enum g2d_base_addr_reg {
+	g2d_dst = 0,
+	g2d_src
+};
+
+enum e_g2d_dir_mode {
+	G2D_DIR_MODE_POSITIVE = 0,
+	G2D_DIR_MODE_NEGATIVE = 1
+};
+
+union g2d_direction_val {
+	unsigned int val[2];
+	struct {
+		/* SRC_MSK_DIRECT_REG [0:1] (source) */
+		enum e_g2d_dir_mode		src_x_direction:1;
+		enum e_g2d_dir_mode		src_y_direction:1;
+
+		/* SRC_MSK_DIRECT_REG [2:3] */
+		unsigned int			reversed1:2;
+
+		/* SRC_MSK_DIRECT_REG [4:5] (mask) */
+		enum e_g2d_dir_mode		mask_x_direction:1;
+		enum e_g2d_dir_mode		mask_y_direction:1;
+
+		/* SRC_MSK_DIRECT_REG [6:31] */
+		unsigned int			padding1:26;
+
+		/* DST_PAT_DIRECT_REG [0:1] (destination) */
+		enum e_g2d_dir_mode		dst_x_direction:1;
+		enum e_g2d_dir_mode		dst_y_direction:1;
+
+		/* DST_PAT_DIRECT_REG [2:3] */
+		unsigned int			reversed2:2;
+
+		/* DST_PAT_DIRECT_REG [4:5] (pattern) */
+		enum e_g2d_dir_mode		pat_x_direction:1;
+		enum e_g2d_dir_mode		pat_y_direction:1;
+
+		/* DST_PAT_DIRECT_REG [6:31] */
+		unsigned int			padding2:26;
+	} data;
+};
+
+static unsigned int g2d_get_scaling(unsigned int src, unsigned int dst)
+{
+	/*
+	 * The G2D hw scaling factor is a normalized inverse of the scaling factor.
+	 * For example: When source width is 100 and destination width is 200
+	 * (scaling of 2x), then the hw factor is NC * 100 / 200.
+	 * The normalization factor (NC) is 2^16 = 0x10000.
+	 */
+
+	return ((src << 16) / dst);
+}
+
+static unsigned int g2d_get_blend_op(enum e_g2d_op op)
+{
+	union g2d_blend_func_val val;
+
+	val.val = 0;
+
+	/*
+	 * The switch statement is missing the default branch since
+	 * we assume that the caller checks the blending operation
+	 * via g2d_validate_blending_op() first.
+	 */
+	switch (op) {
+	case G2D_OP_CLEAR:
+	case G2D_OP_DISJOINT_CLEAR:
+	case G2D_OP_CONJOINT_CLEAR:
+		SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ZERO,
+				0, 0, 0);
+		break;
+	case G2D_OP_SRC:
+	case G2D_OP_DISJOINT_SRC:
+	case G2D_OP_CONJOINT_SRC:
+		SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0, G2D_COEFF_MODE_ZERO,
+				0, 0, 0);
+		break;
+	case G2D_OP_DST:
+	case G2D_OP_DISJOINT_DST:
+	case G2D_OP_CONJOINT_DST:
+		SET_BF(val, G2D_COEFF_MODE_ZERO, 0, 0, 0, G2D_COEFF_MODE_ONE,
+				0, 0, 0);
+		break;
+	case G2D_OP_OVER:
+		SET_BF(val, G2D_COEFF_MODE_ONE, 0, 0, 0,
+				G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0);
+		break;
+	case G2D_OP_INTERPOLATE:
+		SET_BF(val, G2D_COEFF_MODE_SRC_ALPHA, 0, 0, 0,
+				G2D_COEFF_MODE_SRC_ALPHA, 1, 0, 0);
+		break;
+	}
+
+	return val.val;
+}
+
+/*
+ * g2d_check_space - check if command buffers have enough space left.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @num_cmds: number of (regular) commands.
+ * @num_gem_cmds: number of GEM commands.
+ */
+static unsigned int g2d_check_space(const struct g2d_context *ctx,
+	unsigned int num_cmds, unsigned int num_gem_cmds)
+{
+	if (ctx->cmd_nr + num_cmds >= G2D_MAX_CMD_NR ||
+	    ctx->cmd_buf_nr + num_gem_cmds >= G2D_MAX_GEM_CMD_NR)
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * g2d_validate_select_mode - validate select mode.
+ *
+ * @mode: the mode to validate
+ *
+ * Returns zero for an invalid mode and one otherwise.
+ */
+static int g2d_validate_select_mode(
+	enum e_g2d_select_mode mode)
+{
+	switch (mode) {
+	case G2D_SELECT_MODE_NORMAL:
+	case G2D_SELECT_MODE_FGCOLOR:
+	case G2D_SELECT_MODE_BGCOLOR:
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * g2d_validate_blending_op - validate blending operation.
+ *
+ * @operation: the operation to validate
+ *
+ * Returns zero for an invalid mode and one otherwise.
+ */
+static int g2d_validate_blending_op(
+	enum e_g2d_op operation)
+{
+	switch (operation) {
+	case G2D_OP_CLEAR:
+	case G2D_OP_SRC:
+	case G2D_OP_DST:
+	case G2D_OP_OVER:
+	case G2D_OP_INTERPOLATE:
+	case G2D_OP_DISJOINT_CLEAR:
+	case G2D_OP_DISJOINT_SRC:
+	case G2D_OP_DISJOINT_DST:
+	case G2D_OP_CONJOINT_CLEAR:
+	case G2D_OP_CONJOINT_SRC:
+	case G2D_OP_CONJOINT_DST:
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * g2d_add_cmd - set given command and value to user side command buffer.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @cmd: command data.
+ * @value: value data.
+ *
+ * The caller has to make sure that the commands buffers have enough space
+ * left to hold the command. Use g2d_check_space() to ensure this.
+ */
+static void g2d_add_cmd(struct g2d_context *ctx, unsigned long cmd,
+			unsigned long value)
+{
+	switch (cmd & ~(G2D_BUF_USERPTR)) {
+	case SRC_BASE_ADDR_REG:
+	case SRC_PLANE2_BASE_ADDR_REG:
+	case DST_BASE_ADDR_REG:
+	case DST_PLANE2_BASE_ADDR_REG:
+	case PAT_BASE_ADDR_REG:
+	case MASK_BASE_ADDR_REG:
+		assert(ctx->cmd_buf_nr < G2D_MAX_GEM_CMD_NR);
+
+		ctx->cmd_buf[ctx->cmd_buf_nr].offset = cmd;
+		ctx->cmd_buf[ctx->cmd_buf_nr].data = value;
+		ctx->cmd_buf_nr++;
+		break;
+	default:
+		assert(ctx->cmd_nr < G2D_MAX_CMD_NR);
+
+		ctx->cmd[ctx->cmd_nr].offset = cmd;
+		ctx->cmd[ctx->cmd_nr].data = value;
+		ctx->cmd_nr++;
+		break;
+	}
+}
+
+/*
+ * g2d_add_base_addr - helper function to set dst/src base address register.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @img: a pointer to the dst/src g2d_image structure.
+ * @reg: the register that should be set.
+ */
+static void g2d_add_base_addr(struct g2d_context *ctx, struct g2d_image *img,
+			enum g2d_base_addr_reg reg)
+{
+	const unsigned long cmd = (reg == g2d_dst) ?
+		DST_BASE_ADDR_REG : SRC_BASE_ADDR_REG;
+
+	if (img->buf_type == G2D_IMGBUF_USERPTR)
+		g2d_add_cmd(ctx, cmd | G2D_BUF_USERPTR,
+				(unsigned long)&img->user_ptr[0]);
+	else
+		g2d_add_cmd(ctx, cmd, img->bo[0]);
+}
+
+/*
+ * g2d_set_direction - setup direction register (useful for overlapping blits).
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @dir: a pointer to the g2d_direction_val structure.
+ */
+static void g2d_set_direction(struct g2d_context *ctx,
+			const union g2d_direction_val *dir)
+{
+	g2d_add_cmd(ctx, SRC_MASK_DIRECT_REG, dir->val[0]);
+	g2d_add_cmd(ctx, DST_PAT_DIRECT_REG, dir->val[1]);
+}
+
+/*
+ * g2d_flush - submit all commands and values in user side command buffer
+ *		to command queue aware of fimg2d dma.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ *
+ * This function should be called after all commands and values to user
+ * side command buffer are set. It submits that buffer to the kernel side driver.
+ */
+static int g2d_flush(struct g2d_context *ctx)
+{
+	int ret;
+	struct drm_exynos_g2d_set_cmdlist cmdlist = {0};
+
+	if (ctx->cmd_nr == 0 && ctx->cmd_buf_nr == 0)
+		return 0;
+
+	if (ctx->cmdlist_nr >= G2D_MAX_CMD_LIST_NR) {
+		fprintf(stderr, MSG_PREFIX "command list overflow.\n");
+		return -EINVAL;
+	}
+
+	cmdlist.cmd = (uint64_t)(uintptr_t)&ctx->cmd[0];
+	cmdlist.cmd_buf = (uint64_t)(uintptr_t)&ctx->cmd_buf[0];
+	cmdlist.cmd_nr = ctx->cmd_nr;
+	cmdlist.cmd_buf_nr = ctx->cmd_buf_nr;
+
+	if (ctx->event_userdata) {
+		cmdlist.event_type = G2D_EVENT_NONSTOP;
+		cmdlist.user_data = (uint64_t)(uintptr_t)(ctx->event_userdata);
+		ctx->event_userdata = NULL;
+	} else {
+		cmdlist.event_type = G2D_EVENT_NOT;
+		cmdlist.user_data = 0;
+	}
+
+	ctx->cmd_nr = 0;
+	ctx->cmd_buf_nr = 0;
+
+	ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST, &cmdlist);
+	if (ret < 0) {
+		fprintf(stderr, MSG_PREFIX "failed to set cmdlist.\n");
+		return ret;
+	}
+
+	ctx->cmdlist_nr++;
+
+	return ret;
+}
+
+/**
+ * g2d_init - create a new g2d context and get hardware version.
+ *
+ * fd: a file descriptor to an opened drm device.
+ */
+struct g2d_context *g2d_init(int fd)
+{
+	struct drm_exynos_g2d_get_ver ver;
+	struct g2d_context *ctx;
+	int ret;
+
+	ctx = calloc(1, sizeof(*ctx));
+	if (!ctx) {
+		fprintf(stderr, MSG_PREFIX "failed to allocate context.\n");
+		return NULL;
+	}
+
+	ctx->fd = fd;
+
+	ret = drmIoctl(fd, DRM_IOCTL_EXYNOS_G2D_GET_VER, &ver);
+	if (ret < 0) {
+		fprintf(stderr, MSG_PREFIX "failed to get version.\n");
+		free(ctx);
+		return NULL;
+	}
+
+	ctx->major = ver.major;
+	ctx->minor = ver.minor;
+
+	printf(MSG_PREFIX "G2D version (%d.%d).\n", ctx->major, ctx->minor);
+	return ctx;
+}
+
+void g2d_fini(struct g2d_context *ctx)
+{
+	free(ctx);
+}
+
+/**
+ * g2d_config_event - setup userdata configuration for a g2d event.
+ *		The next invocation of a g2d call (e.g. g2d_solid_fill) is
+ *		then going to flag the command buffer as 'nonstop'.
+ *		Completion of the command buffer execution can then be
+ *		determined by using drmHandleEvent on the DRM fd.
+ *		The userdata is 'consumed' in the process.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @userdata: a pointer to the user data
+ */
+void g2d_config_event(struct g2d_context *ctx, void *userdata)
+{
+	ctx->event_userdata = userdata;
+}
+
+/**
+ * g2d_exec - start the dma to process all commands summited by g2d_flush().
+ *
+ * @ctx: a pointer to g2d_context structure.
+ */
+int g2d_exec(struct g2d_context *ctx)
+{
+	struct drm_exynos_g2d_exec exec;
+	int ret;
+
+	if (ctx->cmdlist_nr == 0)
+		return -EINVAL;
+
+	exec.async = 0;
+
+	ret = drmIoctl(ctx->fd, DRM_IOCTL_EXYNOS_G2D_EXEC, &exec);
+	if (ret < 0) {
+		fprintf(stderr, MSG_PREFIX "failed to execute.\n");
+		return ret;
+	}
+
+	ctx->cmdlist_nr = 0;
+
+	return ret;
+}
+
+/**
+ * g2d_solid_fill - fill given buffer with given color data.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @img: a pointer to g2d_image structure including image and buffer
+ *	information.
+ * @x: x start position to buffer filled with given color data.
+ * @y: y start position to buffer filled with given color data.
+ * @w: width value to buffer filled with given color data.
+ * @h: height value to buffer filled with given color data.
+ */
+int
+g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img,
+			unsigned int x, unsigned int y, unsigned int w,
+			unsigned int h)
+{
+	union g2d_bitblt_cmd_val bitblt;
+	union g2d_point_val pt;
+
+	if (g2d_check_space(ctx, 7, 1))
+		return -ENOSPC;
+
+	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
+	g2d_add_base_addr(ctx, img, g2d_dst);
+	g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
+
+	if (x + w > img->width)
+		w = img->width - x;
+	if (y + h > img->height)
+		h = img->height - y;
+
+	pt.data.x = x;
+	pt.data.y = y;
+	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+
+	pt.data.x = x + w;
+	pt.data.y = y + h;
+	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+	g2d_add_cmd(ctx, SF_COLOR_REG, img->color);
+
+	bitblt.val = 0;
+	bitblt.data.fast_solid_color_fill_en = 1;
+	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
+
+	return g2d_flush(ctx);
+}
+
+/**
+ * g2d_copy - copy contents in source buffer to destination buffer.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @src: a pointer to g2d_image structure including image and buffer
+ *	information to source.
+ * @dst: a pointer to g2d_image structure including image and buffer
+ *	information to destination.
+ * @src_x: x start position to source buffer.
+ * @src_y: y start position to source buffer.
+ * @dst_x: x start position to destination buffer.
+ * @dst_y: y start position to destination buffer.
+ * @w: width value to source and destination buffers.
+ * @h: height value to source and destination buffers.
+ */
+int
+g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
+		struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
+		unsigned int dst_x, unsigned dst_y, unsigned int w,
+		unsigned int h)
+{
+	union g2d_rop4_val rop4;
+	union g2d_point_val pt;
+	unsigned int src_w, src_h, dst_w, dst_h;
+
+	src_w = w;
+	src_h = h;
+	if (src_x + src->width > w)
+		src_w = src->width - src_x;
+	if (src_y + src->height > h)
+		src_h = src->height - src_y;
+
+	dst_w = w;
+	dst_h = w;
+	if (dst_x + dst->width > w)
+		dst_w = dst->width - dst_x;
+	if (dst_y + dst->height > h)
+		dst_h = dst->height - dst_y;
+
+	w = MIN(src_w, dst_w);
+	h = MIN(src_h, dst_h);
+
+	if (w <= 0 || h <= 0) {
+		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
+		return -EINVAL;
+	}
+
+	if (g2d_check_space(ctx, 11, 2))
+		return -ENOSPC;
+
+	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
+	g2d_add_base_addr(ctx, dst, g2d_dst);
+	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
+
+	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
+	g2d_add_base_addr(ctx, src, g2d_src);
+	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+
+	pt.data.x = src_x;
+	pt.data.y = src_y;
+	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
+	pt.data.x = src_x + w;
+	pt.data.y = src_y + h;
+	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
+
+	pt.data.x = dst_x;
+	pt.data.y = dst_y;
+	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+	pt.data.x = dst_x + w;
+	pt.data.y = dst_y + h;
+	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+	rop4.val = 0;
+	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
+	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
+
+	return g2d_flush(ctx);
+}
+
+/**
+ * g2d_move - copy content inside single buffer.
+ *	Similar to libc's memmove() this copies a rectangular
+ *	region of the provided buffer to another location, while
+ *	properly handling the situation where source and
+ *	destination rectangle overlap.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @img: a pointer to g2d_image structure providing
+ *	buffer information.
+ * @src_x: x position of source rectangle.
+ * @src_y: y position of source rectangle.
+ * @dst_x: x position of destination rectangle.
+ * @dst_y: y position of destination rectangle.
+ * @w: width of rectangle to move.
+ * @h: height of rectangle to move.
+ */
+int
+g2d_move(struct g2d_context *ctx, struct g2d_image *img,
+		unsigned int src_x, unsigned int src_y,
+		unsigned int dst_x, unsigned dst_y, unsigned int w,
+		unsigned int h)
+{
+	union g2d_rop4_val rop4;
+	union g2d_point_val pt;
+	union g2d_direction_val dir;
+	unsigned int src_w, src_h, dst_w, dst_h;
+
+	src_w = w;
+	src_h = h;
+	if (src_x + img->width > w)
+		src_w = img->width - src_x;
+	if (src_y + img->height > h)
+		src_h = img->height - src_y;
+
+	dst_w = w;
+	dst_h = w;
+	if (dst_x + img->width > w)
+		dst_w = img->width - dst_x;
+	if (dst_y + img->height > h)
+		dst_h = img->height - dst_y;
+
+	w = MIN(src_w, dst_w);
+	h = MIN(src_h, dst_h);
+
+	if (w == 0 || h == 0) {
+		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
+		return -EINVAL;
+	}
+
+	if (g2d_check_space(ctx, 13, 2))
+		return -ENOSPC;
+
+	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+
+	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, img->color_mode);
+	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, img->color_mode);
+
+	g2d_add_base_addr(ctx, img, g2d_dst);
+	g2d_add_base_addr(ctx, img, g2d_src);
+
+	g2d_add_cmd(ctx, DST_STRIDE_REG, img->stride);
+	g2d_add_cmd(ctx, SRC_STRIDE_REG, img->stride);
+
+	dir.val[0] = dir.val[1] = 0;
+
+	if (dst_x >= src_x)
+		dir.data.src_x_direction = dir.data.dst_x_direction = 1;
+	if (dst_y >= src_y)
+		dir.data.src_y_direction = dir.data.dst_y_direction = 1;
+
+	g2d_set_direction(ctx, &dir);
+
+	pt.data.x = src_x;
+	pt.data.y = src_y;
+	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
+	pt.data.x = src_x + w;
+	pt.data.y = src_y + h;
+	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
+
+	pt.data.x = dst_x;
+	pt.data.y = dst_y;
+	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+	pt.data.x = dst_x + w;
+	pt.data.y = dst_y + h;
+	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+	rop4.val = 0;
+	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
+	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
+
+	return g2d_flush(ctx);
+}
+
+/**
+ * g2d_copy_with_scale - copy contents in source buffer to destination buffer
+ *	scaling up or down properly.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @src: a pointer to g2d_image structure including image and buffer
+ *	information to source.
+ * @dst: a pointer to g2d_image structure including image and buffer
+ *	information to destination.
+ * @src_x: x start position to source buffer.
+ * @src_y: y start position to source buffer.
+ * @src_w: width value to source buffer.
+ * @src_h: height value to source buffer.
+ * @dst_x: x start position to destination buffer.
+ * @dst_y: y start position to destination buffer.
+ * @dst_w: width value to destination buffer.
+ * @dst_h: height value to destination buffer.
+ * @negative: indicate that it uses color negative to source and
+ *	destination buffers.
+ */
+int
+g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
+				struct g2d_image *dst, unsigned int src_x,
+				unsigned int src_y, unsigned int src_w,
+				unsigned int src_h, unsigned int dst_x,
+				unsigned int dst_y, unsigned int dst_w,
+				unsigned int dst_h, unsigned int negative)
+{
+	union g2d_rop4_val rop4;
+	union g2d_point_val pt;
+	unsigned int scale, repeat_pad;
+	unsigned int scale_x, scale_y;
+
+	/* Sanitize this parameter to facilitate space computation below. */
+	if (negative)
+		negative = 1;
+
+	if (src_w == dst_w && src_h == dst_h)
+		scale = 0;
+	else {
+		scale = 1;
+		scale_x = g2d_get_scaling(src_w, dst_w);
+		scale_y = g2d_get_scaling(src_h, dst_h);
+	}
+
+	repeat_pad = src->repeat_mode == G2D_REPEAT_MODE_PAD ? 1 : 0;
+
+	if (src_x + src_w > src->width)
+		src_w = src->width - src_x;
+	if (src_y + src_h > src->height)
+		src_h = src->height - src_y;
+
+	if (dst_x + dst_w > dst->width)
+		dst_w = dst->width - dst_x;
+	if (dst_y + dst_h > dst->height)
+		dst_h = dst->height - dst_y;
+
+	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
+		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
+		return -EINVAL;
+	}
+
+	if (g2d_check_space(ctx, 12 + scale * 3 + negative + repeat_pad, 2))
+		return -ENOSPC;
+
+	g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
+	g2d_add_base_addr(ctx, dst, g2d_dst);
+	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
+
+	g2d_add_cmd(ctx, SRC_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
+
+	g2d_add_cmd(ctx, SRC_REPEAT_MODE_REG, src->repeat_mode);
+	if (repeat_pad)
+		g2d_add_cmd(ctx, SRC_PAD_VALUE_REG, dst->color);
+
+	g2d_add_base_addr(ctx, src, g2d_src);
+	g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+
+	rop4.val = 0;
+	rop4.data.unmasked_rop3 = G2D_ROP3_SRC;
+
+	if (negative) {
+		g2d_add_cmd(ctx, BG_COLOR_REG, 0x00FFFFFF);
+		rop4.data.unmasked_rop3 ^= G2D_ROP3_DST;
+	}
+
+	g2d_add_cmd(ctx, ROP4_REG, rop4.val);
+
+	if (scale) {
+		g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
+		g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
+		g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
+	}
+
+	pt.data.x = src_x;
+	pt.data.y = src_y;
+	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
+	pt.data.x = src_x + src_w;
+	pt.data.y = src_y + src_h;
+	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
+
+	pt.data.x = dst_x;
+	pt.data.y = dst_y;
+	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+	pt.data.x = dst_x + dst_w;
+	pt.data.y = dst_y + dst_h;
+	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+	return g2d_flush(ctx);
+}
+
+/**
+ * g2d_blend - blend image data in source and destination buffers.
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @src: a pointer to g2d_image structure including image and buffer
+ *	information to source.
+ * @dst: a pointer to g2d_image structure including image and buffer
+ *	information to destination.
+ * @src_x: x start position to source buffer.
+ * @src_y: y start position to source buffer.
+ * @dst_x: x start position to destination buffer.
+ * @dst_y: y start position to destination buffer.
+ * @w: width value to source and destination buffer.
+ * @h: height value to source and destination buffer.
+ * @op: blend operation type.
+ */
+int
+g2d_blend(struct g2d_context *ctx, struct g2d_image *src,
+		struct g2d_image *dst, unsigned int src_x,
+		unsigned int src_y, unsigned int dst_x, unsigned int dst_y,
+		unsigned int w, unsigned int h, enum e_g2d_op op)
+{
+	union g2d_point_val pt;
+	union g2d_bitblt_cmd_val bitblt;
+	union g2d_blend_func_val blend;
+	unsigned int gem_space;
+	unsigned int src_w, src_h, dst_w, dst_h;
+
+	src_w = w;
+	src_h = h;
+	if (src_x + w > src->width)
+		src_w = src->width - src_x;
+	if (src_y + h > src->height)
+		src_h = src->height - src_y;
+
+	dst_w = w;
+	dst_h = h;
+	if (dst_x + w > dst->width)
+		dst_w = dst->width - dst_x;
+	if (dst_y + h > dst->height)
+		dst_h = dst->height - dst_y;
+
+	w = MIN(src_w, dst_w);
+	h = MIN(src_h, dst_h);
+
+	if (w <= 0 || h <= 0) {
+		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
+		return -EINVAL;
+	}
+
+	if (!g2d_validate_select_mode(src->select_mode)) {
+		fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
+		return -EINVAL;
+	}
+
+	if (!g2d_validate_blending_op(op)) {
+		fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
+		return -EINVAL;
+	}
+
+	gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
+
+	if (g2d_check_space(ctx, 12, gem_space))
+		return -ENOSPC;
+
+	bitblt.val = 0;
+	blend.val = 0;
+
+	if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
+		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+	else
+		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+
+	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
+	g2d_add_base_addr(ctx, dst, g2d_dst);
+	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
+
+	g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
+	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
+
+	switch (src->select_mode) {
+	case G2D_SELECT_MODE_NORMAL:
+		g2d_add_base_addr(ctx, src, g2d_src);
+		g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+		break;
+	case G2D_SELECT_MODE_FGCOLOR:
+		g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
+		break;
+	case G2D_SELECT_MODE_BGCOLOR:
+		g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
+		break;
+	}
+
+	bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
+	blend.val = g2d_get_blend_op(op);
+	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
+	g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
+
+	pt.data.x = src_x;
+	pt.data.y = src_y;
+	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
+	pt.data.x = src_x + w;
+	pt.data.y = src_y + h;
+	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
+
+	pt.data.x = dst_x;
+	pt.data.y = dst_y;
+	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+	pt.data.x = dst_x + w;
+	pt.data.y = dst_y + h;
+	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+	return g2d_flush(ctx);
+}
+
+/**
+ * g2d_scale_and_blend - apply scaling to source buffer and then blend to destination buffer
+ *
+ * @ctx: a pointer to g2d_context structure.
+ * @src: a pointer to g2d_image structure including image and buffer
+ *	information to source.
+ * @dst: a pointer to g2d_image structure including image and buffer
+ *	information to destination.
+ * @src_x: x start position to source buffer.
+ * @src_y: y start position to source buffer.
+ * @src_w: width value to source buffer.
+ * @src_h: height value to source buffer.
+ * @dst_x: x start position to destination buffer.
+ * @dst_y: y start position to destination buffer.
+ * @dst_w: width value to destination buffer.
+ * @dst_h: height value to destination buffer.
+ * @op: blend operation type.
+ */
+int
+g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
+		struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
+		unsigned int src_w, unsigned int src_h, unsigned int dst_x,
+		unsigned int dst_y, unsigned int dst_w, unsigned int dst_h,
+		enum e_g2d_op op)
+{
+	union g2d_point_val pt;
+	union g2d_bitblt_cmd_val bitblt;
+	union g2d_blend_func_val blend;
+	unsigned int scale, gem_space;
+	unsigned int scale_x, scale_y;
+
+	if (src_w == dst_w && src_h == dst_h)
+		scale = 0;
+	else {
+		scale = 1;
+		scale_x = g2d_get_scaling(src_w, dst_w);
+		scale_y = g2d_get_scaling(src_h, dst_h);
+	}
+
+	if (src_x + src_w > src->width)
+		src_w = src->width - src_x;
+	if (src_y + src_h > src->height)
+		src_h = src->height - src_y;
+
+	if (dst_x + dst_w > dst->width)
+		dst_w = dst->width - dst_x;
+	if (dst_y + dst_h > dst->height)
+		dst_h = dst->height - dst_y;
+
+	if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0) {
+		fprintf(stderr, MSG_PREFIX "invalid width or height.\n");
+		return -EINVAL;
+	}
+
+	if (!g2d_validate_select_mode(src->select_mode)) {
+		fprintf(stderr , MSG_PREFIX "invalid select mode for source.\n");
+		return -EINVAL;
+	}
+
+	if (!g2d_validate_blending_op(op)) {
+		fprintf(stderr , MSG_PREFIX "unsupported blending operation.\n");
+		return -EINVAL;
+	}
+
+	gem_space = src->select_mode == G2D_SELECT_MODE_NORMAL ? 2 : 1;
+
+	if (g2d_check_space(ctx, 12 + scale * 3, gem_space))
+		return -ENOSPC;
+
+	bitblt.val = 0;
+	blend.val = 0;
+
+	if (op == G2D_OP_SRC || op == G2D_OP_CLEAR)
+		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_BGCOLOR);
+	else
+		g2d_add_cmd(ctx, DST_SELECT_REG, G2D_SELECT_MODE_NORMAL);
+
+	g2d_add_cmd(ctx, DST_COLOR_MODE_REG, dst->color_mode);
+	g2d_add_base_addr(ctx, dst, g2d_dst);
+	g2d_add_cmd(ctx, DST_STRIDE_REG, dst->stride);
+
+	g2d_add_cmd(ctx, SRC_SELECT_REG, src->select_mode);
+	g2d_add_cmd(ctx, SRC_COLOR_MODE_REG, src->color_mode);
+
+	switch (src->select_mode) {
+	case G2D_SELECT_MODE_NORMAL:
+		g2d_add_base_addr(ctx, src, g2d_src);
+		g2d_add_cmd(ctx, SRC_STRIDE_REG, src->stride);
+		break;
+	case G2D_SELECT_MODE_FGCOLOR:
+		g2d_add_cmd(ctx, FG_COLOR_REG, src->color);
+		break;
+	case G2D_SELECT_MODE_BGCOLOR:
+		g2d_add_cmd(ctx, BG_COLOR_REG, src->color);
+		break;
+	}
+
+	if (scale) {
+		g2d_add_cmd(ctx, SRC_SCALE_CTRL_REG, G2D_SCALE_MODE_BILINEAR);
+		g2d_add_cmd(ctx, SRC_XSCALE_REG, scale_x);
+		g2d_add_cmd(ctx, SRC_YSCALE_REG, scale_y);
+	}
+
+	bitblt.data.alpha_blend_mode = G2D_ALPHA_BLEND_MODE_ENABLE;
+	blend.val = g2d_get_blend_op(op);
+	g2d_add_cmd(ctx, BITBLT_COMMAND_REG, bitblt.val);
+	g2d_add_cmd(ctx, BLEND_FUNCTION_REG, blend.val);
+
+	pt.data.x = src_x;
+	pt.data.y = src_y;
+	g2d_add_cmd(ctx, SRC_LEFT_TOP_REG, pt.val);
+	pt.data.x = src_x + src_w;
+	pt.data.y = src_y + src_h;
+	g2d_add_cmd(ctx, SRC_RIGHT_BOTTOM_REG, pt.val);
+
+	pt.data.x = dst_x;
+	pt.data.y = dst_y;
+	g2d_add_cmd(ctx, DST_LEFT_TOP_REG, pt.val);
+	pt.data.x = dst_x + dst_w;
+	pt.data.y = dst_y + dst_h;
+	g2d_add_cmd(ctx, DST_RIGHT_BOTTOM_REG, pt.val);
+
+	return g2d_flush(ctx);
+}
diff --git a/exynos/exynos_fimg2d.h b/exynos/exynos_fimg2d.h
new file mode 100644
index 0000000..a825c68
--- /dev/null
+++ b/exynos/exynos_fimg2d.h
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co.Ltd
+ * Authors:
+ *	Inki Dae <inki.dae@samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef _FIMG2D_H_
+#define _FIMG2D_H_
+
+#define G2D_PLANE_MAX_NR	2
+
+enum e_g2d_color_mode {
+	/* COLOR FORMAT */
+	G2D_COLOR_FMT_XRGB8888,
+	G2D_COLOR_FMT_ARGB8888,
+	G2D_COLOR_FMT_RGB565,
+	G2D_COLOR_FMT_XRGB1555,
+	G2D_COLOR_FMT_ARGB1555,
+	G2D_COLOR_FMT_XRGB4444,
+	G2D_COLOR_FMT_ARGB4444,
+	G2D_COLOR_FMT_PRGB888,
+	G2D_COLOR_FMT_YCbCr444,
+	G2D_COLOR_FMT_YCbCr422,
+	G2D_COLOR_FMT_YCbCr420,
+	/* alpha 8bit */
+	G2D_COLOR_FMT_A8,
+	/* Luminance 8bit: gray color */
+	G2D_COLOR_FMT_L8,
+	/* alpha 1bit */
+	G2D_COLOR_FMT_A1,
+	/* alpha 4bit */
+	G2D_COLOR_FMT_A4,
+	G2D_COLOR_FMT_MASK,				/* VER4.1 */
+
+	/* COLOR ORDER */
+	G2D_ORDER_AXRGB		= (0 << 4),		/* VER4.1 */
+	G2D_ORDER_RGBAX		= (1 << 4),		/* VER4.1 */
+	G2D_ORDER_AXBGR		= (2 << 4),		/* VER4.1 */
+	G2D_ORDER_BGRAX		= (3 << 4),		/* VER4.1 */
+	G2D_ORDER_MASK		= (3 << 4),		/* VER4.1 */
+
+	/* Number of YCbCr plane */
+	G2D_YCbCr_1PLANE	= (0 << 8),		/* VER4.1 */
+	G2D_YCbCr_2PLANE	= (1 << 8),		/* VER4.1 */
+	G2D_YCbCr_PLANE_MASK	= (3 << 8),		/* VER4.1 */
+
+	/* Order in YCbCr */
+	G2D_YCbCr_ORDER_CrY1CbY0 = (0 << 12),			/* VER4.1 */
+	G2D_YCbCr_ORDER_CbY1CrY0 = (1 << 12),			/* VER4.1 */
+	G2D_YCbCr_ORDER_Y1CrY0Cb = (2 << 12),			/* VER4.1 */
+	G2D_YCbCr_ORDER_Y1CbY0Cr = (3 << 12),			/* VER4.1 */
+	G2D_YCbCr_ORDER_CrCb = G2D_YCbCr_ORDER_CrY1CbY0,	/* VER4.1 */
+	G2D_YCbCr_ORDER_CbCr = G2D_YCbCr_ORDER_CbY1CrY0,	/* VER4.1 */
+	G2D_YCbCr_ORDER_MASK = (3 < 12),			/* VER4.1 */
+
+	/* CSC */
+	G2D_CSC_601 = (0 << 16),		/* VER4.1 */
+	G2D_CSC_709 = (1 << 16),		/* VER4.1 */
+	G2D_CSC_MASK = (1 << 16),		/* VER4.1 */
+
+	/* Valid value range of YCbCr */
+	G2D_YCbCr_RANGE_NARROW = (0 << 17),	/* VER4.1 */
+	G2D_YCbCr_RANGE_WIDE = (1 << 17),	/* VER4.1 */
+	G2D_YCbCr_RANGE_MASK = (1 << 17),	/* VER4.1 */
+
+	G2D_COLOR_MODE_MASK = 0xFFFFFFFF,
+};
+
+enum e_g2d_select_mode {
+	G2D_SELECT_MODE_NORMAL	= (0 << 0),
+	G2D_SELECT_MODE_FGCOLOR = (1 << 0),
+	G2D_SELECT_MODE_BGCOLOR = (2 << 0),
+};
+
+enum e_g2d_repeat_mode {
+	G2D_REPEAT_MODE_REPEAT,
+	G2D_REPEAT_MODE_PAD,
+	G2D_REPEAT_MODE_REFLECT,
+	G2D_REPEAT_MODE_CLAMP,
+	G2D_REPEAT_MODE_NONE,
+};
+
+enum e_g2d_scale_mode {
+	G2D_SCALE_MODE_NONE = 0,
+	G2D_SCALE_MODE_NEAREST,
+	G2D_SCALE_MODE_BILINEAR,
+	G2D_SCALE_MODE_MAX,
+};
+
+enum e_g2d_buf_type {
+	G2D_IMGBUF_COLOR,
+	G2D_IMGBUF_GEM,
+	G2D_IMGBUF_USERPTR,
+};
+
+enum e_g2d_rop3_type {
+	G2D_ROP3_DST = 0xAA,
+	G2D_ROP3_SRC = 0xCC,
+	G2D_ROP3_3RD = 0xF0,
+	G2D_ROP3_MASK = 0xFF,
+};
+
+enum e_g2d_select_alpha_src {
+	G2D_SELECT_SRC_FOR_ALPHA_BLEND,	/* VER4.1 */
+	G2D_SELECT_ROP_FOR_ALPHA_BLEND,	/* VER4.1 */
+};
+
+enum e_g2d_transparent_mode {
+	G2D_TRANSPARENT_MODE_OPAQUE,
+	G2D_TRANSPARENT_MODE_TRANSPARENT,
+	G2D_TRANSPARENT_MODE_BLUESCREEN,
+	G2D_TRANSPARENT_MODE_MAX,
+};
+
+enum e_g2d_color_key_mode {
+	G2D_COLORKEY_MODE_DISABLE	= 0,
+	G2D_COLORKEY_MODE_SRC_RGBA	= (1<<0),
+	G2D_COLORKEY_MODE_DST_RGBA	= (1<<1),
+	G2D_COLORKEY_MODE_SRC_YCbCr	= (1<<2),		/* VER4.1 */
+	G2D_COLORKEY_MODE_DST_YCbCr	= (1<<3),		/* VER4.1 */
+	G2D_COLORKEY_MODE_MASK		= 15,
+};
+
+enum e_g2d_alpha_blend_mode {
+	G2D_ALPHA_BLEND_MODE_DISABLE,
+	G2D_ALPHA_BLEND_MODE_ENABLE,
+	G2D_ALPHA_BLEND_MODE_FADING,				/* VER3.0 */
+	G2D_ALPHA_BLEND_MODE_MAX,
+};
+
+enum e_g2d_op {
+	G2D_OP_CLEAR			= 0x00,
+	G2D_OP_SRC			= 0x01,
+	G2D_OP_DST			= 0x02,
+	G2D_OP_OVER			= 0x03,
+	G2D_OP_INTERPOLATE		= 0x04,
+	G2D_OP_DISJOINT_CLEAR		= 0x10,
+	G2D_OP_DISJOINT_SRC		= 0x11,
+	G2D_OP_DISJOINT_DST		= 0x12,
+	G2D_OP_CONJOINT_CLEAR		= 0x20,
+	G2D_OP_CONJOINT_SRC		= 0x21,
+	G2D_OP_CONJOINT_DST		= 0x22,
+};
+
+/*
+ * The G2D_COEFF_MODE_DST_{COLOR,ALPHA} modes both use the ALPHA_REG(0x618)
+ * register. The registers fields are as follows:
+ * bits 31:8 = color value (RGB order)
+ * bits 7:0 = alpha value
+ */
+enum e_g2d_coeff_mode {
+	G2D_COEFF_MODE_ONE,
+	G2D_COEFF_MODE_ZERO,
+	G2D_COEFF_MODE_SRC_ALPHA,
+	G2D_COEFF_MODE_SRC_COLOR,
+	G2D_COEFF_MODE_DST_ALPHA,
+	G2D_COEFF_MODE_DST_COLOR,
+	/* Global Alpha : Set by ALPHA_REG(0x618) */
+	G2D_COEFF_MODE_GB_ALPHA,
+	/* Global Color and Alpha : Set by ALPHA_REG(0x618) */
+	G2D_COEFF_MODE_GB_COLOR,
+	/* (1-SRC alpha)/DST Alpha */
+	G2D_COEFF_MODE_DISJOINT_S,
+	/* (1-DST alpha)/SRC Alpha */
+	G2D_COEFF_MODE_DISJOINT_D,
+	/* SRC alpha/DST alpha */
+	G2D_COEFF_MODE_CONJOINT_S,
+	/* DST alpha/SRC alpha */
+	G2D_COEFF_MODE_CONJOINT_D,
+	/* DST alpha/SRC alpha */
+	G2D_COEFF_MODE_MASK
+};
+
+enum e_g2d_acoeff_mode {
+	G2D_ACOEFF_MODE_A,          /* alpha */
+	G2D_ACOEFF_MODE_APGA,	/* alpha + global alpha */
+	G2D_ACOEFF_MODE_AMGA,	/* alpha * global alpha */
+	G2D_ACOEFF_MODE_MASK
+};
+
+union g2d_point_val {
+	unsigned int val;
+	struct {
+		/*
+		 * Coordinate of Source Image
+		 * Range: 0 ~ 8000 (Requirement: SrcLeftX < SrcRightX)
+		 * In YCbCr 422 and YCbCr 420 format with even number.
+		 */
+		unsigned int x:16;
+		/*
+		 * Y Coordinate of Source Image
+		 * Range: 0 ~ 8000 (Requirement: SrcTopY < SrcBottomY)
+		 * In YCbCr 420 format with even number.
+		 */
+		unsigned int y:16;
+	} data;
+};
+
+union g2d_rop4_val {
+	unsigned int val;
+	struct {
+		enum e_g2d_rop3_type	unmasked_rop3:8;
+		enum e_g2d_rop3_type	masked_rop3:8;
+		unsigned int		reserved:16;
+	} data;
+};
+
+union g2d_bitblt_cmd_val {
+	unsigned int val;
+	struct {
+		/* [0:3] */
+		unsigned int			mask_rop4_en:1;
+		unsigned int			masking_en:1;
+		enum e_g2d_select_alpha_src	rop4_alpha_en:1;
+		unsigned int			dither_en:1;
+		/* [4:7] */
+		unsigned int			resolved1:4;
+		/* [8:11] */
+		unsigned int			cw_en:4;
+		/* [12:15] */
+		enum e_g2d_transparent_mode	transparent_mode:4;
+		/* [16:19] */
+		enum e_g2d_color_key_mode	color_key_mode:4;
+		/* [20:23] */
+		enum e_g2d_alpha_blend_mode	alpha_blend_mode:4;
+		/* [24:27] */
+		unsigned int src_pre_multiply:1;
+		unsigned int pat_pre_multiply:1;
+		unsigned int dst_pre_multiply:1;
+		unsigned int dst_depre_multiply:1;
+		/* [28:31] */
+		unsigned int fast_solid_color_fill_en:1;
+		unsigned int reserved:3;
+	} data;
+};
+
+union g2d_blend_func_val {
+	unsigned int val;
+	struct {
+		/* [0:15] */
+		enum e_g2d_coeff_mode src_coeff:4;
+		enum e_g2d_acoeff_mode src_coeff_src_a:2;
+		enum e_g2d_acoeff_mode src_coeff_dst_a:2;
+		enum e_g2d_coeff_mode dst_coeff:4;
+		enum e_g2d_acoeff_mode dst_coeff_src_a:2;
+		enum e_g2d_acoeff_mode dst_coeff_dst_a:2;
+		/* [16:19] */
+		unsigned int inv_src_color_coeff:1;
+		unsigned int resoled1:1;
+		unsigned int inv_dst_color_coeff:1;
+		unsigned int resoled2:1;
+		/* [20:23] */
+		unsigned int lighten_en:1;
+		unsigned int darken_en:1;
+		unsigned int win_ce_src_over_en:2;
+		/* [24:31] */
+		unsigned int reserved:8;
+	} data;
+};
+
+struct g2d_image {
+	enum e_g2d_select_mode		select_mode;
+	enum e_g2d_color_mode		color_mode;
+	enum e_g2d_repeat_mode		repeat_mode;
+	enum e_g2d_scale_mode		scale_mode;
+	unsigned int			xscale;
+	unsigned int			yscale;
+	unsigned char			rotate_90;
+	unsigned char			x_dir;
+	unsigned char			y_dir;
+	unsigned char			component_alpha;
+	unsigned int			width;
+	unsigned int			height;
+	unsigned int			stride;
+	unsigned int			need_free;
+	unsigned int			color;
+	enum e_g2d_buf_type		buf_type;
+	unsigned int			bo[G2D_PLANE_MAX_NR];
+	struct drm_exynos_g2d_userptr	user_ptr[G2D_PLANE_MAX_NR];
+	void				*mapped_ptr[G2D_PLANE_MAX_NR];
+};
+
+struct g2d_context;
+
+struct g2d_context *g2d_init(int fd);
+void g2d_fini(struct g2d_context *ctx);
+void g2d_config_event(struct g2d_context *ctx, void *userdata);
+int g2d_exec(struct g2d_context *ctx);
+int g2d_solid_fill(struct g2d_context *ctx, struct g2d_image *img,
+			unsigned int x, unsigned int y, unsigned int w,
+			unsigned int h);
+int g2d_copy(struct g2d_context *ctx, struct g2d_image *src,
+		struct g2d_image *dst, unsigned int src_x,
+		unsigned int src_y, unsigned int dst_x, unsigned int dst_y,
+		unsigned int w, unsigned int h);
+int g2d_move(struct g2d_context *ctx, struct g2d_image *img,
+		unsigned int src_x, unsigned int src_y, unsigned int dst_x,
+		unsigned dst_y, unsigned int w, unsigned int h);
+int g2d_copy_with_scale(struct g2d_context *ctx, struct g2d_image *src,
+				struct g2d_image *dst, unsigned int src_x,
+				unsigned int src_y, unsigned int src_w,
+				unsigned int src_h, unsigned int dst_x,
+				unsigned int dst_y, unsigned int dst_w,
+				unsigned int dst_h, unsigned int negative);
+int g2d_blend(struct g2d_context *ctx, struct g2d_image *src,
+		struct g2d_image *dst, unsigned int src_x,
+		unsigned int src_y, unsigned int dst_x, unsigned int dst_y,
+		unsigned int w, unsigned int h, enum e_g2d_op op);
+int g2d_scale_and_blend(struct g2d_context *ctx, struct g2d_image *src,
+		struct g2d_image *dst, unsigned int src_x, unsigned int src_y,
+		unsigned int src_w, unsigned int src_h, unsigned int dst_x,
+		unsigned int dst_y, unsigned int dst_w, unsigned int dst_h,
+		enum e_g2d_op op);
+#endif /* _FIMG2D_H_ */
diff --git a/exynos/fimg2d_reg.h b/exynos/fimg2d_reg.h
new file mode 100644
index 0000000..07dd634
--- /dev/null
+++ b/exynos/fimg2d_reg.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co.Ltd
+ * Authors:
+ *	Inki Dae <inki.dae@samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef _FIMG2D_REG_H_
+#define _FIMG2D_REG_H_
+
+#define SOFT_RESET_REG			(0x0000)
+#define INTEN_REG			(0x0004)
+#define INTC_PEND_REG			(0x000C)
+#define FIFO_STAT_REG			(0x0010)
+#define AXI_MODE_REG			(0x001C)
+#define DMA_SFR_BASE_ADDR_REG		(0x0080)
+#define DMA_COMMAND_REG			(0x0084)
+#define DMA_EXE_LIST_NUM_REG		(0x0088)
+#define DMA_STATUS_REG			(0x008C)
+#define DMA_HOLD_CMD_REG		(0x0090)
+
+/* COMMAND REGISTER */
+#define BITBLT_START_REG		(0x0100)
+#define BITBLT_COMMAND_REG		(0x0104)
+#define BLEND_FUNCTION_REG		(0x0108)	/* VER4.1 */
+#define ROUND_MODE_REG			(0x010C)	/* VER4.1 */
+
+/* PARAMETER SETTING REGISTER */
+#define ROTATE_REG			(0x0200)
+#define SRC_MASK_DIRECT_REG		(0x0204)
+#define DST_PAT_DIRECT_REG		(0x0208)
+
+/* SOURCE */
+#define SRC_SELECT_REG			(0x0300)
+#define SRC_BASE_ADDR_REG		(0x0304)
+#define SRC_STRIDE_REG			(0x0308)
+#define SRC_COLOR_MODE_REG		(0x030c)
+#define SRC_LEFT_TOP_REG		(0x0310)
+#define SRC_RIGHT_BOTTOM_REG		(0x0314)
+#define SRC_PLANE2_BASE_ADDR_REG	(0x0318)	/* VER4.1 */
+#define SRC_REPEAT_MODE_REG		(0x031C)
+#define SRC_PAD_VALUE_REG		(0x0320)
+#define SRC_A8_RGB_EXT_REG		(0x0324)
+#define SRC_SCALE_CTRL_REG		(0x0328)
+#define SRC_XSCALE_REG			(0x032C)
+#define SRC_YSCALE_REG			(0x0330)
+
+/* DESTINATION */
+#define DST_SELECT_REG			(0x0400)
+#define DST_BASE_ADDR_REG		(0x0404)
+#define DST_STRIDE_REG			(0x0408)
+#define DST_COLOR_MODE_REG		(0x040C)
+#define DST_LEFT_TOP_REG		(0x0410)
+#define DST_RIGHT_BOTTOM_REG		(0x0414)
+#define DST_PLANE2_BASE_ADDR_REG	(0x0418)	/* VER4.1 */
+#define DST_A8_RGB_EXT_REG		(0x041C)
+
+/* PATTERN */
+#define PAT_BASE_ADDR_REG		(0x0500)
+#define PAT_SIZE_REG			(0x0504)
+#define PAT_COLOR_MODE_REG		(0x0508)
+#define PAT_OFFSET_REG			(0x050C)
+#define PAT_STRIDE_REG			(0x0510)
+
+/* MASK	*/
+#define MASK_BASE_ADDR_REG		(0x0520)
+#define MASK_STRIDE_REG			(0x0524)
+#define MASK_LEFT_TOP_REG		(0x0528)	/* VER4.1 */
+#define MASK_RIGHT_BOTTOM_REG		(0x052C)	/* VER4.1 */
+#define MASK_MODE_REG			(0x0530)	/* VER4.1 */
+#define MASK_REPEAT_MODE_REG		(0x0534)
+#define MASK_PAD_VALUE_REG		(0x0538)
+#define MASK_SCALE_CTRL_REG		(0x053C)
+#define MASK_XSCALE_REG			(0x0540)
+#define MASK_YSCALE_REG			(0x0544)
+
+/* CLIPPING WINDOW */
+#define CW_LT_REG			(0x0600)
+#define CW_RB_REG			(0x0604)
+
+/* ROP & ALPHA SETTING */
+#define THIRD_OPERAND_REG		(0x0610)
+#define ROP4_REG			(0x0614)
+#define ALPHA_REG			(0x0618)
+
+/* COLOR SETTING */
+#define FG_COLOR_REG			(0x0700)
+#define BG_COLOR_REG			(0x0704)
+#define BS_COLOR_REG			(0x0708)
+#define SF_COLOR_REG			(0x070C)	/* VER4.1 */
+
+/* COLOR KEY */
+#define SRC_COLORKEY_CTRL_REG		(0x0710)
+#define SRC_COLORKEY_DR_MIN_REG		(0x0714)
+#define SRC_COLORKEY_DR_MAX_REG		(0x0718)
+#define DST_COLORKEY_CTRL_REG		(0x071C)
+#define DST_COLORKEY_DR_MIN_REG		(0x0720)
+#define DST_COLORKEY_DR_MAX_REG		(0x0724)
+/* YCbCr src Color Key */
+#define YCbCr_SRC_COLORKEY_CTRL_REG	(0x0728)	/* VER4.1 */
+#define YCbCr_SRC_COLORKEY_DR_MIN_REG	(0x072C)	/* VER4.1 */
+#define YCbCr_SRC_COLORKEY_DR_MAX_REG	(0x0730)	/* VER4.1 */
+/*Y CbCr dst Color Key */
+#define YCbCr_DST_COLORKEY_CTRL_REG	(0x0734)	/* VER4.1 */
+#define YCbCr_DST_COLORKEY_DR_MIN_REG	(0x0738)	/* VER4.1 */
+#define YCbCr_DST_COLORKEY_DR_MAX_REG	(0x073C)	/* VER4.1 */
+
+#endif
+
diff --git a/exynos/libdrm_exynos.pc.in b/exynos/libdrm_exynos.pc.in
new file mode 100644
index 0000000..ff1c432
--- /dev/null
+++ b/exynos/libdrm_exynos.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_exynos
+Description: Userspace interface to exynos kernel DRM services
+Version: 0.7
+Libs: -L${libdir} -ldrm_exynos
+Cflags: -I${includedir} -I${includedir}/libdrm -I${includedir}/exynos
+Requires.private: libdrm
diff --git a/freedreno/Android.mk b/freedreno/Android.mk
new file mode 100644
index 0000000..2b582ae
--- /dev/null
+++ b/freedreno/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Import variables LIBDRM_FREEDRENO_FILES, LIBDRM_FREEDRENO_H_FILES
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_MODULE := libdrm_freedreno
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+LOCAL_SRC_FILES := $(LIBDRM_FREEDRENO_FILES)
+
+include $(LIBDRM_COMMON_MK)
+include $(BUILD_SHARED_LIBRARY)
diff --git a/freedreno/Makefile.am b/freedreno/Makefile.am
new file mode 100644
index 0000000..cbb0d03
--- /dev/null
+++ b/freedreno/Makefile.am
@@ -0,0 +1,31 @@
+AUTOMAKE_OPTIONS=subdir-objects
+include Makefile.sources
+
+AM_CFLAGS = \
+	$(WARN_CFLAGS) \
+	-I$(top_srcdir) \
+	$(PTHREADSTUBS_CFLAGS) \
+	$(VALGRIND_CFLAGS) \
+	-I$(top_srcdir)/include/drm
+
+libdrm_freedreno_la_LTLIBRARIES = libdrm_freedreno.la
+libdrm_freedreno_ladir = $(libdir)
+libdrm_freedreno_la_LDFLAGS = -version-number 1:0:0 -no-undefined
+libdrm_freedreno_la_LIBADD = \
+	../libdrm.la \
+	@PTHREADSTUBS_LIBS@ \
+	@CLOCK_LIB@
+
+libdrm_freedreno_la_SOURCES = $(LIBDRM_FREEDRENO_FILES)
+if HAVE_FREEDRENO_KGSL
+libdrm_freedreno_la_SOURCES += $(LIBDRM_FREEDRENO_KGSL_FILES)
+endif
+
+libdrm_freedrenocommonincludedir = ${includedir}/freedreno
+libdrm_freedrenocommoninclude_HEADERS = $(LIBDRM_FREEDRENO_H_FILES)
+
+pkgconfigdir = @pkgconfigdir@
+pkgconfig_DATA = libdrm_freedreno.pc
+
+TESTS = freedreno-symbol-check
+EXTRA_DIST = $(TESTS)
diff --git a/freedreno/Makefile.sources b/freedreno/Makefile.sources
new file mode 100644
index 0000000..68a679b
--- /dev/null
+++ b/freedreno/Makefile.sources
@@ -0,0 +1,26 @@
+LIBDRM_FREEDRENO_FILES := \
+	freedreno_device.c \
+	freedreno_pipe.c \
+	freedreno_priv.h \
+	freedreno_ringbuffer.c \
+	freedreno_bo.c \
+	freedreno_bo_cache.c \
+	msm/msm_bo.c \
+	msm/msm_device.c \
+	msm/msm_drm.h \
+	msm/msm_pipe.c \
+	msm/msm_priv.h \
+	msm/msm_ringbuffer.c
+
+LIBDRM_FREEDRENO_KGSL_FILES := \
+	kgsl/kgsl_bo.c \
+	kgsl/kgsl_device.c \
+	kgsl/kgsl_drm.h \
+	kgsl/kgsl_pipe.c \
+	kgsl/kgsl_priv.h \
+	kgsl/kgsl_ringbuffer.c \
+	kgsl/msm_kgsl.h
+
+LIBDRM_FREEDRENO_H_FILES := \
+	freedreno_drmif.h \
+	freedreno_ringbuffer.h
diff --git a/freedreno/freedreno-symbol-check b/freedreno/freedreno-symbol-check
new file mode 100755
index 0000000..42f2c43
--- /dev/null
+++ b/freedreno/freedreno-symbol-check
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# The following symbols (past the first five) are taken from the public headers.
+# A list of the latter should be available Makefile.sources/LIBDRM_FREEDRENO_H_FILES
+
+FUNCS=$(nm -D --format=bsd --defined-only ${1-.libs/libdrm_freedreno.so} | awk '{print $3}'| while read func; do
+( grep -q "^$func$" || echo $func )  <<EOF
+__bss_start
+_edata
+_end
+_fini
+_init
+fd_bo_cpu_fini
+fd_bo_cpu_prep
+fd_bo_del
+fd_bo_dmabuf
+fd_bo_from_dmabuf
+fd_bo_from_fbdev
+fd_bo_from_handle
+fd_bo_from_name
+fd_bo_get_name
+fd_bo_handle
+fd_bo_map
+fd_bo_new
+fd_bo_ref
+fd_bo_size
+fd_device_del
+fd_device_fd
+fd_device_new
+fd_device_new_dup
+fd_device_ref
+fd_device_version
+fd_pipe_del
+fd_pipe_get_param
+fd_pipe_new
+fd_pipe_wait
+fd_pipe_wait_timeout
+fd_ringbuffer_cmd_count
+fd_ringbuffer_del
+fd_ringbuffer_emit_reloc_ring
+fd_ringbuffer_emit_reloc_ring_full
+fd_ringbuffer_flush
+fd_ringbuffer_grow
+fd_ringbuffer_new
+fd_ringbuffer_reloc
+fd_ringbuffer_reloc2
+fd_ringbuffer_reset
+fd_ringbuffer_set_parent
+fd_ringbuffer_timestamp
+fd_ringmarker_del
+fd_ringmarker_dwords
+fd_ringmarker_flush
+fd_ringbuffer_flush2
+fd_ringmarker_mark
+fd_ringmarker_new
+EOF
+done)
+
+test ! -n "$FUNCS" || echo $FUNCS
+test ! -n "$FUNCS"
diff --git a/freedreno/freedreno_bo.c b/freedreno/freedreno_bo.c
new file mode 100644
index 0000000..7f8ea59
--- /dev/null
+++ b/freedreno/freedreno_bo.c
@@ -0,0 +1,334 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "freedreno_drmif.h"
+#include "freedreno_priv.h"
+
+drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
+drm_private void bo_del(struct fd_bo *bo);
+
+/* set buffer name, and add to table, call w/ table_lock held: */
+static void set_name(struct fd_bo *bo, uint32_t name)
+{
+	bo->name = name;
+	/* add ourself into the handle table: */
+	drmHashInsert(bo->dev->name_table, name, bo);
+}
+
+/* lookup a buffer, call w/ table_lock held: */
+static struct fd_bo * lookup_bo(void *tbl, uint32_t key)
+{
+	struct fd_bo *bo = NULL;
+	if (!drmHashLookup(tbl, key, (void **)&bo)) {
+		/* found, incr refcnt and return: */
+		bo = fd_bo_ref(bo);
+
+		/* don't break the bucket if this bo was found in one */
+		list_delinit(&bo->list);
+	}
+	return bo;
+}
+
+/* allocate a new buffer object, call w/ table_lock held */
+static struct fd_bo * bo_from_handle(struct fd_device *dev,
+		uint32_t size, uint32_t handle)
+{
+	struct fd_bo *bo;
+
+	bo = dev->funcs->bo_from_handle(dev, size, handle);
+	if (!bo) {
+		struct drm_gem_close req = {
+				.handle = handle,
+		};
+		drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+		return NULL;
+	}
+	bo->dev = fd_device_ref(dev);
+	bo->size = size;
+	bo->handle = handle;
+	atomic_set(&bo->refcnt, 1);
+	list_inithead(&bo->list);
+	/* add ourself into the handle table: */
+	drmHashInsert(dev->handle_table, handle, bo);
+	return bo;
+}
+
+struct fd_bo *
+fd_bo_new(struct fd_device *dev, uint32_t size, uint32_t flags)
+{
+	struct fd_bo *bo = NULL;
+	uint32_t handle;
+	int ret;
+
+	bo = fd_bo_cache_alloc(&dev->bo_cache, &size, flags);
+	if (bo)
+		return bo;
+
+	ret = dev->funcs->bo_new_handle(dev, size, flags, &handle);
+	if (ret)
+		return NULL;
+
+	pthread_mutex_lock(&table_lock);
+	bo = bo_from_handle(dev, size, handle);
+	bo->bo_reuse = TRUE;
+	pthread_mutex_unlock(&table_lock);
+
+	VG_BO_ALLOC(bo);
+
+	return bo;
+}
+
+struct fd_bo *
+fd_bo_from_handle(struct fd_device *dev, uint32_t handle, uint32_t size)
+{
+	struct fd_bo *bo = NULL;
+
+	pthread_mutex_lock(&table_lock);
+
+	bo = lookup_bo(dev->handle_table, handle);
+	if (bo)
+		goto out_unlock;
+
+	bo = bo_from_handle(dev, size, handle);
+
+	VG_BO_ALLOC(bo);
+
+out_unlock:
+	pthread_mutex_unlock(&table_lock);
+
+	return bo;
+}
+
+struct fd_bo *
+fd_bo_from_dmabuf(struct fd_device *dev, int fd)
+{
+	int ret, size;
+	uint32_t handle;
+	struct fd_bo *bo;
+
+	pthread_mutex_lock(&table_lock);
+	ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
+	if (ret) {
+		pthread_mutex_unlock(&table_lock);
+		return NULL;
+	}
+
+	bo = lookup_bo(dev->handle_table, handle);
+	if (bo)
+		goto out_unlock;
+
+	/* lseek() to get bo size */
+	size = lseek(fd, 0, SEEK_END);
+	lseek(fd, 0, SEEK_CUR);
+
+	bo = bo_from_handle(dev, size, handle);
+
+	VG_BO_ALLOC(bo);
+
+out_unlock:
+	pthread_mutex_unlock(&table_lock);
+
+	return bo;
+}
+
+struct fd_bo * fd_bo_from_name(struct fd_device *dev, uint32_t name)
+{
+	struct drm_gem_open req = {
+			.name = name,
+	};
+	struct fd_bo *bo;
+
+	pthread_mutex_lock(&table_lock);
+
+	/* check name table first, to see if bo is already open: */
+	bo = lookup_bo(dev->name_table, name);
+	if (bo)
+		goto out_unlock;
+
+	if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
+		ERROR_MSG("gem-open failed: %s", strerror(errno));
+		goto out_unlock;
+	}
+
+	bo = lookup_bo(dev->handle_table, req.handle);
+	if (bo)
+		goto out_unlock;
+
+	bo = bo_from_handle(dev, req.size, req.handle);
+	if (bo) {
+		set_name(bo, name);
+		VG_BO_ALLOC(bo);
+	}
+
+out_unlock:
+	pthread_mutex_unlock(&table_lock);
+
+	return bo;
+}
+
+struct fd_bo * fd_bo_ref(struct fd_bo *bo)
+{
+	atomic_inc(&bo->refcnt);
+	return bo;
+}
+
+void fd_bo_del(struct fd_bo *bo)
+{
+	struct fd_device *dev = bo->dev;
+
+	if (!atomic_dec_and_test(&bo->refcnt))
+		return;
+
+	pthread_mutex_lock(&table_lock);
+
+	if (bo->bo_reuse && (fd_bo_cache_free(&dev->bo_cache, bo) == 0))
+		goto out;
+
+	bo_del(bo);
+	fd_device_del_locked(dev);
+out:
+	pthread_mutex_unlock(&table_lock);
+}
+
+/* Called under table_lock */
+drm_private void bo_del(struct fd_bo *bo)
+{
+	VG_BO_FREE(bo);
+
+	if (bo->map)
+		drm_munmap(bo->map, bo->size);
+
+	/* TODO probably bo's in bucket list get removed from
+	 * handle table??
+	 */
+
+	if (bo->handle) {
+		struct drm_gem_close req = {
+				.handle = bo->handle,
+		};
+		drmHashDelete(bo->dev->handle_table, bo->handle);
+		if (bo->name)
+			drmHashDelete(bo->dev->name_table, bo->name);
+		drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+	}
+
+	bo->funcs->destroy(bo);
+}
+
+int fd_bo_get_name(struct fd_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) {
+			return ret;
+		}
+
+		pthread_mutex_lock(&table_lock);
+		set_name(bo, req.name);
+		pthread_mutex_unlock(&table_lock);
+		bo->bo_reuse = FALSE;
+	}
+
+	*name = bo->name;
+
+	return 0;
+}
+
+uint32_t fd_bo_handle(struct fd_bo *bo)
+{
+	return bo->handle;
+}
+
+int fd_bo_dmabuf(struct fd_bo *bo)
+{
+	int ret, prime_fd;
+
+	ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
+			&prime_fd);
+	if (ret) {
+		ERROR_MSG("failed to get dmabuf fd: %d", ret);
+		return ret;
+	}
+
+	bo->bo_reuse = FALSE;
+
+	return prime_fd;
+}
+
+uint32_t fd_bo_size(struct fd_bo *bo)
+{
+	return bo->size;
+}
+
+void * fd_bo_map(struct fd_bo *bo)
+{
+	if (!bo->map) {
+		uint64_t offset;
+		int ret;
+
+		ret = bo->funcs->offset(bo, &offset);
+		if (ret) {
+			return NULL;
+		}
+
+		bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
+				bo->dev->fd, offset);
+		if (bo->map == MAP_FAILED) {
+			ERROR_MSG("mmap failed: %s", strerror(errno));
+			bo->map = NULL;
+		}
+	}
+	return bo->map;
+}
+
+/* a bit odd to take the pipe as an arg, but it's a, umm, quirk of kgsl.. */
+int fd_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
+{
+	return bo->funcs->cpu_prep(bo, pipe, op);
+}
+
+void fd_bo_cpu_fini(struct fd_bo *bo)
+{
+	bo->funcs->cpu_fini(bo);
+}
+
+#ifndef HAVE_FREEDRENO_KGSL
+struct fd_bo * fd_bo_from_fbdev(struct fd_pipe *pipe, int fbfd, uint32_t size)
+{
+    return NULL;
+}
+#endif
diff --git a/freedreno/freedreno_bo_cache.c b/freedreno/freedreno_bo_cache.c
new file mode 100644
index 0000000..d922f3a
--- /dev/null
+++ b/freedreno/freedreno_bo_cache.c
@@ -0,0 +1,224 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2016 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "freedreno_drmif.h"
+#include "freedreno_priv.h"
+
+drm_private void bo_del(struct fd_bo *bo);
+drm_private extern pthread_mutex_t table_lock;
+
+static void
+add_bucket(struct fd_bo_cache *cache, int size)
+{
+	unsigned int i = cache->num_buckets;
+
+	assert(i < ARRAY_SIZE(cache->cache_bucket));
+
+	list_inithead(&cache->cache_bucket[i].list);
+	cache->cache_bucket[i].size = size;
+	cache->num_buckets++;
+}
+
+/**
+ * @coarse: if true, only power-of-two bucket sizes, otherwise
+ *    fill in for a bit smoother size curve..
+ */
+drm_private void
+fd_bo_cache_init(struct fd_bo_cache *cache, int course)
+{
+	unsigned long size, cache_max_size = 64 * 1024 * 1024;
+
+	/* OK, so power of two buckets was too wasteful of memory.
+	 * Give 3 other sizes between each power of two, to hopefully
+	 * cover things accurately enough.  (The alternative is
+	 * probably to just go for exact matching of sizes, and assume
+	 * that for things like composited window resize the tiled
+	 * width/height alignment and rounding of sizes to pages will
+	 * get us useful cache hit rates anyway)
+	 */
+	add_bucket(cache, 4096);
+	add_bucket(cache, 4096 * 2);
+	if (!course)
+		add_bucket(cache, 4096 * 3);
+
+	/* Initialize the linked lists for BO reuse cache. */
+	for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
+		add_bucket(cache, size);
+		if (!course) {
+			add_bucket(cache, size + size * 1 / 4);
+			add_bucket(cache, size + size * 2 / 4);
+			add_bucket(cache, size + size * 3 / 4);
+		}
+	}
+}
+
+/* Frees older cached buffers.  Called under table_lock */
+drm_private void
+fd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time)
+{
+	int i;
+
+	if (cache->time == time)
+		return;
+
+	for (i = 0; i < cache->num_buckets; i++) {
+		struct fd_bo_bucket *bucket = &cache->cache_bucket[i];
+		struct fd_bo *bo;
+
+		while (!LIST_IS_EMPTY(&bucket->list)) {
+			bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list);
+
+			/* keep things in cache for at least 1 second: */
+			if (time && ((time - bo->free_time) <= 1))
+				break;
+
+			VG_BO_OBTAIN(bo);
+			list_del(&bo->list);
+			bo_del(bo);
+		}
+	}
+
+	cache->time = time;
+}
+
+static struct fd_bo_bucket * get_bucket(struct fd_bo_cache *cache, uint32_t size)
+{
+	int i;
+
+	/* hmm, this is what intel does, but I suppose we could calculate our
+	 * way to the correct bucket size rather than looping..
+	 */
+	for (i = 0; i < cache->num_buckets; i++) {
+		struct fd_bo_bucket *bucket = &cache->cache_bucket[i];
+		if (bucket->size >= size) {
+			return bucket;
+		}
+	}
+
+	return NULL;
+}
+
+static int is_idle(struct fd_bo *bo)
+{
+	return fd_bo_cpu_prep(bo, NULL,
+			DRM_FREEDRENO_PREP_READ |
+			DRM_FREEDRENO_PREP_WRITE |
+			DRM_FREEDRENO_PREP_NOSYNC) == 0;
+}
+
+static struct fd_bo *find_in_bucket(struct fd_bo_bucket *bucket, uint32_t flags)
+{
+	struct fd_bo *bo = NULL;
+
+	/* TODO .. if we had an ALLOC_FOR_RENDER flag like intel, we could
+	 * skip the busy check.. if it is only going to be a render target
+	 * then we probably don't need to stall..
+	 *
+	 * NOTE that intel takes ALLOC_FOR_RENDER bo's from the list tail
+	 * (MRU, since likely to be in GPU cache), rather than head (LRU)..
+	 */
+	pthread_mutex_lock(&table_lock);
+	if (!LIST_IS_EMPTY(&bucket->list)) {
+		bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list);
+		/* TODO check for compatible flags? */
+		if (is_idle(bo)) {
+			list_del(&bo->list);
+		} else {
+			bo = NULL;
+		}
+	}
+	pthread_mutex_unlock(&table_lock);
+
+	return bo;
+}
+
+/* NOTE: size is potentially rounded up to bucket size: */
+drm_private struct fd_bo *
+fd_bo_cache_alloc(struct fd_bo_cache *cache, uint32_t *size, uint32_t flags)
+{
+	struct fd_bo *bo = NULL;
+	struct fd_bo_bucket *bucket;
+
+	*size = ALIGN(*size, 4096);
+	bucket = get_bucket(cache, *size);
+
+	/* see if we can be green and recycle: */
+retry:
+	if (bucket) {
+		*size = bucket->size;
+		bo = find_in_bucket(bucket, flags);
+		if (bo) {
+			VG_BO_OBTAIN(bo);
+			if (bo->funcs->madvise(bo, TRUE) <= 0) {
+				/* we've lost the backing pages, delete and try again: */
+				pthread_mutex_lock(&table_lock);
+				bo_del(bo);
+				pthread_mutex_unlock(&table_lock);
+				goto retry;
+			}
+			atomic_set(&bo->refcnt, 1);
+			fd_device_ref(bo->dev);
+			return bo;
+		}
+	}
+
+	return NULL;
+}
+
+drm_private int
+fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo)
+{
+	struct fd_bo_bucket *bucket = get_bucket(cache, bo->size);
+
+	/* see if we can be green and recycle: */
+	if (bucket) {
+		struct timespec time;
+
+		bo->funcs->madvise(bo, FALSE);
+
+		clock_gettime(CLOCK_MONOTONIC, &time);
+
+		bo->free_time = time.tv_sec;
+		VG_BO_RELEASE(bo);
+		list_addtail(&bo->list, &bucket->list);
+		fd_bo_cache_cleanup(cache, time.tv_sec);
+
+		/* bo's in the bucket cache don't have a ref and
+		 * don't hold a ref to the dev:
+		 */
+		fd_device_del_locked(bo->dev);
+
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/freedreno/freedreno_device.c b/freedreno/freedreno_device.c
new file mode 100644
index 0000000..12b95fd
--- /dev/null
+++ b/freedreno/freedreno_device.c
@@ -0,0 +1,148 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "freedreno_drmif.h"
+#include "freedreno_priv.h"
+
+static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
+
+struct fd_device * kgsl_device_new(int fd);
+struct fd_device * msm_device_new(int fd);
+
+struct fd_device * fd_device_new(int fd)
+{
+	struct fd_device *dev;
+	drmVersionPtr version;
+
+	/* figure out if we are kgsl or msm drm driver: */
+	version = drmGetVersion(fd);
+	if (!version) {
+		ERROR_MSG("cannot get version: %s", strerror(errno));
+		return NULL;
+	}
+
+	if (!strcmp(version->name, "msm")) {
+		DEBUG_MSG("msm DRM device");
+		if (version->version_major != 1) {
+			ERROR_MSG("unsupported version: %u.%u.%u", version->version_major,
+				version->version_minor, version->version_patchlevel);
+			dev = NULL;
+			goto out;
+		}
+
+		dev = msm_device_new(fd);
+		dev->version = version->version_minor;
+#ifdef HAVE_FREEDRENO_KGSL
+	} else if (!strcmp(version->name, "kgsl")) {
+		DEBUG_MSG("kgsl DRM device");
+		dev = kgsl_device_new(fd);
+#endif
+	} else {
+		ERROR_MSG("unknown device: %s", version->name);
+		dev = NULL;
+	}
+
+out:
+	drmFreeVersion(version);
+
+	if (!dev)
+		return NULL;
+
+	atomic_set(&dev->refcnt, 1);
+	dev->fd = fd;
+	dev->handle_table = drmHashCreate();
+	dev->name_table = drmHashCreate();
+	fd_bo_cache_init(&dev->bo_cache, FALSE);
+
+	return dev;
+}
+
+/* like fd_device_new() but creates it's own private dup() of the fd
+ * which is close()d when the device is finalized.
+ */
+struct fd_device * fd_device_new_dup(int fd)
+{
+	int dup_fd = dup(fd);
+	struct fd_device *dev = fd_device_new(dup_fd);
+	if (dev)
+		dev->closefd = 1;
+	else
+		close(dup_fd);
+	return dev;
+}
+
+struct fd_device * fd_device_ref(struct fd_device *dev)
+{
+	atomic_inc(&dev->refcnt);
+	return dev;
+}
+
+static void fd_device_del_impl(struct fd_device *dev)
+{
+	int close_fd = dev->closefd ? dev->fd : -1;
+	fd_bo_cache_cleanup(&dev->bo_cache, 0);
+	drmHashDestroy(dev->handle_table);
+	drmHashDestroy(dev->name_table);
+	dev->funcs->destroy(dev);
+	if (close_fd >= 0)
+		close(close_fd);
+}
+
+drm_private void fd_device_del_locked(struct fd_device *dev)
+{
+	if (!atomic_dec_and_test(&dev->refcnt))
+		return;
+	fd_device_del_impl(dev);
+}
+
+void fd_device_del(struct fd_device *dev)
+{
+	if (!atomic_dec_and_test(&dev->refcnt))
+		return;
+	pthread_mutex_lock(&table_lock);
+	fd_device_del_impl(dev);
+	pthread_mutex_unlock(&table_lock);
+}
+
+int fd_device_fd(struct fd_device *dev)
+{
+	return dev->fd;
+}
+
+enum fd_version fd_device_version(struct fd_device *dev)
+{
+	return dev->version;
+}
diff --git a/freedreno/freedreno_drmif.h b/freedreno/freedreno_drmif.h
new file mode 100644
index 0000000..7a8073f
--- /dev/null
+++ b/freedreno/freedreno_drmif.h
@@ -0,0 +1,133 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifndef FREEDRENO_DRMIF_H_
+#define FREEDRENO_DRMIF_H_
+
+#include <xf86drm.h>
+#include <stdint.h>
+
+#if defined(__GNUC__)
+#  define drm_deprecated __attribute__((__deprecated__))
+#else
+#  define drm_deprecated
+#endif
+
+/* an empty marker for things that will be deprecated in the future: */
+#define will_be_deprecated
+
+struct fd_bo;
+struct fd_pipe;
+struct fd_device;
+
+enum fd_pipe_id {
+	FD_PIPE_3D = 1,
+	FD_PIPE_2D = 2,
+	/* some devices have two 2d blocks.. not really sure how to
+	 * use that yet, so just ignoring the 2nd 2d pipe for now
+	 */
+	FD_PIPE_MAX
+};
+
+enum fd_param_id {
+	FD_DEVICE_ID,
+	FD_GMEM_SIZE,
+	FD_GPU_ID,
+	FD_CHIP_ID,
+	FD_MAX_FREQ,
+	FD_TIMESTAMP,
+};
+
+/* bo flags: */
+#define DRM_FREEDRENO_GEM_TYPE_SMI        0x00000001
+#define DRM_FREEDRENO_GEM_TYPE_KMEM       0x00000002
+#define DRM_FREEDRENO_GEM_TYPE_MEM_MASK   0x0000000f
+#define DRM_FREEDRENO_GEM_CACHE_NONE      0x00000000
+#define DRM_FREEDRENO_GEM_CACHE_WCOMBINE  0x00100000
+#define DRM_FREEDRENO_GEM_CACHE_WTHROUGH  0x00200000
+#define DRM_FREEDRENO_GEM_CACHE_WBACK     0x00400000
+#define DRM_FREEDRENO_GEM_CACHE_WBACKWA   0x00800000
+#define DRM_FREEDRENO_GEM_CACHE_MASK      0x00f00000
+#define DRM_FREEDRENO_GEM_GPUREADONLY     0x01000000
+
+/* bo access flags: (keep aligned to MSM_PREP_x) */
+#define DRM_FREEDRENO_PREP_READ           0x01
+#define DRM_FREEDRENO_PREP_WRITE          0x02
+#define DRM_FREEDRENO_PREP_NOSYNC         0x04
+
+/* device functions:
+ */
+
+struct fd_device * fd_device_new(int fd);
+struct fd_device * fd_device_new_dup(int fd);
+struct fd_device * fd_device_ref(struct fd_device *dev);
+void fd_device_del(struct fd_device *dev);
+int fd_device_fd(struct fd_device *dev);
+
+enum fd_version {
+	FD_VERSION_MADVISE = 1,            /* kernel supports madvise */
+	FD_VERSION_UNLIMITED_CMDS = 1,     /* submits w/ >4 cmd buffers (growable ringbuffer) */
+	FD_VERSION_FENCE_FD = 2,           /* submit command supports in/out fences */
+};
+enum fd_version fd_device_version(struct fd_device *dev);
+
+/* pipe functions:
+ */
+
+struct fd_pipe * fd_pipe_new(struct fd_device *dev, enum fd_pipe_id id);
+void fd_pipe_del(struct fd_pipe *pipe);
+int fd_pipe_get_param(struct fd_pipe *pipe, enum fd_param_id param,
+		uint64_t *value);
+int fd_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp);
+/* timeout in nanosec */
+int fd_pipe_wait_timeout(struct fd_pipe *pipe, uint32_t timestamp,
+		uint64_t timeout);
+
+
+/* buffer-object functions:
+ */
+
+struct fd_bo * fd_bo_new(struct fd_device *dev,
+		uint32_t size, uint32_t flags);
+struct fd_bo * fd_bo_from_fbdev(struct fd_pipe *pipe,
+		int fbfd, uint32_t size);
+struct fd_bo *fd_bo_from_handle(struct fd_device *dev,
+		uint32_t handle, uint32_t size);
+struct fd_bo * fd_bo_from_name(struct fd_device *dev, uint32_t name);
+struct fd_bo * fd_bo_from_dmabuf(struct fd_device *dev, int fd);
+struct fd_bo * fd_bo_ref(struct fd_bo *bo);
+void fd_bo_del(struct fd_bo *bo);
+int fd_bo_get_name(struct fd_bo *bo, uint32_t *name);
+uint32_t fd_bo_handle(struct fd_bo *bo);
+int fd_bo_dmabuf(struct fd_bo *bo);
+uint32_t fd_bo_size(struct fd_bo *bo);
+void * fd_bo_map(struct fd_bo *bo);
+int fd_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op);
+void fd_bo_cpu_fini(struct fd_bo *bo);
+
+#endif /* FREEDRENO_DRMIF_H_ */
diff --git a/freedreno/freedreno_pipe.c b/freedreno/freedreno_pipe.c
new file mode 100644
index 0000000..e69cb28
--- /dev/null
+++ b/freedreno/freedreno_pipe.c
@@ -0,0 +1,82 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "freedreno_drmif.h"
+#include "freedreno_priv.h"
+
+struct fd_pipe *
+fd_pipe_new(struct fd_device *dev, enum fd_pipe_id id)
+{
+	struct fd_pipe *pipe;
+	uint64_t val;
+
+	if (id > FD_PIPE_MAX) {
+		ERROR_MSG("invalid pipe id: %d", id);
+		return NULL;
+	}
+
+	pipe = dev->funcs->pipe_new(dev, id);
+	if (!pipe) {
+		ERROR_MSG("allocation failed");
+		return NULL;
+	}
+
+	pipe->dev = dev;
+	pipe->id = id;
+
+	fd_pipe_get_param(pipe, FD_GPU_ID, &val);
+	pipe->gpu_id = val;
+
+	return pipe;
+}
+
+void fd_pipe_del(struct fd_pipe *pipe)
+{
+	pipe->funcs->destroy(pipe);
+}
+
+int fd_pipe_get_param(struct fd_pipe *pipe,
+				 enum fd_param_id param, uint64_t *value)
+{
+	return pipe->funcs->get_param(pipe, param, value);
+}
+
+int fd_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp)
+{
+	return fd_pipe_wait_timeout(pipe, timestamp, ~0);
+}
+
+int fd_pipe_wait_timeout(struct fd_pipe *pipe, uint32_t timestamp,
+		uint64_t timeout)
+{
+	return pipe->funcs->wait(pipe, timestamp, timeout);
+}
diff --git a/freedreno/freedreno_priv.h b/freedreno/freedreno_priv.h
new file mode 100644
index 0000000..8dd3ee6
--- /dev/null
+++ b/freedreno/freedreno_priv.h
@@ -0,0 +1,255 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifndef FREEDRENO_PRIV_H_
+#define FREEDRENO_PRIV_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "libdrm_macros.h"
+#include "xf86drm.h"
+#include "xf86atomic.h"
+
+#include "util_double_list.h"
+
+#include "freedreno_drmif.h"
+#include "freedreno_ringbuffer.h"
+#include "drm.h"
+
+#ifndef TRUE
+#  define TRUE 1
+#endif
+#ifndef FALSE
+#  define FALSE 0
+#endif
+
+struct fd_device_funcs {
+	int (*bo_new_handle)(struct fd_device *dev, uint32_t size,
+			uint32_t flags, uint32_t *handle);
+	struct fd_bo * (*bo_from_handle)(struct fd_device *dev,
+			uint32_t size, uint32_t handle);
+	struct fd_pipe * (*pipe_new)(struct fd_device *dev, enum fd_pipe_id id);
+	void (*destroy)(struct fd_device *dev);
+};
+
+struct fd_bo_bucket {
+	uint32_t size;
+	struct list_head list;
+};
+
+struct fd_bo_cache {
+	struct fd_bo_bucket cache_bucket[14 * 4];
+	int num_buckets;
+	time_t time;
+};
+
+struct fd_device {
+	int fd;
+	enum fd_version version;
+	atomic_t refcnt;
+
+	/* tables to keep track of bo's, to avoid "evil-twin" fd_bo objects:
+	 *
+	 *   handle_table: maps handle to fd_bo
+	 *   name_table: maps flink name to fd_bo
+	 *
+	 * We end up needing two tables, because DRM_IOCTL_GEM_OPEN always
+	 * returns a new handle.  So we need to figure out if the bo is already
+	 * open in the process first, before calling gem-open.
+	 */
+	void *handle_table, *name_table;
+
+	const struct fd_device_funcs *funcs;
+
+	struct fd_bo_cache bo_cache;
+
+	int closefd;        /* call close(fd) upon destruction */
+
+	/* just for valgrind: */
+	int bo_size;
+};
+
+drm_private void fd_bo_cache_init(struct fd_bo_cache *cache, int coarse);
+drm_private void fd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time);
+drm_private struct fd_bo * fd_bo_cache_alloc(struct fd_bo_cache *cache,
+		uint32_t *size, uint32_t flags);
+drm_private int fd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo);
+
+/* for where @table_lock is already held: */
+drm_private void fd_device_del_locked(struct fd_device *dev);
+
+struct fd_pipe_funcs {
+	struct fd_ringbuffer * (*ringbuffer_new)(struct fd_pipe *pipe, uint32_t size);
+	int (*get_param)(struct fd_pipe *pipe, enum fd_param_id param, uint64_t *value);
+	int (*wait)(struct fd_pipe *pipe, uint32_t timestamp, uint64_t timeout);
+	void (*destroy)(struct fd_pipe *pipe);
+};
+
+struct fd_pipe {
+	struct fd_device *dev;
+	enum fd_pipe_id id;
+	uint32_t gpu_id;
+	const struct fd_pipe_funcs *funcs;
+};
+
+struct fd_ringmarker {
+	struct fd_ringbuffer *ring;
+	uint32_t *cur;
+};
+
+struct fd_ringbuffer_funcs {
+	void * (*hostptr)(struct fd_ringbuffer *ring);
+	int (*flush)(struct fd_ringbuffer *ring, uint32_t *last_start,
+			int in_fence_fd, int *out_fence_fd);
+	void (*grow)(struct fd_ringbuffer *ring, uint32_t size);
+	void (*reset)(struct fd_ringbuffer *ring);
+	void (*emit_reloc)(struct fd_ringbuffer *ring,
+			const struct fd_reloc *reloc);
+	uint32_t (*emit_reloc_ring)(struct fd_ringbuffer *ring,
+			struct fd_ringbuffer *target, uint32_t cmd_idx,
+			uint32_t submit_offset, uint32_t size);
+	uint32_t (*cmd_count)(struct fd_ringbuffer *ring);
+	void (*destroy)(struct fd_ringbuffer *ring);
+};
+
+struct fd_bo_funcs {
+	int (*offset)(struct fd_bo *bo, uint64_t *offset);
+	int (*cpu_prep)(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op);
+	void (*cpu_fini)(struct fd_bo *bo);
+	int (*madvise)(struct fd_bo *bo, int willneed);
+	void (*destroy)(struct fd_bo *bo);
+};
+
+struct fd_bo {
+	struct fd_device *dev;
+	uint32_t size;
+	uint32_t handle;
+	uint32_t name;
+	void *map;
+	atomic_t refcnt;
+	const struct fd_bo_funcs *funcs;
+
+	int bo_reuse;
+	struct list_head list;   /* bucket-list entry */
+	time_t free_time;        /* time when added to bucket-list */
+};
+
+#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1))
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define enable_debug 0  /* TODO make dynamic */
+
+#define INFO_MSG(fmt, ...) \
+		do { drmMsg("[I] "fmt " (%s:%d)\n", \
+				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define DEBUG_MSG(fmt, ...) \
+		do if (enable_debug) { drmMsg("[D] "fmt " (%s:%d)\n", \
+				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define WARN_MSG(fmt, ...) \
+		do { drmMsg("[W] "fmt " (%s:%d)\n", \
+				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+#define ERROR_MSG(fmt, ...) \
+		do { drmMsg("[E] " fmt " (%s:%d)\n", \
+				##__VA_ARGS__, __FUNCTION__, __LINE__); } while (0)
+
+#define U642VOID(x) ((void *)(unsigned long)(x))
+#define VOID2U64(x) ((uint64_t)(unsigned long)(x))
+
+static inline uint32_t
+offset_bytes(void *end, void *start)
+{
+	return ((char *)end) - ((char *)start);
+}
+
+#ifdef HAVE_VALGRIND
+#  include <memcheck.h>
+
+/*
+ * For tracking the backing memory (if valgrind enabled, we force a mmap
+ * for the purposes of tracking)
+ */
+static inline void VG_BO_ALLOC(struct fd_bo *bo)
+{
+	if (bo && RUNNING_ON_VALGRIND) {
+		VALGRIND_MALLOCLIKE_BLOCK(fd_bo_map(bo), bo->size, 0, 1);
+	}
+}
+
+static inline void VG_BO_FREE(struct fd_bo *bo)
+{
+	VALGRIND_FREELIKE_BLOCK(bo->map, 0);
+}
+
+/*
+ * For tracking bo structs that are in the buffer-cache, so that valgrind
+ * doesn't attribute ownership to the first one to allocate the recycled
+ * bo.
+ *
+ * Note that the list_head in fd_bo is used to track the buffers in cache
+ * so disable error reporting on the range while they are in cache so
+ * valgrind doesn't squawk about list traversal.
+ *
+ */
+static inline void VG_BO_RELEASE(struct fd_bo *bo)
+{
+	if (RUNNING_ON_VALGRIND) {
+		VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(bo, bo->dev->bo_size);
+		VALGRIND_MAKE_MEM_NOACCESS(bo, bo->dev->bo_size);
+		VALGRIND_FREELIKE_BLOCK(bo->map, 0);
+	}
+}
+static inline void VG_BO_OBTAIN(struct fd_bo *bo)
+{
+	if (RUNNING_ON_VALGRIND) {
+		VALGRIND_MAKE_MEM_DEFINED(bo, bo->dev->bo_size);
+		VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(bo, bo->dev->bo_size);
+		VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, 1);
+	}
+}
+#else
+static inline void VG_BO_ALLOC(struct fd_bo *bo)   {}
+static inline void VG_BO_FREE(struct fd_bo *bo)    {}
+static inline void VG_BO_RELEASE(struct fd_bo *bo) {}
+static inline void VG_BO_OBTAIN(struct fd_bo *bo)  {}
+#endif
+
+
+#endif /* FREEDRENO_PRIV_H_ */
diff --git a/freedreno/freedreno_ringbuffer.c b/freedreno/freedreno_ringbuffer.c
new file mode 100644
index 0000000..7310f1f
--- /dev/null
+++ b/freedreno/freedreno_ringbuffer.c
@@ -0,0 +1,195 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+
+#include "freedreno_drmif.h"
+#include "freedreno_priv.h"
+#include "freedreno_ringbuffer.h"
+
+struct fd_ringbuffer *
+fd_ringbuffer_new(struct fd_pipe *pipe, uint32_t size)
+{
+	struct fd_ringbuffer *ring;
+
+	ring = pipe->funcs->ringbuffer_new(pipe, size);
+	if (!ring)
+		return NULL;
+
+	ring->pipe = pipe;
+	ring->start = ring->funcs->hostptr(ring);
+	ring->end = &(ring->start[ring->size/4]);
+
+	ring->cur = ring->last_start = ring->start;
+
+	return ring;
+}
+
+void fd_ringbuffer_del(struct fd_ringbuffer *ring)
+{
+	fd_ringbuffer_reset(ring);
+	ring->funcs->destroy(ring);
+}
+
+/* ringbuffers which are IB targets should set the toplevel rb (ie.
+ * the IB source) as it's parent before emitting reloc's, to ensure
+ * the bookkeeping works out properly.
+ */
+void fd_ringbuffer_set_parent(struct fd_ringbuffer *ring,
+					 struct fd_ringbuffer *parent)
+{
+	ring->parent = parent;
+}
+
+void fd_ringbuffer_reset(struct fd_ringbuffer *ring)
+{
+	uint32_t *start = ring->start;
+	if (ring->pipe->id == FD_PIPE_2D)
+		start = &ring->start[0x140];
+	ring->cur = ring->last_start = start;
+	if (ring->funcs->reset)
+		ring->funcs->reset(ring);
+}
+
+int fd_ringbuffer_flush(struct fd_ringbuffer *ring)
+{
+	return ring->funcs->flush(ring, ring->last_start, -1, NULL);
+}
+
+int fd_ringbuffer_flush2(struct fd_ringbuffer *ring, int in_fence_fd,
+		int *out_fence_fd)
+{
+	return ring->funcs->flush(ring, ring->last_start, in_fence_fd, out_fence_fd);
+}
+
+void fd_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t ndwords)
+{
+	assert(ring->funcs->grow);     /* unsupported on kgsl */
+
+	/* there is an upper bound on IB size, which appears to be 0x100000 */
+	if (ring->size < 0x100000)
+		ring->size *= 2;
+
+	ring->funcs->grow(ring, ring->size);
+
+	ring->start = ring->funcs->hostptr(ring);
+	ring->end = &(ring->start[ring->size/4]);
+
+	ring->cur = ring->last_start = ring->start;
+}
+
+uint32_t fd_ringbuffer_timestamp(struct fd_ringbuffer *ring)
+{
+	return ring->last_timestamp;
+}
+
+void fd_ringbuffer_reloc(struct fd_ringbuffer *ring,
+				    const struct fd_reloc *reloc)
+{
+	assert(ring->pipe->gpu_id < 500);
+	ring->funcs->emit_reloc(ring, reloc);
+}
+
+void fd_ringbuffer_reloc2(struct fd_ringbuffer *ring,
+				     const struct fd_reloc *reloc)
+{
+	ring->funcs->emit_reloc(ring, reloc);
+}
+
+void fd_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
+		struct fd_ringmarker *target, struct fd_ringmarker *end)
+{
+	uint32_t submit_offset, size;
+
+	/* This function is deprecated and not supported on 64b devices: */
+	assert(ring->pipe->gpu_id < 500);
+	assert(target->ring == end->ring);
+
+	submit_offset = offset_bytes(target->cur, target->ring->start);
+	size = offset_bytes(end->cur, target->cur);
+
+	ring->funcs->emit_reloc_ring(ring, target->ring, 0, submit_offset, size);
+}
+
+uint32_t fd_ringbuffer_cmd_count(struct fd_ringbuffer *ring)
+{
+	if (!ring->funcs->cmd_count)
+		return 1;
+	return ring->funcs->cmd_count(ring);
+}
+
+uint32_t
+fd_ringbuffer_emit_reloc_ring_full(struct fd_ringbuffer *ring,
+		struct fd_ringbuffer *target, uint32_t cmd_idx)
+{
+	uint32_t size = offset_bytes(target->cur, target->start);
+	return ring->funcs->emit_reloc_ring(ring, target, cmd_idx, 0, size);
+}
+
+struct fd_ringmarker * fd_ringmarker_new(struct fd_ringbuffer *ring)
+{
+	struct fd_ringmarker *marker = NULL;
+
+	marker = calloc(1, sizeof(*marker));
+	if (!marker) {
+		ERROR_MSG("allocation failed");
+		return NULL;
+	}
+
+	marker->ring = ring;
+
+	marker->cur = marker->ring->cur;
+
+	return marker;
+}
+
+void fd_ringmarker_del(struct fd_ringmarker *marker)
+{
+	free(marker);
+}
+
+void fd_ringmarker_mark(struct fd_ringmarker *marker)
+{
+	marker->cur = marker->ring->cur;
+}
+
+uint32_t fd_ringmarker_dwords(struct fd_ringmarker *start,
+					 struct fd_ringmarker *end)
+{
+	return end->cur - start->cur;
+}
+
+int fd_ringmarker_flush(struct fd_ringmarker *marker)
+{
+	struct fd_ringbuffer *ring = marker->ring;
+	return ring->funcs->flush(ring, marker->cur, -1, NULL);
+}
diff --git a/freedreno/freedreno_ringbuffer.h b/freedreno/freedreno_ringbuffer.h
new file mode 100644
index 0000000..c501fba
--- /dev/null
+++ b/freedreno/freedreno_ringbuffer.h
@@ -0,0 +1,101 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2012 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifndef FREEDRENO_RINGBUFFER_H_
+#define FREEDRENO_RINGBUFFER_H_
+
+#include <freedreno_drmif.h>
+
+/* the ringbuffer object is not opaque so that OUT_RING() type stuff
+ * can be inlined.  Note that users should not make assumptions about
+ * the size of this struct.. more stuff will be added when we eventually
+ * have a kernel driver that can deal w/ reloc's..
+ */
+
+struct fd_ringbuffer_funcs;
+struct fd_ringmarker;
+
+struct fd_ringbuffer {
+	int size;
+	uint32_t *cur, *end, *start, *last_start;
+	struct fd_pipe *pipe;
+	const struct fd_ringbuffer_funcs *funcs;
+	uint32_t last_timestamp;
+	struct fd_ringbuffer *parent;
+};
+
+struct fd_ringbuffer * fd_ringbuffer_new(struct fd_pipe *pipe,
+		uint32_t size);
+void fd_ringbuffer_del(struct fd_ringbuffer *ring);
+void fd_ringbuffer_set_parent(struct fd_ringbuffer *ring,
+		struct fd_ringbuffer *parent);
+void fd_ringbuffer_reset(struct fd_ringbuffer *ring);
+int fd_ringbuffer_flush(struct fd_ringbuffer *ring);
+/* in_fence_fd: -1 for no in-fence, else fence fd
+ * out_fence_fd: NULL for no output-fence requested, else ptr to return out-fence
+ */
+int fd_ringbuffer_flush2(struct fd_ringbuffer *ring, int in_fence_fd,
+		int *out_fence_fd);
+void fd_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t ndwords);
+uint32_t fd_ringbuffer_timestamp(struct fd_ringbuffer *ring);
+
+static inline void fd_ringbuffer_emit(struct fd_ringbuffer *ring,
+		uint32_t data)
+{
+	(*ring->cur++) = data;
+}
+
+struct fd_reloc {
+	struct fd_bo *bo;
+#define FD_RELOC_READ             0x0001
+#define FD_RELOC_WRITE            0x0002
+	uint32_t flags;
+	uint32_t offset;
+	uint32_t or;
+	int32_t  shift;
+	uint32_t orhi;      /* used for a5xx+ */
+};
+
+/* NOTE: relocs are 2 dwords on a5xx+ */
+
+void fd_ringbuffer_reloc2(struct fd_ringbuffer *ring, const struct fd_reloc *reloc);
+will_be_deprecated void fd_ringbuffer_reloc(struct fd_ringbuffer *ring, const struct fd_reloc *reloc);
+will_be_deprecated void fd_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
+		struct fd_ringmarker *target, struct fd_ringmarker *end);
+uint32_t fd_ringbuffer_cmd_count(struct fd_ringbuffer *ring);
+uint32_t fd_ringbuffer_emit_reloc_ring_full(struct fd_ringbuffer *ring,
+		struct fd_ringbuffer *target, uint32_t cmd_idx);
+
+will_be_deprecated struct fd_ringmarker * fd_ringmarker_new(struct fd_ringbuffer *ring);
+will_be_deprecated void fd_ringmarker_del(struct fd_ringmarker *marker);
+will_be_deprecated void fd_ringmarker_mark(struct fd_ringmarker *marker);
+will_be_deprecated uint32_t fd_ringmarker_dwords(struct fd_ringmarker *start,
+		struct fd_ringmarker *end);
+will_be_deprecated int fd_ringmarker_flush(struct fd_ringmarker *marker);
+
+#endif /* FREEDRENO_RINGBUFFER_H_ */
diff --git a/freedreno/kgsl/README b/freedreno/kgsl/README
new file mode 100644
index 0000000..56874b4
--- /dev/null
+++ b/freedreno/kgsl/README
@@ -0,0 +1,26 @@
+This is a historical discription of what is now the kgsl backend
+in libdrm freedreno (before the upstream drm/msm driver).  Note
+that the kgsl backend requires the "kgsl-drm" shim driver, which
+usually is in disrepair (QCOM does not build it for android), and
+due to random differences between different downstream android
+kernel branches it may or may not work.  So YMMV.
+
+Original README:
+----------------
+
+Note that current msm kernel driver is a bit strange.  It provides a
+DRM interface for GEM, which is basically sufficient to have DRI2
+working.  But it does not provide KMS.  And interface to 2d and 3d
+cores is via different other devices (/dev/kgsl-*).  This is not
+quite how I'd write a DRM driver, but at this stage it is useful for
+xf86-video-freedreno and fdre (and eventual gallium driver) to be
+able to work on existing kernel driver from QCOM, to allow to
+capture cmdstream dumps from the binary blob drivers without having
+to reboot.  So libdrm_freedreno attempts to hide most of the crazy.
+The intention is that when there is a proper kernel driver, it will
+be mostly just changes in libdrm_freedreno to adapt the gallium
+driver and xf86-video-freedreno (ignoring the fbdev->KMS changes).
+
+So don't look at freedreno as an example of how to write a libdrm
+module or a DRM driver.. it is just an attempt to paper over a non-
+standard kernel driver architecture.
diff --git a/freedreno/kgsl/kgsl_bo.c b/freedreno/kgsl/kgsl_bo.c
new file mode 100644
index 0000000..ab3485e
--- /dev/null
+++ b/freedreno/kgsl/kgsl_bo.c
@@ -0,0 +1,315 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "kgsl_priv.h"
+
+#include <linux/fb.h>
+
+static int set_memtype(struct fd_device *dev, uint32_t handle, uint32_t flags)
+{
+	struct drm_kgsl_gem_memtype req = {
+			.handle = handle,
+			.type = flags & DRM_FREEDRENO_GEM_TYPE_MEM_MASK,
+	};
+
+	return drmCommandWrite(dev->fd, DRM_KGSL_GEM_SETMEMTYPE,
+			&req, sizeof(req));
+}
+
+static int bo_alloc(struct kgsl_bo *kgsl_bo)
+{
+	struct fd_bo *bo = &kgsl_bo->base;
+	if (!kgsl_bo->offset) {
+		struct drm_kgsl_gem_alloc req = {
+				.handle = bo->handle,
+		};
+		int ret;
+
+		/* if the buffer is already backed by pages then this
+		 * doesn't actually do anything (other than giving us
+		 * the offset)
+		 */
+		ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_ALLOC,
+				&req, sizeof(req));
+		if (ret) {
+			ERROR_MSG("alloc failed: %s", strerror(errno));
+			return ret;
+		}
+
+		kgsl_bo->offset = req.offset;
+	}
+
+	return 0;
+}
+
+static int kgsl_bo_offset(struct fd_bo *bo, uint64_t *offset)
+{
+	struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
+	int ret = bo_alloc(kgsl_bo);
+	if (ret)
+		return ret;
+	*offset = kgsl_bo->offset;
+	return 0;
+}
+
+static int kgsl_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
+{
+	uint32_t timestamp = kgsl_bo_get_timestamp(to_kgsl_bo(bo));
+
+	if (op & DRM_FREEDRENO_PREP_NOSYNC) {
+		uint32_t current;
+		int ret;
+
+		/* special case for is_idle().. we can't really handle that
+		 * properly in kgsl (perhaps we need a way to just disable
+		 * the bo-cache for kgsl?)
+		 */
+		if (!pipe)
+			return -EBUSY;
+
+		ret = kgsl_pipe_timestamp(to_kgsl_pipe(pipe), &current);
+		if (ret)
+			return ret;
+
+		if (timestamp > current)
+			return -EBUSY;
+
+		return 0;
+	}
+
+	if (timestamp)
+		fd_pipe_wait(pipe, timestamp);
+
+	return 0;
+}
+
+static void kgsl_bo_cpu_fini(struct fd_bo *bo)
+{
+}
+
+static int kgsl_bo_madvise(struct fd_bo *bo, int willneed)
+{
+	return willneed; /* not supported by kgsl */
+}
+
+static void kgsl_bo_destroy(struct fd_bo *bo)
+{
+	struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
+	free(kgsl_bo);
+
+}
+
+static const struct fd_bo_funcs funcs = {
+		.offset = kgsl_bo_offset,
+		.cpu_prep = kgsl_bo_cpu_prep,
+		.cpu_fini = kgsl_bo_cpu_fini,
+		.madvise = kgsl_bo_madvise,
+		.destroy = kgsl_bo_destroy,
+};
+
+/* allocate a buffer handle: */
+drm_private int kgsl_bo_new_handle(struct fd_device *dev,
+		uint32_t size, uint32_t flags, uint32_t *handle)
+{
+	struct drm_kgsl_gem_create req = {
+			.size = size,
+	};
+	int ret;
+
+	ret = drmCommandWriteRead(dev->fd, DRM_KGSL_GEM_CREATE,
+			&req, sizeof(req));
+	if (ret)
+		return ret;
+
+	// TODO make flags match msm driver, since kgsl is legacy..
+	// translate flags in kgsl..
+
+	set_memtype(dev, req.handle, flags);
+
+	*handle = req.handle;
+
+	return 0;
+}
+
+/* allocate a new buffer object */
+drm_private struct fd_bo * kgsl_bo_from_handle(struct fd_device *dev,
+		uint32_t size, uint32_t handle)
+{
+	struct kgsl_bo *kgsl_bo;
+	struct fd_bo *bo;
+	unsigned i;
+
+	kgsl_bo = calloc(1, sizeof(*kgsl_bo));
+	if (!kgsl_bo)
+		return NULL;
+
+	bo = &kgsl_bo->base;
+	bo->funcs = &funcs;
+
+	for (i = 0; i < ARRAY_SIZE(kgsl_bo->list); i++)
+		list_inithead(&kgsl_bo->list[i]);
+
+	return bo;
+}
+
+struct fd_bo *
+fd_bo_from_fbdev(struct fd_pipe *pipe, int fbfd, uint32_t size)
+{
+	struct fd_bo *bo;
+
+	if (!is_kgsl_pipe(pipe))
+		return NULL;
+
+	bo = fd_bo_new(pipe->dev, 1, 0);
+
+	/* this is fugly, but works around a bug in the kernel..
+	 * priv->memdesc.size never gets set, so getbufinfo ioctl
+	 * thinks the buffer hasn't be allocate and fails
+	 */
+	if (bo) {
+		void *fbmem = drm_mmap(NULL, size, PROT_READ | PROT_WRITE,
+				MAP_SHARED, fbfd, 0);
+		struct kgsl_map_user_mem req = {
+				.memtype = KGSL_USER_MEM_TYPE_ADDR,
+				.len     = size,
+				.offset  = 0,
+				.hostptr = (unsigned long)fbmem,
+		};
+		struct kgsl_bo *kgsl_bo = to_kgsl_bo(bo);
+		int ret;
+
+		ret = ioctl(to_kgsl_pipe(pipe)->fd, IOCTL_KGSL_MAP_USER_MEM, &req);
+		if (ret) {
+			ERROR_MSG("mapping user mem failed: %s",
+					strerror(errno));
+			goto fail;
+		}
+		kgsl_bo->gpuaddr = req.gpuaddr;
+		bo->map = fbmem;
+	}
+
+	return bo;
+fail:
+	if (bo)
+		fd_bo_del(bo);
+	return NULL;
+}
+
+drm_private uint32_t kgsl_bo_gpuaddr(struct kgsl_bo *kgsl_bo, uint32_t offset)
+{
+	struct fd_bo *bo = &kgsl_bo->base;
+	if (!kgsl_bo->gpuaddr) {
+		struct drm_kgsl_gem_bufinfo req = {
+				.handle = bo->handle,
+		};
+		int ret;
+
+		ret = bo_alloc(kgsl_bo);
+		if (ret) {
+			return ret;
+		}
+
+		ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_GET_BUFINFO,
+				&req, sizeof(req));
+		if (ret) {
+			ERROR_MSG("get bufinfo failed: %s", strerror(errno));
+			return 0;
+		}
+
+		kgsl_bo->gpuaddr = req.gpuaddr[0];
+	}
+	return kgsl_bo->gpuaddr + offset;
+}
+
+/*
+ * Super-cheezy way to synchronization between mesa and ddx..  the
+ * SET_ACTIVE ioctl gives us a way to stash a 32b # w/ a GEM bo, and
+ * GET_BUFINFO gives us a way to retrieve it.  We use this to stash
+ * the timestamp of the last ISSUEIBCMDS on the buffer.
+ *
+ * To avoid an obscene amount of syscalls, we:
+ *  1) Only set the timestamp for buffers w/ an flink name, ie.
+ *     only buffers shared across processes.  This is enough to
+ *     catch the DRI2 buffers.
+ *  2) Only set the timestamp for buffers submitted to the 3d ring
+ *     and only check the timestamps on buffers submitted to the
+ *     2d ring.  This should be enough to handle synchronizing of
+ *     presentation blit.  We could do synchronization in the other
+ *     direction too, but that would be problematic if we are using
+ *     the 3d ring from DDX, since client side wouldn't know this.
+ *
+ * The waiting on timestamp happens before flush, and setting of
+ * timestamp happens after flush.  It is transparent to the user
+ * of libdrm_freedreno as all the tracking of buffers happens via
+ * _emit_reloc()..
+ */
+
+drm_private void kgsl_bo_set_timestamp(struct kgsl_bo *kgsl_bo,
+		uint32_t timestamp)
+{
+	struct fd_bo *bo = &kgsl_bo->base;
+	if (bo->name) {
+		struct drm_kgsl_gem_active req = {
+				.handle = bo->handle,
+				.active = timestamp,
+		};
+		int ret;
+
+		ret = drmCommandWrite(bo->dev->fd, DRM_KGSL_GEM_SET_ACTIVE,
+				&req, sizeof(req));
+		if (ret) {
+			ERROR_MSG("set active failed: %s", strerror(errno));
+		}
+	}
+}
+
+drm_private uint32_t kgsl_bo_get_timestamp(struct kgsl_bo *kgsl_bo)
+{
+	struct fd_bo *bo = &kgsl_bo->base;
+	uint32_t timestamp = 0;
+	if (bo->name) {
+		struct drm_kgsl_gem_bufinfo req = {
+				.handle = bo->handle,
+		};
+		int ret;
+
+		ret = drmCommandWriteRead(bo->dev->fd, DRM_KGSL_GEM_GET_BUFINFO,
+				&req, sizeof(req));
+		if (ret) {
+			ERROR_MSG("get bufinfo failed: %s", strerror(errno));
+			return 0;
+		}
+
+		timestamp = req.active;
+	}
+	return timestamp;
+}
diff --git a/freedreno/kgsl/kgsl_device.c b/freedreno/kgsl/kgsl_device.c
new file mode 100644
index 0000000..958e8a7
--- /dev/null
+++ b/freedreno/kgsl/kgsl_device.c
@@ -0,0 +1,67 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "kgsl_priv.h"
+
+static void kgsl_device_destroy(struct fd_device *dev)
+{
+	struct kgsl_device *kgsl_dev = to_kgsl_device(dev);
+	free(kgsl_dev);
+}
+
+static const struct fd_device_funcs funcs = {
+		.bo_new_handle = kgsl_bo_new_handle,
+		.bo_from_handle = kgsl_bo_from_handle,
+		.pipe_new = kgsl_pipe_new,
+		.destroy = kgsl_device_destroy,
+};
+
+drm_private struct fd_device * kgsl_device_new(int fd)
+{
+	struct kgsl_device *kgsl_dev;
+	struct fd_device *dev;
+
+	kgsl_dev = calloc(1, sizeof(*kgsl_dev));
+	if (!kgsl_dev)
+		return NULL;
+
+	dev = &kgsl_dev->base;
+	dev->funcs = &funcs;
+
+	dev->bo_size = sizeof(struct kgsl_bo);
+
+	return dev;
+}
diff --git a/freedreno/kgsl/kgsl_drm.h b/freedreno/kgsl/kgsl_drm.h
new file mode 100644
index 0000000..281978e
--- /dev/null
+++ b/freedreno/kgsl/kgsl_drm.h
@@ -0,0 +1,192 @@
+#ifndef _KGSL_DRM_H_
+#define _KGSL_DRM_H_
+
+#include "drm.h"
+
+#define DRM_KGSL_GEM_CREATE 0x00
+#define DRM_KGSL_GEM_PREP   0x01
+#define DRM_KGSL_GEM_SETMEMTYPE 0x02
+#define DRM_KGSL_GEM_GETMEMTYPE 0x03
+#define DRM_KGSL_GEM_MMAP 0x04
+#define DRM_KGSL_GEM_ALLOC 0x05
+#define DRM_KGSL_GEM_BIND_GPU 0x06
+#define DRM_KGSL_GEM_UNBIND_GPU 0x07
+
+#define DRM_KGSL_GEM_GET_BUFINFO 0x08
+#define DRM_KGSL_GEM_SET_BUFCOUNT 0x09
+#define DRM_KGSL_GEM_SET_ACTIVE 0x0A
+#define DRM_KGSL_GEM_LOCK_HANDLE 0x0B
+#define DRM_KGSL_GEM_UNLOCK_HANDLE 0x0C
+#define DRM_KGSL_GEM_UNLOCK_ON_TS 0x0D
+#define DRM_KGSL_GEM_CREATE_FD 0x0E
+
+#define DRM_IOCTL_KGSL_GEM_CREATE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_CREATE, struct drm_kgsl_gem_create)
+
+#define DRM_IOCTL_KGSL_GEM_PREP \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_PREP, struct drm_kgsl_gem_prep)
+
+#define DRM_IOCTL_KGSL_GEM_SETMEMTYPE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_SETMEMTYPE, \
+struct drm_kgsl_gem_memtype)
+
+#define DRM_IOCTL_KGSL_GEM_GETMEMTYPE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_GETMEMTYPE, \
+struct drm_kgsl_gem_memtype)
+
+#define DRM_IOCTL_KGSL_GEM_MMAP \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_MMAP, struct drm_kgsl_gem_mmap)
+
+#define DRM_IOCTL_KGSL_GEM_ALLOC \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_ALLOC, struct drm_kgsl_gem_alloc)
+
+#define DRM_IOCTL_KGSL_GEM_BIND_GPU \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_BIND_GPU, struct drm_kgsl_gem_bind_gpu)
+
+#define DRM_IOCTL_KGSL_GEM_UNBIND_GPU \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_UNBIND_GPU, \
+struct drm_kgsl_gem_bind_gpu)
+
+#define DRM_IOCTL_KGSL_GEM_GET_BUFINFO \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_GET_BUFINFO, \
+	 struct drm_kgsl_gem_bufinfo)
+
+#define DRM_IOCTL_KGSL_GEM_SET_BUFCOUNT \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_SET_BUFCOUNT, \
+	 struct drm_kgsl_gem_bufcount)
+
+#define DRM_IOCTL_KGSL_GEM_SET_ACTIVE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_SET_ACTIVE, \
+	 struct drm_kgsl_gem_active)
+
+#define DRM_IOCTL_KGSL_GEM_LOCK_HANDLE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_LOCK_HANDLE, \
+struct drm_kgsl_gem_lock_handles)
+
+#define DRM_IOCTL_KGSL_GEM_UNLOCK_HANDLE \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_UNLOCK_HANDLE, \
+struct drm_kgsl_gem_unlock_handles)
+
+#define DRM_IOCTL_KGSL_GEM_UNLOCK_ON_TS \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_UNLOCK_ON_TS, \
+struct drm_kgsl_gem_unlock_on_ts)
+
+#define DRM_IOCTL_KGSL_GEM_CREATE_FD \
+DRM_IOWR(DRM_COMMAND_BASE + DRM_KGSL_GEM_CREATE_FD, \
+struct drm_kgsl_gem_create_fd)
+
+/* Maximum number of sub buffers per GEM object */
+#define DRM_KGSL_GEM_MAX_BUFFERS 2
+
+/* Memory types - these define the source and caching policies
+   of the GEM memory chunk */
+
+/* Legacy definitions left for compatibility */
+
+#define DRM_KGSL_GEM_TYPE_EBI          0
+#define DRM_KGSL_GEM_TYPE_SMI          1
+#define DRM_KGSL_GEM_TYPE_KMEM         2
+#define DRM_KGSL_GEM_TYPE_KMEM_NOCACHE 3
+#define DRM_KGSL_GEM_TYPE_MEM_MASK     0xF
+
+/* Contiguous memory (PMEM) */
+#define DRM_KGSL_GEM_TYPE_PMEM       0x000100
+
+/* PMEM memory types */
+#define DRM_KGSL_GEM_PMEM_EBI        0x001000
+#define DRM_KGSL_GEM_PMEM_SMI        0x002000
+
+/* Standard paged memory */
+#define DRM_KGSL_GEM_TYPE_MEM        0x010000
+
+/* Caching controls */
+#define DRM_KGSL_GEM_CACHE_NONE      0x000000
+#define DRM_KGSL_GEM_CACHE_WCOMBINE  0x100000
+#define DRM_KGSL_GEM_CACHE_WTHROUGH  0x200000
+#define DRM_KGSL_GEM_CACHE_WBACK     0x400000
+#define DRM_KGSL_GEM_CACHE_WBACKWA   0x800000
+#define DRM_KGSL_GEM_CACHE_MASK      0xF00000
+
+/* FD based objects */
+#define DRM_KGSL_GEM_TYPE_FD_FBMEM   0x1000000
+#define DRM_KGSL_GEM_TYPE_FD_MASK    0xF000000
+
+/* Timestamp types */
+#define DRM_KGSL_GEM_TS_3D         0x00000430
+#define DRM_KGSL_GEM_TS_2D         0x00000180
+
+
+struct drm_kgsl_gem_create {
+	uint32_t size;
+	uint32_t handle;
+};
+
+struct drm_kgsl_gem_prep {
+	uint32_t handle;
+	uint32_t phys;
+	uint64_t offset;
+};
+
+struct drm_kgsl_gem_memtype {
+	uint32_t handle;
+	uint32_t type;
+};
+
+struct drm_kgsl_gem_mmap {
+	uint32_t handle;
+	uint32_t size;
+	uint32_t hostptr;
+	uint64_t offset;
+};
+
+struct drm_kgsl_gem_alloc {
+	uint32_t handle;
+	uint64_t offset;
+};
+
+struct drm_kgsl_gem_bind_gpu {
+	uint32_t handle;
+	uint32_t gpuptr;
+};
+
+struct drm_kgsl_gem_bufinfo {
+	uint32_t handle;
+	uint32_t count;
+	uint32_t active;
+	uint32_t offset[DRM_KGSL_GEM_MAX_BUFFERS];
+	uint32_t gpuaddr[DRM_KGSL_GEM_MAX_BUFFERS];
+};
+
+struct drm_kgsl_gem_bufcount {
+	uint32_t handle;
+	uint32_t bufcount;
+};
+
+struct drm_kgsl_gem_active {
+	uint32_t handle;
+	uint32_t active;
+};
+
+struct drm_kgsl_gem_lock_handles {
+	uint32_t num_handles;
+	uint32_t *handle_list;
+	uint32_t pid;
+	uint32_t lock_id;	  /* Returned lock id used for unlocking */
+};
+
+struct drm_kgsl_gem_unlock_handles {
+	uint32_t lock_id;
+};
+
+struct drm_kgsl_gem_unlock_on_ts {
+	uint32_t lock_id;
+	uint32_t timestamp;	 /* This field is a hw generated ts */
+	uint32_t type;		 /* Which pipe to check for ts generation */
+};
+
+struct drm_kgsl_gem_create_fd {
+	uint32_t fd;
+	uint32_t handle;
+};
+
+#endif
diff --git a/freedreno/kgsl/kgsl_pipe.c b/freedreno/kgsl/kgsl_pipe.c
new file mode 100644
index 0000000..8a39eb4
--- /dev/null
+++ b/freedreno/kgsl/kgsl_pipe.c
@@ -0,0 +1,285 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "kgsl_priv.h"
+
+
+static int kgsl_pipe_get_param(struct fd_pipe *pipe,
+		enum fd_param_id param, uint64_t *value)
+{
+	struct kgsl_pipe *kgsl_pipe = to_kgsl_pipe(pipe);
+	switch (param) {
+	case FD_DEVICE_ID:
+		*value = kgsl_pipe->devinfo.device_id;
+		return 0;
+	case FD_GPU_ID:
+		*value = kgsl_pipe->devinfo.gpu_id;
+		return 0;
+	case FD_GMEM_SIZE:
+		*value = kgsl_pipe->devinfo.gmem_sizebytes;
+		return 0;
+	case FD_CHIP_ID:
+		*value = kgsl_pipe->devinfo.chip_id;
+		return 0;
+	case FD_MAX_FREQ:
+	case FD_TIMESTAMP:
+		/* unsupported on kgsl */
+		return -1;
+	default:
+		ERROR_MSG("invalid param id: %d", param);
+		return -1;
+	}
+}
+
+static int kgsl_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp,
+		uint64_t timeout)
+{
+	struct kgsl_pipe *kgsl_pipe = to_kgsl_pipe(pipe);
+	struct kgsl_device_waittimestamp req = {
+			.timestamp = timestamp,
+			.timeout   = 5000,
+	};
+	int ret;
+
+	do {
+		ret = ioctl(kgsl_pipe->fd, IOCTL_KGSL_DEVICE_WAITTIMESTAMP, &req);
+	} while ((ret == -1) && ((errno == EINTR) || (errno == EAGAIN)));
+	if (ret)
+		ERROR_MSG("waittimestamp failed! %d (%s)", ret, strerror(errno));
+	else
+		kgsl_pipe_process_pending(kgsl_pipe, timestamp);
+	return ret;
+}
+
+drm_private int kgsl_pipe_timestamp(struct kgsl_pipe *kgsl_pipe,
+		uint32_t *timestamp)
+{
+	struct kgsl_cmdstream_readtimestamp req = {
+			.type = KGSL_TIMESTAMP_RETIRED
+	};
+	int ret = ioctl(kgsl_pipe->fd, IOCTL_KGSL_CMDSTREAM_READTIMESTAMP, &req);
+	if (ret) {
+		ERROR_MSG("readtimestamp failed! %d (%s)",
+				ret, strerror(errno));
+		return ret;
+	}
+	*timestamp = req.timestamp;
+	return 0;
+}
+
+static void kgsl_pipe_destroy(struct fd_pipe *pipe)
+{
+	struct kgsl_pipe *kgsl_pipe = to_kgsl_pipe(pipe);
+	struct kgsl_drawctxt_destroy req = {
+			.drawctxt_id = kgsl_pipe->drawctxt_id,
+	};
+
+	if (kgsl_pipe->drawctxt_id)
+		ioctl(kgsl_pipe->fd, IOCTL_KGSL_DRAWCTXT_DESTROY, &req);
+
+	if (kgsl_pipe->fd >= 0)
+		close(kgsl_pipe->fd);
+
+	free(kgsl_pipe);
+}
+
+static const struct fd_pipe_funcs funcs = {
+		.ringbuffer_new = kgsl_ringbuffer_new,
+		.get_param = kgsl_pipe_get_param,
+		.wait = kgsl_pipe_wait,
+		.destroy = kgsl_pipe_destroy,
+};
+
+drm_private int is_kgsl_pipe(struct fd_pipe *pipe)
+{
+	return pipe->funcs == &funcs;
+}
+
+/* add buffer to submit list when it is referenced in cmdstream: */
+drm_private void kgsl_pipe_add_submit(struct kgsl_pipe *kgsl_pipe,
+		struct kgsl_bo *kgsl_bo)
+{
+	struct fd_pipe *pipe = &kgsl_pipe->base;
+	struct fd_bo *bo = &kgsl_bo->base;
+	struct list_head *list = &kgsl_bo->list[pipe->id];
+	if (LIST_IS_EMPTY(list)) {
+		fd_bo_ref(bo);
+	} else {
+		list_del(list);
+	}
+	list_addtail(list, &kgsl_pipe->submit_list);
+}
+
+/* prepare buffers on submit list before flush: */
+drm_private void kgsl_pipe_pre_submit(struct kgsl_pipe *kgsl_pipe)
+{
+	struct fd_pipe *pipe = &kgsl_pipe->base;
+	struct kgsl_bo *kgsl_bo = NULL;
+
+	if (!kgsl_pipe->p3d)
+		kgsl_pipe->p3d = fd_pipe_new(pipe->dev, FD_PIPE_3D);
+
+	LIST_FOR_EACH_ENTRY(kgsl_bo, &kgsl_pipe->submit_list, list[pipe->id]) {
+		uint32_t timestamp = kgsl_bo_get_timestamp(kgsl_bo);
+		if (timestamp)
+			fd_pipe_wait(kgsl_pipe->p3d, timestamp);
+	}
+}
+
+/* process buffers on submit list after flush: */
+drm_private void kgsl_pipe_post_submit(struct kgsl_pipe *kgsl_pipe,
+		uint32_t timestamp)
+{
+	struct fd_pipe *pipe = &kgsl_pipe->base;
+	struct kgsl_bo *kgsl_bo = NULL, *tmp;
+
+	LIST_FOR_EACH_ENTRY_SAFE(kgsl_bo, tmp, &kgsl_pipe->submit_list, list[pipe->id]) {
+		struct list_head *list = &kgsl_bo->list[pipe->id];
+		list_del(list);
+		kgsl_bo->timestamp[pipe->id] = timestamp;
+		list_addtail(list, &kgsl_pipe->pending_list);
+
+		kgsl_bo_set_timestamp(kgsl_bo, timestamp);
+	}
+
+	if (!kgsl_pipe_timestamp(kgsl_pipe, &timestamp))
+		kgsl_pipe_process_pending(kgsl_pipe, timestamp);
+}
+
+drm_private void kgsl_pipe_process_pending(struct kgsl_pipe *kgsl_pipe,
+		uint32_t timestamp)
+{
+	struct fd_pipe *pipe = &kgsl_pipe->base;
+	struct kgsl_bo *kgsl_bo = NULL, *tmp;
+
+	LIST_FOR_EACH_ENTRY_SAFE(kgsl_bo, tmp, &kgsl_pipe->pending_list, list[pipe->id]) {
+		struct list_head *list = &kgsl_bo->list[pipe->id];
+		if (kgsl_bo->timestamp[pipe->id] > timestamp)
+			return;
+		list_delinit(list);
+		kgsl_bo->timestamp[pipe->id] = 0;
+		fd_bo_del(&kgsl_bo->base);
+	}
+}
+
+static int getprop(int fd, enum kgsl_property_type type,
+		void *value, int sizebytes)
+{
+	struct kgsl_device_getproperty req = {
+			.type = type,
+			.value = value,
+			.sizebytes = sizebytes,
+	};
+	return ioctl(fd, IOCTL_KGSL_DEVICE_GETPROPERTY, &req);
+}
+
+#define GETPROP(fd, prop, x) do { \
+	if (getprop((fd), KGSL_PROP_##prop, &(x), sizeof(x))) {     \
+		ERROR_MSG("failed to get property: " #prop);            \
+		goto fail;                                              \
+	} } while (0)
+
+
+drm_private struct fd_pipe * kgsl_pipe_new(struct fd_device *dev,
+		enum fd_pipe_id id)
+{
+	static const char *paths[] = {
+			[FD_PIPE_3D] = "/dev/kgsl-3d0",
+			[FD_PIPE_2D] = "/dev/kgsl-2d0",
+	};
+	struct kgsl_drawctxt_create req = {
+			.flags = 0x2000, /* ??? */
+	};
+	struct kgsl_pipe *kgsl_pipe = NULL;
+	struct fd_pipe *pipe = NULL;
+	int ret, fd;
+
+	fd = open(paths[id], O_RDWR);
+	if (fd < 0) {
+		ERROR_MSG("could not open %s device: %d (%s)",
+				paths[id], fd, strerror(errno));
+		goto fail;
+	}
+
+	ret = ioctl(fd, IOCTL_KGSL_DRAWCTXT_CREATE, &req);
+	if (ret) {
+		ERROR_MSG("failed to allocate context: %d (%s)",
+				ret, strerror(errno));
+		goto fail;
+	}
+
+	kgsl_pipe = calloc(1, sizeof(*kgsl_pipe));
+	if (!kgsl_pipe) {
+		ERROR_MSG("allocation failed");
+		goto fail;
+	}
+
+	pipe = &kgsl_pipe->base;
+	pipe->funcs = &funcs;
+
+	kgsl_pipe->fd = fd;
+	kgsl_pipe->drawctxt_id = req.drawctxt_id;
+
+	list_inithead(&kgsl_pipe->submit_list);
+	list_inithead(&kgsl_pipe->pending_list);
+
+	GETPROP(fd, VERSION,     kgsl_pipe->version);
+	GETPROP(fd, DEVICE_INFO, kgsl_pipe->devinfo);
+
+	if (kgsl_pipe->devinfo.gpu_id >= 500) {
+		ERROR_MSG("64b unsupported with kgsl");
+		goto fail;
+	}
+
+	INFO_MSG("Pipe Info:");
+	INFO_MSG(" Device:          %s", paths[id]);
+	INFO_MSG(" Chip-id:         %d.%d.%d.%d",
+			(kgsl_pipe->devinfo.chip_id >> 24) & 0xff,
+			(kgsl_pipe->devinfo.chip_id >> 16) & 0xff,
+			(kgsl_pipe->devinfo.chip_id >>  8) & 0xff,
+			(kgsl_pipe->devinfo.chip_id >>  0) & 0xff);
+	INFO_MSG(" Device-id:       %d", kgsl_pipe->devinfo.device_id);
+	INFO_MSG(" GPU-id:          %d", kgsl_pipe->devinfo.gpu_id);
+	INFO_MSG(" MMU enabled:     %d", kgsl_pipe->devinfo.mmu_enabled);
+	INFO_MSG(" GMEM Base addr:  0x%08x", kgsl_pipe->devinfo.gmem_gpubaseaddr);
+	INFO_MSG(" GMEM size:       0x%08x", kgsl_pipe->devinfo.gmem_sizebytes);
+	INFO_MSG(" Driver version:  %d.%d",
+			kgsl_pipe->version.drv_major, kgsl_pipe->version.drv_minor);
+	INFO_MSG(" Device version:  %d.%d",
+			kgsl_pipe->version.dev_major, kgsl_pipe->version.dev_minor);
+
+	return pipe;
+fail:
+	if (pipe)
+		fd_pipe_del(pipe);
+	return NULL;
+}
diff --git a/freedreno/kgsl/kgsl_priv.h b/freedreno/kgsl/kgsl_priv.h
new file mode 100644
index 0000000..6ab6496
--- /dev/null
+++ b/freedreno/kgsl/kgsl_priv.h
@@ -0,0 +1,120 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifndef KGSL_PRIV_H_
+#define KGSL_PRIV_H_
+
+#include "freedreno_priv.h"
+#include "msm_kgsl.h"
+#include "kgsl_drm.h"
+
+struct kgsl_device {
+	struct fd_device base;
+};
+
+static inline struct kgsl_device * to_kgsl_device(struct fd_device *x)
+{
+	return (struct kgsl_device *)x;
+}
+
+struct kgsl_pipe {
+	struct fd_pipe base;
+
+	int fd;
+	uint32_t drawctxt_id;
+
+	/* device properties: */
+	struct kgsl_version version;
+	struct kgsl_devinfo devinfo;
+
+	/* list of bo's that are referenced in ringbuffer but not
+	 * submitted yet:
+	 */
+	struct list_head submit_list;
+
+	/* list of bo's that have been submitted but timestamp has
+	 * not passed yet (so still ref'd in active cmdstream)
+	 */
+	struct list_head pending_list;
+
+	/* if we are the 2d pipe, and want to wait on a timestamp
+	 * from 3d, we need to also internally open the 3d pipe:
+	 */
+	struct fd_pipe *p3d;
+};
+
+static inline struct kgsl_pipe * to_kgsl_pipe(struct fd_pipe *x)
+{
+	return (struct kgsl_pipe *)x;
+}
+
+drm_private int is_kgsl_pipe(struct fd_pipe *pipe);
+
+struct kgsl_bo {
+	struct fd_bo base;
+	uint64_t offset;
+	uint32_t gpuaddr;
+	/* timestamp (per pipe) for bo's in a pipe's pending_list: */
+	uint32_t timestamp[FD_PIPE_MAX];
+	/* list-node for pipe's submit_list or pending_list */
+	struct list_head list[FD_PIPE_MAX];
+};
+
+static inline struct kgsl_bo * to_kgsl_bo(struct fd_bo *x)
+{
+	return (struct kgsl_bo *)x;
+}
+
+
+drm_private struct fd_device * kgsl_device_new(int fd);
+
+drm_private int kgsl_pipe_timestamp(struct kgsl_pipe *kgsl_pipe,
+		uint32_t *timestamp);
+drm_private void kgsl_pipe_add_submit(struct kgsl_pipe *pipe,
+		struct kgsl_bo *bo);
+drm_private void kgsl_pipe_pre_submit(struct kgsl_pipe *pipe);
+drm_private void kgsl_pipe_post_submit(struct kgsl_pipe *pipe,
+		uint32_t timestamp);
+drm_private void kgsl_pipe_process_pending(struct kgsl_pipe *pipe,
+		uint32_t timestamp);
+drm_private struct fd_pipe * kgsl_pipe_new(struct fd_device *dev,
+		enum fd_pipe_id id);
+
+drm_private struct fd_ringbuffer * kgsl_ringbuffer_new(struct fd_pipe *pipe,
+		uint32_t size);
+
+drm_private int kgsl_bo_new_handle(struct fd_device *dev,
+		uint32_t size, uint32_t flags, uint32_t *handle);
+drm_private struct fd_bo * kgsl_bo_from_handle(struct fd_device *dev,
+		uint32_t size, uint32_t handle);
+
+drm_private uint32_t kgsl_bo_gpuaddr(struct kgsl_bo *bo, uint32_t offset);
+drm_private void kgsl_bo_set_timestamp(struct kgsl_bo *bo, uint32_t timestamp);
+drm_private uint32_t kgsl_bo_get_timestamp(struct kgsl_bo *bo);
+
+#endif /* KGSL_PRIV_H_ */
diff --git a/freedreno/kgsl/kgsl_ringbuffer.c b/freedreno/kgsl/kgsl_ringbuffer.c
new file mode 100644
index 0000000..f09c433
--- /dev/null
+++ b/freedreno/kgsl/kgsl_ringbuffer.c
@@ -0,0 +1,235 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+
+#include "freedreno_ringbuffer.h"
+#include "kgsl_priv.h"
+
+
+/* because kgsl tries to validate the gpuaddr on kernel side in ISSUEIBCMDS,
+ * we can't use normal gem bo's for ringbuffer..  someday the kernel part
+ * needs to be reworked into a single sane drm driver :-/
+ */
+struct kgsl_rb_bo {
+	struct kgsl_pipe *pipe;
+	void    *hostptr;
+	uint32_t gpuaddr;
+	uint32_t size;
+};
+
+struct kgsl_ringbuffer {
+	struct fd_ringbuffer base;
+	struct kgsl_rb_bo *bo;
+};
+
+static inline struct kgsl_ringbuffer * to_kgsl_ringbuffer(struct fd_ringbuffer *x)
+{
+	return (struct kgsl_ringbuffer *)x;
+}
+
+static void kgsl_rb_bo_del(struct kgsl_rb_bo *bo)
+{
+	struct kgsl_sharedmem_free req = {
+			.gpuaddr = bo->gpuaddr,
+	};
+	int ret;
+
+	drm_munmap(bo->hostptr, bo->size);
+
+	ret = ioctl(bo->pipe->fd, IOCTL_KGSL_SHAREDMEM_FREE, &req);
+	if (ret) {
+		ERROR_MSG("sharedmem free failed: %s", strerror(errno));
+	}
+
+	free(bo);
+}
+
+static struct kgsl_rb_bo * kgsl_rb_bo_new(struct kgsl_pipe *pipe, uint32_t size)
+{
+	struct kgsl_rb_bo *bo;
+	struct kgsl_gpumem_alloc req = {
+			.size = ALIGN(size, 4096),
+			.flags = KGSL_MEMFLAGS_GPUREADONLY,
+	};
+	int ret;
+
+	bo = calloc(1, sizeof(*bo));
+	if (!bo) {
+		ERROR_MSG("allocation failed");
+		return NULL;
+	}
+	ret = ioctl(pipe->fd, IOCTL_KGSL_GPUMEM_ALLOC, &req);
+	if (ret) {
+		ERROR_MSG("gpumem allocation failed: %s", strerror(errno));
+		goto fail;
+	}
+
+	bo->pipe = pipe;
+	bo->gpuaddr = req.gpuaddr;
+	bo->size = size;
+	bo->hostptr = drm_mmap(NULL, size, PROT_WRITE|PROT_READ,
+				MAP_SHARED, pipe->fd, req.gpuaddr);
+
+	return bo;
+fail:
+	if (bo)
+		kgsl_rb_bo_del(bo);
+	return NULL;
+}
+
+static void * kgsl_ringbuffer_hostptr(struct fd_ringbuffer *ring)
+{
+	struct kgsl_ringbuffer *kgsl_ring = to_kgsl_ringbuffer(ring);
+	return kgsl_ring->bo->hostptr;
+}
+
+static int kgsl_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start,
+		int in_fence_fd, int *out_fence_fd)
+{
+	struct kgsl_ringbuffer *kgsl_ring = to_kgsl_ringbuffer(ring);
+	struct kgsl_pipe *kgsl_pipe = to_kgsl_pipe(ring->pipe);
+	uint32_t offset = (uint8_t *)last_start - (uint8_t *)ring->start;
+	struct kgsl_ibdesc ibdesc = {
+			.gpuaddr     = kgsl_ring->bo->gpuaddr + offset,
+			.hostptr     = last_start,
+			.sizedwords  = ring->cur - last_start,
+	};
+	struct kgsl_ringbuffer_issueibcmds req = {
+			.drawctxt_id = kgsl_pipe->drawctxt_id,
+			.ibdesc_addr = (unsigned long)&ibdesc,
+			.numibs      = 1,
+			.flags       = KGSL_CONTEXT_SUBMIT_IB_LIST,
+	};
+	int ret;
+
+	assert(in_fence_fd == -1);
+	assert(out_fence_fd == NULL);
+
+	kgsl_pipe_pre_submit(kgsl_pipe);
+
+	/* z180_cmdstream_issueibcmds() is made of fail: */
+	if (ring->pipe->id == FD_PIPE_2D) {
+		/* fix up size field in last cmd packet */
+		uint32_t last_size = (uint32_t)(ring->cur - last_start);
+		/* 5 is length of first packet, 2 for the two 7f000000's */
+		last_start[2] = last_size - (5 + 2);
+		ibdesc.gpuaddr = kgsl_ring->bo->gpuaddr;
+		ibdesc.hostptr = kgsl_ring->bo->hostptr;
+		ibdesc.sizedwords = 0x145;
+		req.timestamp = (uintptr_t)kgsl_ring->bo->hostptr;
+	}
+
+	do {
+		ret = ioctl(kgsl_pipe->fd, IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS, &req);
+	} while ((ret == -1) && ((errno == EINTR) || (errno == EAGAIN)));
+	if (ret)
+		ERROR_MSG("issueibcmds failed!  %d (%s)", ret, strerror(errno));
+
+	ring->last_timestamp = req.timestamp;
+	ring->last_start = ring->cur;
+
+	kgsl_pipe_post_submit(kgsl_pipe, req.timestamp);
+
+	return ret;
+}
+
+static void kgsl_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
+		const struct fd_reloc *r)
+{
+	struct kgsl_bo *kgsl_bo = to_kgsl_bo(r->bo);
+	uint32_t addr = kgsl_bo_gpuaddr(kgsl_bo, r->offset);
+	assert(addr);
+	if (r->shift < 0)
+		addr >>= -r->shift;
+	else
+		addr <<= r->shift;
+	(*ring->cur++) = addr | r->or;
+	kgsl_pipe_add_submit(to_kgsl_pipe(ring->pipe), kgsl_bo);
+}
+
+static uint32_t kgsl_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
+		struct fd_ringbuffer *target, uint32_t cmd_idx,
+		uint32_t submit_offset, uint32_t size)
+{
+	struct kgsl_ringbuffer *target_ring = to_kgsl_ringbuffer(target);
+	assert(cmd_idx == 0);
+	(*ring->cur++) = target_ring->bo->gpuaddr + submit_offset;
+	return size;
+}
+
+static void kgsl_ringbuffer_destroy(struct fd_ringbuffer *ring)
+{
+	struct kgsl_ringbuffer *kgsl_ring = to_kgsl_ringbuffer(ring);
+	if (ring->last_timestamp)
+		fd_pipe_wait(ring->pipe, ring->last_timestamp);
+	if (kgsl_ring->bo)
+		kgsl_rb_bo_del(kgsl_ring->bo);
+	free(kgsl_ring);
+}
+
+static const struct fd_ringbuffer_funcs funcs = {
+		.hostptr = kgsl_ringbuffer_hostptr,
+		.flush = kgsl_ringbuffer_flush,
+		.emit_reloc = kgsl_ringbuffer_emit_reloc,
+		.emit_reloc_ring = kgsl_ringbuffer_emit_reloc_ring,
+		.destroy = kgsl_ringbuffer_destroy,
+};
+
+drm_private struct fd_ringbuffer * kgsl_ringbuffer_new(struct fd_pipe *pipe,
+		uint32_t size)
+{
+	struct kgsl_ringbuffer *kgsl_ring;
+	struct fd_ringbuffer *ring = NULL;
+
+	kgsl_ring = calloc(1, sizeof(*kgsl_ring));
+	if (!kgsl_ring) {
+		ERROR_MSG("allocation failed");
+		goto fail;
+	}
+
+	ring = &kgsl_ring->base;
+	ring->funcs = &funcs;
+	ring->size = size;
+
+	kgsl_ring->bo = kgsl_rb_bo_new(to_kgsl_pipe(pipe), size);
+	if (!kgsl_ring->bo) {
+		ERROR_MSG("ringbuffer allocation failed");
+		goto fail;
+	}
+
+	return ring;
+fail:
+	if (ring)
+		fd_ringbuffer_del(ring);
+	return NULL;
+}
diff --git a/freedreno/kgsl/msm_kgsl.h b/freedreno/kgsl/msm_kgsl.h
new file mode 100644
index 0000000..5b36eeb
--- /dev/null
+++ b/freedreno/kgsl/msm_kgsl.h
@@ -0,0 +1,519 @@
+#ifndef _MSM_KGSL_H
+#define _MSM_KGSL_H
+
+#define KGSL_VERSION_MAJOR        3
+#define KGSL_VERSION_MINOR        11
+
+/*context flags */
+#define KGSL_CONTEXT_SAVE_GMEM		0x00000001
+#define KGSL_CONTEXT_NO_GMEM_ALLOC	0x00000002
+#define KGSL_CONTEXT_SUBMIT_IB_LIST	0x00000004
+#define KGSL_CONTEXT_CTX_SWITCH		0x00000008
+#define KGSL_CONTEXT_PREAMBLE		0x00000010
+#define KGSL_CONTEXT_TRASH_STATE	0x00000020
+#define KGSL_CONTEXT_PER_CONTEXT_TS	0x00000040
+
+#define KGSL_CONTEXT_INVALID 0xffffffff
+
+/* Memory allocayion flags */
+#define KGSL_MEMFLAGS_GPUREADONLY	0x01000000
+
+/* generic flag values */
+#define KGSL_FLAGS_NORMALMODE  0x00000000
+#define KGSL_FLAGS_SAFEMODE    0x00000001
+#define KGSL_FLAGS_INITIALIZED0 0x00000002
+#define KGSL_FLAGS_INITIALIZED 0x00000004
+#define KGSL_FLAGS_STARTED     0x00000008
+#define KGSL_FLAGS_ACTIVE      0x00000010
+#define KGSL_FLAGS_RESERVED0   0x00000020
+#define KGSL_FLAGS_RESERVED1   0x00000040
+#define KGSL_FLAGS_RESERVED2   0x00000080
+#define KGSL_FLAGS_SOFT_RESET  0x00000100
+#define KGSL_FLAGS_PER_CONTEXT_TIMESTAMPS 0x00000200
+
+/* Clock flags to show which clocks should be controlled by a given platform */
+#define KGSL_CLK_SRC	0x00000001
+#define KGSL_CLK_CORE	0x00000002
+#define KGSL_CLK_IFACE	0x00000004
+#define KGSL_CLK_MEM	0x00000008
+#define KGSL_CLK_MEM_IFACE 0x00000010
+#define KGSL_CLK_AXI	0x00000020
+
+/*
+ * Reset status values for context
+ */
+enum kgsl_ctx_reset_stat {
+	KGSL_CTX_STAT_NO_ERROR				= 0x00000000,
+	KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT		= 0x00000001,
+	KGSL_CTX_STAT_INNOCENT_CONTEXT_RESET_EXT	= 0x00000002,
+	KGSL_CTX_STAT_UNKNOWN_CONTEXT_RESET_EXT		= 0x00000003
+};
+
+#define KGSL_MAX_PWRLEVELS 5
+
+#define KGSL_CONVERT_TO_MBPS(val) \
+	(val*1000*1000U)
+
+/* device id */
+enum kgsl_deviceid {
+	KGSL_DEVICE_3D0		= 0x00000000,
+	KGSL_DEVICE_2D0		= 0x00000001,
+	KGSL_DEVICE_2D1		= 0x00000002,
+	KGSL_DEVICE_MAX		= 0x00000003
+};
+
+enum kgsl_user_mem_type {
+	KGSL_USER_MEM_TYPE_PMEM		= 0x00000000,
+	KGSL_USER_MEM_TYPE_ASHMEM	= 0x00000001,
+	KGSL_USER_MEM_TYPE_ADDR		= 0x00000002,
+	KGSL_USER_MEM_TYPE_ION		= 0x00000003,
+	KGSL_USER_MEM_TYPE_MAX		= 0x00000004,
+};
+
+struct kgsl_devinfo {
+
+	unsigned int device_id;
+	/* chip revision id
+	* coreid:8 majorrev:8 minorrev:8 patch:8
+	*/
+	unsigned int chip_id;
+	unsigned int mmu_enabled;
+	unsigned int gmem_gpubaseaddr;
+	/*
+	* This field contains the adreno revision
+	* number 200, 205, 220, etc...
+	*/
+	unsigned int gpu_id;
+	unsigned int gmem_sizebytes;
+};
+
+/* this structure defines the region of memory that can be mmap()ed from this
+   driver. The timestamp fields are volatile because they are written by the
+   GPU
+*/
+struct kgsl_devmemstore {
+	volatile unsigned int soptimestamp;
+	unsigned int sbz;
+	volatile unsigned int eoptimestamp;
+	unsigned int sbz2;
+	volatile unsigned int ts_cmp_enable;
+	unsigned int sbz3;
+	volatile unsigned int ref_wait_ts;
+	unsigned int sbz4;
+	unsigned int current_context;
+	unsigned int sbz5;
+};
+
+#define KGSL_MEMSTORE_OFFSET(ctxt_id, field) \
+	((ctxt_id)*sizeof(struct kgsl_devmemstore) + \
+	 offsetof(struct kgsl_devmemstore, field))
+
+/* timestamp id*/
+enum kgsl_timestamp_type {
+	KGSL_TIMESTAMP_CONSUMED = 0x00000001, /* start-of-pipeline timestamp */
+	KGSL_TIMESTAMP_RETIRED  = 0x00000002, /* end-of-pipeline timestamp*/
+	KGSL_TIMESTAMP_QUEUED   = 0x00000003,
+};
+
+/* property types - used with kgsl_device_getproperty */
+enum kgsl_property_type {
+	KGSL_PROP_DEVICE_INFO     = 0x00000001,
+	KGSL_PROP_DEVICE_SHADOW   = 0x00000002,
+	KGSL_PROP_DEVICE_POWER    = 0x00000003,
+	KGSL_PROP_SHMEM           = 0x00000004,
+	KGSL_PROP_SHMEM_APERTURES = 0x00000005,
+	KGSL_PROP_MMU_ENABLE 	  = 0x00000006,
+	KGSL_PROP_INTERRUPT_WAITS = 0x00000007,
+	KGSL_PROP_VERSION         = 0x00000008,
+	KGSL_PROP_GPU_RESET_STAT  = 0x00000009,
+	KGSL_PROP_PWRCTRL         = 0x0000000E,
+};
+
+struct kgsl_shadowprop {
+	unsigned int gpuaddr;
+	unsigned int size;
+	unsigned int flags; /* contains KGSL_FLAGS_ values */
+};
+
+struct kgsl_pwrlevel {
+	unsigned int gpu_freq;
+	unsigned int bus_freq;
+	unsigned int io_fraction;
+};
+
+struct kgsl_version {
+	unsigned int drv_major;
+	unsigned int drv_minor;
+	unsigned int dev_major;
+	unsigned int dev_minor;
+};
+
+#ifdef __KERNEL__
+
+#define KGSL_3D0_REG_MEMORY	"kgsl_3d0_reg_memory"
+#define KGSL_3D0_IRQ		"kgsl_3d0_irq"
+#define KGSL_2D0_REG_MEMORY	"kgsl_2d0_reg_memory"
+#define KGSL_2D0_IRQ		"kgsl_2d0_irq"
+#define KGSL_2D1_REG_MEMORY	"kgsl_2d1_reg_memory"
+#define KGSL_2D1_IRQ		"kgsl_2d1_irq"
+
+enum kgsl_iommu_context_id {
+	KGSL_IOMMU_CONTEXT_USER = 0,
+	KGSL_IOMMU_CONTEXT_PRIV = 1,
+};
+
+struct kgsl_iommu_ctx {
+	const char *iommu_ctx_name;
+	enum kgsl_iommu_context_id ctx_id;
+};
+
+struct kgsl_device_iommu_data {
+	const struct kgsl_iommu_ctx *iommu_ctxs;
+	int iommu_ctx_count;
+	unsigned int physstart;
+	unsigned int physend;
+};
+
+struct kgsl_device_platform_data {
+	struct kgsl_pwrlevel pwrlevel[KGSL_MAX_PWRLEVELS];
+	int init_level;
+	int num_levels;
+	int (*set_grp_async)(void);
+	unsigned int idle_timeout;
+	bool strtstp_sleepwake;
+	unsigned int nap_allowed;
+	unsigned int clk_map;
+	unsigned int idle_needed;
+	struct msm_bus_scale_pdata *bus_scale_table;
+	struct kgsl_device_iommu_data *iommu_data;
+	int iommu_count;
+	struct msm_dcvs_core_info *core_info;
+};
+
+#endif
+
+/* structure holds list of ibs */
+struct kgsl_ibdesc {
+	unsigned int gpuaddr;
+	void *hostptr;
+	unsigned int sizedwords;
+	unsigned int ctrl;
+};
+
+/* ioctls */
+#define KGSL_IOC_TYPE 0x09
+
+/* get misc info about the GPU
+   type should be a value from enum kgsl_property_type
+   value points to a structure that varies based on type
+   sizebytes is sizeof() that structure
+   for KGSL_PROP_DEVICE_INFO, use struct kgsl_devinfo
+   this structure contaings hardware versioning info.
+   for KGSL_PROP_DEVICE_SHADOW, use struct kgsl_shadowprop
+   this is used to find mmap() offset and sizes for mapping
+   struct kgsl_memstore into userspace.
+*/
+struct kgsl_device_getproperty {
+	unsigned int type;
+	void  *value;
+	unsigned int sizebytes;
+};
+
+#define IOCTL_KGSL_DEVICE_GETPROPERTY \
+	_IOWR(KGSL_IOC_TYPE, 0x2, struct kgsl_device_getproperty)
+
+/* IOCTL_KGSL_DEVICE_READ (0x3) - removed 03/2012
+ */
+
+/* block until the GPU has executed past a given timestamp
+ * timeout is in milliseconds.
+ */
+struct kgsl_device_waittimestamp {
+	unsigned int timestamp;
+	unsigned int timeout;
+};
+
+#define IOCTL_KGSL_DEVICE_WAITTIMESTAMP \
+	_IOW(KGSL_IOC_TYPE, 0x6, struct kgsl_device_waittimestamp)
+
+struct kgsl_device_waittimestamp_ctxtid {
+	unsigned int context_id;
+	unsigned int timestamp;
+	unsigned int timeout;
+};
+
+#define IOCTL_KGSL_DEVICE_WAITTIMESTAMP_CTXTID \
+	_IOW(KGSL_IOC_TYPE, 0x7, struct kgsl_device_waittimestamp_ctxtid)
+
+/* issue indirect commands to the GPU.
+ * drawctxt_id must have been created with IOCTL_KGSL_DRAWCTXT_CREATE
+ * ibaddr and sizedwords must specify a subset of a buffer created
+ * with IOCTL_KGSL_SHAREDMEM_FROM_PMEM
+ * flags may be a mask of KGSL_CONTEXT_ values
+ * timestamp is a returned counter value which can be passed to
+ * other ioctls to determine when the commands have been executed by
+ * the GPU.
+ */
+struct kgsl_ringbuffer_issueibcmds {
+	unsigned int drawctxt_id;
+	unsigned int ibdesc_addr;
+	unsigned int numibs;
+	unsigned int timestamp; /*output param */
+	unsigned int flags;
+};
+
+#define IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS \
+	_IOWR(KGSL_IOC_TYPE, 0x10, struct kgsl_ringbuffer_issueibcmds)
+
+/* read the most recently executed timestamp value
+ * type should be a value from enum kgsl_timestamp_type
+ */
+struct kgsl_cmdstream_readtimestamp {
+	unsigned int type;
+	unsigned int timestamp; /*output param */
+};
+
+#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_OLD \
+	_IOR(KGSL_IOC_TYPE, 0x11, struct kgsl_cmdstream_readtimestamp)
+
+#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP \
+	_IOWR(KGSL_IOC_TYPE, 0x11, struct kgsl_cmdstream_readtimestamp)
+
+/* free memory when the GPU reaches a given timestamp.
+ * gpuaddr specify a memory region created by a
+ * IOCTL_KGSL_SHAREDMEM_FROM_PMEM call
+ * type should be a value from enum kgsl_timestamp_type
+ */
+struct kgsl_cmdstream_freememontimestamp {
+	unsigned int gpuaddr;
+	unsigned int type;
+	unsigned int timestamp;
+};
+
+#define IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP \
+	_IOW(KGSL_IOC_TYPE, 0x12, struct kgsl_cmdstream_freememontimestamp)
+
+/* Previous versions of this header had incorrectly defined
+   IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP as a read-only ioctl instead
+   of a write only ioctl.  To ensure binary compatibility, the following
+   #define will be used to intercept the incorrect ioctl
+*/
+
+#define IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP_OLD \
+	_IOR(KGSL_IOC_TYPE, 0x12, struct kgsl_cmdstream_freememontimestamp)
+
+/* create a draw context, which is used to preserve GPU state.
+ * The flags field may contain a mask KGSL_CONTEXT_*  values
+ */
+struct kgsl_drawctxt_create {
+	unsigned int flags;
+	unsigned int drawctxt_id; /*output param */
+};
+
+#define IOCTL_KGSL_DRAWCTXT_CREATE \
+	_IOWR(KGSL_IOC_TYPE, 0x13, struct kgsl_drawctxt_create)
+
+/* destroy a draw context */
+struct kgsl_drawctxt_destroy {
+	unsigned int drawctxt_id;
+};
+
+#define IOCTL_KGSL_DRAWCTXT_DESTROY \
+	_IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy)
+
+/* add a block of pmem, fb, ashmem or user allocated address
+ * into the GPU address space */
+struct kgsl_map_user_mem {
+	int fd;
+	unsigned int gpuaddr;   /*output param */
+	unsigned int len;
+	unsigned int offset;
+	unsigned int hostptr;   /*input param */
+	enum kgsl_user_mem_type memtype;
+	unsigned int reserved;	/* May be required to add
+				params for another mem type */
+};
+
+#define IOCTL_KGSL_MAP_USER_MEM \
+	_IOWR(KGSL_IOC_TYPE, 0x15, struct kgsl_map_user_mem)
+
+struct kgsl_cmdstream_readtimestamp_ctxtid {
+	unsigned int context_id;
+	unsigned int type;
+	unsigned int timestamp; /*output param */
+};
+
+#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_CTXTID \
+	_IOWR(KGSL_IOC_TYPE, 0x16, struct kgsl_cmdstream_readtimestamp_ctxtid)
+
+struct kgsl_cmdstream_freememontimestamp_ctxtid {
+	unsigned int context_id;
+	unsigned int gpuaddr;
+	unsigned int type;
+	unsigned int timestamp;
+};
+
+#define IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP_CTXTID \
+	_IOW(KGSL_IOC_TYPE, 0x17, \
+	struct kgsl_cmdstream_freememontimestamp_ctxtid)
+
+/* add a block of pmem or fb into the GPU address space */
+struct kgsl_sharedmem_from_pmem {
+	int pmem_fd;
+	unsigned int gpuaddr;	/*output param */
+	unsigned int len;
+	unsigned int offset;
+};
+
+#define IOCTL_KGSL_SHAREDMEM_FROM_PMEM \
+	_IOWR(KGSL_IOC_TYPE, 0x20, struct kgsl_sharedmem_from_pmem)
+
+/* remove memory from the GPU's address space */
+struct kgsl_sharedmem_free {
+	unsigned int gpuaddr;
+};
+
+#define IOCTL_KGSL_SHAREDMEM_FREE \
+	_IOW(KGSL_IOC_TYPE, 0x21, struct kgsl_sharedmem_free)
+
+struct kgsl_cff_user_event {
+	unsigned char cff_opcode;
+	unsigned int op1;
+	unsigned int op2;
+	unsigned int op3;
+	unsigned int op4;
+	unsigned int op5;
+	unsigned int __pad[2];
+};
+
+#define IOCTL_KGSL_CFF_USER_EVENT \
+	_IOW(KGSL_IOC_TYPE, 0x31, struct kgsl_cff_user_event)
+
+struct kgsl_gmem_desc {
+	unsigned int x;
+	unsigned int y;
+	unsigned int width;
+	unsigned int height;
+	unsigned int pitch;
+};
+
+struct kgsl_buffer_desc {
+	void 			*hostptr;
+	unsigned int	gpuaddr;
+	int				size;
+	unsigned int	format;
+	unsigned int  	pitch;
+	unsigned int  	enabled;
+};
+
+struct kgsl_bind_gmem_shadow {
+	unsigned int drawctxt_id;
+	struct kgsl_gmem_desc gmem_desc;
+	unsigned int shadow_x;
+	unsigned int shadow_y;
+	struct kgsl_buffer_desc shadow_buffer;
+	unsigned int buffer_id;
+};
+
+#define IOCTL_KGSL_DRAWCTXT_BIND_GMEM_SHADOW \
+    _IOW(KGSL_IOC_TYPE, 0x22, struct kgsl_bind_gmem_shadow)
+
+/* add a block of memory into the GPU address space */
+struct kgsl_sharedmem_from_vmalloc {
+	unsigned int gpuaddr;	/*output param */
+	unsigned int hostptr;
+	unsigned int flags;
+};
+
+#define IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC \
+	_IOWR(KGSL_IOC_TYPE, 0x23, struct kgsl_sharedmem_from_vmalloc)
+
+#define IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE \
+	_IOW(KGSL_IOC_TYPE, 0x24, struct kgsl_sharedmem_free)
+
+struct kgsl_drawctxt_set_bin_base_offset {
+	unsigned int drawctxt_id;
+	unsigned int offset;
+};
+
+#define IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET \
+	_IOW(KGSL_IOC_TYPE, 0x25, struct kgsl_drawctxt_set_bin_base_offset)
+
+enum kgsl_cmdwindow_type {
+	KGSL_CMDWINDOW_MIN     = 0x00000000,
+	KGSL_CMDWINDOW_2D      = 0x00000000,
+	KGSL_CMDWINDOW_3D      = 0x00000001, /* legacy */
+	KGSL_CMDWINDOW_MMU     = 0x00000002,
+	KGSL_CMDWINDOW_ARBITER = 0x000000FF,
+	KGSL_CMDWINDOW_MAX     = 0x000000FF,
+};
+
+/* write to the command window */
+struct kgsl_cmdwindow_write {
+	enum kgsl_cmdwindow_type target;
+	unsigned int addr;
+	unsigned int data;
+};
+
+#define IOCTL_KGSL_CMDWINDOW_WRITE \
+	_IOW(KGSL_IOC_TYPE, 0x2e, struct kgsl_cmdwindow_write)
+
+struct kgsl_gpumem_alloc {
+	unsigned long gpuaddr;
+	size_t size;
+	unsigned int flags;
+};
+
+#define IOCTL_KGSL_GPUMEM_ALLOC \
+	_IOWR(KGSL_IOC_TYPE, 0x2f, struct kgsl_gpumem_alloc)
+
+struct kgsl_cff_syncmem {
+	unsigned int gpuaddr;
+	unsigned int len;
+	unsigned int __pad[2]; /* For future binary compatibility */
+};
+
+#define IOCTL_KGSL_CFF_SYNCMEM \
+	_IOW(KGSL_IOC_TYPE, 0x30, struct kgsl_cff_syncmem)
+
+/*
+ * A timestamp event allows the user space to register an action following an
+ * expired timestamp.
+ */
+
+struct kgsl_timestamp_event {
+	int type;                /* Type of event (see list below) */
+	unsigned int timestamp;  /* Timestamp to trigger event on */
+	unsigned int context_id; /* Context for the timestamp */
+	void *priv;              /* Pointer to the event specific blob */
+	size_t len;              /* Size of the event specific blob */
+};
+
+#define IOCTL_KGSL_TIMESTAMP_EVENT \
+	_IOW(KGSL_IOC_TYPE, 0x31, struct kgsl_timestamp_event)
+
+/* A genlock timestamp event releases an existing lock on timestamp expire */
+
+#define KGSL_TIMESTAMP_EVENT_GENLOCK 1
+
+struct kgsl_timestamp_event_genlock {
+	int handle; /* Handle of the genlock lock to release */
+};
+
+/*
+ * Set a property within the kernel.  Uses the same structure as
+ * IOCTL_KGSL_GETPROPERTY
+ */
+
+#define IOCTL_KGSL_SETPROPERTY \
+	_IOW(KGSL_IOC_TYPE, 0x32, struct kgsl_device_getproperty)
+
+#ifdef __KERNEL__
+#ifdef CONFIG_MSM_KGSL_DRM
+int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start,
+			unsigned long *len);
+#else
+#define kgsl_gem_obj_addr(...) 0
+#endif
+#endif
+#endif /* _MSM_KGSL_H */
diff --git a/freedreno/libdrm_freedreno.pc.in b/freedreno/libdrm_freedreno.pc.in
new file mode 100644
index 0000000..b736b65
--- /dev/null
+++ b/freedreno/libdrm_freedreno.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_freedreno
+Description: Userspace interface to freedreno kernel DRM services
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -ldrm_freedreno
+Cflags: -I${includedir} -I${includedir}/libdrm -I${includedir}/freedreno
+Requires.private: libdrm
diff --git a/freedreno/msm/msm_bo.c b/freedreno/msm/msm_bo.c
new file mode 100644
index 0000000..72471df
--- /dev/null
+++ b/freedreno/msm/msm_bo.c
@@ -0,0 +1,161 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "msm_priv.h"
+
+static int bo_allocate(struct msm_bo *msm_bo)
+{
+	struct fd_bo *bo = &msm_bo->base;
+	if (!msm_bo->offset) {
+		struct drm_msm_gem_info req = {
+				.handle = bo->handle,
+		};
+		int ret;
+
+		/* if the buffer is already backed by pages then this
+		 * doesn't actually do anything (other than giving us
+		 * the offset)
+		 */
+		ret = drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_INFO,
+				&req, sizeof(req));
+		if (ret) {
+			ERROR_MSG("alloc failed: %s", strerror(errno));
+			return ret;
+		}
+
+		msm_bo->offset = req.offset;
+	}
+
+	return 0;
+}
+
+static int msm_bo_offset(struct fd_bo *bo, uint64_t *offset)
+{
+	struct msm_bo *msm_bo = to_msm_bo(bo);
+	int ret = bo_allocate(msm_bo);
+	if (ret)
+		return ret;
+	*offset = msm_bo->offset;
+	return 0;
+}
+
+static int msm_bo_cpu_prep(struct fd_bo *bo, struct fd_pipe *pipe, uint32_t op)
+{
+	struct drm_msm_gem_cpu_prep req = {
+			.handle = bo->handle,
+			.op = op,
+	};
+
+	get_abs_timeout(&req.timeout, 5000000000);
+
+	return drmCommandWrite(bo->dev->fd, DRM_MSM_GEM_CPU_PREP, &req, sizeof(req));
+}
+
+static void msm_bo_cpu_fini(struct fd_bo *bo)
+{
+	struct drm_msm_gem_cpu_fini req = {
+			.handle = bo->handle,
+	};
+
+	drmCommandWrite(bo->dev->fd, DRM_MSM_GEM_CPU_FINI, &req, sizeof(req));
+}
+
+static int msm_bo_madvise(struct fd_bo *bo, int willneed)
+{
+	struct drm_msm_gem_madvise req = {
+			.handle = bo->handle,
+			.madv = willneed ? MSM_MADV_WILLNEED : MSM_MADV_DONTNEED,
+	};
+	int ret;
+
+	/* older kernels do not support this: */
+	if (bo->dev->version < FD_VERSION_MADVISE)
+		return willneed;
+
+	ret = drmCommandWriteRead(bo->dev->fd, DRM_MSM_GEM_MADVISE, &req, sizeof(req));
+	if (ret)
+		return ret;
+
+	return req.retained;
+}
+
+static void msm_bo_destroy(struct fd_bo *bo)
+{
+	struct msm_bo *msm_bo = to_msm_bo(bo);
+	free(msm_bo);
+
+}
+
+static const struct fd_bo_funcs funcs = {
+		.offset = msm_bo_offset,
+		.cpu_prep = msm_bo_cpu_prep,
+		.cpu_fini = msm_bo_cpu_fini,
+		.madvise = msm_bo_madvise,
+		.destroy = msm_bo_destroy,
+};
+
+/* allocate a buffer handle: */
+drm_private int msm_bo_new_handle(struct fd_device *dev,
+		uint32_t size, uint32_t flags, uint32_t *handle)
+{
+	struct drm_msm_gem_new req = {
+			.size = size,
+			.flags = MSM_BO_WC,  // TODO figure out proper flags..
+	};
+	int ret;
+
+	ret = drmCommandWriteRead(dev->fd, DRM_MSM_GEM_NEW,
+			&req, sizeof(req));
+	if (ret)
+		return ret;
+
+	*handle = req.handle;
+
+	return 0;
+}
+
+/* allocate a new buffer object */
+drm_private struct fd_bo * msm_bo_from_handle(struct fd_device *dev,
+		uint32_t size, uint32_t handle)
+{
+	struct msm_bo *msm_bo;
+	struct fd_bo *bo;
+
+	msm_bo = calloc(1, sizeof(*msm_bo));
+	if (!msm_bo)
+		return NULL;
+
+	bo = &msm_bo->base;
+	bo->funcs = &funcs;
+
+	return bo;
+}
diff --git a/freedreno/msm/msm_device.c b/freedreno/msm/msm_device.c
new file mode 100644
index 0000000..c454938
--- /dev/null
+++ b/freedreno/msm/msm_device.c
@@ -0,0 +1,70 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "msm_priv.h"
+
+static void msm_device_destroy(struct fd_device *dev)
+{
+	struct msm_device *msm_dev = to_msm_device(dev);
+	fd_bo_cache_cleanup(&msm_dev->ring_cache, 0);
+	free(msm_dev);
+}
+
+static const struct fd_device_funcs funcs = {
+		.bo_new_handle = msm_bo_new_handle,
+		.bo_from_handle = msm_bo_from_handle,
+		.pipe_new = msm_pipe_new,
+		.destroy = msm_device_destroy,
+};
+
+drm_private struct fd_device * msm_device_new(int fd)
+{
+	struct msm_device *msm_dev;
+	struct fd_device *dev;
+
+	msm_dev = calloc(1, sizeof(*msm_dev));
+	if (!msm_dev)
+		return NULL;
+
+	dev = &msm_dev->base;
+	dev->funcs = &funcs;
+
+	fd_bo_cache_init(&msm_dev->ring_cache, TRUE);
+
+	dev->bo_size = sizeof(struct msm_bo);
+
+	return dev;
+}
diff --git a/freedreno/msm/msm_drm.h b/freedreno/msm/msm_drm.h
new file mode 100644
index 0000000..ed4c8d4
--- /dev/null
+++ b/freedreno/msm/msm_drm.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * 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 __MSM_DRM_H__
+#define __MSM_DRM_H__
+
+#include <stddef.h>
+#include "drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Please note that modifications to all structs defined here are
+ * subject to backwards-compatibility constraints:
+ *  1) Do not use pointers, use __u64 instead for 32 bit / 64 bit
+ *     user/kernel compatibility
+ *  2) Keep fields aligned to their size
+ *  3) Because of how drm_ioctl() works, we can add new fields at
+ *     the end of an ioctl if some care is taken: drm_ioctl() will
+ *     zero out the new fields at the tail of the ioctl, so a zero
+ *     value should have a backwards compatible meaning.  And for
+ *     output params, userspace won't see the newly added output
+ *     fields.. so that has to be somehow ok.
+ */
+
+#define MSM_PIPE_NONE        0x00
+#define MSM_PIPE_2D0         0x01
+#define MSM_PIPE_2D1         0x02
+#define MSM_PIPE_3D0         0x10
+
+/* The pipe-id just uses the lower bits, so can be OR'd with flags in
+ * the upper 16 bits (which could be extended further, if needed, maybe
+ * we extend/overload the pipe-id some day to deal with multiple rings,
+ * but even then I don't think we need the full lower 16 bits).
+ */
+#define MSM_PIPE_ID_MASK     0xffff
+#define MSM_PIPE_ID(x)       ((x) & MSM_PIPE_ID_MASK)
+#define MSM_PIPE_FLAGS(x)    ((x) & ~MSM_PIPE_ID_MASK)
+
+/* timeouts are specified in clock-monotonic absolute times (to simplify
+ * restarting interrupted ioctls).  The following struct is logically the
+ * same as 'struct timespec' but 32/64b ABI safe.
+ */
+struct drm_msm_timespec {
+	__s64 tv_sec;          /* seconds */
+	__s64 tv_nsec;         /* nanoseconds */
+};
+
+#define MSM_PARAM_GPU_ID     0x01
+#define MSM_PARAM_GMEM_SIZE  0x02
+#define MSM_PARAM_CHIP_ID    0x03
+#define MSM_PARAM_MAX_FREQ   0x04
+#define MSM_PARAM_TIMESTAMP  0x05
+
+struct drm_msm_param {
+	__u32 pipe;           /* in, MSM_PIPE_x */
+	__u32 param;          /* in, MSM_PARAM_x */
+	__u64 value;          /* out (get_param) or in (set_param) */
+};
+
+/*
+ * GEM buffers:
+ */
+
+#define MSM_BO_SCANOUT       0x00000001     /* scanout capable */
+#define MSM_BO_GPU_READONLY  0x00000002
+#define MSM_BO_CACHE_MASK    0x000f0000
+/* cache modes */
+#define MSM_BO_CACHED        0x00010000
+#define MSM_BO_WC            0x00020000
+#define MSM_BO_UNCACHED      0x00040000
+
+#define MSM_BO_FLAGS         (MSM_BO_SCANOUT | \
+                              MSM_BO_GPU_READONLY | \
+                              MSM_BO_CACHED | \
+                              MSM_BO_WC | \
+                              MSM_BO_UNCACHED)
+
+struct drm_msm_gem_new {
+	__u64 size;           /* in */
+	__u32 flags;          /* in, mask of MSM_BO_x */
+	__u32 handle;         /* out */
+};
+
+struct drm_msm_gem_info {
+	__u32 handle;         /* in */
+	__u32 pad;
+	__u64 offset;         /* out, offset to pass to mmap() */
+};
+
+#define MSM_PREP_READ        0x01
+#define MSM_PREP_WRITE       0x02
+#define MSM_PREP_NOSYNC      0x04
+
+#define MSM_PREP_FLAGS       (MSM_PREP_READ | MSM_PREP_WRITE | MSM_PREP_NOSYNC)
+
+struct drm_msm_gem_cpu_prep {
+	__u32 handle;         /* in */
+	__u32 op;             /* in, mask of MSM_PREP_x */
+	struct drm_msm_timespec timeout;   /* in */
+};
+
+struct drm_msm_gem_cpu_fini {
+	__u32 handle;         /* in */
+};
+
+/*
+ * Cmdstream Submission:
+ */
+
+/* The value written into the cmdstream is logically:
+ *
+ *   ((relocbuf->gpuaddr + reloc_offset) << shift) | or
+ *
+ * When we have GPU's w/ >32bit ptrs, it should be possible to deal
+ * with this by emit'ing two reloc entries with appropriate shift
+ * values.  Or a new MSM_SUBMIT_CMD_x type would also be an option.
+ *
+ * NOTE that reloc's must be sorted by order of increasing submit_offset,
+ * otherwise EINVAL.
+ */
+struct drm_msm_gem_submit_reloc {
+	__u32 submit_offset;  /* in, offset from submit_bo */
+	__u32 or;             /* in, value OR'd with result */
+	__s32 shift;          /* in, amount of left shift (can be negative) */
+	__u32 reloc_idx;      /* in, index of reloc_bo buffer */
+	__u64 reloc_offset;   /* in, offset from start of reloc_bo */
+};
+
+/* submit-types:
+ *   BUF - this cmd buffer is executed normally.
+ *   IB_TARGET_BUF - this cmd buffer is an IB target.  Reloc's are
+ *      processed normally, but the kernel does not setup an IB to
+ *      this buffer in the first-level ringbuffer
+ *   CTX_RESTORE_BUF - only executed if there has been a GPU context
+ *      switch since the last SUBMIT ioctl
+ */
+#define MSM_SUBMIT_CMD_BUF             0x0001
+#define MSM_SUBMIT_CMD_IB_TARGET_BUF   0x0002
+#define MSM_SUBMIT_CMD_CTX_RESTORE_BUF 0x0003
+struct drm_msm_gem_submit_cmd {
+	__u32 type;           /* in, one of MSM_SUBMIT_CMD_x */
+	__u32 submit_idx;     /* in, index of submit_bo cmdstream buffer */
+	__u32 submit_offset;  /* in, offset into submit_bo */
+	__u32 size;           /* in, cmdstream size */
+	__u32 pad;
+	__u32 nr_relocs;      /* in, number of submit_reloc's */
+	__u64 __user relocs;  /* in, ptr to array of submit_reloc's */
+};
+
+/* Each buffer referenced elsewhere in the cmdstream submit (ie. the
+ * cmdstream buffer(s) themselves or reloc entries) has one (and only
+ * one) entry in the submit->bos[] table.
+ *
+ * As a optimization, the current buffer (gpu virtual address) can be
+ * passed back through the 'presumed' field.  If on a subsequent reloc,
+ * userspace passes back a 'presumed' address that is still valid,
+ * then patching the cmdstream for this entry is skipped.  This can
+ * avoid kernel needing to map/access the cmdstream bo in the common
+ * case.
+ */
+#define MSM_SUBMIT_BO_READ             0x0001
+#define MSM_SUBMIT_BO_WRITE            0x0002
+
+#define MSM_SUBMIT_BO_FLAGS            (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
+
+struct drm_msm_gem_submit_bo {
+	__u32 flags;          /* in, mask of MSM_SUBMIT_BO_x */
+	__u32 handle;         /* in, GEM handle */
+	__u64 presumed;       /* in/out, presumed buffer address */
+};
+
+/* Valid submit ioctl flags: */
+#define MSM_SUBMIT_NO_IMPLICIT   0x80000000 /* disable implicit sync */
+#define MSM_SUBMIT_FENCE_FD_IN   0x40000000 /* enable input fence_fd */
+#define MSM_SUBMIT_FENCE_FD_OUT  0x20000000 /* enable output fence_fd */
+#define MSM_SUBMIT_FLAGS                ( \
+		MSM_SUBMIT_NO_IMPLICIT   | \
+		MSM_SUBMIT_FENCE_FD_IN   | \
+		MSM_SUBMIT_FENCE_FD_OUT  | \
+		0)
+
+/* Each cmdstream submit consists of a table of buffers involved, and
+ * one or more cmdstream buffers.  This allows for conditional execution
+ * (context-restore), and IB buffers needed for per tile/bin draw cmds.
+ */
+struct drm_msm_gem_submit {
+	__u32 flags;          /* MSM_PIPE_x | MSM_SUBMIT_x */
+	__u32 fence;          /* out */
+	__u32 nr_bos;         /* in, number of submit_bo's */
+	__u32 nr_cmds;        /* in, number of submit_cmd's */
+	__u64 __user bos;     /* in, ptr to array of submit_bo's */
+	__u64 __user cmds;    /* in, ptr to array of submit_cmd's */
+	__s32 fence_fd;       /* in/out fence fd (see MSM_SUBMIT_FENCE_FD_IN/OUT) */
+};
+
+/* The normal way to synchronize with the GPU is just to CPU_PREP on
+ * a buffer if you need to access it from the CPU (other cmdstream
+ * submission from same or other contexts, PAGE_FLIP ioctl, etc, all
+ * handle the required synchronization under the hood).  This ioctl
+ * mainly just exists as a way to implement the gallium pipe_fence
+ * APIs without requiring a dummy bo to synchronize on.
+ */
+struct drm_msm_wait_fence {
+	__u32 fence;          /* in */
+	__u32 pad;
+	struct drm_msm_timespec timeout;   /* in */
+};
+
+/* madvise provides a way to tell the kernel in case a buffers contents
+ * can be discarded under memory pressure, which is useful for userspace
+ * bo cache where we want to optimistically hold on to buffer allocate
+ * and potential mmap, but allow the pages to be discarded under memory
+ * pressure.
+ *
+ * Typical usage would involve madvise(DONTNEED) when buffer enters BO
+ * cache, and madvise(WILLNEED) if trying to recycle buffer from BO cache.
+ * In the WILLNEED case, 'retained' indicates to userspace whether the
+ * backing pages still exist.
+ */
+#define MSM_MADV_WILLNEED 0       /* backing pages are needed, status returned in 'retained' */
+#define MSM_MADV_DONTNEED 1       /* backing pages not needed */
+#define __MSM_MADV_PURGED 2       /* internal state */
+
+struct drm_msm_gem_madvise {
+	__u32 handle;         /* in, GEM handle */
+	__u32 madv;           /* in, MSM_MADV_x */
+	__u32 retained;       /* out, whether backing store still exists */
+};
+
+#define DRM_MSM_GET_PARAM              0x00
+/* placeholder:
+#define DRM_MSM_SET_PARAM              0x01
+ */
+#define DRM_MSM_GEM_NEW                0x02
+#define DRM_MSM_GEM_INFO               0x03
+#define DRM_MSM_GEM_CPU_PREP           0x04
+#define DRM_MSM_GEM_CPU_FINI           0x05
+#define DRM_MSM_GEM_SUBMIT             0x06
+#define DRM_MSM_WAIT_FENCE             0x07
+#define DRM_MSM_GEM_MADVISE            0x08
+#define DRM_MSM_NUM_IOCTLS             0x09
+
+#define DRM_IOCTL_MSM_GET_PARAM        DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GET_PARAM, struct drm_msm_param)
+#define DRM_IOCTL_MSM_GEM_NEW          DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_NEW, struct drm_msm_gem_new)
+#define DRM_IOCTL_MSM_GEM_INFO         DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_INFO, struct drm_msm_gem_info)
+#define DRM_IOCTL_MSM_GEM_CPU_PREP     DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_PREP, struct drm_msm_gem_cpu_prep)
+#define DRM_IOCTL_MSM_GEM_CPU_FINI     DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_FINI, struct drm_msm_gem_cpu_fini)
+#define DRM_IOCTL_MSM_GEM_SUBMIT       DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_SUBMIT, struct drm_msm_gem_submit)
+#define DRM_IOCTL_MSM_WAIT_FENCE       DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_WAIT_FENCE, struct drm_msm_wait_fence)
+#define DRM_IOCTL_MSM_GEM_MADVISE      DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_MADVISE, struct drm_msm_gem_madvise)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __MSM_DRM_H__ */
diff --git a/freedreno/msm/msm_pipe.c b/freedreno/msm/msm_pipe.c
new file mode 100644
index 0000000..f872e24
--- /dev/null
+++ b/freedreno/msm/msm_pipe.c
@@ -0,0 +1,165 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "msm_priv.h"
+
+static int query_param(struct fd_pipe *pipe, uint32_t param,
+		uint64_t *value)
+{
+	struct msm_pipe *msm_pipe = to_msm_pipe(pipe);
+	struct drm_msm_param req = {
+			.pipe = msm_pipe->pipe,
+			.param = param,
+	};
+	int ret;
+
+	ret = drmCommandWriteRead(pipe->dev->fd, DRM_MSM_GET_PARAM,
+			&req, sizeof(req));
+	if (ret)
+		return ret;
+
+	*value = req.value;
+
+	return 0;
+}
+
+static int msm_pipe_get_param(struct fd_pipe *pipe,
+		enum fd_param_id param, uint64_t *value)
+{
+	struct msm_pipe *msm_pipe = to_msm_pipe(pipe);
+	switch(param) {
+	case FD_DEVICE_ID: // XXX probably get rid of this..
+	case FD_GPU_ID:
+		*value = msm_pipe->gpu_id;
+		return 0;
+	case FD_GMEM_SIZE:
+		*value = msm_pipe->gmem;
+		return 0;
+	case FD_CHIP_ID:
+		*value = msm_pipe->chip_id;
+		return 0;
+	case FD_MAX_FREQ:
+		return query_param(pipe, MSM_PARAM_MAX_FREQ, value);
+	case FD_TIMESTAMP:
+		return query_param(pipe, MSM_PARAM_TIMESTAMP, value);
+	default:
+		ERROR_MSG("invalid param id: %d", param);
+		return -1;
+	}
+}
+
+static int msm_pipe_wait(struct fd_pipe *pipe, uint32_t timestamp,
+		uint64_t timeout)
+{
+	struct fd_device *dev = pipe->dev;
+	struct drm_msm_wait_fence req = {
+			.fence = timestamp,
+	};
+	int ret;
+
+	get_abs_timeout(&req.timeout, timeout);
+
+	ret = drmCommandWrite(dev->fd, DRM_MSM_WAIT_FENCE, &req, sizeof(req));
+	if (ret) {
+		ERROR_MSG("wait-fence failed! %d (%s)", ret, strerror(errno));
+		return ret;
+	}
+
+	return 0;
+}
+
+static void msm_pipe_destroy(struct fd_pipe *pipe)
+{
+	struct msm_pipe *msm_pipe = to_msm_pipe(pipe);
+	free(msm_pipe);
+}
+
+static const struct fd_pipe_funcs funcs = {
+		.ringbuffer_new = msm_ringbuffer_new,
+		.get_param = msm_pipe_get_param,
+		.wait = msm_pipe_wait,
+		.destroy = msm_pipe_destroy,
+};
+
+static uint64_t get_param(struct fd_pipe *pipe, uint32_t param)
+{
+	uint64_t value;
+	int ret = query_param(pipe, param, &value);
+	if (ret) {
+		ERROR_MSG("get-param failed! %d (%s)", ret, strerror(errno));
+		return 0;
+	}
+	return value;
+}
+
+drm_private struct fd_pipe * msm_pipe_new(struct fd_device *dev,
+		enum fd_pipe_id id)
+{
+	static const uint32_t pipe_id[] = {
+			[FD_PIPE_3D] = MSM_PIPE_3D0,
+			[FD_PIPE_2D] = MSM_PIPE_2D0,
+	};
+	struct msm_pipe *msm_pipe = NULL;
+	struct fd_pipe *pipe = NULL;
+
+	msm_pipe = calloc(1, sizeof(*msm_pipe));
+	if (!msm_pipe) {
+		ERROR_MSG("allocation failed");
+		goto fail;
+	}
+
+	pipe = &msm_pipe->base;
+	pipe->funcs = &funcs;
+
+	/* initialize before get_param(): */
+	pipe->dev = dev;
+	msm_pipe->pipe = pipe_id[id];
+
+	/* these params should be supported since the first version of drm/msm: */
+	msm_pipe->gpu_id = get_param(pipe, MSM_PARAM_GPU_ID);
+	msm_pipe->gmem   = get_param(pipe, MSM_PARAM_GMEM_SIZE);
+	msm_pipe->chip_id = get_param(pipe, MSM_PARAM_CHIP_ID);
+
+	if (! msm_pipe->gpu_id)
+		goto fail;
+
+	INFO_MSG("Pipe Info:");
+	INFO_MSG(" GPU-id:          %d", msm_pipe->gpu_id);
+	INFO_MSG(" Chip-id:         0x%08x", msm_pipe->chip_id);
+	INFO_MSG(" GMEM size:       0x%08x", msm_pipe->gmem);
+
+	return pipe;
+fail:
+	if (pipe)
+		fd_pipe_del(pipe);
+	return NULL;
+}
diff --git a/freedreno/msm/msm_priv.h b/freedreno/msm/msm_priv.h
new file mode 100644
index 0000000..6d670aa
--- /dev/null
+++ b/freedreno/msm/msm_priv.h
@@ -0,0 +1,103 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifndef MSM_PRIV_H_
+#define MSM_PRIV_H_
+
+#include "freedreno_priv.h"
+
+#ifndef __user
+#  define __user
+#endif
+
+#include "msm_drm.h"
+
+struct msm_device {
+	struct fd_device base;
+	struct fd_bo_cache ring_cache;
+	unsigned ring_cnt;
+};
+
+static inline struct msm_device * to_msm_device(struct fd_device *x)
+{
+	return (struct msm_device *)x;
+}
+
+drm_private struct fd_device * msm_device_new(int fd);
+
+struct msm_pipe {
+	struct fd_pipe base;
+	uint32_t pipe;
+	uint32_t gpu_id;
+	uint32_t gmem;
+	uint32_t chip_id;
+};
+
+static inline struct msm_pipe * to_msm_pipe(struct fd_pipe *x)
+{
+	return (struct msm_pipe *)x;
+}
+
+drm_private struct fd_pipe * msm_pipe_new(struct fd_device *dev,
+		enum fd_pipe_id id);
+
+drm_private struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe,
+		uint32_t size);
+
+struct msm_bo {
+	struct fd_bo base;
+	uint64_t offset;
+	uint64_t presumed;
+	/* to avoid excess hashtable lookups, cache the ring this bo was
+	 * last emitted on (since that will probably also be the next ring
+	 * it is emitted on)
+	 */
+	unsigned current_ring_seqno;
+	uint32_t idx;
+};
+
+static inline struct msm_bo * to_msm_bo(struct fd_bo *x)
+{
+	return (struct msm_bo *)x;
+}
+
+drm_private int msm_bo_new_handle(struct fd_device *dev,
+		uint32_t size, uint32_t flags, uint32_t *handle);
+drm_private struct fd_bo * msm_bo_from_handle(struct fd_device *dev,
+		uint32_t size, uint32_t handle);
+
+static inline void get_abs_timeout(struct drm_msm_timespec *tv, uint64_t ns)
+{
+	struct timespec t;
+	uint32_t s = ns / 1000000000;
+	clock_gettime(CLOCK_MONOTONIC, &t);
+	tv->tv_sec = t.tv_sec + s;
+	tv->tv_nsec = t.tv_nsec + ns - (s * 1000000000);
+}
+
+#endif /* MSM_PRIV_H_ */
diff --git a/freedreno/msm/msm_ringbuffer.c b/freedreno/msm/msm_ringbuffer.c
new file mode 100644
index 0000000..5b28fea
--- /dev/null
+++ b/freedreno/msm/msm_ringbuffer.c
@@ -0,0 +1,617 @@
+/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
+
+/*
+ * Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
+ *
+ * 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:
+ *    Rob Clark <robclark@freedesktop.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <inttypes.h>
+
+#include "freedreno_ringbuffer.h"
+#include "msm_priv.h"
+
+/* represents a single cmd buffer in the submit ioctl.  Each cmd buffer has
+ * a backing bo, and a reloc table.
+ */
+struct msm_cmd {
+	struct list_head list;
+
+	struct fd_ringbuffer *ring;
+	struct fd_bo *ring_bo;
+
+	/* reloc's table: */
+	struct drm_msm_gem_submit_reloc *relocs;
+	uint32_t nr_relocs, max_relocs;
+
+	uint32_t size;
+};
+
+struct msm_ringbuffer {
+	struct fd_ringbuffer base;
+
+	/* submit ioctl related tables:
+	 * Note that bos and cmds are tracked by the parent ringbuffer, since
+	 * that is global to the submit ioctl call.  The reloc's table is tracked
+	 * per cmd-buffer.
+	 */
+	struct {
+		/* bo's table: */
+		struct drm_msm_gem_submit_bo *bos;
+		uint32_t nr_bos, max_bos;
+
+		/* cmd's table: */
+		struct drm_msm_gem_submit_cmd *cmds;
+		uint32_t nr_cmds, max_cmds;
+	} submit;
+
+	/* should have matching entries in submit.bos: */
+	/* Note, only in parent ringbuffer */
+	struct fd_bo **bos;
+	uint32_t nr_bos, max_bos;
+
+	/* should have matching entries in submit.cmds: */
+	struct msm_cmd **cmds;
+	uint32_t nr_cmds, max_cmds;
+
+	/* List of physical cmdstream buffers (msm_cmd) assocated with this
+	 * logical fd_ringbuffer.
+	 *
+	 * Note that this is different from msm_ringbuffer::cmds (which
+	 * shadows msm_ringbuffer::submit::cmds for tracking submit ioctl
+	 * related stuff, and *only* is tracked in the parent ringbuffer.
+	 * And only has "completed" cmd buffers (ie. we already know the
+	 * size) added via get_cmd().
+	 */
+	struct list_head cmd_list;
+
+	int is_growable;
+	unsigned cmd_count;
+
+	unsigned seqno;
+
+	/* maps fd_bo to idx: */
+	void *bo_table;
+};
+
+static inline struct msm_ringbuffer * to_msm_ringbuffer(struct fd_ringbuffer *x)
+{
+	return (struct msm_ringbuffer *)x;
+}
+
+#define INIT_SIZE 0x1000
+
+static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
+drm_private extern pthread_mutex_t table_lock;
+
+static void ring_bo_del(struct fd_device *dev, struct fd_bo *bo)
+{
+	int ret;
+
+	pthread_mutex_lock(&table_lock);
+	ret = fd_bo_cache_free(&to_msm_device(dev)->ring_cache, bo);
+	pthread_mutex_unlock(&table_lock);
+
+	if (ret == 0)
+		return;
+
+	fd_bo_del(bo);
+}
+
+static struct fd_bo * ring_bo_new(struct fd_device *dev, uint32_t size)
+{
+	struct fd_bo *bo;
+
+	bo = fd_bo_cache_alloc(&to_msm_device(dev)->ring_cache, &size, 0);
+	if (bo)
+		return bo;
+
+	bo = fd_bo_new(dev, size, 0);
+	if (!bo)
+		return NULL;
+
+	/* keep ringbuffer bo's out of the normal bo cache: */
+	bo->bo_reuse = FALSE;
+
+	return bo;
+}
+
+static void ring_cmd_del(struct msm_cmd *cmd)
+{
+	if (cmd->ring_bo)
+		ring_bo_del(cmd->ring->pipe->dev, cmd->ring_bo);
+	list_del(&cmd->list);
+	to_msm_ringbuffer(cmd->ring)->cmd_count--;
+	free(cmd->relocs);
+	free(cmd);
+}
+
+static struct msm_cmd * ring_cmd_new(struct fd_ringbuffer *ring, uint32_t size)
+{
+	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+	struct msm_cmd *cmd = calloc(1, sizeof(*cmd));
+
+	if (!cmd)
+		return NULL;
+
+	cmd->ring = ring;
+	cmd->ring_bo = ring_bo_new(ring->pipe->dev, size);
+	if (!cmd->ring_bo)
+		goto fail;
+
+	list_addtail(&cmd->list, &msm_ring->cmd_list);
+	msm_ring->cmd_count++;
+
+	return cmd;
+
+fail:
+	ring_cmd_del(cmd);
+	return NULL;
+}
+
+static void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz)
+{
+	if ((nr + 1) > *max) {
+		if ((*max * 2) < (nr + 1))
+			*max = nr + 5;
+		else
+			*max = *max * 2;
+		ptr = realloc(ptr, *max * sz);
+	}
+	return ptr;
+}
+
+#define APPEND(x, name) ({ \
+	(x)->name = grow((x)->name, (x)->nr_ ## name, &(x)->max_ ## name, sizeof((x)->name[0])); \
+	(x)->nr_ ## name ++; \
+})
+
+static struct msm_cmd *current_cmd(struct fd_ringbuffer *ring)
+{
+	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+	assert(!LIST_IS_EMPTY(&msm_ring->cmd_list));
+	return LIST_LAST_ENTRY(&msm_ring->cmd_list, struct msm_cmd, list);
+}
+
+static uint32_t append_bo(struct fd_ringbuffer *ring, struct fd_bo *bo)
+{
+	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+	uint32_t idx;
+
+	idx = APPEND(&msm_ring->submit, bos);
+	idx = APPEND(msm_ring, bos);
+
+	msm_ring->submit.bos[idx].flags = 0;
+	msm_ring->submit.bos[idx].handle = bo->handle;
+	msm_ring->submit.bos[idx].presumed = to_msm_bo(bo)->presumed;
+
+	msm_ring->bos[idx] = fd_bo_ref(bo);
+
+	return idx;
+}
+
+/* add (if needed) bo, return idx: */
+static uint32_t bo2idx(struct fd_ringbuffer *ring, struct fd_bo *bo, uint32_t flags)
+{
+	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+	struct msm_bo *msm_bo = to_msm_bo(bo);
+	uint32_t idx;
+	pthread_mutex_lock(&idx_lock);
+	if (msm_bo->current_ring_seqno == msm_ring->seqno) {
+		idx = msm_bo->idx;
+	} else {
+		void *val;
+
+		if (!msm_ring->bo_table)
+			msm_ring->bo_table = drmHashCreate();
+
+		if (!drmHashLookup(msm_ring->bo_table, bo->handle, &val)) {
+			/* found */
+			idx = (uint32_t)(uintptr_t)val;
+		} else {
+			idx = append_bo(ring, bo);
+			val = (void *)(uintptr_t)idx;
+			drmHashInsert(msm_ring->bo_table, bo->handle, val);
+		}
+		msm_bo->current_ring_seqno = msm_ring->seqno;
+		msm_bo->idx = idx;
+	}
+	pthread_mutex_unlock(&idx_lock);
+	if (flags & FD_RELOC_READ)
+		msm_ring->submit.bos[idx].flags |= MSM_SUBMIT_BO_READ;
+	if (flags & FD_RELOC_WRITE)
+		msm_ring->submit.bos[idx].flags |= MSM_SUBMIT_BO_WRITE;
+	return idx;
+}
+
+static int check_cmd_bo(struct fd_ringbuffer *ring,
+		struct drm_msm_gem_submit_cmd *cmd, struct fd_bo *bo)
+{
+	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+	return msm_ring->submit.bos[cmd->submit_idx].handle == bo->handle;
+}
+
+/* Ensure that submit has corresponding entry in cmds table for the
+ * target cmdstream buffer:
+ */
+static void get_cmd(struct fd_ringbuffer *ring, struct msm_cmd *target_cmd,
+		uint32_t submit_offset, uint32_t size, uint32_t type)
+{
+	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+	struct drm_msm_gem_submit_cmd *cmd;
+	uint32_t i;
+
+	/* figure out if we already have a cmd buf: */
+	for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
+		cmd = &msm_ring->submit.cmds[i];
+		if ((cmd->submit_offset == submit_offset) &&
+				(cmd->size == size) &&
+				(cmd->type == type) &&
+				check_cmd_bo(ring, cmd, target_cmd->ring_bo))
+			return;
+	}
+
+	/* create cmd buf if not: */
+	i = APPEND(&msm_ring->submit, cmds);
+	APPEND(msm_ring, cmds);
+	msm_ring->cmds[i] = target_cmd;
+	cmd = &msm_ring->submit.cmds[i];
+	cmd->type = type;
+	cmd->submit_idx = bo2idx(ring, target_cmd->ring_bo, FD_RELOC_READ);
+	cmd->submit_offset = submit_offset;
+	cmd->size = size;
+	cmd->pad = 0;
+
+	target_cmd->size = size;
+}
+
+static void * msm_ringbuffer_hostptr(struct fd_ringbuffer *ring)
+{
+	return fd_bo_map(current_cmd(ring)->ring_bo);
+}
+
+static uint32_t find_next_reloc_idx(struct msm_cmd *msm_cmd,
+		uint32_t start, uint32_t offset)
+{
+	uint32_t i;
+
+	/* a binary search would be more clever.. */
+	for (i = start; i < msm_cmd->nr_relocs; i++) {
+		struct drm_msm_gem_submit_reloc *reloc = &msm_cmd->relocs[i];
+		if (reloc->submit_offset >= offset)
+			return i;
+	}
+
+	return i;
+}
+
+static void delete_cmds(struct msm_ringbuffer *msm_ring)
+{
+	struct msm_cmd *cmd, *tmp;
+
+	LIST_FOR_EACH_ENTRY_SAFE(cmd, tmp, &msm_ring->cmd_list, list) {
+		ring_cmd_del(cmd);
+	}
+}
+
+static void flush_reset(struct fd_ringbuffer *ring)
+{
+	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+	unsigned i;
+
+	for (i = 0; i < msm_ring->nr_bos; i++) {
+		struct msm_bo *msm_bo = to_msm_bo(msm_ring->bos[i]);
+		msm_bo->current_ring_seqno = 0;
+		fd_bo_del(&msm_bo->base);
+	}
+
+	/* for each of the cmd buffers, clear their reloc's: */
+	for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
+		struct msm_cmd *target_cmd = msm_ring->cmds[i];
+		target_cmd->nr_relocs = 0;
+	}
+
+	msm_ring->submit.nr_cmds = 0;
+	msm_ring->submit.nr_bos = 0;
+	msm_ring->nr_cmds = 0;
+	msm_ring->nr_bos = 0;
+
+	if (msm_ring->bo_table) {
+		drmHashDestroy(msm_ring->bo_table);
+		msm_ring->bo_table = NULL;
+	}
+
+	if (msm_ring->is_growable) {
+		delete_cmds(msm_ring);
+	} else {
+		/* in old mode, just reset the # of relocs: */
+		current_cmd(ring)->nr_relocs = 0;
+	}
+}
+
+static void finalize_current_cmd(struct fd_ringbuffer *ring, uint32_t *last_start)
+{
+	uint32_t submit_offset, size, type;
+	struct fd_ringbuffer *parent;
+
+	if (ring->parent) {
+		parent = ring->parent;
+		type = MSM_SUBMIT_CMD_IB_TARGET_BUF;
+	} else {
+		parent = ring;
+		type = MSM_SUBMIT_CMD_BUF;
+	}
+
+	submit_offset = offset_bytes(last_start, ring->start);
+	size = offset_bytes(ring->cur, last_start);
+
+	get_cmd(parent, current_cmd(ring), submit_offset, size, type);
+}
+
+static void dump_submit(struct msm_ringbuffer *msm_ring)
+{
+	uint32_t i, j;
+
+	for (i = 0; i < msm_ring->submit.nr_bos; i++) {
+		struct drm_msm_gem_submit_bo *bo = &msm_ring->submit.bos[i];
+		ERROR_MSG("  bos[%d]: handle=%u, flags=%x", i, bo->handle, bo->flags);
+	}
+	for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
+		struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i];
+		struct drm_msm_gem_submit_reloc *relocs = U642VOID(cmd->relocs);
+		ERROR_MSG("  cmd[%d]: type=%u, submit_idx=%u, submit_offset=%u, size=%u",
+				i, cmd->type, cmd->submit_idx, cmd->submit_offset, cmd->size);
+		for (j = 0; j < cmd->nr_relocs; j++) {
+			struct drm_msm_gem_submit_reloc *r = &relocs[j];
+			ERROR_MSG("    reloc[%d]: submit_offset=%u, or=%08x, shift=%d, reloc_idx=%u"
+					", reloc_offset=%"PRIu64, j, r->submit_offset, r->or, r->shift,
+					r->reloc_idx, r->reloc_offset);
+		}
+	}
+}
+
+static int msm_ringbuffer_flush(struct fd_ringbuffer *ring, uint32_t *last_start,
+		int in_fence_fd, int *out_fence_fd)
+{
+	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+	struct drm_msm_gem_submit req = {
+			.flags = to_msm_pipe(ring->pipe)->pipe,
+	};
+	uint32_t i;
+	int ret;
+
+	if (in_fence_fd != -1) {
+		req.flags |= MSM_SUBMIT_FENCE_FD_IN | MSM_SUBMIT_NO_IMPLICIT;
+		req.fence_fd = in_fence_fd;
+	}
+
+	if (out_fence_fd) {
+		req.flags |= MSM_SUBMIT_FENCE_FD_OUT;
+	}
+
+	finalize_current_cmd(ring, last_start);
+
+	/* needs to be after get_cmd() as that could create bos/cmds table: */
+	req.bos = VOID2U64(msm_ring->submit.bos),
+	req.nr_bos = msm_ring->submit.nr_bos;
+	req.cmds = VOID2U64(msm_ring->submit.cmds),
+	req.nr_cmds = msm_ring->submit.nr_cmds;
+
+	/* for each of the cmd's fix up their reloc's: */
+	for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
+		struct drm_msm_gem_submit_cmd *cmd = &msm_ring->submit.cmds[i];
+		struct msm_cmd *msm_cmd = msm_ring->cmds[i];
+		uint32_t a = find_next_reloc_idx(msm_cmd, 0, cmd->submit_offset);
+		uint32_t b = find_next_reloc_idx(msm_cmd, a, cmd->submit_offset + cmd->size);
+		cmd->relocs = VOID2U64(&msm_cmd->relocs[a]);
+		cmd->nr_relocs = (b > a) ? b - a : 0;
+	}
+
+	DEBUG_MSG("nr_cmds=%u, nr_bos=%u", req.nr_cmds, req.nr_bos);
+
+	ret = drmCommandWriteRead(ring->pipe->dev->fd, DRM_MSM_GEM_SUBMIT,
+			&req, sizeof(req));
+	if (ret) {
+		ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
+		dump_submit(msm_ring);
+	} else if (!ret) {
+		/* update timestamp on all rings associated with submit: */
+		for (i = 0; i < msm_ring->submit.nr_cmds; i++) {
+			struct msm_cmd *msm_cmd = msm_ring->cmds[i];
+			msm_cmd->ring->last_timestamp = req.fence;
+		}
+
+		if (out_fence_fd) {
+			*out_fence_fd = req.fence_fd;
+		}
+	}
+
+	flush_reset(ring);
+
+	return ret;
+}
+
+static void msm_ringbuffer_grow(struct fd_ringbuffer *ring, uint32_t size)
+{
+	assert(to_msm_ringbuffer(ring)->is_growable);
+	finalize_current_cmd(ring, ring->last_start);
+	ring_cmd_new(ring, size);
+}
+
+static void msm_ringbuffer_reset(struct fd_ringbuffer *ring)
+{
+	flush_reset(ring);
+}
+
+static void msm_ringbuffer_emit_reloc(struct fd_ringbuffer *ring,
+		const struct fd_reloc *r)
+{
+	struct fd_ringbuffer *parent = ring->parent ? ring->parent : ring;
+	struct msm_bo *msm_bo = to_msm_bo(r->bo);
+	struct drm_msm_gem_submit_reloc *reloc;
+	struct msm_cmd *cmd = current_cmd(ring);
+	uint32_t idx = APPEND(cmd, relocs);
+	uint32_t addr;
+
+	reloc = &cmd->relocs[idx];
+
+	reloc->reloc_idx = bo2idx(parent, r->bo, r->flags);
+	reloc->reloc_offset = r->offset;
+	reloc->or = r->or;
+	reloc->shift = r->shift;
+	reloc->submit_offset = offset_bytes(ring->cur, ring->start);
+
+	addr = msm_bo->presumed;
+	if (reloc->shift < 0)
+		addr >>= -reloc->shift;
+	else
+		addr <<= reloc->shift;
+	(*ring->cur++) = addr | r->or;
+
+	if (ring->pipe->gpu_id >= 500) {
+		struct drm_msm_gem_submit_reloc *reloc_hi;
+
+		/* NOTE: grab reloc_idx *before* APPEND() since that could
+		 * realloc() meaning that 'reloc' ptr is no longer valid:
+		 */
+		uint32_t reloc_idx = reloc->reloc_idx;
+
+		idx = APPEND(cmd, relocs);
+
+		reloc_hi = &cmd->relocs[idx];
+
+		reloc_hi->reloc_idx = reloc_idx;
+		reloc_hi->reloc_offset = r->offset;
+		reloc_hi->or = r->orhi;
+		reloc_hi->shift = r->shift - 32;
+		reloc_hi->submit_offset = offset_bytes(ring->cur, ring->start);
+
+		addr = msm_bo->presumed >> 32;
+		if (reloc_hi->shift < 0)
+			addr >>= -reloc_hi->shift;
+		else
+			addr <<= reloc_hi->shift;
+		(*ring->cur++) = addr | r->orhi;
+	}
+}
+
+static uint32_t msm_ringbuffer_emit_reloc_ring(struct fd_ringbuffer *ring,
+		struct fd_ringbuffer *target, uint32_t cmd_idx,
+		uint32_t submit_offset, uint32_t size)
+{
+	struct msm_cmd *cmd = NULL;
+	uint32_t idx = 0;
+
+	LIST_FOR_EACH_ENTRY(cmd, &to_msm_ringbuffer(target)->cmd_list, list) {
+		if (idx == cmd_idx)
+			break;
+		idx++;
+	}
+
+	assert(cmd && (idx == cmd_idx));
+
+	if (idx < (to_msm_ringbuffer(target)->cmd_count - 1)) {
+		/* All but the last cmd buffer is fully "baked" (ie. already has
+		 * done get_cmd() to add it to the cmds table).  But in this case,
+		 * the size we get is invalid (since it is calculated from the
+		 * last cmd buffer):
+		 */
+		size = cmd->size;
+	} else {
+		get_cmd(ring, cmd, submit_offset, size, MSM_SUBMIT_CMD_IB_TARGET_BUF);
+	}
+
+	msm_ringbuffer_emit_reloc(ring, &(struct fd_reloc){
+		.bo = cmd->ring_bo,
+		.flags = FD_RELOC_READ,
+		.offset = submit_offset,
+	});
+
+	return size;
+}
+
+static uint32_t msm_ringbuffer_cmd_count(struct fd_ringbuffer *ring)
+{
+	return to_msm_ringbuffer(ring)->cmd_count;
+}
+
+static void msm_ringbuffer_destroy(struct fd_ringbuffer *ring)
+{
+	struct msm_ringbuffer *msm_ring = to_msm_ringbuffer(ring);
+
+	flush_reset(ring);
+	delete_cmds(msm_ring);
+
+	free(msm_ring->submit.cmds);
+	free(msm_ring->submit.bos);
+	free(msm_ring->bos);
+	free(msm_ring->cmds);
+	free(msm_ring);
+}
+
+static const struct fd_ringbuffer_funcs funcs = {
+		.hostptr = msm_ringbuffer_hostptr,
+		.flush = msm_ringbuffer_flush,
+		.grow = msm_ringbuffer_grow,
+		.reset = msm_ringbuffer_reset,
+		.emit_reloc = msm_ringbuffer_emit_reloc,
+		.emit_reloc_ring = msm_ringbuffer_emit_reloc_ring,
+		.cmd_count = msm_ringbuffer_cmd_count,
+		.destroy = msm_ringbuffer_destroy,
+};
+
+drm_private struct fd_ringbuffer * msm_ringbuffer_new(struct fd_pipe *pipe,
+		uint32_t size)
+{
+	struct msm_ringbuffer *msm_ring;
+	struct fd_ringbuffer *ring;
+
+	msm_ring = calloc(1, sizeof(*msm_ring));
+	if (!msm_ring) {
+		ERROR_MSG("allocation failed");
+		return NULL;
+	}
+
+	if (size == 0) {
+		assert(pipe->dev->version >= FD_VERSION_UNLIMITED_CMDS);
+		size = INIT_SIZE;
+		msm_ring->is_growable = TRUE;
+	}
+
+	list_inithead(&msm_ring->cmd_list);
+	msm_ring->seqno = ++to_msm_device(pipe->dev)->ring_cnt;
+
+	ring = &msm_ring->base;
+	ring->funcs = &funcs;
+	ring->size = size;
+	ring->pipe = pipe;   /* needed in ring_cmd_new() */
+
+	ring_cmd_new(ring, size);
+
+	return ring;
+}
diff --git a/include/drm/README b/include/drm/README
new file mode 100644
index 0000000..870b0b5
--- /dev/null
+++ b/include/drm/README
@@ -0,0 +1,155 @@
+What are these headers ?
+------------------------
+This is the canonical source of drm headers that user space should use for
+communicating with the kernel DRM subsystem.
+
+They flow from the kernel, thus any changes must be merged there first.
+Do _not_ attempt to "fix" these by deviating from the kernel ones !
+
+
+Non-linux platforms - changes/patches
+-------------------------------------
+If your platform has local changes, please send them upstream for inclusion.
+Even if your patches don't get accepted in their current form, devs will
+give you feedback on how to address things properly.
+
+git send-email --subject-prefix="PATCH libdrm" your patches to dri-devel
+mailing list.
+
+Before doing so, please consider the following:
+ - Have the [libdrm vs kernel] headers on your platform deviated ?
+Consider unifying them first.
+
+ - Have you introduced additional ABI that's not available in Linux ?
+Propose it for [Linux kernel] upstream inclusion.
+If that doesn't work out (hopefully it never does), move it to another header
+and/or keep the change(s) local ?
+
+ - Are your changes DRI1/UMS specific ?
+There is virtually no interest/power in keeping those legacy interfaces. They
+are around due to the kernel "thou shalt not break existing user space" rule.
+
+Consider porting the driver to DRI2/KMS - all (almost?) sensible hardware is
+capable of supporting those.
+
+
+Which headers go where ?
+------------------------
+A snipped from the, now removed, Makefile.am used to state:
+
+  XXX airlied says, nothing besides *_drm.h and drm*.h should be necessary.
+  however, r300 and via need their reg headers installed in order to build.
+  better solutions are welcome.
+
+Obviously the r300 and via headers are no longer around ;-)
+
+Reason behind is that the drm headers can be used as a basic communications
+channel with the respective kernel modules. If more advanced functionality is
+required one can pull the specific libdrm_$driver which is free to pull
+additional files from the kernel.
+
+For example: nouveau has nouveau/nvif/*.h while vc4 has vc4/*.h
+
+If your driver is still in prototyping/staging state, consider moving the
+$driver_drm.h into $driver and _not_ installing it. An header providing opaque
+definitions and access [via $driver_drmif.h or similar] would be better fit.
+
+
+When and which headers to update
+--------------------------------
+Ideally all files will be synced (updated) with the latest released kernel on
+each libdrm release. Sadly that's not yet possible since quite a few headers
+differ significantly - see Outdated or Broken Headers section below.
+
+That said, it's up-to the individual developers to sync with newer version
+(from drm-next) as they see fit.
+
+
+When and how to update these files
+----------------------------------
+Note: One should not do _any_ changes to the files apart from the steps below.
+
+In order to update the files do the following:
+ - Switch to a Linux kernel tree/branch which is not rebased.
+For example: airlied/drm-next
+ - Install the headers via `make headers_install' to a separate location.
+ - Copy the drm header[s] + git add + git commit.
+ - Note: Your commit message must include:
+   a) Brief summary on the delta. If there's any change that looks like an
+API/ABI break one _must_ explicitly state why it's safe to do so.
+   b) "Generated using make headers_install."
+   c) "Generated from $tree/branch commit $sha"
+
+
+Outdated or Broken Headers
+--------------------------
+This section contains a list of headers and the respective "issues" they might
+have relative to their kernel equivalent.
+
+Nearly all headers:
+ - Missing extern C notation.
+Status: Trivial.
+
+Most UMS headers:
+ - Not using fixed size integers - compat ioctls are broken.
+Status: ?
+Promote to fixed size ints, which match the current (32bit) ones.
+
+
+drm_mode.h
+ - Missing DPI encode/connector pair.
+Status: Trivial.
+
+i915_drm.h
+ - Missing PARAMS - HAS_POOLED_EU, MIN_EU_IN_POOL CONTEXT_PARAM_NO_ERROR_CAPTURE
+Status: Trivial.
+
+mga_drm.h
+ - Typo fix, use struct over typedef.
+Status: Trivial.
+
+nouveau_drm.h
+ - Missing macros NOUVEAU_GETPARAM*, NOUVEAU_DRM_HEADER_PATCHLEVEL, structs,
+enums, using stdint.h over the __u* types.
+Status: ?
+
+qxl_drm.h
+ - Using the stdint.h uint*_t over the respective __u* ones
+Status: Trivial.
+
+r128_drm.h
+ - Broken compat ioctls.
+
+radeon_drm.h
+ - Missing RADEON_TILING_R600_NO_SCANOUT, CIK_TILE_MODE_*, broken UMS ioctls,
+using stdint types.
+ - Both kernel and libdrm: missing padding -
+drm_radeon_gem_{create,{g,s}et_tiling,set_domain} others ?
+Status: ?
+
+savage_drm.h
+ - Renamed ioctls - DRM_IOCTL_SAVAGE_{,BCI}_EVENT_EMIT, compat ioctls are broken.
+Status: ?
+
+sis_drm.h
+ - Borken ioctls + libdrm uses int vs kernel long
+Status: ?
+
+via_drm.h
+ - Borken ioctls - libdrm int vs kernel long
+Status: ?
+
+
+omap_drm.h (living in $TOP/omap)
+ - License mismatch, missing DRM_IOCTL_OMAP_GEM_NEW and related struct
+Status: ?
+
+msm_drm.h (located in $TOP/freedreno/msm/)
+ - License mismatch, missing MSM_PIPE_*, MSM_SUBMIT_*. Renamed
+drm_msm_gem_submit::flags, missing drm_msm_gem_submit::fence_fd.
+Status: ?
+
+exynos_drm.h (living in $TOP/exynos)
+ - License mismatch, now using fixed size ints (but not everywhere). Lots of
+new stuff.
+Status: ?
diff --git a/include/drm/amdgpu_drm.h b/include/drm/amdgpu_drm.h
new file mode 100644
index 0000000..4c6e8c4
--- /dev/null
+++ b/include/drm/amdgpu_drm.h
@@ -0,0 +1,900 @@
+/* amdgpu_drm.h -- Public header for the amdgpu driver -*- linux-c -*-
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * Copyright 2014 Advanced Micro Devices, Inc.
+ *
+ * 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 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 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.
+ *
+ * Authors:
+ *    Kevin E. Martin <martin@valinux.com>
+ *    Gareth Hughes <gareth@valinux.com>
+ *    Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+#ifndef __AMDGPU_DRM_H__
+#define __AMDGPU_DRM_H__
+
+#include "drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define DRM_AMDGPU_GEM_CREATE		0x00
+#define DRM_AMDGPU_GEM_MMAP		0x01
+#define DRM_AMDGPU_CTX			0x02
+#define DRM_AMDGPU_BO_LIST		0x03
+#define DRM_AMDGPU_CS			0x04
+#define DRM_AMDGPU_INFO			0x05
+#define DRM_AMDGPU_GEM_METADATA		0x06
+#define DRM_AMDGPU_GEM_WAIT_IDLE	0x07
+#define DRM_AMDGPU_GEM_VA		0x08
+#define DRM_AMDGPU_WAIT_CS		0x09
+#define DRM_AMDGPU_GEM_OP		0x10
+#define DRM_AMDGPU_GEM_USERPTR		0x11
+#define DRM_AMDGPU_WAIT_FENCES		0x12
+#define DRM_AMDGPU_VM			0x13
+#define DRM_AMDGPU_FENCE_TO_HANDLE	0x14
+
+#define DRM_IOCTL_AMDGPU_GEM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create)
+#define DRM_IOCTL_AMDGPU_GEM_MMAP	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap)
+#define DRM_IOCTL_AMDGPU_CTX		DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CTX, union drm_amdgpu_ctx)
+#define DRM_IOCTL_AMDGPU_BO_LIST	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_BO_LIST, union drm_amdgpu_bo_list)
+#define DRM_IOCTL_AMDGPU_CS		DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CS, union drm_amdgpu_cs)
+#define DRM_IOCTL_AMDGPU_INFO		DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_INFO, struct drm_amdgpu_info)
+#define DRM_IOCTL_AMDGPU_GEM_METADATA	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_METADATA, struct drm_amdgpu_gem_metadata)
+#define DRM_IOCTL_AMDGPU_GEM_WAIT_IDLE	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_WAIT_IDLE, union drm_amdgpu_gem_wait_idle)
+#define DRM_IOCTL_AMDGPU_GEM_VA		DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_VA, struct drm_amdgpu_gem_va)
+#define DRM_IOCTL_AMDGPU_WAIT_CS	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_CS, union drm_amdgpu_wait_cs)
+#define DRM_IOCTL_AMDGPU_GEM_OP		DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_OP, struct drm_amdgpu_gem_op)
+#define DRM_IOCTL_AMDGPU_GEM_USERPTR	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_USERPTR, struct drm_amdgpu_gem_userptr)
+#define DRM_IOCTL_AMDGPU_WAIT_FENCES	DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_FENCES, union drm_amdgpu_wait_fences)
+#define DRM_IOCTL_AMDGPU_VM		DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_VM, union drm_amdgpu_vm)
+#define DRM_IOCTL_AMDGPU_FENCE_TO_HANDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_FENCE_TO_HANDLE, union drm_amdgpu_fence_to_handle)
+
+#define AMDGPU_GEM_DOMAIN_CPU		0x1
+#define AMDGPU_GEM_DOMAIN_GTT		0x2
+#define AMDGPU_GEM_DOMAIN_VRAM		0x4
+#define AMDGPU_GEM_DOMAIN_GDS		0x8
+#define AMDGPU_GEM_DOMAIN_GWS		0x10
+#define AMDGPU_GEM_DOMAIN_OA		0x20
+
+/* Flag that CPU access will be required for the case of VRAM domain */
+#define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED	(1 << 0)
+/* Flag that CPU access will not work, this VRAM domain is invisible */
+#define AMDGPU_GEM_CREATE_NO_CPU_ACCESS		(1 << 1)
+/* Flag that USWC attributes should be used for GTT */
+#define AMDGPU_GEM_CREATE_CPU_GTT_USWC		(1 << 2)
+/* Flag that the memory should be in VRAM and cleared */
+#define AMDGPU_GEM_CREATE_VRAM_CLEARED		(1 << 3)
+/* Flag that create shadow bo(GTT) while allocating vram bo */
+#define AMDGPU_GEM_CREATE_SHADOW		(1 << 4)
+/* Flag that allocating the BO should use linear VRAM */
+#define AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS	(1 << 5)
+/* Flag that BO is always valid in this VM */
+#define AMDGPU_GEM_CREATE_VM_ALWAYS_VALID	(1 << 6)
+
+struct drm_amdgpu_gem_create_in  {
+	/** the requested memory size */
+	__u64 bo_size;
+	/** physical start_addr alignment in bytes for some HW requirements */
+	__u64 alignment;
+	/** the requested memory domains */
+	__u64 domains;
+	/** allocation flags */
+	__u64 domain_flags;
+};
+
+struct drm_amdgpu_gem_create_out  {
+	/** returned GEM object handle */
+	__u32 handle;
+	__u32 _pad;
+};
+
+union drm_amdgpu_gem_create {
+	struct drm_amdgpu_gem_create_in		in;
+	struct drm_amdgpu_gem_create_out	out;
+};
+
+/** Opcode to create new residency list.  */
+#define AMDGPU_BO_LIST_OP_CREATE	0
+/** Opcode to destroy previously created residency list */
+#define AMDGPU_BO_LIST_OP_DESTROY	1
+/** Opcode to update resource information in the list */
+#define AMDGPU_BO_LIST_OP_UPDATE	2
+
+struct drm_amdgpu_bo_list_in {
+	/** Type of operation */
+	__u32 operation;
+	/** Handle of list or 0 if we want to create one */
+	__u32 list_handle;
+	/** Number of BOs in list  */
+	__u32 bo_number;
+	/** Size of each element describing BO */
+	__u32 bo_info_size;
+	/** Pointer to array describing BOs */
+	__u64 bo_info_ptr;
+};
+
+struct drm_amdgpu_bo_list_entry {
+	/** Handle of BO */
+	__u32 bo_handle;
+	/** New (if specified) BO priority to be used during migration */
+	__u32 bo_priority;
+};
+
+struct drm_amdgpu_bo_list_out {
+	/** Handle of resource list  */
+	__u32 list_handle;
+	__u32 _pad;
+};
+
+union drm_amdgpu_bo_list {
+	struct drm_amdgpu_bo_list_in in;
+	struct drm_amdgpu_bo_list_out out;
+};
+
+/* context related */
+#define AMDGPU_CTX_OP_ALLOC_CTX	1
+#define AMDGPU_CTX_OP_FREE_CTX	2
+#define AMDGPU_CTX_OP_QUERY_STATE	3
+
+/* GPU reset status */
+#define AMDGPU_CTX_NO_RESET		0
+/* this the context caused it */
+#define AMDGPU_CTX_GUILTY_RESET		1
+/* some other context caused it */
+#define AMDGPU_CTX_INNOCENT_RESET	2
+/* unknown cause */
+#define AMDGPU_CTX_UNKNOWN_RESET	3
+
+struct drm_amdgpu_ctx_in {
+	/** AMDGPU_CTX_OP_* */
+	__u32	op;
+	/** For future use, no flags defined so far */
+	__u32	flags;
+	__u32	ctx_id;
+	__u32	_pad;
+};
+
+union drm_amdgpu_ctx_out {
+		struct {
+			__u32	ctx_id;
+			__u32	_pad;
+		} alloc;
+
+		struct {
+			/** For future use, no flags defined so far */
+			__u64	flags;
+			/** Number of resets caused by this context so far. */
+			__u32	hangs;
+			/** Reset status since the last call of the ioctl. */
+			__u32	reset_status;
+		} state;
+};
+
+union drm_amdgpu_ctx {
+	struct drm_amdgpu_ctx_in in;
+	union drm_amdgpu_ctx_out out;
+};
+
+/* vm ioctl */
+#define AMDGPU_VM_OP_RESERVE_VMID	1
+#define AMDGPU_VM_OP_UNRESERVE_VMID	2
+
+struct drm_amdgpu_vm_in {
+	/** AMDGPU_VM_OP_* */
+	__u32	op;
+	__u32	flags;
+};
+
+struct drm_amdgpu_vm_out {
+	/** For future use, no flags defined so far */
+	__u64	flags;
+};
+
+union drm_amdgpu_vm {
+	struct drm_amdgpu_vm_in in;
+	struct drm_amdgpu_vm_out out;
+};
+
+/*
+ * This is not a reliable API and you should expect it to fail for any
+ * number of reasons and have fallback path that do not use userptr to
+ * perform any operation.
+ */
+#define AMDGPU_GEM_USERPTR_READONLY	(1 << 0)
+#define AMDGPU_GEM_USERPTR_ANONONLY	(1 << 1)
+#define AMDGPU_GEM_USERPTR_VALIDATE	(1 << 2)
+#define AMDGPU_GEM_USERPTR_REGISTER	(1 << 3)
+
+struct drm_amdgpu_gem_userptr {
+	__u64		addr;
+	__u64		size;
+	/* AMDGPU_GEM_USERPTR_* */
+	__u32		flags;
+	/* Resulting GEM handle */
+	__u32		handle;
+};
+
+/* SI-CI-VI: */
+/* same meaning as the GB_TILE_MODE and GL_MACRO_TILE_MODE fields */
+#define AMDGPU_TILING_ARRAY_MODE_SHIFT			0
+#define AMDGPU_TILING_ARRAY_MODE_MASK			0xf
+#define AMDGPU_TILING_PIPE_CONFIG_SHIFT			4
+#define AMDGPU_TILING_PIPE_CONFIG_MASK			0x1f
+#define AMDGPU_TILING_TILE_SPLIT_SHIFT			9
+#define AMDGPU_TILING_TILE_SPLIT_MASK			0x7
+#define AMDGPU_TILING_MICRO_TILE_MODE_SHIFT		12
+#define AMDGPU_TILING_MICRO_TILE_MODE_MASK		0x7
+#define AMDGPU_TILING_BANK_WIDTH_SHIFT			15
+#define AMDGPU_TILING_BANK_WIDTH_MASK			0x3
+#define AMDGPU_TILING_BANK_HEIGHT_SHIFT			17
+#define AMDGPU_TILING_BANK_HEIGHT_MASK			0x3
+#define AMDGPU_TILING_MACRO_TILE_ASPECT_SHIFT		19
+#define AMDGPU_TILING_MACRO_TILE_ASPECT_MASK		0x3
+#define AMDGPU_TILING_NUM_BANKS_SHIFT			21
+#define AMDGPU_TILING_NUM_BANKS_MASK			0x3
+
+/* GFX9 and later: */
+#define AMDGPU_TILING_SWIZZLE_MODE_SHIFT		0
+#define AMDGPU_TILING_SWIZZLE_MODE_MASK			0x1f
+
+/* Set/Get helpers for tiling flags. */
+#define AMDGPU_TILING_SET(field, value) \
+	(((__u64)(value) & AMDGPU_TILING_##field##_MASK) << AMDGPU_TILING_##field##_SHIFT)
+#define AMDGPU_TILING_GET(value, field) \
+	(((__u64)(value) >> AMDGPU_TILING_##field##_SHIFT) & AMDGPU_TILING_##field##_MASK)
+
+#define AMDGPU_GEM_METADATA_OP_SET_METADATA                  1
+#define AMDGPU_GEM_METADATA_OP_GET_METADATA                  2
+
+/** The same structure is shared for input/output */
+struct drm_amdgpu_gem_metadata {
+	/** GEM Object handle */
+	__u32	handle;
+	/** Do we want get or set metadata */
+	__u32	op;
+	struct {
+		/** For future use, no flags defined so far */
+		__u64	flags;
+		/** family specific tiling info */
+		__u64	tiling_info;
+		__u32	data_size_bytes;
+		__u32	data[64];
+	} data;
+};
+
+struct drm_amdgpu_gem_mmap_in {
+	/** the GEM object handle */
+	__u32 handle;
+	__u32 _pad;
+};
+
+struct drm_amdgpu_gem_mmap_out {
+	/** mmap offset from the vma offset manager */
+	__u64 addr_ptr;
+};
+
+union drm_amdgpu_gem_mmap {
+	struct drm_amdgpu_gem_mmap_in   in;
+	struct drm_amdgpu_gem_mmap