[Mesa-dev] [PATCH v3 5/6] tegra: Initial support

Thierry Reding thierry.reding at gmail.com
Thu Mar 1 13:54:53 UTC 2018


Tegra K1 and later use a GPU that can be driven by the Nouveau driver.
But the GPU is a pure render node and has no display engine, hence the
scanout needs to happen on the Tegra display hardware. The GPU and the
display engine each have a separate DRM device node exposed by the
kernel.

To make the setup appear as a single device, this driver instantiates
a Nouveau screen with each instance of a Tegra screen and forwards GPU
requests to the Nouveau screen. For purposes of scanout it will import
buffers created on the GPU into the display driver. Handles that
userspace requests are those of the display driver so that they can be
used to create framebuffers.

This has been tested with some GBM test programs, as well as kmscube and
weston. All of those run without modifications, but I'm sure there is a
lot that can be improved.

Some fixes contributed by Hector Martin <marcan at marcan.st>.

Changes in v2:
- duplicate file descriptor in winsys to avoid potential issues
- require nouveau when building the tegra driver
- check for nouveau driver name on render node
- remove unneeded dependency on libdrm_tegra
- remove zombie references to libudev
- add missing headers to C_SOURCES variable
- drop unneeded tegra/ prefix for includes
- open device files with O_CLOEXEC
- update copyrights

Changes in v3:
- properly unwrap resources in ->resource_copy_region()
- support vertex buffers passed by user pointer
- allocate custom stream and const uploader
- silence error message on pre-Tegra124
- support X without explicit PRIME

Reviewed-by: Emil Velikov <emil.velikov at collabora.com>
Acked-by: Emil Velikov <emil.velikov at collabora.com>
Tested-by: Andre Heider <a.heider at gmail.com>
Signed-off-by: Thierry Reding <treding at nvidia.com>
---
 configure.ac                                       |   12 +-
 include/drm-uapi/tegra_drm.h                       |  225 ++++
 meson.build                                        |    7 +-
 src/gallium/Makefile.am                            |    5 +
 .../auxiliary/pipe-loader/pipe_loader_drm.c        |    7 +-
 src/gallium/auxiliary/target-helpers/drm_helper.h  |   23 +
 .../auxiliary/target-helpers/drm_helper_public.h   |    3 +
 src/gallium/drivers/tegra/Automake.inc             |   11 +
 src/gallium/drivers/tegra/Makefile.am              |   11 +
 src/gallium/drivers/tegra/Makefile.sources         |    6 +
 src/gallium/drivers/tegra/meson.build              |   41 +
 src/gallium/drivers/tegra/tegra_context.c          | 1325 ++++++++++++++++++++
 src/gallium/drivers/tegra/tegra_context.h          |   81 ++
 src/gallium/drivers/tegra/tegra_resource.h         |   76 ++
 src/gallium/drivers/tegra/tegra_screen.c           |  688 ++++++++++
 src/gallium/drivers/tegra/tegra_screen.h           |   45 +
 src/gallium/meson.build                            |    6 +
 src/gallium/targets/dri/Makefile.am                |    2 +
 src/gallium/targets/dri/meson.build                |    4 +-
 src/gallium/targets/dri/target.c                   |    4 +
 src/gallium/targets/vdpau/Makefile.am              |    2 +
 src/gallium/winsys/tegra/drm/Makefile.am           |   10 +
 src/gallium/winsys/tegra/drm/Makefile.sources      |    2 +
 src/gallium/winsys/tegra/drm/meson.build           |   33 +
 src/gallium/winsys/tegra/drm/tegra_drm_public.h    |   31 +
 src/gallium/winsys/tegra/drm/tegra_drm_winsys.c    |   49 +
 26 files changed, 2705 insertions(+), 4 deletions(-)
 create mode 100644 include/drm-uapi/tegra_drm.h
 create mode 100644 src/gallium/drivers/tegra/Automake.inc
 create mode 100644 src/gallium/drivers/tegra/Makefile.am
 create mode 100644 src/gallium/drivers/tegra/Makefile.sources
 create mode 100644 src/gallium/drivers/tegra/meson.build
 create mode 100644 src/gallium/drivers/tegra/tegra_context.c
 create mode 100644 src/gallium/drivers/tegra/tegra_context.h
 create mode 100644 src/gallium/drivers/tegra/tegra_resource.h
 create mode 100644 src/gallium/drivers/tegra/tegra_screen.c
 create mode 100644 src/gallium/drivers/tegra/tegra_screen.h
 create mode 100644 src/gallium/winsys/tegra/drm/Makefile.am
 create mode 100644 src/gallium/winsys/tegra/drm/Makefile.sources
 create mode 100644 src/gallium/winsys/tegra/drm/meson.build
 create mode 100644 src/gallium/winsys/tegra/drm/tegra_drm_public.h
 create mode 100644 src/gallium/winsys/tegra/drm/tegra_drm_winsys.c

diff --git a/configure.ac b/configure.ac
index d8093597dd04..27528181b73e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1351,7 +1351,7 @@ GALLIUM_DRIVERS_DEFAULT="r300,r600,svga,swrast"
 AC_ARG_WITH([gallium-drivers],
     [AS_HELP_STRING([--with-gallium-drivers@<:@=DIRS...@:>@],
         [comma delimited Gallium drivers list, e.g.
-        "i915,nouveau,r300,r600,radeonsi,freedreno,pl111,svga,swrast,swr,vc4,vc5,virgl,etnaviv,imx"
+        "i915,nouveau,r300,r600,radeonsi,freedreno,pl111,svga,swrast,swr,tegra,vc4,vc5,virgl,etnaviv,imx"
         @<:@default=r300,r600,svga,swrast@:>@])],
     [with_gallium_drivers="$withval"],
     [with_gallium_drivers="$GALLIUM_DRIVERS_DEFAULT"])
@@ -2590,6 +2590,10 @@ if test -n "$with_gallium_drivers"; then
        ximx)
             HAVE_GALLIUM_IMX=yes
             ;;
+        xtegra)
+            HAVE_GALLIUM_TEGRA=yes
+            require_libdrm "tegra"
+            ;;
         xswrast)
             HAVE_GALLIUM_SOFTPIPE=yes
             if test "x$enable_llvm" = xyes; then
@@ -2714,6 +2718,9 @@ if test "x$HAVE_GALLIUM_VC4" != xyes -a "x$HAVE_GALLIUM_PL111" = xyes  ; then
     AC_MSG_ERROR([Building with pl111 requires vc4])
 fi
 
