diff mbox

[1/2] libdrm: add etnaviv drm support

Message ID 1472541291-8770-2-git-send-email-christian.gmeiner@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Christian Gmeiner Aug. 30, 2016, 7:14 a.m. UTC
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@gmail.com>
Signed-off-by: Lucas Stach <l.stach@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

Comments

Emil Velikov Aug. 30, 2016, 1:03 p.m. UTC | #1
On 30 August 2016 at 08:14, Christian Gmeiner
<christian.gmeiner@gmail.com> wrote:
> 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@gmail.com>
> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
Just double-checking:
 - you've looked that all the relevant freedreno patches have been
ported over, correct ?
 - the feature checking bug (mentioned on IRC) has been fixed ?

> diff --git a/configure.ac b/configure.ac
> index e3048c7..64f3e6c 100644
> --- a/configure.ac
> +++ b/configure.ac

> @@ -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)
Reading this hunk reminds me what a bad name I've used. Then again
nothing better comes up atm. If you can think of any please shout.

> +++ b/etnaviv/Android.mk
Have you tried building/using etna on Android ?


> +++ b/etnaviv/Makefile.am
> @@ -0,0 +1,27 @@
> +AUTOMAKE_OPTIONS=subdir-objects
There are no subdirectories so you don't need this. Then again, can we
move the freedreno one to configure.ac - just append to
AM_INIT_AUTOMAKE.

> +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
Maybe make this 1:0:0 ?

> --- /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
> +
LIBDRM_ETNAVIV_H_FILES?


> new file mode 100644
> index 0000000..462241c
> --- /dev/null
> +++ b/etnaviv/etnaviv_drm.h
Double-checking: this file is identical to the one produced by make
headers_install, correct ?

-Emil
Christian Gmeiner Sept. 1, 2016, 7:08 p.m. UTC | #2
Hi Emil,

thanks a lot for the review.

2016-08-30 15:03 GMT+02:00 Emil Velikov <emil.l.velikov@gmail.com>:
> On 30 August 2016 at 08:14, Christian Gmeiner
> <christian.gmeiner@gmail.com> wrote:
>> 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@gmail.com>
>> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
> Just double-checking:
>  - you've looked that all the relevant freedreno patches have been
> ported over, correct ?
>  - the feature checking bug (mentioned on IRC) has been fixed ?
>
>> diff --git a/configure.ac b/configure.ac
>> index e3048c7..64f3e6c 100644
>> --- a/configure.ac
>> +++ b/configure.ac
>
>> @@ -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)
> Reading this hunk reminds me what a bad name I've used. Then again
> nothing better comes up atm. If you can think of any please shout.
>
>> +++ b/etnaviv/Android.mk
> Have you tried building/using etna on Android ?
>

No.. if it is an easy job I would give it a try. Shall I drop it?

>
>> +++ b/etnaviv/Makefile.am
>> @@ -0,0 +1,27 @@
>> +AUTOMAKE_OPTIONS=subdir-objects
> There are no subdirectories so you don't need this. Then again, can we
> move the freedreno one to configure.ac - just append to
> AM_INIT_AUTOMAKE.
>

Okay

>> +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
> Maybe make this 1:0:0 ?
>

Sure.. why not.

>> --- /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
>> +
> LIBDRM_ETNAVIV_H_FILES?
>

Opps... good catch. Will fix it in v2.

>
>> new file mode 100644
>> index 0000000..462241c
>> --- /dev/null
>> +++ b/etnaviv/etnaviv_drm.h
> Double-checking: this file is identical to the one produced by make
> headers_install, correct ?
>

I need to check that - if something is wrong it will be fixed in v2.

greets
--
Christian Gmeiner, MSc

https://soundcloud.com/christian-gmeiner
Emil Velikov Sept. 1, 2016, 11:21 p.m. UTC | #3
On 1 September 2016 at 20:08, Christian Gmeiner
<christian.gmeiner@gmail.com> wrote:
> Hi Emil,
>
> thanks a lot for the review.
>
> 2016-08-30 15:03 GMT+02:00 Emil Velikov <emil.l.velikov@gmail.com>:
>> On 30 August 2016 at 08:14, Christian Gmeiner
>> <christian.gmeiner@gmail.com> wrote:
>>> 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@gmail.com>
>>> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
>> Just double-checking:
>>  - you've looked that all the relevant freedreno patches have been
>> ported over, correct ?
>>  - the feature checking bug (mentioned on IRC) has been fixed ?
>>
>>> diff --git a/configure.ac b/configure.ac
>>> index e3048c7..64f3e6c 100644
>>> --- a/configure.ac
>>> +++ b/configure.ac
>>
>>> @@ -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)
>> Reading this hunk reminds me what a bad name I've used. Then again
>> nothing better comes up atm. If you can think of any please shout.
>>
>>> +++ b/etnaviv/Android.mk
>> Have you tried building/using etna on Android ?
>>
>
> No.. if it is an easy job I would give it a try. Shall I drop it?
>
Mostly curious. It doesn't get in the way so I'd keep it as-is.

Everything but the "NB use make headers_install for etnaviv_drm.h" is
just polish. So if that's OK you can address any/all of the remainder
as small follow up patches on top. It's up-to you really.

Since you're likely giving mesa side a final polish before sending,
please point out where the latest work can be found in the interim.

