[PATCH 1/2] drm: add libdrm_amdgpu

Alex Deucher alexdeucher at gmail.com
Tue Apr 21 09:39:13 PDT 2015


This is the new ioctl wrapper used by the new admgpu driver.
It's primarily used by xf86-video-amdgpu and mesa.

Signed-off-by: Alex Deucher <alexander.deucher at amd.com>
---
 Makefile.am                |    5 +
 amdgpu/Makefile.am         |   55 ++
 amdgpu/amdgpu.h            | 1278 ++++++++++++++++++++++++++++++++++++++++++++
 amdgpu/amdgpu_bo.c         |  622 +++++++++++++++++++++
 amdgpu/amdgpu_cs.c         |  981 ++++++++++++++++++++++++++++++++++
 amdgpu/amdgpu_device.c     |  242 +++++++++
 amdgpu/amdgpu_gpu_info.c   |  275 ++++++++++
 amdgpu/amdgpu_internal.h   |  210 ++++++++
 amdgpu/amdgpu_vamgr.c      |  169 ++++++
 amdgpu/libdrm_amdgpu.pc.in |   10 +
 amdgpu/util_double_list.h  |  146 +++++
 amdgpu/util_hash.c         |  382 +++++++++++++
 amdgpu/util_hash.h         |   99 ++++
 amdgpu/util_hash_table.c   |  257 +++++++++
 amdgpu/util_hash_table.h   |   65 +++
 amdgpu/util_math.h         |   32 ++
 configure.ac               |   20 +
 include/drm/amdgpu_drm.h   |  600 +++++++++++++++++++++
 18 files changed, 5448 insertions(+)
 create mode 100644 amdgpu/Makefile.am
 create mode 100644 amdgpu/amdgpu.h
 create mode 100644 amdgpu/amdgpu_bo.c
 create mode 100644 amdgpu/amdgpu_cs.c
 create mode 100644 amdgpu/amdgpu_device.c
 create mode 100644 amdgpu/amdgpu_gpu_info.c
 create mode 100644 amdgpu/amdgpu_internal.h
 create mode 100644 amdgpu/amdgpu_vamgr.c
 create mode 100644 amdgpu/libdrm_amdgpu.pc.in
 create mode 100644 amdgpu/util_double_list.h
 create mode 100644 amdgpu/util_hash.c
 create mode 100644 amdgpu/util_hash.h
 create mode 100644 amdgpu/util_hash_table.c
 create mode 100644 amdgpu/util_hash_table.h
 create mode 100644 amdgpu/util_math.h
 create mode 100644 include/drm/amdgpu_drm.h

diff --git a/Makefile.am b/Makefile.am
index 42d3d7f..5defeb2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -57,6 +57,10 @@ if HAVE_RADEON
 RADEON_SUBDIR = radeon
 endif
 
+if HAVE_AMDGPU
+AMDGPU_SUBDIR = amdgpu
+endif
+
 if HAVE_OMAP
 OMAP_SUBDIR = omap
 endif
@@ -79,6 +83,7 @@ SUBDIRS = \
 	$(INTEL_SUBDIR) \
 	$(NOUVEAU_SUBDIR) \
 	$(RADEON_SUBDIR) \
+	$(AMDGPU_SUBDIR) \
 	$(OMAP_SUBDIR) \
 	$(EXYNOS_SUBDIR) \
 	$(FREEDRENO_SUBDIR) \
