drm/nouveau: port all engines to new engine module format

This is a HUGE commit, but it's not nearly as bad as it looks - any problems
can be isolated to a particular chipset and engine combination.  It was
simply too difficult to port each one at a time, the compat layers are
*already* ridiculous.

Most of the changes here are simply to the glue, the process for each of the
engine modules was to start with a standard skeleton and copy+paste the old
code into the appropriate places, fixing up variable names etc as needed.

v2: Marcin Slusarz <marcin.slusarz@gmail.com>
- fix find/replace bug in license header

v3: Ben Skeggs <bskeggs@redhat.com>
- bump indirect pushbuf size to 8KiB, 4KiB barely enough for userspace and
  left no space for kernel's requirements during GEM pushbuf submission.
- fix duplicate assignments noticed by clang

v4: Marcin Slusarz <marcin.slusarz@gmail.com>
- add sparse annotations to nv04_fifo_pause/nv04_fifo_start
- use ioread32_native/iowrite32_native for fifo control registers

v5: Ben Skeggs <bskeggs@redhat.com>
- rebase on v3.6-rc4, modified to keep copy engine fix intact
- nv10/fence: unmap fence bo before destroying
- fixed fermi regression when using nvidia gr fuc
- fixed typo in supported dma_mask checking

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
new file mode 100644
index 0000000..edeb76e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * 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.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/object.h>
+#include <core/handle.h>
+
+#include <engine/dmaobj.h>
+#include <engine/fifo.h>
+
+int
+nouveau_fifo_channel_create_(struct nouveau_object *parent,
+			     struct nouveau_object *engine,
+			     struct nouveau_oclass *oclass,
+			     int bar, u32 addr, u32 size, u32 pushbuf,
+			     u32 engmask, int len, void **ptr)
+{
+	struct nouveau_device *device = nv_device(engine);
+	struct nouveau_fifo *priv = (void *)engine;
+	struct nouveau_fifo_chan *chan;
+	struct nouveau_dmaeng *dmaeng;
+	unsigned long flags;
+	int ret;
+
+	/* create base object class */
+	ret = nouveau_namedb_create_(parent, engine, oclass, 0, NULL,
+				     engmask, len, ptr);
+	chan = *ptr;
+	if (ret)
+		return ret;
+
+	/* validate dma object representing push buffer */
+	chan->pushdma = (void *)nouveau_handle_ref(parent, pushbuf);
+	if (!chan->pushdma)
+		return -ENOENT;
+
+	dmaeng = (void *)chan->pushdma->base.engine;
+	switch (chan->pushdma->base.oclass->handle) {
+	case 0x0002:
+	case 0x003d:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (dmaeng->bind) {
+		ret = dmaeng->bind(dmaeng, parent, chan->pushdma, &chan->pushgpu);
+		if (ret)
+			return ret;
+	}
+
+	/* find a free fifo channel */
+	spin_lock_irqsave(&priv->lock, flags);
+	for (chan->chid = priv->min; chan->chid < priv->max; chan->chid++) {
+		if (!priv->channel[chan->chid]) {
+			priv->channel[chan->chid] = nv_object(chan);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	if (chan->chid == priv->max) {
+		nv_error(priv, "no free channels\n");
+		return -ENOSPC;
+	}
+
+	/* map fifo control registers */
+	chan->user = ioremap(pci_resource_start(device->pdev, bar) + addr +
+			     (chan->chid * size), size);
+	if (!chan->user)
+		return -EFAULT;
+
+	chan->size = size;
+	return 0;
+}
+
+void
+nouveau_fifo_channel_destroy(struct nouveau_fifo_chan *chan)
+{
+	struct nouveau_fifo *priv = (void *)nv_object(chan)->engine;
+	unsigned long flags;
+
+	iounmap(chan->user);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	priv->channel[chan->chid] = NULL;
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	nouveau_gpuobj_ref(NULL, &chan->pushgpu);
+	nouveau_object_ref(NULL, (struct nouveau_object **)&chan->pushdma);
+	nouveau_namedb_destroy(&chan->base);
+}
+
+void
+_nouveau_fifo_channel_dtor(struct nouveau_object *object)
+{
+	struct nouveau_fifo_chan *chan = (void *)object;
+	nouveau_fifo_channel_destroy(chan);
+}
+
+u32
+_nouveau_fifo_channel_rd32(struct nouveau_object *object, u32 addr)
+{
+	struct nouveau_fifo_chan *chan = (void *)object;
+	return ioread32_native(chan->user + addr);
+}
+
+void
+_nouveau_fifo_channel_wr32(struct nouveau_object *object, u32 addr, u32 data)
+{
+	struct nouveau_fifo_chan *chan = (void *)object;
+	iowrite32_native(data, chan->user + addr);
+}
+
+void
+nouveau_fifo_destroy(struct nouveau_fifo *priv)
+{
+	kfree(priv->channel);
+	nouveau_engine_destroy(&priv->base);
+}
+
+int
+nouveau_fifo_create_(struct nouveau_object *parent,
+		     struct nouveau_object *engine,
+		     struct nouveau_oclass *oclass,
+		     int min, int max, int length, void **pobject)
+{
+	struct nouveau_fifo *priv;
+	int ret;
+
+	ret = nouveau_engine_create_(parent, engine, oclass, true, "PFIFO",
+				     "fifo", length, pobject);
+	priv = *pobject;
+	if (ret)
+		return ret;
+
+	priv->min = min;
+	priv->max = max;
+	priv->channel = kzalloc(sizeof(*priv->channel) * (max + 1), GFP_KERNEL);
+	if (!priv->channel)
+		return -ENOMEM;
+
+	spin_lock_init(&priv->lock);
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
index 6ab7eb0..8b7513f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
@@ -1,44 +1,45 @@
 /*
- * Copyright (C) 2012 Ben Skeggs.
- * All Rights Reserved.
+ * Copyright 2012 Red Hat Inc.
  *
- * 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:
+ * 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 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 OWNER(S) 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.
+ * 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.
  *
+ * Authors: Ben Skeggs
  */
 
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <engine/fifo.h>
-#include "nouveau_util.h"
+#include <core/os.h>
+#include <core/class.h>
+#include <core/engctx.h>
+#include <core/namedb.h>
+#include <core/handle.h>
 #include <core/ramht.h>
-#include "nouveau_software.h"
 
-static struct ramfc_desc {
-	unsigned bits:6;
-	unsigned ctxs:5;
-	unsigned ctxp:8;
-	unsigned regs:5;
-	unsigned regp;
-} nv04_ramfc[] = {
+#include <subdev/instmem.h>
+#include <subdev/instmem/nv04.h>
+#include <subdev/timer.h>
+#include <subdev/fb.h>
+
+#include <engine/fifo.h>
+
+#include "nv04.h"
+
+static struct ramfc_desc
+nv04_ramfc[] = {
 	{ 32,  0, 0x00,  0, NV04_PFIFO_CACHE1_DMA_PUT },
 	{ 32,  0, 0x04,  0, NV04_PFIFO_CACHE1_DMA_GET },
 	{ 16,  0, 0x08,  0, NV04_PFIFO_CACHE1_DMA_INSTANCE },
@@ -50,259 +51,284 @@
 	{}
 };
 
-struct nv04_fifo_priv {
-	struct nouveau_fifo_priv base;
-	struct ramfc_desc *ramfc_desc;
-	struct nouveau_gpuobj *ramro;
-	struct nouveau_gpuobj *ramfc;
-};
+/*******************************************************************************
+ * FIFO channel objects
+ ******************************************************************************/
 
-struct nv04_fifo_chan {
-	struct nouveau_fifo_chan base;
-	u32 ramfc;
-};
-
-bool
-nv04_fifo_cache_pull(struct drm_device *dev, bool enable)
+int
+nv04_fifo_object_attach(struct nouveau_object *parent,
+			struct nouveau_object *object, u32 handle)
 {
-	int pull = nv_mask(dev, NV04_PFIFO_CACHE1_PULL0, 1, enable);
+	struct nv04_fifo_priv *priv = (void *)parent->engine;
+	struct nv04_fifo_chan *chan = (void *)parent;
+	u32 context, chid = chan->base.chid;
+	int ret;
 
-	if (!enable) {
-		/* In some cases the PFIFO puller may be left in an
-		 * inconsistent state if you try to stop it when it's
-		 * busy translating handles. Sometimes you get a
-		 * PFIFO_CACHE_ERROR, sometimes it just fails silently
-		 * sending incorrect instance offsets to PGRAPH after
-		 * it's started up again. To avoid the latter we
-		 * invalidate the most recently calculated instance.
-		 */
-		if (!nv_wait(dev, NV04_PFIFO_CACHE1_PULL0,
-				  NV04_PFIFO_CACHE1_PULL0_HASH_BUSY, 0))
-			NV_ERROR(dev, "Timeout idling the PFIFO puller.\n");
+	if (nv_iclass(object, NV_GPUOBJ_CLASS))
+		context = nv_gpuobj(object)->addr >> 4;
+	else
+		context = 0x00000004; /* just non-zero */
 
-		if (nv_rd32(dev, NV04_PFIFO_CACHE1_PULL0) &
-				 NV04_PFIFO_CACHE1_PULL0_HASH_FAILED)
-			nv_wr32(dev, NV03_PFIFO_INTR_0,
-				     NV_PFIFO_INTR_CACHE_ERROR);
-
-		nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0);
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_DMAOBJ:
+	case NVDEV_ENGINE_SW:
+		context |= 0x00000000;
+		break;
+	case NVDEV_ENGINE_GR:
+		context |= 0x00010000;
+		break;
+	case NVDEV_ENGINE_MPEG:
+		context |= 0x00020000;
+		break;
+	default:
+		return -EINVAL;
 	}
 
-	return pull & 1;
+	context |= 0x80000000; /* valid */
+	context |= chid << 24;
+
+	mutex_lock(&nv_subdev(priv)->mutex);
+	ret = nouveau_ramht_insert(priv->ramht, chid, handle, context);
+	mutex_unlock(&nv_subdev(priv)->mutex);
+	return ret;
+}
+
+void
+nv04_fifo_object_detach(struct nouveau_object *parent, int cookie)
+{
+	struct nv04_fifo_priv *priv = (void *)parent->engine;
+	mutex_lock(&nv_subdev(priv)->mutex);
+	nouveau_ramht_remove(priv->ramht, cookie);
+	mutex_unlock(&nv_subdev(priv)->mutex);
 }
 
 static int
-nv04_fifo_context_new(struct nouveau_channel *chan, int engine)
+nv04_fifo_chan_ctor(struct nouveau_object *parent,
+		    struct nouveau_object *engine,
+		    struct nouveau_oclass *oclass, void *data, u32 size,
+		    struct nouveau_object **pobject)
 {
-	struct drm_device *dev = chan->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv04_fifo_priv *priv = nv_engine(dev, engine);
-	struct nv04_fifo_chan *fctx;
-	unsigned long flags;
+	struct nv04_fifo_priv *priv = (void *)engine;
+	struct nv04_fifo_chan *chan;
+	struct nv_channel_dma_class *args = data;
 	int ret;
 
-	fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL);
-	if (!fctx)
-		return -ENOMEM;
+	if (size < sizeof(*args))
+		return -EINVAL;
 
-	fctx->ramfc = chan->id * 32;
+	ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0x800000,
+					  0x10000, args->pushbuf,
+					  (1 << NVDEV_ENGINE_DMAOBJ) |
+					  (1 << NVDEV_ENGINE_SW) |
+					  (1 << NVDEV_ENGINE_GR), &chan);
+	*pobject = nv_object(chan);
+	if (ret)
+		return ret;
 
-	/* map channel control registers */
-	chan->user = ioremap(pci_resource_start(dev->pdev, 0) +
-			     NV03_USER(chan->id), PAGE_SIZE);
-	if (!chan->user) {
-		ret = -ENOMEM;
-		goto error;
-	}
+	nv_parent(chan)->object_attach = nv04_fifo_object_attach;
+	nv_parent(chan)->object_detach = nv04_fifo_object_detach;
+	chan->ramfc = chan->base.chid * 32;
 
-	/* initialise default fifo context */
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x00, chan->pushbuf_base);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x04, chan->pushbuf_base);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x08, chan->pushbuf->addr >> 4);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x10,
+	nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->offset);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->offset);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x08, chan->base.pushgpu->addr >> 4);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x10,
 			     NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
 			     NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
 #ifdef __BIG_ENDIAN
 			     NV_PFIFO_CACHE1_BIG_ENDIAN |
 #endif
 			     NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8);
-
-	/* enable dma mode on the channel */
-	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv_mask(dev, NV04_PFIFO_MODE, (1 << chan->id), (1 << chan->id));
-	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
-
-error:
-	if (ret)
-		priv->base.base.context_del(chan, engine);
-	return ret;
+	return 0;
 }
 
 void
-nv04_fifo_context_del(struct nouveau_channel *chan, int engine)
+nv04_fifo_chan_dtor(struct nouveau_object *object)
 {
-	struct drm_device *dev = chan->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv04_fifo_priv *priv = nv_engine(chan->dev, engine);
-	struct nv04_fifo_chan *fctx = chan->engctx[engine];
+	struct nv04_fifo_priv *priv = (void *)object->engine;
+	struct nv04_fifo_chan *chan = (void *)object;
 	struct ramfc_desc *c = priv->ramfc_desc;
-	unsigned long flags;
-	int chid;
 
-	/* prevent fifo context switches */
-	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv_wr32(dev, NV03_PFIFO_CACHES, 0);
+	do {
+		nv_wo32(priv->ramfc, chan->ramfc + c->ctxp, 0x00000000);
+	} while ((++c)->bits);
 
-	/* if this channel is active, replace it with a null context */
-	chid = nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) & priv->base.channels;
-	if (chid == chan->id) {
-		nv_mask(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0x00000001, 0);
-		nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 0);
-		nv_mask(dev, NV04_PFIFO_CACHE1_PULL0, 0x00000001, 0);
-
-		do {
-			u32 mask = ((1ULL << c->bits) - 1) << c->regs;
-			nv_mask(dev, c->regp, mask, 0x00000000);
-			nv_wo32(priv->ramfc, fctx->ramfc + c->ctxp, 0x00000000);
-		} while ((++c)->bits);
-
-		nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
-		nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
-		nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, priv->base.channels);
-		nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1);
-		nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
-	}
-
-	/* restore normal operation, after disabling dma mode */
-	nv_mask(dev, NV04_PFIFO_MODE, 1 << chan->id, 0);
-	nv_wr32(dev, NV03_PFIFO_CACHES, 1);
-	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
-
-	/* clean up */
-	nouveau_gpuobj_ref(NULL, &chan->ramfc); /*XXX: nv40 */
-	if (chan->user) {
-		iounmap(chan->user);
-		chan->user = NULL;
-	}
+	nouveau_fifo_channel_destroy(&chan->base);
 }
 
 int
-nv04_fifo_init(struct drm_device *dev, int engine)
+nv04_fifo_chan_init(struct nouveau_object *object)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv04_fifo_priv *priv = nv_engine(dev, engine);
-	int i;
+	struct nv04_fifo_priv *priv = (void *)object->engine;
+	struct nv04_fifo_chan *chan = (void *)object;
+	u32 mask = 1 << chan->base.chid;
+	unsigned long flags;
+	int ret;
 
-	nv_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, 0);
-	nv_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, NV_PMC_ENABLE_PFIFO);
+	ret = nouveau_fifo_channel_init(&chan->base);
+	if (ret)
+		return ret;
 
-	nv_wr32(dev, NV04_PFIFO_DELAY_0, 0x000000ff);
-	nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, 0x0101ffff);
-
-	nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
-				       ((dev_priv->ramht->bits - 9) << 16) |
-				       (dev_priv->ramht->gpuobj->addr >> 8));
-	nv_wr32(dev, NV03_PFIFO_RAMRO, priv->ramro->addr >> 8);
-	nv_wr32(dev, NV03_PFIFO_RAMFC, priv->ramfc->addr >> 8);
-
-	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, priv->base.channels);
-
-	nv_wr32(dev, NV03_PFIFO_INTR_0, 0xffffffff);
-	nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xffffffff);
-
-	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1);
-	nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
-	nv_wr32(dev, NV03_PFIFO_CACHES, 1);
-
-	for (i = 0; i < priv->base.channels; i++) {
-		if (dev_priv->channels.ptr[i])
-			nv_mask(dev, NV04_PFIFO_MODE, (1 << i), (1 << i));
-	}
-
+	spin_lock_irqsave(&priv->base.lock, flags);
+	nv_mask(priv, NV04_PFIFO_MODE, mask, mask);
+	spin_unlock_irqrestore(&priv->base.lock, flags);
 	return 0;
 }
 
 int
-nv04_fifo_fini(struct drm_device *dev, int engine, bool suspend)
+nv04_fifo_chan_fini(struct nouveau_object *object, bool suspend)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv04_fifo_priv *priv = nv_engine(dev, engine);
-	struct nouveau_channel *chan;
-	int chid;
+	struct nv04_fifo_priv *priv = (void *)object->engine;
+	struct nv04_fifo_chan *chan = (void *)object;
+	struct nouveau_gpuobj *fctx = priv->ramfc;
+	struct ramfc_desc *c;
+	unsigned long flags;
+	u32 data = chan->ramfc;
+	u32 chid;
 
-	/* prevent context switches and halt fifo operation */
-	nv_wr32(dev, NV03_PFIFO_CACHES, 0);
-	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
-	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 0);
-	nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 0);
+	/* prevent fifo context switches */
+	spin_lock_irqsave(&priv->base.lock, flags);
+	nv_wr32(priv, NV03_PFIFO_CACHES, 0);
 
-	/* store current fifo context in ramfc */
-	chid = nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) & priv->base.channels;
-	chan = dev_priv->channels.ptr[chid];
-	if (suspend && chid != priv->base.channels && chan) {
-		struct nv04_fifo_chan *fctx = chan->engctx[engine];
-		struct nouveau_gpuobj *ctx = priv->ramfc;
-		struct ramfc_desc *c = priv->ramfc_desc;
+	/* if this channel is active, replace it with a null context */
+	chid = nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH1) & priv->base.max;
+	if (chid == chan->base.chid) {
+		nv_mask(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0x00000001, 0);
+		nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, 0);
+		nv_mask(priv, NV04_PFIFO_CACHE1_PULL0, 0x00000001, 0);
+
+		c = priv->ramfc_desc;
 		do {
 			u32 rm = ((1ULL << c->bits) - 1) << c->regs;
 			u32 cm = ((1ULL << c->bits) - 1) << c->ctxs;
-			u32 rv = (nv_rd32(dev, c->regp) &  rm) >> c->regs;
-			u32 cv = (nv_ro32(ctx, c->ctxp + fctx->ramfc) & ~cm);
-			nv_wo32(ctx, c->ctxp + fctx->ramfc, cv | (rv << c->ctxs));
+			u32 rv = (nv_rd32(priv, c->regp) &  rm) >> c->regs;
+			u32 cv = (nv_ro32(fctx, c->ctxp + data) & ~cm);
+			nv_wo32(fctx, c->ctxp + data, cv | (rv << c->ctxs));
 		} while ((++c)->bits);
+
+		c = priv->ramfc_desc;
+		do {
+			nv_wr32(priv, c->regp, 0x00000000);
+		} while ((++c)->bits);
+
+		nv_wr32(priv, NV03_PFIFO_CACHE1_GET, 0);
+		nv_wr32(priv, NV03_PFIFO_CACHE1_PUT, 0);
+		nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH1, priv->base.max);
+		nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, 1);
+		nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
 	}
 
-	nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0x00000000);
+	/* restore normal operation, after disabling dma mode */
+	nv_mask(priv, NV04_PFIFO_MODE, 1 << chan->base.chid, 0);
+	nv_wr32(priv, NV03_PFIFO_CACHES, 1);
+	spin_unlock_irqrestore(&priv->base.lock, flags);
+
+	return nouveau_fifo_channel_fini(&chan->base, suspend);
+}
+
+static struct nouveau_ofuncs
+nv04_fifo_ofuncs = {
+	.ctor = nv04_fifo_chan_ctor,
+	.dtor = nv04_fifo_chan_dtor,
+	.init = nv04_fifo_chan_init,
+	.fini = nv04_fifo_chan_fini,
+	.rd32 = _nouveau_fifo_channel_rd32,
+	.wr32 = _nouveau_fifo_channel_wr32,
+};
+
+static struct nouveau_oclass
+nv04_fifo_sclass[] = {
+	{ 0x006e, &nv04_fifo_ofuncs },
+	{}
+};
+
+/*******************************************************************************
+ * FIFO context - basically just the instmem reserved for the channel
+ ******************************************************************************/
+
+int
+nv04_fifo_context_ctor(struct nouveau_object *parent,
+		       struct nouveau_object *engine,
+		       struct nouveau_oclass *oclass, void *data, u32 size,
+		       struct nouveau_object **pobject)
+{
+	struct nv04_fifo_base *base;
+	int ret;
+
+	ret = nouveau_fifo_context_create(parent, engine, oclass, NULL, 0x1000,
+				          0x1000, NVOBJ_FLAG_HEAP, &base);
+	*pobject = nv_object(base);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
-static bool
-nouveau_fifo_swmthd(struct drm_device *dev, u32 chid, u32 addr, u32 data)
+static struct nouveau_oclass
+nv04_fifo_cclass = {
+	.handle = NV_ENGCTX(FIFO, 0x04),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv04_fifo_context_ctor,
+		.dtor = _nouveau_fifo_context_dtor,
+		.init = _nouveau_fifo_context_init,
+		.fini = _nouveau_fifo_context_fini,
+		.rd32 = _nouveau_fifo_context_rd32,
+		.wr32 = _nouveau_fifo_context_wr32,
+	},
+};
+
+/*******************************************************************************
+ * PFIFO engine
+ ******************************************************************************/
+
+void
+nv04_fifo_pause(struct nouveau_fifo *pfifo, unsigned long *pflags)
+__acquires(priv->base.lock)
 {
-	struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO);
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_channel *chan = NULL;
-	struct nouveau_gpuobj *obj;
+	struct nv04_fifo_priv *priv = (void *)pfifo;
 	unsigned long flags;
-	const int subc = (addr >> 13) & 0x7;
-	const int mthd = addr & 0x1ffc;
-	bool handled = false;
-	u32 engine;
 
-	spin_lock_irqsave(&dev_priv->channels.lock, flags);
-	if (likely(chid >= 0 && chid < pfifo->channels))
-		chan = dev_priv->channels.ptr[chid];
-	if (unlikely(!chan))
-		goto out;
+	spin_lock_irqsave(&priv->base.lock, flags);
+	*pflags = flags;
 
-	switch (mthd) {
-	case 0x0000: /* bind object to subchannel */
-		obj = nouveau_ramht_find(chan, data);
-		if (unlikely(!obj || obj->engine != NVOBJ_ENGINE_SW))
-			break;
+	nv_wr32(priv, NV03_PFIFO_CACHES, 0x00000000);
+	nv_mask(priv, NV04_PFIFO_CACHE1_PULL0, 0x00000001, 0x00000000);
 
-		engine = 0x0000000f << (subc * 4);
+	/* in some cases the puller may be left in an inconsistent state
+	 * if you try to stop it while it's busy translating handles.
+	 * sometimes you get a CACHE_ERROR, sometimes it just fails
+	 * silently; sending incorrect instance offsets to PGRAPH after
+	 * it's started up again.
+	 *
+	 * to avoid this, we invalidate the most recently calculated
+	 * instance.
+	 */
+	if (!nv_wait(priv, NV04_PFIFO_CACHE1_PULL0,
+			   NV04_PFIFO_CACHE1_PULL0_HASH_BUSY, 0x00000000))
+		nv_warn(priv, "timeout idling puller\n");
 
-		nv_mask(dev, NV04_PFIFO_CACHE1_ENGINE, engine, 0x00000000);
-		handled = true;
-		break;
-	default:
-		engine = nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE);
-		if (unlikely(((engine >> (subc * 4)) & 0xf) != 0))
-			break;
+	if (nv_rd32(priv, NV04_PFIFO_CACHE1_PULL0) &
+			  NV04_PFIFO_CACHE1_PULL0_HASH_FAILED)
+		nv_wr32(priv, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_CACHE_ERROR);
 
