From: The etnaviv authors <dri-devel@lists.freedesktop.org>

Add the libdrm_etnaviv helper library to encapsulate etnaviv-specific 
interfaces to the DRM.

Signed-off-by: Christian Gmeiner <christian.gmeiner at gmail.com>
Signed-off-by: Lucas Stach <l.stach at pengutronix.de>
---
 .gitignore                   |   1 +
 Makefile.am                  |   6 +
 configure.ac                 |  16 ++
 etnaviv/Android.mk           |  18 +++
 etnaviv/Makefile.am          |  27 ++++
 etnaviv/Makefile.sources     |  12 ++
 etnaviv/etnaviv-symbol-check |  45 ++++++
 etnaviv/etnaviv_bo.c         | 347 +++++++++++++++++++++++++++++++++++++++++++
 etnaviv/etnaviv_bo_cache.c   | 196 ++++++++++++++++++++++++
 etnaviv/etnaviv_cmd_stream.c | 243 ++++++++++++++++++++++++++++++
 etnaviv/etnaviv_device.c     |  96 ++++++++++++
 etnaviv/etnaviv_drm.h        | 225 ++++++++++++++++++++++++++++
 etnaviv/etnaviv_drmif.h      | 188 +++++++++++++++++++++++
 etnaviv/etnaviv_gpu.c        | 175 ++++++++++++++++++++++
 etnaviv/etnaviv_pipe.c       |  78 ++++++++++
 etnaviv/etnaviv_priv.h       | 200 +++++++++++++++++++++++++
 etnaviv/libdrm_etnaviv.pc.in |  11 ++
 17 files changed, 1884 insertions(+)
 create mode 100644 etnaviv/Android.mk
 create mode 100644 etnaviv/Makefile.am
 create mode 100644 etnaviv/Makefile.sources
 create mode 100755 etnaviv/etnaviv-symbol-check
 create mode 100644 etnaviv/etnaviv_bo.c
 create mode 100644 etnaviv/etnaviv_bo_cache.c
 create mode 100644 etnaviv/etnaviv_cmd_stream.c
 create mode 100644 etnaviv/etnaviv_device.c
 create mode 100644 etnaviv/etnaviv_drm.h
 create mode 100644 etnaviv/etnaviv_drmif.h
 create mode 100644 etnaviv/etnaviv_gpu.c
 create mode 100644 etnaviv/etnaviv_pipe.c
 create mode 100644 etnaviv/etnaviv_priv.h
 create mode 100644 etnaviv/libdrm_etnaviv.pc.in

diff --git a/.gitignore b/.gitignore
index a44566f..3226b3a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,6 +57,7 @@ libdrm_exynos.pc
 libdrm_freedreno.pc
 libdrm_amdgpu.pc
 libdrm_vc4.pc
+libdrm_etnaviv.pc
 libkms.pc
 libtool
 ltmain.sh
diff --git a/Makefile.am b/Makefile.am
index 2ceb352..630edc4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -36,6 +36,7 @@ AM_DISTCHECK_CONFIGURE_FLAGS = \
        --enable-freedreno \
        --enable-freedreno-kgsl\
        --enable-tegra-experimental-api \
+       --enable-etnaviv-experimental-api \
        --enable-install-test-programs \
        --enable-cairo-tests \
        --enable-manpages \
@@ -84,6 +85,10 @@ if HAVE_VC4
 VC4_SUBDIR = vc4
 endif

+if HAVE_ETNAVIV
+ETNAVIV_SUBDIR = etnaviv
+endif
+
 if BUILD_MANPAGES
 if HAVE_MANPAGES_STYLESHEET
 MAN_SUBDIR = man
@@ -102,6 +107,7 @@ SUBDIRS = \
        $(FREEDRENO_SUBDIR) \
        $(TEGRA_SUBDIR) \
        $(VC4_SUBDIR) \
+       $(ETNAVIV_SUBDIR) \
        tests \
        $(MAN_SUBDIR)