+if test "x$HAVE_GALLIUM_NOUVEAU" != xyes -a "x$HAVE_GALLIUM_TEGRA" = xyes; then
+    AC_MSG_ERROR([Building with tegra requires nouveau])
+fi
 
 detect_old_buggy_llvm() {
     dnl llvm-config may not give the right answer when llvm is a built as a
@@ -2813,6 +2820,7 @@ AM_CONDITIONAL(HAVE_GALLIUM_NOUVEAU, test "x$HAVE_GALLIUM_NOUVEAU" = xyes)
 AM_CONDITIONAL(HAVE_GALLIUM_FREEDRENO, test "x$HAVE_GALLIUM_FREEDRENO" = xyes)
 AM_CONDITIONAL(HAVE_GALLIUM_ETNAVIV, test "x$HAVE_GALLIUM_ETNAVIV" = xyes)
 AM_CONDITIONAL(HAVE_GALLIUM_IMX, test "x$HAVE_GALLIUM_IMX" = xyes)
+AM_CONDITIONAL(HAVE_GALLIUM_TEGRA, test "x$HAVE_GALLIUM_TEGRA" = xyes)
 AM_CONDITIONAL(HAVE_GALLIUM_SOFTPIPE, test "x$HAVE_GALLIUM_SOFTPIPE" = xyes)
 AM_CONDITIONAL(HAVE_GALLIUM_LLVMPIPE, test "x$HAVE_GALLIUM_LLVMPIPE" = xyes)
 AM_CONDITIONAL(HAVE_GALLIUM_SWR, test "x$HAVE_GALLIUM_SWR" = xyes)
@@ -2965,6 +2973,7 @@ AC_CONFIG_FILES([Makefile
                  src/gallium/drivers/softpipe/Makefile
                  src/gallium/drivers/svga/Makefile
                  src/gallium/drivers/swr/Makefile
+                 src/gallium/drivers/tegra/Makefile
                  src/gallium/drivers/trace/Makefile
                  src/gallium/drivers/etnaviv/Makefile
                  src/gallium/drivers/imx/Makefile
@@ -3013,6 +3022,7 @@ AC_CONFIG_FILES([Makefile
                  src/gallium/winsys/sw/null/Makefile
                  src/gallium/winsys/sw/wrapper/Makefile
                  src/gallium/winsys/sw/xlib/Makefile
+                 src/gallium/winsys/tegra/drm/Makefile
                  src/gallium/winsys/vc4/drm/Makefile
                  src/gallium/winsys/vc5/drm/Makefile
                  src/gallium/winsys/virgl/drm/Makefile
diff --git a/include/drm-uapi/tegra_drm.h b/include/drm-uapi/tegra_drm.h
new file mode 100644
index 000000000000..910cbe02d873
--- /dev/null
+++ b/include/drm-uapi/tegra_drm.h
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2012-2013, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _TEGRA_DRM_H_
+#define _TEGRA_DRM_H_
+
+#include "drm.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define DRM_TEGRA_GEM_CREATE_TILED     (1 << 0)
+#define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1)
+
+struct drm_tegra_gem_create {
+	__u64 size;
+	__u32 flags;
+	__u32 handle;
+};
+
+struct drm_tegra_gem_mmap {
+	__u32 handle;
+	__u32 pad;
+	__u64 offset;
+};
+
+struct drm_tegra_syncpt_read {
+	__u32 id;
+	__u32 value;
+};
+
+struct drm_tegra_syncpt_incr {
+	__u32 id;
+	__u32 pad;
+};
+
+struct drm_tegra_syncpt_wait {
+	__u32 id;
+	__u32 thresh;
+	__u32 timeout;
+	__u32 value;
+};
+
+#define DRM_TEGRA_NO_TIMEOUT	(0xffffffff)
+
+struct drm_tegra_open_channel {
+	__u32 client;
+	__u32 pad;
+	__u64 context;
+};
+
+struct drm_tegra_close_channel {
+	__u64 context;
+};
+
+struct drm_tegra_get_syncpt {
+	__u64 context;
+	__u32 index;
+	__u32 id;
+};
+
+struct drm_tegra_get_syncpt_base {
+	__u64 context;
+	__u32 syncpt;
+	__u32 id;
+};
+
+struct drm_tegra_syncpt {
+	__u32 id;
+	__u32 incrs;
+};
+
+struct drm_tegra_cmdbuf {
+	__u32 handle;
+	__u32 offset;
+	__u32 words;
+	__u32 pad;
+};
+
+struct drm_tegra_reloc {
+	struct {
+		__u32 handle;
+		__u32 offset;
+	} cmdbuf;
+	struct {
+		__u32 handle;
+		__u32 offset;
+	} target;
+	__u32 shift;
+	__u32 pad;
+};
+
+struct drm_tegra_waitchk {
+	__u32 handle;
+	__u32 offset;
+	__u32 syncpt;
+	__u32 thresh;
+};
+
+#define DRM_TEGRA_FENCE_WAIT	(1 << 0)
+#define DRM_TEGRA_FENCE_EMIT	(1 << 1)
+#define DRM_TEGRA_FENCE_FD	(1 << 2)
+#define DRM_TEGRA_FENCE_FLAGS	(DRM_TEGRA_FENCE_WAIT | \
+				 DRM_TEGRA_FENCE_EMIT | \
+				 DRM_TEGRA_FENCE_FD)
+
+struct drm_tegra_fence {
+	__u32 handle;
+	__u32 flags;
+};
+
+#define DRM_TEGRA_SUBMIT_FLAGS	0
+
+struct drm_tegra_submit {
+	__u64 context;
+	__u32 num_syncpts;
+	__u32 num_cmdbufs;
+	__u32 num_relocs;
+	__u32 num_waitchks;
+	__u32 waitchk_mask;
+	__u32 timeout;
+	__u64 syncpts;
+	__u64 cmdbufs;
+	__u64 relocs;
+	__u64 waitchks;
+	__u32 flags;
+	__u32 num_fences;
+	__u64 fences;
+
+	__u32 reserved[2];	/* future expansion */
+};
+
+#define DRM_TEGRA_GEM_TILING_MODE_PITCH 0
+#define DRM_TEGRA_GEM_TILING_MODE_TILED 1
+#define DRM_TEGRA_GEM_TILING_MODE_BLOCK 2
+
+struct drm_tegra_gem_set_tiling {
+	/* input */
+	__u32 handle;
+	__u32 mode;
+	__u32 value;
+	__u32 pad;
+};
+
+struct drm_tegra_gem_get_tiling {
+	/* input */
+	__u32 handle;
+	/* output */
+	__u32 mode;
+	__u32 value;
+	__u32 pad;
+};
+
+#define DRM_TEGRA_GEM_BOTTOM_UP		(1 << 0)
+#define DRM_TEGRA_GEM_FLAGS		(DRM_TEGRA_GEM_BOTTOM_UP)
+
+struct drm_tegra_gem_set_flags {
+	/* input */
+	__u32 handle;
+	/* output */
+	__u32 flags;
+};
+
+struct drm_tegra_gem_get_flags {
+	/* input */
+	__u32 handle;
+	/* output */
+	__u32 flags;
+};
+
+#define DRM_TEGRA_GEM_CREATE		0x00
+#define DRM_TEGRA_GEM_MMAP		0x01
+#define DRM_TEGRA_SYNCPT_READ		0x02
+#define DRM_TEGRA_SYNCPT_INCR		0x03
+#define DRM_TEGRA_SYNCPT_WAIT		0x04
+#define DRM_TEGRA_OPEN_CHANNEL		0x05
+#define DRM_TEGRA_CLOSE_CHANNEL		0x06
+#define DRM_TEGRA_GET_SYNCPT		0x07
+#define DRM_TEGRA_SUBMIT		0x08
+#define DRM_TEGRA_GET_SYNCPT_BASE	0x09
+#define DRM_TEGRA_GEM_SET_TILING	0x0a
+#define DRM_TEGRA_GEM_GET_TILING	0x0b
+#define DRM_TEGRA_GEM_SET_FLAGS		0x0c
+#define DRM_TEGRA_GEM_GET_FLAGS		0x0d
+
+#define DRM_IOCTL_TEGRA_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_CREATE, struct drm_tegra_gem_create)
+#define DRM_IOCTL_TEGRA_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_MMAP, struct drm_tegra_gem_mmap)
+#define DRM_IOCTL_TEGRA_SYNCPT_READ DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_READ, struct drm_tegra_syncpt_read)
+#define DRM_IOCTL_TEGRA_SYNCPT_INCR DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_INCR, struct drm_tegra_syncpt_incr)
+#define DRM_IOCTL_TEGRA_SYNCPT_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_WAIT, struct drm_tegra_syncpt_wait)
+#define DRM_IOCTL_TEGRA_OPEN_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_OPEN_CHANNEL, struct drm_tegra_open_channel)
+#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_open_channel)
+#define DRM_IOCTL_TEGRA_GET_SYNCPT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT, struct drm_tegra_get_syncpt)
+#define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit)
+#define DRM_IOCTL_TEGRA_GET_SYNCPT_BASE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT_BASE, struct drm_tegra_get_syncpt_base)
+#define DRM_IOCTL_TEGRA_GEM_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_SET_TILING, struct drm_tegra_gem_set_tiling)
+#define DRM_IOCTL_TEGRA_GEM_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_TILING, struct drm_tegra_gem_get_tiling)
+#define DRM_IOCTL_TEGRA_GEM_SET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_SET_FLAGS, struct drm_tegra_gem_set_flags)
+#define DRM_IOCTL_TEGRA_GEM_GET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_FLAGS, struct drm_tegra_gem_get_flags)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _TEGRA_DRM_H_ */
diff --git a/meson.build b/meson.build
index e9928a379313..83e06c1a4a39 100644
--- a/meson.build
+++ b/meson.build
@@ -133,6 +133,7 @@ with_gallium_vc4 = false
 with_gallium_vc5 = false
 with_gallium_etnaviv = false
 with_gallium_imx = false
+with_gallium_tegra = false
 with_gallium_i915 = false
 with_gallium_svga = false
 with_gallium_virgl = false
@@ -144,7 +145,7 @@ if _drivers == 'auto'
     if ['x86', 'x86_64'].contains(host_machine.cpu_family())
       _drivers = 'r300,r600,radeonsi,nouveau,virgl,svga,swrast'
     elif ['arm', 'aarch64'].contains(host_machine.cpu_family())
-      _drivers = 'pl111,vc4,vc5,freedreno,etnaviv,imx,virgl,svga,swrast'
+      _drivers = 'pl111,vc4,vc5,freedreno,etnaviv,imx,nouveau,tegra,virgl,svga,swrast'
     else
       error('Unknown architecture. Please pass -Dgallium-drivers to set driver options. Patches gladly accepted to fix this.')
     endif
@@ -167,6 +168,7 @@ if _drivers != ''
   with_gallium_vc5 = _split.contains('vc5')
   with_gallium_etnaviv = _split.contains('etnaviv')
   with_gallium_imx = _split.contains('imx')
+  with_gallium_tegra = _split.contains('tegra')
   with_gallium_i915 = _split.contains('i915')
   with_gallium_svga = _split.contains('svga')
   with_gallium_virgl = _split.contains('virgl')
@@ -211,6 +213,9 @@ endif
 if with_gallium_pl111 and not with_gallium_vc4
   error('pl111 driver requires vc4 driver')
 endif
+if with_gallium_tegra and not with_gallium_nouveau
+  error('tegra driver requires nouveau driver')
+endif
 
 dep_libdrm_intel = []
 if with_dri_i915 or with_gallium_i915
diff --git a/src/gallium/Makefile.am b/src/gallium/Makefile.am
index af010c89f8d9..cd1447f9e12b 100644
--- a/src/gallium/Makefile.am
+++ b/src/gallium/Makefile.am
@@ -89,6 +89,11 @@ if HAVE_GALLIUM_SWR
 SUBDIRS += drivers/swr
 endif
 
+## tegra
+if HAVE_GALLIUM_TEGRA
+SUBDIRS += drivers/tegra winsys/tegra/drm
+endif
+
 ## vc4/rpi
 if HAVE_GALLIUM_VC4
 SUBDIRS += drivers/vc4 winsys/vc4/drm
diff --git a/src/gallium/auxiliary/pipe-loader/pipe_loader_drm.c b/src/gallium/auxiliary/pipe-loader/pipe_loader_drm.c
index 810542101ede..b6be1b4f1270 100644
--- a/src/gallium/auxiliary/pipe-loader/pipe_loader_drm.c
+++ b/src/gallium/auxiliary/pipe-loader/pipe_loader_drm.c
@@ -134,7 +134,12 @@ static const struct drm_driver_descriptor driver_descriptors[] = {
         .driver_name = "imx-drm",
         .create_screen = pipe_imx_drm_create_screen,
         .configuration = pipe_default_configuration_query,
-    }
+    },
+    {
+        .driver_name = "tegra",
+        .create_screen = pipe_tegra_create_screen,
+        .configuration = pipe_default_configuration_query,
+    },
 };
 #endif
 
diff --git a/src/gallium/auxiliary/target-helpers/drm_helper.h b/src/gallium/auxiliary/target-helpers/drm_helper.h
index 7aea83b8842c..7dc2497c0879 100644
--- a/src/gallium/auxiliary/target-helpers/drm_helper.h
+++ b/src/gallium/auxiliary/target-helpers/drm_helper.h
@@ -379,5 +379,28 @@ pipe_imx_drm_create_screen(int fd, const struct pipe_screen_config *config)
 
 #endif
 
+#ifdef GALLIUM_TEGRA
+#include "tegra/drm/tegra_drm_public.h"
+
+struct pipe_screen *
+pipe_tegra_create_screen(int fd, const struct pipe_screen_config *config)
+{
+   struct pipe_screen *screen;
+
+   screen = tegra_drm_screen_create(fd);
+
+   return screen ? debug_screen_wrap(screen) : NULL;
+}
+
+#else
+
+struct pipe_screen *
+pipe_tegra_create_screen(int fd, const struct pipe_screen_config *config)
+{
+   fprintf(stderr, "tegra: driver missing\n");
+   return NULL;
+}
+
+#endif
 
 #endif /* DRM_HELPER_H */
diff --git a/src/gallium/auxiliary/target-helpers/drm_helper_public.h b/src/gallium/auxiliary/target-helpers/drm_helper_public.h
index e21ea32fabe0..c1a7bf4e2520 100644
--- a/src/gallium/auxiliary/target-helpers/drm_helper_public.h
+++ b/src/gallium/auxiliary/target-helpers/drm_helper_public.h
@@ -51,6 +51,9 @@ pipe_etna_create_screen(int fd, const struct pipe_screen_config *config);
 struct pipe_screen *
 pipe_imx_drm_create_screen(int fd, const struct pipe_screen_config *config);
 
+struct pipe_screen *
+pipe_tegra_create_screen(int fd, const struct pipe_screen_config *config);
+
 const struct drm_conf_ret *
 pipe_default_configuration_query(enum drm_conf conf);
 
diff --git a/src/gallium/drivers/tegra/Automake.inc b/src/gallium/drivers/tegra/Automake.inc
new file mode 100644
index 000000000000..f65281916245
--- /dev/null
+++ b/src/gallium/drivers/tegra/Automake.inc
@@ -0,0 +1,11 @@
+if HAVE_GALLIUM_TEGRA
+
+TARGET_DRIVERS += tegra
+TARGET_CPPFLAGS += -DGALLIUM_TEGRA
+TARGET_LIB_DEPS += \
+	$(top_builddir)/src/gallium/winsys/tegra/drm/libtegradrm.la \
+	$(top_builddir)/src/gallium/drivers/tegra/libtegra.la \
+	$(LIBDRM_LIBS) \
+	$(TEGRA_LIBS)
+
+endif
diff --git a/src/gallium/drivers/tegra/Makefile.am b/src/gallium/drivers/tegra/Makefile.am
new file mode 100644
index 000000000000..7e87ea048733
--- /dev/null
+++ b/src/gallium/drivers/tegra/Makefile.am
@@ -0,0 +1,11 @@
+include Makefile.sources
+include $(top_srcdir)/src/gallium/Automake.inc
+
+AM_CFLAGS = \
+	-I$(top_srcdir)/include/drm-uapi \
+	$(GALLIUM_DRIVER_CFLAGS)
+
+noinst_LTLIBRARIES = libtegra.la
+
+libtegra_la_SOURCES = \
+	$(C_SOURCES)
diff --git a/src/gallium/drivers/tegra/Makefile.sources b/src/gallium/drivers/tegra/Makefile.sources
new file mode 100644
index 000000000000..af4ff838c7ca
--- /dev/null
+++ b/src/gallium/drivers/tegra/Makefile.sources
@@ -0,0 +1,6 @@
+C_SOURCES := \
+	tegra_context.c \
+	tegra_context.h \
+	tegra_resource.h \
+	tegra_screen.c \
+	tegra_screen.h
diff --git a/src/gallium/drivers/tegra/meson.build b/src/gallium/drivers/tegra/meson.build
new file mode 100644
index 000000000000..79ccd143f292
--- /dev/null
+++ b/src/gallium/drivers/tegra/meson.build
@@ -0,0 +1,41 @@
+# Copyright © 2018 NVIDIA CORPORATION
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice 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.
+
+files_tegra = files(
+  'tegra_context.c',
+  'tegra_context.h',
+  'tegra_resource.h',
+  'tegra_screen.c',
+)
+
+libtegra = static_library(
+  'tegra',
+  files_tegra,
+  c_args : [c_vis_args],
+  include_directories : [
+    inc_include, inc_src, inc_gallium, inc_gallium_aux, inc_gallium_drivers,
+    inc_gallium_winsys, inc_drm_uapi
+  ],
+)
+
+driver_tegra = declare_dependency(
+  compile_args : '-DGALLIUM_TEGRA',
+  link_with : [libtegra, libtegradrm],
+)
diff --git a/src/gallium/drivers/tegra/tegra_context.c b/src/gallium/drivers/tegra/tegra_context.c
new file mode 100644
index 000000000000..34ffa4cf06c5
--- /dev/null
+++ b/src/gallium/drivers/tegra/tegra_context.c
@@ -0,0 +1,1325 @@
+/*
+ * Copyright © 2014-2018 NVIDIA Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include "util/u_debug.h"
+#include "util/u_inlines.h"
+#include "util/u_upload_mgr.h"
+
+#include "tegra_context.h"
+#include "tegra_resource.h"
+#include "tegra_screen.h"
+
+static void
+tegra_destroy(struct pipe_context *pcontext)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   if (context->base.stream_uploader)
+      u_upload_destroy(context->base.stream_uploader);
+
+   context->gpu->destroy(context->gpu);
+   free(context);
+}
+
+static void
+tegra_draw_vbo(struct pipe_context *pcontext,
+               const struct pipe_draw_info *pinfo)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct pipe_draw_indirect_info indirect;
+   struct pipe_draw_info info;
+
+   if (pinfo && (pinfo->indirect || pinfo->index_size)) {
+      memcpy(&info, pinfo, sizeof(info));
+
+      if (pinfo->indirect) {
+         memcpy(&indirect, pinfo->indirect, sizeof(indirect));
+         indirect.buffer = tegra_resource_unwrap(info.indirect->buffer);
+         info.indirect = &indirect;
+      }
+
+      if (pinfo->index_size && !pinfo->has_user_indices)
+         info.index.resource = tegra_resource_unwrap(info.index.resource);
+
+      pinfo = &info;
+   }
+
+   context->gpu->draw_vbo(context->gpu, pinfo);
+}
+
+static void
+tegra_render_condition(struct pipe_context *pcontext,
+                       struct pipe_query *query,
+                       boolean condition,
+                       unsigned int mode)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->render_condition(context->gpu, query, condition, mode);
+}
+
+static struct pipe_query *
+tegra_create_query(struct pipe_context *pcontext, unsigned int query_type,
+                   unsigned int index)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_query(context->gpu, query_type, index);
+}
+
+static struct pipe_query *
+tegra_create_batch_query(struct pipe_context *pcontext,
+                         unsigned int num_queries,
+                         unsigned int *queries)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_batch_query(context->gpu, num_queries,
+                                           queries);
+}
+
+static void
+tegra_destroy_query(struct pipe_context *pcontext, struct pipe_query *query)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->destroy_query(context->gpu, query);
+}
+
+static boolean
+tegra_begin_query(struct pipe_context *pcontext, struct pipe_query *query)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->begin_query(context->gpu, query);
+}
+
+static bool
+tegra_end_query(struct pipe_context *pcontext, struct pipe_query *query)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->end_query(context->gpu, query);
+}
+
+static boolean
+tegra_get_query_result(struct pipe_context *pcontext,
+                       struct pipe_query *query,
+                       boolean wait,
+                       union pipe_query_result *result)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->get_query_result(context->gpu, query, wait,
+                     result);
+}
+
+static void
+tegra_get_query_result_resource(struct pipe_context *pcontext,
+                                struct pipe_query *query,
+                                boolean wait,
+                                enum pipe_query_value_type result_type,
+                                int index,
+                                struct pipe_resource *resource,
+                                unsigned int offset)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->get_query_result_resource(context->gpu, query, wait,
+                                           result_type, index, resource,
+                                           offset);
+}
+
+static void
+tegra_set_active_query_state(struct pipe_context *pcontext, boolean enable)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_active_query_state(context->gpu, enable);
+}
+
+static void *
+tegra_create_blend_state(struct pipe_context *pcontext,
+                         const struct pipe_blend_state *cso)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_blend_state(context->gpu, cso);
+}
+
+static void
+tegra_bind_blend_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->bind_blend_state(context->gpu, so);
+}
+
+static void
+tegra_delete_blend_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->delete_blend_state(context->gpu, so);
+}
+
+static void *
+tegra_create_sampler_state(struct pipe_context *pcontext,
+                           const struct pipe_sampler_state *cso)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_sampler_state(context->gpu, cso);
+}
+
+static void
+tegra_bind_sampler_states(struct pipe_context *pcontext, unsigned shader,
+                          unsigned start_slot, unsigned num_samplers,
+                          void **samplers)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->bind_sampler_states(context->gpu, shader, start_slot,
+                                     num_samplers, samplers);
+}
+
+static void
+tegra_delete_sampler_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->delete_sampler_state(context->gpu, so);
+}
+
+static void *
+tegra_create_rasterizer_state(struct pipe_context *pcontext,
+                              const struct pipe_rasterizer_state *cso)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_rasterizer_state(context->gpu, cso);
+}
+
+static void
+tegra_bind_rasterizer_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->bind_rasterizer_state(context->gpu, so);
+}
+
+static void
+tegra_delete_rasterizer_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->delete_rasterizer_state(context->gpu, so);
+}
+
+static void *
+tegra_create_depth_stencil_alpha_state(struct pipe_context *pcontext,
+                                       const struct pipe_depth_stencil_alpha_state *cso)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_depth_stencil_alpha_state(context->gpu, cso);
+}
+
+static void
+tegra_bind_depth_stencil_alpha_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->bind_depth_stencil_alpha_state(context->gpu, so);
+}
+
+static void
+tegra_delete_depth_stencil_alpha_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->delete_depth_stencil_alpha_state(context->gpu, so);
+}
+
+static void *
+tegra_create_fs_state(struct pipe_context *pcontext,
+                      const struct pipe_shader_state *cso)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_fs_state(context->gpu, cso);
+}
+
+static void
+tegra_bind_fs_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->bind_fs_state(context->gpu, so);
+}
+
+static void
+tegra_delete_fs_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->delete_fs_state(context->gpu, so);
+}
+
+static void *
+tegra_create_vs_state(struct pipe_context *pcontext,
+                      const struct pipe_shader_state *cso)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_vs_state(context->gpu, cso);
+}
+
+static void
+tegra_bind_vs_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->bind_vs_state(context->gpu, so);
+}
+
+static void
+tegra_delete_vs_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->delete_vs_state(context->gpu, so);
+}
+
+static void *
+tegra_create_gs_state(struct pipe_context *pcontext,
+                      const struct pipe_shader_state *cso)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_gs_state(context->gpu, cso);
+}
+
+static void
+tegra_bind_gs_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->bind_gs_state(context->gpu, so);
+}
+
+static void
+tegra_delete_gs_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->delete_gs_state(context->gpu, so);
+}
+
+static void *
+tegra_create_tcs_state(struct pipe_context *pcontext,
+                       const struct pipe_shader_state *cso)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_tcs_state(context->gpu, cso);
+}
+
+static void
+tegra_bind_tcs_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->bind_tcs_state(context->gpu, so);
+}
+
+static void
+tegra_delete_tcs_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->delete_tcs_state(context->gpu, so);
+}
+
+static void *
+tegra_create_tes_state(struct pipe_context *pcontext,
+                       const struct pipe_shader_state *cso)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_tes_state(context->gpu, cso);
+}
+
+static void
+tegra_bind_tes_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->bind_tes_state(context->gpu, so);
+}
+
+static void
+tegra_delete_tes_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->delete_tes_state(context->gpu, so);
+}
+
+static void *
+tegra_create_vertex_elements_state(struct pipe_context *pcontext,
+                                   unsigned num_elements,
+                                   const struct pipe_vertex_element *elements)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_vertex_elements_state(context->gpu,
+                                                     num_elements,
+                                                     elements);
+}
+
+static void
+tegra_bind_vertex_elements_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->bind_vertex_elements_state(context->gpu, so);
+}
+
+static void
+tegra_delete_vertex_elements_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->delete_vertex_elements_state(context->gpu, so);
+}
+
+static void
+tegra_set_blend_color(struct pipe_context *pcontext,
+                      const struct pipe_blend_color *color)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_blend_color(context->gpu, color);
+}
+
+static void
+tegra_set_stencil_ref(struct pipe_context *pcontext,
+                      const struct pipe_stencil_ref *ref)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_stencil_ref(context->gpu, ref);
+}
+
+static void
+tegra_set_sample_mask(struct pipe_context *pcontext, unsigned int mask)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_sample_mask(context->gpu, mask);
+}
+
+static void
+tegra_set_min_samples(struct pipe_context *pcontext, unsigned int samples)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_min_samples(context->gpu, samples);
+}
+
+static void
+tegra_set_clip_state(struct pipe_context *pcontext,
+                     const struct pipe_clip_state *state)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_clip_state(context->gpu, state);
+}
+
+static void
+tegra_set_constant_buffer(struct pipe_context *pcontext, unsigned int shader,
+                          unsigned int index,
+                          const struct pipe_constant_buffer *buf)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct pipe_constant_buffer buffer;
+
+   if (buf && buf->buffer) {
+      memcpy(&buffer, buf, sizeof(buffer));
+      buffer.buffer = tegra_resource_unwrap(buffer.buffer);
+      buf = &buffer;
+   }
+
+   context->gpu->set_constant_buffer(context->gpu, shader, index, buf);
+}
+
+static void
+tegra_set_framebuffer_state(struct pipe_context *pcontext,
+                            const struct pipe_framebuffer_state *fb)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct pipe_framebuffer_state state;
+   unsigned i;
+
+   if (fb) {
+      memcpy(&state, fb, sizeof(state));
+
+      for (i = 0; i < fb->nr_cbufs; i++)
+         state.cbufs[i] = tegra_surface_unwrap(fb->cbufs[i]);
+
+      while (i < PIPE_MAX_COLOR_BUFS)
+         state.cbufs[i++] = NULL;
+
+      state.zsbuf = tegra_surface_unwrap(fb->zsbuf);
+
+      fb = &state;
+   }
+
+   context->gpu->set_framebuffer_state(context->gpu, fb);
+}
+
+static void
+tegra_set_polygon_stipple(struct pipe_context *pcontext,
+                          const struct pipe_poly_stipple *stipple)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_polygon_stipple(context->gpu, stipple);
+}
+
+static void
+tegra_set_scissor_states(struct pipe_context *pcontext, unsigned start_slot,
+                         unsigned num_scissors,
+                         const struct pipe_scissor_state *scissors)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_scissor_states(context->gpu, start_slot, num_scissors,
+                                    scissors);
+}
+
+static void
+tegra_set_window_rectangles(struct pipe_context *pcontext, boolean include,
+                            unsigned int num_rectangles,
+                            const struct pipe_scissor_state *rectangles)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_window_rectangles(context->gpu, include, num_rectangles,
+                                       rectangles);
+}
+
+static void
+tegra_set_viewport_states(struct pipe_context *pcontext, unsigned start_slot,
+                          unsigned num_viewports,
+                          const struct pipe_viewport_state *viewports)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_viewport_states(context->gpu, start_slot, num_viewports,
+                                     viewports);
+}
+
+static void
+tegra_set_sampler_views(struct pipe_context *pcontext, unsigned shader,
+                        unsigned start_slot, unsigned num_views,
+                        struct pipe_sampler_view **pviews)
+{
+   struct pipe_sampler_view *views[PIPE_MAX_SHADER_SAMPLER_VIEWS];
+   struct tegra_context *context = to_tegra_context(pcontext);
+   unsigned i;
+
+   for (i = 0; i < num_views; i++)
+      views[i] = tegra_sampler_view_unwrap(pviews[i]);
+
+   context->gpu->set_sampler_views(context->gpu, shader, start_slot,
+                                   num_views, views);
+}
+
+static void
+tegra_set_tess_state(struct pipe_context *pcontext,
+                     const float default_outer_level[4],
+                     const float default_inner_level[2])
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_tess_state(context->gpu, default_outer_level,
+                                default_inner_level);
+}
+
+static void
+tegra_set_debug_callback(struct pipe_context *pcontext,
+                         const struct pipe_debug_callback *callback)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_debug_callback(context->gpu, callback);
+}
+
+static void
+tegra_set_shader_buffers(struct pipe_context *pcontext, unsigned int shader,
+                         unsigned start, unsigned count,
+                         const struct pipe_shader_buffer *buffers)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_shader_buffers(context->gpu, shader, start, count,
+                                    buffers);
+}
+
+static void
+tegra_set_shader_images(struct pipe_context *pcontext, unsigned int shader,
+                        unsigned start, unsigned count,
+                        const struct pipe_image_view *images)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_shader_images(context->gpu, shader, start, count,
+                                   images);
+}
+
+static void
+tegra_set_vertex_buffers(struct pipe_context *pcontext, unsigned start_slot,
+                         unsigned num_buffers,
+                         const struct pipe_vertex_buffer *buffers)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct pipe_vertex_buffer buf[PIPE_MAX_SHADER_INPUTS];
+   unsigned i;
+
+   if (num_buffers && buffers) {
+      memcpy(buf, buffers, num_buffers * sizeof(struct pipe_vertex_buffer));
+
+      for (i = 0; i < num_buffers; i++) {
+         if (!buf[i].is_user_buffer)
+            buf[i].buffer.resource = tegra_resource_unwrap(buf[i].buffer.resource);
+      }
+
+      buffers = buf;
+   }
+
+   context->gpu->set_vertex_buffers(context->gpu, start_slot, num_buffers,
+                                    buffers);
+}
+
+static struct pipe_stream_output_target *
+tegra_create_stream_output_target(struct pipe_context *pcontext,
+                                  struct pipe_resource *presource,
+                                  unsigned buffer_offset,
+                                  unsigned buffer_size)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_stream_output_target(context->gpu,
+                                                    resource->gpu,
+                                                    buffer_offset,
+                                                    buffer_size);
+}
+
+static void
+tegra_stream_output_target_destroy(struct pipe_context *pcontext,
+                                   struct pipe_stream_output_target *target)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->stream_output_target_destroy(context->gpu, target);
+}
+
+static void
+tegra_set_stream_output_targets(struct pipe_context *pcontext,
+                                unsigned num_targets,
+                                struct pipe_stream_output_target **targets,
+                                const unsigned *offsets)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_stream_output_targets(context->gpu, num_targets,
+                                           targets, offsets);
+}
+
+static void
+tegra_resource_copy_region(struct pipe_context *pcontext,
+                           struct pipe_resource *pdst,
+                           unsigned int dst_level,
+                           unsigned int dstx,
+                           unsigned int dsty,
+                           unsigned int dstz,
+                           struct pipe_resource *psrc,
+                           unsigned int src_level,
+                           const struct pipe_box *src_box)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct tegra_resource *dst = to_tegra_resource(pdst);
+   struct tegra_resource *src = to_tegra_resource(psrc);
+
+   context->gpu->resource_copy_region(context->gpu, dst->gpu, dst_level, dstx,
+                                      dsty, dstz, src->gpu, src_level,
+                                      src_box);
+}
+
+static void
+tegra_blit(struct pipe_context *pcontext, const struct pipe_blit_info *pinfo)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct pipe_blit_info info;
+
+   if (pinfo) {
+      memcpy(&info, pinfo, sizeof(info));
+      info.dst.resource = tegra_resource_unwrap(info.dst.resource);
+      info.src.resource = tegra_resource_unwrap(info.src.resource);
+      pinfo = &info;
+   }
+
+   context->gpu->blit(context->gpu, pinfo);
+}
+
+static void
+tegra_clear(struct pipe_context *pcontext, unsigned buffers,
+            const union pipe_color_union *color, double depth,
+            unsigned stencil)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->clear(context->gpu, buffers, color, depth, stencil);
+}
+
+static void
+tegra_clear_render_target(struct pipe_context *pcontext,
+                          struct pipe_surface *pdst,
+                          const union pipe_color_union *color,
+                          unsigned int dstx,
+                          unsigned int dsty,
+                          unsigned int width,
+                          unsigned int height,
+                          bool render_condition)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct tegra_surface *dst = to_tegra_surface(pdst);
+
+   context->gpu->clear_render_target(context->gpu, dst->gpu, color, dstx,
+                                     dsty, width, height, render_condition);
+}
+
+static void
+tegra_clear_depth_stencil(struct pipe_context *pcontext,
+                          struct pipe_surface *pdst,
+                          unsigned int flags,
+                          double depth,
+                          unsigned int stencil,
+                          unsigned int dstx,
+                          unsigned int dsty,
+                          unsigned int width,
+                          unsigned int height,
+                          bool render_condition)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct tegra_surface *dst = to_tegra_surface(pdst);
+
+   context->gpu->clear_depth_stencil(context->gpu, dst->gpu, flags, depth,
+                                     stencil, dstx, dsty, width, height,
+                                     render_condition);
+}
+
+static void
+tegra_clear_texture(struct pipe_context *pcontext,
+                    struct pipe_resource *presource,
+                    unsigned int level,
+                    const struct pipe_box *box,
+                    const void *data)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->clear_texture(context->gpu, resource->gpu, level, box, data);
+}
+
+static void
+tegra_clear_buffer(struct pipe_context *pcontext,
+                   struct pipe_resource *presource,
+                   unsigned int offset,
+                   unsigned int size,
+                   const void *value,
+                   int value_size)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->clear_buffer(context->gpu, resource->gpu, offset, size,
+                              value, value_size);
+}
+
+static void
+tegra_flush(struct pipe_context *pcontext, struct pipe_fence_handle **fence,
+            unsigned flags)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->flush(context->gpu, fence, flags);
+}
+
+static void
+tegra_create_fence_fd(struct pipe_context *pcontext,
+                      struct pipe_fence_handle **fence,
+                      int fd, enum pipe_fd_type type)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   assert(type == PIPE_FD_TYPE_NATIVE_SYNC);
+   context->gpu->create_fence_fd(context->gpu, fence, fd, type);
+}
+
+static void
+tegra_fence_server_sync(struct pipe_context *pcontext,
+                        struct pipe_fence_handle *fence)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->fence_server_sync(context->gpu, fence);
+}
+
+static struct pipe_sampler_view *
+tegra_create_sampler_view(struct pipe_context *pcontext,
+                          struct pipe_resource *presource,
+                          const struct pipe_sampler_view *template)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct tegra_sampler_view *view;
+
+   view = calloc(1, sizeof(*view));
+   if (!view)
+      return NULL;
+
+   view->gpu = context->gpu->create_sampler_view(context->gpu, resource->gpu,
+                                                 template);
+   memcpy(&view->base, view->gpu, sizeof(*view->gpu));
+   /* overwrite to prevent reference from being released */
+   view->base.texture = NULL;
+
+   pipe_reference_init(&view->base.reference, 1);
+   pipe_resource_reference(&view->base.texture, presource);
+   view->base.context = pcontext;
+
+   return &view->base;
+}
+
+static void
+tegra_sampler_view_destroy(struct pipe_context *pcontext,
+                           struct pipe_sampler_view *pview)
+{
+   struct tegra_sampler_view *view = to_tegra_sampler_view(pview);
+
+   pipe_resource_reference(&view->base.texture, NULL);
+   pipe_sampler_view_reference(&view->gpu, NULL);
+   free(view);
+}
+
+static struct pipe_surface *
+tegra_create_surface(struct pipe_context *pcontext,
+                     struct pipe_resource *presource,
+                     const struct pipe_surface *template)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct tegra_surface *surface;
+
+   surface = calloc(1, sizeof(*surface));
+   if (!surface)
+      return NULL;
+
+   surface->gpu = context->gpu->create_surface(context->gpu, resource->gpu,
+                                               template);
+   if (!surface->gpu) {
+      free(surface);
+      return NULL;
+   }
+
+   memcpy(&surface->base, surface->gpu, sizeof(*surface->gpu));
+   /* overwrite to prevent reference from being released */
+   surface->base.texture = NULL;
+
+   pipe_reference_init(&surface->base.reference, 1);
+   pipe_resource_reference(&surface->base.texture, presource);
+   surface->base.context = &context->base;
+
+   return &surface->base;
+}
+
+static void
+tegra_surface_destroy(struct pipe_context *pcontext,
+                      struct pipe_surface *psurface)
+{
+   struct tegra_surface *surface = to_tegra_surface(psurface);
+
+   pipe_resource_reference(&surface->base.texture, NULL);
+   pipe_surface_reference(&surface->gpu, NULL);
+   free(surface);
+}
+
+static void *
+tegra_transfer_map(struct pipe_context *pcontext,
+                   struct pipe_resource *presource,
+                   unsigned level, unsigned usage,
+                   const struct pipe_box *box,
+                   struct pipe_transfer **ptransfer)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct tegra_transfer *transfer;
+
+   transfer = calloc(1, sizeof(*transfer));
+   if (!transfer)
+      return NULL;
+
+   transfer->map = context->gpu->transfer_map(context->gpu, resource->gpu,
+                                              level, usage, box,
+                                              &transfer->gpu);
+   memcpy(&transfer->base, transfer->gpu, sizeof(*transfer->gpu));
+   transfer->base.resource = NULL;
+   pipe_resource_reference(&transfer->base.resource, presource);
+
+   *ptransfer = &transfer->base;
+
+   return transfer->map;
+}
+
+static void
+tegra_transfer_flush_region(struct pipe_context *pcontext,
+                            struct pipe_transfer *ptransfer,
+                            const struct pipe_box *box)
+{
+   struct tegra_transfer *transfer = to_tegra_transfer(ptransfer);
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->transfer_flush_region(context->gpu, transfer->gpu, box);
+}
+
+static void
+tegra_transfer_unmap(struct pipe_context *pcontext,
+                     struct pipe_transfer *ptransfer)
+{
+   struct tegra_transfer *transfer = to_tegra_transfer(ptransfer);
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->transfer_unmap(context->gpu, transfer->gpu);
+   pipe_resource_reference(&transfer->base.resource, NULL);
+   free(transfer);
+}
+
+static void
+tegra_buffer_subdata(struct pipe_context *pcontext,
+                     struct pipe_resource *presource,
+                     unsigned usage, unsigned offset,
+                     unsigned size, const void *data)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->buffer_subdata(context->gpu, resource->gpu, usage, offset,
+                                size, data);
+}
+
+static void
+tegra_texture_subdata(struct pipe_context *pcontext,
+                      struct pipe_resource *presource,
+                      unsigned level,
+                      unsigned usage,
+                      const struct pipe_box *box,
+                      const void *data,
+                      unsigned stride,
+                      unsigned layer_stride)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->texture_subdata(context->gpu, resource->gpu, level, usage,
+                                 box, data, stride, layer_stride);
+}
+
+static void
+tegra_texture_barrier(struct pipe_context *pcontext, unsigned int flags)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->texture_barrier(context->gpu, flags);
+}
+
+static void
+tegra_memory_barrier(struct pipe_context *pcontext, unsigned int flags)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->memory_barrier(context->gpu, flags);
+}
+
+static struct pipe_video_codec *
+tegra_create_video_codec(struct pipe_context *pcontext,
+                         const struct pipe_video_codec *template)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_video_codec(context->gpu, template);
+}
+
+static struct pipe_video_buffer *
+tegra_create_video_buffer(struct pipe_context *pcontext,
+                          const struct pipe_video_buffer *template)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_video_buffer(context->gpu, template);
+}
+
+static void *
+tegra_create_compute_state(struct pipe_context *pcontext,
+                           const struct pipe_compute_state *template)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->create_compute_state(context->gpu, template);
+}
+
+static void
+tegra_bind_compute_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->bind_compute_state(context->gpu, so);
+}
+
+static void
+tegra_delete_compute_state(struct pipe_context *pcontext, void *so)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->delete_compute_state(context->gpu, so);
+}
+
+static void
+tegra_set_compute_resources(struct pipe_context *pcontext,
+                            unsigned int start, unsigned int count,
+                            struct pipe_surface **resources)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   /* XXX unwrap resources */
+
+   context->gpu->set_compute_resources(context->gpu, start, count, resources);
+}
+
+static void
+tegra_set_global_binding(struct pipe_context *pcontext, unsigned int first,
+                         unsigned int count, struct pipe_resource **resources,
+                         uint32_t **handles)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   /* XXX unwrap resources */
+
+   context->gpu->set_global_binding(context->gpu, first, count, resources,
+                                    handles);
+}
+
+static void
+tegra_launch_grid(struct pipe_context *pcontext,
+                  const struct pipe_grid_info *info)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   /* XXX unwrap info->indirect? */
+
+   context->gpu->launch_grid(context->gpu, info);
+}
+
+static void
+tegra_get_sample_position(struct pipe_context *pcontext, unsigned int count,
+                          unsigned int index, float *value)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->get_sample_position(context->gpu, count, index, value);
+}
+
+static uint64_t
+tegra_get_timestamp(struct pipe_context *pcontext)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->get_timestamp(context->gpu);
+}
+
+static void
+tegra_flush_resource(struct pipe_context *pcontext,
+                     struct pipe_resource *presource)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->flush_resource(context->gpu, resource->gpu);
+}
+
+static void
+tegra_invalidate_resource(struct pipe_context *pcontext,
+                          struct pipe_resource *presource)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->invalidate_resource(context->gpu, resource->gpu);
+}
+
+static enum pipe_reset_status
+tegra_get_device_reset_status(struct pipe_context *pcontext)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->get_device_reset_status(context->gpu);
+}
+
+static void
+tegra_set_device_reset_callback(struct pipe_context *pcontext,
+                                const struct pipe_device_reset_callback *cb)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->set_device_reset_callback(context->gpu, cb);
+}
+
+static void
+tegra_dump_debug_state(struct pipe_context *pcontext, FILE *stream,
+                       unsigned int flags)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->dump_debug_state(context->gpu, stream, flags);
+}
+
+static void
+tegra_emit_string_marker(struct pipe_context *pcontext, const char *string,
+                         int length)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   context->gpu->emit_string_marker(context->gpu, string, length);
+}
+
+static boolean
+tegra_generate_mipmap(struct pipe_context *pcontext,
+                      struct pipe_resource *presource,
+                      enum pipe_format format,
+                      unsigned int base_level,
+                      unsigned int last_level,
+                      unsigned int first_layer,
+                      unsigned int last_layer)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+
+   return context->gpu->generate_mipmap(context->gpu, resource->gpu, format,
+                                        base_level, last_level, first_layer,
+                                        last_layer);
+}
+
+struct pipe_context *
+tegra_screen_context_create(struct pipe_screen *pscreen, void *priv,
+                            unsigned int flags)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+   struct tegra_context *context;
+
+   context = calloc(1, sizeof(*context));
+   if (!context)
+      return NULL;
+
+   context->gpu = screen->gpu->context_create(screen->gpu, priv, flags);
+   if (!context->gpu) {
+      debug_error("failed to create GPU context\n");
+      goto free;
+   }
+
+   context->base.screen = &screen->base;
+   context->base.priv = priv;
+
+   /*
+    * Create custom stream and const uploaders. Note that technically nouveau
+    * already creates uploaders that could be reused, but that would make the
+    * resource unwrapping rather complicate. The reason for that is that both
+    * uploaders create resources based on the context that they were created
+    * from, which means that nouveau's uploader will use the nouveau context
+    * which means that those resources must not be unwrapped. So before each
+    * resource is unwrapped, the code would need to check that it does not
+    * correspond to the uploaders' buffers.
+    *
+    * However, duplicating the uploaders here sounds worse than it is. The
+    * default implementation that nouveau uses allocates buffers lazily, and
+    * since it is never used, no buffers will every be allocated and the only
+    * memory wasted is that occupied by the nouveau uploader itself.
+    */
+   context->base.stream_uploader = u_upload_create_default(&context->base);
+   if (!context->base.stream_uploader)
+      goto destroy;
+
+   context->base.const_uploader = context->base.stream_uploader;
+
+   context->base.destroy = tegra_destroy;
+
+   context->base.draw_vbo = tegra_draw_vbo;
+
+   context->base.render_condition = tegra_render_condition;
+
+   context->base.create_query = tegra_create_query;
+   context->base.create_batch_query = tegra_create_batch_query;
+   context->base.destroy_query = tegra_destroy_query;
+   context->base.begin_query = tegra_begin_query;
+   context->base.end_query = tegra_end_query;
+   context->base.get_query_result = tegra_get_query_result;
+   context->base.get_query_result_resource = tegra_get_query_result_resource;
+   context->base.set_active_query_state = tegra_set_active_query_state;
+
+   context->base.create_blend_state = tegra_create_blend_state;
+   context->base.bind_blend_state = tegra_bind_blend_state;
+   context->base.delete_blend_state = tegra_delete_blend_state;
+
+   context->base.create_sampler_state = tegra_create_sampler_state;
+   context->base.bind_sampler_states = tegra_bind_sampler_states;
+   context->base.delete_sampler_state = tegra_delete_sampler_state;
+
+   context->base.create_rasterizer_state = tegra_create_rasterizer_state;
+   context->base.bind_rasterizer_state = tegra_bind_rasterizer_state;
+   context->base.delete_rasterizer_state = tegra_delete_rasterizer_state;
+
+   context->base.create_depth_stencil_alpha_state = tegra_create_depth_stencil_alpha_state;
+   context->base.bind_depth_stencil_alpha_state = tegra_bind_depth_stencil_alpha_state;
+   context->base.delete_depth_stencil_alpha_state = tegra_delete_depth_stencil_alpha_state;
+
+   context->base.create_fs_state = tegra_create_fs_state;
+   context->base.bind_fs_state = tegra_bind_fs_state;
+   context->base.delete_fs_state = tegra_delete_fs_state;
+
+   context->base.create_vs_state = tegra_create_vs_state;
+   context->base.bind_vs_state = tegra_bind_vs_state;
+   context->base.delete_vs_state = tegra_delete_vs_state;
+
+   context->base.create_gs_state = tegra_create_gs_state;
+   context->base.bind_gs_state = tegra_bind_gs_state;
+   context->base.delete_gs_state = tegra_delete_gs_state;
+
+   context->base.create_tcs_state = tegra_create_tcs_state;
+   context->base.bind_tcs_state = tegra_bind_tcs_state;
+   context->base.delete_tcs_state = tegra_delete_tcs_state;
+
+   context->base.create_tes_state = tegra_create_tes_state;
+   context->base.bind_tes_state = tegra_bind_tes_state;
+   context->base.delete_tes_state = tegra_delete_tes_state;
+
+   context->base.create_vertex_elements_state = tegra_create_vertex_elements_state;
+   context->base.bind_vertex_elements_state = tegra_bind_vertex_elements_state;
+   context->base.delete_vertex_elements_state = tegra_delete_vertex_elements_state;
+
+   context->base.set_blend_color = tegra_set_blend_color;
+   context->base.set_stencil_ref = tegra_set_stencil_ref;
+   context->base.set_sample_mask = tegra_set_sample_mask;
+   context->base.set_min_samples = tegra_set_min_samples;
+   context->base.set_clip_state = tegra_set_clip_state;
+
+   context->base.set_constant_buffer = tegra_set_constant_buffer;
+   context->base.set_framebuffer_state = tegra_set_framebuffer_state;
+   context->base.set_polygon_stipple = tegra_set_polygon_stipple;
+   context->base.set_scissor_states = tegra_set_scissor_states;
+   context->base.set_window_rectangles = tegra_set_window_rectangles;
+   context->base.set_viewport_states = tegra_set_viewport_states;
+   context->base.set_sampler_views = tegra_set_sampler_views;
+   context->base.set_tess_state = tegra_set_tess_state;
+
+   context->base.set_debug_callback = tegra_set_debug_callback;
+
+   context->base.set_shader_buffers = tegra_set_shader_buffers;
+   context->base.set_shader_images = tegra_set_shader_images;
+   context->base.set_vertex_buffers = tegra_set_vertex_buffers;
+
+   context->base.create_stream_output_target = tegra_create_stream_output_target;
+   context->base.stream_output_target_destroy = tegra_stream_output_target_destroy;
+   context->base.set_stream_output_targets = tegra_set_stream_output_targets;
+
+   context->base.resource_copy_region = tegra_resource_copy_region;
+   context->base.blit = tegra_blit;
+   context->base.clear = tegra_clear;
+   context->base.clear_render_target = tegra_clear_render_target;
+   context->base.clear_depth_stencil = tegra_clear_depth_stencil;
+   context->base.clear_texture = tegra_clear_texture;
+   context->base.clear_buffer = tegra_clear_buffer;
+   context->base.flush = tegra_flush;
+
+   context->base.create_fence_fd = tegra_create_fence_fd;
+   context->base.fence_server_sync = tegra_fence_server_sync;
+
+   context->base.create_sampler_view = tegra_create_sampler_view;
+   context->base.sampler_view_destroy = tegra_sampler_view_destroy;
+
+   context->base.create_surface = tegra_create_surface;
+   context->base.surface_destroy = tegra_surface_destroy;
+
+   context->base.transfer_map = tegra_transfer_map;
+   context->base.transfer_flush_region = tegra_transfer_flush_region;
+   context->base.transfer_unmap = tegra_transfer_unmap;
+   context->base.buffer_subdata = tegra_buffer_subdata;
+   context->base.texture_subdata = tegra_texture_subdata;
+
+   context->base.texture_barrier = tegra_texture_barrier;
+   context->base.memory_barrier = tegra_memory_barrier;
+
+   context->base.create_video_codec = tegra_create_video_codec;
+   context->base.create_video_buffer = tegra_create_video_buffer;
+
+   context->base.create_compute_state = tegra_create_compute_state;
+   context->base.bind_compute_state = tegra_bind_compute_state;
+   context->base.delete_compute_state = tegra_delete_compute_state;
+   context->base.set_compute_resources = tegra_set_compute_resources;
+   context->base.set_global_binding = tegra_set_global_binding;
+   context->base.launch_grid = tegra_launch_grid;
+   context->base.get_sample_position = tegra_get_sample_position;
+   context->base.get_timestamp = tegra_get_timestamp;
+
+   context->base.flush_resource = tegra_flush_resource;
+   context->base.invalidate_resource = tegra_invalidate_resource;
+
+   context->base.get_device_reset_status = tegra_get_device_reset_status;
+   context->base.set_device_reset_callback = tegra_set_device_reset_callback;
+   context->base.dump_debug_state = tegra_dump_debug_state;
+   context->base.emit_string_marker = tegra_emit_string_marker;
+
+   context->base.generate_mipmap = tegra_generate_mipmap;
+
+   return &context->base;
+
+destroy:
+   context->gpu->destroy(context->gpu);
+free:
+   free(context);
+   return NULL;
+}
diff --git a/src/gallium/drivers/tegra/tegra_context.h b/src/gallium/drivers/tegra/tegra_context.h
new file mode 100644
index 000000000000..4869b0913a6f
--- /dev/null
+++ b/src/gallium/drivers/tegra/tegra_context.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2014-2018 NVIDIA Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef TEGRA_CONTEXT_H
+#define TEGRA_CONTEXT_H
+
+#include "pipe/p_context.h"
+#include "pipe/p_state.h"
+
+struct tegra_screen;
+
+struct tegra_context {
+   struct pipe_context base;
+   struct pipe_context *gpu;
+};
+
+static inline struct tegra_context *
+to_tegra_context(struct pipe_context *context)
+{
+   return (struct tegra_context *)context;
+}
+
+struct pipe_context *
+tegra_screen_context_create(struct pipe_screen *pscreen, void *priv,
+                            unsigned int flags);
+
+struct tegra_sampler_view {
+   struct pipe_sampler_view base;
+   struct pipe_sampler_view *gpu;
+};
+
+static inline struct tegra_sampler_view *
+to_tegra_sampler_view(struct pipe_sampler_view *view)
+{
+   return (struct tegra_sampler_view *)view;
+}
+
+static inline struct pipe_sampler_view *
+tegra_sampler_view_unwrap(struct pipe_sampler_view *view)
+{
+   if (!view)
+      return NULL;
+
+   return to_tegra_sampler_view(view)->gpu;
+}
+
+struct tegra_transfer {
+   struct pipe_transfer base;
+   struct pipe_transfer *gpu;
+
+   unsigned int count;
+   void *map;
+};
+
+static inline struct tegra_transfer *
+to_tegra_transfer(struct pipe_transfer *transfer)
+{
+   return (struct tegra_transfer *)transfer;
+}
+
+#endif /* TEGRA_SCREEN_H */
diff --git a/src/gallium/drivers/tegra/tegra_resource.h b/src/gallium/drivers/tegra/tegra_resource.h
new file mode 100644
index 000000000000..67507d64590d
--- /dev/null
+++ b/src/gallium/drivers/tegra/tegra_resource.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2014-2018 NVIDIA Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef TEGRA_RESOURCE_H
+#define TEGRA_RESOURCE_H
+
+#include "pipe/p_state.h"
+
+struct winsys_handle;
+
+struct tegra_resource {
+   struct pipe_resource base;
+   struct pipe_resource *gpu;
+
+   uint64_t modifier;
+   uint32_t stride;
+   uint32_t handle;
+   size_t size;
+};
+
+static inline struct tegra_resource *
+to_tegra_resource(struct pipe_resource *resource)
+{
+   return (struct tegra_resource *)resource;
+}
+
+static inline struct pipe_resource *
+tegra_resource_unwrap(struct pipe_resource *resource)
+{
+   if (!resource)
+      return NULL;
+
+   return to_tegra_resource(resource)->gpu;
+}
+
+struct tegra_surface {
+   struct pipe_surface base;
+   struct pipe_surface *gpu;
+};
+
+static inline struct tegra_surface *
+to_tegra_surface(struct pipe_surface *surface)
+{
+   return (struct tegra_surface *)surface;
+}
+
+static inline struct pipe_surface *
+tegra_surface_unwrap(struct pipe_surface *surface)
+{
+   if (!surface)
+      return NULL;
+
+   return to_tegra_surface(surface)->gpu;
+}
+
+#endif /* TEGRA_RESOURCE_H */
diff --git a/src/gallium/drivers/tegra/tegra_screen.c b/src/gallium/drivers/tegra/tegra_screen.c
new file mode 100644
index 000000000000..669f22a19449
--- /dev/null
+++ b/src/gallium/drivers/tegra/tegra_screen.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright © 2014-2018 NVIDIA Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <sys/stat.h>
+
+#include <drm_fourcc.h>
+#include <tegra_drm.h>
+#include <xf86drm.h>
+
+#include "pipe/p_state.h"
+#include "util/u_debug.h"
+#include "util/u_inlines.h"
+
+#include "state_tracker/drm_driver.h"
+
+#include "nouveau/drm/nouveau_drm_public.h"
+
+#include "tegra_context.h"
+#include "tegra_resource.h"
+#include "tegra_screen.h"
+
+static void tegra_screen_destroy(struct pipe_screen *pscreen)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   screen->gpu->destroy(screen->gpu);
+   free(pscreen);
+}
+
+static const char *
+tegra_screen_get_name(struct pipe_screen *pscreen)
+{
+   return "tegra";
+}
+
+static const char *
+tegra_screen_get_vendor(struct pipe_screen *pscreen)
+{
+   return "NVIDIA";
+}
+
+static const char *
+tegra_screen_get_device_vendor(struct pipe_screen *pscreen)
+{
+   return "NVIDIA";
+}
+
+static int
+tegra_screen_get_param(struct pipe_screen *pscreen, enum pipe_cap param)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->get_param(screen->gpu, param);
+}
+
+static float
+tegra_screen_get_paramf(struct pipe_screen *pscreen, enum pipe_capf param)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->get_paramf(screen->gpu, param);
+}
+
+static int
+tegra_screen_get_shader_param(struct pipe_screen *pscreen, unsigned shader,
+                              enum pipe_shader_cap param)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->get_shader_param(screen->gpu, shader, param);
+}
+
+static int
+tegra_screen_get_video_param(struct pipe_screen *pscreen,
+                             enum pipe_video_profile profile,
+                             enum pipe_video_entrypoint entrypoint,
+                             enum pipe_video_cap param)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->get_video_param(screen->gpu, profile, entrypoint,
+                                       param);
+}
+
+static int
+tegra_screen_get_compute_param(struct pipe_screen *pscreen,
+                               enum pipe_shader_ir ir_type,
+                               enum pipe_compute_cap param,
+                               void *retp)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->get_compute_param(screen->gpu, ir_type, param,
+                                         retp);
+}
+
+static uint64_t
+tegra_screen_get_timestamp(struct pipe_screen *pscreen)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->get_timestamp(screen->gpu);
+}
+
+static boolean
+tegra_screen_is_format_supported(struct pipe_screen *pscreen,
+                                 enum pipe_format format,
+                                 enum pipe_texture_target target,
+                                 unsigned sample_count,
+                                 unsigned usage)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->is_format_supported(screen->gpu, format, target,
+                                           sample_count, usage);
+}
+
+static boolean
+tegra_screen_is_video_format_supported(struct pipe_screen *pscreen,
+                                       enum pipe_format format,
+                                       enum pipe_video_profile profile,
+                                       enum pipe_video_entrypoint entrypoint)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->is_video_format_supported(screen->gpu, format, profile,
+                                                 entrypoint);
+}
+
+static boolean
+tegra_screen_can_create_resource(struct pipe_screen *pscreen,
+                                 const struct pipe_resource *template)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->can_create_resource(screen->gpu, template);
+}
+
+static int tegra_open_render_node(void)
+{
+   drmDevicePtr *devices, device;
+   int err, render = -ENOENT, fd;
+   unsigned int num, i;
+
+   err = drmGetDevices2(0, NULL, 0);
+   if (err < 0)
+      return err;
+
+   num = err;
+
+   devices = calloc(num, sizeof(*devices));
+   if (!devices)
+      return -ENOMEM;
+
+   err = drmGetDevices2(0, devices, num);
+   if (err < 0) {
+      render = err;
+      goto free;
+   }
+
+   for (i = 0; i < num; i++) {
+      device = devices[i];
+
+      if ((device->available_nodes & (1 << DRM_NODE_RENDER)) &&
+          (device->bustype == DRM_BUS_PLATFORM)) {
+         drmVersionPtr version;
+
+         fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR | O_CLOEXEC);
+         if (fd < 0)
+            continue;
+
+         version = drmGetVersion(fd);
+         if (!version) {
+            close(fd);
+            continue;
+         }
+
+         if (strcmp(version->name, "nouveau") != 0) {
+            close(fd);
+            continue;
+         }
+
+         drmFreeVersion(version);
+         render = fd;
+         break;
+      }
+   }
+
+   drmFreeDevices(devices, num);
+
+free:
+   free(devices);
+   return render;
+}
+
+static int tegra_screen_import_resource(struct tegra_screen *screen,
+                                        struct tegra_resource *resource,
+                                        bool has_modifiers)
+{
+   unsigned usage = PIPE_HANDLE_USAGE_READ;
+   struct drm_tegra_gem_set_tiling args;
+   struct winsys_handle handle;
+   boolean status;
+   int fd, err;
+
+   memset(&handle, 0, sizeof(handle));
+   handle.modifier = DRM_FORMAT_MOD_INVALID;
+   handle.type = DRM_API_HANDLE_TYPE_FD;
+
+   status = screen->gpu->resource_get_handle(screen->gpu, NULL, resource->gpu,
+                                             &handle, usage);
+   if (!status)
+      return -EINVAL;
+
+   assert(handle.modifier != DRM_FORMAT_MOD_INVALID);
+
+   if (handle.modifier == DRM_FORMAT_MOD_INVALID) {
+      close(handle.handle);
+      return -EINVAL;
+   }
+
+   resource->modifier = handle.modifier;
+   resource->stride = handle.stride;
+   fd = handle.handle;
+
+   err = drmPrimeFDToHandle(screen->fd, fd, &resource->handle);
+   if (err < 0)
+      err = -errno;
+
+   close(fd);
+
+   if (!has_modifiers) {
+      memset(&args, 0, sizeof(args));
+      args.handle = resource->handle;
+
+      switch (handle.modifier) {
+         case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED:
+            args.mode = DRM_TEGRA_GEM_TILING_MODE_TILED;
+            break;
+
+         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB:
+            args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
+            args.value = 0;
+            break;
+
+         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB:
+            args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
+            args.value = 1;
+            break;
+
+         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB:
+            args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
+            args.value = 2;
+            break;
+
+         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB:
+            args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
+            args.value = 3;
+            break;
+
+         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB:
+            args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
+            args.value = 4;
+            break;
+
+         case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB:
+            args.mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
+            args.value = 5;
+            break;
+
+         default:
+            debug_printf("unsupported modifier %" PRIx64 ", assuming linear\n",
+                         handle.modifier);
+            /* fall-through */
+
+         case DRM_FORMAT_MOD_LINEAR:
+            args.mode = DRM_TEGRA_GEM_TILING_MODE_PITCH;
+            break;
+      }
+
+      err = drmIoctl(screen->fd, DRM_IOCTL_TEGRA_GEM_SET_TILING, &args);
+      if (err < 0) {
+         fprintf(stderr, "failed to set tiling parameters: %s\n",
+                 strerror(errno));
+         err = -errno;
+         goto out;
+      }
+   }
+
+   return 0;
+
+out:
+   return err;
+}
+
+static struct pipe_resource *
+tegra_screen_resource_create(struct pipe_screen *pscreen,
+                             const struct pipe_resource *template)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+   struct tegra_resource *resource;
+   int err;
+
+   resource = calloc(1, sizeof(*resource));
+   if (!resource)
+      return NULL;
+
+   resource->gpu = screen->gpu->resource_create(screen->gpu, template);
+   if (!resource->gpu)
+      goto free;
+
+   /* import scanout buffers for display */
+   if (template->bind & PIPE_BIND_SCANOUT) {
+      err = tegra_screen_import_resource(screen, resource, false);
+      if (err < 0)
+         goto destroy;
+   }
+
+   memcpy(&resource->base, resource->gpu, sizeof(*resource->gpu));
+   pipe_reference_init(&resource->base.reference, 1);
+   resource->base.screen = &screen->base;
+
+   return &resource->base;
+
+destroy:
+   screen->gpu->resource_destroy(screen->gpu, resource->gpu);
+free:
+   free(resource);
+   return NULL;
+}
+
+/* XXX */
+static struct pipe_resource *
+tegra_screen_resource_create_front(struct pipe_screen *pscreen,
+                                   const struct pipe_resource *template,
+                                   const void *map_front_private)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+   struct pipe_resource *resource;
+
+   resource = screen->gpu->resource_create_front(screen->gpu, template,
+                                                 map_front_private);
+   if (resource)
+      resource->screen = pscreen;
+
+   return resource;
+}
+
+static struct pipe_resource *
+tegra_screen_resource_from_handle(struct pipe_screen *pscreen,
+                                  const struct pipe_resource *template,
+                                  struct winsys_handle *handle,
+                                  unsigned usage)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+   struct tegra_resource *resource;
+
+   resource = calloc(1, sizeof(*resource));
+   if (!resource)
+      return NULL;
+
+   resource->gpu = screen->gpu->resource_from_handle(screen->gpu, template,
+                                                     handle, usage);
+   if (!resource->gpu) {
+      free(resource);
+      return NULL;
+   }
+
+   memcpy(&resource->base, resource->gpu, sizeof(*resource->gpu));
+   pipe_reference_init(&resource->base.reference, 1);
+   resource->base.screen = &screen->base;
+
+   return &resource->base;
+}
+
+/* XXX */
+static struct pipe_resource *
+tegra_screen_resource_from_user_memory(struct pipe_screen *pscreen,
+                                       const struct pipe_resource *template,
+                                       void *buffer)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+   struct pipe_resource *resource;
+
+   resource = screen->gpu->resource_from_user_memory(screen->gpu, template,
+                                                     buffer);
+   if (resource)
+      resource->screen = pscreen;
+
+   return resource;
+}
+
+static boolean
+tegra_screen_resource_get_handle(struct pipe_screen *pscreen,
+                                 struct pipe_context *pcontext,
+                                 struct pipe_resource *presource,
+                                 struct winsys_handle *handle,
+                                 unsigned usage)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+   boolean ret = TRUE;
+
+   /*
+    * Assume that KMS handles for scanout resources will only ever be used
+    * to pass buffers into Tegra DRM for display. In all other cases, return
+    * the Nouveau handle, assuming they will be used for sharing in DRI2/3.
+    */
+   if (handle->type == DRM_API_HANDLE_TYPE_KMS &&
+       presource->bind & PIPE_BIND_SCANOUT) {
+      handle->modifier = resource->modifier;
+      handle->handle = resource->handle;
+      handle->stride = resource->stride;
+   } else {
+      ret = screen->gpu->resource_get_handle(screen->gpu,
+                                             context ? context->gpu : NULL,
+                                             resource->gpu, handle, usage);
+   }
+
+   return ret;
+}
+
+static void
+tegra_screen_resource_destroy(struct pipe_screen *pscreen,
+                              struct pipe_resource *presource)
+{
+   struct tegra_resource *resource = to_tegra_resource(presource);
+
+   pipe_resource_reference(&resource->gpu, NULL);
+   free(resource);
+}
+
+static void
+tegra_screen_flush_frontbuffer(struct pipe_screen *pscreen,
+                               struct pipe_resource *resource,
+                               unsigned int level,
+                               unsigned int layer,
+                               void *winsys_drawable_handle,
+                               struct pipe_box *box)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   screen->gpu->flush_frontbuffer(screen->gpu, resource, level, layer,
+                                  winsys_drawable_handle, box);
+}
+
+static void
+tegra_screen_fence_reference(struct pipe_screen *pscreen,
+                             struct pipe_fence_handle **ptr,
+                             struct pipe_fence_handle *fence)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   screen->gpu->fence_reference(screen->gpu, ptr, fence);
+}
+
+static boolean
+tegra_screen_fence_finish(struct pipe_screen *pscreen,
+                          struct pipe_context *pcontext,
+                          struct pipe_fence_handle *fence,
+                          uint64_t timeout)
+{
+   struct tegra_context *context = to_tegra_context(pcontext);
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->fence_finish(screen->gpu,
+                                    context ? context->gpu : NULL,
+                                    fence, timeout);
+}
+
+static int
+tegra_screen_fence_get_fd(struct pipe_screen *pscreen,
+                          struct pipe_fence_handle *fence)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->fence_get_fd(screen->gpu, fence);
+}
+
+static int
+tegra_screen_get_driver_query_info(struct pipe_screen *pscreen,
+                                   unsigned int index,
+                                   struct pipe_driver_query_info *info)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->get_driver_query_info(screen->gpu, index, info);
+}
+
+static int
+tegra_screen_get_driver_query_group_info(struct pipe_screen *pscreen,
+                                         unsigned int index,
+                                         struct pipe_driver_query_group_info *info)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->get_driver_query_group_info(screen->gpu, index, info);
+}
+
+static void
+tegra_screen_query_memory_info(struct pipe_screen *pscreen,
+                               struct pipe_memory_info *info)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   screen->gpu->query_memory_info(screen->gpu, info);
+}
+
+static const void *
+tegra_screen_get_compiler_options(struct pipe_screen *pscreen,
+                                  enum pipe_shader_ir ir,
+                                  unsigned int shader)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+   const void *options = NULL;
+
+   if (screen->gpu->get_compiler_options)
+      options = screen->gpu->get_compiler_options(screen->gpu, ir, shader);
+
+   return options;
+}
+
+static struct disk_cache *
+tegra_screen_get_disk_shader_cache(struct pipe_screen *pscreen)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->get_disk_shader_cache(screen->gpu);
+}
+
+static struct pipe_resource *
+tegra_screen_resource_create_with_modifiers(struct pipe_screen *pscreen,
+                                            const struct pipe_resource *template,
+                                            const uint64_t *modifiers,
+                                            int count)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+   struct tegra_resource *resource;
+   int err;
+
+   resource = calloc(1, sizeof(*resource));
+   if (!resource)
+      return NULL;
+
+   resource->gpu = screen->gpu->resource_create_with_modifiers(screen->gpu,
+                                                               template,
+                                                               modifiers,
+                                                               count);
+   if (!resource->gpu)
+      goto free;
+
+   err = tegra_screen_import_resource(screen, resource, true);
+   if (err < 0)
+      goto destroy;
+
+   memcpy(&resource->base, resource->gpu, sizeof(*resource->gpu));
+   pipe_reference_init(&resource->base.reference, 1);
+   resource->base.screen = &screen->base;
+
+   return &resource->base;
+
+destroy:
+   screen->gpu->resource_destroy(screen->gpu, resource->gpu);
+free:
+   free(resource);
+   return NULL;
+}
+
+static void tegra_screen_query_dmabuf_modifiers(struct pipe_screen *pscreen,
+                                                enum pipe_format format,
+                                                int max, uint64_t *modifiers,
+                                                unsigned int *external_only,
+                                                int *count)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   screen->gpu->query_dmabuf_modifiers(screen->gpu, format, max, modifiers,
+                                       external_only, count);
+}
+
+static struct pipe_memory_object *
+tegra_screen_memobj_create_from_handle(struct pipe_screen *pscreen,
+                                       struct winsys_handle *handle,
+                                       bool dedicated)
+{
+   struct tegra_screen *screen = to_tegra_screen(pscreen);
+
+   return screen->gpu->memobj_create_from_handle(screen->gpu, handle,
+                                                 dedicated);
+}
+
+struct pipe_screen *
+tegra_screen_create(int fd)
+{
+   struct tegra_screen *screen;
+
+   screen = calloc(1, sizeof(*screen));
+   if (!screen)
+      return NULL;
+
+   screen->fd = fd;
+
+   screen->gpu_fd = tegra_open_render_node();
+   if (screen->gpu_fd < 0) {
+      if (errno != ENOENT)
+         fprintf(stderr, "failed to open GPU device: %s\n", strerror(errno));
+
+      free(screen);
+      return NULL;
+   }
+
+   screen->gpu = nouveau_drm_screen_create(screen->gpu_fd);
+   if (!screen->gpu) {
+      fprintf(stderr, "failed to create GPU screen\n");
+      close(screen->gpu_fd);
+      free(screen);
+      return NULL;
+   }
+
+   screen->base.destroy = tegra_screen_destroy;
+   screen->base.get_name = tegra_screen_get_name;
+   screen->base.get_vendor = tegra_screen_get_vendor;
+   screen->base.get_device_vendor = tegra_screen_get_device_vendor;
+   screen->base.get_param = tegra_screen_get_param;
+   screen->base.get_paramf = tegra_screen_get_paramf;
+   screen->base.get_shader_param = tegra_screen_get_shader_param;
+   screen->base.get_video_param = tegra_screen_get_video_param;
+   screen->base.get_compute_param = tegra_screen_get_compute_param;
+   screen->base.get_timestamp = tegra_screen_get_timestamp;
+   screen->base.context_create = tegra_screen_context_create;
+   screen->base.is_format_supported = tegra_screen_is_format_supported;
+   screen->base.is_video_format_supported = tegra_screen_is_video_format_supported;
+
+   /* allow fallback implementation if GPU driver doesn't implement it */
+   if (screen->gpu->can_create_resource)
+      screen->base.can_create_resource = tegra_screen_can_create_resource;
+
+   screen->base.resource_create = tegra_screen_resource_create;
+   screen->base.resource_create_front = tegra_screen_resource_create_front;
+   screen->base.resource_from_handle = tegra_screen_resource_from_handle;
+   screen->base.resource_from_user_memory = tegra_screen_resource_from_user_memory;
+   screen->base.resource_get_handle = tegra_screen_resource_get_handle;
+   screen->base.resource_destroy = tegra_screen_resource_destroy;
+
+   screen->base.flush_frontbuffer = tegra_screen_flush_frontbuffer;
+   screen->base.fence_reference = tegra_screen_fence_reference;
+   screen->base.fence_finish = tegra_screen_fence_finish;
+   screen->base.fence_get_fd = tegra_screen_fence_get_fd;
+
+   screen->base.get_driver_query_info = tegra_screen_get_driver_query_info;
+   screen->base.get_driver_query_group_info = tegra_screen_get_driver_query_group_info;
+   screen->base.query_memory_info = tegra_screen_query_memory_info;
+
+   screen->base.get_compiler_options = tegra_screen_get_compiler_options;
+   screen->base.get_disk_shader_cache = tegra_screen_get_disk_shader_cache;
+
+   screen->base.resource_create_with_modifiers = tegra_screen_resource_create_with_modifiers;
+   screen->base.query_dmabuf_modifiers = tegra_screen_query_dmabuf_modifiers;
+   screen->base.memobj_create_from_handle = tegra_screen_memobj_create_from_handle;
+
+   return &screen->base;
+}
diff --git a/src/gallium/drivers/tegra/tegra_screen.h b/src/gallium/drivers/tegra/tegra_screen.h
new file mode 100644
index 000000000000..558d22f2f993
--- /dev/null
+++ b/src/gallium/drivers/tegra/tegra_screen.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2014-2018 NVIDIA Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef TEGRA_SCREEN_H
+#define TEGRA_SCREEN_H
+
+#include "pipe/p_screen.h"
+
+struct tegra_screen {
+   struct pipe_screen base;
+   int fd;
+
+   struct pipe_screen *gpu;
+   int gpu_fd;
+};
+
+static inline struct tegra_screen *
+to_tegra_screen(struct pipe_screen *pscreen)
+{
+   return (struct tegra_screen *)pscreen;
+}
+
+struct pipe_screen *tegra_screen_create(int fd);
+
+#endif /* TEGRA_SCREEN_H */
diff --git a/src/gallium/meson.build b/src/gallium/meson.build
index 320fc0176e9a..4a6b73848433 100644
--- a/src/gallium/meson.build
+++ b/src/gallium/meson.build
@@ -119,6 +119,12 @@ if with_gallium_imx
 else
   driver_imx = declare_dependency()
 endif