-		if (!nouveau_gpuobj_mthd_call(chan, nouveau_software_class(dev),
-					      mthd, data))
-			handled = true;
-		break;
-	}
-
-out:
-	spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
-	return handled;
+	nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0x00000000);
 }
 
-static const char *nv_dma_state_err(u32 state)
+void
+nv04_fifo_start(struct nouveau_fifo *pfifo, unsigned long *pflags)
+__releases(priv->base.lock)
+{
+	struct nv04_fifo_priv *priv = (void *)pfifo;
+	unsigned long flags = *pflags;
+
+	nv_mask(priv, NV04_PFIFO_CACHE1_PULL0, 0x00000001, 0x00000001);
+	nv_wr32(priv, NV03_PFIFO_CACHES, 0x00000001);
+
+	spin_unlock_irqrestore(&priv->base.lock, flags);
+}
+
+static const char *
+nv_dma_state_err(u32 state)
 {
 	static const char * const desc[] = {
 		"NONE", "CALL_SUBR_ACTIVE", "INVALID_MTHD", "RET_SUBR_INACTIVE",
@@ -311,22 +337,74 @@
 	return desc[(state >> 29) & 0x7];
 }
 
-void
-nv04_fifo_isr(struct drm_device *dev)
+static bool
+nv04_fifo_swmthd(struct nv04_fifo_priv *priv, u32 chid, u32 addr, u32 data)
 {
-	struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO);
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_fifo_chan *chan = NULL;
+	struct nouveau_handle *bind;
+	const int subc = (addr >> 13) & 0x7;
+	const int mthd = addr & 0x1ffc;
+	bool handled = false;
+	unsigned long flags;
+	u32 engine;
+
+	spin_lock_irqsave(&priv->base.lock, flags);
+	if (likely(chid >= priv->base.min && chid <= priv->base.max))
+		chan = (void *)priv->base.channel[chid];
+	if (unlikely(!chan))
+		goto out;
+
+	switch (mthd) {
+	case 0x0000:
+		bind = nouveau_namedb_get(nv_namedb(chan), data);
+		if (unlikely(!bind))
+			break;
+
+		if (nv_engidx(bind->object->engine) == NVDEV_ENGINE_SW) {
+			engine = 0x0000000f << (subc * 4);
+			chan->subc[subc] = data;
+			handled = true;
+
+			nv_mask(priv, NV04_PFIFO_CACHE1_ENGINE, engine, 0);
+		}
+
+		nouveau_namedb_put(bind);
+		break;
+	default:
+		engine = nv_rd32(priv, NV04_PFIFO_CACHE1_ENGINE);
+		if (unlikely(((engine >> (subc * 4)) & 0xf) != 0))
+			break;
+
+		bind = nouveau_namedb_get(nv_namedb(chan), chan->subc[subc]);
+		if (likely(bind)) {
+			if (!nv_call(bind->object, mthd, data))
+				handled = true;
+			nouveau_namedb_put(bind);
+		}
+		break;
+	}
+
+out:
+	spin_unlock_irqrestore(&priv->base.lock, flags);
+	return handled;
+}
+
+void
+nv04_fifo_intr(struct nouveau_subdev *subdev)
+{
+	struct nouveau_device *device = nv_device(subdev);
+	struct nv04_fifo_priv *priv = (void *)subdev;
 	uint32_t status, reassign;
 	int cnt = 0;
 
-	reassign = nv_rd32(dev, NV03_PFIFO_CACHES) & 1;
-	while ((status = nv_rd32(dev, NV03_PFIFO_INTR_0)) && (cnt++ < 100)) {
+	reassign = nv_rd32(priv, NV03_PFIFO_CACHES) & 1;
+	while ((status = nv_rd32(priv, NV03_PFIFO_INTR_0)) && (cnt++ < 100)) {
 		uint32_t chid, get;
 
-		nv_wr32(dev, NV03_PFIFO_CACHES, 0);
+		nv_wr32(priv, NV03_PFIFO_CACHES, 0);
 
-		chid = nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) & pfifo->channels;
-		get  = nv_rd32(dev, NV03_PFIFO_CACHE1_GET);
+		chid = nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH1) & priv->base.max;
+		get  = nv_rd32(priv, NV03_PFIFO_CACHE1_GET);
 
 		if (status & NV_PFIFO_INTR_CACHE_ERROR) {
 			uint32_t mthd, data;
@@ -340,86 +418,85 @@
 			 */
 			ptr = (get & 0x7ff) >> 2;
 
-			if (dev_priv->card_type < NV_40) {
-				mthd = nv_rd32(dev,
+			if (device->card_type < NV_40) {
+				mthd = nv_rd32(priv,
 					NV04_PFIFO_CACHE1_METHOD(ptr));
-				data = nv_rd32(dev,
+				data = nv_rd32(priv,
 					NV04_PFIFO_CACHE1_DATA(ptr));
 			} else {
-				mthd = nv_rd32(dev,
+				mthd = nv_rd32(priv,
 					NV40_PFIFO_CACHE1_METHOD(ptr));
-				data = nv_rd32(dev,
+				data = nv_rd32(priv,
 					NV40_PFIFO_CACHE1_DATA(ptr));
 			}
 
-			if (!nouveau_fifo_swmthd(dev, chid, mthd, data)) {
-				NV_INFO(dev, "PFIFO_CACHE_ERROR - Ch %d/%d "
-					     "Mthd 0x%04x Data 0x%08x\n",
+			if (!nv04_fifo_swmthd(priv, chid, mthd, data)) {
+				nv_info(priv, "CACHE_ERROR - Ch %d/%d "
+					      "Mthd 0x%04x Data 0x%08x\n",
 					chid, (mthd >> 13) & 7, mthd & 0x1ffc,
 					data);
 			}
 
-			nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
-			nv_wr32(dev, NV03_PFIFO_INTR_0,
+			nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
+			nv_wr32(priv, NV03_PFIFO_INTR_0,
 						NV_PFIFO_INTR_CACHE_ERROR);
 
-			nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0,
-				nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) & ~1);
-			nv_wr32(dev, NV03_PFIFO_CACHE1_GET, get + 4);
-			nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0,
-				nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) | 1);
-			nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0);
+			nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
+				nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) & ~1);
+			nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4);
+			nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0,
+				nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH0) | 1);
+			nv_wr32(priv, NV04_PFIFO_CACHE1_HASH, 0);
 
-			nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH,
-				nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
-			nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
+			nv_wr32(priv, NV04_PFIFO_CACHE1_DMA_PUSH,
+				nv_rd32(priv, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
+			nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
 
 			status &= ~NV_PFIFO_INTR_CACHE_ERROR;
 		}
 
 		if (status & NV_PFIFO_INTR_DMA_PUSHER) {
-			u32 dma_get = nv_rd32(dev, 0x003244);
-			u32 dma_put = nv_rd32(dev, 0x003240);
-			u32 push = nv_rd32(dev, 0x003220);
-			u32 state = nv_rd32(dev, 0x003228);
+			u32 dma_get = nv_rd32(priv, 0x003244);
+			u32 dma_put = nv_rd32(priv, 0x003240);
+			u32 push = nv_rd32(priv, 0x003220);
+			u32 state = nv_rd32(priv, 0x003228);
 
-			if (dev_priv->card_type == NV_50) {
-				u32 ho_get = nv_rd32(dev, 0x003328);
-				u32 ho_put = nv_rd32(dev, 0x003320);
-				u32 ib_get = nv_rd32(dev, 0x003334);
-				u32 ib_put = nv_rd32(dev, 0x003330);
+			if (device->card_type == NV_50) {
+				u32 ho_get = nv_rd32(priv, 0x003328);
+				u32 ho_put = nv_rd32(priv, 0x003320);
+				u32 ib_get = nv_rd32(priv, 0x003334);
+				u32 ib_put = nv_rd32(priv, 0x003330);
 
-				if (nouveau_ratelimit())
-					NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d Get 0x%02x%08x "
-					     "Put 0x%02x%08x IbGet 0x%08x IbPut 0x%08x "
-					     "State 0x%08x (err: %s) Push 0x%08x\n",
-						chid, ho_get, dma_get, ho_put,
-						dma_put, ib_get, ib_put, state,
-						nv_dma_state_err(state),
-						push);
+				nv_info(priv, "DMA_PUSHER - Ch %d Get 0x%02x%08x "
+				     "Put 0x%02x%08x IbGet 0x%08x IbPut 0x%08x "
+				     "State 0x%08x (err: %s) Push 0x%08x\n",
+					chid, ho_get, dma_get, ho_put,
+					dma_put, ib_get, ib_put, state,
+					nv_dma_state_err(state),
+					push);
 
 				/* METHOD_COUNT, in DMA_STATE on earlier chipsets */
-				nv_wr32(dev, 0x003364, 0x00000000);
+				nv_wr32(priv, 0x003364, 0x00000000);
 				if (dma_get != dma_put || ho_get != ho_put) {
-					nv_wr32(dev, 0x003244, dma_put);
-					nv_wr32(dev, 0x003328, ho_put);
+					nv_wr32(priv, 0x003244, dma_put);
+					nv_wr32(priv, 0x003328, ho_put);
 				} else
 				if (ib_get != ib_put) {
-					nv_wr32(dev, 0x003334, ib_put);
+					nv_wr32(priv, 0x003334, ib_put);
 				}
 			} else {
-				NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d Get 0x%08x "
+				nv_info(priv, "DMA_PUSHER - Ch %d Get 0x%08x "
 					     "Put 0x%08x State 0x%08x (err: %s) Push 0x%08x\n",
 					chid, dma_get, dma_put, state,
 					nv_dma_state_err(state), push);
 
 				if (dma_get != dma_put)
-					nv_wr32(dev, 0x003244, dma_put);
+					nv_wr32(priv, 0x003244, dma_put);
 			}
 
-			nv_wr32(dev, 0x003228, 0x00000000);
-			nv_wr32(dev, 0x003220, 0x00000001);
-			nv_wr32(dev, 0x002100, NV_PFIFO_INTR_DMA_PUSHER);
+			nv_wr32(priv, 0x003228, 0x00000000);
+			nv_wr32(priv, 0x003220, 0x00000001);
+			nv_wr32(priv, 0x002100, NV_PFIFO_INTR_DMA_PUSHER);
 			status &= ~NV_PFIFO_INTR_DMA_PUSHER;
 		}
 
@@ -427,81 +504,118 @@
 			uint32_t sem;
 
 			status &= ~NV_PFIFO_INTR_SEMAPHORE;
-			nv_wr32(dev, NV03_PFIFO_INTR_0,
+			nv_wr32(priv, NV03_PFIFO_INTR_0,
 				NV_PFIFO_INTR_SEMAPHORE);
 
-			sem = nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE);
-			nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, sem | 0x1);
+			sem = nv_rd32(priv, NV10_PFIFO_CACHE1_SEMAPHORE);
+			nv_wr32(priv, NV10_PFIFO_CACHE1_SEMAPHORE, sem | 0x1);
 
-			nv_wr32(dev, NV03_PFIFO_CACHE1_GET, get + 4);
-			nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
+			nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4);
+			nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
 		}
 
-		if (dev_priv->card_type == NV_50) {
+		if (device->card_type == NV_50) {
 			if (status & 0x00000010) {
-				nv50_fb_vm_trap(dev, nouveau_ratelimit());
+				nv50_fb_trap(nouveau_fb(priv), 1);
 				status &= ~0x00000010;
-				nv_wr32(dev, 0x002100, 0x00000010);
+				nv_wr32(priv, 0x002100, 0x00000010);
 			}
 		}
 
 		if (status) {
-			if (nouveau_ratelimit())
-				NV_INFO(dev, "PFIFO_INTR 0x%08x - Ch %d\n",
-					status, chid);
-			nv_wr32(dev, NV03_PFIFO_INTR_0, status);
+			nv_info(priv, "unknown intr 0x%08x, ch %d\n",
+				status, chid);
+			nv_wr32(priv, NV03_PFIFO_INTR_0, status);
 			status = 0;
 		}
 
-		nv_wr32(dev, NV03_PFIFO_CACHES, reassign);
+		nv_wr32(priv, NV03_PFIFO_CACHES, reassign);
 	}
 
 	if (status) {
-		NV_INFO(dev, "PFIFO still angry after %d spins, halt\n", cnt);
-		nv_wr32(dev, 0x2140, 0);
-		nv_wr32(dev, 0x140, 0);
+		nv_info(priv, "still angry after %d spins, halt\n", cnt);
+		nv_wr32(priv, 0x002140, 0);
+		nv_wr32(priv, 0x000140, 0);
 	}
 
-	nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PFIFO_PENDING);
+	nv_wr32(priv, 0x000100, 0x00000100);
+}
+
+static int
+nv04_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	       struct nouveau_oclass *oclass, void *data, u32 size,
+	       struct nouveau_object **pobject)
+{
+	struct nv04_instmem_priv *imem = nv04_instmem(parent);
+	struct nv04_fifo_priv *priv;
+	int ret;
+
+	ret = nouveau_fifo_create(parent, engine, oclass, 0, 15, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+
+	nouveau_ramht_ref(imem->ramht, &priv->ramht);
+	nouveau_gpuobj_ref(imem->ramro, &priv->ramro);
+	nouveau_gpuobj_ref(imem->ramfc, &priv->ramfc);
+
+	nv_subdev(priv)->unit = 0x00000100;
+	nv_subdev(priv)->intr = nv04_fifo_intr;
+	nv_engine(priv)->cclass = &nv04_fifo_cclass;
+	nv_engine(priv)->sclass = nv04_fifo_sclass;
+	priv->base.pause = nv04_fifo_pause;
+	priv->base.start = nv04_fifo_start;
+	priv->ramfc_desc = nv04_ramfc;
+	return 0;
 }
 
 void
-nv04_fifo_destroy(struct drm_device *dev, int engine)
+nv04_fifo_dtor(struct nouveau_object *object)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv04_fifo_priv *priv = nv_engine(dev, engine);
-
-	nouveau_irq_unregister(dev, 8);
-
+	struct nv04_fifo_priv *priv = (void *)object;
 	nouveau_gpuobj_ref(NULL, &priv->ramfc);
 	nouveau_gpuobj_ref(NULL, &priv->ramro);
-
-	dev_priv->eng[engine] = NULL;
-	kfree(priv);
+	nouveau_ramht_ref(NULL, &priv->ramht);
+	nouveau_fifo_destroy(&priv->base);
 }
 
 int
-nv04_fifo_create(struct drm_device *dev)
+nv04_fifo_init(struct nouveau_object *object)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv04_fifo_priv *priv;
+	struct nv04_fifo_priv *priv = (void *)object;
+	int ret;
 
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
+	ret = nouveau_fifo_init(&priv->base);
+	if (ret)
+		return ret;
 
-	nouveau_gpuobj_ref(nvimem_ramro(dev), &priv->ramro);
-	nouveau_gpuobj_ref(nvimem_ramfc(dev), &priv->ramfc);
+	nv_wr32(priv, NV04_PFIFO_DELAY_0, 0x000000ff);
+	nv_wr32(priv, NV04_PFIFO_DMA_TIMESLICE, 0x0101ffff);
 
-	priv->base.base.destroy = nv04_fifo_destroy;
-	priv->base.base.init = nv04_fifo_init;
-	priv->base.base.fini = nv04_fifo_fini;
-	priv->base.base.context_new = nv04_fifo_context_new;
-	priv->base.base.context_del = nv04_fifo_context_del;
-	priv->base.channels = 15;
-	priv->ramfc_desc = nv04_ramfc;
-	dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base;
+	nv_wr32(priv, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
+				       ((priv->ramht->bits - 9) << 16) |
+				        (priv->ramht->base.addr >> 8));
+	nv_wr32(priv, NV03_PFIFO_RAMRO, priv->ramro->addr >> 8);
+	nv_wr32(priv, NV03_PFIFO_RAMFC, priv->ramfc->addr >> 8);
 
-	nouveau_irq_register(dev, 8, nv04_fifo_isr);
+	nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH1, priv->base.max);
+
+	nv_wr32(priv, NV03_PFIFO_INTR_0, 0xffffffff);
+	nv_wr32(priv, NV03_PFIFO_INTR_EN_0, 0xffffffff);
+
+	nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, 1);
+	nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
+	nv_wr32(priv, NV03_PFIFO_CACHES, 1);
 	return 0;
 }
+
+struct nouveau_oclass
+nv04_fifo_oclass = {
+	.handle = NV_ENGINE(FIFO, 0x04),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv04_fifo_ctor,
+		.dtor = nv04_fifo_dtor,
+		.init = nv04_fifo_init,
+		.fini = _nouveau_fifo_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.h b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.h
new file mode 100644
index 0000000..496a4b4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.h
@@ -0,0 +1,178 @@
+#ifndef __NV04_FIFO_H__
+#define __NV04_FIFO_H__
+
+#include <engine/fifo.h>
+
+#define NV04_PFIFO_DELAY_0                                 0x00002040
+#define NV04_PFIFO_DMA_TIMESLICE                           0x00002044
+#define NV04_PFIFO_NEXT_CHANNEL                            0x00002050
+#define NV03_PFIFO_INTR_0                                  0x00002100
+#define NV03_PFIFO_INTR_EN_0                               0x00002140
+#    define NV_PFIFO_INTR_CACHE_ERROR                          (1<<0)
+#    define NV_PFIFO_INTR_RUNOUT                               (1<<4)
+#    define NV_PFIFO_INTR_RUNOUT_OVERFLOW                      (1<<8)
+#    define NV_PFIFO_INTR_DMA_PUSHER                          (1<<12)
+#    define NV_PFIFO_INTR_DMA_PT                              (1<<16)
+#    define NV_PFIFO_INTR_SEMAPHORE                           (1<<20)
+#    define NV_PFIFO_INTR_ACQUIRE_TIMEOUT                     (1<<24)
+#define NV03_PFIFO_RAMHT                                   0x00002210
+#define NV03_PFIFO_RAMFC                                   0x00002214
+#define NV03_PFIFO_RAMRO                                   0x00002218
+#define NV40_PFIFO_RAMFC                                   0x00002220
+#define NV03_PFIFO_CACHES                                  0x00002500
+#define NV04_PFIFO_MODE                                    0x00002504
+#define NV04_PFIFO_DMA                                     0x00002508
+#define NV04_PFIFO_SIZE                                    0x0000250c
+#define NV50_PFIFO_CTX_TABLE(c)                        (0x2600+(c)*4)
+#define NV50_PFIFO_CTX_TABLE__SIZE                                128
+#define NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED                  (1<<31)
+#define NV50_PFIFO_CTX_TABLE_UNK30_BAD                        (1<<30)
+#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80             0x0FFFFFFF
+#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84             0x00FFFFFF
+#define NV03_PFIFO_CACHE0_PUSH0                            0x00003000
+#define NV03_PFIFO_CACHE0_PULL0                            0x00003040
+#define NV04_PFIFO_CACHE0_PULL0                            0x00003050
+#define NV04_PFIFO_CACHE0_PULL1                            0x00003054
+#define NV03_PFIFO_CACHE1_PUSH0                            0x00003200
+#define NV03_PFIFO_CACHE1_PUSH1                            0x00003204
+#define NV03_PFIFO_CACHE1_PUSH1_DMA                            (1<<8)
+#define NV40_PFIFO_CACHE1_PUSH1_DMA                           (1<<16)
+#define NV03_PFIFO_CACHE1_PUSH1_CHID_MASK                  0x0000000f
+#define NV10_PFIFO_CACHE1_PUSH1_CHID_MASK                  0x0000001f
+#define NV50_PFIFO_CACHE1_PUSH1_CHID_MASK                  0x0000007f
+#define NV03_PFIFO_CACHE1_PUT                              0x00003210
+#define NV04_PFIFO_CACHE1_DMA_PUSH                         0x00003220
+#define NV04_PFIFO_CACHE1_DMA_FETCH                        0x00003224
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_8_BYTES         0x00000000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_16_BYTES        0x00000008
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_24_BYTES        0x00000010
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_32_BYTES        0x00000018
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_40_BYTES        0x00000020
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_48_BYTES        0x00000028
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_56_BYTES        0x00000030
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_64_BYTES        0x00000038
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_72_BYTES        0x00000040
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_80_BYTES        0x00000048
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_88_BYTES        0x00000050
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_96_BYTES        0x00000058
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_104_BYTES       0x00000060
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_112_BYTES       0x00000068
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_120_BYTES       0x00000070
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES       0x00000078
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_136_BYTES       0x00000080
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_144_BYTES       0x00000088
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_152_BYTES       0x00000090
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_160_BYTES       0x00000098
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_168_BYTES       0x000000A0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_176_BYTES       0x000000A8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_184_BYTES       0x000000B0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_192_BYTES       0x000000B8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_200_BYTES       0x000000C0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_208_BYTES       0x000000C8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_216_BYTES       0x000000D0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_224_BYTES       0x000000D8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_232_BYTES       0x000000E0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_240_BYTES       0x000000E8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_248_BYTES       0x000000F0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_256_BYTES       0x000000F8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE                 0x0000E000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_32_BYTES        0x00000000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_64_BYTES        0x00002000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_96_BYTES        0x00004000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES       0x00006000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_160_BYTES       0x00008000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_192_BYTES       0x0000A000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_224_BYTES       0x0000C000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_256_BYTES       0x0000E000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS             0x001F0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_0           0x00000000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_1           0x00010000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_2           0x00020000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_3           0x00030000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_4           0x00040000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_5           0x00050000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_6           0x00060000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_7           0x00070000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8           0x00080000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_9           0x00090000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_10          0x000A0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_11          0x000B0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_12          0x000C0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_13          0x000D0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_14          0x000E0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_15          0x000F0000
+#    define NV_PFIFO_CACHE1_ENDIAN                         0x80000000
+#    define NV_PFIFO_CACHE1_LITTLE_ENDIAN                  0x7FFFFFFF
+#    define NV_PFIFO_CACHE1_BIG_ENDIAN                     0x80000000
+#define NV04_PFIFO_CACHE1_DMA_STATE                        0x00003228
+#define NV04_PFIFO_CACHE1_DMA_INSTANCE                     0x0000322c
+#define NV04_PFIFO_CACHE1_DMA_CTL                          0x00003230
+#define NV04_PFIFO_CACHE1_DMA_PUT                          0x00003240
+#define NV04_PFIFO_CACHE1_DMA_GET                          0x00003244
+#define NV10_PFIFO_CACHE1_REF_CNT                          0x00003248
+#define NV10_PFIFO_CACHE1_DMA_SUBROUTINE                   0x0000324C
+#define NV03_PFIFO_CACHE1_PULL0                            0x00003240
+#define NV04_PFIFO_CACHE1_PULL0                            0x00003250
+#    define NV04_PFIFO_CACHE1_PULL0_HASH_FAILED            0x00000010
+#    define NV04_PFIFO_CACHE1_PULL0_HASH_BUSY              0x00001000
+#define NV03_PFIFO_CACHE1_PULL1                            0x00003250
+#define NV04_PFIFO_CACHE1_PULL1                            0x00003254
+#define NV04_PFIFO_CACHE1_HASH                             0x00003258
+#define NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT                  0x00003260
+#define NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP                0x00003264
+#define NV10_PFIFO_CACHE1_ACQUIRE_VALUE                    0x00003268
+#define NV10_PFIFO_CACHE1_SEMAPHORE                        0x0000326C
+#define NV03_PFIFO_CACHE1_GET                              0x00003270
+#define NV04_PFIFO_CACHE1_ENGINE                           0x00003280
+#define NV04_PFIFO_CACHE1_DMA_DCOUNT                       0x000032A0
+#define NV40_PFIFO_GRCTX_INSTANCE                          0x000032E0
+#define NV40_PFIFO_UNK32E4                                 0x000032E4
+#define NV04_PFIFO_CACHE1_METHOD(i)                (0x00003800+(i*8))
+#define NV04_PFIFO_CACHE1_DATA(i)                  (0x00003804+(i*8))
+#define NV40_PFIFO_CACHE1_METHOD(i)                (0x00090000+(i*8))
+#define NV40_PFIFO_CACHE1_DATA(i)                  (0x00090004+(i*8))
+
+struct ramfc_desc {
+	unsigned bits:6;
+	unsigned ctxs:5;
+	unsigned ctxp:8;
+	unsigned regs:5;
+	unsigned regp;
+};
+
+struct nv04_fifo_priv {
+	struct nouveau_fifo base;
+	struct ramfc_desc *ramfc_desc;
+	struct nouveau_ramht  *ramht;
+	struct nouveau_gpuobj *ramro;
+	struct nouveau_gpuobj *ramfc;
+};
+
+struct nv04_fifo_base {
+	struct nouveau_fifo_base base;
+};
+
+struct nv04_fifo_chan {
+	struct nouveau_fifo_chan base;
+	u32 subc[8];
+	u32 ramfc;
+};
+
+int  nv04_fifo_object_attach(struct nouveau_object *,
+			     struct nouveau_object *, u32);
+void nv04_fifo_object_detach(struct nouveau_object *, int);
+
+void nv04_fifo_chan_dtor(struct nouveau_object *);
+int  nv04_fifo_chan_init(struct nouveau_object *);
+int  nv04_fifo_chan_fini(struct nouveau_object *, bool suspend);
+
+int  nv04_fifo_context_ctor(struct nouveau_object *, struct nouveau_object *,
+			    struct nouveau_oclass *, void *, u32,
+			    struct nouveau_object **);
+
+void nv04_fifo_dtor(struct nouveau_object *);
+int  nv04_fifo_init(struct nouveau_object *);
+void nv04_fifo_pause(struct nouveau_fifo *, unsigned long *);
+void nv04_fifo_start(struct nouveau_fifo *, unsigned long *);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c
index 2d38fa8..391fefa 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c
@@ -1,43 +1,42 @@
 /*
- * Copyright (C) 2012 Ben Skeggs.
- * All Rights Reserved.
+ * Copyright 2012 Red Hat Inc.
  *
- * 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:
+ * 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 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 OWNER(S) 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.
+ * 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.
  *
+ * Authors: Ben Skeggs
  */
 
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <engine/fifo.h>
-#include "nouveau_util.h"
+#include <core/os.h>
+#include <core/class.h>
+#include <core/engctx.h>
 #include <core/ramht.h>
 
-static struct ramfc_desc {
-	unsigned bits:6;
-	unsigned ctxs:5;
-	unsigned ctxp:8;
-	unsigned regs:5;
-	unsigned regp;
-} nv10_ramfc[] = {
+#include <subdev/instmem.h>
+#include <subdev/instmem/nv04.h>
+#include <subdev/fb.h>
+
+#include <engine/fifo.h>
+
+#include "nv04.h"
+
+static struct ramfc_desc
+nv10_ramfc[] = {
 	{ 32,  0, 0x00,  0, NV04_PFIFO_CACHE1_DMA_PUT },
 	{ 32,  0, 0x04,  0, NV04_PFIFO_CACHE1_DMA_GET },
 	{ 32,  0, 0x08,  0, NV10_PFIFO_CACHE1_REF_CNT },
@@ -50,87 +49,122 @@
 	{}
 };
 
-struct nv10_fifo_priv {
-	struct nouveau_fifo_priv base;
-	struct ramfc_desc *ramfc_desc;
-	struct nouveau_gpuobj *ramro;
-	struct nouveau_gpuobj *ramfc;
-};
-
-struct nv10_fifo_chan {
-	struct nouveau_fifo_chan base;
-	u32 ramfc;
-};
+/*******************************************************************************
+ * FIFO channel objects
+ ******************************************************************************/
 
 static int
-nv10_fifo_context_new(struct nouveau_channel *chan, int engine)
+nv10_fifo_chan_ctor(struct nouveau_object *parent,
+		    struct nouveau_object *engine,
+		    struct nouveau_oclass *oclass, void *data, u32 size,
+		    struct nouveau_object **pobject)
 {
-	struct drm_device *dev = chan->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv10_fifo_priv *priv = nv_engine(dev, engine);
-	struct nv10_fifo_chan *fctx;
-	unsigned long flags;
+	struct nv04_fifo_priv *priv = (void *)engine;
+	struct nv04_fifo_chan *chan;
+	struct nv_channel_dma_class *args = data;
 	int ret;
 
-	fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL);
-	if (!fctx)
-		return -ENOMEM;
+	if (size < sizeof(*args))
+		return -EINVAL;
 
-	fctx->ramfc = chan->id * 32;
+	ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0x800000,
+					  0x10000, args->pushbuf,
+					  (1 << NVDEV_ENGINE_DMAOBJ) |
+					  (1 << NVDEV_ENGINE_SW) |
+					  (1 << NVDEV_ENGINE_GR), &chan);
+	*pobject = nv_object(chan);
+	if (ret)
+		return ret;
 
-	/* map channel control registers */
-	chan->user = ioremap(pci_resource_start(dev->pdev, 0) +
-			     NV03_USER(chan->id), PAGE_SIZE);
-	if (!chan->user) {
-		ret = -ENOMEM;
-		goto error;
-	}
+	nv_parent(chan)->object_attach = nv04_fifo_object_attach;
+	nv_parent(chan)->object_detach = nv04_fifo_object_detach;
+	chan->ramfc = chan->base.chid * 32;
 
-	/* initialise default fifo context */
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x00, chan->pushbuf_base);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x04, chan->pushbuf_base);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x0c, chan->pushbuf->addr >> 4);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x14,
+	nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->offset);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->offset);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x0c, chan->base.pushgpu->addr >> 4);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x14,
 			     NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
 			     NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
 #ifdef __BIG_ENDIAN
 			     NV_PFIFO_CACHE1_BIG_ENDIAN |
 #endif
 			     NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8);