diff --git a/amdgpu/Makefile.am b/amdgpu/Makefile.am
new file mode 100644
index 0000000..9baf194
--- /dev/null
+++ b/amdgpu/Makefile.am
@@ -0,0 +1,55 @@
+# 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 at freedesktop.org>
+
+AM_CFLAGS = \
+	$(WARN_CFLAGS) -Wno-switch-enum \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/amdgpu \
+	$(PTHREADSTUBS_CFLAGS) \
+	-I$(top_srcdir)/include/drm
+
+libdrm_amdgpu_la_LTLIBRARIES = libdrm_amdgpu.la
+libdrm_amdgpu_ladir = $(libdir)
+libdrm_amdgpu_la_LDFLAGS = -version-number 1:0:1 -no-undefined
+libdrm_amdgpu_la_LIBADD = ../libdrm.la @PTHREADSTUBS_LIBS@
+
+libdrm_amdgpu_la_SOURCES = \
+	amdgpu_gpu_info.c \
+	amdgpu_device.c \
+	amdgpu_bo.c \
+	util_hash.c \
+	util_hash_table.c \
+	amdgpu_vamgr.c \
+	amdgpu_cs.c
+
+nodist_EXTRA_libdrm_amdgpu_la_SOURCES = dummy.cxx
+
+libdrm_amdgpuincludedir = ${includedir}/libdrm
+libdrm_amdgpuinclude_HEADERS = \
+	amdgpu.h
+
+pkgconfigdir = @pkgconfigdir@
+pkgconfig_DATA = libdrm_amdgpu.pc
+
+EXTRA_DIST = libdrm_amdgpu.pc.in
diff --git a/amdgpu/amdgpu.h b/amdgpu/amdgpu.h
new file mode 100644
index 0000000..90dc33c
--- /dev/null
+++ b/amdgpu/amdgpu.h
@@ -0,0 +1,1278 @@
+/*
+ * 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>
+
+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
+
+/**
+ *
+ */
+#define AMDGPU_TIMEOUT_INFINITE			0xffffffffffffffffull
+
+/**
+ * The special flag for GFX submission to identify that this is CE IB
+ * \sa amdgpu_cs_ib_info
+*/
+#define AMDGPU_CS_GFX_IB_CE			0x1
+
+/**
+ * The special flag to mark that this IB will re-used
+ * by client and should not be automatically return back
+ * to free pool by libdrm_amdgpu when submission is completed.
+ *
+ * \sa amdgpu_cs_ib_info
+*/
+#define AMDGPU_CS_REUSE_IB			0x2
+
+/**
+ * The special resource flag for IB submission.
+ * When VRAM is full, some resources may be moved to GTT to make place
+ * for other resources which want to be in VRAM. This flag affects the order
+ * in which resources are moved back to VRAM until there is no space there.
+ * The resources with the highest priority will be moved first.
+ * The value can be between 0 and 15, inclusive.
+ */
+#define AMDGPU_IB_RESOURCE_PRIORITY(x)		((x) & 0xf)
+
+
+/*--------------------------------------------------------------------------*/
+/* ----------------------------- 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
+};
+
+/**
+ * Enum describing possible context reset states
+ *
+ * \sa amdgpu_cs_query_reset_state()
+ *
+*/
+enum amdgpu_cs_ctx_reset_state {
+	/** No reset was detected */
+	amdgpu_cs_reset_no_error = 0,
+
+	/** Reset/TDR was detected and context caused */
+	amdgpu_cs_reset_guilty   = 1,
+
+	/** Reset/TDR was detected caused by other context */
+	amdgpu_cs_reset_innocent = 2,
+
+	/** Reset TDR was detected by cause of it unknown */
+	amdgpu_cs_reset_unknown  = 3
+};
+
+/**
+ * For performance reasons and to simplify logic libdrm_amdgpu will handle
+ * IBs only some pre-defined sizes.
+ *
+ * \sa amdgpu_cs_alloc_ib()
+ */
+enum amdgpu_cs_ib_size {
+	amdgpu_cs_ib_size_4K	= 1,
+	amdgpu_cs_ib_size_16K	= 2,
+	amdgpu_cs_ib_size_32K	= 3,
+	amdgpu_cs_ib_size_64K	= 4,
+	amdgpu_cs_ib_size_128K	= 5
+};
+
+/** The number of different IB sizes */
+#define AMDGPU_CS_IB_SIZE_NUM 6
+
+
+/*--------------------------------------------------------------------------*/
+/* -------------------------- 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 to be used when dealing with command
+ * buffers (a.k.a. ibs)
+ *
+ */
+typedef struct amdgpu_ib *amdgpu_ib_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;
+};
+
+/**
+ * Structure describing memory allocation request
+ *
+ * \sa amdgpu_bo_alloc()
+*/
+struct amdgpu_bo_alloc_result {
+	/** Assigned virtual MC Base Address */
+	uint64_t virtual_mc_base_address;
+
+	/** Handle of allocated memory to be used by the given process only. */
+	amdgpu_bo_handle buf_handle;
+};
+
+/**
+ * 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;
+
+	/**
+	 * Assigned virtual MC Base Address.
+	 * \note  This information will be returned only if this buffer was
+	 * allocated in the same process otherwise 0 will be returned.
+	*/
+	uint64_t virtual_mc_base_address;
+
+	/** 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;
+
+	 /** Assigned virtual MC Base Address */
+	uint64_t virtual_mc_base_address;
+};
+
+
+/**
+ *
+ * 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 result of request to allocate GDS
+ *
+ *  \sa amdgpu_gpu_resource_gds_alloc
+ *
+*/
+struct amdgpu_gds_alloc_info {
+	/** Handle assigned to gds allocation */
+	amdgpu_bo_handle resource_handle;
+
+	/** How much was really allocated */
+	uint32_t   gds_memory_size;
+
+	/** Number of GWS resources allocated */
+	uint32_t   gws;
+
+	/** Number of OA resources allocated */
+	uint32_t   oa;
+};
+
+/**
+ * Structure to described allocated command buffer (a.k.a. IB)
+ *
+ * \sa amdgpu_cs_alloc_ib()
+ *
+*/
+struct amdgpu_cs_ib_alloc_result {
+	/** IB allocation handle */
+	amdgpu_ib_handle handle;
+
+	/** Assigned GPU VM MC Address of command buffer */
+	uint64_t	mc_address;
+
+	/** Address to be used for CPU access */
+	void		*cpu;
+};
+
+/**
+ * Structure describing IB
+ *
+ * \sa amdgpu_cs_request, amdgpu_cs_submit()
+ *
+*/
+struct amdgpu_cs_ib_info {
+	/** Special flags */
+	uint64_t      flags;
+
+	/** Handle of command buffer */
+	amdgpu_ib_handle ib_handle;
+
+	/**
+	 * Size of Command Buffer to be submitted.
+	 *   - The size is in units of dwords (4 bytes).
+	 *   - Must be less or equal to the size of allocated IB
+	 *   - Could be 0
+	 */
+	uint32_t       size;
+};
+
+/**
+ * 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;
+
+	/**
+	 * Specify number of resource handles passed.
+	 * Size of 'handles' array
+	 *
+	 */
+	uint32_t number_of_resources;
+
+	/** Array of resources used by submission. */
+	amdgpu_bo_handle   *resources;
+
+	/** Array of resources flags. This is optional and can be NULL. */
+	uint8_t *resource_flags;
+
+	/** 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;
+};
+
+/**
+ * Structure describing request to check submission state using fence
+ *
+ * \sa amdgpu_cs_query_fence_status()
+ *
+*/
+struct amdgpu_cs_query_fence {
+
+	/** In which context IB was sent to execution */
+	amdgpu_context_handle  context;
+
+	/** Timeout in nanoseconds. */
+	uint64_t  timeout_ns;
+
+	/** To which HW IP type the fence belongs */
+	unsigned  ip_type;
+
+	/** IP instance index if there are several IPs of the same type. */
+	unsigned ip_instance;
+
+	/** Ring index of the HW IP */
+	uint32_t      ring;
+
+	/** Flags */
+	uint64_t  flags;
+
+	/** Specify fence for which we need to check
+	 * submission status.*/
+	uint64_t	fence;
+};
+
+/**
+ * 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;
+	/** 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;
+	/** Active render backend pipe number */
+	uint32_t active_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];
+};
+
+
+/*--------------------------------------------------------------------------*/
+/*------------------------- 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 - AMD specific error code\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 - AMD specific error code\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   info	 - \c [out] Pointer to structure which return
+ *				    information about allocated memory
+ *
+ * \return   0 on success\n
+ *          >0 - AMD specific error code\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,
+		    struct amdgpu_bo_alloc_result *info);
+
+/**
+ * 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 - AMD specific error code\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 - AMD specific error code\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 - AMD specific error code\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 - AMD specific error code\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);
+
+/**
+ * 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 - AMD specific error code\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 accessable 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 - AMD specific error code\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 - AMD specific error code\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_lib_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 - AMD specific error code
+ */
+int amdgpu_bo_wait_for_idle(amdgpu_bo_handle buf_handle,
+			    uint64_t timeout_ns,
+			    bool *buffer_busy);
+
+
+/*
+ * Special GPU Resources
+ *
+*/
+
+
+
+/**
+ * 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 - AMD specific error code\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_gpu_resource_query_gds_info(amdgpu_device_handle dev,
+					struct amdgpu_gds_resource_info *
+								gds_info);
+
+
+/**
+ * Allocate GDS partitions
+ *
+ * \param   dev	       - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   gds_size   - \c [in] Size of gds allocation. Must be aligned
+ *				accordingly.
+ * \param   alloc_info - \c [out] Pointer to structure to receive information
+ *				  about allocation
+ *
+ * \return   0 on success\n
+ *          >0 - AMD specific error code\n
+ *          <0 - Negative POSIX Error code
+ *
+ *
+*/
+int amdgpu_gpu_resource_gds_alloc(amdgpu_device_handle dev,
+				   uint32_t gds_size,
+				   struct amdgpu_gds_alloc_info *alloc_info);
+
+
+
+
+/**
+ * Release GDS resource. When GDS and associated resources not needed any
+ * more UMD should free them
+ *
+ * \param   dev	   - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   handle - \c [in] Handle assigned to GDS allocation
+ *
+ * \return   0 on success\n
+ *          >0 - AMD specific error code\n
+ *          <0 - Negative POSIX Error code
+ *
+*/
+int amdgpu_gpu_resource_gds_free(amdgpu_bo_handle handle);
+
+
+
+/*
+ * 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 - AMD specific error code\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   dev	    - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   context - \c [in] GPU Context handle
+ *
+ * \return   0 on success\n
+ *          >0 - AMD specific error code\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_cs_ctx_create()
+ *
+*/
+int amdgpu_cs_ctx_free(amdgpu_device_handle dev,
+		       amdgpu_context_handle context);
+
+/**
+ * Query reset state for the specific GPU Context
+ *
+ * \param   dev	    - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   context - \c [in]  GPU Context handle
+ * \param   state   - \c [out] Reset state status
+ *
+ * \return   0 on success\n
+ *          >0 - AMD specific error code\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_cs_ctx_create()
+ *
+*/
+int amdgpu_cs_query_reset_state(amdgpu_device_handle dev,
+				amdgpu_context_handle context,
+				enum amdgpu_cs_ctx_reset_state *state);
+
+
+/*
+ * Command Buffers Management
+ *
+*/
+
+
+/**
+ * Allocate memory to be filled with PM4 packets and be served as the first
+ * entry point of execution (a.k.a. Indirect Buffer)
+ *
+ * \param   dev	    - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   context - \c [in]  GPU Context which will use IB
+ * \param   ib_size - \c [in]  Size of allocation
+ * \param   output  - \c [out] Pointer to structure to get information about
+ *				   allocated IB
+ *
+ * \return   0 on success\n
+ *          >0 - AMD specific error code\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \sa amdgpu_cs_free_ib()
+ *
+*/
+int amdgpu_cs_alloc_ib(amdgpu_device_handle dev,
+		       amdgpu_context_handle context,
+		       enum amdgpu_cs_ib_size ib_size,
+		       struct amdgpu_cs_ib_alloc_result *output);
+
+/**
+ * If UMD has allocates IBs which doesn’t need any more than those IBs must
+ * be explicitly freed
+ *
+ * \param   dev	    - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   context - \c [in] GPU Context containing IB
+ * \param   handle  - \c [in] IB handle
+ *
+ * \return   0 on success\n
+ *          >0 - AMD specific error code\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \note Libdrm_amdgpu will guarantee that it will correctly detect when it
+ *	is safe to return IB to free pool
+ *
+ * \sa amdgpu_cs_alloc_ib()
+ *
+*/
+int amdgpu_cs_free_ib(amdgpu_device_handle dev,
+		      amdgpu_context_handle context,
+		      amdgpu_ib_handle handle);
+
+/**
+ * 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.
+ *
+ *
+ * \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]  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
+ * \param   fences             - \c [out] Pointer to array of data to get
+ *					  fences to identify submission
+ *					  requests. Timestamps are valid
+ *					  in this GPU context and could be used
+ *					  to identify/detect completion of
+ *					  submission request
+ *
+ * \return   0 on success\n
+ *          >0 - AMD specific error code\n
+ *          <0 - Negative POSIX Error code
+ *
+ * \note It is assumed that by default IB will be returned to free pool
+ *	 automatically by libdrm_amdgpu when submission will completed.
+ *	 It is possible for UMD to make decision to re-use the same IB in
+ *	 this case it should be explicitly freed.\n
+ *	 Accordingly, by default, after submission UMD should not touch passed
+ *	 IBs. If UMD needs to re-use IB then the special flag AMDGPU_CS_REUSE_IB
+ *	 must be passed.
+ *
+ * \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_device_handle  dev,
+		     amdgpu_context_handle context,
+		     uint64_t flags,
+		     struct amdgpu_cs_request *ibs_request,
+		     uint32_t number_of_requests,
+		     uint64_t *fences);
+
+/**
+ *  Query status of Command Buffer Submission
+ *
+ * \param   dev	    - \c [in] Device handle. See #amdgpu_device_initialize()
+ * \param   fence   - \c [in] Structure describing fence to 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 - AMD specific error code\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(amdgpu_device_handle dev,
+				 struct amdgpu_cs_query_fence *fence,
+				 uint32_t *expired);
+
+
+/*
+ * 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 - AMD specific error code\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 - AMD specific error code\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 - AMD specific error code\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 - AMD specific error code\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 - AMD specific error code\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 - AMD specific error code\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 - AMD specific error code\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 - AMD specific error code\n
+ *          <0 - Negative POSIX error code
+ *
+*/
+int amdgpu_query_info(amdgpu_device_handle dev, unsigned info_id,
+		      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 - AMD specific error code\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);
+
+
+
+/**
+ * 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 amdgpu_bo_alloc_result - [out] Handle of allocation to be passed as resource
+ * on submission and be used in other operations.(e.g. for VA submission)
+ * ( Temporally defined amdgpu_bo_alloc_result as parameter for return mc address. )
+ *
+ *
+ * \return 0 on success
+ * >0 - AMD specific error code
+ * <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,
+				    struct amdgpu_bo_alloc_result *info);
+
+
+#endif /* #ifdef _amdgpu_h_ */
+
+
diff --git a/amdgpu/amdgpu_bo.c b/amdgpu/amdgpu_bo.c
new file mode 100644
index 0000000..ce7e9d1
--- /dev/null
+++ b/amdgpu/amdgpu_bo.c
@@ -0,0 +1,622 @@
+/*
+ * 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.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#include <stdlib.h>
+#include <stdio.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 "xf86drm.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+#include "util_hash_table.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);
+}
+
+void amdgpu_bo_free_internal(amdgpu_bo_handle bo)
+{
+	/* Remove the buffer from the hash tables. */
+	pthread_mutex_lock(&bo->dev->bo_table_mutex);
+	util_hash_table_remove(bo->dev->bo_handles,
+			       (void*)(uintptr_t)bo->handle);
+	if (bo->flink_name) {
+		util_hash_table_remove(bo->dev->bo_flink_names,
+				       (void*)(uintptr_t)bo->flink_name);
+	}
+	pthread_mutex_unlock(&bo->dev->bo_table_mutex);
+
+	/* Release CPU access. */
+	if (bo->cpu_map_count > 0) {
+		bo->cpu_map_count = 1;
+		amdgpu_bo_cpu_unmap(bo);
+	}
+
+	amdgpu_close_kms_handle(bo->dev, bo->handle);
+	pthread_mutex_destroy(&bo->cpu_access_mutex);
+	amdgpu_vamgr_free_va(&bo->dev->vamgr, bo->virtual_mc_base_address, bo->alloc_size);
+	free(bo);
+}
+
+int amdgpu_bo_alloc(amdgpu_device_handle dev,
+		    struct amdgpu_bo_alloc_request *alloc_buffer,
+		    struct amdgpu_bo_alloc_result *info)
+{
+	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 & AMDGPU_GEM_DOMAIN_MASK;
+	args.in.domain_flags = alloc_buffer->flags & AMDGPU_GEM_CREATE_CPU_GTT_MASK;
+
+	/* 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);
+
+	/* map the buffer to the GPU virtual address space */
+	{
+		union drm_amdgpu_gem_va va;
+
+		memset(&va, 0, sizeof(va));
+
+		bo->virtual_mc_base_address = amdgpu_vamgr_find_va(&dev->vamgr, alloc_buffer->alloc_size, alloc_buffer->phys_alignment);
+
+		va.in.handle = bo->handle;
+		va.in.operation = AMDGPU_VA_OP_MAP;
+		va.in.flags = 	AMDGPU_VM_PAGE_READABLE |
+				AMDGPU_VM_PAGE_WRITEABLE |
+				AMDGPU_VM_PAGE_EXECUTABLE;
+		va.in.va_address = bo->virtual_mc_base_address;
+
+		r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_VA, &va, sizeof(va));
+		if (r || va.out.result == AMDGPU_VA_RESULT_ERROR) {
+			amdgpu_bo_free_internal(bo);
+			return r;
+		}
+		pthread_mutex_lock(&dev->bo_table_mutex);
+
+		util_hash_table_set(dev->bo_vas,
+				    (void*)(uintptr_t)bo->virtual_mc_base_address, bo);
+		pthread_mutex_unlock(&dev->bo_table_mutex);
+	}
+
+	info->buf_handle = bo;
+	info->virtual_mc_base_address = bo->virtual_mc_base_address;
+	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;
+
+	/* 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 = (intptr_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->virtual_mc_base_address = bo->virtual_mc_base_address;
+	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:
+		r = amdgpu_bo_export_flink(bo);
+		if (r)
+			return r;
+
+		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 = {};
+	union drm_amdgpu_gem_va va;
+	struct amdgpu_bo *bo = NULL;
+	int r;
+	int dma_fd;
+	uint64_t dma_buf_size = 0;
+
+	/* 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) {
+			return r;
+		}
+
+		/* Query the buffer size. */
+		size = lseek(shared_handle, 0, SEEK_END);
+		if (size == (off_t)-1) {
+			amdgpu_close_kms_handle(dev, handle);
+			return -errno;
+		}
+		lseek(shared_handle, 0, SEEK_SET);
+
+		dma_buf_size = size;
+		shared_handle = handle;
+	}
+
+	/* 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);
+
+	/* 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) {
+		pthread_mutex_unlock(&dev->bo_table_mutex);
+
+		/* The buffer already exists, just bump the refcount. */
+		atomic_inc(&bo->refcount);
+
+		output->buf_handle = bo;
+		output->alloc_size = bo->alloc_size;
+		output->virtual_mc_base_address =
+			bo->virtual_mc_base_address;
+		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);
+
+	bo->virtual_mc_base_address = amdgpu_vamgr_find_va(&dev->vamgr, bo->alloc_size, 1 << 20);
+
+	memset(&va, 0, sizeof(va));
+	va.in.handle = bo->handle;
+	va.in.operation = AMDGPU_VA_OP_MAP;
+	va.in.va_address = bo->virtual_mc_base_address;
+	va.in.flags =	AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_PAGE_WRITEABLE |
+			AMDGPU_VM_PAGE_EXECUTABLE;
+
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_VA, &va, sizeof(va));
+	if (r || va.out.result == AMDGPU_VA_RESULT_ERROR) {
+		pthread_mutex_unlock(&dev->bo_table_mutex);
+		amdgpu_vamgr_free_va(&dev->vamgr, bo->virtual_mc_base_address, bo->alloc_size);
+		amdgpu_bo_reference(&bo, NULL);
+		return r;
+	}
+
+	util_hash_table_set(dev->bo_vas,
+			    (void*)(uintptr_t)bo->virtual_mc_base_address, bo);
+	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;
+	output->virtual_mc_base_address = bo->virtual_mc_base_address;
+	return 0;
+}
+
+int amdgpu_bo_free(amdgpu_bo_handle buf_handle)
+{
+	/* Just drop the reference. */
+	amdgpu_bo_reference(&buf_handle, NULL);
+	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 = 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 -EBADMSG;
+	}
+
+	bo->cpu_map_count--;
+	if (bo->cpu_map_count > 0) {
+		/* mapped multiple times */
+		pthread_mutex_unlock(&bo->cpu_access_mutex);
+		return 0;
+	}
+
+	r = 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,
+				    struct amdgpu_bo_alloc_result *info)
+{
+	int r;
+	struct amdgpu_bo *bo;
+	struct drm_amdgpu_gem_userptr args;
+	union drm_amdgpu_gem_va va;
+
+	memset(&args, 0, sizeof(args));
+	args.addr = (uint64_t)cpu;
+	args.flags = AMDGPU_GEM_USERPTR_ANONONLY | AMDGPU_GEM_USERPTR_REGISTER;
+	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;
+	bo->virtual_mc_base_address = amdgpu_vamgr_find_va(&dev->vamgr, size, 4 * 1024);
+
+	memset(&va, 0, sizeof(va));
+	va.in.handle = bo->handle;
+	va.in.operation = AMDGPU_VA_OP_MAP;
+	va.in.flags =	AMDGPU_VM_PAGE_READABLE | AMDGPU_VM_PAGE_WRITEABLE |
+			AMDGPU_VM_PAGE_EXECUTABLE;
+	va.in.va_address = bo->virtual_mc_base_address;
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_GEM_VA, &va, sizeof(va));
+	if (r || va.out.result == AMDGPU_VA_RESULT_ERROR) {
+		amdgpu_bo_free_internal(bo);
+		return r;
+	}
+	util_hash_table_set(dev->bo_vas,
+			    (void*)(uintptr_t)bo->virtual_mc_base_address, bo);
+	info->buf_handle = bo;
+	info->virtual_mc_base_address = bo->virtual_mc_base_address;
+	return r;
+}
diff --git a/amdgpu/amdgpu_cs.c b/amdgpu/amdgpu_cs.c
new file mode 100644
index 0000000..614904d
--- /dev/null
+++ b/amdgpu/amdgpu_cs.c
@@ -0,0 +1,981 @@
+/*
+ * 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.
+ *
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/ioctl.h>
+
+#include "xf86drm.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+
+/**
+ * Create an IB buffer.
+ *
+ * \param   dev - \c [in] Device handle
+ * \param   context - \c [in] GPU Context
+ * \param   ib_size - \c [in] Size of allocation
+ * \param   ib - \c [out] return the pointer to the created IB buffer
+ *
+ * \return  0 on success otherwise POSIX Error code
+*/
+static int amdgpu_cs_create_ib(amdgpu_device_handle dev,
+			       amdgpu_context_handle context,
+			       enum amdgpu_cs_ib_size ib_size,
+			       amdgpu_ib_handle *ib)
+{
+	struct amdgpu_bo_alloc_request alloc_buffer;
+	struct amdgpu_bo_alloc_result info;
+	int r;
+	void *cpu;
+	struct amdgpu_ib *new_ib;
+
+	memset(&alloc_buffer, 0, sizeof(alloc_buffer));
+
+	switch (ib_size) {
+	case amdgpu_cs_ib_size_4K:
+		alloc_buffer.alloc_size = 4 * 1024;
+		break;
+	case amdgpu_cs_ib_size_16K:
+		alloc_buffer.alloc_size = 16 * 1024;
+		break;
+	case amdgpu_cs_ib_size_32K:
+		alloc_buffer.alloc_size = 32 * 1024;
+		break;
+	case amdgpu_cs_ib_size_64K:
+		alloc_buffer.alloc_size = 64 * 1024;
+		break;
+	case amdgpu_cs_ib_size_128K:
+		alloc_buffer.alloc_size = 128 * 1024;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	alloc_buffer.phys_alignment = 4 * 1024;
+
+	alloc_buffer.preferred_heap = AMDGPU_GEM_DOMAIN_GTT;
+
+	r = amdgpu_bo_alloc(dev,
+			    &alloc_buffer,
+			    &info);
+	if (r)
+		return r;
+
+	r = amdgpu_bo_cpu_map(info.buf_handle, &cpu);
+	if (r) {
+		amdgpu_bo_free(info.buf_handle);
+		return r;
+	}
+
+	new_ib = malloc(sizeof(struct amdgpu_ib));
+	if (NULL == new_ib) {
+		amdgpu_bo_cpu_unmap(info.buf_handle);
+		amdgpu_bo_free(info.buf_handle);
+		return -ENOMEM;
+	}
+
+	new_ib->buf_handle = info.buf_handle;
+	new_ib->cpu = cpu;
+	new_ib->virtual_mc_base_address = info.virtual_mc_base_address;
+	new_ib->ib_size = ib_size;
+	*ib = new_ib;
+	return 0;
+}
+
+/**
+ * Destroy an IB buffer.
+ *
+ * \param   dev - \c [in]  Device handle
+ * \param   ib - \c [in] the IB buffer
+ *
+ * \return  0 on success otherwise POSIX Error code
+*/
+static int amdgpu_cs_destroy_ib(amdgpu_device_handle dev,
+				amdgpu_ib_handle ib)
+{
+	int r;
+	r = amdgpu_bo_cpu_unmap(ib->buf_handle);
+	if (r)
+		return r;
+
+	r = amdgpu_bo_free(ib->buf_handle);
+	if (r)
+		return r;
+
+	free(ib);
+	return 0;
+}
+
+/**
+ * Initialize IB pools to empty.
+ *
+ * \param   context - \c [in]  GPU Context
+ *
+ * \return  0 on success otherwise POSIX Error code
+*/
+static int amdgpu_cs_init_ib_pool(amdgpu_context_handle context)
+{
+	int i;
+	int r;
+
+	r = pthread_mutex_init(&context->pool_mutex, NULL);
+	if (r)
+		return r;
+
+	for (i = 0; i < AMDGPU_CS_IB_SIZE_NUM; i++)
+		LIST_INITHEAD(&context->ib_pools[i]);
+
+	return 0;
+}
+
+/**
+ * Allocate an IB buffer from IB pools.
+ *
+ * \param   dev - \c [in]  Device handle
+ * \param   context - \c [in] GPU Context
+ * \param   ib_size - \c [in]  Size of allocation
+ * \param   ib - \c [out] return the pointer to the allocated IB buffer
+ *
+ * \return  0 on success otherwise POSIX Error code
+*/
+static int amdgpu_cs_alloc_from_ib_pool(amdgpu_device_handle dev,
+					amdgpu_context_handle context,
+					enum amdgpu_cs_ib_size ib_size,
+					amdgpu_ib_handle *ib)
+{
+	int r;
+	struct list_head *head;
+	head = &context->ib_pools[ib_size];
+
+	r = -ENOMEM;
+	pthread_mutex_lock(&context->pool_mutex);
+	if (!LIST_IS_EMPTY(head)) {
+		*ib = LIST_ENTRY(struct amdgpu_ib, head->next, list_node);
+		LIST_DEL(&(*ib)->list_node);
+		r = 0;
+	}
+	pthread_mutex_unlock(&context->pool_mutex);
+
+	return r;
+}
+
+/**
+ * Free an IB buffer to IB pools.
+ *
+ * \param   context - \c [in]  GPU Context
+ * \param   ib - \c [in] the IB buffer
+ *
+ * \return  N/A
+*/
+static void amdgpu_cs_free_to_ib_pool(amdgpu_context_handle context,
+				      amdgpu_ib_handle ib)
+{
+	struct list_head *head;
+	head = &context->ib_pools[ib->ib_size];
+	pthread_mutex_lock(&context->pool_mutex);
+	LIST_ADD(&ib->list_node, head);
+	pthread_mutex_unlock(&context->pool_mutex);
+	return;
+}
+
+/**
+ * Destroy all IB buffers in pools
+ *
+ * \param   dev - \c [in]  Device handle
+ * \param   context - \c [in]  GPU Context
+ *
+ * \return  0 on success otherwise POSIX Error code
+*/
+static int amdgpu_cs_destroy_ib_pool(amdgpu_device_handle dev,
+				     amdgpu_context_handle context)
+{
+	int i;
+	int r;
+	struct list_head *head;
+	struct amdgpu_ib *next;
+	struct amdgpu_ib *storage;
+
+	r = 0;
+	pthread_mutex_lock(&context->pool_mutex);
+	for (i = 0; i < AMDGPU_CS_IB_SIZE_NUM; i++) {
+		head = &context->ib_pools[i];
+		LIST_FOR_EACH_ENTRY_SAFE(next, storage, head, list_node) {
+			r = amdgpu_cs_destroy_ib(dev, next);
+			if (r)
+				break;
+		}
+	}
+	pthread_mutex_unlock(&context->pool_mutex);
+	pthread_mutex_destroy(&context->pool_mutex);
+	return r;
+}
+
+/**
+ * Initialize pending IB lists
+ *
+ * \param   context - \c [in]  GPU Context
+ *
+ * \return  0 on success otherwise POSIX Error code
+*/
+static int amdgpu_cs_init_pendings(amdgpu_context_handle context)
+{
+	unsigned ip, inst;
+	uint32_t ring;
+	int r;
+
+	r = pthread_mutex_init(&context->pendings_mutex, NULL);
+	if (r)
+		return r;
+
+	for (ip = 0; ip < AMDGPU_HW_IP_NUM; ip++)
+		for (inst = 0; inst < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; inst++)
+			for (ring = 0; ring < AMDGPU_CS_MAX_RINGS; ring++)
+				LIST_INITHEAD(&context->pendings[ip][inst][ring]);
+
+	LIST_INITHEAD(&context->freed);
+	return 0;
+}
+
+/**
+ * Free pending IBs
+ *
+ * \param   dev - \c [in]  Device handle
+ * \param   context - \c [in]  GPU Context
+ *
+ * \return  0 on success otherwise POSIX Error code
+*/
+static int amdgpu_cs_destroy_pendings(amdgpu_device_handle dev,
+				      amdgpu_context_handle context)
+{
+	int ip, inst;
+	uint32_t ring;
+	int r;
+	struct amdgpu_ib *next;
+	struct amdgpu_ib *s;
+	struct list_head *head;
+
+	r = 0;
+	pthread_mutex_lock(&context->pendings_mutex);
+	for (ip = 0; ip < AMDGPU_HW_IP_NUM; ip++)
+		for (inst = 0; inst < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; inst++)
+			for (ring = 0; ring < AMDGPU_CS_MAX_RINGS; ring++) {
+				head = &context->pendings[ip][inst][ring];
+				LIST_FOR_EACH_ENTRY_SAFE(next, s, head, list_node) {
+					r = amdgpu_cs_destroy_ib(dev, next);
+					if (r)
+						break;
+				}
+			}
+
+	head = &context->freed;
+	LIST_FOR_EACH_ENTRY_SAFE(next, s, head, list_node) {
+		r = amdgpu_cs_destroy_ib(dev, next);
+		if (r)
+			break;
+	}
+
+	pthread_mutex_unlock(&context->pendings_mutex);
+	pthread_mutex_destroy(&context->pendings_mutex);
+	return r;
+}
+
+/**
+ * Add IB to pending IB lists without holding sequence_mutex.
+ *
+ * \param   context - \c [in]  GPU Context
+ * \param   ib - \c [in]  ib to added to pending lists
+ * \param   ip - \c [in]  hw ip block
+ * \param   ip_instance - \c [in]  instance of the hw ip block
+ * \param   ring - \c [in]  Ring of hw ip
+ *
+ * \return  N/A
+*/
+static void amdgpu_cs_add_pending(amdgpu_context_handle context,
+				  amdgpu_ib_handle ib,
+				  unsigned ip, unsigned ip_instance,
+				  uint32_t ring)
+{
+	struct list_head *head;
+	pthread_mutex_lock(&context->pendings_mutex);
+	head = &context->pendings[ip][ip_instance][ring];
+	LIST_ADDTAIL(&ib->list_node, head);
+	pthread_mutex_unlock(&context->pendings_mutex);
+	return;
+}
+
+/**
+ * Garbage collector on a pending IB list without holding pendings_mutex.
+ * This function by itself is not multithread safe.
+ *
+ * \param   context - \c [in]  GPU Context
+ * \param   ip - \c [in]  hw ip block
+ * \param   ip_instance - \c [in]  instance of the hw ip block
+ * \param   ring - \c [in]  Ring of hw ip
+ * \param   expired_fence - \c [in]  fence expired
+ *
+ * \return  N/A
+ * \note Hold pendings_mutex before calling this function.
+*/
+static void amdgpu_cs_pending_gc_not_safe(amdgpu_context_handle context,
+					  unsigned ip, unsigned ip_instance,
+					  uint32_t ring,
+					  uint64_t expired_fence)
+{
+	struct list_head *head;
+	struct amdgpu_ib *next;
+	struct amdgpu_ib *s;
+	int r;
+
+	head = &context->pendings[ip][ip_instance][ring];
+	LIST_FOR_EACH_ENTRY_SAFE(next, s, head, list_node)
+		if (next->cs_handle <= expired_fence) {
+			LIST_DEL(&next->list_node);
+			amdgpu_cs_free_to_ib_pool(context, next);
+		} else {
+			/* The pending list is a sorted list.
+			   There is no need to continue. */
+			break;
+		}
+
+	/* walk the freed list as well */
+	head = &context->freed;
+	LIST_FOR_EACH_ENTRY_SAFE(next, s, head, list_node) {
+		bool busy;
+
+		r = amdgpu_bo_wait_for_idle(next->buf_handle, 0, &busy);
+		if (r || busy)
+			break;
+
+		LIST_DEL(&next->list_node);
+		amdgpu_cs_free_to_ib_pool(context, next);
+	}
+
+	return;
+}
+
+/**
+ * Garbage collector on a pending IB list
+ *
+ * \param   context - \c [in]  GPU Context
+ * \param   ip - \c [in]  hw ip block
+ * \param   ip_instance - \c [in]  instance of the hw ip block
+ * \param   ring - \c [in]  Ring of hw ip
+ * \param   expired_fence - \c [in]  fence expired
+ *
+ * \return  N/A
+*/
+static void amdgpu_cs_pending_gc(amdgpu_context_handle context,
+				 unsigned ip, unsigned ip_instance,
+				 uint32_t ring,
+				 uint64_t expired_fence)
+{
+	pthread_mutex_lock(&context->pendings_mutex);
+	amdgpu_cs_pending_gc_not_safe(context, ip, ip_instance, ring,
+				      expired_fence);
+	pthread_mutex_unlock(&context->pendings_mutex);
+	return;
+}
+
+/**
+ * Garbage collector on all pending IB lists
+ *
+ * \param   context - \c [in]  GPU Context
+ *
+ * \return  N/A
+*/
+static void amdgpu_cs_all_pending_gc(amdgpu_context_handle context)
+{
+	unsigned ip, inst;
+	uint32_t ring;
+	uint64_t expired_fences[AMDGPU_HW_IP_NUM][AMDGPU_HW_IP_INSTANCE_MAX_COUNT][AMDGPU_CS_MAX_RINGS];
+
+	pthread_mutex_lock(&context->sequence_mutex);
+	for (ip = 0; ip < AMDGPU_HW_IP_NUM; ip++)
+		for (inst = 0; inst < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; inst++)
+			for (ring = 0; ring < AMDGPU_CS_MAX_RINGS; ring++)
+				expired_fences[ip][inst][ring] =
+					context->expired_fences[ip][inst][ring];
+	pthread_mutex_unlock(&context->sequence_mutex);
+
+	pthread_mutex_lock(&context->pendings_mutex);
+	for (ip = 0; ip < AMDGPU_HW_IP_NUM; ip++)
+		for (inst = 0; inst < AMDGPU_HW_IP_INSTANCE_MAX_COUNT; inst++)
+			for (ring = 0; ring < AMDGPU_CS_MAX_RINGS; ring++)
+				amdgpu_cs_pending_gc_not_safe(context, ip, inst, ring,
+					expired_fences[ip][inst][ring]);
+	pthread_mutex_unlock(&context->pendings_mutex);
+}
+
+/**
+ * Allocate an IB buffer
+ * If there is no free IB buffer in pools, create one.
+ *
+ * \param   dev - \c [in] Device handle
+ * \param   context - \c [in] GPU Context
+ * \param   ib_size - \c [in] Size of allocation
+ * \param   ib - \c [out] return the pointer to the allocated IB buffer
+ *
+ * \return  0 on success otherwise POSIX Error code
+*/
+static int amdgpu_cs_alloc_ib_local(amdgpu_device_handle dev,
+				    amdgpu_context_handle context,
+				    enum amdgpu_cs_ib_size ib_size,
+				    amdgpu_ib_handle *ib)
+{
+	int r;
+
+	r = amdgpu_cs_alloc_from_ib_pool(dev, context, ib_size, ib);
+	if (!r)
+		return r;
+
+	amdgpu_cs_all_pending_gc(context);
+
+	/* Retry to allocate from free IB pools after garbage collector. */
+	r = amdgpu_cs_alloc_from_ib_pool(dev, context, ib_size, ib);
+	if (!r)
+		return r;
+
+	/* There is no suitable IB in free pools. Create one. */
+	r = amdgpu_cs_create_ib(dev, context, ib_size, ib);
+	return r;
+}
+
+int amdgpu_cs_alloc_ib(amdgpu_device_handle dev,
+		       amdgpu_context_handle context,
+		       enum amdgpu_cs_ib_size ib_size,
+		       struct amdgpu_cs_ib_alloc_result *output)
+{
+	int r;
+	amdgpu_ib_handle ib;
+
+	if (NULL == dev)
+		return -EINVAL;
+	if (NULL == context)
+		return -EINVAL;
+	if (NULL == output)
+		return -EINVAL;
+	if (ib_size >= AMDGPU_CS_IB_SIZE_NUM)
+		return -EINVAL;
+
+	r = amdgpu_cs_alloc_ib_local(dev, context, ib_size, &ib);
+	if (!r) {
+		output->handle = ib;
+		output->cpu = ib->cpu;
+		output->mc_address = ib->virtual_mc_base_address;
+	}
+
+	return r;
+}
+
+int amdgpu_cs_free_ib(amdgpu_device_handle dev,
+		      amdgpu_context_handle context,
+		      amdgpu_ib_handle handle)
+{
+	if (NULL == dev)
+		return -EINVAL;
+	if (NULL == context)
+		return -EINVAL;
+	if (NULL == handle)
+		return -EINVAL;
+
+	pthread_mutex_lock(&context->pendings_mutex);
+	LIST_ADD(&handle->list_node, &context->freed);
+	pthread_mutex_unlock(&context->pendings_mutex);
+	return 0;
+}
+
+/**
+ * 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 r;
+
+	if (NULL == dev)
+		return -EINVAL;
+	if (NULL == context)
+		return -EINVAL;
+
+	gpu_context = calloc(1, sizeof(struct amdgpu_context));
+	if (NULL == gpu_context)
+		return -ENOMEM;
+
+	r = pthread_mutex_init(&gpu_context->sequence_mutex, NULL);
+	if (r)
+		goto error_mutex;
+
+	r = amdgpu_cs_init_ib_pool(gpu_context);
+	if (r)
+		goto error_pool;
+
+	r = amdgpu_cs_init_pendings(gpu_context);
+	if (r)
+		goto error_pendings;
+
+	r = amdgpu_cs_alloc_ib_local(dev, gpu_context, amdgpu_cs_ib_size_4K,
+				     &gpu_context->fence_ib);
+	if (r)
+		goto error_fence_ib;
+
+
+	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_kernel;
+
+	gpu_context->id = args.out.alloc.ctx_id;
+	*context = (amdgpu_context_handle)gpu_context;
+
+	return 0;
+
+error_kernel:
+	amdgpu_cs_free_ib(dev, gpu_context, gpu_context->fence_ib);
+
+error_fence_ib:
+	amdgpu_cs_destroy_pendings(dev, gpu_context);
+
+error_pendings:
+	amdgpu_cs_destroy_ib_pool(dev, gpu_context);
+
+error_pool:
+	pthread_mutex_destroy(&gpu_context->sequence_mutex);
+
+error_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_device_handle dev,
+		       amdgpu_context_handle context)
+{
+	int r;
+	union drm_amdgpu_ctx args;
+
+	if (NULL == dev)
+		return -EINVAL;
+	if (NULL == context)
+		return -EINVAL;
+
+	r = amdgpu_cs_free_ib(dev, context, context->fence_ib);
+	if (r)
+		return r;
+
+	r = amdgpu_cs_destroy_pendings(dev, context);
+	if (r)
+		return r;
+
+	r = amdgpu_cs_destroy_ib_pool(dev, context);
+	if (r)
+		return r;
+
+	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(dev->fd, DRM_AMDGPU_CTX, &args, sizeof(args));
+
+	free(context);
+
+	return r;
+}
+
+static int amdgpu_cs_create_bo_list(amdgpu_device_handle dev,
+				    amdgpu_context_handle context,
+				    struct amdgpu_cs_request *request,
+				    amdgpu_ib_handle fence_ib,
+				    uint32_t *handle)
+{
+	struct drm_amdgpu_bo_list_entry *list;
+	union drm_amdgpu_bo_list args;
+	unsigned num_resources;
+	unsigned i;
+	int r;
+
+	num_resources = request->number_of_resources;
+	if (fence_ib)
+		++num_resources;
+
+	list = alloca(sizeof(struct drm_amdgpu_bo_list_entry) * num_resources);
+
+	memset(&args, 0, sizeof(args));
+	args.in.operation = AMDGPU_BO_LIST_OP_CREATE;
+	args.in.bo_number = num_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 < request->number_of_resources; i++) {
+		list[i].bo_handle = request->resources[i]->handle;
+		if (request->resource_flags)
+			list[i].bo_priority = request->resource_flags[i];
+		else
+			list[i].bo_priority = 0;
+	}
+
+	if (fence_ib)
+		list[i].bo_handle = fence_ib->buf_handle->handle;
+
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_BO_LIST,
+				&args, sizeof(args));
+	if (r)
+		return r;
+
+	*handle = args.out.list_handle;
+	return 0;
+}
+
+static int amdgpu_cs_free_bo_list(amdgpu_device_handle dev, uint32_t handle)
+{
+	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 = handle;
+
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_BO_LIST,
+				&args, sizeof(args));
+
+	return r;
+}
+
+static uint32_t amdgpu_cs_fence_index(unsigned ip, unsigned ring)
+{
+	return ip * AMDGPU_CS_MAX_RINGS + ring;
+}
+
+/**
+ * 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_device_handle dev,
+				amdgpu_context_handle context,
+				struct amdgpu_cs_request *ibs_request,
+				uint64_t *fence)
+{
+	int r;
+	uint32_t i, size;
+	union drm_amdgpu_cs cs;
+	uint64_t *chunk_array;
+	struct drm_amdgpu_cs_chunk *chunks;
+	struct drm_amdgpu_cs_chunk_data *chunk_data;
+
+	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;
+
+	size = (ibs_request->number_of_ibs + 1) * ((sizeof(uint64_t) +
+		sizeof(struct drm_amdgpu_cs_chunk) +
+		sizeof(struct drm_amdgpu_cs_chunk_data)) +
+	       ibs_request->number_of_resources + 1) *
+	       sizeof(struct drm_amdgpu_bo_list_entry);
+	chunk_array = malloc(size);
+	if (NULL == chunk_array)
+		return -ENOMEM;
+	memset(chunk_array, 0, size);
+
+	chunks = (struct drm_amdgpu_cs_chunk *)(chunk_array + ibs_request->number_of_ibs + 1);
+	chunk_data = (struct drm_amdgpu_cs_chunk_data *)(chunks + ibs_request->number_of_ibs + 1);
+
+	memset(&cs, 0, sizeof(cs));
+	cs.in.chunks = (uint64_t)(uintptr_t)chunk_array;
+	cs.in.ctx_id = context->id;
+	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.handle = ib->ib_handle->buf_handle->handle;
+		chunk_data[i].ib_data.va_start = ib->ib_handle->virtual_mc_base_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;
+
+		if (ib->flags & AMDGPU_CS_GFX_IB_CE)
+			chunk_data[i].ib_data.flags = AMDGPU_IB_FLAG_CE;
+	}
+
+	r = amdgpu_cs_create_bo_list(dev, context, ibs_request, NULL,
+				     &cs.in.bo_list_handle);
+	if (r)
+		goto error_unlock;
+
+	pthread_mutex_lock(&context->sequence_mutex);
+
+	if (ibs_request->ip_type != AMDGPU_HW_IP_UVD &&
+	    ibs_request->ip_type != AMDGPU_HW_IP_VCE) {
+		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 = context->fence_ib->buf_handle->handle;
+		/* offset */
+		chunk_data[i].fence_data.offset = amdgpu_cs_fence_index(
+			ibs_request->ip_type, ibs_request->ring);
+		chunk_data[i].fence_data.offset *= sizeof(uint64_t);
+	}
+
+	r = drmCommandWriteRead(dev->fd, DRM_AMDGPU_CS,
+				&cs, sizeof(cs));
+	if (r)
+		goto error_unlock;
+
+
+	/* Hold sequence_mutex while adding record to the pending list.
+	   So the pending list is a sorted list according to fence value. */
+
+	for (i = 0; i < ibs_request->number_of_ibs; i++) {
+		struct amdgpu_cs_ib_info *ib;
+
+		ib = &ibs_request->ibs[i];
+		if (ib->flags & AMDGPU_CS_REUSE_IB)
+			continue;
+
+		ib->ib_handle->cs_handle = cs.out.handle;
+
+		amdgpu_cs_add_pending(context, ib->ib_handle, ibs_request->ip_type,
+				      ibs_request->ip_instance,
+				      ibs_request->ring);
+	}
+
+	*fence = cs.out.handle;
+
+	pthread_mutex_unlock(&context->sequence_mutex);
+
+	r = amdgpu_cs_free_bo_list(dev, cs.in.bo_list_handle);
+	if (r)
+		goto error_free;
+
+	free(chunk_array);
+	return 0;
+
+error_unlock:
+	pthread_mutex_unlock(&context->sequence_mutex);
+
+error_free:
+	free(chunk_array);
+	return r;
+}
+
+int amdgpu_cs_submit(amdgpu_device_handle  dev,
+		     amdgpu_context_handle context,
+		     uint64_t flags,
+		     struct amdgpu_cs_request *ibs_request,
+		     uint32_t number_of_requests,
+		     uint64_t *fences)
+{
+	int r;
+	uint32_t i;
+
+	if (NULL == dev)
+		return -EINVAL;
+	if (NULL == context)
+		return -EINVAL;
+	if (NULL == ibs_request)
+		return -EINVAL;
+	if (NULL == fences)
+		return -EINVAL;
+
+	r = 0;
+	for (i = 0; i < number_of_requests; i++) {
+		r = amdgpu_cs_submit_one(dev, context, ibs_request, fences);
+		if (r)
+			break;
+		fences++;
+		ibs_request++;
+	}
+
+	return r;
+}
+
+/**
+ * Calculate absolute timeout.
+ *
+ * \param   timeout - \c [in] timeout in nanoseconds.
+ *
+ * \return  absolute timeout in nanoseconds
+*/
+uint64_t amdgpu_cs_calculate_timeout(uint64_t timeout)
+{
+	int r;
+
+	if (timeout != AMDGPU_TIMEOUT_INFINITE) {
+		struct timespec current;
+		r = clock_gettime(CLOCK_MONOTONIC, &current);
+		if (r)
+			return r;
+
+		timeout += ((uint64_t)current.tv_sec) * 1000000000ull;
+		timeout += current.tv_nsec;
+	}
+	return timeout;
+}
+
+static int amdgpu_ioctl_wait_cs(amdgpu_device_handle dev,
+				unsigned ip,
+				unsigned ip_instance,
+				uint32_t ring,
+				uint64_t handle,
+				uint64_t timeout_ns,
+				bool *busy)
+{
+	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.timeout = amdgpu_cs_calculate_timeout(timeout_ns);
+
+	/* Handle errors manually here because of timeout */
+	r = ioctl(dev->fd, DRM_IOCTL_AMDGPU_WAIT_CS, &args);
+	if (r == -1 && (errno == EINTR || errno == EAGAIN)) {
+		*busy = true;
+		return 0;
+	} else if (r)
+		return -errno;
+
+	*busy = args.out.status;
+	return 0;
+}
+
+int amdgpu_cs_query_fence_status(amdgpu_device_handle dev,
+				 struct amdgpu_cs_query_fence *fence,
+				 uint32_t *expired)
+{
+	amdgpu_context_handle context;
+	uint64_t *signaled_fence;
+	uint64_t *expired_fence;
+	unsigned ip_type, ip_instance;
+	uint32_t ring;
+	bool busy = true;
+	int r;
+
+	if (NULL == dev)
+		return -EINVAL;
+	if (NULL == fence)
+		return -EINVAL;
+	if (NULL == expired)
+		return -EINVAL;
+	if (NULL == fence->context)
+		return -EINVAL;
+	if (fence->ip_type >= AMDGPU_HW_IP_NUM)
+		return -EINVAL;
+	if (fence->ring >= AMDGPU_CS_MAX_RINGS)
+		return -EINVAL;
+
+	context = fence->context;
+	ip_type = fence->ip_type;
+	ip_instance = fence->ip_instance;
+	ring = fence->ring;
+	signaled_fence = context->fence_ib->cpu;
+	signaled_fence += amdgpu_cs_fence_index(ip_type, ring);
+	expired_fence = &context->expired_fences[ip_type][ip_instance][ring];
+	*expired = false;
+
+	pthread_mutex_lock(&context->sequence_mutex);
+	if (fence->fence <= *expired_fence) {
+		/* This fence value is expired already. */
+		pthread_mutex_unlock(&context->sequence_mutex);
+		*expired = true;
+		return 0;
+	}
+
+	if (fence->fence <= *signaled_fence) {
+		/* This fence value is signaled already. */
+		*expired_fence = *signaled_fence;
+		pthread_mutex_unlock(&context->sequence_mutex);
+		amdgpu_cs_pending_gc(context, ip_type, ip_instance, ring,
+				     fence->fence);
+		*expired = true;
+		return 0;
+	}
+
+	pthread_mutex_unlock(&context->sequence_mutex);
+
+	r = amdgpu_ioctl_wait_cs(dev, ip_type, ip_instance, ring,
+				 fence->fence, fence->timeout_ns, &busy);
+	if (!r && !busy) {
+		*expired = true;
+		pthread_mutex_lock(&context->sequence_mutex);
+		/* The thread doesn't hold sequence_mutex. Other thread could
+		   update *expired_fence already. Check whether there is a
+		   newerly expired fence. */
+		if (fence->fence > *expired_fence) {
+			*expired_fence = fence->fence;
+			pthread_mutex_unlock(&context->sequence_mutex);
+			amdgpu_cs_pending_gc(context, ip_type, ip_instance,
+					     ring, fence->fence);
+		} else {
+			pthread_mutex_unlock(&context->sequence_mutex);
+		}
+	}
+
+	return r;
+}
+
diff --git a/amdgpu/amdgpu_device.c b/amdgpu/amdgpu_device.c
new file mode 100644
index 0000000..66fa187
--- /dev/null
+++ b/amdgpu/amdgpu_device.c
@@ -0,0 +1,242 @@
+/*
+ * 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
+ *
+ *
+ */
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "xf86drm.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+#include "util_hash_table.h"
+
+#define PTR_TO_UINT(x) ((unsigned)((intptr_t)(x)))
+#define UINT_TO_PTR(x) ((void *)((intptr_t)(x)))
+#define RENDERNODE_MINOR_MASK 0xff7f
+
+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);
+	struct stat stat;
+	fstat(fd, &stat);
+
+	if (!S_ISCHR(stat.st_mode))
+		return stat.st_dev ^ stat.st_ino;
+	else
+		return stat.st_dev ^ (stat.st_rdev & RENDERNODE_MINOR_MASK);
+}
+
+static int fd_compare(void *key1, void *key2)
+{
+	int fd1 = PTR_TO_UINT(key1);
+	int fd2 = PTR_TO_UINT(key2);
+	struct stat stat1, stat2;
+	fstat(fd1, &stat1);
+	fstat(fd2, &stat2);
+
+	if (!S_ISCHR(stat1.st_mode) || !S_ISCHR(stat2.st_mode))
+		return stat1.st_dev != stat2.st_dev ||
+			stat1.st_ino != stat2.st_ino;
+        else
+		return major(stat1.st_rdev) != major(stat2.st_rdev) ||
+			(minor(stat1.st_rdev) & RENDERNODE_MINOR_MASK) !=
+			(minor(stat2.st_rdev) & RENDERNODE_MINOR_MASK);
+}
+
+/**
+* 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;
+	struct stat stat1;
+	fstat(fd,&stat1);
+	if (minor(stat1.st_rdev) & ~RENDERNODE_MINOR_MASK)/* find a render node fd */
+		*auth = 0;
+	else {
+		client.idx=0;
+		r= drmIoctl(fd, DRM_IOCTL_GET_CLIENT, &client);
+		if (!r)
+			*auth = client.auth;
+	}
+	return r;
+}
+
+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;
+
+	*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) {
+		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) {
+			pthread_mutex_unlock(&fd_mutex);
+			return r;
+		}
+		if ((flag_auth) && (!flag_authexist)) {
+			dev->flink_fd = 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) {
+		pthread_mutex_unlock(&fd_mutex);
+		return -ENOMEM;
+	}
+
+	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 = fd;
+	dev->flink_fd = 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);
+	dev->bo_vas = 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)
+		goto cleanup;
+	if (!accel_working) {
+		r = -EBADF;
+		goto cleanup;
+	}
+
+	r = amdgpu_query_gpu_info_init(dev);
+	if (r)
+		goto cleanup;
+
+	amdgpu_vamgr_init(dev);
+
+	*major_version = dev->major_version;
+	*minor_version = dev->minor_version;
+	*device_handle = dev;
+	util_hash_table_set(fd_tab, UINT_TO_PTR(fd), dev);
+	pthread_mutex_unlock(&fd_mutex);
+
+	return 0;
+
+cleanup:
+	free(dev);
+	pthread_mutex_unlock(&fd_mutex);
+	return r;
+}
+
+void amdgpu_device_free_internal(amdgpu_device_handle dev)
+{
+	util_hash_table_destroy(dev->bo_flink_names);
+	util_hash_table_destroy(dev->bo_handles);
+	util_hash_table_destroy(dev->bo_vas);
+	pthread_mutex_destroy(&dev->bo_table_mutex);
+	pthread_mutex_destroy(&(dev->vamgr.bo_va_mutex));
+	util_hash_table_remove(fd_tab, UINT_TO_PTR(dev->fd));
+	free(dev);
+}
+
+int amdgpu_device_deinitialize(amdgpu_device_handle dev)
+{
+	amdgpu_device_reference(&dev, NULL);
+	return 0;
+}
+
+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;
+}
diff --git a/amdgpu/amdgpu_gpu_info.c b/amdgpu/amdgpu_gpu_info.c
new file mode 100644
index 0000000..0b77731
--- /dev/null
+++ b/amdgpu/amdgpu_gpu_info.c
@@ -0,0 +1,275 @@
+/*
+ * 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.
+ */
+
+#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;
+}
+
+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.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;
+
+	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;
+
+		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, 0x2644, 32, 0xffffffff, 0,
+				     dev->info.gb_tile_mode);
+	if (r)
+		return r;
+
+	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, 0x263e, 1, 0xffffffff, 0,
+				     &dev->info.gb_addr_cfg);
+	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 */
+	/* TODO: info->active_rb_pipes is not set */
+	return 0;
+}
+
+int amdgpu_query_gpu_info(amdgpu_device_handle dev,
+			struct amdgpu_gpu_info *info)
+{
+	/* 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;
+}
diff --git a/amdgpu/amdgpu_internal.h b/amdgpu/amdgpu_internal.h
new file mode 100644
index 0000000..b27756d
--- /dev/null
+++ b/amdgpu/amdgpu_internal.h
@@ -0,0 +1,210 @@
+/*
+ * 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 "xf86atomic.h"
+#include "amdgpu.h"
+#include "util_double_list.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+#define AMDGPU_CS_MAX_RINGS 8
+
+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;
+	struct list_head va_holes;
+	pthread_mutex_t bo_va_mutex;
+	uint32_t va_alignment;
+};
+
+struct amdgpu_device {
+	atomic_t refcount;
+	int fd;
+	int flink_fd;
+	unsigned major_version;
+	unsigned minor_version;
+
+	/** 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;
+	/** List of buffer virtual memory ranges. Protected by bo_table_mutex. */
+	struct util_hash_table *bo_vas;
+	/** This protects all hash tables. */
+	pthread_mutex_t bo_table_mutex;
+	struct amdgpu_bo_va_mgr vamgr;
+	struct drm_amdgpu_info_device dev_info;
+	struct amdgpu_gpu_info info;
+};
+
+struct amdgpu_bo {
+	atomic_t refcount;
+	struct amdgpu_device *dev;
+
+	uint64_t alloc_size;
+	uint64_t virtual_mc_base_address;
+
+	uint32_t handle;
+	uint32_t flink_name;
+
+	pthread_mutex_t cpu_access_mutex;
+	void *cpu_ptr;
+	int cpu_map_count;
+};
+
+/*
+ * There are three mutexes.
+ * To avoid deadlock, only hold the mutexes in this order:
+ * sequence_mutex -> pendings_mutex -> pool_mutex.
+*/
+struct amdgpu_context {
+	/** Mutex for accessing fences and to maintain command submissions
+	    and pending lists in good sequence. */
+	pthread_mutex_t sequence_mutex;
+	/** Buffer for user fences */
+	struct amdgpu_ib *fence_ib;
+	/** The newest expired fence for the ring of the ip blocks. */
+	uint64_t expired_fences[AMDGPU_HW_IP_NUM][AMDGPU_HW_IP_INSTANCE_MAX_COUNT][AMDGPU_CS_MAX_RINGS];
+	/** Mutex for accessing pendings list. */
+	pthread_mutex_t pendings_mutex;
+	/** Pending IBs. */
+	struct list_head pendings[AMDGPU_HW_IP_NUM][AMDGPU_HW_IP_INSTANCE_MAX_COUNT][AMDGPU_CS_MAX_RINGS];
+	/** Freed IBs not yet in pool */
+	struct list_head freed;
+	/** Mutex for accessing free ib pool. */
+	pthread_mutex_t pool_mutex;
+	/** Internal free IB pools. */
+	struct list_head ib_pools[AMDGPU_CS_IB_SIZE_NUM];
+	/* context id*/
+	uint32_t id;
+};
+
+struct amdgpu_ib {
+	struct list_head list_node;
+	amdgpu_bo_handle buf_handle;
+	void *cpu;
+	uint64_t virtual_mc_base_address;
+	enum amdgpu_cs_ib_size ib_size;
+	uint64_t cs_handle;
+};
+
+/**
+ * Functions.
+ */
+
+void amdgpu_device_free_internal(amdgpu_device_handle dev);
+
+void amdgpu_bo_free_internal(amdgpu_bo_handle bo);
+
+void amdgpu_vamgr_init(struct amdgpu_device *dev);
+
+uint64_t amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr,
+                               uint64_t size, uint64_t alignment);
+
+void amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr, uint64_t va,
+                           uint64_t size);
+
+int amdgpu_query_gpu_info_init(amdgpu_device_handle dev);
+
+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;
+}
+
+/**
+ * Assignment between two amdgpu_bo pointers with reference counting.
+ *
+ * Usage:
+ *    struct amdgpu_bo *dst = ... , *src = ...;
+ *
+ *    dst = src;
+ *    // No reference counting. Only use this when you need to move
+ *    // a reference from one pointer to another.
+ *
+ *    amdgpu_bo_reference(&dst, src);
+ *    // Reference counters are updated. dst is decremented and src is
+ *    // incremented. dst is freed if its reference counter is 0.
+ */
+static inline void amdgpu_bo_reference(struct amdgpu_bo **dst,
+					struct amdgpu_bo *src)
+{
+	if (update_references(&(*dst)->refcount, &src->refcount))
+		amdgpu_bo_free_internal(*dst);
+	*dst = src;
+}
+
+/**
+ * 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.
+ */
+void amdgpu_device_reference(struct amdgpu_device **dst,
+			     struct amdgpu_device *src);
+#endif
diff --git a/amdgpu/amdgpu_vamgr.c b/amdgpu/amdgpu_vamgr.c
new file mode 100644
index 0000000..2335912
--- /dev/null
+++ b/amdgpu/amdgpu_vamgr.c
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ *
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "amdgpu.h"
+#include "amdgpu_drm.h"
+#include "amdgpu_internal.h"
+#include "util_math.h"
+
+void amdgpu_vamgr_init(struct amdgpu_device *dev)
+{
+	struct amdgpu_bo_va_mgr *vamgr = &dev->vamgr;
+
+	vamgr->va_offset = dev->dev_info.virtual_address_offset;
+	vamgr->va_alignment = dev->dev_info.virtual_address_alignment;
+
+	list_inithead(&vamgr->va_holes);
+	pthread_mutex_init(&vamgr->bo_va_mutex, NULL);
+}
+
+uint64_t amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr,
+                               uint64_t size, uint64_t alignment)
+{
+        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);
+
+        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) {
+                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;
+                }
+        }
+
+        offset = mgr->va_offset;
+        waste = offset % alignment;
+        waste = waste ? alignment - waste : 0;
+        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;
+}
+
+void amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr, uint64_t va,
+                           uint64_t size)
+{
+        struct amdgpu_bo_va_hole *hole;
+
+        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);
+}
diff --git a/amdgpu/libdrm_amdgpu.pc.in b/amdgpu/libdrm_amdgpu.pc.in
new file mode 100644
index 0000000..417865e
--- /dev/null
+++ b/amdgpu/libdrm_amdgpu.pc.in
@@ -0,0 +1,10 @@
+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
diff --git a/amdgpu/util_double_list.h b/amdgpu/util_double_list.h
new file mode 100644
index 0000000..3f48ae2
--- /dev/null
+++ b/amdgpu/util_double_list.h
@@ -0,0 +1,146 @@
+/**************************************************************************
+ *
+ * Copyright 2006 VMware, Inc., Bismarck, ND. USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ **************************************************************************/
+
+/**
+ * \file
+ * List macros heavily inspired by the Linux kernel
+ * list handling. No list looping yet.
+ *
+ * Is not threadsafe, so common operations need to
+ * be protected using an external mutex.
+ */
+
+#ifndef _U_DOUBLE_LIST_H_
+#define _U_DOUBLE_LIST_H_
+
+
+#include <stddef.h>
+
+
+struct list_head
+{
+    struct list_head *prev;
+    struct list_head *next;
+};
+
+static inline void list_inithead(struct list_head *item)
+{
+    item->prev = item;
+    item->next = item;
+}
+
+static inline void list_add(struct list_head *item, struct list_head *list)
+{
+    item->prev = list;
+    item->next = list->next;
+    list->next->prev = item;
+    list->next = item;
+}
+
+static inline void list_addtail(struct list_head *item, struct list_head *list)
+{
+    item->next = list;
+    item->prev = list->prev;
+    list->prev->next = item;
+    list->prev = item;
+}
+
+static inline void list_replace(struct list_head *from, struct list_head *to)
+{
+    to->prev = from->prev;
+    to->next = from->next;
+    from->next->prev = to;
+    from->prev->next = to;
+}
+
+static inline void list_del(struct list_head *item)
+{
+    item->prev->next = item->next;
+    item->next->prev = item->prev;
+    item->prev = item->next = NULL;
+}
+
+static inline void list_delinit(struct list_head *item)
+{
+    item->prev->next = item->next;
+    item->next->prev = item->prev;
+    item->next = item;
+    item->prev = item;
+}
+
+#define LIST_INITHEAD(__item) list_inithead(__item)
+#define LIST_ADD(__item, __list) list_add(__item, __list)
+#define LIST_ADDTAIL(__item, __list) list_addtail(__item, __list)
+#define LIST_REPLACE(__from, __to) list_replace(__from, __to)
+#define LIST_DEL(__item) list_del(__item)
+#define LIST_DELINIT(__item) list_delinit(__item)
+
+#define LIST_ENTRY(__type, __item, __field)   \
+    ((__type *)(((char *)(__item)) - offsetof(__type, __field)))
+
+#define LIST_IS_EMPTY(__list)                   \
+    ((__list)->next == (__list))
+
+/**
+ * Cast from a pointer to a member of a struct back to the containing struct.
+ *
+ * 'sample' MUST be initialized, or else the result is undefined!
+ */
+#ifndef container_of
+#define container_of(ptr, sample, member)				\
+    (void *)((char *)(ptr)						\
+	     - ((char *)&(sample)->member - (char *)(sample)))
+#endif
+
+#define LIST_FOR_EACH_ENTRY(pos, head, member)				\
+   for (pos = NULL, pos = container_of((head)->next, pos, member);	\
+	&pos->member != (head);						\
+	pos = container_of(pos->member.next, pos, member))
+
+#define LIST_FOR_EACH_ENTRY_SAFE(pos, storage, head, member)	\
+   for (pos = NULL, pos = container_of((head)->next, pos, member),	\
+	storage = container_of(pos->member.next, pos, member);	\
+	&pos->member != (head);						\
+	pos = storage, storage = container_of(storage->member.next, storage, member))
+
+#define LIST_FOR_EACH_ENTRY_SAFE_REV(pos, storage, head, member)	\
+   for (pos = NULL, pos = container_of((head)->prev, pos, member),	\
+	storage = container_of(pos->member.prev, pos, member);		\
+	&pos->member != (head);						\
+	pos = storage, storage = container_of(storage->member.prev, storage, member))
+
+#define LIST_FOR_EACH_ENTRY_FROM(pos, start, head, member)		\
+   for (pos = NULL, pos = container_of((start), pos, member);		\
+	&pos->member != (head);						\
+	pos = container_of(pos->member.next, pos, member))
+
+#define LIST_FOR_EACH_ENTRY_FROM_REV(pos, start, head, member)		\
+   for (pos = NULL, pos = container_of((start), pos, member);		\
+	&pos->member != (head);						\
+	pos = container_of(pos->member.prev, pos, member))
+
+#endif /*_U_DOUBLE_LIST_H_*/
diff --git a/amdgpu/util_hash.c b/amdgpu/util_hash.c
new file mode 100644
index 0000000..b1e12c4
--- /dev/null
+++ b/amdgpu/util_hash.c
@@ -0,0 +1,382 @@
+/**************************************************************************
+ *
+ * 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 at vmware.com>
+  */
+
+#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;
+}
+
+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;
+      }
+   }
+}
+
+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;
+}
+
+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);
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+int util_hash_iter_is_null(struct util_hash_iter iter)
+{
+   if (!iter.node || iter.node == iter.hash->data.e)
+      return 1;
+   return 0;
+}
+
+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;
+}
+
+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;
+}
+
+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..8e0f9a2
--- /dev/null
+++ b/amdgpu/util_hash.h
@@ -0,0 +1,99 @@
+/**************************************************************************
+ *
+ * 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 at vmware.com>
+ */
+
+#ifndef UTIL_HASH_H
+#define UTIL_HASH_H
+
+#include <stdbool.h>
+
+struct util_hash;
+struct util_node;
+
+struct util_hash_iter {
+	struct util_hash *hash;
+	struct util_node *node;
+};
+
+
+struct util_hash *util_hash_create(void);
+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.
+ */
+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.
+ */
+struct util_hash_iter util_hash_erase(struct util_hash *hash,
+				      struct util_hash_iter iter);
+
+void *util_hash_take(struct util_hash *hash, unsigned key);
+
+
+struct util_hash_iter util_hash_first_node(struct util_hash *hash);
+
+/**
+ * Return an iterator pointing to the first entry in the collision list.
+ */
+struct util_hash_iter util_hash_find(struct util_hash *hash, unsigned key);
+
+
+int util_hash_iter_is_null(struct util_hash_iter iter);
+unsigned util_hash_iter_key(struct util_hash_iter iter);
+void *util_hash_iter_data(struct util_hash_iter iter);
+
+
+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..cb7213c
--- /dev/null
+++ b/amdgpu/util_hash_table.c
@@ -0,0 +1,257 @@
+/**************************************************************************
+ *
+ * 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 at vmware.com>
+ */
+
+
+
+#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);
+}
+
+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;
+}
+
+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;
+	}
+}
+
+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;
+}
+
+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);
+}
+
+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);
+	}
+}
+
+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);
+	}
+}
+
+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..04fe704
--- /dev/null
+++ b/amdgpu/util_hash_table.h
@@ -0,0 +1,65 @@
+/**************************************************************************
+ *
+ * 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 at vmware.com>
+ */
+
+#ifndef U_HASH_TABLE_H_
+#define U_HASH_TABLE_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.
+ */
+struct util_hash_table *util_hash_table_create(unsigned (*hash)(void *key),
+				int (*compare)(void *key1, void *key2));
+
+void util_hash_table_set(struct util_hash_table *ht, void *key, void *value);
+
+void *util_hash_table_get(struct util_hash_table *ht, void *key);
+
+void util_hash_table_remove(struct util_hash_table *ht, void *key);
+
+void util_hash_table_clear(struct util_hash_table *ht);
+
+void util_hash_table_foreach(struct util_hash_table *ht,
+			void (*callback)(void *key, void *value, void *data),
+			void *data);
+
+void util_hash_table_destroy(struct util_hash_table *ht);
+
+#endif /* U_HASH_TABLE_H_ */
diff --git a/amdgpu/util_math.h b/amdgpu/util_math.h
new file mode 100644
index 0000000..b8de0f8
--- /dev/null
+++ b/amdgpu/util_math.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ *
+*/
+
+#ifndef _UTIL_MATH_H_
+#define _UTIL_MATH_H_
+
+#define MIN2( A, B )   ( (A)<(B) ? (A) : (B) )
+#define MAX2( A, B )   ( (A)>(B) ? (A) : (B) )
+
+#define ALIGN( value, alignment )   ( ((value) + (alignment) - 1) & ~((alignment) - 1) )
+
+#endif /*_UTIL_MATH_H_*/
diff --git a/configure.ac b/configure.ac
index 155d577..509f2d4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -36,6 +36,7 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 
 # Check for programs
 AC_PROG_CC
