summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThe etnaviv authors <dri-devel@lists.freedesktop.org>2016-09-06 18:15:53 +0200
committerChristian Gmeiner <christian.gmeiner@gmail.com>2016-09-17 15:05:25 +0200
commit95e2cc6a801d92a0634b75fb02221213bd11e9da (patch)
treee2502b4adf10dfc2d2b9311752b51c399c2a4b57
parent2d00869599a1c853238401a38a334c3bc8673343 (diff)
libdrm: add etnaviv drm support
Add the libdrm_etnaviv helper library to encapsulate etnaviv-specific interfaces to the DRM. Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com> Signed-off-by: Lucas Stach <l.stach@pengutronix.de> Tested-by: Rob Herring <robh@kernel.org>
-rw-r--r--.gitignore1
-rw-r--r--Makefile.am6
-rw-r--r--configure.ac16
-rw-r--r--etnaviv/Android.mk18
-rw-r--r--etnaviv/Makefile.am26
-rw-r--r--etnaviv/Makefile.sources12
-rwxr-xr-xetnaviv/etnaviv-symbol-check45
-rw-r--r--etnaviv/etnaviv_bo.c347
-rw-r--r--etnaviv/etnaviv_bo_cache.c196
-rw-r--r--etnaviv/etnaviv_cmd_stream.c243
-rw-r--r--etnaviv/etnaviv_device.c96
-rw-r--r--etnaviv/etnaviv_drm.h233
-rw-r--r--etnaviv/etnaviv_drmif.h188
-rw-r--r--etnaviv/etnaviv_gpu.c175
-rw-r--r--etnaviv/etnaviv_pipe.c78
-rw-r--r--etnaviv/etnaviv_priv.h199
-rw-r--r--etnaviv/libdrm_etnaviv.pc.in11
17 files changed, 1890 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index a44566fb..3226b3a6 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 2ceb3526..630edc4c 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 e3048c76..64f3e6c9 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 00000000..a3a22959
--- /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 00000000..be96ba86
--- /dev/null
+++ b/etnaviv/Makefile.am
@@ -0,0 +1,26 @@
+include Makefile.sources
+
+AM_CFLAGS = \
+ $(WARN_CFLAGS) \
+ -I$(top_srcdir) \
+ $(PTHREADSTUBS_CFLAGS) \
+ -I$(top_srcdir)/include/drm
+
+libdrm_etnaviv_ladir = $(libdir)
+libdrm_etnaviv_la_LTLIBRARIES = libdrm_etnaviv.la
+libdrm_etnaviv_la_LDFLAGS = -version-number 1:0:0 -no-undefined
+libdrm_etnaviv_la_LIBADD = \
+ ../libdrm.la \
+ @PTHREADSTUBS_LIBS@ \
+ @CLOCK_LIB@
+
+libdrm_etnaviv_la_SOURCES = $(LIBDRM_ETNAVIV_FILES)
+
+libdrm_etnavivincludedir = ${includedir}/libdrm
+libdrm_etnavivinclude_HEADERS = $(LIBDRM_ETNAVIV_H_FILES)
+
+pkgconfigdir = @pkgconfigdir@
+pkgconfig_DATA = libdrm_etnaviv.pc
+
+TESTS = etnaviv-symbol-check
+EXTRA_DIST = $(TESTS)
diff --git a/etnaviv/Makefile.sources b/etnaviv/Makefile.sources
new file mode 100644
index 00000000..52580567
--- /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 00000000..77c94c6e
--- /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_ETNAVIV_H_FILES
+
+FUNCS=$(nm -D --format=bsd --defined-only ${1-.libs/libdrm_etnaviv.so} | awk '{print $3}'| while read func; do
+( grep -q "^$func$" || echo $func ) <<EOF
+__bss_start
+_edata
+_end
+_fini
+_init
+etna_device_new
+etna_device_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 00000000..833f8bd4
--- /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@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 00000000..8924651f
--- /dev/null
+++ b/etnaviv/etnaviv_bo_cache.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "etnaviv_priv.h"
+#include "etnaviv_drmif.h"
+
+drm_private void bo_del(struct etna_bo *bo);
+drm_private extern pthread_mutex_t table_lock;
+
+static void add_bucket(struct etna_bo_cache *cache, int size)
+{
+ unsigned i = cache->num_buckets;
+
+ assert(i < ARRAY_SIZE(cache->cache_bucket));
+
+ list_inithead(&cache->cache_bucket[i].list);
+ cache->cache_bucket[i].size = size;
+ cache->num_buckets++;
+}
+
+drm_private void etna_bo_cache_init(struct etna_bo_cache *cache)
+{
+ unsigned long size, cache_max_size = 64 * 1024 * 1024;
+
+ /* OK, so power of two buckets was too wasteful of memory.
+ * Give 3 other sizes between each power of two, to hopefully
+ * cover things accurately enough. (The alternative is
+ * probably to just go for exact matching of sizes, and assume
+ * that for things like composited window resize the tiled
+ * width/height alignment and rounding of sizes to pages will
+ * get us useful cache hit rates anyway)
+ */
+ add_bucket(cache, 4096);
+ add_bucket(cache, 4096 * 2);
+ add_bucket(cache, 4096 * 3);
+
+ /* Initialize the linked lists for BO reuse cache. */
+ for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
+ add_bucket(cache, size);
+ add_bucket(cache, size + size * 1 / 4);
+ add_bucket(cache, size + size * 2 / 4);
+ add_bucket(cache, size + size * 3 / 4);
+ }
+}
+
+/* Frees older cached buffers. Called under table_lock */
+drm_private void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time)
+{
+ unsigned i;
+
+ if (cache->time == time)
+ return;
+
+ for (i = 0; i < cache->num_buckets; i++) {
+ struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
+ struct etna_bo *bo;
+
+ while (!LIST_IS_EMPTY(&bucket->list)) {
+ bo = LIST_ENTRY(struct etna_bo, bucket->list.next, list);
+
+ /* keep things in cache for at least 1 second: */
+ if (time && ((time - bo->free_time) <= 1))
+ break;
+
+ list_del(&bo->list);
+ bo_del(bo);
+ }
+ }
+
+ cache->time = time;
+}
+
+static struct etna_bo_bucket *get_bucket(struct etna_bo_cache *cache, uint32_t size)
+{
+ unsigned i;
+
+ /* hmm, this is what intel does, but I suppose we could calculate our
+ * way to the correct bucket size rather than looping..
+ */
+ for (i = 0; i < cache->num_buckets; i++) {
+ struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
+ if (bucket->size >= size) {
+ return bucket;
+ }
+ }
+
+ return NULL;
+}
+
+static int is_idle(struct etna_bo *bo)
+{
+ return etna_bo_cpu_prep(bo,
+ DRM_ETNA_PREP_READ |
+ DRM_ETNA_PREP_WRITE |
+ DRM_ETNA_PREP_NOSYNC) == 0;
+}
+
+static struct etna_bo *find_in_bucket(struct etna_bo_bucket *bucket, uint32_t flags)
+{
+ struct etna_bo *bo = NULL;
+
+ pthread_mutex_lock(&table_lock);
+ while (!LIST_IS_EMPTY(&bucket->list)) {
+ bo = LIST_ENTRY(struct etna_bo, bucket->list.next, list);
+
+ if (bo->flags == flags && is_idle(bo)) {
+ list_del(&bo->list);
+ break;
+ }
+
+ bo = NULL;
+ break;
+ }
+ pthread_mutex_unlock(&table_lock);
+
+ return bo;
+}
+
+/* allocate a new (un-tiled) buffer object
+ *
+ * NOTE: size is potentially rounded up to bucket size
+ */
+drm_private struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache, uint32_t *size,
+ uint32_t flags)
+{
+ struct etna_bo *bo;
+ struct etna_bo_bucket *bucket;
+
+ *size = ALIGN(*size, 4096);
+ bucket = get_bucket(cache, *size);
+
+ /* see if we can be green and recycle: */
+ if (bucket) {
+ *size = bucket->size;
+ bo = find_in_bucket(bucket, flags);
+ if (bo) {
+ atomic_set(&bo->refcnt, 1);
+ etna_device_ref(bo->dev);
+ return bo;
+ }
+ }
+
+ return NULL;
+}
+
+drm_private int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo)
+{
+ struct etna_bo_bucket *bucket = get_bucket(cache, bo->size);
+
+ /* see if we can be green and recycle: */
+ if (bucket) {
+ struct timespec time;
+
+ clock_gettime(CLOCK_MONOTONIC, &time);
+
+ bo->free_time = time.tv_sec;
+ list_addtail(&bo->list, &bucket->list);
+ etna_bo_cache_cleanup(cache, time.tv_sec);
+
+ /* bo's in the bucket cache don't have a ref and
+ * don't hold a ref to the dev:
+ */
+ etna_device_del_locked(bo->dev);
+
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/etnaviv/etnaviv_cmd_stream.c b/etnaviv/etnaviv_cmd_stream.c
new file mode 100644
index 00000000..9ce3f363
--- /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@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 00000000..f954ca4e
--- /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@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 00000000..2584c1cc
--- /dev/null
+++ b/etnaviv/etnaviv_drm.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2015 Etnaviv Project
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ETNAVIV_DRM_H__
+#define __ETNAVIV_DRM_H__
+
+#include "drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Please note that modifications to all structs defined here are
+ * subject to backwards-compatibility constraints:
+ * 1) Do not use pointers, use __u64 instead for 32 bit / 64 bit
+ * user/kernel compatibility
+ * 2) Keep fields aligned to their size
+ * 3) Because of how drm_ioctl() works, we can add new fields at
+ * the end of an ioctl if some care is taken: drm_ioctl() will
+ * zero out the new fields at the tail of the ioctl, so a zero
+ * value should have a backwards compatible meaning. And for
+ * output params, userspace won't see the newly added output
+ * fields.. so that has to be somehow ok.
+ */
+
+/* timeouts are specified in clock-monotonic absolute times (to simplify
+ * restarting interrupted ioctls). The following struct is logically the
+ * same as 'struct timespec' but 32/64b ABI safe.
+ */
+struct drm_etnaviv_timespec {
+ __s64 tv_sec; /* seconds */
+ __s64 tv_nsec; /* nanoseconds */
+};
+
+#define ETNAVIV_PARAM_GPU_MODEL 0x01
+#define ETNAVIV_PARAM_GPU_REVISION 0x02
+#define ETNAVIV_PARAM_GPU_FEATURES_0 0x03
+#define ETNAVIV_PARAM_GPU_FEATURES_1 0x04
+#define ETNAVIV_PARAM_GPU_FEATURES_2 0x05
+#define ETNAVIV_PARAM_GPU_FEATURES_3 0x06
+#define ETNAVIV_PARAM_GPU_FEATURES_4 0x07
+#define ETNAVIV_PARAM_GPU_FEATURES_5 0x08
+#define ETNAVIV_PARAM_GPU_FEATURES_6 0x09
+
+#define ETNAVIV_PARAM_GPU_STREAM_COUNT 0x10
+#define ETNAVIV_PARAM_GPU_REGISTER_MAX 0x11
+#define ETNAVIV_PARAM_GPU_THREAD_COUNT 0x12
+#define ETNAVIV_PARAM_GPU_VERTEX_CACHE_SIZE 0x13
+#define ETNAVIV_PARAM_GPU_SHADER_CORE_COUNT 0x14
+#define ETNAVIV_PARAM_GPU_PIXEL_PIPES 0x15
+#define ETNAVIV_PARAM_GPU_VERTEX_OUTPUT_BUFFER_SIZE 0x16
+#define ETNAVIV_PARAM_GPU_BUFFER_SIZE 0x17
+#define ETNAVIV_PARAM_GPU_INSTRUCTION_COUNT 0x18
+#define ETNAVIV_PARAM_GPU_NUM_CONSTANTS 0x19
+#define ETNAVIV_PARAM_GPU_NUM_VARYINGS 0x1a
+
+#define ETNA_MAX_PIPES 4
+
+struct drm_etnaviv_param {
+ __u32 pipe; /* in */
+ __u32 param; /* in, ETNAVIV_PARAM_x */
+ __u64 value; /* out (get_param) or in (set_param) */
+};
+
+/*
+ * GEM buffers:
+ */
+
+#define ETNA_BO_CACHE_MASK 0x000f0000
+/* cache modes */
+#define ETNA_BO_CACHED 0x00010000
+#define ETNA_BO_WC 0x00020000
+#define ETNA_BO_UNCACHED 0x00040000
+/* map flags */
+#define ETNA_BO_FORCE_MMU 0x00100000
+
+struct drm_etnaviv_gem_new {
+ __u64 size; /* in */
+ __u32 flags; /* in, mask of ETNA_BO_x */
+ __u32 handle; /* out */
+};
+
+struct drm_etnaviv_gem_info {
+ __u32 handle; /* in */
+ __u32 pad;
+ __u64 offset; /* out, offset to pass to mmap() */
+};
+
+#define ETNA_PREP_READ 0x01
+#define ETNA_PREP_WRITE 0x02
+#define ETNA_PREP_NOSYNC 0x04
+
+struct drm_etnaviv_gem_cpu_prep {
+ __u32 handle; /* in */
+ __u32 op; /* in, mask of ETNA_PREP_x */
+ struct drm_etnaviv_timespec timeout; /* in */
+};
+
+struct drm_etnaviv_gem_cpu_fini {
+ __u32 handle; /* in */
+ __u32 flags; /* in, placeholder for now, no defined values */
+};
+
+/*
+ * Cmdstream Submission:
+ */
+
+/* The value written into the cmdstream is logically:
+ * relocbuf->gpuaddr + reloc_offset
+ *
+ * NOTE that reloc's must be sorted by order of increasing submit_offset,
+ * otherwise EINVAL.
+ */
+struct drm_etnaviv_gem_submit_reloc {
+ __u32 submit_offset; /* in, offset from submit_bo */
+ __u32 reloc_idx; /* in, index of reloc_bo buffer */
+ __u64 reloc_offset; /* in, offset from start of reloc_bo */
+ __u32 flags; /* in, placeholder for now, no defined values */
+};
+
+/* Each buffer referenced elsewhere in the cmdstream submit (ie. the
+ * cmdstream buffer(s) themselves or reloc entries) has one (and only
+ * one) entry in the submit->bos[] table.
+ *
+ * As a optimization, the current buffer (gpu virtual address) can be
+ * passed back through the 'presumed' field. If on a subsequent reloc,
+ * userspace passes back a 'presumed' address that is still valid,
+ * then patching the cmdstream for this entry is skipped. This can
+ * avoid kernel needing to map/access the cmdstream bo in the common
+ * case.
+ */
+#define ETNA_SUBMIT_BO_READ 0x0001
+#define ETNA_SUBMIT_BO_WRITE 0x0002
+struct drm_etnaviv_gem_submit_bo {
+ __u32 flags; /* in, mask of ETNA_SUBMIT_BO_x */
+ __u32 handle; /* in, GEM handle */
+ __u64 presumed; /* in/out, presumed buffer address */
+};
+
+/* Each cmdstream submit consists of a table of buffers involved, and
+ * one or more cmdstream buffers. This allows for conditional execution
+ * (context-restore), and IB buffers needed for per tile/bin draw cmds.
+ */
+#define ETNA_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)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ETNAVIV_DRM_H__ */
diff --git a/etnaviv/etnaviv_drmif.h b/etnaviv/etnaviv_drmif.h
new file mode 100644
index 00000000..979b16a9
--- /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@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 00000000..35dec6cd
--- /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@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 00000000..402b71d6
--- /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@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 00000000..6bb0c8dc
--- /dev/null
+++ b/etnaviv/etnaviv_priv.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2014-2015 Etnaviv Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#ifndef ETNAVIV_PRIV_H_
+#define ETNAVIV_PRIV_H_
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "libdrm_macros.h"
+#include "xf86drm.h"
+#include "xf86atomic.h"
+
+#include "util_double_list.h"
+
+#include "etnaviv_drmif.h"
+#include "etnaviv_drm.h"
+
+#define VIV_FEATURES_WORD_COUNT 7
+
+struct etna_specs {
+ uint32_t model;
+ uint32_t revision;
+ uint32_t features[VIV_FEATURES_WORD_COUNT];
+ 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 00000000..13fed01d
--- /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