-
-	/* enable dma mode on the channel */
-	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv_mask(dev, NV04_PFIFO_MODE, (1 << chan->id), (1 << chan->id));
-	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
-
-error:
-	if (ret)
-		priv->base.base.context_del(chan, engine);
-	return ret;
-}
-
-int
-nv10_fifo_create(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv10_fifo_priv *priv;
-
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	nouveau_gpuobj_ref(nvimem_ramro(dev), &priv->ramro);
-	nouveau_gpuobj_ref(nvimem_ramfc(dev), &priv->ramfc);
-
-	priv->base.base.destroy = nv04_fifo_destroy;
-	priv->base.base.init = nv04_fifo_init;
-	priv->base.base.fini = nv04_fifo_fini;
-	priv->base.base.context_new = nv10_fifo_context_new;
-	priv->base.base.context_del = nv04_fifo_context_del;
-	priv->base.channels = 31;
-	priv->ramfc_desc = nv10_ramfc;
-	dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base;
-
-	nouveau_irq_register(dev, 8, nv04_fifo_isr);
 	return 0;
 }
+
+static struct nouveau_ofuncs
+nv10_fifo_ofuncs = {
+	.ctor = nv10_fifo_chan_ctor,
+	.dtor = nv04_fifo_chan_dtor,
+	.init = nv04_fifo_chan_init,
+	.fini = nv04_fifo_chan_fini,
+	.rd32 = _nouveau_fifo_channel_rd32,
+	.wr32 = _nouveau_fifo_channel_wr32,
+};
+
+static struct nouveau_oclass
+nv10_fifo_sclass[] = {
+	{ 0x006e, &nv10_fifo_ofuncs },
+	{}
+};
+
+/*******************************************************************************
+ * FIFO context - basically just the instmem reserved for the channel
+ ******************************************************************************/
+
+static struct nouveau_oclass
+nv10_fifo_cclass = {
+	.handle = NV_ENGCTX(FIFO, 0x10),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv04_fifo_context_ctor,
+		.dtor = _nouveau_fifo_context_dtor,
+		.init = _nouveau_fifo_context_init,
+		.fini = _nouveau_fifo_context_fini,
+		.rd32 = _nouveau_fifo_context_rd32,
+		.wr32 = _nouveau_fifo_context_wr32,
+	},
+};
+
+/*******************************************************************************
+ * PFIFO engine
+ ******************************************************************************/
+
+static int
+nv10_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	       struct nouveau_oclass *oclass, void *data, u32 size,
+	       struct nouveau_object **pobject)
+{
+	struct nv04_instmem_priv *imem = nv04_instmem(parent);
+	struct nv04_fifo_priv *priv;
+	int ret;
+
+	ret = nouveau_fifo_create(parent, engine, oclass, 0, 31, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+
+	nouveau_ramht_ref(imem->ramht, &priv->ramht);
+	nouveau_gpuobj_ref(imem->ramro, &priv->ramro);
+	nouveau_gpuobj_ref(imem->ramfc, &priv->ramfc);
+
+	nv_subdev(priv)->unit = 0x00000100;
+	nv_subdev(priv)->intr = nv04_fifo_intr;
+	nv_engine(priv)->cclass = &nv10_fifo_cclass;
+	nv_engine(priv)->sclass = nv10_fifo_sclass;
+	priv->base.pause = nv04_fifo_pause;
+	priv->base.start = nv04_fifo_start;
+	priv->ramfc_desc = nv10_ramfc;
+	return 0;
+}
+
+struct nouveau_oclass
+nv10_fifo_oclass = {
+	.handle = NV_ENGINE(FIFO, 0x10),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv10_fifo_ctor,
+		.dtor = nv04_fifo_dtor,
+		.init = nv04_fifo_init,
+		.fini = _nouveau_fifo_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c
index 2f700a15e..3b9d6c9 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c
@@ -1,43 +1,42 @@
 /*
- * Copyright (C) 2012 Ben Skeggs.
- * All Rights Reserved.
+ * Copyright 2012 Red Hat Inc.
  *
- * 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:
+ * 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 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 OWNER(S) 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.
+ * 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.
  *
+ * Authors: Ben Skeggs
  */
 
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <engine/fifo.h>
-#include "nouveau_util.h"
+#include <core/os.h>
+#include <core/class.h>
+#include <core/engctx.h>
 #include <core/ramht.h>
 
-static struct ramfc_desc {
-	unsigned bits:6;
-	unsigned ctxs:5;
-	unsigned ctxp:8;
-	unsigned regs:5;
-	unsigned regp;
-} nv17_ramfc[] = {
+#include <subdev/instmem.h>
+#include <subdev/instmem/nv04.h>
+#include <subdev/fb.h>
+
+#include <engine/fifo.h>
+
+#include "nv04.h"
+
+static struct ramfc_desc
+nv17_ramfc[] = {
 	{ 32,  0, 0x00,  0, NV04_PFIFO_CACHE1_DMA_PUT },
 	{ 32,  0, 0x04,  0, NV04_PFIFO_CACHE1_DMA_GET },
 	{ 32,  0, 0x08,  0, NV10_PFIFO_CACHE1_REF_CNT },
@@ -55,124 +54,154 @@
 	{}
 };
 
-struct nv17_fifo_priv {
-	struct nouveau_fifo_priv base;
-	struct ramfc_desc *ramfc_desc;
-	struct nouveau_gpuobj *ramro;
-	struct nouveau_gpuobj *ramfc;
-};
-
-struct nv17_fifo_chan {
-	struct nouveau_fifo_chan base;
-	u32 ramfc;
-};
+/*******************************************************************************
+ * FIFO channel objects
+ ******************************************************************************/
 
 static int
-nv17_fifo_context_new(struct nouveau_channel *chan, int engine)
+nv17_fifo_chan_ctor(struct nouveau_object *parent,
+		    struct nouveau_object *engine,
+		    struct nouveau_oclass *oclass, void *data, u32 size,
+		    struct nouveau_object **pobject)
 {
-	struct drm_device *dev = chan->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv17_fifo_priv *priv = nv_engine(dev, engine);
-	struct nv17_fifo_chan *fctx;
-	unsigned long flags;
+	struct nv04_fifo_priv *priv = (void *)engine;
+	struct nv04_fifo_chan *chan;
+	struct nv_channel_dma_class *args = data;
 	int ret;
 
-	fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL);
-	if (!fctx)
-		return -ENOMEM;
+	if (size < sizeof(*args))
+		return -EINVAL;
 
-	fctx->ramfc = chan->id * 64;
+	ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0x800000,
+					  0x10000, args->pushbuf,
+					  (1 << NVDEV_ENGINE_DMAOBJ) |
+					  (1 << NVDEV_ENGINE_SW) |
+					  (1 << NVDEV_ENGINE_GR) |
+					  (1 << NVDEV_ENGINE_MPEG), /* NV31- */
+					  &chan);
+	*pobject = nv_object(chan);
+	if (ret)
+		return ret;
 
-	/* map channel control registers */
-	chan->user = ioremap(pci_resource_start(dev->pdev, 0) +
-			     NV03_USER(chan->id), PAGE_SIZE);
-	if (!chan->user) {
-		ret = -ENOMEM;
-		goto error;
-	}
+	nv_parent(chan)->object_attach = nv04_fifo_object_attach;
+	nv_parent(chan)->object_detach = nv04_fifo_object_detach;
+	chan->ramfc = chan->base.chid * 64;
 
-	/* initialise default fifo context */
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x00, chan->pushbuf_base);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x04, chan->pushbuf_base);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x0c, chan->pushbuf->addr >> 4);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x14,
+	nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->offset);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->offset);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x0c, chan->base.pushgpu->addr >> 4);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x14,
 			     NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
 			     NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
 #ifdef __BIG_ENDIAN
 			     NV_PFIFO_CACHE1_BIG_ENDIAN |
 #endif
 			     NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8);
+	return 0;
+}
 
-	/* enable dma mode on the channel */
-	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv_mask(dev, NV04_PFIFO_MODE, (1 << chan->id), (1 << chan->id));
-	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+static struct nouveau_ofuncs
+nv17_fifo_ofuncs = {
+	.ctor = nv17_fifo_chan_ctor,
+	.dtor = nv04_fifo_chan_dtor,
+	.init = nv04_fifo_chan_init,
+	.fini = nv04_fifo_chan_fini,
+	.rd32 = _nouveau_fifo_channel_rd32,
+	.wr32 = _nouveau_fifo_channel_wr32,
+};
 
-error:
+static struct nouveau_oclass
+nv17_fifo_sclass[] = {
+	{ 0x006e, &nv17_fifo_ofuncs },
+	{}
+};
+
+/*******************************************************************************
+ * FIFO context - basically just the instmem reserved for the channel
+ ******************************************************************************/
+
+static struct nouveau_oclass
+nv17_fifo_cclass = {
+	.handle = NV_ENGCTX(FIFO, 0x17),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv04_fifo_context_ctor,
+		.dtor = _nouveau_fifo_context_dtor,
+		.init = _nouveau_fifo_context_init,
+		.fini = _nouveau_fifo_context_fini,
+		.rd32 = _nouveau_fifo_context_rd32,
+		.wr32 = _nouveau_fifo_context_wr32,
+	},
+};
+
+/*******************************************************************************
+ * PFIFO engine
+ ******************************************************************************/
+
+static int
+nv17_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	       struct nouveau_oclass *oclass, void *data, u32 size,
+	       struct nouveau_object **pobject)
+{
+	struct nv04_instmem_priv *imem = nv04_instmem(parent);
+	struct nv04_fifo_priv *priv;
+	int ret;
+
+	ret = nouveau_fifo_create(parent, engine, oclass, 0, 31, &priv);
+	*pobject = nv_object(priv);
 	if (ret)
-		priv->base.base.context_del(chan, engine);
-	return ret;
+		return ret;
+
+	nouveau_ramht_ref(imem->ramht, &priv->ramht);
+	nouveau_gpuobj_ref(imem->ramro, &priv->ramro);
+	nouveau_gpuobj_ref(imem->ramfc, &priv->ramfc);
+
+	nv_subdev(priv)->unit = 0x00000100;
+	nv_subdev(priv)->intr = nv04_fifo_intr;
+	nv_engine(priv)->cclass = &nv17_fifo_cclass;
+	nv_engine(priv)->sclass = nv17_fifo_sclass;
+	priv->base.pause = nv04_fifo_pause;
+	priv->base.start = nv04_fifo_start;
+	priv->ramfc_desc = nv17_ramfc;
+	return 0;
 }
 
 static int
-nv17_fifo_init(struct drm_device *dev, int engine)
+nv17_fifo_init(struct nouveau_object *object)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv17_fifo_priv *priv = nv_engine(dev, engine);
-	int i;
+	struct nv04_fifo_priv *priv = (void *)object;
+	int ret;
 
-	nv_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, 0);
-	nv_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, NV_PMC_ENABLE_PFIFO);
+	ret = nouveau_fifo_init(&priv->base);
+	if (ret)
+		return ret;
 
-	nv_wr32(dev, NV04_PFIFO_DELAY_0, 0x000000ff);
-	nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, 0x0101ffff);
+	nv_wr32(priv, NV04_PFIFO_DELAY_0, 0x000000ff);
+	nv_wr32(priv, NV04_PFIFO_DMA_TIMESLICE, 0x0101ffff);
 
-	nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
-				       ((dev_priv->ramht->bits - 9) << 16) |
-				       (dev_priv->ramht->gpuobj->addr >> 8));
-	nv_wr32(dev, NV03_PFIFO_RAMRO, priv->ramro->addr >> 8);
-	nv_wr32(dev, NV03_PFIFO_RAMFC, 0x00010000 |
-				       priv->ramfc->addr >> 8);
+	nv_wr32(priv, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
+				       ((priv->ramht->bits - 9) << 16) |
+				        (priv->ramht->base.addr >> 8));
+	nv_wr32(priv, NV03_PFIFO_RAMRO, priv->ramro->addr >> 8);
+	nv_wr32(priv, NV03_PFIFO_RAMFC, priv->ramfc->addr >> 8 | 0x00010000);
 
-	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, priv->base.channels);
+	nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH1, priv->base.max);
 
-	nv_wr32(dev, NV03_PFIFO_INTR_0, 0xffffffff);
-	nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xffffffff);
+	nv_wr32(priv, NV03_PFIFO_INTR_0, 0xffffffff);
+	nv_wr32(priv, NV03_PFIFO_INTR_EN_0, 0xffffffff);
 
-	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1);
-	nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
-	nv_wr32(dev, NV03_PFIFO_CACHES, 1);
-
-	for (i = 0; i < priv->base.channels; i++) {
-		if (dev_priv->channels.ptr[i])
-			nv_mask(dev, NV04_PFIFO_MODE, (1 << i), (1 << i));
-	}
-
+	nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, 1);
+	nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
+	nv_wr32(priv, NV03_PFIFO_CACHES, 1);
 	return 0;
 }
 
-int
-nv17_fifo_create(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv17_fifo_priv *priv;
-
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	nouveau_gpuobj_ref(nvimem_ramro(dev), &priv->ramro);
-	nouveau_gpuobj_ref(nvimem_ramfc(dev), &priv->ramfc);
-
-	priv->base.base.destroy = nv04_fifo_destroy;
-	priv->base.base.init = nv17_fifo_init;
-	priv->base.base.fini = nv04_fifo_fini;
-	priv->base.base.context_new = nv17_fifo_context_new;
-	priv->base.base.context_del = nv04_fifo_context_del;
-	priv->base.channels = 31;
-	priv->ramfc_desc = nv17_ramfc;
-	dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base;
-
-	nouveau_irq_register(dev, 8, nv04_fifo_isr);
-	return 0;
-}
+struct nouveau_oclass
+nv17_fifo_oclass = {
+	.handle = NV_ENGINE(FIFO, 0x17),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv17_fifo_ctor,
+		.dtor = nv04_fifo_dtor,
+		.init = nv17_fifo_init,
+		.fini = _nouveau_fifo_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c
index 65a670f..43d5c9e 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c
@@ -1,43 +1,42 @@
 /*
- * Copyright (C) 2012 Ben Skeggs.
- * All Rights Reserved.
+ * Copyright 2012 Red Hat Inc.
  *
- * 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:
+ * 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 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 OWNER(S) 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.
+ * 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.
  *
+ * Authors: Ben Skeggs
  */
 
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <engine/fifo.h>
-#include "nouveau_util.h"
+#include <core/os.h>
+#include <core/class.h>
+#include <core/engctx.h>
 #include <core/ramht.h>
 
-static struct ramfc_desc {
-	unsigned bits:6;
-	unsigned ctxs:5;
-	unsigned ctxp:8;
-	unsigned regs:5;
-	unsigned regp;
-} nv40_ramfc[] = {
+#include <subdev/instmem.h>
+#include <subdev/instmem/nv04.h>
+#include <subdev/fb.h>
+
+#include <engine/fifo.h>
+
+#include "nv04.h"
+
+static struct ramfc_desc
+nv40_ramfc[] = {
 	{ 32,  0, 0x00,  0, NV04_PFIFO_CACHE1_DMA_PUT },
 	{ 32,  0, 0x04,  0, NV04_PFIFO_CACHE1_DMA_GET },
 	{ 32,  0, 0x08,  0, NV10_PFIFO_CACHE1_REF_CNT },
@@ -63,148 +62,287 @@
 	{}
 };
 
-struct nv40_fifo_priv {
-	struct nouveau_fifo_priv base;
-	struct ramfc_desc *ramfc_desc;
-	struct nouveau_gpuobj *ramro;
-	struct nouveau_gpuobj *ramfc;
-};
-
-struct nv40_fifo_chan {
-	struct nouveau_fifo_chan base;
-	u32 ramfc;
-};
+/*******************************************************************************
+ * FIFO channel objects
+ ******************************************************************************/
 
 static int
-nv40_fifo_context_new(struct nouveau_channel *chan, int engine)
+nv40_fifo_object_attach(struct nouveau_object *parent,
+			struct nouveau_object *object, u32 handle)
 {
-	struct drm_device *dev = chan->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv40_fifo_priv *priv = nv_engine(dev, engine);
-	struct nv40_fifo_chan *fctx;
-	unsigned long flags;
+	struct nv04_fifo_priv *priv = (void *)parent->engine;
+	struct nv04_fifo_chan *chan = (void *)parent;
+	u32 context, chid = chan->base.chid;
 	int ret;
 
-	fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL);
-	if (!fctx)
-		return -ENOMEM;
+	if (nv_iclass(object, NV_GPUOBJ_CLASS))
+		context = nv_gpuobj(object)->addr >> 4;
+	else
+		context = 0x00000004; /* just non-zero */
 
-	fctx->ramfc = chan->id * 128;
-
-	/* map channel control registers */
-	chan->user = ioremap(pci_resource_start(dev->pdev, 0) +
-			     NV03_USER(chan->id), PAGE_SIZE);
-	if (!chan->user) {
-		ret = -ENOMEM;
-		goto error;
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_DMAOBJ:
+	case NVDEV_ENGINE_SW:
+		context |= 0x00000000;
+		break;
+	case NVDEV_ENGINE_GR:
+		context |= 0x00100000;
+		break;
+	case NVDEV_ENGINE_MPEG:
+		context |= 0x00200000;
+		break;
+	default:
+		return -EINVAL;
 	}
 
-	/* initialise default fifo context */
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x00, chan->pushbuf_base);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x04, chan->pushbuf_base);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x0c, chan->pushbuf->addr >> 4);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x18, 0x30000000 |
+	context |= chid << 23;
+
+	mutex_lock(&nv_subdev(priv)->mutex);
+	ret = nouveau_ramht_insert(priv->ramht, chid, handle, context);
+	mutex_unlock(&nv_subdev(priv)->mutex);
+	return ret;
+}
+
+static int
+nv40_fifo_context_attach(struct nouveau_object *parent,
+			 struct nouveau_object *engctx)
+{
+	struct nv04_fifo_priv *priv = (void *)parent->engine;
+	struct nv04_fifo_chan *chan = (void *)parent;
+	unsigned long flags;
+	u32 reg, ctx;
+
+	switch (nv_engidx(engctx->engine)) {
+	case NVDEV_ENGINE_SW:
+		return 0;
+	case NVDEV_ENGINE_GR:
+		reg = 0x32e0;
+		ctx = 0x38;
+		break;
+	case NVDEV_ENGINE_MPEG:
+		reg = 0x330c;
+		ctx = 0x54;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&priv->base.lock, flags);
+	nv_mask(priv, 0x002500, 0x00000001, 0x00000000);
+
+	if ((nv_rd32(priv, 0x003204) & priv->base.max) == chan->base.chid)
+		nv_wr32(priv, reg, nv_gpuobj(engctx)->addr >> 4);
+	nv_wo32(priv->ramfc, chan->ramfc + ctx, nv_gpuobj(engctx)->addr >> 4);
+
+	nv_mask(priv, 0x002500, 0x00000001, 0x00000001);
+	spin_unlock_irqrestore(&priv->base.lock, flags);
+	return 0;
+}
+
+static int
+nv40_fifo_context_detach(struct nouveau_object *parent, bool suspend,
+			 struct nouveau_object *engctx)
+{
+	struct nv04_fifo_priv *priv = (void *)parent->engine;
+	struct nv04_fifo_chan *chan = (void *)parent;
+	unsigned long flags;
+	u32 reg, ctx;
+
+	switch (nv_engidx(engctx->engine)) {
+	case NVDEV_ENGINE_SW:
+		return 0;
+	case NVDEV_ENGINE_GR:
+		reg = 0x32e0;
+		ctx = 0x38;
+		break;
+	case NVDEV_ENGINE_MPEG:
+		reg = 0x330c;
+		ctx = 0x54;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&priv->base.lock, flags);
+	nv_mask(priv, 0x002500, 0x00000001, 0x00000000);
+
+	if ((nv_rd32(priv, 0x003204) & priv->base.max) == chan->base.chid)
+		nv_wr32(priv, reg, 0x00000000);
+	nv_wo32(priv->ramfc, chan->ramfc + ctx, 0x00000000);
+
+	nv_mask(priv, 0x002500, 0x00000001, 0x00000001);
+	spin_unlock_irqrestore(&priv->base.lock, flags);
+	return 0;
+}
+
+static int
+nv40_fifo_chan_ctor(struct nouveau_object *parent,
+		    struct nouveau_object *engine,
+		    struct nouveau_oclass *oclass, void *data, u32 size,
+		    struct nouveau_object **pobject)
+{
+	struct nv04_fifo_priv *priv = (void *)engine;
+	struct nv04_fifo_chan *chan;
+	struct nv_channel_dma_class *args = data;
+	int ret;
+
+	if (size < sizeof(*args))
+		return -EINVAL;
+
+	ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000,
+					  0x1000, args->pushbuf,
+					  (1 << NVDEV_ENGINE_DMAOBJ) |
+					  (1 << NVDEV_ENGINE_SW) |
+					  (1 << NVDEV_ENGINE_GR) |
+					  (1 << NVDEV_ENGINE_MPEG), &chan);
+	*pobject = nv_object(chan);
+	if (ret)
+		return ret;
+
+	nv_parent(chan)->context_attach = nv40_fifo_context_attach;
+	nv_parent(chan)->context_detach = nv40_fifo_context_detach;
+	nv_parent(chan)->object_attach = nv40_fifo_object_attach;
+	nv_parent(chan)->object_detach = nv04_fifo_object_detach;
+	chan->ramfc = chan->base.chid * 128;
+
+	nv_wo32(priv->ramfc, chan->ramfc + 0x00, args->offset);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x04, args->offset);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x0c, chan->base.pushgpu->addr >> 4);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x18, 0x30000000 |
 			     NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
 			     NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
 #ifdef __BIG_ENDIAN
 			     NV_PFIFO_CACHE1_BIG_ENDIAN |
 #endif
 			     NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8);
