From: Ben Skeggs <bskeggs@redhat.com>
Date: Sat, 9 Aug 2014 18:10:24 +0000 (+1000)
Subject: drm/nouveau/dmaobj: switch to a slightly saner design
X-Git-Tag: firefly_0821_release~176^2~3421^2~35
X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=b2c817031bc99d6e809fa10dcd8bf709b61d088d;p=firefly-linux-kernel-4.4.55.git

drm/nouveau/dmaobj: switch to a slightly saner design

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
---

diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c
index b04d4572603c..2472c6b7489f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c
@@ -30,22 +30,43 @@
 #include "priv.h"
 
 static int
-nouveau_dmaobj_ctor(struct nouveau_object *parent,
+nvkm_dmaobj_bind(struct nouveau_dmaobj *dmaobj, struct nouveau_object *parent,
+		 struct nouveau_gpuobj **pgpuobj)
+{
+	const struct nvkm_dmaeng_impl *impl = (void *)
+		nv_oclass(nv_object(dmaobj)->engine);
+	int ret = 0;
+
+	if (nv_object(dmaobj) == parent) { /* ctor bind */
+		if (nv_mclass(parent->parent) == NV_DEVICE) {
+			/* delayed, or no, binding */
+			return 0;
+		}
+		ret = impl->bind(dmaobj, parent, pgpuobj);
+		if (ret == 0)
+			nouveau_object_ref(NULL, &parent);
+		return ret;
+	}
+
+	return impl->bind(dmaobj, parent, pgpuobj);
+}
+
+int
+nvkm_dmaobj_create_(struct nouveau_object *parent,
 		    struct nouveau_object *engine,
-		    struct nouveau_oclass *oclass, void *data, u32 size,
-		    struct nouveau_object **pobject)
+		    struct nouveau_oclass *oclass, void **pdata, u32 *psize,
+		    int length, void **pobject)
 {
-	struct nouveau_dmaeng *dmaeng = (void *)engine;
+	struct nv_dma_class *args = *pdata;
 	struct nouveau_dmaobj *dmaobj;
-	struct nouveau_gpuobj *gpuobj;
-	struct nv_dma_class *args = data;
 	int ret;
 
-	if (size < sizeof(*args))
+	if (*psize < sizeof(*args))
 		return -EINVAL;
+	*pdata = &args->conf0;
 
-	ret = nouveau_object_create(parent, engine, oclass, 0, &dmaobj);
-	*pobject = nv_object(dmaobj);
+	ret = nouveau_object_create_(parent, engine, oclass, 0, length, pobject);
+	dmaobj = *pobject;
 	if (ret)
 		return ret;
 
@@ -87,39 +108,9 @@ nouveau_dmaobj_ctor(struct nouveau_object *parent,
 	dmaobj->start = args->start;
 	dmaobj->limit = args->limit;
 	dmaobj->conf0 = args->conf0;
-
-	switch (nv_mclass(parent)) {
-	case NV_DEVICE:
-		/* delayed, or no, binding */
-		break;
-	default:
-		ret = dmaeng->bind(dmaeng, *pobject, dmaobj, &gpuobj);
-		if (ret == 0) {
-			nouveau_object_ref(NULL, pobject);
-			*pobject = nv_object(gpuobj);
-		}
-		break;
-	}
-
 	return ret;
 }
 
-static struct nouveau_ofuncs
-nouveau_dmaobj_ofuncs = {
-	.ctor = nouveau_dmaobj_ctor,
-	.dtor = nouveau_object_destroy,
-	.init = nouveau_object_init,
-	.fini = nouveau_object_fini,
-};
-
-static struct nouveau_oclass
-nouveau_dmaobj_sclass[] = {
-	{ NV_DMA_FROM_MEMORY_CLASS, &nouveau_dmaobj_ofuncs },
-	{ NV_DMA_TO_MEMORY_CLASS, &nouveau_dmaobj_ofuncs },
-	{ NV_DMA_IN_MEMORY_CLASS, &nouveau_dmaobj_ofuncs },
-	{}
-};
-
 int
 _nvkm_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 		  struct nouveau_oclass *oclass, void *data, u32 size,
@@ -135,7 +126,7 @@ _nvkm_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 	if (ret)
 		return ret;
 
-	nv_engine(dmaeng)->sclass = nouveau_dmaobj_sclass;
-	dmaeng->bind = impl->bind;
+	nv_engine(dmaeng)->sclass = impl->sclass;
+	dmaeng->bind = nvkm_dmaobj_bind;
 	return 0;
 }
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c
index 29fb95c5d058..62d25a0634be 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c
@@ -30,19 +30,23 @@
 
 #include "priv.h"
 