+if with_gallium_tegra
+  subdir('winsys/tegra/drm')
+  subdir('drivers/tegra')
+else
+  driver_tegra = declare_dependency()
+endif
 if with_gallium_i915
   subdir('winsys/i915/drm')
   subdir('drivers/i915')
diff --git a/src/gallium/targets/dri/Makefile.am b/src/gallium/targets/dri/Makefile.am
index 1d05d91a6103..73bb71db0c4c 100644
--- a/src/gallium/targets/dri/Makefile.am
+++ b/src/gallium/targets/dri/Makefile.am
@@ -82,6 +82,8 @@ include $(top_srcdir)/src/gallium/drivers/svga/Automake.inc
 
 include $(top_srcdir)/src/gallium/drivers/freedreno/Automake.inc
 
+include $(top_srcdir)/src/gallium/drivers/tegra/Automake.inc
+
 include $(top_srcdir)/src/gallium/drivers/vc4/Automake.inc
 include $(top_srcdir)/src/gallium/drivers/vc5/Automake.inc
 include $(top_srcdir)/src/gallium/drivers/pl111/Automake.inc
diff --git a/src/gallium/targets/dri/meson.build b/src/gallium/targets/dri/meson.build
index a4326227f0f8..34402975386d 100644
--- a/src/gallium/targets/dri/meson.build
+++ b/src/gallium/targets/dri/meson.build
@@ -62,7 +62,8 @@ libgallium_dri = shared_library(
     dep_selinux, dep_expat, dep_libdrm, dep_llvm, dep_thread,
     driver_swrast, driver_r300, driver_r600, driver_radeonsi, driver_nouveau,
     driver_pl111, driver_vc4, driver_vc5, driver_freedreno, driver_etnaviv,
-    driver_imx, driver_i915, driver_svga, driver_virgl, driver_swr,
+    driver_imx, driver_tegra, driver_i915, driver_svga, driver_virgl,
+    driver_swr,
   ],
 )
 