-	nv_wo32(priv->ramfc, fctx->ramfc + 0x3c, 0x0001ffff);
+	nv_wo32(priv->ramfc, chan->ramfc + 0x3c, 0x0001ffff);
+	return 0;
+}
 
-	/* enable dma mode on the channel */
-	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv_mask(dev, NV04_PFIFO_MODE, (1 << chan->id), (1 << chan->id));
-	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+static struct nouveau_ofuncs
+nv40_fifo_ofuncs = {
+	.ctor = nv40_fifo_chan_ctor,
+	.dtor = nv04_fifo_chan_dtor,
+	.init = nv04_fifo_chan_init,
+	.fini = nv04_fifo_chan_fini,
+	.rd32 = _nouveau_fifo_channel_rd32,
+	.wr32 = _nouveau_fifo_channel_wr32,
+};
 
-	/*XXX: remove this later, need fifo engine context commit hook */
-	nouveau_gpuobj_ref(priv->ramfc, &chan->ramfc);
+static struct nouveau_oclass
+nv40_fifo_sclass[] = {
+	{ 0x006e, &nv40_fifo_ofuncs },
+	{}
+};
 
-error:
+/*******************************************************************************
+ * FIFO context - basically just the instmem reserved for the channel
+ ******************************************************************************/
+
+static struct nouveau_oclass
+nv40_fifo_cclass = {
+	.handle = NV_ENGCTX(FIFO, 0x40),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv04_fifo_context_ctor,
+		.dtor = _nouveau_fifo_context_dtor,
+		.init = _nouveau_fifo_context_init,
+		.fini = _nouveau_fifo_context_fini,
+		.rd32 = _nouveau_fifo_context_rd32,
+		.wr32 = _nouveau_fifo_context_wr32,
+	},
+};
+
+/*******************************************************************************
+ * PFIFO engine
+ ******************************************************************************/
+
+static int
+nv40_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	       struct nouveau_oclass *oclass, void *data, u32 size,
+	       struct nouveau_object **pobject)
+{
+	struct nv04_instmem_priv *imem = nv04_instmem(parent);
+	struct nv04_fifo_priv *priv;
+	int ret;
+
+	ret = nouveau_fifo_create(parent, engine, oclass, 0, 31, &priv);
+	*pobject = nv_object(priv);
 	if (ret)
-		priv->base.base.context_del(chan, engine);
-	return ret;
+		return ret;
+
+	nouveau_ramht_ref(imem->ramht, &priv->ramht);
+	nouveau_gpuobj_ref(imem->ramro, &priv->ramro);
+	nouveau_gpuobj_ref(imem->ramfc, &priv->ramfc);
+
+	nv_subdev(priv)->unit = 0x00000100;
+	nv_subdev(priv)->intr = nv04_fifo_intr;
+	nv_engine(priv)->cclass = &nv40_fifo_cclass;
+	nv_engine(priv)->sclass = nv40_fifo_sclass;
+	priv->base.pause = nv04_fifo_pause;
+	priv->base.start = nv04_fifo_start;
+	priv->ramfc_desc = nv40_ramfc;
+	return 0;
 }
 
 static int
-nv40_fifo_init(struct drm_device *dev, int engine)
+nv40_fifo_init(struct nouveau_object *object)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv40_fifo_priv *priv = nv_engine(dev, engine);
-	int i;
+	struct nv04_fifo_priv *priv = (void *)object;
+	struct nouveau_fb *pfb = nouveau_fb(object);
+	int ret;
 
-	nv_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, 0);
-	nv_mask(dev, NV03_PMC_ENABLE, NV_PMC_ENABLE_PFIFO, NV_PMC_ENABLE_PFIFO);
+	ret = nouveau_fifo_init(&priv->base);
+	if (ret)
+		return ret;
 
-	nv_wr32(dev, 0x002040, 0x000000ff);
-	nv_wr32(dev, 0x002044, 0x2101ffff);
-	nv_wr32(dev, 0x002058, 0x00000001);
+	nv_wr32(priv, 0x002040, 0x000000ff);
+	nv_wr32(priv, 0x002044, 0x2101ffff);
+	nv_wr32(priv, 0x002058, 0x00000001);
 
-	nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
-				       ((dev_priv->ramht->bits - 9) << 16) |
-				       (dev_priv->ramht->gpuobj->addr >> 8));
-	nv_wr32(dev, NV03_PFIFO_RAMRO, priv->ramro->addr >> 8);
+	nv_wr32(priv, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
+				       ((priv->ramht->bits - 9) << 16) |
+				        (priv->ramht->base.addr >> 8));
+	nv_wr32(priv, NV03_PFIFO_RAMRO, priv->ramro->addr >> 8);
 
-	switch (dev_priv->chipset) {
+	switch (nv_device(priv)->chipset) {
 	case 0x47:
 	case 0x49:
 	case 0x4b:
-		nv_wr32(dev, 0x002230, 0x00000001);
+		nv_wr32(priv, 0x002230, 0x00000001);
 	case 0x40:
 	case 0x41:
 	case 0x42:
 	case 0x43:
 	case 0x45:
 	case 0x48:
-		nv_wr32(dev, 0x002220, 0x00030002);
+		nv_wr32(priv, 0x002220, 0x00030002);
 		break;
 	default:
-		nv_wr32(dev, 0x002230, 0x00000000);
-		nv_wr32(dev, 0x002220, ((nvfb_vram_size(dev) - 512 * 1024 +
+		nv_wr32(priv, 0x002230, 0x00000000);
+		nv_wr32(priv, 0x002220, ((pfb->ram.size - 512 * 1024 +
 					 priv->ramfc->addr) >> 16) |
-				       0x00030000);
+					0x00030000);
 		break;
 	}
 
-	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, priv->base.channels);
+	nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH1, priv->base.max);
 
-	nv_wr32(dev, NV03_PFIFO_INTR_0, 0xffffffff);
-	nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xffffffff);
+	nv_wr32(priv, NV03_PFIFO_INTR_0, 0xffffffff);
+	nv_wr32(priv, NV03_PFIFO_INTR_EN_0, 0xffffffff);
 
-	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1);
-	nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
-	nv_wr32(dev, NV03_PFIFO_CACHES, 1);
-
-	for (i = 0; i < priv->base.channels; i++) {
-		if (dev_priv->channels.ptr[i])
-			nv_mask(dev, NV04_PFIFO_MODE, (1 << i), (1 << i));
-	}
-
+	nv_wr32(priv, NV03_PFIFO_CACHE1_PUSH0, 1);
+	nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
+	nv_wr32(priv, NV03_PFIFO_CACHES, 1);
 	return 0;
 }
 
-int
-nv40_fifo_create(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv40_fifo_priv *priv;
-
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	nouveau_gpuobj_ref(nvimem_ramro(dev), &priv->ramro);
-	nouveau_gpuobj_ref(nvimem_ramfc(dev), &priv->ramfc);
-
-	priv->base.base.destroy = nv04_fifo_destroy;
-	priv->base.base.init = nv40_fifo_init;
-	priv->base.base.fini = nv04_fifo_fini;
-	priv->base.base.context_new = nv40_fifo_context_new;
-	priv->base.base.context_del = nv04_fifo_context_del;
-	priv->base.channels = 31;
-	priv->ramfc_desc = nv40_ramfc;
-	dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base;
-
-	nouveau_irq_register(dev, 8, nv04_fifo_isr);
-	return 0;
-}
+struct nouveau_oclass
+nv40_fifo_oclass = {
+	.handle = NV_ENGINE(FIFO, 0x40),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv40_fifo_ctor,
+		.dtor = nv04_fifo_dtor,
+		.init = nv40_fifo_init,
+		.fini = _nouveau_fifo_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
index 7b5b159..4914c3b 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c
@@ -1,126 +1,123 @@
 /*
- * Copyright (C) 2012 Ben Skeggs.
- * All Rights Reserved.
+ * Copyright 2012 Red Hat Inc.
  *
- * 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:
+ * 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 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 OWNER(S) 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.
+ * 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.
  *
+ * Authors: Ben Skeggs
  */
 
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <engine/fifo.h>
+#include <core/client.h>
+#include <core/engctx.h>
 #include <core/ramht.h>
+#include <core/class.h>
+#include <core/math.h>
 
-struct nv50_fifo_priv {
-	struct nouveau_fifo_priv base;
-	struct nouveau_gpuobj *playlist[2];
-	int cur_playlist;
-};
+#include <subdev/timer.h>
+#include <subdev/bar.h>
 
-struct nv50_fifo_chan {
-	struct nouveau_fifo_chan base;
-};
+#include <engine/dmaobj.h>
+#include <engine/fifo.h>
+
+#include "nv50.h"
+
+/*******************************************************************************
+ * FIFO channel objects
+ ******************************************************************************/
 
 void
-nv50_fifo_playlist_update(struct drm_device *dev)
+nv50_fifo_playlist_update(struct nv50_fifo_priv *priv)
 {
-	struct nv50_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO);
+	struct nouveau_bar *bar = nouveau_bar(priv);
 	struct nouveau_gpuobj *cur;
 	int i, p;
 
 	cur = priv->playlist[priv->cur_playlist];
 	priv->cur_playlist = !priv->cur_playlist;
 
-	for (i = 0, p = 0; i < priv->base.channels; i++) {
-		if (nv_rd32(dev, 0x002600 + (i * 4)) & 0x80000000)
+	for (i = priv->base.min, p = 0; i < priv->base.max; i++) {
+		if (nv_rd32(priv, 0x002600 + (i * 4)) & 0x80000000)
 			nv_wo32(cur, p++ * 4, i);
 	}
 
-	nvimem_flush(dev);
+	bar->flush(bar);
 
-	nv_wr32(dev, 0x0032f4, cur->addr >> 12);
-	nv_wr32(dev, 0x0032ec, p);
-	nv_wr32(dev, 0x002500, 0x00000101);
+	nv_wr32(priv, 0x0032f4, cur->addr >> 12);
+	nv_wr32(priv, 0x0032ec, p);
+	nv_wr32(priv, 0x002500, 0x00000101);
 }
 
 static int
-nv50_fifo_context_new(struct nouveau_channel *chan, int engine)
+nv50_fifo_context_attach(struct nouveau_object *parent,
+			 struct nouveau_object *object)
 {
-	struct nv50_fifo_priv *priv = nv_engine(chan->dev, engine);
-	struct nv50_fifo_chan *fctx;
-	struct drm_device *dev = chan->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	u64 ib_offset = chan->pushbuf_base + chan->dma.ib_base * 4;
-	u64 instance = chan->ramin->addr >> 12;
-	unsigned long flags;
-	int ret = 0, i;
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nv50_fifo_base *base = (void *)parent->parent;
+	struct nouveau_gpuobj *ectx = (void *)object;
+	u64 limit = ectx->addr + ectx->size - 1;
+	u64 start = ectx->addr;
+	u32 addr;
 
-	fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL);
-	if (!fctx)
-		return -ENOMEM;
-	nvvm_engref(chan->vm, engine, 1);
-
-	chan->user = ioremap(pci_resource_start(dev->pdev, 0) +
-			     NV50_USER(chan->id), PAGE_SIZE);
-	if (!chan->user) {
-		ret = -ENOMEM;
-		goto error;
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_SW   : return 0;
+	case NVDEV_ENGINE_GR   : addr = 0x0000; break;
+	case NVDEV_ENGINE_MPEG : addr = 0x0060; break;
+	default:
+		return -EINVAL;
 	}
 
-	for (i = 0; i < 0x100; i += 4)
-		nv_wo32(chan->ramin, i, 0x00000000);
-	nv_wo32(chan->ramin, 0x3c, 0x403f6078);
-	nv_wo32(chan->ramin, 0x40, 0x00000000);
-	nv_wo32(chan->ramin, 0x44, 0x01003fff);
-	nv_wo32(chan->ramin, 0x48, chan->pushbuf->node->offset >> 4);
-	nv_wo32(chan->ramin, 0x50, lower_32_bits(ib_offset));
-	nv_wo32(chan->ramin, 0x54, upper_32_bits(ib_offset) |
-				   drm_order(chan->dma.ib_max + 1) << 16);
-	nv_wo32(chan->ramin, 0x60, 0x7fffffff);
-	nv_wo32(chan->ramin, 0x78, 0x00000000);
-	nv_wo32(chan->ramin, 0x7c, 0x30000001);
-	nv_wo32(chan->ramin, 0x80, ((chan->ramht->bits - 9) << 27) |
-				   (4 << 24) /* SEARCH_FULL */ |
-				   (chan->ramht->gpuobj->node->offset >> 4));
-
-	nvimem_flush(dev);
-
-	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv_wr32(dev, 0x002600 + (chan->id * 4), 0x80000000 | instance);
-	nv50_fifo_playlist_update(dev);
-	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
-
-error:
-	if (ret)
-		priv->base.base.context_del(chan, engine);
-	return ret;
+	nv_wo32(base->eng, addr + 0x00, 0x00190000);
+	nv_wo32(base->eng, addr + 0x04, lower_32_bits(limit));
+	nv_wo32(base->eng, addr + 0x08, lower_32_bits(start));
+	nv_wo32(base->eng, addr + 0x0c, upper_32_bits(limit) << 24 |
+					upper_32_bits(start));
+	nv_wo32(base->eng, addr + 0x10, 0x00000000);
+	nv_wo32(base->eng, addr + 0x14, 0x00000000);
+	bar->flush(bar);
+	return 0;
 }
 
-static bool
-nv50_fifo_kickoff(struct nouveau_channel *chan)
+static int
+nv50_fifo_context_detach(struct nouveau_object *parent, bool suspend,
+			 struct nouveau_object *object)
 {
-	struct drm_device *dev = chan->dev;
-	bool done = true;
-	u32 me;
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nv50_fifo_priv *priv = (void *)parent->engine;
+	struct nv50_fifo_base *base = (void *)parent->parent;
+	struct nv50_fifo_chan *chan = (void *)parent;
+	u32 addr, me;
+	int ret = 0;
+
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_SW   : return 0;
+	case NVDEV_ENGINE_GR   : addr = 0x0000; break;
+	case NVDEV_ENGINE_MPEG : addr = 0x0060; break;
+	default:
+		return -EINVAL;
+	}
+
+	nv_wo32(base->eng, addr + 0x00, 0x00000000);
+	nv_wo32(base->eng, addr + 0x04, 0x00000000);
+	nv_wo32(base->eng, addr + 0x08, 0x00000000);
+	nv_wo32(base->eng, addr + 0x0c, 0x00000000);
+	nv_wo32(base->eng, addr + 0x10, 0x00000000);
+	nv_wo32(base->eng, addr + 0x14, 0x00000000);
+	bar->flush(bar);
 
 	/* HW bug workaround:
 	 *
@@ -134,159 +131,308 @@
 	 * there's also a "ignore these engines" bitmask reg we can use
 	 * if we hit the issue there..
 	 */
-
-	/* PME: make sure engine is enabled */
-	me = nv_mask(dev, 0x00b860, 0x00000001, 0x00000001);
+	me = nv_mask(priv, 0x00b860, 0x00000001, 0x00000001);
 
 	/* do the kickoff... */
-	nv_wr32(dev, 0x0032fc, chan->ramin->addr >> 12);
-	if (!nv_wait_ne(dev, 0x0032fc, 0xffffffff, 0xffffffff)) {
-		NV_INFO(dev, "PFIFO: channel %d unload timeout\n", chan->id);
-		done = false;
+	nv_wr32(priv, 0x0032fc, nv_gpuobj(base)->addr >> 12);
+	if (!nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff)) {
+		nv_error(priv, "channel %d unload timeout\n", chan->base.chid);
+		if (suspend)
+			ret = -EBUSY;
 	}
 
-	/* restore any engine states we changed, and exit */
-	nv_wr32(dev, 0x00b860, me);
-	return done;
-}
-
-static void
-nv50_fifo_context_del(struct nouveau_channel *chan, int engine)
-{
-	struct nv50_fifo_chan *fctx = chan->engctx[engine];
-	struct drm_device *dev = chan->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	unsigned long flags;
-
-	/* remove channel from playlist, will context switch if active */
-	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv_mask(dev, 0x002600 + (chan->id * 4), 0x80000000, 0x00000000);
-	nv50_fifo_playlist_update(dev);
-
-	/* tell any engines on this channel to unload their contexts */
-	nv50_fifo_kickoff(chan);
-
-	nv_wr32(dev, 0x002600 + (chan->id * 4), 0x00000000);
-	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
-
-	/* clean up */
-	if (chan->user) {
-		iounmap(chan->user);
-		chan->user = NULL;
-	}
-
-	nvvm_engref(chan->vm, engine, -1);
-	chan->engctx[engine] = NULL;
-	kfree(fctx);
+	nv_wr32(priv, 0x00b860, me);
+	return ret;
 }
 
 static int
-nv50_fifo_init(struct drm_device *dev, int engine)
+nv50_fifo_object_attach(struct nouveau_object *parent,
+			struct nouveau_object *object, u32 handle)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	u32 instance;
-	int i;
+	struct nv50_fifo_chan *chan = (void *)parent;
+	u32 context;
 
-	nv_mask(dev, 0x000200, 0x00000100, 0x00000000);
-	nv_mask(dev, 0x000200, 0x00000100, 0x00000100);
-	nv_wr32(dev, 0x00250c, 0x6f3cfc34);
-	nv_wr32(dev, 0x002044, 0x01003fff);
+	if (nv_iclass(object, NV_GPUOBJ_CLASS))
+		context = nv_gpuobj(object)->node->offset >> 4;
+	else
+		context = 0x00000004; /* just non-zero */
 
-	nv_wr32(dev, 0x002100, 0xffffffff);
-	nv_wr32(dev, 0x002140, 0xffffffff);
-
-	for (i = 0; i < 128; i++) {
-		struct nouveau_channel *chan = dev_priv->channels.ptr[i];
-		if (chan && chan->engctx[engine])
-			instance = 0x80000000 | chan->ramin->addr >> 12;
-		else
-			instance = 0x00000000;
-		nv_wr32(dev, 0x002600 + (i * 4), instance);
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_DMAOBJ:
+	case NVDEV_ENGINE_SW    : context |= 0x00000000; break;
+	case NVDEV_ENGINE_GR    : context |= 0x00100000; break;
+	case NVDEV_ENGINE_MPEG  : context |= 0x00200000; break;
+	default:
+		return -EINVAL;
 	}
 
-	nv50_fifo_playlist_update(dev);
+	return nouveau_ramht_insert(chan->ramht, 0, handle, context);
+}
 
-	nv_wr32(dev, 0x003200, 1);
-	nv_wr32(dev, 0x003250, 1);
-	nv_wr32(dev, 0x002500, 1);
-	return 0;
+void
+nv50_fifo_object_detach(struct nouveau_object *parent, int cookie)
+{
+	struct nv50_fifo_chan *chan = (void *)parent;
+	nouveau_ramht_remove(chan->ramht, cookie);
 }
 
 static int
-nv50_fifo_fini(struct drm_device *dev, int engine, bool suspend)
+nv50_fifo_chan_ctor(struct nouveau_object *parent,
+		    struct nouveau_object *engine,
+		    struct nouveau_oclass *oclass, void *data, u32 size,
+		    struct nouveau_object **pobject)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv50_fifo_priv *priv = nv_engine(dev, engine);
-	int i;
+	struct nv_channel_ind_class *args = data;
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nv50_fifo_base *base = (void *)parent;
+	struct nv50_fifo_chan *chan;
+	u64 ioffset, ilength;
+	int ret;
 
-	/* set playlist length to zero, fifo will unload context */
-	nv_wr32(dev, 0x0032ec, 0);
+	if (size < sizeof(*args))
+		return -EINVAL;
 
-	/* tell all connected engines to unload their contexts */
-	for (i = 0; i < priv->base.channels; i++) {
-		struct nouveau_channel *chan = dev_priv->channels.ptr[i];
-		if (chan && !nv50_fifo_kickoff(chan))
-			return -EBUSY;
-	}
+	ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000,
+					  0x2000, args->pushbuf,
+					  (1 << NVDEV_ENGINE_DMAOBJ) |
+					  (1 << NVDEV_ENGINE_SW) |
+					  (1 << NVDEV_ENGINE_GR) |
+					  (1 << NVDEV_ENGINE_MPEG), &chan);
+	*pobject = nv_object(chan);
+	if (ret)
+		return ret;
 