+struct nv04_dmaobj_priv {
+	struct nouveau_dmaobj base;
+	bool clone;
+	u32 flags0;
+	u32 flags2;
+};
+
 static int
-nv04_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
+nv04_dmaobj_bind(struct nouveau_dmaobj *dmaobj,
 		 struct nouveau_object *parent,
-		 struct nouveau_dmaobj *dmaobj,
 		 struct nouveau_gpuobj **pgpuobj)
 {
-	struct nv04_vmmgr_priv *vmm = nv04_vmmgr(dmaeng);
+	struct nv04_dmaobj_priv *priv = (void *)dmaobj;
 	struct nouveau_gpuobj *gpuobj;
-	u32 flags0 = nv_mclass(dmaobj);
-	u32 flags2 = 0x00000000;
-	u64 offset = dmaobj->start & 0xfffff000;
-	u64 adjust = dmaobj->start & 0x00000fff;
-	u32 length = dmaobj->limit - dmaobj->start;
+	u64 offset = priv->base.start & 0xfffff000;
+	u64 adjust = priv->base.start & 0x00000fff;
+	u32 length = priv->base.limit - priv->base.start;
 	int ret;
 
 	if (!nv_iclass(parent, NV_ENGCTX_CLASS)) {
@@ -57,58 +61,96 @@ nv04_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
 		}
 	}
 