+AC_PROG_CXX
 
 AC_USE_SYSTEM_EXTENSIONS
 AC_SYS_LARGEFILE
@@ -74,6 +75,11 @@ AC_ARG_ENABLE(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)]),
@@ -236,6 +242,9 @@ if test "x$drm_cv_atomic_primitives" = "xnone"; then
 	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
 
@@ -257,6 +266,9 @@ else
 	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
@@ -336,6 +348,11 @@ if test "x$RADEON" = xyes; then
 	AC_DEFINE(HAVE_RADEON, 1, [Have radeon support])
 fi
 
+AM_CONDITIONAL(HAVE_AMDGPU, [test "x$AMDGPU" = xyes])
+if test "x$AMDGPU" = xyes; then
+	AC_DEFINE(HAVE_AMDGPU, 1, [Have amdgpu support])
+fi
+
 AM_CONDITIONAL(HAVE_TEGRA, [test "x$TEGRA" = xyes])
 if test "x$TEGRA" = xyes; then
 	AC_DEFINE(HAVE_TEGRA, 1, [Have Tegra support])
@@ -432,6 +449,8 @@ AC_CONFIG_FILES([
 	intel/libdrm_intel.pc
 	radeon/Makefile
 	radeon/libdrm_radeon.pc
+	amdgpu/Makefile
+	amdgpu/libdrm_amdgpu.pc
 	nouveau/Makefile
 	nouveau/libdrm_nouveau.pc
 	omap/Makefile
@@ -463,6 +482,7 @@ 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"
diff --git a/include/drm/amdgpu_drm.h b/include/drm/amdgpu_drm.h
new file mode 100644
index 0000000..d248d77
--- /dev/null
+++ b/include/drm/amdgpu_drm.h
@@ -0,0 +1,600 @@
+/* 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 at valinux.com>
+ *    Gareth Hughes <gareth at valinux.com>
+ *    Keith Whitwell <keith at tungstengraphics.com>
+ */
+
+#ifndef __AMDGPU_DRM_H__
+#define __AMDGPU_DRM_H__
+
+#include <drm.h>
+
+#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_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_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_VA, union 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 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
+
+#define AMDGPU_GEM_DOMAIN_MASK		0x3F
+
+/* 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 un-cached attributes should be used for GTT */
+#define AMDGPU_GEM_CREATE_CPU_GTT_UC		(1 << 2)
+/* Flag that USWC attributes should be used for GTT */
+#define AMDGPU_GEM_CREATE_CPU_GTT_WC		(1 << 3)
+
+/* Flag mask for GTT domain_flags */
+#define AMDGPU_GEM_CREATE_CPU_GTT_MASK \
+	(AMDGPU_GEM_CREATE_CPU_GTT_WC | \
+	 AMDGPU_GEM_CREATE_CPU_GTT_UC | \
+	 AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED | \
+	 AMDGPU_GEM_CREATE_NO_CPU_ACCESS)
+
+struct drm_amdgpu_gem_create_in  {
+	/** the requested memory size */
+	uint64_t bo_size;
+	/** physical start_addr alignment in bytes for some HW requirements */
+	uint64_t alignment;
+	/** the requested memory domains */
+	uint64_t domains;
+	/** allocation flags */
+	uint64_t domain_flags;
+};
+
+struct drm_amdgpu_gem_create_out  {
+	/** returned GEM object handle */
+	uint32_t handle;
+};
+
+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 */
+	uint32_t operation;
+	/** Handle of list or 0 if we want to create one */
+	uint32_t list_handle;
+	/** Number of BOs in list  */
+	uint32_t bo_number;
+	/** Size of each element describing BO */
+	uint32_t bo_info_size;
+	/** Pointer to array describing BOs */
+	uint64_t bo_info_ptr;
+};
+
+struct drm_amdgpu_bo_list_entry {
+	/** Handle of BO */
+	uint32_t bo_handle;
+	/** New (if specified) BO priority to be used during migration */
+	uint32_t bo_priority;
+};
+
+struct drm_amdgpu_bo_list_out {
+	/** Handle of resource list  */
+	uint32_t list_handle;
+};
+
+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
+
+#define AMDGPU_CTX_OP_STATE_RUNNING	1
+
+struct drm_amdgpu_ctx_in {
+	uint32_t	op;
+	uint32_t	flags;
+	uint32_t	ctx_id;
+	uint32_t	pad;
+};
+
+union drm_amdgpu_ctx_out {
+		struct {
+			uint32_t	ctx_id;
+		} alloc;
+
+		struct {
+			uint64_t	flags;
+			uint64_t	hangs;
+		} state;
+};
+
+union drm_amdgpu_ctx {
+	struct drm_amdgpu_ctx_in in;
+	union drm_amdgpu_ctx_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 {
+	uint64_t		addr;
+	uint64_t		size;
+	uint32_t		flags;
+	uint32_t		handle;
+};
+
+#define AMDGPU_TILING_MACRO				0x1
+#define AMDGPU_TILING_MICRO				0x2
+#define AMDGPU_TILING_SWAP_16BIT			0x4
+#define AMDGPU_TILING_R600_NO_SCANOUT			AMDGPU_TILING_SWAP_16BIT
+#define AMDGPU_TILING_SWAP_32BIT			0x8
+/* this object requires a surface when mapped - i.e. front buffer */
+#define AMDGPU_TILING_SURFACE				0x10
+#define AMDGPU_TILING_MICRO_SQUARE			0x20
+#define AMDGPU_TILING_EG_BANKW_SHIFT			8
+#define AMDGPU_TILING_EG_BANKW_MASK			0xf
+#define AMDGPU_TILING_EG_BANKH_SHIFT			12
+#define AMDGPU_TILING_EG_BANKH_MASK			0xf
+#define AMDGPU_TILING_EG_MACRO_TILE_ASPECT_SHIFT	16
+#define AMDGPU_TILING_EG_MACRO_TILE_ASPECT_MASK	0xf
+#define AMDGPU_TILING_EG_TILE_SPLIT_SHIFT		24
+#define AMDGPU_TILING_EG_TILE_SPLIT_MASK		0xf
+#define AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_SHIFT	28
+#define AMDGPU_TILING_EG_STENCIL_TILE_SPLIT_MASK	0xf
+
+#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 {
+	uint32_t	handle;		/* GEM Object handle */
+	uint32_t	op;		/** Do we want get or set metadata */
+	struct {
+		uint64_t	flags;
+		uint64_t	tiling_info; /* family specific tiling info */
+		uint32_t	data_size_bytes;
+		uint32_t	data[64];
+	} data;
+};
+
+struct drm_amdgpu_gem_mmap_in {
+	uint32_t handle;		/** the GEM object handle */
+};
+
+struct drm_amdgpu_gem_mmap_out {
+	uint64_t addr_ptr;	/** mmap offset from the vma offset manager */
+};
+
+union drm_amdgpu_gem_mmap {
+	struct drm_amdgpu_gem_mmap_in   in;
+	struct drm_amdgpu_gem_mmap_out out;
+};
+
+struct drm_amdgpu_gem_wait_idle_in {
+	uint32_t handle;   	/* GEM object handle */
+	uint32_t flags;
+	uint64_t timeout; 	/* Timeout to wait. If 0 then returned immediately with the status */
+};
+
+struct drm_amdgpu_gem_wait_idle_out {
+	uint32_t status;	/*   BO status:  0 - BO is idle, 1 - BO is busy */
+	uint32_t domain; /*  Returned current memory domain */
+};
+
+union drm_amdgpu_gem_wait_idle {
+	struct drm_amdgpu_gem_wait_idle_in  in;
+	struct drm_amdgpu_gem_wait_idle_out out;
+};
+
+struct drm_amdgpu_wait_cs_in {
+	uint64_t handle;
+	uint64_t timeout;
+	uint32_t ip_type;
+	uint32_t ip_instance;
+	uint32_t ring;
+};
+
+struct drm_amdgpu_wait_cs_out {
+	uint64_t status;
+};
+
+union drm_amdgpu_wait_cs {
+	struct drm_amdgpu_wait_cs_in in;
+	struct drm_amdgpu_wait_cs_out out;
+};
+
+/* Sets or returns a value associated with a buffer. */
+struct drm_amdgpu_gem_op {
+	uint32_t	handle; /* buffer */
+	uint32_t	op;     /* AMDGPU_GEM_OP_* */
+	uint64_t	value;  /* input or return value */
+};
+
+#define AMDGPU_GEM_OP_GET_GEM_CREATE_INFO		0
+#define AMDGPU_GEM_OP_SET_INITIAL_DOMAIN	1
+
+#define AMDGPU_VA_OP_MAP			1
+#define AMDGPU_VA_OP_UNMAP			2
+
+#define AMDGPU_VA_RESULT_OK			0
+#define AMDGPU_VA_RESULT_ERROR			1
+#define AMDGPU_VA_RESULT_VA_INVALID_ALIGNMENT	2
+
+/* Mapping flags */
+/* readable mapping */
+#define AMDGPU_VM_PAGE_READABLE		(1 << 1)
+/* writable mapping */
+#define AMDGPU_VM_PAGE_WRITEABLE	(1 << 2)
+/* executable mapping, new for VI */
+#define AMDGPU_VM_PAGE_EXECUTABLE	(1 << 3)
+
+struct drm_amdgpu_gem_va_in {
+	/* GEM object handle */
+	uint32_t handle;
+	uint32_t pad;
+	/* map or unmap*/
+	uint32_t operation;
+	/* specify mapping flags */
+	uint32_t flags;
+	/* va address to assign . Must be correctly aligned.*/
+	uint64_t va_address;
+	/* Specify offset inside of BO to assign. Must be correctly aligned.*/
+	uint64_t offset_in_bo;
+	/* Specify mapping size. If 0 and offset is 0 then map the whole BO.*/
+	/* Must be correctly aligned. */
+	uint64_t map_size;
+};
+
+struct drm_amdgpu_gem_va_out {
+	uint32_t result;
+};
+
+union drm_amdgpu_gem_va {
+	struct drm_amdgpu_gem_va_in  in;
+	struct drm_amdgpu_gem_va_out out;
+};
+
+#define AMDGPU_HW_IP_GFX          0
+#define AMDGPU_HW_IP_COMPUTE      1
+#define AMDGPU_HW_IP_DMA          2
+#define AMDGPU_HW_IP_UVD          3
+#define AMDGPU_HW_IP_VCE          4
+#define AMDGPU_HW_IP_NUM          5
+
+#define AMDGPU_HW_IP_INSTANCE_MAX_COUNT 1
+
+#define AMDGPU_CHUNK_ID_IB		0x01
+#define AMDGPU_CHUNK_ID_FENCE		0x02
+struct drm_amdgpu_cs_chunk {
+	uint32_t		chunk_id;
+	uint32_t		length_dw;
+	uint64_t		chunk_data;
+};
+
+struct drm_amdgpu_cs_in {
+	/** Rendering context id */
+	uint32_t		ctx_id;
+	/**  Handle of resource list associated with CS */
+	uint32_t		bo_list_handle;
+	uint32_t		num_chunks;
+	uint32_t		pad;
+	/* this points to uint64_t * which point to cs chunks */
+	uint64_t		chunks;
+};
+
+struct drm_amdgpu_cs_out {
+	uint64_t handle;
+};
+
+union drm_amdgpu_cs {
+       struct drm_amdgpu_cs_in in;
+       struct drm_amdgpu_cs_out out;
+};
+
+/* Specify flags to be used for IB */
+
+/* This IB should be submitted to CE */
+#define AMDGPU_IB_FLAG_CE	(1<<0)
+
+/* GDS is used by this IB */
+#define AMDGPU_IB_FLAG_GDS	(1<<1)
+
+struct drm_amdgpu_cs_chunk_ib {
+	/**
+	 * Handle of GEM object to be used as IB or 0 if it is already in
+	 * residency list.
+	 */
+	uint32_t handle;
+	uint32_t flags;		/* IB Flags */
+	uint64_t va_start;	/* Virtual address to begin IB execution */
+	uint32_t ib_bytes;	/* Size of submission */
+	uint32_t ip_type;	/* HW IP to submit to */
+	uint32_t ip_instance;	/* HW IP index of the same type to submit to  */
+	uint32_t ring;		/* Ring index to submit to */
+};
+
+struct drm_amdgpu_cs_chunk_fence {
+	uint32_t handle;
+	uint32_t offset;
+};
+
+struct drm_amdgpu_cs_chunk_data {
+	union {
+		struct drm_amdgpu_cs_chunk_ib		ib_data;
+		struct drm_amdgpu_cs_chunk_fence	fence_data;
+	};
+};
+
+/**
+ *  Query h/w info: Flag that this is integrated (a.h.a. fusion) GPU
+ *
+ */
+#define AMDGPU_IDS_FLAGS_FUSION         0x1
+
+/* indicate if acceleration can be working */
+#define AMDGPU_INFO_ACCEL_WORKING		0x00
+/* get the crtc_id from the mode object id? */
+#define AMDGPU_INFO_CRTC_FROM_ID		0x01
+/* query hw IP info */
+#define AMDGPU_INFO_HW_IP_INFO			0x02
+/* query hw IP instance count for the specified type */
+#define AMDGPU_INFO_HW_IP_COUNT			0x03
+/* timestamp for GL_ARB_timer_query */
+#define AMDGPU_INFO_TIMESTAMP			0x05
+/* Query the firmware version */
+#define AMDGPU_INFO_FW_VERSION			0x0e
+	/* Subquery id: Query VCE firmware version */
+	#define AMDGPU_INFO_FW_VCE		0x1
+	/* Subquery id: Query UVD firmware version */
+	#define AMDGPU_INFO_FW_UVD		0x2
+	/* Subquery id: Query GMC firmware version */
+	#define AMDGPU_INFO_FW_GMC		0x03
+	/* Subquery id: Query GFX ME firmware version */
+	#define AMDGPU_INFO_FW_GFX_ME		0x04
+	/* Subquery id: Query GFX PFP firmware version */
+	#define AMDGPU_INFO_FW_GFX_PFP		0x05
+	/* Subquery id: Query GFX CE firmware version */
+	#define AMDGPU_INFO_FW_GFX_CE		0x06
+	/* Subquery id: Query GFX RLC firmware version */
+	#define AMDGPU_INFO_FW_GFX_RLC		0x07
+	/* Subquery id: Query GFX MEC firmware version */
+	#define AMDGPU_INFO_FW_GFX_MEC		0x08
+	/* Subquery id: Query SMC firmware version */
+	#define AMDGPU_INFO_FW_SMC		0x0a
+	/* Subquery id: Query SDMA firmware version */
+	#define AMDGPU_INFO_FW_SDMA		0x0b
+/* number of bytes moved for TTM migration */
+#define AMDGPU_INFO_NUM_BYTES_MOVED		0x0f
+/* the used VRAM size */
+#define AMDGPU_INFO_VRAM_USAGE			0x10
+/* the used GTT size */
+#define AMDGPU_INFO_GTT_USAGE			0x11
+/* Information about GDS, etc. resource configuration */
+#define AMDGPU_INFO_GDS_CONFIG			0x13
+/* Query information about VRAM and GTT domains */
+#define AMDGPU_INFO_VRAM_GTT			0x14
+/* Query information about register in MMR address space*/
+#define AMDGPU_INFO_READ_MMR_REG		0x15
+/* Query information about device: rev id, family, etc. */
+#define AMDGPU_INFO_DEV_INFO			0x16
+/* visible vram usage */
+#define AMDGPU_INFO_VIS_VRAM_USAGE		0x17
+
+#define AMDGPU_INFO_MMR_SE_INDEX_SHIFT	0
+#define AMDGPU_INFO_MMR_SE_INDEX_MASK	0xff
+#define AMDGPU_INFO_MMR_SH_INDEX_SHIFT	8
+#define AMDGPU_INFO_MMR_SH_INDEX_MASK	0xff
+
+/* Input structure for the INFO ioctl */
+struct drm_amdgpu_info {
+	/* Where the return value will be stored */
+	uint64_t return_pointer;
+	/* The size of the return value. Just like "size" in "snprintf",
+	 * it limits how many bytes the kernel can write. */
+	uint32_t return_size;
+	/* The query request id. */
+	uint32_t query;
+
+	union {
+		struct {
+			uint32_t id;
+		} mode_crtc;
+
+		struct {
+			/** AMDGPU_HW_IP_* */
+			uint32_t type;
+			/**
+			 * Index of the IP if there are more IPs of the same type.
+			 * Ignored by AMDGPU_INFO_HW_IP_COUNT.
+			 */
+			uint32_t ip_instance;
+		} query_hw_ip;
+
+		struct {
+			uint32_t dword_offset;
+			uint32_t count; /* number of registers to read */
+			uint32_t instance;
+			uint32_t flags;
+		} read_mmr_reg;
+
+		struct {
+			/** AMDGPU_INFO_FW_* */
+			uint32_t fw_type;
+			/** Index of the IP if there are more IPs of the same type. */
+			uint32_t ip_instance;
+			/**
+			 * Index of the engine. Whether this is used depends
+			 * on the firmware type. (e.g. MEC, SDMA)
+			 */
+			uint32_t index;
+		} query_fw;
+	};
+};
+
+struct drm_amdgpu_info_gds {
+	/** GDS GFX partition size */
+	uint32_t gds_gfx_partition_size;
+	/** GDS compute partition size */
+	uint32_t compute_partition_size;
+	/** total GDS memory size */
+	uint32_t gds_total_size;
+	/** GWS size per GFX partition */
+	uint32_t gws_per_gfx_partition;
+	/** GSW size per compute partition */
+	uint32_t gws_per_compute_partition;
+	/** OA size per GFX partition */
+	uint32_t oa_per_gfx_partition;
+	/** OA size per compute partition */
+	uint32_t oa_per_compute_partition;
+};
+
+struct drm_amdgpu_info_vram_gtt {
+	uint64_t vram_size;
+	uint64_t vram_cpu_accessible_size;
+	uint64_t gtt_size;
+};
+
+struct drm_amdgpu_info_firmware {
+	uint32_t ver;
+	uint32_t feature;
+};
+
+struct drm_amdgpu_info_device {
+	/** PCI Device ID */
+	uint32_t device_id;
+	/** Internal chip revision: A0, A1, etc.) */
+	uint32_t chip_rev;
+	uint32_t external_rev;
+	/** Revision id in PCI Config space */
+	uint32_t pci_rev;
+	uint32_t family;
+	uint32_t num_shader_engines;
+	uint32_t num_shader_arrays_per_engine;
+	uint32_t gpu_counter_freq; /* in KHz */
+	uint64_t max_engine_clock; /* in KHz */
+	/* cu information */
+	uint32_t cu_active_number;
+	uint32_t cu_ao_mask;
+	uint32_t cu_bitmap[4][4];
+	/** Render backend pipe mask. One render backend is CB+DB. */
+	uint32_t enabled_rb_pipes_mask;
+	uint32_t num_rb_pipes;
+	uint32_t num_hw_gfx_contexts;
+	uint32_t _pad;
+	uint64_t ids_flags;
+	/** Starting virtual address for UMDs. */
+	uint64_t virtual_address_offset;
+	/** Required alignment of virtual addresses. */
+	uint32_t virtual_address_alignment;
+	/** Page table entry - fragment size */
+	uint32_t pte_fragment_size;
+	uint32_t gart_page_size;
+};
+
+struct drm_amdgpu_info_hw_ip {
+	/** Version of h/w IP */
+	uint32_t  hw_ip_version_major;
+	uint32_t  hw_ip_version_minor;
+	/** Capabilities */
+	uint64_t  capabilities_flags;
+	/** Bitmask of available rings. Bit 0 means ring 0, etc. */
+	uint32_t  available_rings;
+};
+
+/* Those correspond to the tile index to use, this is to explicitly state
+ * the API that is implicitly defined by the tile mode array.
+ */
+#define SI_TILE_MODE_COLOR_LINEAR_ALIGNED	8
+#define SI_TILE_MODE_COLOR_1D			13
+#define SI_TILE_MODE_COLOR_1D_SCANOUT		9
+#define SI_TILE_MODE_COLOR_2D_8BPP		14
+#define SI_TILE_MODE_COLOR_2D_16BPP		15
+#define SI_TILE_MODE_COLOR_2D_32BPP		16
+#define SI_TILE_MODE_COLOR_2D_64BPP		17
+#define SI_TILE_MODE_COLOR_2D_SCANOUT_16BPP	11
+#define SI_TILE_MODE_COLOR_2D_SCANOUT_32BPP	12
+#define SI_TILE_MODE_DEPTH_STENCIL_1D		4
+#define SI_TILE_MODE_DEPTH_STENCIL_2D		0
+#define SI_TILE_MODE_DEPTH_STENCIL_2D_2AA	3
+#define SI_TILE_MODE_DEPTH_STENCIL_2D_4AA	3
+#define SI_TILE_MODE_DEPTH_STENCIL_2D_8AA	2
+
+#define CIK_TILE_MODE_DEPTH_STENCIL_1D		5
+
+/*
+ * Supported GPU families
+ */
+#define AMDGPU_FAMILY_UNKNOWN			0
+#define AMDGPU_FAMILY_CI			120 /* Bonaire, Hawaii */
+#define AMDGPU_FAMILY_KV			125 /* Kaveri, Kabini, Mullins */
+#define AMDGPU_FAMILY_VI			130 /* Iceland, Tonga */
+#define AMDGPU_FAMILY_CZ			135 /* Carrizo */
+
+#endif
-- 
1.8.3.1



More information about the dri-devel mailing list