-	nv_wr32(dev, 0x002140, 0);
+	nv_parent(chan)->context_attach = nv50_fifo_context_attach;
+	nv_parent(chan)->context_detach = nv50_fifo_context_detach;
+	nv_parent(chan)->object_attach = nv50_fifo_object_attach;
+	nv_parent(chan)->object_detach = nv50_fifo_object_detach;
+
+	ret = nouveau_ramht_new(parent, parent, 0x8000, 16, &chan->ramht);
+	if (ret)
+		return ret;
+
+	ioffset = args->ioffset;
+	ilength = log2i(args->ilength / 8);
+
+	nv_wo32(base->ramfc, 0x3c, 0x403f6078);
+	nv_wo32(base->ramfc, 0x44, 0x01003fff);
+	nv_wo32(base->ramfc, 0x48, chan->base.pushgpu->node->offset >> 4);
+	nv_wo32(base->ramfc, 0x50, lower_32_bits(ioffset));
+	nv_wo32(base->ramfc, 0x54, upper_32_bits(ioffset) | (ilength << 16));
+	nv_wo32(base->ramfc, 0x60, 0x7fffffff);
+	nv_wo32(base->ramfc, 0x78, 0x00000000);
+	nv_wo32(base->ramfc, 0x7c, 0x30000001);
+	nv_wo32(base->ramfc, 0x80, ((chan->ramht->bits - 9) << 27) |
+				   (4 << 24) /* SEARCH_FULL */ |
+				   (chan->ramht->base.node->offset >> 4));
+	bar->flush(bar);
 	return 0;
 }
 
 void
-nv50_fifo_tlb_flush(struct drm_device *dev, int engine)
+nv50_fifo_chan_dtor(struct nouveau_object *object)
 {
-	nv50_vm_flush_engine(dev, 5);
+	struct nv50_fifo_chan *chan = (void *)object;
+	nouveau_ramht_ref(NULL, &chan->ramht);
+	nouveau_fifo_channel_destroy(&chan->base);
 }
 
-void
-nv50_fifo_destroy(struct drm_device *dev, int engine)
+static int
+nv50_fifo_chan_init(struct nouveau_object *object)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv50_fifo_priv *priv = nv_engine(dev, engine);
+	struct nv50_fifo_priv *priv = (void *)object->engine;
+	struct nv50_fifo_base *base = (void *)object->parent;
+	struct nv50_fifo_chan *chan = (void *)object;
+	struct nouveau_gpuobj *ramfc = base->ramfc;
+	u32 chid = chan->base.chid;
+	int ret;
 
-	nouveau_irq_unregister(dev, 8);
+	ret = nouveau_fifo_channel_init(&chan->base);
+	if (ret)
+		return ret;
 
-	nouveau_gpuobj_ref(NULL, &priv->playlist[0]);
-	nouveau_gpuobj_ref(NULL, &priv->playlist[1]);
-
-	dev_priv->eng[engine] = NULL;
-	kfree(priv);
+	nv_wr32(priv, 0x002600 + (chid * 4), 0x80000000 | ramfc->addr >> 12);
+	nv50_fifo_playlist_update(priv);
+	return 0;
 }
 
 int
-nv50_fifo_create(struct drm_device *dev)
+nv50_fifo_chan_fini(struct nouveau_object *object, bool suspend)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv50_fifo_priv *priv = (void *)object->engine;
+	struct nv50_fifo_chan *chan = (void *)object;
+	u32 chid = chan->base.chid;
+
+	/* remove channel from playlist, fifo will unload context */
+	nv_mask(priv, 0x002600 + (chid * 4), 0x80000000, 0x00000000);
+	nv50_fifo_playlist_update(priv);
+	nv_wr32(priv, 0x002600 + (chid * 4), 0x00000000);
+
+	return nouveau_fifo_channel_fini(&chan->base, suspend);
+}
+
+static struct nouveau_ofuncs
+nv50_fifo_ofuncs = {
+	.ctor = nv50_fifo_chan_ctor,
+	.dtor = nv50_fifo_chan_dtor,
+	.init = nv50_fifo_chan_init,
+	.fini = nv50_fifo_chan_fini,
+	.rd32 = _nouveau_fifo_channel_rd32,
+	.wr32 = _nouveau_fifo_channel_wr32,
+};
+
+static struct nouveau_oclass
+nv50_fifo_sclass[] = {
+	{ 0x506f, &nv50_fifo_ofuncs },
+	{}
+};
+
+/*******************************************************************************
+ * FIFO context - basically just the instmem reserved for the channel
+ ******************************************************************************/
+
+static int
+nv50_fifo_context_ctor(struct nouveau_object *parent,
+		       struct nouveau_object *engine,
+		       struct nouveau_oclass *oclass, void *data, u32 size,
+		       struct nouveau_object **pobject)
+{
+	struct nv50_fifo_base *base;
+	int ret;
+
+	ret = nouveau_fifo_context_create(parent, engine, oclass, NULL, 0x10000,
+				          0x1000, NVOBJ_FLAG_HEAP, &base);
+	*pobject = nv_object(base);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_new(parent, nv_object(base), 0x0200, 0x1000,
+				 NVOBJ_FLAG_ZERO_ALLOC, &base->ramfc);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_new(parent, nv_object(base), 0x1200, 0,
+				 NVOBJ_FLAG_ZERO_ALLOC, &base->eng);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_new(parent, nv_object(base), 0x4000, 0, 0,
+				&base->pgd);
+	if (ret)
+		return ret;
+
+	ret = nouveau_vm_ref(nouveau_client(parent)->vm, &base->vm, base->pgd);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void
+nv50_fifo_context_dtor(struct nouveau_object *object)
+{
+	struct nv50_fifo_base *base = (void *)object;
+	nouveau_vm_ref(NULL, &base->vm, base->pgd);
+	nouveau_gpuobj_ref(NULL, &base->pgd);
+	nouveau_gpuobj_ref(NULL, &base->eng);
+	nouveau_gpuobj_ref(NULL, &base->ramfc);
+	nouveau_gpuobj_ref(NULL, &base->cache);
+	nouveau_fifo_context_destroy(&base->base);
+}
+
+static struct nouveau_oclass
+nv50_fifo_cclass = {
+	.handle = NV_ENGCTX(FIFO, 0x50),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv50_fifo_context_ctor,
+		.dtor = nv50_fifo_context_dtor,
+		.init = _nouveau_fifo_context_init,
+		.fini = _nouveau_fifo_context_fini,
+		.rd32 = _nouveau_fifo_context_rd32,
+		.wr32 = _nouveau_fifo_context_wr32,
+	},
+};
+
+/*******************************************************************************
+ * PFIFO engine
+ ******************************************************************************/
+
+static int
+nv50_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	       struct nouveau_oclass *oclass, void *data, u32 size,
+	       struct nouveau_object **pobject)
+{
 	struct nv50_fifo_priv *priv;
 	int ret;
 
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	priv->base.base.destroy = nv50_fifo_destroy;
-	priv->base.base.init = nv50_fifo_init;
-	priv->base.base.fini = nv50_fifo_fini;
-	priv->base.base.context_new = nv50_fifo_context_new;
-	priv->base.base.context_del = nv50_fifo_context_del;
-	priv->base.base.tlb_flush = nv50_fifo_tlb_flush;
-	priv->base.channels = 127;
-	dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base;
-
-	ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 4, 0x1000,
-				 NVOBJ_FLAG_ZERO_ALLOC, &priv->playlist[0]);
+	ret = nouveau_fifo_create(parent, engine, oclass, 1, 127, &priv);
+	*pobject = nv_object(priv);
 	if (ret)
-		goto error;
+		return ret;
 
-	ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 4, 0x1000,
-				 NVOBJ_FLAG_ZERO_ALLOC, &priv->playlist[1]);
+	ret = nouveau_gpuobj_new(parent, NULL, 128 * 4, 0x1000, 0,
+				&priv->playlist[0]);
 	if (ret)
-		goto error;
+		return ret;
 
-	nouveau_irq_register(dev, 8, nv04_fifo_isr);
-error:
+	ret = nouveau_gpuobj_new(parent, NULL, 128 * 4, 0x1000, 0,
+				&priv->playlist[1]);
 	if (ret)
-		priv->base.base.destroy(dev, NVOBJ_ENGINE_FIFO);
-	return ret;
+		return ret;
+
+	nv_subdev(priv)->unit = 0x00000100;
+	nv_subdev(priv)->intr = nv04_fifo_intr;
+	nv_engine(priv)->cclass = &nv50_fifo_cclass;
+	nv_engine(priv)->sclass = nv50_fifo_sclass;
+	return 0;
 }
+
+void
+nv50_fifo_dtor(struct nouveau_object *object)
+{
+	struct nv50_fifo_priv *priv = (void *)object;
+
+	nouveau_gpuobj_ref(NULL, &priv->playlist[1]);
+	nouveau_gpuobj_ref(NULL, &priv->playlist[0]);
+
+	nouveau_fifo_destroy(&priv->base);
+}
+
+int
+nv50_fifo_init(struct nouveau_object *object)
+{
+	struct nv50_fifo_priv *priv = (void *)object;
+	int ret, i;
+
+	ret = nouveau_fifo_init(&priv->base);
+	if (ret)
+		return ret;
+
+	nv_mask(priv, 0x000200, 0x00000100, 0x00000000);
+	nv_mask(priv, 0x000200, 0x00000100, 0x00000100);
+	nv_wr32(priv, 0x00250c, 0x6f3cfc34);
+	nv_wr32(priv, 0x002044, 0x01003fff);
+
+	nv_wr32(priv, 0x002100, 0xffffffff);
+	nv_wr32(priv, 0x002140, 0xffffffff);
+
+	for (i = 0; i < 128; i++)
+		nv_wr32(priv, 0x002600 + (i * 4), 0x00000000);
+	nv50_fifo_playlist_update(priv);
+
+	nv_wr32(priv, 0x003200, 0x00000001);
+	nv_wr32(priv, 0x003250, 0x00000001);
+	nv_wr32(priv, 0x002500, 0x00000001);
+	return 0;
+}
+
+struct nouveau_oclass
+nv50_fifo_oclass = {
+	.handle = NV_ENGINE(FIFO, 0x50),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv50_fifo_ctor,
+		.dtor = nv50_fifo_dtor,
+		.init = nv50_fifo_init,
+		.fini = _nouveau_fifo_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.h b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.h
new file mode 100644
index 0000000..3a9ceb3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.h
@@ -0,0 +1,36 @@
+#ifndef __NV50_FIFO_H__
+#define __NV50_FIFO_H__
+
+struct nv50_fifo_priv {
+	struct nouveau_fifo base;
+	struct nouveau_gpuobj *playlist[2];
+	int cur_playlist;
+};
+
+struct nv50_fifo_base {
+	struct nouveau_fifo_base base;
+	struct nouveau_gpuobj *ramfc;
+	struct nouveau_gpuobj *cache;
+	struct nouveau_gpuobj *eng;
+	struct nouveau_gpuobj *pgd;
+	struct nouveau_vm *vm;
+};
+
+struct nv50_fifo_chan {
+	struct nouveau_fifo_chan base;
+	u32 subc[8];
+	struct nouveau_ramht *ramht;
+};
+
+void nv50_fifo_playlist_update(struct nv50_fifo_priv *);
+
+void nv50_fifo_object_detach(struct nouveau_object *, int);
+void nv50_fifo_chan_dtor(struct nouveau_object *);
+int  nv50_fifo_chan_fini(struct nouveau_object *, bool);
+
+void nv50_fifo_context_dtor(struct nouveau_object *);
+
+void nv50_fifo_dtor(struct nouveau_object *);
+int  nv50_fifo_init(struct nouveau_object *);
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
index 63a4941..765affb 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c
@@ -1,249 +1,343 @@
 /*
- * Copyright (C) 2012 Ben Skeggs.
- * All Rights Reserved.
+ * Copyright 2012 Red Hat Inc.
  *
- * 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:
+ * 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 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 OWNER(S) 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.
+ * 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.
  *
+ * Authors: Ben Skeggs
  */
 
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <engine/fifo.h>
+#include <core/os.h>
+#include <core/client.h>
+#include <core/engctx.h>
 #include <core/ramht.h>
+#include <core/class.h>
+#include <core/math.h>
 
-struct nv84_fifo_priv {
-	struct nouveau_fifo_priv base;
-	struct nouveau_gpuobj *playlist[2];
-	int cur_playlist;
-};
+#include <subdev/timer.h>
+#include <subdev/bar.h>
 
-struct nv84_fifo_chan {
-	struct nouveau_fifo_chan base;
-	struct nouveau_gpuobj *ramfc;
-	struct nouveau_gpuobj *cache;
-};
+#include <engine/dmaobj.h>
+#include <engine/fifo.h>
+
+#include "nv50.h"
+
+/*******************************************************************************
+ * FIFO channel objects
+ ******************************************************************************/
 
 static int
-nv84_fifo_context_new(struct nouveau_channel *chan, int engine)
+nv84_fifo_context_attach(struct nouveau_object *parent,
+			 struct nouveau_object *object)
 {
-	struct nv84_fifo_priv *priv = nv_engine(chan->dev, engine);
-	struct nv84_fifo_chan *fctx;
-	struct drm_device *dev = chan->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-        u64 ib_offset = chan->pushbuf_base + chan->dma.ib_base * 4;
-	u64 instance;
-	unsigned long flags;
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nv50_fifo_base *base = (void *)parent->parent;
+	struct nouveau_gpuobj *ectx = (void *)object;
+	u64 limit = ectx->addr + ectx->size - 1;
+	u64 start = ectx->addr;
+	u32 addr;
+
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_SW   : return 0;
+	case NVDEV_ENGINE_GR   : addr = 0x0020; break;
+	case NVDEV_ENGINE_MPEG : addr = 0x0060; break;
+	case NVDEV_ENGINE_CRYPT: addr = 0x00a0; break;
+	case NVDEV_ENGINE_COPY0: addr = 0x00c0; break;
+	default:
+		return -EINVAL;
+	}
+
+	nv_wo32(base->eng, addr + 0x00, 0x00190000);
+	nv_wo32(base->eng, addr + 0x04, lower_32_bits(limit));
+	nv_wo32(base->eng, addr + 0x08, lower_32_bits(start));
+	nv_wo32(base->eng, addr + 0x0c, upper_32_bits(limit) << 24 |
+					upper_32_bits(start));
+	nv_wo32(base->eng, addr + 0x10, 0x00000000);
+	nv_wo32(base->eng, addr + 0x14, 0x00000000);
+	bar->flush(bar);
+	return 0;
+}
+
+static int
+nv84_fifo_context_detach(struct nouveau_object *parent, bool suspend,
+			 struct nouveau_object *object)
+{
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nv50_fifo_priv *priv = (void *)parent->engine;
+	struct nv50_fifo_base *base = (void *)parent->parent;
+	struct nv50_fifo_chan *chan = (void *)parent;
+	u32 addr;
+
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_SW   : return 0;
+	case NVDEV_ENGINE_GR   : addr = 0x0020; break;
+	case NVDEV_ENGINE_MPEG : addr = 0x0060; break;
+	case NVDEV_ENGINE_CRYPT: addr = 0x00a0; break;
+	case NVDEV_ENGINE_COPY0: addr = 0x00c0; break;
+	default:
+		return -EINVAL;
+	}
+
+	nv_wo32(base->eng, addr + 0x00, 0x00000000);
+	nv_wo32(base->eng, addr + 0x04, 0x00000000);
+	nv_wo32(base->eng, addr + 0x08, 0x00000000);
+	nv_wo32(base->eng, addr + 0x0c, 0x00000000);
+	nv_wo32(base->eng, addr + 0x10, 0x00000000);
+	nv_wo32(base->eng, addr + 0x14, 0x00000000);
+	bar->flush(bar);
+
+	nv_wr32(priv, 0x0032fc, nv_gpuobj(base)->addr >> 12);
+	if (!nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff)) {
+		nv_error(priv, "channel %d unload timeout\n", chan->base.chid);
+		if (suspend)
+			return -EBUSY;
+	}
+	return 0;
+}
+
+static int
+nv84_fifo_object_attach(struct nouveau_object *parent,
+			struct nouveau_object *object, u32 handle)
+{
+	struct nv50_fifo_chan *chan = (void *)parent;
+	u32 context;
+
+	if (nv_iclass(object, NV_GPUOBJ_CLASS))
+		context = nv_gpuobj(object)->node->offset >> 4;
+	else
+		context = 0x00000004; /* just non-zero */
+
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_DMAOBJ:
+	case NVDEV_ENGINE_SW    : context |= 0x00000000; break;
+	case NVDEV_ENGINE_GR    : context |= 0x00100000; break;
+	case NVDEV_ENGINE_MPEG  :
+	case NVDEV_ENGINE_PPP   : context |= 0x00200000; break;
+	case NVDEV_ENGINE_ME    :
+	case NVDEV_ENGINE_COPY0 : context |= 0x00300000; break;
+	case NVDEV_ENGINE_VP    : context |= 0x00400000; break;
+	case NVDEV_ENGINE_CRYPT :
+	case NVDEV_ENGINE_UNK1C1: context |= 0x00500000; break;
+	case NVDEV_ENGINE_BSP   : context |= 0x00600000; break;
+	default:
+		return -EINVAL;
+	}
+
+	return nouveau_ramht_insert(chan->ramht, 0, handle, context);
+}
+
+static int
+nv84_fifo_chan_ctor(struct nouveau_object *parent,
+		    struct nouveau_object *engine,
+		    struct nouveau_oclass *oclass, void *data, u32 size,
+		    struct nouveau_object **pobject)
+{
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nv50_fifo_base *base = (void *)parent;
+	struct nv50_fifo_chan *chan;
+	struct nv_channel_ind_class *args = data;
+	u64 ioffset, ilength;
 	int ret;
 
-	fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL);
-	if (!fctx)
-		return -ENOMEM;
-	nvvm_engref(chan->vm, engine, 1);
+	if (size < sizeof(*args))
+		return -EINVAL;
 
-	chan->user = ioremap(pci_resource_start(dev->pdev, 0) +
-			     NV50_USER(chan->id), PAGE_SIZE);
-	if (!chan->user) {
-		ret = -ENOMEM;
-		goto error;
-	}
-
-	ret = nouveau_gpuobj_new(dev, chan, 256, 256, NVOBJ_FLAG_ZERO_ALLOC |
-				 NVOBJ_FLAG_ZERO_FREE, &fctx->ramfc);
+	ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000,
+					  0x2000, args->pushbuf,
+					  (1 << NVDEV_ENGINE_DMAOBJ) |
+					  (1 << NVDEV_ENGINE_SW) |
+					  (1 << NVDEV_ENGINE_GR) |
+					  (1 << NVDEV_ENGINE_MPEG) |
+					  (1 << NVDEV_ENGINE_ME) |
+					  (1 << NVDEV_ENGINE_VP) |
+					  (1 << NVDEV_ENGINE_CRYPT) |
+					  (1 << NVDEV_ENGINE_BSP) |
+					  (1 << NVDEV_ENGINE_PPP) |
+					  (1 << NVDEV_ENGINE_COPY0) |
+					  (1 << NVDEV_ENGINE_UNK1C1), &chan);
+	*pobject = nv_object(chan);
 	if (ret)
-		goto error;
+		return ret;
 
-	instance = fctx->ramfc->addr >> 8;
-
-	ret = nouveau_gpuobj_new(dev, chan, 4096, 1024, 0, &fctx->cache);
+	ret = nouveau_ramht_new(parent, parent, 0x8000, 16, &chan->ramht);
 	if (ret)
-		goto error;
+		return ret;
 
-	nv_wo32(fctx->ramfc, 0x3c, 0x403f6078);
-	nv_wo32(fctx->ramfc, 0x40, 0x00000000);
-	nv_wo32(fctx->ramfc, 0x44, 0x01003fff);
-	nv_wo32(fctx->ramfc, 0x48, chan->pushbuf->node->offset >> 4);
-	nv_wo32(fctx->ramfc, 0x50, lower_32_bits(ib_offset));
-	nv_wo32(fctx->ramfc, 0x54, upper_32_bits(ib_offset) |
-				   drm_order(chan->dma.ib_max + 1) << 16);
-	nv_wo32(fctx->ramfc, 0x60, 0x7fffffff);
-	nv_wo32(fctx->ramfc, 0x78, 0x00000000);
-	nv_wo32(fctx->ramfc, 0x7c, 0x30000001);
-	nv_wo32(fctx->ramfc, 0x80, ((chan->ramht->bits - 9) << 27) |
+	nv_parent(chan)->context_attach = nv84_fifo_context_attach;
+	nv_parent(chan)->context_detach = nv84_fifo_context_detach;
+	nv_parent(chan)->object_attach = nv84_fifo_object_attach;
+	nv_parent(chan)->object_detach = nv50_fifo_object_detach;
+
+	ioffset = args->ioffset;
+	ilength = log2i(args->ilength / 8);
+
+	nv_wo32(base->ramfc, 0x3c, 0x403f6078);
+	nv_wo32(base->ramfc, 0x44, 0x01003fff);
+	nv_wo32(base->ramfc, 0x48, chan->base.pushgpu->node->offset >> 4);
+	nv_wo32(base->ramfc, 0x50, lower_32_bits(ioffset));
+	nv_wo32(base->ramfc, 0x54, upper_32_bits(ioffset) | (ilength << 16));
+	nv_wo32(base->ramfc, 0x60, 0x7fffffff);
+	nv_wo32(base->ramfc, 0x78, 0x00000000);
+	nv_wo32(base->ramfc, 0x7c, 0x30000001);
+	nv_wo32(base->ramfc, 0x80, ((chan->ramht->bits - 9) << 27) |
 				   (4 << 24) /* SEARCH_FULL */ |
-				   (chan->ramht->gpuobj->node->offset >> 4));
-	nv_wo32(fctx->ramfc, 0x88, fctx->cache->addr >> 10);
-	nv_wo32(fctx->ramfc, 0x98, chan->ramin->addr >> 12);
+				   (chan->ramht->base.node->offset >> 4));
+	nv_wo32(base->ramfc, 0x88, base->cache->addr >> 10);
+	nv_wo32(base->ramfc, 0x98, nv_gpuobj(base)->addr >> 12);
+	bar->flush(bar);
+	return 0;
+}
 
-	nv_wo32(chan->ramin, 0x00, chan->id);
-	nv_wo32(chan->ramin, 0x04, fctx->ramfc->addr >> 8);
+static int
+nv84_fifo_chan_init(struct nouveau_object *object)
+{
+	struct nv50_fifo_priv *priv = (void *)object->engine;
+	struct nv50_fifo_base *base = (void *)object->parent;
+	struct nv50_fifo_chan *chan = (void *)object;
+	struct nouveau_gpuobj *ramfc = base->ramfc;
+	u32 chid = chan->base.chid;
+	int ret;
 
-	nvimem_flush(dev);
-
-	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv_wr32(dev, 0x002600 + (chan->id * 4), 0x80000000 | instance);
-	nv50_fifo_playlist_update(dev);
-	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
-
-error:
+	ret = nouveau_fifo_channel_init(&chan->base);
 	if (ret)
-		priv->base.base.context_del(chan, engine);
-	return ret;
-}
+		return ret;
 
