diff --git a/tegra/Makefile.am b/tegra/Makefile.am
index 1b83145b120d..c73587e8661e 100644
--- a/tegra/Makefile.am
+++ b/tegra/Makefile.am
@@ -11,6 +11,10 @@ libdrm_tegra_la_LDFLAGS = -version-number 0:0:0 -no-undefined
 libdrm_tegra_la_LIBADD = ../libdrm.la @PTHREADSTUBS_LIBS@
 
 libdrm_tegra_la_SOURCES = \
+	channel.c \
+	fence.c \
+	job.c \
+	pushbuf.c \
 	tegra.c
 
 libdrm_tegraincludedir = ${includedir}/libdrm
diff --git a/tegra/channel.c b/tegra/channel.c
new file mode 100644
index 000000000000..3ab1d578f8e5
--- /dev/null
+++ b/tegra/channel.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright © 2012, 2013 Thierry Reding
+ * Copyright © 2013 Erik Faye-Lund
+ * Copyright © 2014 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 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 <string.h>
+
+#include "private.h"
+
+static int drm_tegra_channel_setup(struct drm_tegra_channel *channel)
+{
+	struct drm_tegra *drm = channel->drm;
+	struct drm_tegra_get_syncpt args;
+	int err;
+
+	memset(&args, 0, sizeof(args));
+	args.context = channel->context;
+	args.index = 0;
+
+	err = ioctl(drm->fd, DRM_IOCTL_TEGRA_GET_SYNCPT, &args);
+	if (err < 0)
+		return -errno;
+
+	channel->syncpt = args.id;
+
+	return 0;
+}
+
+drm_public
+int drm_tegra_channel_open(struct drm_tegra_channel **channelp,
+			   struct drm_tegra *drm,
+			   enum drm_tegra_class client)
+{
+	struct drm_tegra_open_channel args;
+	struct drm_tegra_channel *channel;
+	enum host1x_class class;
+	int err;
+
+	switch (client) {
+	case DRM_TEGRA_GR2D:
+		class = HOST1X_CLASS_GR2D;
+		break;
+
+	case DRM_TEGRA_GR3D:
+		class = HOST1X_CLASS_GR3D;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	channel = calloc(1, sizeof(*channel));
+	if (!channel)
+		return -ENOMEM;
+
+	channel->drm = drm;
+
+	memset(&args, 0, sizeof(args));
+	args.client = class;
+
+	err = ioctl(drm->fd, DRM_IOCTL_TEGRA_OPEN_CHANNEL, &args);
+	if (err < 0) {
+		free(channel);
+		return -errno;
+	}
+
+	channel->context = args.context;
+	channel->class = class;
+
+	err = drm_tegra_channel_setup(channel);
+	if (err < 0) {
+		free(channel);
+		return err;
+	}
+
+	*channelp = channel;
+
+	return 0;
+}
+
+drm_public
+int drm_tegra_channel_close(struct drm_tegra_channel *channel)
+{
+	struct drm_tegra_close_channel args;
+	struct drm_tegra *drm;
+	int err;
+
+	if (!channel)
+		return -EINVAL;
+
+	drm = channel->drm;
+
+	memset(&args, 0, sizeof(args));
+	args.context = channel->context;
+
+	err = ioctl(drm->fd, DRM_IOCTL_TEGRA_CLOSE_CHANNEL, &args);
+	if (err < 0)
+		return -errno;
+
+	free(channel);
+
+	return 0;
+}
diff --git a/tegra/fence.c b/tegra/fence.c
new file mode 100644
index 000000000000..f58725ca8472
--- /dev/null
+++ b/tegra/fence.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2012, 2013 Thierry Reding
+ * Copyright © 2013 Erik Faye-Lund
+ * Copyright © 2014 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 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 <string.h>
+
+#include "private.h"
+
+drm_public
+int drm_tegra_fence_wait_timeout(struct drm_tegra_fence *fence,
+				 unsigned long timeout)
+{
+	struct drm_tegra_syncpt_wait args;
+	int err;
+
+	memset(&args, 0, sizeof(args));
+	args.id = fence->syncpt;
+	args.thresh = fence->value;
+	args.timeout = timeout;
+
+	while (true) {
+		err = ioctl(fence->drm->fd, DRM_IOCTL_TEGRA_SYNCPT_WAIT, &args);
+		if (err < 0) {
+			if (errno == EINTR)
+				continue;
+
+			return -errno;
+		}
+
+		break;
+	}
+
+	return 0;
+}
+
+drm_public
+int drm_tegra_fence_wait(struct drm_tegra_fence *fence)
+{
+	return drm_tegra_fence_wait_timeout(fence, -1);
+}
+
+drm_public
+void drm_tegra_fence_free(struct drm_tegra_fence *fence)
+{
+	free(fence);
+}
diff --git a/tegra/job.c b/tegra/job.c
new file mode 100644
index 000000000000..8df643c9a9bb
--- /dev/null
+++ b/tegra/job.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright © 2012, 2013 Thierry Reding
+ * Copyright © 2013 Erik Faye-Lund
+ * Copyright © 2014 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 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 <stdlib.h>
+#include <string.h>
+
+#include "private.h"
+
+int drm_tegra_job_add_reloc(struct drm_tegra_job *job,
+			    const struct drm_tegra_reloc *reloc)
+{
+	struct drm_tegra_reloc *relocs;
+	size_t size;
+
+	size = (job->num_relocs + 1) * sizeof(*reloc);
+
+	relocs = realloc(job->relocs, size);
+	if (!reloc)
+		return -ENOMEM;
+
+	job->relocs = relocs;
+
+	job->relocs[job->num_relocs++] = *reloc;
+
+	return 0;
+}
+
+int drm_tegra_job_add_cmdbuf(struct drm_tegra_job *job,
+			     const struct drm_tegra_cmdbuf *cmdbuf)
+{
+	struct drm_tegra_cmdbuf *cmdbufs;
+	size_t size;
+
+	size = (job->num_cmdbufs + 1) * sizeof(*cmdbuf);
+
+	cmdbufs = realloc(job->cmdbufs, size);
+	if (!cmdbufs)
+		return -ENOMEM;
+
+	cmdbufs[job->num_cmdbufs++] = *cmdbuf;
+	job->cmdbufs = cmdbufs;
+
+	return 0;
+}
+
+drm_public
+int drm_tegra_job_new(struct drm_tegra_job **jobp,
+		      struct drm_tegra_channel *channel)
+{
+	struct drm_tegra_job *job;
+
+	job = calloc(1, sizeof(*job));
+	if (!job)
+		return -ENOMEM;
+
+	DRMINITLISTHEAD(&job->pushbufs);
+	job->channel = channel;
+
+	*jobp = job;
+
+	return 0;
+}
+
+drm_public
+int drm_tegra_job_free(struct drm_tegra_job *job)
+{
+	struct drm_tegra_pushbuf_private *pushbuf;
+
+	if (!job)
+		return -EINVAL;
+
+	DRMLISTFOREACHENTRY(pushbuf, &job->pushbufs, list)
+		drm_tegra_pushbuf_free(&pushbuf->base);
+
+	free(job->cmdbufs);
+	free(job->relocs);
+	free(job);
+
+	return 0;
+}
+
+drm_public
+int drm_tegra_job_submit(struct drm_tegra_job *job,
+			 struct drm_tegra_fence **fencep)
+{
+	struct drm_tegra *drm = job->channel->drm;
+	struct drm_tegra_pushbuf_private *pushbuf;
+	struct drm_tegra_fence *fence = NULL;
+	struct drm_tegra_cmdbuf *cmdbufs;
+	struct drm_tegra_syncpt *syncpts;
+	struct drm_tegra_submit args;
+	unsigned int i;
+	int err;
+
+	/*
+	 * Make sure the current command stream buffer is queued for
+	 * submission.
+	 */
+	err = drm_tegra_pushbuf_queue(job->pushbuf);
+	if (err < 0)
+		return err;
+
+	job->pushbuf = NULL;
+
+	if (fencep) {
+		fence = calloc(1, sizeof(*fence));
+		if (!fence)
+			return -ENOMEM;
+	}
+
+	syncpts = calloc(1, sizeof(*syncpts));
+	if (!syncpts) {
+		free(cmdbufs);
+		free(fence);
+		return -ENOMEM;
+	}
+
+	syncpts[0].id = job->syncpt;
+	syncpts[0].incrs = job->increments;
+
+	memset(&args, 0, sizeof(args));
+	args.context = job->channel->context;
+	args.num_syncpts = 1;
+	args.num_cmdbufs = job->num_cmdbufs;
+	args.num_relocs = job->num_relocs;
+	args.num_waitchks = 0;
+	args.waitchk_mask = 0;
+	args.timeout = 1000;
+
+	args.syncpts = (uintptr_t)syncpts;
+	args.cmdbufs = (uintptr_t)job->cmdbufs;
+	args.relocs = (uintptr_t)job->relocs;
+	args.waitchks = 0;
+
+	err = ioctl(drm->fd, DRM_IOCTL_TEGRA_SUBMIT, &args);
+	if (err < 0) {
+		free(syncpts);
+		free(cmdbufs);
+		free(fence);
+		return -errno;
+	}
+
+	if (fence) {
+		fence->syncpt = job->syncpt;
+		fence->value = args.fence;
+		fence->drm = drm;
+		*fencep = fence;
+	}
+
+	free(syncpts);
+	free(cmdbufs);
+
+	return 0;
+}
diff --git a/tegra/private.h b/tegra/private.h
index 9b6bc9395d23..fc74fb56b58d 100644
--- a/tegra/private.h
+++ b/tegra/private.h
@@ -26,13 +26,31 @@
 #define __DRM_TEGRA_PRIVATE_H__ 1
 
 #include <stdbool.h>
+#include <stddef.h>
 #include <stdint.h>
 
 #include <libdrm.h>
+#include <libdrm_lists.h>
 #include <xf86atomic.h>
 
+#include "tegra_drm.h"
 #include "tegra.h"
 
+#define container_of(ptr, type, member) ({				\
+		const typeof(((type *)0)->member) *__mptr = (ptr);	\
+		(type *)((char *)__mptr - offsetof(type, member));	\
+	})
+
+#define align(offset, align) \
+	(((offset) + (align) - 1) & ~((align) - 1))
+
+enum host1x_class {
+	HOST1X_CLASS_HOST1X = 0x01,
+	HOST1X_CLASS_GR2D = 0x51,
+	HOST1X_CLASS_GR2D_SB = 0x52,
+	HOST1X_CLASS_GR3D = 0x60,
+};
+
 struct drm_tegra {
 	bool close;
 	int fd;
@@ -40,6 +58,7 @@ struct drm_tegra {
 
 struct drm_tegra_bo {
 	struct drm_tegra *drm;
+	drmMMListHead list;
 	uint32_t handle;
 	uint32_t offset;
 	uint32_t flags;
@@ -48,4 +67,57 @@ struct drm_tegra_bo {
 	void *map;
 };
 
+struct drm_tegra_channel {
+	struct drm_tegra *drm;
+	enum host1x_class class;
+	uint64_t context;
+	uint32_t syncpt;
+};
+
+struct drm_tegra_fence {
+	struct drm_tegra *drm;
+	uint32_t syncpt;
+	uint32_t value;
+};
+
+struct drm_tegra_pushbuf_private {
+	struct drm_tegra_pushbuf base;
+	struct drm_tegra_job *job;
+	drmMMListHead list;
+	drmMMListHead bos;
+
+	struct drm_tegra_bo *bo;
+	uint32_t *start;
+	uint32_t *end;
+};
+
+static inline struct drm_tegra_pushbuf_private *
+pushbuf_priv(struct drm_tegra_pushbuf *pb)
+{
+	return container_of(pb, struct drm_tegra_pushbuf_private, base);
+}
+
+int drm_tegra_pushbuf_queue(struct drm_tegra_pushbuf_private *pushbuf);
+
+struct drm_tegra_job {
+	struct drm_tegra_channel *channel;
+
+	unsigned int increments;
+	uint32_t syncpt;
+
+	struct drm_tegra_reloc *relocs;
+	unsigned int num_relocs;
+
+	struct drm_tegra_cmdbuf *cmdbufs;
+	unsigned int num_cmdbufs;
+
+	struct drm_tegra_pushbuf_private *pushbuf;
+	drmMMListHead pushbufs;
+};
+
+int drm_tegra_job_add_reloc(struct drm_tegra_job *job,
+			    const struct drm_tegra_reloc *reloc);
+int drm_tegra_job_add_cmdbuf(struct drm_tegra_job *job,
+			     const struct drm_tegra_cmdbuf *cmdbuf);
+
 #endif /* __DRM_TEGRA_PRIVATE_H__ */
diff --git a/tegra/pushbuf.c b/tegra/pushbuf.c
new file mode 100644
index 000000000000..178d5cd57541
--- /dev/null
+++ b/tegra/pushbuf.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright © 2012, 2013 Thierry Reding
+ * Copyright © 2013 Erik Faye-Lund
+ * Copyright © 2014 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 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 <stdlib.h>
+#include <string.h>
+
+#include "private.h"
+
+#define HOST1X_OPCODE_NONINCR(offset, count) \
+	((0x2 << 28) | (((offset) & 0xfff) << 16) | ((count) & 0xffff))
+
+int drm_tegra_pushbuf_queue(struct drm_tegra_pushbuf_private *pushbuf)
+{
+	struct drm_tegra_cmdbuf cmdbuf;
+	int err;
+
+	if (!pushbuf || !pushbuf->bo)
+		return 0;
+
+	/* unmap buffer object since it won't be accessed anymore */
+	drm_tegra_bo_unmap(pushbuf->bo);
+
+	/* add buffer object as command buffers for this job */
+	memset(&cmdbuf, 0, sizeof(cmdbuf));
+	cmdbuf.words = pushbuf->base.ptr - pushbuf->start;
+	cmdbuf.handle = pushbuf->bo->handle;
+	cmdbuf.offset = 0;
+
+	err = drm_tegra_job_add_cmdbuf(pushbuf->job, &cmdbuf);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static inline unsigned long
+drm_tegra_pushbuf_get_offset(struct drm_tegra_pushbuf *pushbuf)
+{
+	struct drm_tegra_pushbuf_private *priv = pushbuf_priv(pushbuf);
+
+	return (unsigned long)pushbuf->ptr - (unsigned long)priv->start;
+}
+
+drm_public
+int drm_tegra_pushbuf_new(struct drm_tegra_pushbuf **pushbufp,
+			  struct drm_tegra_job *job)
+{
+	struct drm_tegra_pushbuf_private *pushbuf;
+	void *ptr;
+	int err;
+
+	pushbuf = calloc(1, sizeof(*pushbuf));
+	if (!pushbuf)
+		return -ENOMEM;
+
+	DRMINITLISTHEAD(&pushbuf->list);
+	DRMINITLISTHEAD(&pushbuf->bos);
+	pushbuf->job = job;
+
+	*pushbufp = &pushbuf->base;
+
+	DRMLISTADD(&pushbuf->list, &job->pushbufs);
+	job->pushbuf = pushbuf;
+
+	return 0;
+}
+
+drm_public
+int drm_tegra_pushbuf_free(struct drm_tegra_pushbuf *pushbuf)
+{
+	struct drm_tegra_pushbuf_private *priv = pushbuf_priv(pushbuf);
+	struct drm_tegra_bo *bo, *tmp;
+
+	if (!pushbuf)
+		return -EINVAL;
+
+	drm_tegra_bo_unmap(priv->bo);
+
+	DRMLISTFOREACHENTRYSAFE(bo, tmp, &priv->bos, list)
+		drm_tegra_bo_put(priv->bo);
+
+	DRMLISTDEL(&priv->list);
+	free(priv);
+
+	return 0;
+}
+
+/**
+ * drm_tegra_pushbuf_prepare() - prepare push buffer for a series of pushes
+ * @pushbuf: push buffer
+ * @words: maximum number of words in series of pushes to follow
+ */
+drm_public
+int drm_tegra_pushbuf_prepare(struct drm_tegra_pushbuf *pushbuf,
+			      unsigned int words)
+{
+	struct drm_tegra_pushbuf_private *priv = pushbuf_priv(pushbuf);
+	struct drm_tegra_channel *channel = priv->job->channel;
+	struct drm_tegra_bo *bo;
+	void *ptr;
+	int err;
+
+	if (priv->bo && (pushbuf->ptr + words < priv->end))
+		return 0;
+
+	/*
+	 * Align to full pages, since buffer object allocations are page
+	 * granular anyway.
+	 */
+	words = align(words, 1024);
+
+	err = drm_tegra_bo_new(&bo, channel->drm, 0, words * sizeof(uint32_t));
+	if (err < 0)
+		return err;
+
+	err = drm_tegra_bo_map(bo, &ptr);
+	if (err < 0) {
+		drm_tegra_bo_put(bo);
+		return err;
+	}
+
+	/* queue current command stream buffer for submission */
+	err = drm_tegra_pushbuf_queue(priv);
+	if (err < 0) {
+		drm_tegra_bo_unmap(bo);
+		drm_tegra_bo_put(bo);
+		return err;
+	}
+
+	DRMLISTADD(&bo->list, &priv->bos);
+
+	priv->start = priv->base.ptr = ptr;
+	priv->end = priv->start + bo->size;
+	priv->bo = bo;
+
+	return 0;
+}
+
+drm_public
+int drm_tegra_pushbuf_relocate(struct drm_tegra_pushbuf *pushbuf,
+			       struct drm_tegra_bo *target,
+			       unsigned long offset,
+			       unsigned long shift)
+{
+	struct drm_tegra_pushbuf_private *priv = pushbuf_priv(pushbuf);
+	struct drm_tegra_reloc reloc;
+	int err;
+
+	memset(&reloc, 0, sizeof(reloc));
+	reloc.cmdbuf.handle = priv->bo->handle;
+	reloc.cmdbuf.offset = drm_tegra_pushbuf_get_offset(pushbuf);
+	reloc.target.handle = target->handle;
+	reloc.target.offset = offset;
+	reloc.shift = shift;
+
+	err = drm_tegra_job_add_reloc(priv->job, &reloc);
+	if (err < 0)
+		return err;
+
+	*pushbuf->ptr++ = 0xdeadbeef;
+
+	return 0;
+}
+
+drm_public
+int drm_tegra_pushbuf_sync(struct drm_tegra_pushbuf *pushbuf,
+			   enum drm_tegra_syncpt_cond cond)
+{
+	struct drm_tegra_pushbuf_private *priv = pushbuf_priv(pushbuf);
+
+	if (cond >= DRM_TEGRA_SYNCPT_COND_MAX)
+		return -EINVAL;
+
+	*pushbuf->ptr++ = HOST1X_OPCODE_NONINCR(0x0, 0x1);
+	*pushbuf->ptr++ = cond << 8 | priv->job->syncpt;
+	priv->job->increments++;
+
+	return 0;
+}
diff --git a/tegra/tegra.c b/tegra/tegra.c
index bf6d035453d5..1f7b9e7345b6 100644
--- a/tegra/tegra.c
+++ b/tegra/tegra.c
@@ -56,6 +56,7 @@ static void drm_tegra_bo_free(struct drm_tegra_bo *bo)
 
 	drmIoctl(drm->fd, DRM_IOCTL_GEM_CLOSE, &args);
 
+	DRMLISTDEL(&bo->list);
 	free(bo);
 }
 
@@ -127,6 +128,7 @@ int drm_tegra_bo_new(struct drm_tegra_bo **bop, struct drm_tegra *drm,
 	if (!bo)
 		return -ENOMEM;
 
+	DRMINITLISTHEAD(&bo->list);
 	atomic_set(&bo->ref, 1);
 	bo->flags = flags;
 	bo->size = size;
diff --git a/tegra/tegra.h b/tegra/tegra.h
index 0731cb3bd4dc..4cf3b49a3e3e 100644
--- a/tegra/tegra.h
+++ b/tegra/tegra.h
@@ -28,6 +28,13 @@
 #include <stdint.h>
 #include <stdlib.h>
 
+#include <tegra_drm.h>
+
+enum drm_tegra_class {
+	DRM_TEGRA_GR2D,
+	DRM_TEGRA_GR3D,
+};
+
 struct drm_tegra_bo;
 struct drm_tegra;
 
@@ -44,4 +51,49 @@ int drm_tegra_bo_get_handle(struct drm_tegra_bo *bo, uint32_t *handle);
 int drm_tegra_bo_map(struct drm_tegra_bo *bo, void **ptr);
 int drm_tegra_bo_unmap(struct drm_tegra_bo *bo);
 
+struct drm_tegra_channel;
+struct drm_tegra_job;
+
+struct drm_tegra_pushbuf {
+	uint32_t *ptr;
+};
+
+struct drm_tegra_fence;
+
+enum drm_tegra_syncpt_cond {
+	DRM_TEGRA_SYNCPT_COND_IMMEDIATE,
+	DRM_TEGRA_SYNCPT_COND_OP_DONE,
+	DRM_TEGRA_SYNCPT_COND_RD_DONE,
+	DRM_TEGRA_SYNCPT_COND_WR_SAFE,
+	DRM_TEGRA_SYNCPT_COND_MAX,
+};
+
+int drm_tegra_channel_open(struct drm_tegra_channel **channelp,
+			   struct drm_tegra *drm,
+			   enum drm_tegra_class client);
+int drm_tegra_channel_close(struct drm_tegra_channel *channel);
+
+int drm_tegra_job_new(struct drm_tegra_job **jobp,
+		      struct drm_tegra_channel *channel);
+int drm_tegra_job_free(struct drm_tegra_job *job);
+int drm_tegra_job_submit(struct drm_tegra_job *job,
+			 struct drm_tegra_fence **fencep);
+
+int drm_tegra_pushbuf_new(struct drm_tegra_pushbuf **pushbufp,
+			  struct drm_tegra_job *job);
+int drm_tegra_pushbuf_free(struct drm_tegra_pushbuf *pushbuf);
+int drm_tegra_pushbuf_prepare(struct drm_tegra_pushbuf *pushbuf,
+			      unsigned int words);
+int drm_tegra_pushbuf_relocate(struct drm_tegra_pushbuf *pushbuf,
+			       struct drm_tegra_bo *target,
+			       unsigned long offset,
+			       unsigned long shift);
+int drm_tegra_pushbuf_sync(struct drm_tegra_pushbuf *pushbuf,
+			   enum drm_tegra_syncpt_cond cond);
+
+int drm_tegra_fence_wait_timeout(struct drm_tegra_fence *fence,
+				 unsigned long timeout);
+int drm_tegra_fence_wait(struct drm_tegra_fence *fence);
+void drm_tegra_fence_free(struct drm_tegra_fence *fence);
+
 #endif /* __DRM_TEGRA_H__ */