@@ -76,6 +77,7 @@ foreach d : [[with_gallium_pl111, 'pl111_dri.so'],
              [with_gallium_vc5, 'vc5_dri.so'],
              [with_gallium_etnaviv, 'etnaviv_dri.so'],
              [with_gallium_imx, 'imx-drm_dri.so'],
+             [with_gallium_tegra, 'tegra_dri.so'],
              [with_gallium_i915, 'i915_dri.so'],
              [with_gallium_r300, 'r300_dri.so'],
              [with_gallium_r600, 'r600_dri.so'],
diff --git a/src/gallium/targets/dri/target.c b/src/gallium/targets/dri/target.c
index 5ee1761fdba8..e09e77687112 100644
--- a/src/gallium/targets/dri/target.c
+++ b/src/gallium/targets/dri/target.c
@@ -86,3 +86,7 @@ DEFINE_LOADER_DRM_ENTRYPOINT(vc5)
 DEFINE_LOADER_DRM_ENTRYPOINT(imx_drm)
 DEFINE_LOADER_DRM_ENTRYPOINT(etnaviv)
 #endif
+
+#if defined(GALLIUM_TEGRA)
+DEFINE_LOADER_DRM_ENTRYPOINT(tegra);
+#endif
diff --git a/src/gallium/targets/vdpau/Makefile.am b/src/gallium/targets/vdpau/Makefile.am
index 2742c7acd44d..cd05a0244510 100644
--- a/src/gallium/targets/vdpau/Makefile.am
+++ b/src/gallium/targets/vdpau/Makefile.am
@@ -57,6 +57,8 @@ include $(top_srcdir)/src/gallium/drivers/r300/Automake.inc
 include $(top_srcdir)/src/gallium/drivers/r600/Automake.inc
 include $(top_srcdir)/src/gallium/drivers/radeonsi/Automake.inc
 
+include $(top_srcdir)/src/gallium/drivers/tegra/Automake.inc
+
 if HAVE_GALLIUM_STATIC_TARGETS
 
 libvdpau_gallium_la_SOURCES += target.c
diff --git a/src/gallium/winsys/tegra/drm/Makefile.am b/src/gallium/winsys/tegra/drm/Makefile.am
new file mode 100644
index 000000000000..8518dedf3fb1
--- /dev/null
+++ b/src/gallium/winsys/tegra/drm/Makefile.am
@@ -0,0 +1,10 @@
+include Makefile.sources
+include $(top_srcdir)/src/gallium/Automake.inc
+
+AM_CFLAGS = \
+	-I$(top_srcdir)/src/gallium/drivers \
+	$(GALLIUM_WINSYS_CFLAGS)
+
+noinst_LTLIBRARIES = libtegradrm.la
+
+libtegradrm_la_SOURCES = $(C_SOURCES)
diff --git a/src/gallium/winsys/tegra/drm/Makefile.sources b/src/gallium/winsys/tegra/drm/Makefile.sources
new file mode 100644
index 000000000000..fe0d5c42e72d
--- /dev/null
+++ b/src/gallium/winsys/tegra/drm/Makefile.sources
@@ -0,0 +1,2 @@
+C_SOURCES := \
+	tegra_drm_winsys.c
diff --git a/src/gallium/winsys/tegra/drm/meson.build b/src/gallium/winsys/tegra/drm/meson.build
new file mode 100644
index 000000000000..46a6ab782a51
--- /dev/null
+++ b/src/gallium/winsys/tegra/drm/meson.build
@@ -0,0 +1,33 @@
+# Copyright © 2018 NVIDIA CORPORATION
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice 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.
+
+libtegradrm = static_library(
+  'tegradrm',
+  'tegra_drm_winsys.c',
+  include_directories : [
+    inc_include, inc_src, inc_gallium, inc_gallium_aux, inc_gallium_drivers,
+    inc_gallium_winsys
+  ],
+)
+
+driver_tegra = declare_dependency(
+  compile_args : '-DGALLIUM_TEGRA',
+  link_with : libtegradrm,
+)
diff --git a/src/gallium/winsys/tegra/drm/tegra_drm_public.h b/src/gallium/winsys/tegra/drm/tegra_drm_public.h
new file mode 100644
index 000000000000..8105180003f0
--- /dev/null
+++ b/src/gallium/winsys/tegra/drm/tegra_drm_public.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright © 2014-2018 NVIDIA Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef __TEGRA_DRM_PUBLIC_H__
+#define __TEGRA_DRM_PUBLIC_H__
+
+struct pipe_screen;
+
+struct pipe_screen *tegra_drm_screen_create(int fd);
+
+#endif /* __TEGRA_DRM_PUBLIC_H__ */
diff --git a/src/gallium/winsys/tegra/drm/tegra_drm_winsys.c b/src/gallium/winsys/tegra/drm/tegra_drm_winsys.c
new file mode 100644
index 000000000000..e2a8efb0f63f
--- /dev/null
+++ b/src/gallium/winsys/tegra/drm/tegra_drm_winsys.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2014-2018 NVIDIA Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <fcntl.h>
+
+#include "util/u_debug.h"
+
+#include "tegra/tegra_screen.h"
+
+struct pipe_screen *tegra_drm_screen_create(int fd);
+
+struct pipe_screen *tegra_drm_screen_create(int fd)
+{
+   struct pipe_screen *screen;
+
+   /*
+    * NOTE: There are reportedly issues with reusing the file descriptor
+    * as-is related to Xinerama. Duplicate it to side-step any issues.
+    */
+   fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+   if (fd < 0)
+      return NULL;
+
+   screen = tegra_screen_create(fd);
+   if (!screen)
+      close(fd);
+
+   return screen;
+}
-- 
2.16.2



More information about the mesa-dev mailing list