-static void
-nv84_fifo_context_del(struct nouveau_channel *chan, int engine)
-{
-	struct nv84_fifo_chan *fctx = chan->engctx[engine];
-	struct drm_device *dev = chan->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	unsigned long flags;
-	u32 save;
-
-	/* remove channel from playlist, will context switch if active */
-	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	nv_mask(dev, 0x002600 + (chan->id * 4), 0x80000000, 0x00000000);
-	nv50_fifo_playlist_update(dev);
-
-	save = nv_mask(dev, 0x002520, 0x0000003f, 0x15);
-
-	/* tell any engines on this channel to unload their contexts */
-	nv_wr32(dev, 0x0032fc, chan->ramin->addr >> 12);
-	if (!nv_wait_ne(dev, 0x0032fc, 0xffffffff, 0xffffffff))
-		NV_INFO(dev, "PFIFO: channel %d unload timeout\n", chan->id);
-
-	nv_wr32(dev, 0x002520, save);
-
-	nv_wr32(dev, 0x002600 + (chan->id * 4), 0x00000000);
-	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
-
-	/* clean up */
-	if (chan->user) {
-		iounmap(chan->user);
-		chan->user = NULL;
-	}
-
-	nouveau_gpuobj_ref(NULL, &fctx->ramfc);
-	nouveau_gpuobj_ref(NULL, &fctx->cache);
-
-	nvvm_engref(chan->vm, engine, -1);
-	chan->engctx[engine] = NULL;
-	kfree(fctx);
-}
-
-static int
-nv84_fifo_init(struct drm_device *dev, int engine)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv84_fifo_chan *fctx;
-	u32 instance;
-	int i;
-
-	nv_mask(dev, 0x000200, 0x00000100, 0x00000000);
-	nv_mask(dev, 0x000200, 0x00000100, 0x00000100);
-	nv_wr32(dev, 0x00250c, 0x6f3cfc34);
-	nv_wr32(dev, 0x002044, 0x01003fff);
-
-	nv_wr32(dev, 0x002100, 0xffffffff);
-	nv_wr32(dev, 0x002140, 0xffffffff);
-
-	for (i = 0; i < 128; i++) {
-		struct nouveau_channel *chan = dev_priv->channels.ptr[i];
-		if (chan && (fctx = chan->engctx[engine]))
-			instance = 0x80000000 | fctx->ramfc->addr >> 8;
-		else
-			instance = 0x00000000;
-		nv_wr32(dev, 0x002600 + (i * 4), instance);
-	}
-
-	nv50_fifo_playlist_update(dev);
-
-	nv_wr32(dev, 0x003200, 1);
-	nv_wr32(dev, 0x003250, 1);
-	nv_wr32(dev, 0x002500, 1);
+	nv_wr32(priv, 0x002600 + (chid * 4), 0x80000000 | ramfc->addr >> 8);
+	nv50_fifo_playlist_update(priv);
 	return 0;
 }
 
-static int
-nv84_fifo_fini(struct drm_device *dev, int engine, bool suspend)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv84_fifo_priv *priv = nv_engine(dev, engine);
-	int i;
-	u32 save;
+static struct nouveau_ofuncs
+nv84_fifo_ofuncs = {
+	.ctor = nv84_fifo_chan_ctor,
+	.dtor = nv50_fifo_chan_dtor,
+	.init = nv84_fifo_chan_init,
+	.fini = nv50_fifo_chan_fini,
+	.rd32 = _nouveau_fifo_channel_rd32,
+	.wr32 = _nouveau_fifo_channel_wr32,
+};
 
-	/* set playlist length to zero, fifo will unload context */
-	nv_wr32(dev, 0x0032ec, 0);
+static struct nouveau_oclass
+nv84_fifo_sclass[] = {
+	{ 0x826f, &nv84_fifo_ofuncs },
+	{}
+};
 
-	save = nv_mask(dev, 0x002520, 0x0000003f, 0x15);
-
-	/* tell all connected engines to unload their contexts */
-	for (i = 0; i < priv->base.channels; i++) {
-		struct nouveau_channel *chan = dev_priv->channels.ptr[i];
-		if (chan)
-			nv_wr32(dev, 0x0032fc, chan->ramin->addr >> 12);
-		if (!nv_wait_ne(dev, 0x0032fc, 0xffffffff, 0xffffffff)) {
-			NV_INFO(dev, "PFIFO: channel %d unload timeout\n", i);
-			return -EBUSY;
-		}
-	}
-
-	nv_wr32(dev, 0x002520, save);
-	nv_wr32(dev, 0x002140, 0);
-	return 0;
-}
+/*******************************************************************************
+ * FIFO context - basically just the instmem reserved for the channel
+ ******************************************************************************/
 
 int
-nv84_fifo_create(struct drm_device *dev)
+nv84_fifo_context_ctor(struct nouveau_object *parent,
+		       struct nouveau_object *engine,
+		       struct nouveau_oclass *oclass, void *data, u32 size,
+		       struct nouveau_object **pobject)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv84_fifo_priv *priv;
+	struct nv50_fifo_base *base;
 	int ret;
 
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	priv->base.base.destroy = nv50_fifo_destroy;
-	priv->base.base.init = nv84_fifo_init;
-	priv->base.base.fini = nv84_fifo_fini;
-	priv->base.base.context_new = nv84_fifo_context_new;
-	priv->base.base.context_del = nv84_fifo_context_del;
-	priv->base.base.tlb_flush = nv50_fifo_tlb_flush;
-	priv->base.channels = 127;
-	dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base;
-
-	ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 4, 0x1000,
-				 NVOBJ_FLAG_ZERO_ALLOC, &priv->playlist[0]);
+	ret = nouveau_fifo_context_create(parent, engine, oclass, NULL, 0x10000,
+				          0x1000, NVOBJ_FLAG_HEAP, &base);
+	*pobject = nv_object(base);
 	if (ret)
-		goto error;
+		return ret;
 
-	ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 4, 0x1000,
-				 NVOBJ_FLAG_ZERO_ALLOC, &priv->playlist[1]);
+	ret = nouveau_gpuobj_new(parent, nv_object(base), 0x0200, 0,
+				 NVOBJ_FLAG_ZERO_ALLOC, &base->eng);
 	if (ret)
-		goto error;
+		return ret;
 
-	nouveau_irq_register(dev, 8, nv04_fifo_isr);
-error:
+	ret = nouveau_gpuobj_new(parent, nv_object(base), 0x4000, 0,
+				 0, &base->pgd);
 	if (ret)
-		priv->base.base.destroy(dev, NVOBJ_ENGINE_FIFO);
-	return ret;
+		return ret;
+
+	ret = nouveau_vm_ref(nouveau_client(parent)->vm, &base->vm, base->pgd);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_new(parent, nv_object(base), 0x1000, 0x400,
+				 NVOBJ_FLAG_ZERO_ALLOC, &base->cache);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_new(parent, nv_object(base), 0x0100, 0x100,
+				 NVOBJ_FLAG_ZERO_ALLOC, &base->ramfc);
+	if (ret)
+		return ret;
+
+	return 0;
 }
+
+static struct nouveau_oclass
+nv84_fifo_cclass = {
+	.handle = NV_ENGCTX(FIFO, 0x84),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv84_fifo_context_ctor,
+		.dtor = nv50_fifo_context_dtor,
+		.init = _nouveau_fifo_context_init,
+		.fini = _nouveau_fifo_context_fini,
+		.rd32 = _nouveau_fifo_context_rd32,
+		.wr32 = _nouveau_fifo_context_wr32,
+	},
+};
+
+/*******************************************************************************
+ * PFIFO engine
+ ******************************************************************************/
+
+static int
+nv84_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	       struct nouveau_oclass *oclass, void *data, u32 size,
+	       struct nouveau_object **pobject)
+{
+	struct nv50_fifo_priv *priv;
+	int ret;
+
+	ret = nouveau_fifo_create(parent, engine, oclass, 1, 127, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_new(parent, NULL, 128 * 4, 0x1000, 0,
+				&priv->playlist[0]);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_new(parent, NULL, 128 * 4, 0x1000, 0,
+				&priv->playlist[1]);
+	if (ret)
+		return ret;
+
+	nv_subdev(priv)->unit = 0x00000100;
+	nv_subdev(priv)->intr = nv04_fifo_intr;
+	nv_engine(priv)->cclass = &nv84_fifo_cclass;
+	nv_engine(priv)->sclass = nv84_fifo_sclass;
+	return 0;
+}
+
+struct nouveau_oclass
+nv84_fifo_oclass = {
+	.handle = NV_ENGINE(FIFO, 0x84),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv84_fifo_ctor,
+		.dtor = nv50_fifo_dtor,
+		.init = nv50_fifo_init,
+		.fini = _nouveau_fifo_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
index b99d976..ef403fe 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 Red Hat Inc.
+ * Copyright 2012 Red Hat Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -22,17 +22,24 @@
  * Authors: Ben Skeggs
  */
 
-#include "drmP.h"
+#include <core/client.h>
+#include <core/handle.h>
+#include <core/namedb.h>
+#include <core/gpuobj.h>
+#include <core/engctx.h>
+#include <core/class.h>
+#include <core/math.h>
+#include <core/enum.h>
 
-#include "nouveau_drv.h"
-#include <core/mm.h>
+#include <subdev/timer.h>
+#include <subdev/bar.h>
+#include <subdev/vm.h>
+
+#include <engine/dmaobj.h>
 #include <engine/fifo.h>
-#include "nouveau_software.h"
-
-static void nvc0_fifo_isr(struct drm_device *);
 
 struct nvc0_fifo_priv {
-	struct nouveau_fifo_priv base;
+	struct nouveau_fifo base;
 	struct nouveau_gpuobj *playlist[2];
 	int cur_playlist;
 	struct {
@@ -42,14 +49,24 @@
 	int spoon_nr;
 };
 
+struct nvc0_fifo_base {
+	struct nouveau_fifo_base base;
+	struct nouveau_gpuobj *pgd;
+	struct nouveau_vm *vm;
+};
+
 struct nvc0_fifo_chan {
 	struct nouveau_fifo_chan base;
 };
 
+/*******************************************************************************
+ * FIFO channel objects
+ ******************************************************************************/
+
 static void
-nvc0_fifo_playlist_update(struct drm_device *dev)
+nvc0_fifo_playlist_update(struct nvc0_fifo_priv *priv)
 {
-	struct nvc0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO);
+	struct nouveau_bar *bar = nouveau_bar(priv);
 	struct nouveau_gpuobj *cur;
 	int i, p;
 
@@ -57,174 +74,253 @@
 	priv->cur_playlist = !priv->cur_playlist;
 
 	for (i = 0, p = 0; i < 128; i++) {
-		if (!(nv_rd32(dev, 0x3004 + (i * 8)) & 1))
+		if (!(nv_rd32(priv, 0x003004 + (i * 8)) & 1))
 			continue;
 		nv_wo32(cur, p + 0, i);
 		nv_wo32(cur, p + 4, 0x00000004);
 		p += 8;
 	}
-	nvimem_flush(dev);
+	bar->flush(bar);
 
-	nv_wr32(dev, 0x002270, cur->addr >> 12);
-	nv_wr32(dev, 0x002274, 0x01f00000 | (p >> 3));
-	if (!nv_wait(dev, 0x00227c, 0x00100000, 0x00000000))
-		NV_ERROR(dev, "PFIFO - playlist update failed\n");
+	nv_wr32(priv, 0x002270, cur->addr >> 12);
+	nv_wr32(priv, 0x002274, 0x01f00000 | (p >> 3));
+	if (!nv_wait(priv, 0x00227c, 0x00100000, 0x00000000))
+		nv_error(priv, "playlist update failed\n");
 }
 
 static int
-nvc0_fifo_context_new(struct nouveau_channel *chan, int engine)
+nvc0_fifo_context_attach(struct nouveau_object *parent,
+			 struct nouveau_object *object)
 {
-	struct drm_device *dev = chan->dev;
-	struct nvc0_fifo_priv *priv = nv_engine(dev, engine);
-	struct nvc0_fifo_chan *fctx;
-	u64 usermem = priv->user.mem->addr + chan->id * 0x1000;
-	u64 ib_virt = chan->pushbuf_base + chan->dma.ib_base * 4;
-	int ret, i;
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nvc0_fifo_base *base = (void *)parent->parent;
+	struct nouveau_engctx *ectx = (void *)object;
+	u32 addr;
+	int ret;
 
-	fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL);
-	if (!fctx)
-		return -ENOMEM;
-
-	chan->user = ioremap_wc(pci_resource_start(dev->pdev, 1) +
-				priv->user.bar.offset + (chan->id * 0x1000),
-				PAGE_SIZE);
-	if (!chan->user) {
-		ret = -ENOMEM;
-		goto error;
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_SW   : return 0;
+	case NVDEV_ENGINE_GR   : addr = 0x0210; break;
+	case NVDEV_ENGINE_COPY0: addr = 0x0230; break;
+	case NVDEV_ENGINE_COPY1: addr = 0x0240; break;
+	default:
+		return -EINVAL;
 	}
 
-	for (i = 0; i < 0x100; i += 4)
-		nv_wo32(chan->ramin, i, 0x00000000);
-	nv_wo32(chan->ramin, 0x08, lower_32_bits(usermem));
-	nv_wo32(chan->ramin, 0x0c, upper_32_bits(usermem));
-	nv_wo32(chan->ramin, 0x10, 0x0000face);
-	nv_wo32(chan->ramin, 0x30, 0xfffff902);
-	nv_wo32(chan->ramin, 0x48, lower_32_bits(ib_virt));
-	nv_wo32(chan->ramin, 0x4c, drm_order(chan->dma.ib_max + 1) << 16 |
-				   upper_32_bits(ib_virt));
-	nv_wo32(chan->ramin, 0x54, 0x00000002);
-	nv_wo32(chan->ramin, 0x84, 0x20400000);
-	nv_wo32(chan->ramin, 0x94, 0x30000001);
-	nv_wo32(chan->ramin, 0x9c, 0x00000100);
-	nv_wo32(chan->ramin, 0xa4, 0x1f1f1f1f);
-	nv_wo32(chan->ramin, 0xa8, 0x1f1f1f1f);
-	nv_wo32(chan->ramin, 0xac, 0x0000001f);
-	nv_wo32(chan->ramin, 0xb8, 0xf8000000);
-	nv_wo32(chan->ramin, 0xf8, 0x10003080); /* 0x002310 */
-	nv_wo32(chan->ramin, 0xfc, 0x10000010); /* 0x002350 */
-	nvimem_flush(dev);
+	if (!ectx->vma.node) {
+		ret = nouveau_gpuobj_map_vm(nv_gpuobj(ectx), base->vm,
+					    NV_MEM_ACCESS_RW, &ectx->vma);
+		if (ret)
+			return ret;
+	}
 
-	nv_wr32(dev, 0x003000 + (chan->id * 8), 0xc0000000 |
-						(chan->ramin->addr >> 12));
-	nv_wr32(dev, 0x003004 + (chan->id * 8), 0x001f0001);
-	nvc0_fifo_playlist_update(dev);
+	nv_wo32(base, addr + 0x00, lower_32_bits(ectx->vma.offset) | 4);
+	nv_wo32(base, addr + 0x04, upper_32_bits(ectx->vma.offset));
+	bar->flush(bar);
+	return 0;
+}
 
-error:
+static int
+nvc0_fifo_context_detach(struct nouveau_object *parent, bool suspend,
+			 struct nouveau_object *object)
+{
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nvc0_fifo_priv *priv = (void *)parent->engine;
+	struct nvc0_fifo_base *base = (void *)parent->parent;
+	struct nvc0_fifo_chan *chan = (void *)parent;
+	u32 addr;
+
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_SW   : return 0;
+	case NVDEV_ENGINE_GR   : addr = 0x0210; break;
+	case NVDEV_ENGINE_COPY0: addr = 0x0230; break;
+	case NVDEV_ENGINE_COPY1: addr = 0x0240; break;
+	default:
+		return -EINVAL;
+	}
+
+	nv_wo32(base, addr + 0x00, 0x00000000);
+	nv_wo32(base, addr + 0x04, 0x00000000);
+	bar->flush(bar);
+
+	nv_wr32(priv, 0x002634, chan->base.chid);
+	if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) {
+		nv_error(priv, "channel %d kick timeout\n", chan->base.chid);
+		if (suspend)
+			return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int
+nvc0_fifo_chan_ctor(struct nouveau_object *parent,
+		    struct nouveau_object *engine,
+		    struct nouveau_oclass *oclass, void *data, u32 size,
+		    struct nouveau_object **pobject)
+{
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nvc0_fifo_priv *priv = (void *)engine;
+	struct nvc0_fifo_base *base = (void *)parent;
+	struct nvc0_fifo_chan *chan;
+	struct nv_channel_ind_class *args = data;
+	u64 usermem, ioffset, ilength;
+	int ret, i;
+
+	if (size < sizeof(*args))
+		return -EINVAL;
+
+	ret = nouveau_fifo_channel_create(parent, engine, oclass, 1,
+					  priv->user.bar.offset, 0x1000,
+					  args->pushbuf,
+					  (1 << NVDEV_ENGINE_SW) |
+					  (1 << NVDEV_ENGINE_GR) |
+					  (1 << NVDEV_ENGINE_COPY0) |
+					  (1 << NVDEV_ENGINE_COPY1), &chan);
+	*pobject = nv_object(chan);
 	if (ret)
-		priv->base.base.context_del(chan, engine);
-	return ret;
+		return ret;
+
+	nv_parent(chan)->context_attach = nvc0_fifo_context_attach;
+	nv_parent(chan)->context_detach = nvc0_fifo_context_detach;
+
+	usermem = chan->base.chid * 0x1000;
+	ioffset = args->ioffset;
+	ilength = log2i(args->ilength / 8);
+
+	for (i = 0; i < 0x1000; i += 4)
+		nv_wo32(priv->user.mem, usermem + i, 0x00000000);
+
+	nv_wo32(base, 0x08, lower_32_bits(priv->user.mem->addr + usermem));
+	nv_wo32(base, 0x0c, upper_32_bits(priv->user.mem->addr + usermem));
+	nv_wo32(base, 0x10, 0x0000face);
+	nv_wo32(base, 0x30, 0xfffff902);
+	nv_wo32(base, 0x48, lower_32_bits(ioffset));
+	nv_wo32(base, 0x4c, upper_32_bits(ioffset) | (ilength << 16));
+	nv_wo32(base, 0x54, 0x00000002);
+	nv_wo32(base, 0x84, 0x20400000);
+	nv_wo32(base, 0x94, 0x30000001);
+	nv_wo32(base, 0x9c, 0x00000100);
+	nv_wo32(base, 0xa4, 0x1f1f1f1f);
+	nv_wo32(base, 0xa8, 0x1f1f1f1f);
+	nv_wo32(base, 0xac, 0x0000001f);
+	nv_wo32(base, 0xb8, 0xf8000000);
+	nv_wo32(base, 0xf8, 0x10003080); /* 0x002310 */
+	nv_wo32(base, 0xfc, 0x10000010); /* 0x002350 */
+	bar->flush(bar);
+	return 0;
+}
+
+static int
+nvc0_fifo_chan_init(struct nouveau_object *object)
+{
+	struct nouveau_gpuobj *base = nv_gpuobj(object->parent);
+	struct nvc0_fifo_priv *priv = (void *)object->engine;
+	struct nvc0_fifo_chan *chan = (void *)object;
+	u32 chid = chan->base.chid;
+	int ret;
+
+	ret = nouveau_fifo_channel_init(&chan->base);
+	if (ret)
+		return ret;
+
+	nv_wr32(priv, 0x003000 + (chid * 8), 0xc0000000 | base->addr >> 12);
+	nv_wr32(priv, 0x003004 + (chid * 8), 0x001f0001);
+	nvc0_fifo_playlist_update(priv);
+	return 0;
+}
+
+static int
+nvc0_fifo_chan_fini(struct nouveau_object *object, bool suspend)
+{
+	struct nvc0_fifo_priv *priv = (void *)object->engine;
+	struct nvc0_fifo_chan *chan = (void *)object;
+	u32 chid = chan->base.chid;
+
+	nv_mask(priv, 0x003004 + (chid * 8), 0x00000001, 0x00000000);
+	nvc0_fifo_playlist_update(priv);
+	nv_wr32(priv, 0x003000 + (chid * 8), 0x00000000);
+
+	return nouveau_fifo_channel_fini(&chan->base, suspend);
+}
+
+static struct nouveau_ofuncs
+nvc0_fifo_ofuncs = {
+	.ctor = nvc0_fifo_chan_ctor,
+	.dtor = _nouveau_fifo_channel_dtor,
+	.init = nvc0_fifo_chan_init,
+	.fini = nvc0_fifo_chan_fini,
+	.rd32 = _nouveau_fifo_channel_rd32,
+	.wr32 = _nouveau_fifo_channel_wr32,
+};
+
+static struct nouveau_oclass
+nvc0_fifo_sclass[] = {
+	{ 0x906f, &nvc0_fifo_ofuncs },
+	{}
+};
+
+/*******************************************************************************
+ * FIFO context - instmem heap and vm setup
+ ******************************************************************************/
+
+static int
+nvc0_fifo_context_ctor(struct nouveau_object *parent,
+		       struct nouveau_object *engine,
+		       struct nouveau_oclass *oclass, void *data, u32 size,
+		       struct nouveau_object **pobject)
+{
+	struct nvc0_fifo_base *base;
+	int ret;
+
+	ret = nouveau_fifo_context_create(parent, engine, oclass, NULL, 0x1000,
+				          0x1000, NVOBJ_FLAG_ZERO_ALLOC |
+					  NVOBJ_FLAG_HEAP, &base);
+	*pobject = nv_object(base);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_new(parent, NULL, 0x10000, 0x1000, 0, &base->pgd);
+	if (ret)
+		return ret;
+
+	nv_wo32(base, 0x0200, lower_32_bits(base->pgd->addr));
+	nv_wo32(base, 0x0204, upper_32_bits(base->pgd->addr));
+	nv_wo32(base, 0x0208, 0xffffffff);
+	nv_wo32(base, 0x020c, 0x000000ff);
+
+	ret = nouveau_vm_ref(nouveau_client(parent)->vm, &base->vm, base->pgd);
+	if (ret)
+		return ret;
+
+	return 0;
 }
 
 static void
-nvc0_fifo_context_del(struct nouveau_channel *chan, int engine)
+nvc0_fifo_context_dtor(struct nouveau_object *object)
 {
-	struct nvc0_fifo_chan *fctx = chan->engctx[engine];
-	struct drm_device *dev = chan->dev;
-
-	nv_mask(dev, 0x003004 + (chan->id * 8), 0x00000001, 0x00000000);
-	nv_wr32(dev, 0x002634, chan->id);
-	if (!nv_wait(dev, 0x0002634, 0xffffffff, chan->id))
-		NV_WARN(dev, "0x2634 != chid: 0x%08x\n", nv_rd32(dev, 0x2634));
-	nvc0_fifo_playlist_update(dev);
-	nv_wr32(dev, 0x003000 + (chan->id * 8), 0x00000000);
-
-	if (chan->user) {
-		iounmap(chan->user);
-		chan->user = NULL;
-	}
-
-	chan->engctx[engine] = NULL;
-	kfree(fctx);
+	struct nvc0_fifo_base *base = (void *)object;
+	nouveau_vm_ref(NULL, &base->vm, base->pgd);
+	nouveau_gpuobj_ref(NULL, &base->pgd);
+	nouveau_fifo_context_destroy(&base->base);
 }
 
-static int
-nvc0_fifo_init(struct drm_device *dev, int engine)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nvc0_fifo_priv *priv = nv_engine(dev, engine);
-	struct nouveau_channel *chan;
-	int i;
+static struct nouveau_oclass
+nvc0_fifo_cclass = {
+	.handle = NV_ENGCTX(FIFO, 0xc0),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nvc0_fifo_context_ctor,
+		.dtor = nvc0_fifo_context_dtor,
+		.init = _nouveau_fifo_context_init,
+		.fini = _nouveau_fifo_context_fini,
+		.rd32 = _nouveau_fifo_context_rd32,
+		.wr32 = _nouveau_fifo_context_wr32,
+	},
+};
 