diff --git a/configure.ac b/configure.ac
index e3048c7..64f3e6c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -132,6 +132,11 @@ AC_ARG_ENABLE(vc4,
              [Enable support for vc4's API (default: auto, enabled on arm)]),
              [VC4=$enableval], [VC4=auto])

+AC_ARG_ENABLE(etnaviv-experimental-api,
+             AS_HELP_STRING([--enable-etnaviv-experimental-api],
+             [Enable support for etnaviv's experimental API (default: 
disabled)]),
+             [ETNAVIV=$enableval], [ETNAVIV=no])
+
 AC_ARG_ENABLE(install-test-programs,
                  AS_HELP_STRING([--enable-install-test-programs],
                  [Install test programs (default: no)]),
@@ -274,6 +279,9 @@ if test "x$drm_cv_atomic_primitives" = "xnone"; then

        LIBDRM_ATOMICS_NOT_FOUND_MSG($TEGRA, tegra, NVIDIA Tegra, 
tegra-experimental-api)
        TEGRA=no
+
+       LIBDRM_ATOMICS_NOT_FOUND_MSG($ETNAVIV, etnaviv, Vivante, 
etnaviv-experimental-api)
+       ETNAVIV=no
 else
        if test "x$INTEL" = xauto; then
                case $host_cpu in
@@ -413,6 +421,11 @@ if test "x$VC4" = xyes; then
        AC_DEFINE(HAVE_VC4, 1, [Have VC4 support])
 fi

+AM_CONDITIONAL(HAVE_ETNAVIV, [test "x$ETNAVIV" = xyes])
+if test "x$ETNAVIV" = xyes; then
+       AC_DEFINE(HAVE_ETNAVIV, 1, [Have etnaviv support])
+fi
+
 AM_CONDITIONAL(HAVE_INSTALL_TESTS, [test "x$INSTALL_TESTS" = xyes])
 if test "x$INSTALL_TESTS" = xyes; then
        AC_DEFINE(HAVE_INSTALL_TESTS, 1, [Install test programs])
@@ -524,6 +537,8 @@ AC_CONFIG_FILES([
        tegra/libdrm_tegra.pc
        vc4/Makefile
        vc4/libdrm_vc4.pc
+       etnaviv/Makefile
+       etnaviv/libdrm_etnaviv.pc
        tests/Makefile
        tests/modeprint/Makefile
        tests/modetest/Makefile
@@ -555,4 +570,5 @@ echo "  EXYNOS API     $EXYNOS"
 echo "  Freedreno API  $FREEDRENO (kgsl: $FREEDRENO_KGSL)"
 echo "  Tegra API      $TEGRA"
 echo "  VC4 API        $VC4"
+echo "  Etnaviv API    $ETNAVIV"
 echo ""
diff --git a/etnaviv/Android.mk b/etnaviv/Android.mk
new file mode 100644
index 0000000..a3a2295
--- /dev/null
+++ b/etnaviv/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Import variables LIBDRM_ETNAVIV_FILES, LIBDRM_ETNAVIV_H_FILES
+include $(LOCAL_PATH)/Makefile.sources
+
+LOCAL_MODULE := libdrm_etnaviv
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := libdrm
+
+LOCAL_SRC_FILES := $(patsubst %.h, , $(LIBDRM_ETNAVIV_FILES))
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+
+LOCAL_CFLAGS := \
+       -DHAVE_LIBDRM_ATOMIC_PRIMITIVES=1
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/etnaviv/Makefile.am b/etnaviv/Makefile.am
new file mode 100644
index 0000000..1c40e56
--- /dev/null
+++ b/etnaviv/Makefile.am
@@ -0,0 +1,27 @@
+AUTOMAKE_OPTIONS=subdir-objects
+include Makefile.sources
+
+AM_CFLAGS = \
+       $(WARN_CFLAGS) \
+       -I$(top_srcdir) \
+       $(PTHREADSTUBS_CFLAGS) \
+       -I$(top_srcdir)/include/drm
+
+libdrm_etnaviv_ladir = $(libdir)
+libdrm_etnaviv_la_LTLIBRARIES = libdrm_etnaviv.la
+libdrm_etnaviv_la_LDFLAGS = -version-number 0:0:0 -no-undefined
+libdrm_etnaviv_la_LIBADD = \
+       ../libdrm.la \
+       @PTHREADSTUBS_LIBS@ \
+       @CLOCK_LIB@
+
+libdrm_etnaviv_la_SOURCES = $(LIBDRM_ETNAVIV_FILES)
+
+libdrm_etnavivincludedir = ${includedir}/libdrm
+libdrm_etnavivinclude_HEADERS = $(LIBDRM_ETNAVIV_H_FILES)
+
+pkgconfigdir = @pkgconfigdir@
+pkgconfig_DATA = libdrm_etnaviv.pc
+
+TESTS = etnaviv-symbol-check
+EXTRA_DIST = $(TESTS)
diff --git a/etnaviv/Makefile.sources b/etnaviv/Makefile.sources
new file mode 100644
index 0000000..5258056
--- /dev/null
+++ b/etnaviv/Makefile.sources
@@ -0,0 +1,12 @@
+LIBDRM_ETNAVIV_FILES := \
+       etnaviv_device.c \
+       etnaviv_gpu.c \
+       etnaviv_bo.c \
+       etnaviv_bo_cache.c \
+       etnaviv_pipe.c \
+       etnaviv_cmd_stream.c \
+       etnaviv_drm.h \
+       etnaviv_priv.h
+
+LIBDRM_ETNAVIV_H_FILES := \
+       etnaviv_drmif.h
diff --git a/etnaviv/etnaviv-symbol-check b/etnaviv/etnaviv-symbol-check
new file mode 100755
index 0000000..7795023
--- /dev/null
+++ b/etnaviv/etnaviv-symbol-check
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# The following symbols (past the first five) are taken from the public 
headers.
+# A list of the latter should be available 
Makefile.sources/LIBDRM_FREEDRENO_H_FILES
+
+FUNCS=$(nm -D --format=bsd --defined-only ${1-.libs/libdrm_etnaviv.so} | awk 
'{print $3}'| while read func; do
+( grep -q "^$func$" || echo $func )  <<EOF
+__bss_start
+_edata
+_end
+_fini
+_init
+etna_device_new
+etna_device_ref
+etna_device_del
+etna_gpu_new
+etna_gpu_del
+etna_gpu_get_param
+etna_pipe_new
+etna_pipe_del
+etna_pipe_wait
+etna_bo_new
+etna_bo_from_handle
+etna_bo_from_name
+etna_bo_from_dmabuf
+etna_bo_ref
+etna_bo_del
+etna_bo_get_name
+etna_bo_handle
+etna_bo_dmabuf
+etna_bo_size
+etna_bo_map
+etna_bo_cpu_prep
+etna_bo_cpu_fini
+etna_cmd_stream_new
+etna_cmd_stream_del
+etna_cmd_stream_timestamp
+etna_cmd_stream_flush
+etna_cmd_stream_finish
+etna_cmd_stream_reloc
+EOF
+done)
+
+test ! -n "$FUNCS" || echo $FUNCS
+test ! -n "$FUNCS"
diff --git a/etnaviv/etnaviv_bo.c b/etnaviv/etnaviv_bo.c
new file mode 100644
index 0000000..833f8bd
--- /dev/null
+++ b/etnaviv/etnaviv_bo.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2014 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner at gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+drm_private pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
+drm_private void bo_del(struct etna_bo *bo);
+
+/* set buffer name, and add to table, call w/ table_lock held: */
+static void set_name(struct etna_bo *bo, uint32_t name)
+{
+       bo->name = name;
+       /* add ourself into the name table: */
+       drmHashInsert(bo->dev->name_table, name, bo);
+}
+
+/* Called under table_lock */
+drm_private void bo_del(struct etna_bo *bo)
+{
+       if (bo->map)
+               drm_munmap(bo->map, bo->size);
+
+       if (bo->name)
+               drmHashDelete(bo->dev->name_table, bo->name);
+
+       if (bo->handle) {
+               struct drm_gem_close req = {
+                       .handle = bo->handle,
+               };
+
+               drmHashDelete(bo->dev->handle_table, bo->handle);
+               drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+       }
+
+       free(bo);
+}
+
+/* lookup a buffer from it's handle, call w/ table_lock held: */
+static struct etna_bo *lookup_bo(void *tbl, uint32_t handle)
+{
+       struct etna_bo *bo = NULL;
+
+       if (!drmHashLookup(tbl, handle, (void **)&bo)) {
+               /* found, incr refcnt and return: */
+               bo = etna_bo_ref(bo);
+
+               /* don't break the bucket if this bo was found in one */
+               list_delinit(&bo->list);
+       }
+
+       return bo;
+}
+
+/* allocate a new buffer object, call w/ table_lock held */
+static struct etna_bo *bo_from_handle(struct etna_device *dev,
+               uint32_t size, uint32_t handle, uint32_t flags)
+{
+       struct etna_bo *bo = calloc(sizeof(*bo), 1);
+
+       if (!bo) {
+               struct drm_gem_close req = {
+                       .handle = handle,
+               };
+
+               drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
+
+               return NULL;
+       }
+
+       bo->dev = etna_device_ref(dev);
+       bo->size = size;
+       bo->handle = handle;
+       bo->flags = flags;
+       atomic_set(&bo->refcnt, 1);
+       list_inithead(&bo->list);
+       /* add ourselves to the handle table: */
+       drmHashInsert(dev->handle_table, handle, bo);
+
+       return bo;
+}
+
+/* allocate a new (un-tiled) buffer object */
+struct etna_bo *etna_bo_new(struct etna_device *dev, uint32_t size,
+               uint32_t flags)
+{
+       struct etna_bo *bo;
+       int ret;
+       struct drm_etnaviv_gem_new req = {
+                       .flags = flags,
+       };
+
+       bo = etna_bo_cache_alloc(&dev->bo_cache, &size, flags);
+       if (bo)
+               return bo;
+
+       req.size = size;
+       ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GEM_NEW,
+                       &req, sizeof(req));
+       if (ret)
+               return NULL;
+
+       pthread_mutex_lock(&table_lock);
+       bo = bo_from_handle(dev, size, req.handle, flags);
+       bo->reuse = 1;
+       pthread_mutex_unlock(&table_lock);
+
+       return bo;
+}
+
+struct etna_bo *etna_bo_ref(struct etna_bo *bo)
+{
+       atomic_inc(&bo->refcnt);
+
+       return bo;
+}
+
+/* get buffer info */
+static int get_buffer_info(struct etna_bo *bo)
+{
+       int ret;
+       struct drm_etnaviv_gem_info req = {
+               .handle = bo->handle,
+       };
+
+       ret = drmCommandWriteRead(bo->dev->fd, DRM_ETNAVIV_GEM_INFO,
+                       &req, sizeof(req));
+       if (ret) {
+               return ret;
+       }
+
+       /* really all we need for now is mmap offset */
+       bo->offset = req.offset;
+
+       return 0;
+}
+
+/* import a buffer object from DRI2 name */
+struct etna_bo *etna_bo_from_name(struct etna_device *dev, uint32_t name)
+{
+       struct etna_bo *bo;
+       struct drm_gem_open req = {
+               .name = name,
+       };
+
+       pthread_mutex_lock(&table_lock);
+
+       /* check name table first, to see if bo is already open: */
+       bo = lookup_bo(dev->name_table, req.handle);
+       if (bo)
+               goto out_unlock;
+
+       if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
+               ERROR_MSG("gem-open failed: %s", strerror(errno));
+               goto out_unlock;
+       }
+
+       bo = lookup_bo(dev->handle_table, req.handle);
+       if (bo)
+               goto out_unlock;
+
+       bo = bo_from_handle(dev, req.size, req.handle, 0);
+       if (bo)
+               set_name(bo, name);
+
+out_unlock:
+       pthread_mutex_unlock(&table_lock);
+
+       return bo;
+}
+
+/* import a buffer from dmabuf fd, does not take ownership of the
+ * fd so caller should close() the fd when it is otherwise done
+ * with it (even if it is still using the 'struct etna_bo *')
+ */
+struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd)
+{
+       struct etna_bo *bo;
+       int ret, size;
+       uint32_t handle;
+
+       pthread_mutex_lock(&table_lock);
+
+       ret = drmPrimeFDToHandle(dev->fd, fd, &handle);
+       if (ret) {
+               return NULL;
+       }
+
+       bo = lookup_bo(dev->handle_table, handle);
+       if (bo)
+               goto out_unlock;
+
+       /* lseek() to get bo size */
+       size = lseek(fd, 0, SEEK_END);
+       lseek(fd, 0, SEEK_CUR);
+
+       bo = bo_from_handle(dev, size, handle, 0);
+
+out_unlock:
+       pthread_mutex_unlock(&table_lock);
+
+       return bo;
+}
+
+/* destroy a buffer object */
+void etna_bo_del(struct etna_bo *bo)
+{
+       struct etna_device *dev = bo->dev;
+
+       if (!bo)
+               return;
+
+       if (!atomic_dec_and_test(&bo->refcnt))
+               return;
+
+       pthread_mutex_lock(&table_lock);
+
+       if (bo->reuse && (etna_bo_cache_free(&dev->bo_cache, bo) == 0))
+               goto out;
+
+       bo_del(bo);
+       etna_device_del_locked(dev);
+out:
+       pthread_mutex_unlock(&table_lock);
+}
+
+/* get the global flink/DRI2 buffer name */
+int etna_bo_get_name(struct etna_bo *bo, uint32_t *name)
+{
+       if (!bo->name) {
+               struct drm_gem_flink req = {
+                       .handle = bo->handle,
+               };
+               int ret;
+
+               ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
+               if (ret) {
+                       return ret;
+               }
+
+               pthread_mutex_lock(&table_lock);
+               set_name(bo, req.name);
+               pthread_mutex_unlock(&table_lock);
+               bo->reuse = 0;
+       }
+
+       *name = bo->name;
+
+       return 0;
+}
+
+uint32_t etna_bo_handle(struct etna_bo *bo)
+{
+       return bo->handle;
+}
+
+/* caller owns the dmabuf fd that is returned and is responsible
+ * to close() it when done
+ */
+int etna_bo_dmabuf(struct etna_bo *bo)
+{
+       int ret, prime_fd;
+
+       ret = drmPrimeHandleToFD(bo->dev->fd, bo->handle, DRM_CLOEXEC,
+                               &prime_fd);
+       if (ret) {
+               ERROR_MSG("failed to get dmabuf fd: %d", ret);
+               return ret;
+       }
+
+       bo->reuse = 0;
+
+       return prime_fd;
+}
+
+uint32_t etna_bo_size(struct etna_bo *bo)
+{
+       return bo->size;
+}
+
+void *etna_bo_map(struct etna_bo *bo)
+{
+       if (!bo->map) {
+               if (!bo->offset) {
+                       get_buffer_info(bo);
+               }
+
+               bo->map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE,
+                               MAP_SHARED, bo->dev->fd, bo->offset);
+               if (bo->map == MAP_FAILED) {
+                       ERROR_MSG("mmap failed: %s", strerror(errno));
+                       bo->map = NULL;
+               }
+       }
+
+       return bo->map;
+}
+
+int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op)
+{
+       struct drm_etnaviv_gem_cpu_prep req = {
+               .handle = bo->handle,
+               .op = op,
+       };
+
+       get_abs_timeout(&req.timeout, 5000);
+
+       return drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_PREP,
+                       &req, sizeof(req));
+}
+
+void etna_bo_cpu_fini(struct etna_bo *bo)
+{
+       struct drm_etnaviv_gem_cpu_fini req = {
+               .handle = bo->handle,
+       };
+
+       drmCommandWrite(bo->dev->fd, DRM_ETNAVIV_GEM_CPU_FINI,
+                       &req, sizeof(req));
+}
diff --git a/etnaviv/etnaviv_bo_cache.c b/etnaviv/etnaviv_bo_cache.c
new file mode 100644
index 0000000..8924651
--- /dev/null
+++ b/etnaviv/etnaviv_bo_cache.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner at gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+drm_private void bo_del(struct etna_bo *bo);
+drm_private extern pthread_mutex_t table_lock;
+
+static void add_bucket(struct etna_bo_cache *cache, int size)
+{
+       unsigned i = cache->num_buckets;
+
+       assert(i < ARRAY_SIZE(cache->cache_bucket));
+
+       list_inithead(&cache->cache_bucket[i].list);
+       cache->cache_bucket[i].size = size;
+       cache->num_buckets++;
+}
+
+drm_private void etna_bo_cache_init(struct etna_bo_cache *cache)
+{
+       unsigned long size, cache_max_size = 64 * 1024 * 1024;
+
+       /* OK, so power of two buckets was too wasteful of memory.
+        * Give 3 other sizes between each power of two, to hopefully
+        * cover things accurately enough.  (The alternative is
+        * probably to just go for exact matching of sizes, and assume
+        * that for things like composited window resize the tiled
+        * width/height alignment and rounding of sizes to pages will
+        * get us useful cache hit rates anyway)
+        */
+       add_bucket(cache, 4096);
+       add_bucket(cache, 4096 * 2);
+       add_bucket(cache, 4096 * 3);
+
+       /* Initialize the linked lists for BO reuse cache. */
+       for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
+               add_bucket(cache, size);
+               add_bucket(cache, size + size * 1 / 4);
+               add_bucket(cache, size + size * 2 / 4);
+               add_bucket(cache, size + size * 3 / 4);
+       }
+}
+
+/* Frees older cached buffers.  Called under table_lock */
+drm_private void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t 
time)
+{
+       unsigned i;
+
+       if (cache->time == time)
+               return;
+
+       for (i = 0; i < cache->num_buckets; i++) {
+               struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
+               struct etna_bo *bo;
+
+               while (!LIST_IS_EMPTY(&bucket->list)) {
+                       bo = LIST_ENTRY(struct etna_bo, bucket->list.next, 
list);
+
+                       /* keep things in cache for at least 1 second: */
+                       if (time && ((time - bo->free_time) <= 1))
+                               break;
+
+                       list_del(&bo->list);
+                       bo_del(bo);
+               }
+       }
+
+       cache->time = time;
+}
+
+static struct etna_bo_bucket *get_bucket(struct etna_bo_cache *cache, uint32_t 
size)
+{
+       unsigned i;
+
+       /* hmm, this is what intel does, but I suppose we could calculate our
+        * way to the correct bucket size rather than looping..
+        */
+       for (i = 0; i < cache->num_buckets; i++) {
+               struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
+               if (bucket->size >= size) {
+                       return bucket;
+               }
+       }
+
+       return NULL;
+}
+
+static int is_idle(struct etna_bo *bo)
+{
+       return etna_bo_cpu_prep(bo,
+                       DRM_ETNA_PREP_READ |
+                       DRM_ETNA_PREP_WRITE |
+                       DRM_ETNA_PREP_NOSYNC) == 0;
+}
+
+static struct etna_bo *find_in_bucket(struct etna_bo_bucket *bucket, uint32_t 
flags)
+{
+       struct etna_bo *bo = NULL;
+
+       pthread_mutex_lock(&table_lock);
+       while (!LIST_IS_EMPTY(&bucket->list)) {
+               bo = LIST_ENTRY(struct etna_bo, bucket->list.next, list);
+
+               if (bo->flags == flags && is_idle(bo)) {
+                       list_del(&bo->list);
+                       break;
+               }
+
+               bo = NULL;
+               break;
+       }
+       pthread_mutex_unlock(&table_lock);
+
+       return bo;
+}
+
+/* allocate a new (un-tiled) buffer object
+ *
+ * NOTE: size is potentially rounded up to bucket size
+ */
+drm_private struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache, 
uint32_t *size,
+    uint32_t flags)
+{
+       struct etna_bo *bo;
+       struct etna_bo_bucket *bucket;
+
+       *size = ALIGN(*size, 4096);
+       bucket = get_bucket(cache, *size);
+
+       /* see if we can be green and recycle: */
+       if (bucket) {
+               *size = bucket->size;
+               bo = find_in_bucket(bucket, flags);
+               if (bo) {
+                       atomic_set(&bo->refcnt, 1);
+                       etna_device_ref(bo->dev);
+                       return bo;
+               }
+       }
+
+       return NULL;
+}
+
+drm_private int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo 
*bo)
+{
+       struct etna_bo_bucket *bucket = get_bucket(cache, bo->size);
+
+       /* see if we can be green and recycle: */
+       if (bucket) {
+               struct timespec time;
+
+               clock_gettime(CLOCK_MONOTONIC, &time);
+
+               bo->free_time = time.tv_sec;
+               list_addtail(&bo->list, &bucket->list);
+               etna_bo_cache_cleanup(cache, time.tv_sec);
+
+               /* bo's in the bucket cache don't have a ref and
+                * don't hold a ref to the dev:
+                */
+               etna_device_del_locked(bo->dev);
+
+               return 0;
+       }
+
+       return -1;
+}
diff --git a/etnaviv/etnaviv_cmd_stream.c b/etnaviv/etnaviv_cmd_stream.c
new file mode 100644
index 0000000..9ce3f36
--- /dev/null
+++ b/etnaviv/etnaviv_cmd_stream.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner at gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+
+#include "etnaviv_drmif.h"
+#include "etnaviv_priv.h"
+
+static pthread_mutex_t idx_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static void *grow(void *ptr, uint32_t nr, uint32_t *max, uint32_t sz)
+{
+       if ((nr + 1) > *max) {
+               if ((*max * 2) < (nr + 1))
+                       *max = nr + 5;
+               else
+                       *max = *max * 2;
+               ptr = realloc(ptr, *max * sz);
+       }
+
+       return ptr;
+}
+
+#define APPEND(x, name) ({ \
+       (x)->name = grow((x)->name, (x)->nr_ ## name, &(x)->max_ ## name, 
sizeof((x)->name[0])); \
+       (x)->nr_ ## name ++; \
+})
+
+static inline struct etna_cmd_stream_priv *
+etna_cmd_stream_priv(struct etna_cmd_stream *stream)
+{
+    return (struct etna_cmd_stream_priv *)stream;
+}
+
+struct etna_cmd_stream *etna_cmd_stream_new(struct etna_pipe *pipe, uint32_t 
size,
+               void (*reset_notify)(struct etna_cmd_stream *stream, void 
*priv),
+               void *priv)
+{
+       struct etna_cmd_stream_priv *stream = NULL;
+
+       if (size == 0) {
+               ERROR_MSG("invalid size of 0");
+               goto fail;
+       }
+
+       stream = calloc(1, sizeof(*stream));
+       if (!stream) {
+               ERROR_MSG("allocation failed");
+               goto fail;
+       }
+
+       /* allocate even number of 32-bit words */
+       size = ALIGN(size, 2);
+
+       stream->base.buffer = malloc(size * sizeof(uint32_t));
+       if (!stream->base.buffer) {
+               ERROR_MSG("allocation failed");
+               goto fail;
+       }
+
+       stream->base.size = size;
+       stream->pipe = pipe;
+       stream->reset_notify = reset_notify;
+       stream->reset_notify_priv = priv;
+
+       return &stream->base;
+
+fail:
+       if (stream)
+               etna_cmd_stream_del(&stream->base);
+
+       return NULL;
+}
+
+void etna_cmd_stream_del(struct etna_cmd_stream *stream)
+{
+       struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+
+       free(stream->buffer);
+       free(priv->submit.relocs);
+       free(priv);
+}
+
+static void reset_buffer(struct etna_cmd_stream *stream)
+{
+       struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+
+       stream->offset = 0;
+       priv->submit.nr_bos = 0;
+       priv->submit.nr_relocs = 0;
+       priv->nr_bos = 0;
+
+       if (priv->reset_notify)
+               priv->reset_notify(stream, priv->reset_notify_priv);
+}
+
+uint32_t etna_cmd_stream_timestamp(struct etna_cmd_stream *stream)
+{
+       return etna_cmd_stream_priv(stream)->last_timestamp;
+}
+
+static uint32_t append_bo(struct etna_cmd_stream *stream, struct etna_bo *bo)
+{
+       struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+       uint32_t idx;
+
+       idx = APPEND(&priv->submit, bos);
+       idx = APPEND(priv, bos);
+
+       priv->submit.bos[idx].flags = 0;
+       priv->submit.bos[idx].handle = bo->handle;
+
+       priv->bos[idx] = etna_bo_ref(bo);
+
+       return idx;
+}
+
+/* add (if needed) bo, return idx: */
+static uint32_t bo2idx(struct etna_cmd_stream *stream, struct etna_bo *bo,
+               uint32_t flags)
+{
+       struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+       uint32_t idx;
+
+       pthread_mutex_lock(&idx_lock);
+
+       if (!bo->current_stream) {
+               idx = append_bo(stream, bo);
+               bo->current_stream = stream;
+               bo->idx = idx;
+       } else if (bo->current_stream == stream) {
+               idx = bo->idx;
+       } else {
+               /* slow-path: */
+               for (idx = 0; idx < priv->nr_bos; idx++)
+                       if (priv->bos[idx] == bo)
+                               break;
+               if (idx == priv->nr_bos) {
+                       /* not found */
+                       idx = append_bo(stream, bo);
+               }
+       }
+       pthread_mutex_unlock(&idx_lock);
+
+       if (flags & ETNA_RELOC_READ)
+               priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_READ;
+       if (flags & ETNA_RELOC_WRITE)
+               priv->submit.bos[idx].flags |= ETNA_SUBMIT_BO_WRITE;
+
+       return idx;
+}
+
+static void flush(struct etna_cmd_stream *stream)
+{
+       struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+       int ret, id = priv->pipe->id;
+       struct etna_gpu *gpu = priv->pipe->gpu;
+
+       struct drm_etnaviv_gem_submit req = {
+               .pipe = gpu->core,
+               .exec_state = id,
+               .bos = VOID2U64(priv->submit.bos),
+               .nr_bos = priv->submit.nr_bos,
+               .relocs = VOID2U64(priv->submit.relocs),
+               .nr_relocs = priv->submit.nr_relocs,
+               .stream = VOID2U64(stream->buffer),
+               .stream_size = stream->offset * 4, /* in bytes */
+       };
+
+       ret = drmCommandWriteRead(gpu->dev->fd, DRM_ETNAVIV_GEM_SUBMIT,
+                       &req, sizeof(req));
+
+       if (ret)
+               ERROR_MSG("submit failed: %d (%s)", ret, strerror(errno));
+       else
+               priv->last_timestamp = req.fence;
+
+       for (uint32_t i = 0; i < priv->nr_bos; i++) {
+               struct etna_bo *bo = priv->bos[i];
+
+               bo->current_stream = NULL;
+               etna_bo_del(bo);
+       }
+}
+
+void etna_cmd_stream_flush(struct etna_cmd_stream *stream)
+{
+       flush(stream);
+       reset_buffer(stream);
+}
+
+void etna_cmd_stream_finish(struct etna_cmd_stream *stream)
+{
+       struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+
+       flush(stream);
+       etna_pipe_wait(priv->pipe, priv->last_timestamp, 5000);
+       reset_buffer(stream);
+}
+
+void etna_cmd_stream_reloc(struct etna_cmd_stream *stream, const struct 
etna_reloc *r)
+{
+       struct etna_cmd_stream_priv *priv = etna_cmd_stream_priv(stream);
+       struct drm_etnaviv_gem_submit_reloc *reloc;
+       uint32_t idx = APPEND(&priv->submit, relocs);
+       uint32_t addr = 0;
+
+       reloc = &priv->submit.relocs[idx];
+
+       reloc->reloc_idx = bo2idx(stream, r->bo, r->flags);
+       reloc->reloc_offset = r->offset;
+       reloc->submit_offset = stream->offset * 4; /* in bytes */
+       reloc->flags = 0;
+
+       etna_cmd_stream_emit(stream, addr);
+}
diff --git a/etnaviv/etnaviv_device.c b/etnaviv/etnaviv_device.c
new file mode 100644
index 0000000..f954ca4
--- /dev/null
+++ b/etnaviv/etnaviv_device.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner at gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include <xf86drm.h>
+#include <xf86atomic.h>
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER;
+
+struct etna_device *etna_device_new(int fd)
+{
+       struct etna_device *dev = calloc(sizeof(*dev), 1);
+
+       if (!dev)
+               return NULL;
+
+       atomic_set(&dev->refcnt, 1);
+       dev->fd = fd;
+       dev->handle_table = drmHashCreate();
+       dev->name_table = drmHashCreate();
+       etna_bo_cache_init(&dev->bo_cache);
+
+       return dev;
+}
+
+struct etna_device *etna_device_ref(struct etna_device *dev)
+{
+       atomic_inc(&dev->refcnt);
+
+       return dev;
+}
+
+static void etna_device_del_impl(struct etna_device *dev)
+{
+       etna_bo_cache_cleanup(&dev->bo_cache, 0);
+       drmHashDestroy(dev->handle_table);
+       drmHashDestroy(dev->name_table);
+
+       free(dev);
+}
+
+drm_private void etna_device_del_locked(struct etna_device *dev)
+{
+       if (!atomic_dec_and_test(&dev->refcnt))
+               return;
+
+       etna_device_del_impl(dev);
+}
+
+void etna_device_del(struct etna_device *dev)
+{
+       if (!atomic_dec_and_test(&dev->refcnt))
+               return;
+
+       pthread_mutex_lock(&table_lock);
+       etna_device_del_impl(dev);
+       pthread_mutex_unlock(&table_lock);
+}
diff --git a/etnaviv/etnaviv_drm.h b/etnaviv/etnaviv_drm.h
new file mode 100644
index 0000000..462241c
--- /dev/null
+++ b/etnaviv/etnaviv_drm.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2015 Etnaviv Project
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ETNAVIV_DRM_H__
+#define __ETNAVIV_DRM_H__
+
+#include "drm.h"
+
+/* Please note that modifications to all structs defined here are
+ * subject to backwards-compatibility constraints:
+ *  1) Do not use pointers, use __u64 instead for 32 bit / 64 bit
+ *     user/kernel compatibility
+ *  2) Keep fields aligned to their size
+ *  3) Because of how drm_ioctl() works, we can add new fields at
+ *     the end of an ioctl if some care is taken: drm_ioctl() will
+ *     zero out the new fields at the tail of the ioctl, so a zero
+ *     value should have a backwards compatible meaning.  And for
+ *     output params, userspace won't see the newly added output
+ *     fields.. so that has to be somehow ok.
+ */
+
+/* timeouts are specified in clock-monotonic absolute times (to simplify
+ * restarting interrupted ioctls).  The following struct is logically the
+ * same as 'struct timespec' but 32/64b ABI safe.
+ */
+struct drm_etnaviv_timespec {
+       __s64 tv_sec;          /* seconds */
+       __s64 tv_nsec;         /* nanoseconds */
+};
+
+#define ETNAVIV_PARAM_GPU_MODEL                     0x01
+#define ETNAVIV_PARAM_GPU_REVISION                  0x02
+#define ETNAVIV_PARAM_GPU_FEATURES_0                0x03
+#define ETNAVIV_PARAM_GPU_FEATURES_1                0x04
+#define ETNAVIV_PARAM_GPU_FEATURES_2                0x05
+#define ETNAVIV_PARAM_GPU_FEATURES_3                0x06
+#define ETNAVIV_PARAM_GPU_FEATURES_4                0x07
+#define ETNAVIV_PARAM_GPU_FEATURES_5                0x08
+#define ETNAVIV_PARAM_GPU_FEATURES_6                0x09
+
+#define ETNAVIV_PARAM_GPU_STREAM_COUNT              0x10
+#define ETNAVIV_PARAM_GPU_REGISTER_MAX              0x11
+#define ETNAVIV_PARAM_GPU_THREAD_COUNT              0x12
+#define ETNAVIV_PARAM_GPU_VERTEX_CACHE_SIZE         0x13
+#define ETNAVIV_PARAM_GPU_SHADER_CORE_COUNT         0x14
+#define ETNAVIV_PARAM_GPU_PIXEL_PIPES               0x15
+#define ETNAVIV_PARAM_GPU_VERTEX_OUTPUT_BUFFER_SIZE 0x16
+#define ETNAVIV_PARAM_GPU_BUFFER_SIZE               0x17
+#define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT         0x18
+#define ETNAVIV_PARAM_GPU_NUM_CONSTANTS             0x19
+#define ETNAVIV_PARAM_GPU_NUM_VARYINGS              0x1a
+
+#define ETNA_MAX_PIPES 4
+
+struct drm_etnaviv_param {
+       __u32 pipe;           /* in */
+       __u32 param;          /* in, ETNAVIV_PARAM_x */
+       __u64 value;          /* out (get_param) or in (set_param) */
+};
+
+/*
+ * GEM buffers:
+ */
+
+#define ETNA_BO_CACHE_MASK   0x000f0000
+/* cache modes */
+#define ETNA_BO_CACHED       0x00010000
+#define ETNA_BO_WC           0x00020000
+#define ETNA_BO_UNCACHED     0x00040000
+/* map flags */
+#define ETNA_BO_FORCE_MMU    0x00100000
+
+struct drm_etnaviv_gem_new {
+       __u64 size;           /* in */
+       __u32 flags;          /* in, mask of ETNA_BO_x */
+       __u32 handle;         /* out */
+};
+
+struct drm_etnaviv_gem_info {
+       __u32 handle;         /* in */
+       __u32 pad;
+       __u64 offset;         /* out, offset to pass to mmap() */
+};
+
+#define ETNA_PREP_READ        0x01
+#define ETNA_PREP_WRITE       0x02
+#define ETNA_PREP_NOSYNC      0x04
+
+struct drm_etnaviv_gem_cpu_prep {
+       __u32 handle;         /* in */
+       __u32 op;             /* in, mask of ETNA_PREP_x */
+       struct drm_etnaviv_timespec timeout;   /* in */
+};
+
+struct drm_etnaviv_gem_cpu_fini {
+       __u32 handle;         /* in */
+       __u32 flags;          /* in, placeholder for now, no defined values */
+};
+
+/*
+ * Cmdstream Submission:
+ */
+
+/* The value written into the cmdstream is logically:
+ * relocbuf->gpuaddr + reloc_offset
+ *
+ * NOTE that reloc's must be sorted by order of increasing submit_offset,
+ * otherwise EINVAL.
+ */
+struct drm_etnaviv_gem_submit_reloc {
+       __u32 submit_offset;  /* in, offset from submit_bo */
+       __u32 reloc_idx;      /* in, index of reloc_bo buffer */
+       __u64 reloc_offset;   /* in, offset from start of reloc_bo */
+       __u32 flags;          /* in, placeholder for now, no defined values */
+};
+
+/* Each buffer referenced elsewhere in the cmdstream submit (ie. the
+ * cmdstream buffer(s) themselves or reloc entries) has one (and only
+ * one) entry in the submit->bos[] table.
+ *
+ * As a optimization, the current buffer (gpu virtual address) can be
+ * passed back through the 'presumed' field.  If on a subsequent reloc,
+ * userspace passes back a 'presumed' address that is still valid,
+ * then patching the cmdstream for this entry is skipped.  This can
+ * avoid kernel needing to map/access the cmdstream bo in the common
+ * case.
+ */
+#define ETNA_SUBMIT_BO_READ             0x0001
+#define ETNA_SUBMIT_BO_WRITE            0x0002
+struct drm_etnaviv_gem_submit_bo {
+       __u32 flags;          /* in, mask of ETNA_SUBMIT_BO_x */
+       __u32 handle;         /* in, GEM handle */
+       __u64 presumed;       /* in/out, presumed buffer address */
+};
+
+/* Each cmdstream submit consists of a table of buffers involved, and
+ * one or more cmdstream buffers.  This allows for conditional execution
+ * (context-restore), and IB buffers needed for per tile/bin draw cmds.
+ */
+#define ETNA_PIPE_3D      0x00
+#define ETNA_PIPE_2D      0x01
+#define ETNA_PIPE_VG      0x02
+struct drm_etnaviv_gem_submit {
+       __u32 fence;          /* out */
+       __u32 pipe;           /* in */
+       __u32 exec_state;     /* in, initial execution state (ETNA_PIPE_x) */
+       __u32 nr_bos;         /* in, number of submit_bo's */
+       __u32 nr_relocs;      /* in, number of submit_reloc's */
+       __u32 stream_size;    /* in, cmdstream size */
+       __u64 bos;            /* in, ptr to array of submit_bo's */
+       __u64 relocs;         /* in, ptr to array of submit_reloc's */
+       __u64 stream;         /* in, ptr to cmdstream */
+};
+
+/* The normal way to synchronize with the GPU is just to CPU_PREP on
+ * a buffer if you need to access it from the CPU (other cmdstream
+ * submission from same or other contexts, PAGE_FLIP ioctl, etc, all
+ * handle the required synchronization under the hood).  This ioctl
+ * mainly just exists as a way to implement the gallium pipe_fence
+ * APIs without requiring a dummy bo to synchronize on.
+ */
+#define ETNA_WAIT_NONBLOCK      0x01
+struct drm_etnaviv_wait_fence {
+       __u32 pipe;           /* in */
+       __u32 fence;          /* in */
+       __u32 flags;          /* in, mask of ETNA_WAIT_x  */
+       __u32 pad;
+       struct drm_etnaviv_timespec timeout;   /* in */
+};
+
+#define ETNA_USERPTR_READ      0x01
+#define ETNA_USERPTR_WRITE     0x02
+struct drm_etnaviv_gem_userptr {
+       __u64 user_ptr; /* in, page aligned user pointer */
+       __u64 user_size;        /* in, page aligned user size */
+       __u32 flags;            /* in, flags */
+       __u32 handle;   /* out, non-zero handle */
+};
+
+struct drm_etnaviv_gem_wait {
+       __u32 pipe;                             /* in */
+       __u32 handle;                           /* in, bo to be waited for */
+       __u32 flags;                            /* in, mask of ETNA_WAIT_x  */
+       __u32 pad;
+       struct drm_etnaviv_timespec timeout;    /* in */
+};
+
+#define DRM_ETNAVIV_GET_PARAM          0x00
+/* placeholder:
+#define DRM_ETNAVIV_SET_PARAM          0x01
+ */
+#define DRM_ETNAVIV_GEM_NEW            0x02
+#define DRM_ETNAVIV_GEM_INFO           0x03
+#define DRM_ETNAVIV_GEM_CPU_PREP       0x04
+#define DRM_ETNAVIV_GEM_CPU_FINI       0x05
+#define DRM_ETNAVIV_GEM_SUBMIT         0x06
+#define DRM_ETNAVIV_WAIT_FENCE         0x07
+#define DRM_ETNAVIV_GEM_USERPTR        0x08
+#define DRM_ETNAVIV_GEM_WAIT           0x09
+#define DRM_ETNAVIV_NUM_IOCTLS         0x0a
+
+#define DRM_IOCTL_ETNAVIV_GET_PARAM    DRM_IOWR(DRM_COMMAND_BASE + 
DRM_ETNAVIV_GET_PARAM, struct drm_etnaviv_param)
+#define DRM_IOCTL_ETNAVIV_GEM_NEW      DRM_IOWR(DRM_COMMAND_BASE + 
DRM_ETNAVIV_GEM_NEW, struct drm_etnaviv_gem_new)
+#define DRM_IOCTL_ETNAVIV_GEM_INFO     DRM_IOWR(DRM_COMMAND_BASE + 
DRM_ETNAVIV_GEM_INFO, struct drm_etnaviv_gem_info)
+#define DRM_IOCTL_ETNAVIV_GEM_CPU_PREP DRM_IOW(DRM_COMMAND_BASE + 
DRM_ETNAVIV_GEM_CPU_PREP, struct drm_etnaviv_gem_cpu_prep)
+#define DRM_IOCTL_ETNAVIV_GEM_CPU_FINI DRM_IOW(DRM_COMMAND_BASE + 
DRM_ETNAVIV_GEM_CPU_FINI, struct drm_etnaviv_gem_cpu_fini)
+#define DRM_IOCTL_ETNAVIV_GEM_SUBMIT   DRM_IOWR(DRM_COMMAND_BASE + 
DRM_ETNAVIV_GEM_SUBMIT, struct drm_etnaviv_gem_submit)
+#define DRM_IOCTL_ETNAVIV_WAIT_FENCE   DRM_IOW(DRM_COMMAND_BASE + 
DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence)
+#define DRM_IOCTL_ETNAVIV_GEM_USERPTR  DRM_IOWR(DRM_COMMAND_BASE + 
DRM_ETNAVIV_GEM_USERPTR, struct drm_etnaviv_gem_userptr)
+#define DRM_IOCTL_ETNAVIV_GEM_WAIT     DRM_IOW(DRM_COMMAND_BASE + 
DRM_ETNAVIV_GEM_WAIT, struct drm_etnaviv_gem_wait)
+
+#endif /* __ETNAVIV_DRM_H__ */
diff --git a/etnaviv/etnaviv_drmif.h b/etnaviv/etnaviv_drmif.h
new file mode 100644
index 0000000..979b16a
--- /dev/null
+++ b/etnaviv/etnaviv_drmif.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner at gmail.com>
+ */
+
+#ifndef ETNAVIV_DRMIF_H_
+#define ETNAVIV_DRMIF_H_
+
+#include <xf86drm.h>
+#include <stdint.h>
+
+struct etna_bo;
+struct etna_pipe;
+struct etna_gpu;
+struct etna_device;
+struct etna_cmd_stream;
+
+enum etna_pipe_id {
+       ETNA_PIPE_3D = 0,
+       ETNA_PIPE_2D = 1,
+       ETNA_PIPE_VG = 2,
+       ETNA_PIPE_MAX
+};
+
+enum etna_param_id {
+       ETNA_GPU_MODEL                     = 0x1,
+       ETNA_GPU_REVISION                  = 0x2,
+       ETNA_GPU_FEATURES_0                = 0x3,
+       ETNA_GPU_FEATURES_1                = 0x4,
+       ETNA_GPU_FEATURES_2                = 0x5,
+       ETNA_GPU_FEATURES_3                = 0x6,
+       ETNA_GPU_FEATURES_4                = 0x7,
+       ETNA_GPU_FEATURES_5                = 0x8,
+       ETNA_GPU_FEATURES_6                = 0x9,
+
+       ETNA_GPU_STREAM_COUNT              = 0x10,
+       ETNA_GPU_REGISTER_MAX              = 0x11,
+       ETNA_GPU_THREAD_COUNT              = 0x12,
+       ETNA_GPU_VERTEX_CACHE_SIZE         = 0x13,
+       ETNA_GPU_SHADER_CORE_COUNT         = 0x14,
+       ETNA_GPU_PIXEL_PIPES               = 0x15,
+       ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE = 0x16,
+       ETNA_GPU_BUFFER_SIZE               = 0x17,
+       ETNA_GPU_INSTRUCTION_COUNT         = 0x18,
+       ETNA_GPU_NUM_CONSTANTS             = 0x19,
+       ETNA_GPU_NUM_VARYINGS              = 0x1a
+};
+
+/* bo flags: */
+#define DRM_ETNA_GEM_CACHE_CACHED       0x00010000
+#define DRM_ETNA_GEM_CACHE_WC           0x00020000
+#define DRM_ETNA_GEM_CACHE_UNCACHED     0x00040000
+#define DRM_ETNA_GEM_CACHE_MASK         0x000f0000
+/* map flags */
+#define DRM_ETNA_GEM_FORCE_MMU          0x00100000
+
+/* bo access flags: (keep aligned to ETNA_PREP_x) */
+#define DRM_ETNA_PREP_READ              0x01
+#define DRM_ETNA_PREP_WRITE             0x02
+#define DRM_ETNA_PREP_NOSYNC            0x04
+
+/* device functions:
+ */
+
+struct etna_device *etna_device_new(int fd);
+struct etna_device *etna_device_ref(struct etna_device *dev);
+void etna_device_del(struct etna_device *dev);
+
+/* gpu functions:
+ */
+
+struct etna_gpu *etna_gpu_new(struct etna_device *dev, unsigned int core);
+void etna_gpu_del(struct etna_gpu *gpu);
+int etna_gpu_get_param(struct etna_gpu *gpu, enum etna_param_id param,
+               uint64_t *value);
+
+
+/* pipe functions:
+ */
+
+struct etna_pipe *etna_pipe_new(struct etna_gpu *gpu, enum etna_pipe_id id);
+void etna_pipe_del(struct etna_pipe *pipe);
+int etna_pipe_wait(struct etna_pipe *pipe, uint32_t timestamp, uint32_t ms);
+
+
+/* buffer-object functions:
+ */
+
+struct etna_bo *etna_bo_new(struct etna_device *dev,
+               uint32_t size, uint32_t flags);
+struct etna_bo *etna_bo_from_handle(struct etna_device *dev,
+               uint32_t handle, uint32_t size);
+struct etna_bo *etna_bo_from_name(struct etna_device *dev, uint32_t name);
+struct etna_bo *etna_bo_from_dmabuf(struct etna_device *dev, int fd);
+struct etna_bo *etna_bo_ref(struct etna_bo *bo);
+void etna_bo_del(struct etna_bo *bo);
+int etna_bo_get_name(struct etna_bo *bo, uint32_t *name);
+uint32_t etna_bo_handle(struct etna_bo *bo);
+int etna_bo_dmabuf(struct etna_bo *bo);
+uint32_t etna_bo_size(struct etna_bo *bo);
+void * etna_bo_map(struct etna_bo *bo);
+int etna_bo_cpu_prep(struct etna_bo *bo, uint32_t op);
+void etna_bo_cpu_fini(struct etna_bo *bo);
+
+
+/* cmd stream functions:
+ */
+
+struct etna_cmd_stream {
+       uint32_t *buffer;
+       uint32_t offset;        /* in 32-bit words */
+       uint32_t size;          /* in 32-bit words */
+};
+
+struct etna_cmd_stream *etna_cmd_stream_new(struct etna_pipe *pipe, uint32_t 
size,
+               void (*reset_notify)(struct etna_cmd_stream *stream, void 
*priv),
+               void *priv);
+void etna_cmd_stream_del(struct etna_cmd_stream *stream);
+uint32_t etna_cmd_stream_timestamp(struct etna_cmd_stream *stream);
+void etna_cmd_stream_flush(struct etna_cmd_stream *stream);
+void etna_cmd_stream_finish(struct etna_cmd_stream *stream);
+
+static inline uint32_t etna_cmd_stream_avail(struct etna_cmd_stream *stream)
+{
+       static const uint32_t END_CLEARANCE = 2; /* LINK op code */
+
+       return stream->size - stream->offset - END_CLEARANCE;
+}
+
+static inline void etna_cmd_stream_reserve(struct etna_cmd_stream *stream, 
size_t n)
+{
+       if (etna_cmd_stream_avail(stream) < n)
+               etna_cmd_stream_flush(stream);
+}
+
+static inline void etna_cmd_stream_emit(struct etna_cmd_stream *stream, 
uint32_t data)
+{
+       stream->buffer[stream->offset++] = data;
+}
+
+static inline uint32_t etna_cmd_stream_get(struct etna_cmd_stream *stream, 
uint32_t offset)
+{
+       return stream->buffer[offset];
+}
+
+static inline void etna_cmd_stream_set(struct etna_cmd_stream *stream, 
uint32_t offset,
+               uint32_t data)
+{
+       stream->buffer[offset] = data;
+}
+
+static inline uint32_t etna_cmd_stream_offset(struct etna_cmd_stream *stream)
+{
+       return stream->offset;
+}
+
+struct etna_reloc {
+       struct etna_bo *bo;
+#define ETNA_RELOC_READ             0x0001
+#define ETNA_RELOC_WRITE            0x0002
+       uint32_t flags;
+       uint32_t offset;
+};
+
+void etna_cmd_stream_reloc(struct etna_cmd_stream *stream, const struct 
etna_reloc *r);
+
+#endif /* ETNAVIV_DRMIF_H_ */
diff --git a/etnaviv/etnaviv_gpu.c b/etnaviv/etnaviv_gpu.c
new file mode 100644
index 0000000..35dec6c
--- /dev/null
+++ b/etnaviv/etnaviv_gpu.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2015 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner at gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+static uint64_t get_param(struct etna_device *dev, uint32_t core, uint32_t 
param)
+{
+       struct drm_etnaviv_param req = {
+               .pipe = core,
+               .param = param,
+       };
+       int ret;
+
+       ret = drmCommandWriteRead(dev->fd, DRM_ETNAVIV_GET_PARAM, &req, 
sizeof(req));
+       if (ret) {
+               ERROR_MSG("get-param (%x) failed! %d (%s)", param, ret, 
strerror(errno));
+               return 0;
+       }
+
+       return req.value;
+}
+
+struct etna_gpu *etna_gpu_new(struct etna_device *dev, unsigned int core)
+{
+       struct etna_gpu *gpu;
+
+       gpu = calloc(1, sizeof(*gpu));
+       if (!gpu) {
+               ERROR_MSG("allocation failed");
+               goto fail;
+       }
+
+       gpu->dev = dev;
+       gpu->core = core;
+
+       /* get specs from kernel space */
+       gpu->specs.model        = get_param(dev, core, ETNAVIV_PARAM_GPU_MODEL);
+       gpu->specs.revision     = get_param(dev, core, 
ETNAVIV_PARAM_GPU_REVISION);
+       gpu->specs.features[0] = get_param(dev, core, 
ETNAVIV_PARAM_GPU_FEATURES_0);
+       gpu->specs.features[1] = get_param(dev, core, 
ETNAVIV_PARAM_GPU_FEATURES_1);
+       gpu->specs.features[2] = get_param(dev, core, 
ETNAVIV_PARAM_GPU_FEATURES_2);
+       gpu->specs.features[3] = get_param(dev, core, 
ETNAVIV_PARAM_GPU_FEATURES_3);
+       gpu->specs.features[4] = get_param(dev, core, 
ETNAVIV_PARAM_GPU_FEATURES_4);
+       gpu->specs.features[5] = get_param(dev, core, 
ETNAVIV_PARAM_GPU_FEATURES_5);
+       gpu->specs.features[6] = get_param(dev, core, 
ETNAVIV_PARAM_GPU_FEATURES_6);
+       gpu->specs.stream_count = get_param(dev, core, ETNA_GPU_STREAM_COUNT);
+       gpu->specs.register_max = get_param(dev, core, ETNA_GPU_REGISTER_MAX);
+       gpu->specs.thread_count = get_param(dev, core, ETNA_GPU_THREAD_COUNT);
+       gpu->specs.vertex_cache_size = get_param(dev, core, 
ETNA_GPU_VERTEX_CACHE_SIZE);
+       gpu->specs.shader_core_count = get_param(dev, core, 
ETNA_GPU_SHADER_CORE_COUNT);
+       gpu->specs.pixel_pipes = get_param(dev, core, ETNA_GPU_PIXEL_PIPES);
+       gpu->specs.vertex_output_buffer_size = get_param(dev, core, 
ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE);
+       gpu->specs.buffer_size = get_param(dev, core, ETNA_GPU_BUFFER_SIZE);
+       gpu->specs.instruction_count = get_param(dev, core, 
ETNA_GPU_INSTRUCTION_COUNT);
+       gpu->specs.num_constants = get_param(dev, core, ETNA_GPU_NUM_CONSTANTS);
+       gpu->specs.num_varyings = get_param(dev, core, ETNA_GPU_NUM_VARYINGS);
+
+       if (!gpu->specs.model)
+               goto fail;
+
+       INFO_MSG(" GPU model:          0x%x (rev %x)", gpu->specs.model, 
gpu->specs.revision);
+
+       return gpu;
+fail:
+       if (gpu)
+               etna_gpu_del(gpu);
+
+       return NULL;
+}
+
+void etna_gpu_del(struct etna_gpu *gpu)
+{
+       free(gpu);
+}
+
+int etna_gpu_get_param(struct etna_gpu *gpu, enum etna_param_id param,
+               uint64_t *value)
+{
+       switch(param) {
+       case ETNA_GPU_MODEL:
+               *value = gpu->specs.model;
+               return 0;
+       case ETNA_GPU_REVISION:
+               *value = gpu->specs.revision;
+               return 0;
+       case ETNA_GPU_FEATURES_0:
+               *value = gpu->specs.features[0];
+               return 0;
+       case ETNA_GPU_FEATURES_1:
+               *value = gpu->specs.features[1];
+               return 0;
+       case ETNA_GPU_FEATURES_2:
+               *value = gpu->specs.features[2];
+               return 0;
+       case ETNA_GPU_FEATURES_3:
+               *value = gpu->specs.features[3];
+               return 0;
+       case ETNA_GPU_FEATURES_4:
+               *value = gpu->specs.features[4];
+               return 0;
+       case ETNA_GPU_FEATURES_5:
+               *value = gpu->specs.features[5];
+               return 0;
+       case ETNA_GPU_FEATURES_6:
+               *value = gpu->specs.features[6];
+               return 0;
+       case ETNA_GPU_STREAM_COUNT:
+               *value = gpu->specs.stream_count;
+               return 0;
+       case ETNA_GPU_REGISTER_MAX:
+               *value = gpu->specs.register_max;
+               return 0;
+       case ETNA_GPU_THREAD_COUNT:
+               *value = gpu->specs.thread_count;
+               return 0;
+       case ETNA_GPU_VERTEX_CACHE_SIZE:
+               *value = gpu->specs.vertex_cache_size;
+               return 0;
+       case ETNA_GPU_SHADER_CORE_COUNT:
+               *value = gpu->specs.shader_core_count;
+               return 0;
+       case ETNA_GPU_PIXEL_PIPES:
+               *value = gpu->specs.pixel_pipes;
+               return 0;
+       case ETNA_GPU_VERTEX_OUTPUT_BUFFER_SIZE:
+               *value = gpu->specs.vertex_output_buffer_size;
+               return 0;
+       case ETNA_GPU_BUFFER_SIZE:
+               *value = gpu->specs.buffer_size;
+               return 0;
+       case ETNA_GPU_INSTRUCTION_COUNT:
+               *value = gpu->specs.instruction_count;
+               return 0;
+       case ETNA_GPU_NUM_CONSTANTS:
+               *value = gpu->specs.num_constants;
+               return 0;
+       case ETNA_GPU_NUM_VARYINGS:
+               *value = gpu->specs.num_varyings;
+               return 0;
+
+       default:
+               ERROR_MSG("invalid param id: %d", param);
+               return -1;
+       }
+
+       return 0;
+}
diff --git a/etnaviv/etnaviv_pipe.c b/etnaviv/etnaviv_pipe.c
new file mode 100644
index 0000000..402b71d
--- /dev/null
+++ b/etnaviv/etnaviv_pipe.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner at gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "etnaviv_priv.h"
+
+int etna_pipe_wait(struct etna_pipe *pipe, uint32_t timestamp, uint32_t ms)
+{
+       struct etna_device *dev = pipe->gpu->dev;
+       int ret;
+
+       struct drm_etnaviv_wait_fence req = {
+               .pipe = pipe->gpu->core,
+               .fence = timestamp,
+       };
+
+       if (ms == 0)
+               req.flags |= ETNA_WAIT_NONBLOCK;
+
+       get_abs_timeout(&req.timeout, ms);
+
+       ret = drmCommandWrite(dev->fd, DRM_ETNAVIV_WAIT_FENCE, &req, 
sizeof(req));
+       if (ret) {
+               ERROR_MSG("wait-fence failed! %d (%s)", ret, strerror(errno));
+               return ret;
+       }
+
+       return 0;
+}
+
+void etna_pipe_del(struct etna_pipe *pipe)
+{
+       free(pipe);
+}
+
+struct etna_pipe *etna_pipe_new(struct etna_gpu *gpu, enum etna_pipe_id id)
+{
+       struct etna_pipe *pipe;
+
+       pipe = calloc(1, sizeof(*pipe));
+       if (!pipe) {
+               ERROR_MSG("allocation failed");
+               goto fail;
+       }
+
+       pipe->id = id;
+       pipe->gpu = gpu;
+
+       return pipe;
+fail:
+       return NULL;
+}
diff --git a/etnaviv/etnaviv_priv.h b/etnaviv/etnaviv_priv.h
new file mode 100644
index 0000000..42d9a45
--- /dev/null
+++ b/etnaviv/etnaviv_priv.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner at gmail.com>
+ */
+
+#ifndef ETNAVIV_PRIV_H_
+#define ETNAVIV_PRIV_H_
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "libdrm_macros.h"
+#include "xf86drm.h"
+#include "xf86atomic.h"
+
+#include "util_double_list.h"
+
+#include "etnaviv_drmif.h"
+//#include "drm.h"
+#include "etnaviv_drm.h"
+
+#define VIV_FEATURES_WORD_COUNT 6
+
+struct etna_specs {
+       uint32_t model;
+       uint32_t revision;
+       uint32_t features[VIV_FEATURES_WORD_COUNT + 1];
+       uint32_t stream_count;
+       uint32_t register_max;
+       uint32_t thread_count;
+       uint32_t shader_core_count;
+       uint32_t vertex_cache_size;
+       uint32_t vertex_output_buffer_size;
+       uint32_t pixel_pipes;
+       uint32_t instruction_count;
+       uint32_t num_constants;
+       uint32_t num_varyings;
+       uint32_t buffer_size;
+};
+
+struct etna_bo_bucket {
+       uint32_t size;
+       struct list_head list;
+};
+
+struct etna_bo_cache {
+       struct etna_bo_bucket cache_bucket[14 * 4];
+       unsigned num_buckets;
+       time_t time;
+};
+
+struct etna_device {
+       int fd;
+       atomic_t refcnt;
+
+       /* tables to keep track of bo's, to avoid "evil-twin" etna_bo objects:
+        *
+        *   handle_table: maps handle to etna_bo
+        *   name_table: maps flink name to etna_bo
+        *
+        * We end up needing two tables, because DRM_IOCTL_GEM_OPEN always
+        * returns a new handle.  So we need to figure out if the bo is already
+        * open in the process first, before calling gem-open.
+        */
+       void *handle_table, *name_table;
+
+       struct etna_bo_cache bo_cache;
+};
+
+drm_private void etna_bo_cache_init(struct etna_bo_cache *cache);
+drm_private void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t 
time);
+drm_private struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache,
+               uint32_t *size, uint32_t flags);
+drm_private int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo 
*bo);
+
+/* for where @table_lock is already held: */
+drm_private void etna_device_del_locked(struct etna_device *dev);
+
+/* a GEM buffer object allocated from the DRM device */
+struct etna_bo {
+       struct etna_device      *dev;
+       void            *map;           /* userspace mmap'ing (if there is one) 
*/
+       uint32_t        size;
+       uint32_t        handle;
+       uint32_t        flags;
+       uint32_t        name;           /* flink global handle (DRI2 name) */
+       uint64_t        offset;         /* offset to mmap() */
+       atomic_t        refcnt;
+
+       /* in the common case, a bo won't be referenced by more than a single
+        * command stream.  So to avoid looping over all the bo's in the
+        * reloc table to find the idx of a bo that might already be in the
+        * table, we cache the idx in the bo.  But in order to detect the
+        * slow-path where bo is ref'd in multiple streams, we also must track
+        * the current_stream for which the idx is valid.  See bo2idx().
+        */
+       struct etna_cmd_stream *current_stream;
+       uint32_t idx;
+
+       int reuse;
+       struct list_head list;   /* bucket-list entry */
+       time_t free_time;        /* time when added to bucket-list */
+};
+
+struct etna_gpu {
+       struct etna_device *dev;
+       struct etna_specs specs;
+       uint32_t core;
+};
+
+struct etna_pipe {
+       enum etna_pipe_id id;
+       struct etna_gpu *gpu;
+};
+
+struct etna_cmd_stream_priv {
+       struct etna_cmd_stream base;
+       struct etna_pipe *pipe;
+
+       uint32_t last_timestamp;
+
+       /* submit ioctl related tables: */
+       struct {
+               /* bo's table: */
+               struct drm_etnaviv_gem_submit_bo *bos;
+               uint32_t nr_bos, max_bos;
+
+               /* reloc's table: */
+               struct drm_etnaviv_gem_submit_reloc *relocs;
+               uint32_t nr_relocs, max_relocs;
+       } submit;
+
+       /* should have matching entries in submit.bos: */
+       struct etna_bo **bos;
+       uint32_t nr_bos, max_bos;
+
+       /* notify callback if buffer reset happend */
+       void (*reset_notify)(struct etna_cmd_stream *stream, void *priv);
+       void *reset_notify_priv;
+};
+
+#define ALIGN(v,a) (((v) + (a) - 1) & ~((a) - 1))
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define enable_debug 1  /* TODO make dynamic */
+
+#define INFO_MSG(fmt, ...) \
+               do { drmMsg("[I] "fmt " (%s:%d)\n", \
+                               ##__VA_ARGS__, __FUNCTION__, __LINE__); } while 
(0)
+#define DEBUG_MSG(fmt, ...) \
+               do if (enable_debug) { drmMsg("[D] "fmt " (%s:%d)\n", \
+                               ##__VA_ARGS__, __FUNCTION__, __LINE__); } while 
(0)
+#define WARN_MSG(fmt, ...) \
+               do { drmMsg("[W] "fmt " (%s:%d)\n", \
+                               ##__VA_ARGS__, __FUNCTION__, __LINE__); } while 
(0)
+#define ERROR_MSG(fmt, ...) \
+               do { drmMsg("[E] " fmt " (%s:%d)\n", \
+                               ##__VA_ARGS__, __FUNCTION__, __LINE__); } while 
(0)
+
+#define VOID2U64(x) ((uint64_t)(unsigned long)(x))
+
+static inline void get_abs_timeout(struct drm_etnaviv_timespec *tv, uint32_t 
ms)
+{
+       struct timespec t;
+       uint32_t s = ms / 1000;
+       clock_gettime(CLOCK_MONOTONIC, &t);
+       tv->tv_sec = t.tv_sec + s;
+       tv->tv_nsec = t.tv_nsec + ((ms - (s * 1000)) * 1000000);
+}
+
+#endif /* ETNAVIV_PRIV_H_ */
diff --git a/etnaviv/libdrm_etnaviv.pc.in b/etnaviv/libdrm_etnaviv.pc.in
new file mode 100644
index 0000000..13fed01
--- /dev/null
+++ b/etnaviv/libdrm_etnaviv.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_etnaviv
+Description: Userspace interface to etnaviv kernel DRM services
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -ldrm_etnaviv
+Cflags: -I${includedir} -I${includedir}/libdrm
+Requires.private: libdrm
-- 
2.7.4

Reply via email to