-	if (dmaobj->target == NV_MEM_TARGET_VM) {
-		if (nv_object(vmm)->oclass == &nv04_vmmgr_oclass) {
-			struct nouveau_gpuobj *pgt = vmm->vm->pgt[0].obj[0];
-			if (!dmaobj->start)
-				return nouveau_gpuobj_dup(parent, pgt, pgpuobj);
-			offset  = nv_ro32(pgt, 8 + (offset >> 10));
-			offset &= 0xfffff000;
-		}
+	if (priv->clone) {
+		struct nv04_vmmgr_priv *vmm = nv04_vmmgr(dmaobj);
+		struct nouveau_gpuobj *pgt = vmm->vm->pgt[0].obj[0];
+		if (!dmaobj->start)
+			return nouveau_gpuobj_dup(parent, pgt, pgpuobj);
+		offset  = nv_ro32(pgt, 8 + (offset >> 10));
+		offset &= 0xfffff000;
+	}
+
+	ret = nouveau_gpuobj_new(parent, parent, 16, 16, 0, &gpuobj);
+	*pgpuobj = gpuobj;
+	if (ret == 0) {
+		nv_wo32(*pgpuobj, 0x00, priv->flags0 | (adjust << 20));
+		nv_wo32(*pgpuobj, 0x04, length);
+		nv_wo32(*pgpuobj, 0x08, priv->flags2 | offset);
+		nv_wo32(*pgpuobj, 0x0c, priv->flags2 | offset);
+	}
+
+	return ret;
+}
+
+static int
+nv04_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+		 struct nouveau_oclass *oclass, void *data, u32 size,
+		 struct nouveau_object **pobject)
+{
+	struct nouveau_dmaeng *dmaeng = (void *)engine;
+	struct nv04_vmmgr_priv *vmm = nv04_vmmgr(engine);
+	struct nv04_dmaobj_priv *priv;
+	int ret;
+
+	ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
 
-		dmaobj->target = NV_MEM_TARGET_PCI;
-		dmaobj->access = NV_MEM_ACCESS_RW;
+	if (priv->base.target == NV_MEM_TARGET_VM) {
+		if (nv_object(vmm)->oclass == &nv04_vmmgr_oclass)
+			priv->clone = true;
+		priv->base.target = NV_MEM_TARGET_PCI;
+		priv->base.access = NV_MEM_ACCESS_RW;
 	}
 
-	switch (dmaobj->target) {
+	priv->flags0 = nv_mclass(priv);
+	switch (priv->base.target) {
 	case NV_MEM_TARGET_VRAM:
-		flags0 |= 0x00003000;
+		priv->flags0 |= 0x00003000;
 		break;
 	case NV_MEM_TARGET_PCI:
-		flags0 |= 0x00023000;
+		priv->flags0 |= 0x00023000;
 		break;
 	case NV_MEM_TARGET_PCI_NOSNOOP:
-		flags0 |= 0x00033000;
+		priv->flags0 |= 0x00033000;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-	switch (dmaobj->access) {
+	switch (priv->base.access) {
 	case NV_MEM_ACCESS_RO:
-		flags0 |= 0x00004000;
+		priv->flags0 |= 0x00004000;
 		break;
 	case NV_MEM_ACCESS_WO:
-		flags0 |= 0x00008000;
+		priv->flags0 |= 0x00008000;
 	case NV_MEM_ACCESS_RW:
-		flags2 |= 0x00000002;
+		priv->flags2 |= 0x00000002;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-	ret = nouveau_gpuobj_new(parent, parent, 16, 16, 0, &gpuobj);
-	*pgpuobj = gpuobj;
-	if (ret == 0) {
-		nv_wo32(*pgpuobj, 0x00, flags0 | (adjust << 20));
-		nv_wo32(*pgpuobj, 0x04, length);
-		nv_wo32(*pgpuobj, 0x08, flags2 | offset);
-		nv_wo32(*pgpuobj, 0x0c, flags2 | offset);
-	}
-
-	return ret;
+	return dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject);
 }
 
+static struct nouveau_ofuncs
+nv04_dmaobj_ofuncs = {
+	.ctor =  nv04_dmaobj_ctor,
+	.dtor = _nvkm_dmaobj_dtor,
+	.init = _nvkm_dmaobj_init,
+	.fini = _nvkm_dmaobj_fini,
+};
+
+static struct nouveau_oclass
+nv04_dmaeng_sclass[] = {
+	{ NV_DMA_FROM_MEMORY_CLASS, &nv04_dmaobj_ofuncs },
+	{ NV_DMA_TO_MEMORY_CLASS, &nv04_dmaobj_ofuncs },
+	{ NV_DMA_IN_MEMORY_CLASS, &nv04_dmaobj_ofuncs },
+	{}
+};
+
 struct nouveau_oclass *
 nv04_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
 	.base.handle = NV_ENGINE(DMAOBJ, 0x04),
@@ -118,5 +160,6 @@ nv04_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
 		.init = _nvkm_dmaeng_init,
 		.fini = _nvkm_dmaeng_fini,
 	},
+	.sclass = nv04_dmaeng_sclass,
 	.bind = nv04_dmaobj_bind,
 }.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c
index a1603a872196..5996a30b22ab 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c
@@ -29,14 +29,18 @@
 
 #include "priv.h"
 
+struct nv50_dmaobj_priv {
+	struct nouveau_dmaobj base;
+	u32 flags0;
+	u32 flags5;
+};
+
 static int
-nv50_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
+nv50_dmaobj_bind(struct nouveau_dmaobj *dmaobj,
 		 struct nouveau_object *parent,
-		 struct nouveau_dmaobj *dmaobj,
 		 struct nouveau_gpuobj **pgpuobj)
 {
-	u32 flags0 = nv_mclass(dmaobj);
-	u32 flags5 = 0x00000000;
+	struct nv50_dmaobj_priv *priv = (void *)dmaobj;
 	int ret;
 
 	if (!nv_iclass(parent, NV_ENGCTX_CLASS)) {
@@ -66,68 +70,107 @@ nv50_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
 		}
 	}
 
-	if (!(dmaobj->conf0 & NV50_DMA_CONF0_ENABLE)) {
-		if (dmaobj->target == NV_MEM_TARGET_VM) {
-			dmaobj->conf0  = NV50_DMA_CONF0_PRIV_VM;
-			dmaobj->conf0 |= NV50_DMA_CONF0_PART_VM;
-			dmaobj->conf0 |= NV50_DMA_CONF0_COMP_VM;
-			dmaobj->conf0 |= NV50_DMA_CONF0_TYPE_VM;
+	ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
+	if (ret == 0) {
+		nv_wo32(*pgpuobj, 0x00, priv->flags0 | nv_mclass(dmaobj));
+		nv_wo32(*pgpuobj, 0x04, lower_32_bits(priv->base.limit));
+		nv_wo32(*pgpuobj, 0x08, lower_32_bits(priv->base.start));
+		nv_wo32(*pgpuobj, 0x0c, upper_32_bits(priv->base.limit) << 24 |
+					upper_32_bits(priv->base.start));
+		nv_wo32(*pgpuobj, 0x10, 0x00000000);
+		nv_wo32(*pgpuobj, 0x14, priv->flags5);
+	}
+
+	return ret;
+}
+
+static int
+nv50_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+		 struct nouveau_oclass *oclass, void *data, u32 size,
+		 struct nouveau_object **pobject)
+{
+	struct nouveau_dmaeng *dmaeng = (void *)engine;
+	struct nv50_dmaobj_priv *priv;
+	union {
+		u32 conf0;
+	} *args;
+	int ret;
+
+	ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+	args = data;
+
+	if (!(args->conf0 & NV50_DMA_CONF0_ENABLE)) {
+		if (priv->base.target == NV_MEM_TARGET_VM) {
+			args->conf0  = NV50_DMA_CONF0_PRIV_VM;
+			args->conf0 |= NV50_DMA_CONF0_PART_VM;
+			args->conf0 |= NV50_DMA_CONF0_COMP_VM;
+			args->conf0 |= NV50_DMA_CONF0_TYPE_VM;
 		} else {
-			dmaobj->conf0  = NV50_DMA_CONF0_PRIV_US;
-			dmaobj->conf0 |= NV50_DMA_CONF0_PART_256;
-			dmaobj->conf0 |= NV50_DMA_CONF0_COMP_NONE;
-			dmaobj->conf0 |= NV50_DMA_CONF0_TYPE_LINEAR;
+			args->conf0  = NV50_DMA_CONF0_PRIV_US;
+			args->conf0 |= NV50_DMA_CONF0_PART_256;
+			args->conf0 |= NV50_DMA_CONF0_COMP_NONE;
+			args->conf0 |= NV50_DMA_CONF0_TYPE_LINEAR;
 		}
 	}
 
-	flags0 |= (dmaobj->conf0 & NV50_DMA_CONF0_COMP) << 22;
-	flags0 |= (dmaobj->conf0 & NV50_DMA_CONF0_TYPE) << 22;
-	flags0 |= (dmaobj->conf0 & NV50_DMA_CONF0_PRIV);
-	flags5 |= (dmaobj->conf0 & NV50_DMA_CONF0_PART);
+	priv->flags0 |= (args->conf0 & NV50_DMA_CONF0_COMP) << 22;
+	priv->flags0 |= (args->conf0 & NV50_DMA_CONF0_TYPE) << 22;
+	priv->flags0 |= (args->conf0 & NV50_DMA_CONF0_PRIV);
+	priv->flags5 |= (args->conf0 & NV50_DMA_CONF0_PART);
 
-	switch (dmaobj->target) {
+	switch (priv->base.target) {
 	case NV_MEM_TARGET_VM:
-		flags0 |= 0x00000000;
+		priv->flags0 |= 0x00000000;
 		break;
 	case NV_MEM_TARGET_VRAM:
-		flags0 |= 0x00010000;
+		priv->flags0 |= 0x00010000;
 		break;
 	case NV_MEM_TARGET_PCI:
-		flags0 |= 0x00020000;
+		priv->flags0 |= 0x00020000;
 		break;
 	case NV_MEM_TARGET_PCI_NOSNOOP:
-		flags0 |= 0x00030000;
+		priv->flags0 |= 0x00030000;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-	switch (dmaobj->access) {
+	switch (priv->base.access) {
 	case NV_MEM_ACCESS_VM:
 		break;
 	case NV_MEM_ACCESS_RO:
-		flags0 |= 0x00040000;
+		priv->flags0 |= 0x00040000;
 		break;
 	case NV_MEM_ACCESS_WO:
 	case NV_MEM_ACCESS_RW:
-		flags0 |= 0x00080000;
+		priv->flags0 |= 0x00080000;
 		break;
+	default:
+		return -EINVAL;
 	}
 
-	ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
-	if (ret == 0) {
-		nv_wo32(*pgpuobj, 0x00, flags0);
-		nv_wo32(*pgpuobj, 0x04, lower_32_bits(dmaobj->limit));
-		nv_wo32(*pgpuobj, 0x08, lower_32_bits(dmaobj->start));
-		nv_wo32(*pgpuobj, 0x0c, upper_32_bits(dmaobj->limit) << 24 |
-					upper_32_bits(dmaobj->start));
-		nv_wo32(*pgpuobj, 0x10, 0x00000000);
-		nv_wo32(*pgpuobj, 0x14, flags5);
-	}
-
-	return ret;
+	return dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject);
 }
 
+static struct nouveau_ofuncs
+nv50_dmaobj_ofuncs = {
+	.ctor =  nv50_dmaobj_ctor,
+	.dtor = _nvkm_dmaobj_dtor,
+	.init = _nvkm_dmaobj_init,
+	.fini = _nvkm_dmaobj_fini,
+};
+
+static struct nouveau_oclass
+nv50_dmaeng_sclass[] = {
+	{ NV_DMA_FROM_MEMORY_CLASS, &nv50_dmaobj_ofuncs },
+	{ NV_DMA_TO_MEMORY_CLASS, &nv50_dmaobj_ofuncs },
+	{ NV_DMA_IN_MEMORY_CLASS, &nv50_dmaobj_ofuncs },
+	{}
+};
+
 struct nouveau_oclass *
 nv50_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
 	.base.handle = NV_ENGINE(DMAOBJ, 0x50),
@@ -137,5 +180,6 @@ nv50_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
 		.init = _nvkm_dmaeng_init,
 		.fini = _nvkm_dmaeng_fini,
 	},
+	.sclass = nv50_dmaeng_sclass,
 	.bind = nv50_dmaobj_bind,
 }.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c
index bd639de7fea0..c6753a08d3a4 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c
@@ -30,14 +30,18 @@
 
 #include "priv.h"
 
+struct nvc0_dmaobj_priv {
+	struct nouveau_dmaobj base;
+	u32 flags0;
+	u32 flags5;
+};
+
 static int
-nvc0_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
+nvc0_dmaobj_bind(struct nouveau_dmaobj *dmaobj,
 		 struct nouveau_object *parent,
-		 struct nouveau_dmaobj *dmaobj,
 		 struct nouveau_gpuobj **pgpuobj)
 {
-	u32 flags0 = nv_mclass(dmaobj);
-	u32 flags5 = 0x00000000;
+	struct nvc0_dmaobj_priv *priv = (void *)dmaobj;
 	int ret;
 
 	if (!nv_iclass(parent, NV_ENGCTX_CLASS)) {
@@ -52,64 +56,101 @@ nvc0_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
 	} else
 		return 0;
 
-	if (!(dmaobj->conf0 & NVC0_DMA_CONF0_ENABLE)) {
-		if (dmaobj->target == NV_MEM_TARGET_VM) {
-			dmaobj->conf0  = NVC0_DMA_CONF0_PRIV_VM;
-			dmaobj->conf0 |= NVC0_DMA_CONF0_TYPE_VM;
+	ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
+	if (ret == 0) {
+		nv_wo32(*pgpuobj, 0x00, priv->flags0 | nv_mclass(dmaobj));
+		nv_wo32(*pgpuobj, 0x04, lower_32_bits(priv->base.limit));
+		nv_wo32(*pgpuobj, 0x08, lower_32_bits(priv->base.start));
+		nv_wo32(*pgpuobj, 0x0c, upper_32_bits(priv->base.limit) << 24 |
+					upper_32_bits(priv->base.start));
+		nv_wo32(*pgpuobj, 0x10, 0x00000000);
+		nv_wo32(*pgpuobj, 0x14, priv->flags5);
+	}
+
+	return ret;
+}
+
+static int
+nvc0_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+		 struct nouveau_oclass *oclass, void *data, u32 size,
+		 struct nouveau_object **pobject)
+{
+	struct nouveau_dmaeng *dmaeng = (void *)engine;
+	struct nvc0_dmaobj_priv *priv;
+	union {
+		u32 conf0;
+	} *args;
+	int ret;
+
+	ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+	args = data;
+
+	if (!(args->conf0 & NVC0_DMA_CONF0_ENABLE)) {
+		if (priv->base.target == NV_MEM_TARGET_VM) {
+			args->conf0  = NVC0_DMA_CONF0_PRIV_VM;
+			args->conf0 |= NVC0_DMA_CONF0_TYPE_VM;
 		} else {
-			dmaobj->conf0  = NVC0_DMA_CONF0_PRIV_US;
-			dmaobj->conf0 |= NVC0_DMA_CONF0_TYPE_LINEAR;
-			dmaobj->conf0 |= 0x00020000;
+			args->conf0  = NVC0_DMA_CONF0_PRIV_US;
+			args->conf0 |= NVC0_DMA_CONF0_TYPE_LINEAR;
+			args->conf0 |= 0x00020000;
 		}
 	}
 
-	flags0 |= (dmaobj->conf0 & NVC0_DMA_CONF0_TYPE) << 22;
-	flags0 |= (dmaobj->conf0 & NVC0_DMA_CONF0_PRIV);
-	flags5 |= (dmaobj->conf0 & NVC0_DMA_CONF0_UNKN);
+	priv->flags0 |= (args->conf0 & NVC0_DMA_CONF0_TYPE) << 22;
+	priv->flags0 |= (args->conf0 & NVC0_DMA_CONF0_PRIV);
+	priv->flags5 |= (args->conf0 & NVC0_DMA_CONF0_UNKN);
 
-	switch (dmaobj->target) {
+	switch (priv->base.target) {
 	case NV_MEM_TARGET_VM:
-		flags0 |= 0x00000000;
+		priv->flags0 |= 0x00000000;
 		break;
 	case NV_MEM_TARGET_VRAM:
-		flags0 |= 0x00010000;
+		priv->flags0 |= 0x00010000;
 		break;
 	case NV_MEM_TARGET_PCI:
-		flags0 |= 0x00020000;
+		priv->flags0 |= 0x00020000;
 		break;
 	case NV_MEM_TARGET_PCI_NOSNOOP:
-		flags0 |= 0x00030000;
+		priv->flags0 |= 0x00030000;
 		break;
 	default:
 		return -EINVAL;
 	}
 
-	switch (dmaobj->access) {
+	switch (priv->base.access) {
 	case NV_MEM_ACCESS_VM:
 		break;
 	case NV_MEM_ACCESS_RO:
-		flags0 |= 0x00040000;
+		priv->flags0 |= 0x00040000;
 		break;
 	case NV_MEM_ACCESS_WO:
 	case NV_MEM_ACCESS_RW:
-		flags0 |= 0x00080000;
+		priv->flags0 |= 0x00080000;
 		break;
 	}
 
-	ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
-	if (ret == 0) {
-		nv_wo32(*pgpuobj, 0x00, flags0);
-		nv_wo32(*pgpuobj, 0x04, lower_32_bits(dmaobj->limit));
-		nv_wo32(*pgpuobj, 0x08, lower_32_bits(dmaobj->start));
-		nv_wo32(*pgpuobj, 0x0c, upper_32_bits(dmaobj->limit) << 24 |
-					upper_32_bits(dmaobj->start));
-		nv_wo32(*pgpuobj, 0x10, 0x00000000);
-		nv_wo32(*pgpuobj, 0x14, flags5);
-	}
-
-	return ret;
+	return dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject);
 }
 
+static struct nouveau_ofuncs
+nvc0_dmaobj_ofuncs = {
+	.ctor =  nvc0_dmaobj_ctor,
+	.dtor = _nvkm_dmaobj_dtor,
+	.init = _nvkm_dmaobj_init,
+	.fini = _nvkm_dmaobj_fini,
+};
+
+static struct nouveau_oclass
+nvc0_dmaeng_sclass[] = {
+	{ NV_DMA_FROM_MEMORY_CLASS, &nvc0_dmaobj_ofuncs },
+	{ NV_DMA_TO_MEMORY_CLASS, &nvc0_dmaobj_ofuncs },
+	{ NV_DMA_IN_MEMORY_CLASS, &nvc0_dmaobj_ofuncs },
+	{}
+};
+
 struct nouveau_oclass *
 nvc0_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
 	.base.handle = NV_ENGINE(DMAOBJ, 0xc0),
@@ -119,5 +160,6 @@ nvc0_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
 		.init = _nvkm_dmaeng_init,
 		.fini = _nvkm_dmaeng_fini,
 	},
+	.sclass = nvc0_dmaeng_sclass,
 	.bind = nvc0_dmaobj_bind,
 }.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c
index 461cc01a0d50..c4b9b8deed71 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c
@@ -30,17 +30,17 @@
 
 #include "priv.h"
 
-struct nvd0_dmaeng_priv {
-	struct nouveau_dmaeng base;
+struct nvd0_dmaobj_priv {
+	struct nouveau_dmaobj base;
+	u32 flags0;
 };
 
 static int
-nvd0_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
+nvd0_dmaobj_bind(struct nouveau_dmaobj *dmaobj,
 		 struct nouveau_object *parent,
-		 struct nouveau_dmaobj *dmaobj,
 		 struct nouveau_gpuobj **pgpuobj)
 {
-	u32 flags0 = 0x00000000;
+	struct nvd0_dmaobj_priv *priv = (void *)dmaobj;
 	int ret;
 
 	if (!nv_iclass(parent, NV_ENGCTX_CLASS)) {
@@ -64,41 +64,86 @@ nvd0_dmaobj_bind(struct nouveau_dmaeng *dmaeng,
 	} else
 		return 0;
 
-	if (!(dmaobj->conf0 & NVD0_DMA_CONF0_ENABLE)) {
-		if (dmaobj->target == NV_MEM_TARGET_VM) {
-			dmaobj->conf0 |= NVD0_DMA_CONF0_TYPE_VM;
-			dmaobj->conf0 |= NVD0_DMA_CONF0_PAGE_LP;
+	ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
+	if (ret == 0) {
+		nv_wo32(*pgpuobj, 0x00, priv->flags0);
+		nv_wo32(*pgpuobj, 0x04, priv->base.start >> 8);
+		nv_wo32(*pgpuobj, 0x08, priv->base.limit >> 8);
+		nv_wo32(*pgpuobj, 0x0c, 0x00000000);
+		nv_wo32(*pgpuobj, 0x10, 0x00000000);
+		nv_wo32(*pgpuobj, 0x14, 0x00000000);
+	}
+
+	return ret;
+}
+
+static int
+nvd0_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+		 struct nouveau_oclass *oclass, void *data, u32 size,
+		 struct nouveau_object **pobject)
+{
+	struct nouveau_dmaeng *dmaeng = (void *)engine;
+	struct nvd0_dmaobj_priv *priv;
+	union {
+		u32 conf0;
+	} *args;
+	int ret;
+
+	ret = nvkm_dmaobj_create(parent, engine, oclass, &data, &size, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+	args = data;
+
+	if (!(args->conf0 & NVD0_DMA_CONF0_ENABLE)) {
+		if (priv->base.target == NV_MEM_TARGET_VM) {
+			args->conf0 |= NVD0_DMA_CONF0_TYPE_VM;
+			args->conf0 |= NVD0_DMA_CONF0_PAGE_LP;
 		} else {
-			dmaobj->conf0 |= NVD0_DMA_CONF0_TYPE_LINEAR;
-			dmaobj->conf0 |= NVD0_DMA_CONF0_PAGE_SP;
+			args->conf0 |= NVD0_DMA_CONF0_TYPE_LINEAR;
+			args->conf0 |= NVD0_DMA_CONF0_PAGE_SP;
 		}
 	}
 
-	flags0 |= (dmaobj->conf0 & NVD0_DMA_CONF0_TYPE) << 20;
-	flags0 |= (dmaobj->conf0 & NVD0_DMA_CONF0_PAGE) >> 4;
+	priv->flags0 |= (args->conf0 & NVD0_DMA_CONF0_TYPE) << 20;
+	priv->flags0 |= (args->conf0 & NVD0_DMA_CONF0_PAGE) >> 4;
 
-	switch (dmaobj->target) {
+	switch (priv->base.target) {
 	case NV_MEM_TARGET_VRAM:
-		flags0 |= 0x00000009;
+		priv->flags0 |= 0x00000009;
+		break;
+	case NV_MEM_TARGET_VM:
+	case NV_MEM_TARGET_PCI:
+	case NV_MEM_TARGET_PCI_NOSNOOP:
+		/* XXX: don't currently know how to construct a real one
+		 *      of these.  we only use them to represent pushbufs
+		 *      on these chipsets, and the classes that use them
+		 *      deal with the target themselves.
+		 */
 		break;
 	default:
 		return -EINVAL;
-		break;
-	}
-
-	ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj);
-	if (ret == 0) {
-		nv_wo32(*pgpuobj, 0x00, flags0);
-		nv_wo32(*pgpuobj, 0x04, dmaobj->start >> 8);
-		nv_wo32(*pgpuobj, 0x08, dmaobj->limit >> 8);
-		nv_wo32(*pgpuobj, 0x0c, 0x00000000);
-		nv_wo32(*pgpuobj, 0x10, 0x00000000);
-		nv_wo32(*pgpuobj, 0x14, 0x00000000);
 	}
 
-	return ret;
+	return dmaeng->bind(&priv->base, nv_object(priv), (void *)pobject);
 }
 
+static struct nouveau_ofuncs
+nvd0_dmaobj_ofuncs = {
+	.ctor =  nvd0_dmaobj_ctor,
+	.dtor = _nvkm_dmaobj_dtor,
+	.init = _nvkm_dmaobj_init,
+	.fini = _nvkm_dmaobj_fini,
+};
+
+static struct nouveau_oclass
+nvd0_dmaeng_sclass[] = {
+	{ NV_DMA_FROM_MEMORY_CLASS, &nvd0_dmaobj_ofuncs },
+	{ NV_DMA_TO_MEMORY_CLASS, &nvd0_dmaobj_ofuncs },
+	{ NV_DMA_IN_MEMORY_CLASS, &nvd0_dmaobj_ofuncs },
+	{}
+};
+
 struct nouveau_oclass *
 nvd0_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
 	.base.handle = NV_ENGINE(DMAOBJ, 0xd0),
@@ -108,5 +153,6 @@ nvd0_dmaeng_oclass = &(struct nvkm_dmaeng_impl) {
 		.init = _nvkm_dmaeng_init,
 		.fini = _nvkm_dmaeng_fini,
 	},
+	.sclass = nvd0_dmaeng_sclass,
 	.bind = nvd0_dmaobj_bind,
 }.base;
diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/priv.h b/drivers/gpu/drm/nouveau/core/engine/dmaobj/priv.h
index f9aeea5564d9..36f743866937 100644
--- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/priv.h
+++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/priv.h
@@ -3,6 +3,16 @@
 
 #include <engine/dmaobj.h>
 
+#define nvkm_dmaobj_create(p,e,c,pa,sa,d)                                      \
+	nvkm_dmaobj_create_((p), (e), (c), (pa), (sa), sizeof(**d), (void **)d)
+
+int nvkm_dmaobj_create_(struct nouveau_object *, struct nouveau_object *,
+			struct nouveau_oclass *, void **, u32 *,
+			int, void **);
+#define _nvkm_dmaobj_dtor nouveau_object_destroy
+#define _nvkm_dmaobj_init nouveau_object_init
+#define _nvkm_dmaobj_fini nouveau_object_fini
+
 int _nvkm_dmaeng_ctor(struct nouveau_object *, struct nouveau_object *,
 		      struct nouveau_oclass *, void *, u32,
 		      struct nouveau_object **);
@@ -12,8 +22,9 @@ int _nvkm_dmaeng_ctor(struct nouveau_object *, struct nouveau_object *,
 
 struct nvkm_dmaeng_impl {
 	struct nouveau_oclass base;
-	int (*bind)(struct nouveau_dmaeng *, struct nouveau_object *,
-		    struct nouveau_dmaobj *, struct nouveau_gpuobj **);
+	struct nouveau_oclass *sclass;
+	int (*bind)(struct nouveau_dmaobj *, struct nouveau_object *,
+		    struct nouveau_gpuobj **);
 };
 
 #endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
index 812d85dc9f52..2f81cbc4b472 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
@@ -83,7 +83,7 @@ nouveau_fifo_channel_create_(struct nouveau_object *parent,
 		return -EINVAL;
 	}
 
-	ret = dmaeng->bind(dmaeng, parent, chan->pushdma, &chan->pushgpu);
+	ret = dmaeng->bind(chan->pushdma, parent, &chan->pushgpu);
 	if (ret)
 		return ret;
 
diff --git a/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h b/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h
index e09df667094b..f060b063d7de 100644
--- a/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h
+++ b/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h
@@ -19,9 +19,8 @@ struct nouveau_dmaeng {
 	struct nouveau_engine base;
 
 	/* creates a "physical" dma object from a struct nouveau_dmaobj */
-	int (*bind)(struct nouveau_dmaeng *dmaeng,
+	int (*bind)(struct nouveau_dmaobj *dmaobj,
 		    struct nouveau_object *parent,
-		    struct nouveau_dmaobj *dmaobj,
 		    struct nouveau_gpuobj **);
 };