-	/* reset PFIFO, enable all available PSUBFIFO areas */
-	nv_mask(dev, 0x000200, 0x00000100, 0x00000000);
-	nv_mask(dev, 0x000200, 0x00000100, 0x00000100);
-	nv_wr32(dev, 0x000204, 0xffffffff);
-	nv_wr32(dev, 0x002204, 0xffffffff);
-
-	priv->spoon_nr = hweight32(nv_rd32(dev, 0x002204));
-	NV_DEBUG(dev, "PFIFO: %d subfifo(s)\n", priv->spoon_nr);
-
-	/* assign engines to subfifos */
-	if (priv->spoon_nr >= 3) {
-		nv_wr32(dev, 0x002208, ~(1 << 0)); /* PGRAPH */
-		nv_wr32(dev, 0x00220c, ~(1 << 1)); /* PVP */
-		nv_wr32(dev, 0x002210, ~(1 << 1)); /* PPP */
-		nv_wr32(dev, 0x002214, ~(1 << 1)); /* PBSP */
-		nv_wr32(dev, 0x002218, ~(1 << 2)); /* PCE0 */
-		nv_wr32(dev, 0x00221c, ~(1 << 1)); /* PCE1 */
-	}
-
-	/* PSUBFIFO[n] */
-	for (i = 0; i < priv->spoon_nr; i++) {
-		nv_mask(dev, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000);
-		nv_wr32(dev, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */
-		nv_wr32(dev, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTR_EN */
-	}
-
-	nv_mask(dev, 0x002200, 0x00000001, 0x00000001);
-	nv_wr32(dev, 0x002254, 0x10000000 | priv->user.bar.offset >> 12);
-
-	nv_wr32(dev, 0x002a00, 0xffffffff); /* clears PFIFO.INTR bit 30 */
-	nv_wr32(dev, 0x002100, 0xffffffff);
-	nv_wr32(dev, 0x002140, 0xbfffffff);
-
-	/* restore PFIFO context table */
-	for (i = 0; i < 128; i++) {
-		chan = dev_priv->channels.ptr[i];
-		if (!chan || !chan->engctx[engine])
-			continue;
-
-		nv_wr32(dev, 0x003000 + (i * 8), 0xc0000000 |
-						 (chan->ramin->addr >> 12));
-		nv_wr32(dev, 0x003004 + (i * 8), 0x001f0001);
-	}
-	nvc0_fifo_playlist_update(dev);
-
-	return 0;
-}
-
-static int
-nvc0_fifo_fini(struct drm_device *dev, int engine, bool suspend)
-{
-	int i;
-
-	for (i = 0; i < 128; i++) {
-		if (!(nv_rd32(dev, 0x003004 + (i * 8)) & 1))
-			continue;
-
-		nv_mask(dev, 0x003004 + (i * 8), 0x00000001, 0x00000000);
-		nv_wr32(dev, 0x002634, i);
-		if (!nv_wait(dev, 0x002634, 0xffffffff, i)) {
-			NV_INFO(dev, "PFIFO: kick ch %d failed: 0x%08x\n",
-				i, nv_rd32(dev, 0x002634));
-			return -EBUSY;
-		}
-	}
-
-	nv_wr32(dev, 0x002140, 0x00000000);
-	return 0;
-}
-
+/*******************************************************************************
+ * PFIFO engine
+ ******************************************************************************/
 
 struct nouveau_enum nvc0_fifo_fault_unit[] = {
 	{ 0x00, "PGRAPH" },
@@ -289,16 +385,16 @@
 };
 
 static void
-nvc0_fifo_isr_vm_fault(struct drm_device *dev, int unit)
+nvc0_fifo_isr_vm_fault(struct nvc0_fifo_priv *priv, int unit)
 {
-	u32 inst = nv_rd32(dev, 0x2800 + (unit * 0x10));
-	u32 valo = nv_rd32(dev, 0x2804 + (unit * 0x10));
-	u32 vahi = nv_rd32(dev, 0x2808 + (unit * 0x10));
-	u32 stat = nv_rd32(dev, 0x280c + (unit * 0x10));
+	u32 inst = nv_rd32(priv, 0x2800 + (unit * 0x10));
+	u32 valo = nv_rd32(priv, 0x2804 + (unit * 0x10));
+	u32 vahi = nv_rd32(priv, 0x2808 + (unit * 0x10));
+	u32 stat = nv_rd32(priv, 0x280c + (unit * 0x10));
 	u32 client = (stat & 0x00001f00) >> 8;
 
-	NV_INFO(dev, "PFIFO: %s fault at 0x%010llx [",
-		(stat & 0x00000080) ? "write" : "read", (u64)vahi << 32 | valo);
+	nv_error(priv, "%s fault at 0x%010llx [", (stat & 0x00000080) ?
+		 "write" : "read", (u64)vahi << 32 | valo);
 	nouveau_enum_print(nvc0_fifo_fault_reason, stat & 0x0000000f);
 	printk("] from ");
 	nouveau_enum_print(nvc0_fifo_fault_unit, unit);
@@ -313,165 +409,223 @@
 }
 
 static int
-nvc0_fifo_page_flip(struct drm_device *dev, u32 chid)
+nvc0_fifo_swmthd(struct nvc0_fifo_priv *priv, u32 chid, u32 mthd, u32 data)
 {
-	struct nvc0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO);
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_channel *chan = NULL;
+	struct nvc0_fifo_chan *chan = NULL;
+	struct nouveau_handle *bind;
 	unsigned long flags;
 	int ret = -EINVAL;
 
-	spin_lock_irqsave(&dev_priv->channels.lock, flags);
-	if (likely(chid >= 0 && chid < priv->base.channels)) {
-		chan = dev_priv->channels.ptr[chid];
-		if (likely(chan)) {
-			struct nouveau_software_chan *swch =
-				chan->engctx[NVOBJ_ENGINE_SW];
-			ret = swch->flip(swch->flip_data);
-		}
+	spin_lock_irqsave(&priv->base.lock, flags);
+	if (likely(chid >= priv->base.min && chid <= priv->base.max))
+		chan = (void *)priv->base.channel[chid];
+	if (unlikely(!chan))
+		goto out;
+
+	bind = nouveau_namedb_get_class(nv_namedb(chan), 0x906e);
+	if (likely(bind)) {
+		if (!mthd || !nv_call(bind->object, mthd, data))
+			ret = 0;
+		nouveau_namedb_put(bind);
 	}
-	spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
+
+out:
+	spin_unlock_irqrestore(&priv->base.lock, flags);
 	return ret;
 }
 
 static void
-nvc0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
+nvc0_fifo_isr_subfifo_intr(struct nvc0_fifo_priv *priv, int unit)
 {
-	u32 stat = nv_rd32(dev, 0x040108 + (unit * 0x2000));
-	u32 addr = nv_rd32(dev, 0x0400c0 + (unit * 0x2000));
-	u32 data = nv_rd32(dev, 0x0400c4 + (unit * 0x2000));
-	u32 chid = nv_rd32(dev, 0x040120 + (unit * 0x2000)) & 0x7f;
-	u32 subc = (addr & 0x00070000);
+	u32 stat = nv_rd32(priv, 0x040108 + (unit * 0x2000));
+	u32 addr = nv_rd32(priv, 0x0400c0 + (unit * 0x2000));
+	u32 data = nv_rd32(priv, 0x0400c4 + (unit * 0x2000));
+	u32 chid = nv_rd32(priv, 0x040120 + (unit * 0x2000)) & 0x7f;
+	u32 subc = (addr & 0x00070000) >> 16;
 	u32 mthd = (addr & 0x00003ffc);
 	u32 show = stat;
 
 	if (stat & 0x00200000) {
 		if (mthd == 0x0054) {
-			if (!nvc0_fifo_page_flip(dev, chid))
+			if (!nvc0_fifo_swmthd(priv, chid, 0x0500, 0x00000000))
 				show &= ~0x00200000;
 		}
 	}
 
-	if (show) {
-		NV_INFO(dev, "PFIFO%d:", unit);
-		nouveau_bitfield_print(nvc0_fifo_subfifo_intr, show);
-		NV_INFO(dev, "PFIFO%d: ch %d subc %d mthd 0x%04x data 0x%08x\n",
-			     unit, chid, subc, mthd, data);
+	if (stat & 0x00800000) {
+		if (!nvc0_fifo_swmthd(priv, chid, mthd, data))
+			show &= ~0x00800000;
 	}
 
-	nv_wr32(dev, 0x0400c0 + (unit * 0x2000), 0x80600008);
-	nv_wr32(dev, 0x040108 + (unit * 0x2000), stat);
+	if (show) {
+		nv_error(priv, "SUBFIFO%d:", unit);
+		nouveau_bitfield_print(nvc0_fifo_subfifo_intr, show);
+		printk("\n");
+		nv_error(priv, "SUBFIFO%d: ch %d subc %d mthd 0x%04x "
+			       "data 0x%08x\n",
+			 unit, chid, subc, mthd, data);
+	}
+
+	nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008);
+	nv_wr32(priv, 0x040108 + (unit * 0x2000), stat);
 }
 
 static void
-nvc0_fifo_isr(struct drm_device *dev)
+nvc0_fifo_intr(struct nouveau_subdev *subdev)
 {
-	u32 mask = nv_rd32(dev, 0x002140);
-	u32 stat = nv_rd32(dev, 0x002100) & mask;
+	struct nvc0_fifo_priv *priv = (void *)subdev;
+	u32 mask = nv_rd32(priv, 0x002140);
+	u32 stat = nv_rd32(priv, 0x002100) & mask;
 
 	if (stat & 0x00000100) {
-		NV_INFO(dev, "PFIFO: unknown status 0x00000100\n");
-		nv_wr32(dev, 0x002100, 0x00000100);
+		nv_info(priv, "unknown status 0x00000100\n");
+		nv_wr32(priv, 0x002100, 0x00000100);
 		stat &= ~0x00000100;
 	}
 
 	if (stat & 0x10000000) {
-		u32 units = nv_rd32(dev, 0x00259c);
+		u32 units = nv_rd32(priv, 0x00259c);
 		u32 u = units;
 
 		while (u) {
 			int i = ffs(u) - 1;
-			nvc0_fifo_isr_vm_fault(dev, i);
+			nvc0_fifo_isr_vm_fault(priv, i);
 			u &= ~(1 << i);
 		}
 
-		nv_wr32(dev, 0x00259c, units);
+		nv_wr32(priv, 0x00259c, units);
 		stat &= ~0x10000000;
 	}
 
 	if (stat & 0x20000000) {
-		u32 units = nv_rd32(dev, 0x0025a0);
+		u32 units = nv_rd32(priv, 0x0025a0);
 		u32 u = units;
 
 		while (u) {
 			int i = ffs(u) - 1;
-			nvc0_fifo_isr_subfifo_intr(dev, i);
+			nvc0_fifo_isr_subfifo_intr(priv, i);
 			u &= ~(1 << i);
 		}
 
-		nv_wr32(dev, 0x0025a0, units);
+		nv_wr32(priv, 0x0025a0, units);
 		stat &= ~0x20000000;
 	}
 
 	if (stat & 0x40000000) {
-		NV_INFO(dev, "PFIFO: unknown status 0x40000000\n");
-		nv_mask(dev, 0x002a00, 0x00000000, 0x00000000);
+		nv_warn(priv, "unknown status 0x40000000\n");
+		nv_mask(priv, 0x002a00, 0x00000000, 0x00000000);
 		stat &= ~0x40000000;
 	}
 
 	if (stat) {
-		NV_INFO(dev, "PFIFO: unhandled status 0x%08x\n", stat);
-		nv_wr32(dev, 0x002100, stat);
-		nv_wr32(dev, 0x002140, 0);
+		nv_fatal(priv, "unhandled status 0x%08x\n", stat);
+		nv_wr32(priv, 0x002100, stat);
+		nv_wr32(priv, 0x002140, 0);
 	}
 }
 
-static void
-nvc0_fifo_destroy(struct drm_device *dev, int engine)
+static int
+nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	       struct nouveau_oclass *oclass, void *data, u32 size,
+	       struct nouveau_object **pobject)
 {
-	struct nvc0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO);
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-
-	nouveau_gpuobj_unmap(&priv->user.bar);
-	nouveau_gpuobj_ref(NULL, &priv->user.mem);
-
-	nouveau_gpuobj_ref(NULL, &priv->playlist[1]);
-	nouveau_gpuobj_ref(NULL, &priv->playlist[0]);
-
-	dev_priv->eng[engine] = NULL;
-	kfree(priv);
-}
-
-int
-nvc0_fifo_create(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nvc0_fifo_priv *priv;
 	int ret;
 
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	priv->base.base.destroy = nvc0_fifo_destroy;
-	priv->base.base.init = nvc0_fifo_init;
-	priv->base.base.fini = nvc0_fifo_fini;
-	priv->base.base.context_new = nvc0_fifo_context_new;
-	priv->base.base.context_del = nvc0_fifo_context_del;
-	priv->base.channels = 128;
-	dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base;
-
-	ret = nouveau_gpuobj_new(dev, NULL, 4096, 4096, 0, &priv->playlist[0]);
+	ret = nouveau_fifo_create(parent, engine, oclass, 0, 127, &priv);
+	*pobject = nv_object(priv);
 	if (ret)
-		goto error;
+		return ret;
 
-	ret = nouveau_gpuobj_new(dev, NULL, 4096, 4096, 0, &priv->playlist[1]);
+	ret = nouveau_gpuobj_new(parent, NULL, 0x1000, 0x1000, 0,
+				&priv->playlist[0]);
 	if (ret)
-		goto error;
+		return ret;
 
-	ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 4096, 0x1000,
-				 NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem);
+	ret = nouveau_gpuobj_new(parent, NULL, 0x1000, 0x1000, 0,
+				&priv->playlist[1]);
 	if (ret)
-		goto error;
+		return ret;
 
-	ret = nouveau_gpuobj_map_bar(priv->user.mem, NV_MEM_ACCESS_RW,
-				    &priv->user.bar);
+	ret = nouveau_gpuobj_new(parent, NULL, 128 * 0x1000, 0x1000, 0,
+				&priv->user.mem);
 	if (ret)
-		goto error;
+		return ret;
 
-	nouveau_irq_register(dev, 8, nvc0_fifo_isr);
-error:
+	ret = nouveau_gpuobj_map(priv->user.mem, NV_MEM_ACCESS_RW,
+				&priv->user.bar);
 	if (ret)
-		priv->base.base.destroy(dev, NVOBJ_ENGINE_FIFO);
-	return ret;
+		return ret;
+
+	nv_subdev(priv)->unit = 0x00000100;
+	nv_subdev(priv)->intr = nvc0_fifo_intr;
+	nv_engine(priv)->cclass = &nvc0_fifo_cclass;
+	nv_engine(priv)->sclass = nvc0_fifo_sclass;
+	return 0;
 }
