diff --git a/nx/include/switch/nvidia/address_space.h b/nx/include/switch/nvidia/address_space.h
index 63059974..6ed435fc 100644
--- a/nx/include/switch/nvidia/address_space.h
+++ b/nx/include/switch/nvidia/address_space.h
@@ -20,7 +20,7 @@ Result nvasReserveAlign(NvAddressSpace* a, NvPageSize align, u32 pages, NvPageSi
 Result nvasReserveAtFixedAddr(NvAddressSpace* a, iova_t addr, u32 pages, NvPageSize page_sz);
 Result nvasReserveFull(NvAddressSpace* a);
 
-Result nvasMapBuffer(NvAddressSpace* a, NvBuffer* buffer, NvBufferKind kind, iova_t* iova_out);
+Result nvasMapBuffer(NvAddressSpace* a, NvBuffer* buffer, iova_t* iova_out);
 
 struct NvChannel;
 Result nvasBindToChannel(NvAddressSpace* a, struct NvChannel* channel);
diff --git a/nx/include/switch/nvidia/buffer.h b/nx/include/switch/nvidia/buffer.h
index cfdf5967..4f06259c 100644
--- a/nx/include/switch/nvidia/buffer.h
+++ b/nx/include/switch/nvidia/buffer.h
@@ -2,19 +2,12 @@
 
 #include "../types.h"
 
-typedef struct {
-    u32   fd;
-    u32   size;
-    void* ptr;
-    bool  has_init;
-} NvBuffer;
-
 typedef enum {
     NvBufferFlags_Writable=1
 } NvBufferFlags;
 
 typedef enum {
-    NvBufferKind_PitCh=0x0,
+    NvBufferKind_Pitch=0x0,
     NvBufferKind_Z16=0x1,
     NvBufferKind_Z16_2C=0x2,
     NvBufferKind_Z16_MS2_2C=0x3,
@@ -249,8 +242,16 @@ typedef enum {
     NvBufferKind_Invalid=0xff,
 } NvBufferKind;
 
+typedef struct {
+    u32   fd;
+    u32   size;
+    void* ptr;
+    NvBufferKind kind;
+    bool  has_init;
+} NvBuffer;
+
 Result nvbufInit(void);
-void nvbufExit(void);
+void   nvbufExit(void);
 
 Result nvbufCreate(NvBuffer* m, size_t size, u32 align, NvBufferKind kind);
 Result nvbufCreateRw(NvBuffer* m, size_t size, u32 align, NvBufferKind kind);
diff --git a/nx/include/switch/nvidia/channel.h b/nx/include/switch/nvidia/channel.h
index f5758007..427afded 100644
--- a/nx/include/switch/nvidia/channel.h
+++ b/nx/include/switch/nvidia/channel.h
@@ -1,4 +1,5 @@
 #pragma once
+#include "ioctl.h"
 
 typedef struct NvChannel {
     u32  fd;
@@ -6,4 +7,6 @@ typedef struct NvChannel {
 } NvChannel;
 
 Result nvchannelCreate(NvChannel* c, const char* dev);
-void nvchannelClose(NvChannel* c);
+void   nvchannelClose(NvChannel* c);
+
+Result nvchannelSetPriority(NvChannel* c, NvChannelPriority prio);
diff --git a/nx/include/switch/nvidia/gpu/zcull_ctx.h b/nx/include/switch/nvidia/gpu/zcull_ctx.h
index 197b70db..cf423c04 100644
--- a/nx/include/switch/nvidia/gpu/zcull_ctx.h
+++ b/nx/include/switch/nvidia/gpu/zcull_ctx.h
@@ -3,7 +3,8 @@
 typedef struct NvGpu NvGpu;
 
 typedef struct {
-    NvGpu* parent;
+    NvGpu*   parent;
+    NvBuffer ctx_buf;
 } NvZcullContext;
 
 Result nvzcullCreate(NvZcullContext* z, NvGpu* parent);
diff --git a/nx/include/switch/nvidia/ioctl.h b/nx/include/switch/nvidia/ioctl.h
index f1839032..cf521107 100644
--- a/nx/include/switch/nvidia/ioctl.h
+++ b/nx/include/switch/nvidia/ioctl.h
@@ -104,22 +104,30 @@ typedef struct {
     u32 entry1;
 } nvioctl_gpfifo_entry;
 
-//Used with nvioctlChannel_AllocObjCtx().
-enum nvioctl_channel_obj_classnum {
-    NvChannelObjClassNum_2D = 0x902D,
-    NvChannelObjClassNum_3D = 0xB197,
-    NvChannelObjClassNum_Compute = 0xB1C0,
-    NvChannelObjClassNum_Kepler = 0xA140,
-    NvChannelObjClassNum_DMA = 0xB0B5,
-    NvChannelObjClassNum_ChannelGpfifo = 0xB06F
-};
+// Used with nvioctlChannel_AllocObjCtx().
+typedef enum nvioctl_channel_obj_classnum {
+    NvClassNumber_2D = 0x902D,
+    NvClassNumber_3D = 0xB197,
+    NvClassNumber_Compute = 0xB1C0,
+    NvClassNumber_Kepler = 0xA140,
+    NvClassNumber_DMA = 0xB0B5,
+    NvClassNumber_ChannelGpfifo = 0xB06F
+} NvClassNumber;
 
-//Used with nvioctlChannel_SetPriority().
-enum nvioctl_channel_priority {
-    NvChannelPriority_Low = 0x32,
-    NvChannelPriority_Medium = 0x64,
-    NvChannelPriority_High = 0x96
-};
+// Used with nvioctlChannel_SetPriority().
+typedef enum nvioctl_channel_priority {
+    NvChannelPriority_Low    = 50,
+    NvChannelPriority_Medium = 100,
+    NvChannelPriority_High   = 150
+} NvChannelPriority;
+
+// Used with nvioctlChannel_ZCullBind().
+typedef enum {
+    NvZcullConfig_Global = 0,
+    NvZcullConfig_NoCtxSwitch = 1,
+    NvZcullConfig_SeparateBuffer = 2,
+    NvZcullConfig_PartOfRegularBuffer = 3
+} NvZcullConfig;
 
 Result nvioctlNvhostCtrl_EventSignal(u32 fd, u32 event_id);
 Result nvioctlNvhostCtrl_EventWait(u32 fd, u32 syncpt_id, u32 threshold, s32 timeout, u32 event_id, u32 *out);
diff --git a/nx/source/nvidia/address_space.c b/nx/source/nvidia/address_space.c
index ee0211ec..224849c4 100644
--- a/nx/source/nvidia/address_space.c
+++ b/nx/source/nvidia/address_space.c
@@ -43,8 +43,8 @@ Result nvasReserveFull(NvAddressSpace* a) {
     return nvasReserveAlign(a, NvPageSize_64K, 0x10000, NvPageSize_64K, NULL);
 }
 
-Result nvasMapBuffer(NvAddressSpace* a, NvBuffer* buffer, NvBufferKind kind, iova_t* iova_out) {
-    return nvioctlNvhostAsGpu_MapBufferEx(a->fd, 0, kind, buffer->fd, 0, 0, buffer->size, 0, iova_out);
+Result nvasMapBuffer(NvAddressSpace* a, NvBuffer* buffer, iova_t* iova_out) {
+    return nvioctlNvhostAsGpu_MapBufferEx(a->fd, 0, buffer->kind, buffer->fd, 0, 0, buffer->size, 0, iova_out);
 }
 
 Result nvasBindToChannel(NvAddressSpace* a, NvChannel* channel)
diff --git a/nx/source/nvidia/buffer.c b/nx/source/nvidia/buffer.c
index ac74d1e2..1ce3a80c 100644
--- a/nx/source/nvidia/buffer.c
+++ b/nx/source/nvidia/buffer.c
@@ -43,6 +43,7 @@ static Result _nvbufCreate(NvBuffer* m, size_t size, u32 flags, u32 align, NvBuf
     m->size = size;
     m->fd = -1;
     m->ptr = memalign(size, align);
+    m->kind = kind;
 
     if (m->ptr == NULL)
         return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
diff --git a/nx/source/nvidia/channel.c b/nx/source/nvidia/channel.c
index e2dc1cdf..f069da03 100644
--- a/nx/source/nvidia/channel.c
+++ b/nx/source/nvidia/channel.c
@@ -27,3 +27,7 @@ void nvchannelClose(NvChannel* c)
 
     c->fd = -1;
 }
+
+Result nvchannelSetPriority(NvChannel* c, NvChannelPriority prio) {
+    return nvioctlChannel_SetPriority(c->fd, prio);
+}
diff --git a/nx/source/nvidia/gpu/3d_ctx.c b/nx/source/nvidia/gpu/3d_ctx.c
index bac68fdb..966dcb2b 100644
--- a/nx/source/nvidia/gpu/3d_ctx.c
+++ b/nx/source/nvidia/gpu/3d_ctx.c
@@ -3,9 +3,7 @@
 Result nv3dCreate(Nv3dContext* t, NvGpu* parent)
 {
     t->parent = parent;
-
-    // TODO: Get class number from nvinfo*().
-    return nvioctlChannel_AllocObjCtx(parent->gpu_channel.fd, 0xB197, 0, &t->obj_id);
+    return nvioctlChannel_AllocObjCtx(parent->gpu_channel.fd, NvClassNumber_3D, 0, &t->obj_id);
 }
 
 void nv3dClose(Nv3dContext* t) {
diff --git a/nx/source/nvidia/gpu/zcull_ctx.c b/nx/source/nvidia/gpu/zcull_ctx.c
index e8f0e46d..8986c71a 100644
--- a/nx/source/nvidia/gpu/zcull_ctx.c
+++ b/nx/source/nvidia/gpu/zcull_ctx.c
@@ -2,10 +2,23 @@
 
 Result nvzcullCreate(NvZcullContext* z, NvGpu* parent)
 {
+    Result rc;
+
     z->parent = parent;
-    return 0;
+    rc = nvbufCreateRw(&z->ctx_buf, nvinfoGetZcullCtxSize(), 0x1000, NvBufferKind_Pitch);
+
+    iova_t iova_out;
+
+    if (R_SUCCEEDED(rc))
+        rc = nvasMapBuffer(&parent->addr_space, &z->ctx_buf, &iova_out);
+
+    if (R_SUCCEEDED(rc))
+        rc = nvioctlChannel_ZCullBind(parent->gpu_channel.fd, iova_out, NvZcullConfig_SeparateBuffer);
+
+    return rc;
 }
 
 void nvzcullClose(NvZcullContext* z) {
-    /**/
+    // TODO: Unmap z->ctx_buf from parent->addr_space?
+    nvbufFree(&z->ctx_buf);
 }
diff --git a/nx/source/nvidia/ioctl/nvchannel.c b/nx/source/nvidia/ioctl/nvchannel.c
index 80665580..9dcfe56a 100644
--- a/nx/source/nvidia/ioctl/nvchannel.c
+++ b/nx/source/nvidia/ioctl/nvchannel.c
@@ -56,10 +56,18 @@ Result nvioctlChannel_AllocObjCtx(u32 fd, u32 class_num, u32 flags, u64* id_out)
     memset(&data, 0, sizeof(data));
     data.class_num = class_num;
     data.flags = flags;
-    if (id_out != NULL)
-        *id_out = data.obj_id;
+    data.obj_id = 0xDEADBEEF;
+
+    Result rc = nvIoctl(fd, _NV_IOWR(0x48, 0x09, data), &data);
+
+    if (R_SUCCEEDED(rc)) {
+        if (id_out != NULL) {
+            *id_out = data.obj_id;
+        }
+    }
+
+    return rc;
 
-    return nvIoctl(fd, _NV_IOWR(0x48, 0x09, data), &data);
 }
 
 Result nvioctlChannel_ZCullBind(u32 fd, u64 gpu_va, u32 mode) {