diff --git a/Makefile.am b/Makefile.am
index 8ecd9d9..e90ae43 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,7 +49,11 @@ if HAVE_EXYNOS
 EXYNOS_SUBDIR = exynos
 endif
 
-SUBDIRS = . $(LIBKMS_SUBDIR) $(INTEL_SUBDIR) $(NOUVEAU_SUBDIR) $(RADEON_SUBDIR) $(OMAP_SUBDIR) $(EXYNOS_SUBDIR) tests include man
+if HAVE_TEGRA
+TEGRA_SUBDIR = tegra
+endif
+
+SUBDIRS = . $(LIBKMS_SUBDIR) $(INTEL_SUBDIR) $(NOUVEAU_SUBDIR) $(RADEON_SUBDIR) $(OMAP_SUBDIR) $(EXYNOS_SUBDIR) $(TEGRA_SUBDIR) tests include man
 
 libdrm_la_LTLIBRARIES = libdrm.la
 libdrm_ladir = $(libdir)
diff --git a/configure.ac b/configure.ac
index 0c19929..72d6dfa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -114,6 +114,11 @@ AC_ARG_ENABLE(exynos-experimental-api,
 	      [Enable support for EXYNOS's experimental API (default: disabled)]),
 	      [EXYNOS=$enableval], [EXYNOS=no])
 
+AC_ARG_ENABLE(tegra-experimental-api,
+	      AS_HELP_STRING([--enable-tegra-experimental-api],
+	      [Enable support for Tegra's experimental API (default: disabled)]),
+	      [TEGRA=$enableval], [TEGRA=no])
+
 dnl ===========================================================================
 dnl check compiler flags
 AC_DEFUN([LIBDRM_CC_TRY_FLAG], [
@@ -222,6 +227,11 @@ if test "x$EXYNOS" = xyes; then
 	AC_DEFINE(HAVE_EXYNOS, 1, [Have EXYNOS support])
 fi
 
+AM_CONDITIONAL(HAVE_TEGRA, [test "x$TEGRA" = xyes])
+if test "x$TEGRA" = xyes; then
+	AC_DEFINE(HAVE_TEGRA, 1, [Have Tegra support])
+fi
+
 AC_ARG_ENABLE([cairo-tests],
               [AS_HELP_STRING([--enable-cairo-tests],
                               [Enable support for Cairo rendering in tests (default: auto)])],
@@ -247,7 +257,7 @@ if test "x$HAVE_LIBUDEV" = xyes; then
 fi
 AM_CONDITIONAL(HAVE_LIBUDEV, [test "x$HAVE_LIBUDEV" = xyes])
 
-if test "x$INTEL" != "xno" -o "x$RADEON" != "xno" -o "x$NOUVEAU" != "xno" -o "x$OMAP" != "xno"; then
+if test "x$INTEL" != "xno" -o "x$RADEON" != "xno" -o "x$NOUVEAU" != "xno" -o "x$OMAP" != "xno" -o "x$TEGRA" != "xno"; then
     # Check for atomic intrinsics
     AC_CACHE_CHECK([for native atomic primitives], drm_cv_atomic_primitives,
     [
@@ -358,6 +368,8 @@ AC_CONFIG_FILES([
 	omap/libdrm_omap.pc
 	exynos/Makefile
 	exynos/libdrm_exynos.pc
+	tegra/Makefile
+	tegra/libdrm_tegra.pc
 	tests/Makefile
 	tests/modeprint/Makefile
 	tests/modetest/Makefile
@@ -380,4 +392,5 @@ echo "  Radeon API     $RADEON"
 echo "  Nouveau API    $NOUVEAU"
 echo "  OMAP API       $OMAP"
 echo "  EXYNOS API     $EXYNOS"
+echo "  Tegra API      $TEGRA"
 echo ""
diff --git a/include/drm/Makefile.am b/include/drm/Makefile.am
index 2923ab4..3e33ed0 100644
--- a/include/drm/Makefile.am
+++ b/include/drm/Makefile.am
@@ -35,6 +35,7 @@ klibdrminclude_HEADERS = \
 	radeon_drm.h \
 	savage_drm.h \
 	sis_drm.h \
+	tegra_drm.h \
 	via_drm.h \
 	mach64_drm.h
 
diff --git a/include/drm/tegra_drm.h b/include/drm/tegra_drm.h
new file mode 100644
index 0000000..eaec602
--- /dev/null
+++ b/include/drm/tegra_drm.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2012 Thierry Reding
+ * 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 (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
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __TEGRA_DRM_H__
+#define __TEGRA_DRM_H__
+
+struct drm_tegra_gem_create {
+	uint32_t handle;
+	uint32_t offset;
+	uint32_t flags;
+	uint32_t size;
+};
+
+struct drm_tegra_gem_info {
+	uint32_t handle;
+	uint32_t offset;
+	uint32_t flags;
+	uint32_t size;
+};
+
+#define DRM_TEGRA_GEM_CREATE		0x40
+#define DRM_TEGRA_GEM_INFO		0x41
+
+#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_INFO	DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_INFO, struct drm_tegra_gem_info)
+
+#endif /* __TEGRA_DRM_H__ */
diff --git a/tegra/Makefile.am b/tegra/Makefile.am
new file mode 100644
index 0000000..65cd628
--- /dev/null
+++ b/tegra/Makefile.am
@@ -0,0 +1,17 @@
+AM_CPPFLAGS = \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/include/drm
+
+libdrm_tegra_ladir = $(libdir)
+libdrm_tegra_la_LTLIBRARIES = libdrm_tegra.la
+libdrm_tegra_la_LDFLAGS = -version-number 0:0:0 -no-undefined
+libdrm_tegra_la_LIBADD = ../libdrm.la @PTHREADSTUBS_LIBS@
+
+libdrm_tegra_la_SOURCES = \
+	tegra.c
+
+libdrm_tegraincludedir = ${includedir}/libdrm
+libdrm_tegrainclude_HEADERS = tegra.h
+
+pkgconfigdir = @pkgconfigdir@
+pkgconfig_DATA = libdrm_tegra.pc
diff --git a/tegra/libdrm_tegra.pc.in b/tegra/libdrm_tegra.pc.in
new file mode 100644
index 0000000..f83c74c
--- /dev/null
+++ b/tegra/libdrm_tegra.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdrm_tegra
+Description: Userspace interface to Tegra kernel DRM services
+Version: 2.4.40
+Libs: -L${libdir} -ldrm_tegra
+Cflags: -I${includedir} -I${includedir}/libdrm
+Requires.private: libdrm
diff --git a/tegra/tegra.c b/tegra/tegra.c
new file mode 100644
index 0000000..d92eca5
--- /dev/null
+++ b/tegra/tegra.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright © 2012 Thierry Reding
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <sys/mman.h>
+
+#include <libdrm_lists.h>
+#include <xf86atomic.h>
+#include <xf86drm.h>
+
+#include <tegra_drm.h>
+
+#include "tegra.h"
+
+struct drm_tegra {
+	drmMMListHead bo_list;
+	int fd;
+};
+struct tegra_bo {
+	struct drm_tegra_bo base;
+	drmMMListHead list;
+	atomic_t ref;
+};
+
+static inline struct tegra_bo *tegra_bo(struct drm_tegra_bo *bo)
+{
+	return (struct tegra_bo *)bo;
+}
+
+static void drm_tegra_bo_free(struct drm_tegra_bo *bo)
+{
+	struct tegra_bo *priv = tegra_bo(bo);
+	struct drm_gem_close args;
+
+	DRMLISTDEL(&priv->list);
+
+	if (bo->map)
+		munmap(bo->map, bo->size);
+
+	memset(&args, 0, sizeof(args));
+	args.handle = bo->handle;
+
+	drmIoctl(bo->device->fd, DRM_IOCTL_GEM_CLOSE, &args);
+
+	free(bo);
+}
+
+int drm_tegra_open(const char *path, struct drm_tegra **devicep)
+{
+	struct drm_tegra *device;
+	int err;
+
+	if (!path || !devicep)
+		return -EINVAL;
+
+	device = calloc(1, sizeof(*device));
+	if (!device)
+		return -ENOMEM;
+
+	DRMINITLISTHEAD(&device->bo_list);
+
+	device->fd = open(path, O_RDWR);
+	if (device->fd < 0) {
+		err = -errno;
+		free(device);
+		return err;
+	}
+
+	*devicep = device;
+
+	return 0;
+}
+
+void drm_tegra_close(struct drm_tegra *device)
+{
+	if (device) {
+		close(device->fd);
+		free(device);
+	}
+}
+
+int drm_tegra_bo_new(struct drm_tegra *device, uint32_t flags, uint32_t size,
+		     struct drm_tegra_bo **bop)
+{
+	struct drm_tegra_gem_create args;
+	struct drm_tegra_bo *bo;
+	struct tegra_bo *priv;
+	int err;
+
+	if (!device || size == 0 || !bop)
+		return -EINVAL;
+
+	bo = calloc(1, sizeof(*bo));
+	if (!bo)
+		return -ENOMEM;
+
+	priv = tegra_bo(bo);
+
+	DRMLISTINITHEAD(&priv->list);
+	atomic_set(&priv->ref, 1);
+	bo->device = device;
+	bo->flags = flags;
+	bo->size = size;
+
+	memset(&args, 0, sizeof(args));
+	args.flags = flags;
+	args.size = size;
+
+	err = drmCommandWriteRead(device->fd, DRM_TEGRA_GEM_CREATE, &args,
+				  sizeof(args));
+	if (err < 0) {
+		err = -errno;
+		free(bo);
+		return err;
+	}
+
+	DRMLISTADD(&priv->list, &device->bo_list);
+	bo->handle = args.handle;
+	bo->offset = args.offset;
+
+	*bop = bo;
+
+	return 0;
+}
+
+int drm_tegra_bo_open(struct drm_tegra *device, uint32_t handle,
+		      struct drm_tegra_bo **bop)
+{
+	struct drm_tegra_gem_info args;
+	struct drm_tegra_bo *bo;
+	struct tegra_bo *priv;
+	int err;
+
+	if (!device || !bop)
+		return -EINVAL;
+
+	bo = calloc(1, sizeof(*bo));
+	if (!bo)
+		return -ENOMEM;
+
+	priv = tegra_bo(bo);
+
+	DRMLISTINITHEAD(&priv->list);
+	atomic_set(&priv->ref, 1);
+	bo->device = device;
+
+	memset(&args, 0, sizeof(args));
+	args.handle = handle;
+
+	err = drmCommandWriteRead(device->fd, DRM_TEGRA_GEM_INFO, &args,
+				  sizeof(args));
+	if (err < 0) {
+		err = -errno;
+		free(bo);
+		return err;
+	}
+
+	DRMLISTADD(&priv->list, &device->bo_list);
+	bo->handle = args.handle;
+	bo->offset = args.offset;
+	bo->flags = args.flags;
+	bo->size = args.size;
+
+	*bop = bo;
+
+	return 0;
+}
+
+struct drm_tegra_bo *drm_tegra_bo_get(struct drm_tegra_bo *bo)
+{
+	if (bo) {
+		struct tegra_bo *priv = tegra_bo(bo);
+		atomic_inc(&priv->ref);
+	}
+
+	return bo;
+}
+
+void drm_tegra_bo_put(struct drm_tegra_bo *bo)
+{
+	if (bo) {
+		struct tegra_bo *priv = tegra_bo(bo);
+
+		if (atomic_dec_and_test(&priv->ref))
+			drm_tegra_bo_free(bo);
+	}
+}
+
+int drm_tegra_bo_map(struct drm_tegra_bo *bo)
+{
+	if (!bo->map) {
+		bo->map = mmap(0, bo->size, PROT_READ | PROT_WRITE,
+			       MAP_SHARED, bo->device->fd, bo->offset);
+		if (bo->map == MAP_FAILED) {
+			bo->map = NULL;
+			return -errno;
+		}
+	}
+
+	return 0;
+}
diff --git a/tegra/tegra.h b/tegra/tegra.h
new file mode 100644
index 0000000..619090c
--- /dev/null
+++ b/tegra/tegra.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright © 2012 Thierry Reding
+ *
+ * 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 __DRM_TEGRA_H__
+#define __DRM_TEGRA_H__ 1
+
+#include <stdint.h>
+#include <stdlib.h>
+
+struct drm_tegra;
+
+struct drm_tegra_bo {
+	struct drm_tegra *device;
+	uint32_t handle;
+	uint32_t offset;
+	uint32_t flags;
+	uint32_t size;
+	void *map;
+};
+
+int drm_tegra_open(const char *path, struct drm_tegra **devicep);
+void drm_tegra_close(struct drm_tegra *device);
+
+int drm_tegra_bo_new(struct drm_tegra *device, uint32_t flags, uint32_t size,
+		     struct drm_tegra_bo **bop);
+int drm_tegra_bo_open(struct drm_tegra *device, uint32_t handle,
+		      struct drm_tegra_bo **bop);
+int drm_tegra_bo_map(struct drm_tegra_bo *bo);
+struct drm_tegra_bo *drm_tegra_bo_get(struct drm_tegra_bo *bo);
+void drm_tegra_bo_put(struct drm_tegra_bo *bo);
+
+#endif /* __DRM_TEGRA_H__ */
diff --git a/tests/modetest/modetest.c b/tests/modetest/modetest.c
index c91bb9d..913ede8 100644
--- a/tests/modetest/modetest.c
+++ b/tests/modetest/modetest.c
@@ -939,7 +939,7 @@ int main(int argc, char **argv)
 	int c;
 	int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
 	int test_vsync = 0;
-	char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos" };
+	char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos", "tegra" };
 	unsigned int i;
 	int count = 0, plane_count = 0;
 	struct connector con_args[2];
diff --git a/tests/vbltest/vbltest.c b/tests/vbltest/vbltest.c
index 4fccd59..22a5339 100644
--- a/tests/vbltest/vbltest.c
+++ b/tests/vbltest/vbltest.c
@@ -103,7 +103,7 @@ static void usage(char *name)
 int main(int argc, char **argv)
 {
 	int i, c, fd, ret;
-	char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "exynos" };
+	char *modules[] = { "i915", "radeon", "nouveau", "vmwgfx", "exynos", "tegra" };
 	drmVBlank vbl;
 	drmEventContext evctx;
 	struct vbl_info handler_info;