Thanks
Emil
Rob Herring (Arm) Sept. 8, 2016, 6:52 p.m. UTC | #4
On Thu, Sep 1, 2016 at 2:08 PM, Christian Gmeiner
<christian.gmeiner@gmail.com> wrote:
> Hi Emil,
>
> thanks a lot for the review.
>
> 2016-08-30 15:03 GMT+02:00 Emil Velikov <emil.l.velikov@gmail.com>:
>> On 30 August 2016 at 08:14, Christian Gmeiner
>> <christian.gmeiner@gmail.com> wrote:
>>> 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@gmail.com>
>>> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
>> Just double-checking:
>>  - you've looked that all the relevant freedreno patches have been
>> ported over, correct ?
>>  - the feature checking bug (mentioned on IRC) has been fixed ?
>>
>>> diff --git a/configure.ac b/configure.ac
>>> index e3048c7..64f3e6c 100644
>>> --- a/configure.ac
>>> +++ b/configure.ac
>>
>>> @@ -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)
>> Reading this hunk reminds me what a bad name I've used. Then again
>> nothing better comes up atm. If you can think of any please shout.
>>
>>> +++ b/etnaviv/Android.mk
>> Have you tried building/using etna on Android ?
>>
>
> No.. if it is an easy job I would give it a try. Shall I drop it?

But I have. libdrm just needs this patch (for master and N):

@@ -9,7 +9,7 @@ LOCAL_MODULE_TAGS := optional

 LOCAL_SHARED_LIBRARIES := libdrm

-LOCAL_SRC_FILES := $(LIBDRM_ETNAVIV_FILES)
+LOCAL_SRC_FILES := $(patsubst %.h, , $(LIBDRM_ETNAVIV_FILES))
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)

 LOCAL_CFLAGS := \


I've got mesa building on Android, too. It's a few patches so far of
Android.mk additions and things that break with clang or post 12.0.
The etnaviv branch also breaks other drivers with the max vertex
buffer capability addition.

Rob
Rob Herring (Arm) Sept. 8, 2016, 7:08 p.m. UTC | #5
On Thu, Sep 8, 2016 at 1:52 PM, Rob Herring <robh@kernel.org> wrote:
> On Thu, Sep 1, 2016 at 2:08 PM, Christian Gmeiner
> <christian.gmeiner@gmail.com> wrote:
>> Hi Emil,
>>
>> thanks a lot for the review.
>>
>> 2016-08-30 15:03 GMT+02:00 Emil Velikov <emil.l.velikov@gmail.com>:
>>> On 30 August 2016 at 08:14, Christian Gmeiner
>>> <christian.gmeiner@gmail.com> wrote:
>>>> 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@gmail.com>
>>>> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
>>> Just double-checking:
>>>  - you've looked that all the relevant freedreno patches have been
>>> ported over, correct ?
>>>  - the feature checking bug (mentioned on IRC) has been fixed ?
>>>
>>>> diff --git a/configure.ac b/configure.ac
>>>> index e3048c7..64f3e6c 100644
>>>> --- a/configure.ac
>>>> +++ b/configure.ac
>>>
>>>> @@ -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)
>>> Reading this hunk reminds me what a bad name I've used. Then again
>>> nothing better comes up atm. If you can think of any please shout.
>>>
>>>> +++ b/etnaviv/Android.mk
>>> Have you tried building/using etna on Android ?
>>>
>>
>> No.. if it is an easy job I would give it a try. Shall I drop it?
>
> But I have. libdrm just needs this patch (for master and N):

NM. I see you already have that change in this patch, so Android build
should be fine. I'll give v2 patch a try.

Rob
Christian Gmeiner Sept. 8, 2016, 9:01 p.m. UTC | #6
Hi Rob,

2016-09-08 20:52 GMT+02:00 Rob Herring <robh@kernel.org>:
> On Thu, Sep 1, 2016 at 2:08 PM, Christian Gmeiner
> <christian.gmeiner@gmail.com> wrote:
>> Hi Emil,
>>
>> thanks a lot for the review.
>>
>> 2016-08-30 15:03 GMT+02:00 Emil Velikov <emil.l.velikov@gmail.com>:
>>> On 30 August 2016 at 08:14, Christian Gmeiner
>>> <christian.gmeiner@gmail.com> wrote:
>>>> 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@gmail.com>
>>>> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
>>> Just double-checking:
>>>  - you've looked that all the relevant freedreno patches have been
>>> ported over, correct ?
>>>  - the feature checking bug (mentioned on IRC) has been fixed ?
>>>
>>>> diff --git a/configure.ac b/configure.ac
>>>> index e3048c7..64f3e6c 100644
>>>> --- a/configure.ac
>>>> +++ b/configure.ac
>>>
>>>> @@ -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)
>>> Reading this hunk reminds me what a bad name I've used. Then again
>>> nothing better comes up atm. If you can think of any please shout.
>>>
>>>> +++ b/etnaviv/Android.mk
>>> Have you tried building/using etna on Android ?
>>>
>>
>> No.. if it is an easy job I would give it a try. Shall I drop it?
>
> But I have. libdrm just needs this patch (for master and N):
>

Great!

> @@ -9,7 +9,7 @@ LOCAL_MODULE_TAGS := optional
>
>  LOCAL_SHARED_LIBRARIES := libdrm
>
> -LOCAL_SRC_FILES := $(LIBDRM_ETNAVIV_FILES)
> +LOCAL_SRC_FILES := $(patsubst %.h, , $(LIBDRM_ETNAVIV_FILES))
>  LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
>
>  LOCAL_CFLAGS := \
>
>
> I've got mesa building on Android, too. It's a few patches so far of
> Android.mk additions and things that break with clang or post 12.0.
> The etnaviv branch also breaks other drivers with the max vertex
> buffer capability addition.
>

Yeah I am aware of that and I am currently working on a fix for that.

thanks
--
Christian Gmeiner, MSc

https://soundcloud.com/christian-gmeiner
diff mbox

Patch

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@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@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@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@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@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@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@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@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