+
+static void
+nvc0_fifo_dtor(struct nouveau_object *object)
+{
+	struct nvc0_fifo_priv *priv = (void *)object;
+
+	nouveau_gpuobj_unmap(&priv->user.bar);
+	nouveau_gpuobj_ref(NULL, &priv->user.mem);
+	nouveau_gpuobj_ref(NULL, &priv->playlist[1]);
+	nouveau_gpuobj_ref(NULL, &priv->playlist[0]);
+
+	nouveau_fifo_destroy(&priv->base);
+}
+
+static int
+nvc0_fifo_init(struct nouveau_object *object)
+{
+	struct nvc0_fifo_priv *priv = (void *)object;
+	int ret, i;
+
+	ret = nouveau_fifo_init(&priv->base);
+	if (ret)
+		return ret;
+
+	nv_wr32(priv, 0x000204, 0xffffffff);
+	nv_wr32(priv, 0x002204, 0xffffffff);
+
+	priv->spoon_nr = hweight32(nv_rd32(priv, 0x002204));
+	nv_debug(priv, "%d subfifo(s)\n", priv->spoon_nr);
+
+	/* assign engines to subfifos */
+	if (priv->spoon_nr >= 3) {
+		nv_wr32(priv, 0x002208, ~(1 << 0)); /* PGRAPH */
+		nv_wr32(priv, 0x00220c, ~(1 << 1)); /* PVP */
+		nv_wr32(priv, 0x002210, ~(1 << 1)); /* PPP */
+		nv_wr32(priv, 0x002214, ~(1 << 1)); /* PBSP */
+		nv_wr32(priv, 0x002218, ~(1 << 2)); /* PCE0 */
+		nv_wr32(priv, 0x00221c, ~(1 << 1)); /* PCE1 */
+	}
+
+	/* PSUBFIFO[n] */
+	for (i = 0; i < priv->spoon_nr; i++) {
+		nv_mask(priv, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000);
+		nv_wr32(priv, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */
+		nv_wr32(priv, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTREN */
+	}
+
+	nv_mask(priv, 0x002200, 0x00000001, 0x00000001);
+	nv_wr32(priv, 0x002254, 0x10000000 | priv->user.bar.offset >> 12);
+
+	nv_wr32(priv, 0x002a00, 0xffffffff); /* clears PFIFO.INTR bit 30 */
+	nv_wr32(priv, 0x002100, 0xffffffff);
+	nv_wr32(priv, 0x002140, 0xbfffffff);
+	return 0;
+}
+
+struct nouveau_oclass
+nvc0_fifo_oclass = {
+	.handle = NV_ENGINE(FIFO, 0xc0),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nvc0_fifo_ctor,
+		.dtor = nvc0_fifo_dtor,
+		.init = nvc0_fifo_init,
+		.fini = _nouveau_fifo_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
index 0b356f1..aaff086 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2010 Red Hat Inc.
+ * Copyright 2012 Red Hat Inc.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
@@ -22,25 +22,30 @@
  * Authors: Ben Skeggs
  */
 
-#include "drmP.h"
+#include <core/client.h>
+#include <core/handle.h>
+#include <core/namedb.h>
+#include <core/gpuobj.h>
+#include <core/engctx.h>
+#include <core/class.h>
+#include <core/math.h>
+#include <core/enum.h>
 
-#include "nouveau_drv.h"
-#include <core/mm.h>
+#include <subdev/timer.h>
+#include <subdev/bar.h>
+#include <subdev/vm.h>
+
+#include <engine/dmaobj.h>
 #include <engine/fifo.h>
-#include "nouveau_software.h"
 
-#define NVE0_FIFO_ENGINE_NUM 32
-
-static void nve0_fifo_isr(struct drm_device *);
-
-struct nve0_fifo_engine {
+struct nve0_fifo_engn {
 	struct nouveau_gpuobj *playlist[2];
 	int cur_playlist;
 };
 
 struct nve0_fifo_priv {
-	struct nouveau_fifo_priv base;
-	struct nve0_fifo_engine engine[NVE0_FIFO_ENGINE_NUM];
+	struct nouveau_fifo base;
+	struct nve0_fifo_engn engine[16];
 	struct {
 		struct nouveau_gpuobj *mem;
 		struct nouveau_vma bar;
@@ -48,193 +53,285 @@
 	int spoon_nr;
 };
 
+struct nve0_fifo_base {
+	struct nouveau_fifo_base base;
+	struct nouveau_gpuobj *pgd;
+	struct nouveau_vm *vm;
+};
+
 struct nve0_fifo_chan {
 	struct nouveau_fifo_chan base;
 	u32 engine;
 };
 
+/*******************************************************************************
+ * FIFO channel objects
+ ******************************************************************************/
+
 static void
-nve0_fifo_playlist_update(struct drm_device *dev, u32 engine)
+nve0_fifo_playlist_update(struct nve0_fifo_priv *priv, u32 engine)
 {
-	struct nve0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO);
-	struct nve0_fifo_engine *peng = &priv->engine[engine];
+	struct nouveau_bar *bar = nouveau_bar(priv);
+	struct nve0_fifo_engn *engn = &priv->engine[engine];
 	struct nouveau_gpuobj *cur;
 	u32 match = (engine << 16) | 0x00000001;
-	int ret, i, p;
+	int i, p;
 
-	cur = peng->playlist[peng->cur_playlist];
+	cur = engn->playlist[engn->cur_playlist];
 	if (unlikely(cur == NULL)) {
-		ret = nouveau_gpuobj_new(dev, NULL, 0x8000, 0x1000, 0, &cur);
+		int ret = nouveau_gpuobj_new(nv_object(priv)->parent, NULL,
+					     0x8000, 0x1000, 0, &cur);
 		if (ret) {
-			NV_ERROR(dev, "PFIFO: playlist alloc failed\n");
+			nv_error(priv, "playlist alloc failed\n");
 			return;
 		}
 
-		peng->playlist[peng->cur_playlist] = cur;
+		engn->playlist[engn->cur_playlist] = cur;
 	}
 
-	peng->cur_playlist = !peng->cur_playlist;
+	engn->cur_playlist = !engn->cur_playlist;
 
-	for (i = 0, p = 0; i < priv->base.channels; i++) {
-		u32 ctrl = nv_rd32(dev, 0x800004 + (i * 8)) & 0x001f0001;
+	for (i = 0, p = 0; i < priv->base.max; i++) {
+		u32 ctrl = nv_rd32(priv, 0x800004 + (i * 8)) & 0x001f0001;
 		if (ctrl != match)
 			continue;
 		nv_wo32(cur, p + 0, i);
 		nv_wo32(cur, p + 4, 0x00000000);
 		p += 8;
 	}
-	nvimem_flush(dev);
+	bar->flush(bar);
 
-	nv_wr32(dev, 0x002270, cur->addr >> 12);
-	nv_wr32(dev, 0x002274, (engine << 20) | (p >> 3));
-	if (!nv_wait(dev, 0x002284 + (engine * 4), 0x00100000, 0x00000000))
-		NV_ERROR(dev, "PFIFO: playlist %d update timeout\n", engine);
+	nv_wr32(priv, 0x002270, cur->addr >> 12);
+	nv_wr32(priv, 0x002274, (engine << 20) | (p >> 3));
+	if (!nv_wait(priv, 0x002284 + (engine * 4), 0x00100000, 0x00000000))
+		nv_error(priv, "playlist %d update timeout\n", engine);
 }
 
 static int
-nve0_fifo_context_new(struct nouveau_channel *chan, int engine)
+nve0_fifo_context_attach(struct nouveau_object *parent,
+			 struct nouveau_object *object)
 {
-	struct drm_device *dev = chan->dev;
-	struct nve0_fifo_priv *priv = nv_engine(dev, engine);
-	struct nve0_fifo_chan *fctx;
-	u64 usermem = priv->user.mem->addr + chan->id * 512;
-	u64 ib_virt = chan->pushbuf_base + chan->dma.ib_base * 4;
-	int ret = 0, i;
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nve0_fifo_base *base = (void *)parent->parent;
+	struct nouveau_engctx *ectx = (void *)object;
+	u32 addr;
+	int ret;
 
-	fctx = chan->engctx[engine] = kzalloc(sizeof(*fctx), GFP_KERNEL);
-	if (!fctx)
-		return -ENOMEM;
-
-	fctx->engine = 0; /* PGRAPH */
-
-	/* allocate vram for control regs, map into polling area */
-	chan->user = ioremap_wc(pci_resource_start(dev->pdev, 1) +
-				priv->user.bar.offset + (chan->id * 512), 512);
-	if (!chan->user) {
-		ret = -ENOMEM;
-		goto error;
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_SW   : return 0;
+	case NVDEV_ENGINE_GR   : addr = 0x0210; break;
+	default:
+		return -EINVAL;
 	}
 
-	for (i = 0; i < 0x100; i += 4)
-		nv_wo32(chan->ramin, i, 0x00000000);
-	nv_wo32(chan->ramin, 0x08, lower_32_bits(usermem));
-	nv_wo32(chan->ramin, 0x0c, upper_32_bits(usermem));
-	nv_wo32(chan->ramin, 0x10, 0x0000face);
-	nv_wo32(chan->ramin, 0x30, 0xfffff902);
-	nv_wo32(chan->ramin, 0x48, lower_32_bits(ib_virt));
-	nv_wo32(chan->ramin, 0x4c, drm_order(chan->dma.ib_max + 1) << 16 |
-				     upper_32_bits(ib_virt));
-	nv_wo32(chan->ramin, 0x84, 0x20400000);
-	nv_wo32(chan->ramin, 0x94, 0x30000001);
-	nv_wo32(chan->ramin, 0x9c, 0x00000100);
-	nv_wo32(chan->ramin, 0xac, 0x0000001f);
-	nv_wo32(chan->ramin, 0xe4, 0x00000000);
-	nv_wo32(chan->ramin, 0xe8, chan->id);
-	nv_wo32(chan->ramin, 0xf8, 0x10003080); /* 0x002310 */
-	nv_wo32(chan->ramin, 0xfc, 0x10000010); /* 0x002350 */
-	nvimem_flush(dev);
+	if (!ectx->vma.node) {
+		ret = nouveau_gpuobj_map_vm(nv_gpuobj(ectx), base->vm,
+					    NV_MEM_ACCESS_RW, &ectx->vma);
+		if (ret)
+			return ret;
+	}
 
-	nv_wr32(dev, 0x800000 + (chan->id * 8), 0x80000000 |
-						(chan->ramin->addr >> 12));
-	nv_mask(dev, 0x800004 + (chan->id * 8), 0x00000400, 0x00000400);
-	nve0_fifo_playlist_update(dev, fctx->engine);
-	nv_mask(dev, 0x800004 + (chan->id * 8), 0x00000400, 0x00000400);
+	nv_wo32(base, addr + 0x00, lower_32_bits(ectx->vma.offset) | 4);
+	nv_wo32(base, addr + 0x04, upper_32_bits(ectx->vma.offset));
+	bar->flush(bar);
+	return 0;
+}
 
-error:
+static int
+nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend,
+			 struct nouveau_object *object)
+{
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nve0_fifo_priv *priv = (void *)parent->engine;
+	struct nve0_fifo_base *base = (void *)parent->parent;
+	struct nve0_fifo_chan *chan = (void *)parent;
+	u32 addr;
+
+	switch (nv_engidx(object->engine)) {
+	case NVDEV_ENGINE_SW   : return 0;
+	case NVDEV_ENGINE_GR   : addr = 0x0210; break;
+	default:
+		return -EINVAL;
+	}
+
+	nv_wo32(base, addr + 0x00, 0x00000000);
+	nv_wo32(base, addr + 0x04, 0x00000000);
+	bar->flush(bar);
+
+	nv_wr32(priv, 0x002634, chan->base.chid);
+	if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) {
+		nv_error(priv, "channel %d kick timeout\n", chan->base.chid);
+		if (suspend)
+			return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int
+nve0_fifo_chan_ctor(struct nouveau_object *parent,
+		    struct nouveau_object *engine,
+		    struct nouveau_oclass *oclass, void *data, u32 size,
+		    struct nouveau_object **pobject)
+{
+	struct nouveau_bar *bar = nouveau_bar(parent);
+	struct nve0_fifo_priv *priv = (void *)engine;
+	struct nve0_fifo_base *base = (void *)parent;
+	struct nve0_fifo_chan *chan;
+	struct nv_channel_ind_class *args = data;
+	u64 usermem, ioffset, ilength;
+	int ret, i;
+
+	if (size < sizeof(*args))
+		return -EINVAL;
+
+	ret = nouveau_fifo_channel_create(parent, engine, oclass, 1,
+					  priv->user.bar.offset, 0x200,
+					  args->pushbuf,
+					  (1 << NVDEV_ENGINE_SW) |
+					  (1 << NVDEV_ENGINE_GR), &chan);
+	*pobject = nv_object(chan);
 	if (ret)
-		priv->base.base.context_del(chan, engine);
-	return ret;
+		return ret;
+
+	nv_parent(chan)->context_attach = nve0_fifo_context_attach;
+	nv_parent(chan)->context_detach = nve0_fifo_context_detach;
+
+	usermem = chan->base.chid * 0x200;
+	ioffset = args->ioffset;
+	ilength = log2i(args->ilength / 8);
+
+	for (i = 0; i < 0x200; i += 4)
+		nv_wo32(priv->user.mem, usermem + i, 0x00000000);
+
+	nv_wo32(base, 0x08, lower_32_bits(priv->user.mem->addr + usermem));
+	nv_wo32(base, 0x0c, upper_32_bits(priv->user.mem->addr + usermem));
+	nv_wo32(base, 0x10, 0x0000face);
+	nv_wo32(base, 0x30, 0xfffff902);
+	nv_wo32(base, 0x48, lower_32_bits(ioffset));
+	nv_wo32(base, 0x4c, upper_32_bits(ioffset) | (ilength << 16));
+	nv_wo32(base, 0x84, 0x20400000);
+	nv_wo32(base, 0x94, 0x30000001);
+	nv_wo32(base, 0x9c, 0x00000100);
+	nv_wo32(base, 0xac, 0x0000001f);
+	nv_wo32(base, 0xe8, chan->base.chid);
+	nv_wo32(base, 0xb8, 0xf8000000);
+	nv_wo32(base, 0xf8, 0x10003080); /* 0x002310 */
+	nv_wo32(base, 0xfc, 0x10000010); /* 0x002350 */
+	bar->flush(bar);
+	return 0;
+}
+
+static int
+nve0_fifo_chan_init(struct nouveau_object *object)
+{
+	struct nouveau_gpuobj *base = nv_gpuobj(object->parent);
+	struct nve0_fifo_priv *priv = (void *)object->engine;
+	struct nve0_fifo_chan *chan = (void *)object;
+	u32 chid = chan->base.chid;
+	int ret;
+
+	ret = nouveau_fifo_channel_init(&chan->base);
+	if (ret)
+		return ret;
+
+	nv_wr32(priv, 0x800000 + (chid * 8), 0x80000000 | base->addr >> 12);
+	nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400);
+	nve0_fifo_playlist_update(priv, chan->engine);
+	nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400);
+	return 0;
+}
+
+static int
+nve0_fifo_chan_fini(struct nouveau_object *object, bool suspend)
+{
+	struct nve0_fifo_priv *priv = (void *)object->engine;
+	struct nve0_fifo_chan *chan = (void *)object;
+	u32 chid = chan->base.chid;
+
+	nv_mask(priv, 0x800004 + (chid * 8), 0x00000800, 0x00000800);
+	nve0_fifo_playlist_update(priv, chan->engine);
+	nv_wr32(priv, 0x800000 + (chid * 8), 0x00000000);
+
+	return nouveau_fifo_channel_fini(&chan->base, suspend);
+}
+
+static struct nouveau_ofuncs
+nve0_fifo_ofuncs = {
+	.ctor = nve0_fifo_chan_ctor,
+	.dtor = _nouveau_fifo_channel_dtor,
+	.init = nve0_fifo_chan_init,
+	.fini = nve0_fifo_chan_fini,
+	.rd32 = _nouveau_fifo_channel_rd32,
+	.wr32 = _nouveau_fifo_channel_wr32,
+};
+
+static struct nouveau_oclass
+nve0_fifo_sclass[] = {
+	{ 0xa06f, &nve0_fifo_ofuncs },
+	{}
+};
+
+/*******************************************************************************
+ * FIFO context - instmem heap and vm setup
+ ******************************************************************************/
+
+static int
+nve0_fifo_context_ctor(struct nouveau_object *parent,
+		    struct nouveau_object *engine,
+		    struct nouveau_oclass *oclass, void *data, u32 size,
+		    struct nouveau_object **pobject)
+{
+	struct nve0_fifo_base *base;
+	int ret;
+
+	ret = nouveau_fifo_context_create(parent, engine, oclass, NULL, 0x1000,
+				          0x1000, NVOBJ_FLAG_ZERO_ALLOC, &base);
+	*pobject = nv_object(base);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_new(parent, NULL, 0x10000, 0x1000, 0, &base->pgd);
+	if (ret)
+		return ret;
+
+	nv_wo32(base, 0x0200, lower_32_bits(base->pgd->addr));
+	nv_wo32(base, 0x0204, upper_32_bits(base->pgd->addr));
+	nv_wo32(base, 0x0208, 0xffffffff);
+	nv_wo32(base, 0x020c, 0x000000ff);
+
+	ret = nouveau_vm_ref(nouveau_client(parent)->vm, &base->vm, base->pgd);
+	if (ret)
+		return ret;
+
+	return 0;
 }
 
 static void
-nve0_fifo_context_del(struct nouveau_channel *chan, int engine)
+nve0_fifo_context_dtor(struct nouveau_object *object)
 {
-	struct nve0_fifo_chan *fctx = chan->engctx[engine];
-	struct drm_device *dev = chan->dev;
-
-	nv_mask(dev, 0x800004 + (chan->id * 8), 0x00000800, 0x00000800);
-	nv_wr32(dev, 0x002634, chan->id);
-	if (!nv_wait(dev, 0x0002634, 0xffffffff, chan->id))
-		NV_WARN(dev, "0x2634 != chid: 0x%08x\n", nv_rd32(dev, 0x2634));
-	nve0_fifo_playlist_update(dev, fctx->engine);
-	nv_wr32(dev, 0x800000 + (chan->id * 8), 0x00000000);
-
-	if (chan->user) {
-		iounmap(chan->user);
-		chan->user = NULL;
-	}
-
-	chan->engctx[NVOBJ_ENGINE_FIFO] = NULL;
-	kfree(fctx);
+	struct nve0_fifo_base *base = (void *)object;
+	nouveau_vm_ref(NULL, &base->vm, base->pgd);
+	nouveau_gpuobj_ref(NULL, &base->pgd);
+	nouveau_fifo_context_destroy(&base->base);
 }
 
-static int
-nve0_fifo_init(struct drm_device *dev, int engine)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nve0_fifo_priv *priv = nv_engine(dev, engine);
-	struct nve0_fifo_chan *fctx;
-	int i;
+static struct nouveau_oclass
+nve0_fifo_cclass = {
+	.handle = NV_ENGCTX(FIFO, 0xe0),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nve0_fifo_context_ctor,
+		.dtor = nve0_fifo_context_dtor,
+		.init = _nouveau_fifo_context_init,
+		.fini = _nouveau_fifo_context_fini,
+		.rd32 = _nouveau_fifo_context_rd32,
+		.wr32 = _nouveau_fifo_context_wr32,
+	},
+};
 
-	/* reset PFIFO, enable all available PSUBFIFO areas */
-	nv_mask(dev, 0x000200, 0x00000100, 0x00000000);
-	nv_mask(dev, 0x000200, 0x00000100, 0x00000100);
-	nv_wr32(dev, 0x000204, 0xffffffff);
-
-	priv->spoon_nr = hweight32(nv_rd32(dev, 0x000204));
-	NV_DEBUG(dev, "PFIFO: %d subfifo(s)\n", priv->spoon_nr);
-
-	/* PSUBFIFO[n] */
-	for (i = 0; i < priv->spoon_nr; i++) {
-		nv_mask(dev, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000);
-		nv_wr32(dev, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */
-		nv_wr32(dev, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTR_EN */
-	}
-
-	nv_wr32(dev, 0x002254, 0x10000000 | priv->user.bar.offset >> 12);
-
-	nv_wr32(dev, 0x002a00, 0xffffffff);
-	nv_wr32(dev, 0x002100, 0xffffffff);
-	nv_wr32(dev, 0x002140, 0xbfffffff);
-
-	/* restore PFIFO context table */
-	for (i = 0; i < priv->base.channels; i++) {
-		struct nouveau_channel *chan = dev_priv->channels.ptr[i];
-		if (!chan || !(fctx = chan->engctx[engine]))
-			continue;
-
-		nv_wr32(dev, 0x800000 + (i * 8), 0x80000000 |
-						 (chan->ramin->addr >> 12));
-		nv_mask(dev, 0x800004 + (i * 8), 0x00000400, 0x00000400);
-		nve0_fifo_playlist_update(dev, fctx->engine);
-		nv_mask(dev, 0x800004 + (i * 8), 0x00000400, 0x00000400);
-	}
-
-	return 0;
-}
-
-static int
-nve0_fifo_fini(struct drm_device *dev, int engine, bool suspend)
-{
-	struct nve0_fifo_priv *priv = nv_engine(dev, engine);
-	int i;
-
-	for (i = 0; i < priv->base.channels; i++) {
-		if (!(nv_rd32(dev, 0x800004 + (i * 8)) & 1))
-			continue;
-
-		nv_mask(dev, 0x800004 + (i * 8), 0x00000800, 0x00000800);
-		nv_wr32(dev, 0x002634, i);
-		if (!nv_wait(dev, 0x002634, 0xffffffff, i)) {
-			NV_INFO(dev, "PFIFO: kick ch %d failed: 0x%08x\n",
-				i, nv_rd32(dev, 0x002634));
-			return -EBUSY;
-		}
-	}
-
-	nv_wr32(dev, 0x002140, 0x00000000);
-	return 0;
-}
+/*******************************************************************************
+ * PFIFO engine
+ ******************************************************************************/
 
 struct nouveau_enum nve0_fifo_fault_unit[] = {
 	{}
@@ -268,16 +365,16 @@
 };
 
 static void
-nve0_fifo_isr_vm_fault(struct drm_device *dev, int unit)
+nve0_fifo_isr_vm_fault(struct nve0_fifo_priv *priv, int unit)
 {
-	u32 inst = nv_rd32(dev, 0x2800 + (unit * 0x10));
-	u32 valo = nv_rd32(dev, 0x2804 + (unit * 0x10));
-	u32 vahi = nv_rd32(dev, 0x2808 + (unit * 0x10));
-	u32 stat = nv_rd32(dev, 0x280c + (unit * 0x10));
+	u32 inst = nv_rd32(priv, 0x2800 + (unit * 0x10));
+	u32 valo = nv_rd32(priv, 0x2804 + (unit * 0x10));
+	u32 vahi = nv_rd32(priv, 0x2808 + (unit * 0x10));
+	u32 stat = nv_rd32(priv, 0x280c + (unit * 0x10));
 	u32 client = (stat & 0x00001f00) >> 8;
 
-	NV_INFO(dev, "PFIFO: %s fault at 0x%010llx [",
-		(stat & 0x00000080) ? "write" : "read", (u64)vahi << 32 | valo);
+	nv_error(priv, "PFIFO: %s fault at 0x%010llx [", (stat & 0x00000080) ?
+		       "write" : "read", (u64)vahi << 32 | valo);
 	nouveau_enum_print(nve0_fifo_fault_reason, stat & 0x0000000f);
 	printk("] from ");
 	nouveau_enum_print(nve0_fifo_fault_unit, unit);
@@ -292,160 +389,205 @@
 }
 
 static int
-nve0_fifo_page_flip(struct drm_device *dev, u32 chid)
+nve0_fifo_swmthd(struct nve0_fifo_priv *priv, u32 chid, u32 mthd, u32 data)
 {
-	struct nve0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO);
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_channel *chan = NULL;
+	struct nve0_fifo_chan *chan = NULL;
+	struct nouveau_handle *bind;
 	unsigned long flags;
 	int ret = -EINVAL;
 
-	spin_lock_irqsave(&dev_priv->channels.lock, flags);
-	if (likely(chid >= 0 && chid < priv->base.channels)) {
-		chan = dev_priv->channels.ptr[chid];
-		if (likely(chan)) {
-			struct nouveau_software_chan *swch =
-				chan->engctx[NVOBJ_ENGINE_SW];
-			ret = swch->flip(swch->flip_data);
-		}
+	spin_lock_irqsave(&priv->base.lock, flags);
+	if (likely(chid >= priv->base.min && chid <= priv->base.max))
+		chan = (void *)priv->base.channel[chid];
+	if (unlikely(!chan))
+		goto out;
+
+	bind = nouveau_namedb_get_class(nv_namedb(chan), 0x906e);
+	if (likely(bind)) {
+		if (!mthd || !nv_call(bind->object, mthd, data))
+			ret = 0;
+		nouveau_namedb_put(bind);
 	}
-	spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
+
+out:
+	spin_unlock_irqrestore(&priv->base.lock, flags);
 	return ret;
 }
 
 static void
-nve0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
+nve0_fifo_isr_subfifo_intr(struct nve0_fifo_priv *priv, int unit)
 {
-	u32 stat = nv_rd32(dev, 0x040108 + (unit * 0x2000));
-	u32 addr = nv_rd32(dev, 0x0400c0 + (unit * 0x2000));
-	u32 data = nv_rd32(dev, 0x0400c4 + (unit * 0x2000));
-	u32 chid = nv_rd32(dev, 0x040120 + (unit * 0x2000)) & 0xfff;
-	u32 subc = (addr & 0x00070000);
+	u32 stat = nv_rd32(priv, 0x040108 + (unit * 0x2000));
+	u32 addr = nv_rd32(priv, 0x0400c0 + (unit * 0x2000));
+	u32 data = nv_rd32(priv, 0x0400c4 + (unit * 0x2000));
+	u32 chid = nv_rd32(priv, 0x040120 + (unit * 0x2000)) & 0xfff;
+	u32 subc = (addr & 0x00070000) >> 16;
 	u32 mthd = (addr & 0x00003ffc);
 	u32 show = stat;
 
 	if (stat & 0x00200000) {
 		if (mthd == 0x0054) {
-			if (!nve0_fifo_page_flip(dev, chid))
+			if (!nve0_fifo_swmthd(priv, chid, 0x0500, 0x00000000))
 				show &= ~0x00200000;
 		}
 	}
 
-	if (show) {
-		NV_INFO(dev, "PFIFO%d:", unit);
-		nouveau_bitfield_print(nve0_fifo_subfifo_intr, show);
-		NV_INFO(dev, "PFIFO%d: ch %d subc %d mthd 0x%04x data 0x%08x\n",
-			unit, chid, subc, mthd, data);
+	if (stat & 0x00800000) {
+		if (!nve0_fifo_swmthd(priv, chid, mthd, data))
+			show &= ~0x00800000;
 	}
 
-	nv_wr32(dev, 0x0400c0 + (unit * 0x2000), 0x80600008);
-	nv_wr32(dev, 0x040108 + (unit * 0x2000), stat);
+	if (show) {
+		nv_error(priv, "SUBFIFO%d:", unit);
+		nouveau_bitfield_print(nve0_fifo_subfifo_intr, show);
+		printk("\n");
+		nv_error(priv, "SUBFIFO%d: ch %d subc %d mthd 0x%04x "
+			       "data 0x%08x\n",
+			 unit, chid, subc, mthd, data);
+	}
+
+	nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008);
+	nv_wr32(priv, 0x040108 + (unit * 0x2000), stat);
 }
 
 static void
-nve0_fifo_isr(struct drm_device *dev)
+nve0_fifo_intr(struct nouveau_subdev *subdev)
 {
-	u32 mask = nv_rd32(dev, 0x002140);
-	u32 stat = nv_rd32(dev, 0x002100) & mask;
+	struct nve0_fifo_priv *priv = (void *)subdev;
+	u32 mask = nv_rd32(priv, 0x002140);
+	u32 stat = nv_rd32(priv, 0x002100) & mask;
 
 	if (stat & 0x00000100) {
-		NV_INFO(dev, "PFIFO: unknown status 0x00000100\n");
-		nv_wr32(dev, 0x002100, 0x00000100);
+		nv_warn(priv, "unknown status 0x00000100\n");
+		nv_wr32(priv, 0x002100, 0x00000100);
 		stat &= ~0x00000100;
 	}
 
 	if (stat & 0x10000000) {
-		u32 units = nv_rd32(dev, 0x00259c);
+		u32 units = nv_rd32(priv, 0x00259c);
 		u32 u = units;
 
 		while (u) {
 			int i = ffs(u) - 1;
-			nve0_fifo_isr_vm_fault(dev, i);
+			nve0_fifo_isr_vm_fault(priv, i);
 			u &= ~(1 << i);
 		}
 
-		nv_wr32(dev, 0x00259c, units);
+		nv_wr32(priv, 0x00259c, units);
 		stat &= ~0x10000000;
 	}
 
 	if (stat & 0x20000000) {
-		u32 units = nv_rd32(dev, 0x0025a0);
+		u32 units = nv_rd32(priv, 0x0025a0);
 		u32 u = units;
 
 		while (u) {
 			int i = ffs(u) - 1;
-			nve0_fifo_isr_subfifo_intr(dev, i);
+			nve0_fifo_isr_subfifo_intr(priv, i);
 			u &= ~(1 << i);
 		}
 
-		nv_wr32(dev, 0x0025a0, units);
+		nv_wr32(priv, 0x0025a0, units);
 		stat &= ~0x20000000;
 	}
 
 	if (stat & 0x40000000) {
-		NV_INFO(dev, "PFIFO: unknown status 0x40000000\n");
-		nv_mask(dev, 0x002a00, 0x00000000, 0x00000000);
+		nv_warn(priv, "unknown status 0x40000000\n");
+		nv_mask(priv, 0x002a00, 0x00000000, 0x00000000);
 		stat &= ~0x40000000;
 	}
 
 	if (stat) {
-		NV_INFO(dev, "PFIFO: unhandled status 0x%08x\n", stat);
-		nv_wr32(dev, 0x002100, stat);
-		nv_wr32(dev, 0x002140, 0);
+		nv_fatal(priv, "unhandled status 0x%08x\n", stat);
+		nv_wr32(priv, 0x002100, stat);
+		nv_wr32(priv, 0x002140, 0);
 	}
 }
 
-static void
-nve0_fifo_destroy(struct drm_device *dev, int engine)
+static int
+nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	       struct nouveau_oclass *oclass, void *data, u32 size,
+	       struct nouveau_object **pobject)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nve0_fifo_priv *priv = nv_engine(dev, engine);
+	struct nve0_fifo_priv *priv;
+	int ret;
+
+	ret = nouveau_fifo_create(parent, engine, oclass, 0, 4095, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_new(parent, NULL, 4096 * 0x200, 0x1000,
+				 NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_map(priv->user.mem, NV_MEM_ACCESS_RW,
+				&priv->user.bar);
+	if (ret)
+		return ret;
+
+	nv_subdev(priv)->unit = 0x00000100;
+	nv_subdev(priv)->intr = nve0_fifo_intr;
+	nv_engine(priv)->cclass = &nve0_fifo_cclass;
+	nv_engine(priv)->sclass = nve0_fifo_sclass;
+	return 0;
+}
+
+static void
+nve0_fifo_dtor(struct nouveau_object *object)
+{
+	struct nve0_fifo_priv *priv = (void *)object;
 	int i;
 
 	nouveau_gpuobj_unmap(&priv->user.bar);
 	nouveau_gpuobj_ref(NULL, &priv->user.mem);
 
-	for (i = 0; i < NVE0_FIFO_ENGINE_NUM; i++) {
-		nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[0]);
+	for (i = 0; i < ARRAY_SIZE(priv->engine); i++) {
 		nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[1]);
+		nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[0]);
 	}
 
-	dev_priv->eng[engine] = NULL;
-	kfree(priv);
+	nouveau_fifo_destroy(&priv->base);
 }
 
-int
-nve0_fifo_create(struct drm_device *dev)
+static int
+nve0_fifo_init(struct nouveau_object *object)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nve0_fifo_priv *priv;
-	int ret;
+	struct nve0_fifo_priv *priv = (void *)object;
+	int ret, i;
 
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	priv->base.base.destroy = nve0_fifo_destroy;
-	priv->base.base.init = nve0_fifo_init;
-	priv->base.base.fini = nve0_fifo_fini;
-	priv->base.base.context_new = nve0_fifo_context_new;
-	priv->base.base.context_del = nve0_fifo_context_del;
-	priv->base.channels = 4096;
-	dev_priv->eng[NVOBJ_ENGINE_FIFO] = &priv->base.base;
-
-	ret = nouveau_gpuobj_new(dev, NULL, priv->base.channels * 512, 0x1000,
-				 NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem);
+	ret = nouveau_fifo_init(&priv->base);
 	if (ret)
-		goto error;
+		return ret;
 
-	ret = nouveau_gpuobj_map_bar(priv->user.mem, NV_MEM_ACCESS_RW,
-				    &priv->user.bar);
-	if (ret)
-		goto error;
+	/* enable all available PSUBFIFOs */
+	nv_wr32(priv, 0x000204, 0xffffffff);
+	priv->spoon_nr = hweight32(nv_rd32(priv, 0x000204));
+	nv_debug(priv, "%d subfifo(s)\n", priv->spoon_nr);
 
-	nouveau_irq_register(dev, 8, nve0_fifo_isr);
-error:
-	if (ret)
-		priv->base.base.destroy(dev, NVOBJ_ENGINE_FIFO);
-	return ret;
+	/* PSUBFIFO[n] */
+	for (i = 0; i < priv->spoon_nr; i++) {
+		nv_mask(priv, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000);
+		nv_wr32(priv, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */
+		nv_wr32(priv, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTREN */
+	}
+
+	nv_wr32(priv, 0x002254, 0x10000000 | priv->user.bar.offset >> 12);
+
+	nv_wr32(priv, 0x002a00, 0xffffffff);
+	nv_wr32(priv, 0x002100, 0xffffffff);
+	nv_wr32(priv, 0x002140, 0xbfffffff);
+	return 0;
 }
+
+struct nouveau_oclass
+nve0_fifo_oclass = {
+	.handle = NV_ENGINE(FIFO, 0xe0),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nve0_fifo_ctor,
+		.dtor = nve0_fifo_dtor,
+		.init = nve0_fifo_init,
+		.fini = _nouveau_fifo_fini,
+	},
+};