drm/nouveau/fb: merge fb/vram and port to subdev interfaces

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 0615e95..5e18a53 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -8,6 +8,7 @@
 
 nouveau-y := core/core/client.o
 nouveau-y += core/core/engine.o
+nouveau-y += core/core/enum.o
 nouveau-y += core/core/handle.o
 nouveau-y += core/core/mm.o
 nouveau-y += core/core/namedb.o
@@ -50,6 +51,7 @@
 nouveau-y += core/subdev/devinit/nv1a.o
 nouveau-y += core/subdev/devinit/nv20.o
 nouveau-y += core/subdev/devinit/nv50.o
+nouveau-y += core/subdev/fb/base.o
 nouveau-y += core/subdev/fb/nv04.o
 nouveau-y += core/subdev/fb/nv10.o
 nouveau-y += core/subdev/fb/nv20.o
@@ -57,8 +59,6 @@
 nouveau-y += core/subdev/fb/nv40.o
 nouveau-y += core/subdev/fb/nv50.o
 nouveau-y += core/subdev/fb/nvc0.o
-nouveau-y += core/subdev/fb/nv50_vram.o
-nouveau-y += core/subdev/fb/nvc0_vram.o
 nouveau-y += core/subdev/gpio/base.o
 nouveau-y += core/subdev/gpio/nv10.o
 nouveau-y += core/subdev/gpio/nv50.o
@@ -69,6 +69,7 @@
 nouveau-y += core/subdev/instmem/nv04.o
 nouveau-y += core/subdev/instmem/nv50.o
 nouveau-y += core/subdev/instmem/nvc0.o
+nouveau-y += core/subdev/ltcg/nvc0.o
 nouveau-y += core/subdev/mc/base.o
 nouveau-y += core/subdev/mc/nv04.o
 nouveau-y += core/subdev/mc/nv44.o
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c
index 3079f08..23ae459 100644
--- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c
@@ -163,7 +163,7 @@
 		break;
 	default:
 		nv_wr32(dev, 0x002230, 0x00000000);
-		nv_wr32(dev, 0x002220, ((dev_priv->vram_size - 512 * 1024 +
+		nv_wr32(dev, 0x002220, ((nvfb_vram_size(dev) - 512 * 1024 +
 					 dev_priv->ramfc->pinst) >> 16) |
 				       0x00030000);
 		break;
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
index 19e303b..d006658 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c
@@ -895,9 +895,7 @@
 static void
 nv10_graph_set_tile_region(struct drm_device *dev, int i)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
-
+	struct nouveau_fb_tile *tile = nvfb_tile(dev, i);
 	nv_wr32(dev, NV10_PGRAPH_TLIMIT(i), tile->limit);
 	nv_wr32(dev, NV10_PGRAPH_TSIZE(i), tile->pitch);
 	nv_wr32(dev, NV10_PGRAPH_TILE(i), tile->addr);
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
index 718ecf7..dd31156 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c
@@ -472,7 +472,7 @@
 nv20_graph_set_tile_region(struct drm_device *dev, int i)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
+	struct nouveau_fb_tile *tile = nvfb_tile(dev, i);
 
 	nv_wr32(dev, NV20_PGRAPH_TLIMIT(i), tile->limit);
 	nv_wr32(dev, NV20_PGRAPH_TSIZE(i), tile->pitch);
diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
index 4ad9759..ab3af6d 100644
--- a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c
@@ -125,7 +125,7 @@
 nv40_graph_set_tile_region(struct drm_device *dev, int i)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
+	struct nouveau_fb_tile *tile = nvfb_tile(dev, i);
 
 	switch (dev_priv->chipset) {
 	case 0x40:
@@ -178,7 +178,6 @@
 {
 	struct nv40_graph_engine *pgraph = nv_engine(dev, engine);
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
 	uint32_t vramsz;
 	int i, j;
 
@@ -280,7 +279,7 @@
 	}
 
 	/* Turn all the tiling regions off. */
-	for (i = 0; i < pfb->num_tiles; i++)
+	for (i = 0; i < nvfb_tile_nr(dev); i++)
 		nv40_graph_set_tile_region(dev, i);
 
 	/* begin RAM config */
diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
index 4a14a67..eb5455f 100644
--- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
+++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c
@@ -128,7 +128,6 @@
 static int
 nv31_mpeg_init(struct drm_device *dev, int engine)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nv31_mpeg_engine *pmpeg = nv_engine(dev, engine);
 	int i;
 
@@ -138,7 +137,7 @@
 	nv_wr32(dev, 0x00b0e0, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */
 	nv_wr32(dev, 0x00b0e8, 0x00000020); /* nvidia: rd 0x01, wr 0x20 */
 
-	for (i = 0; i < dev_priv->engine.fb.num_tiles; i++)
+	for (i = 0; i < nvfb_tile_nr(dev); i++)
 		pmpeg->base.set_tile_region(dev, i);
 
 	/* PMPEG init */
@@ -235,9 +234,7 @@
 static void
 nv31_vpe_set_tile_region(struct drm_device *dev, int i)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
-
+	struct nouveau_fb_tile *tile = nvfb_tile(dev, i);
 	nv_wr32(dev, 0x00b008 + (i * 0x10), tile->pitch);
 	nv_wr32(dev, 0x00b004 + (i * 0x10), tile->limit);
 	nv_wr32(dev, 0x00b000 + (i * 0x10), tile->addr);
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h
new file mode 100644
index 0000000..91dbdc1
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h
@@ -0,0 +1,137 @@
+#ifndef __NOUVEAU_FB_H__
+#define __NOUVEAU_FB_H__
+
+#ifndef XXX_THIS_IS_A_HACK
+#include <core/subdev.h>
+#include <core/device.h>
+#endif
+#include <core/mm.h>
+#include <subdev/vm.h>
+
+/* memory type/access flags, do not match hardware values */
+#define NV_MEM_ACCESS_RO  1
+#define NV_MEM_ACCESS_WO  2
+#define NV_MEM_ACCESS_RW (NV_MEM_ACCESS_RO | NV_MEM_ACCESS_WO)
+#define NV_MEM_ACCESS_SYS 4
+#define NV_MEM_ACCESS_VM  8
+#define NV_MEM_ACCESS_NOSNOOP 16
+
+#define NV_MEM_TARGET_VRAM        0
+#define NV_MEM_TARGET_PCI         1
+#define NV_MEM_TARGET_PCI_NOSNOOP 2
+#define NV_MEM_TARGET_VM          3
+#define NV_MEM_TARGET_GART        4
+
+#define NV_MEM_TYPE_VM 0x7f
+#define NV_MEM_COMP_VM 0x03
+
+struct nouveau_mem {
+	struct drm_device *dev;
+
+	struct nouveau_vma bar_vma;
+	struct nouveau_vma vma[2];
+	u8  page_shift;
+
+	struct nouveau_mm_node *tag;
+	struct list_head regions;
+	dma_addr_t *pages;
+	u32 memtype;
+	u64 offset;
+	u64 size;
+	struct sg_table *sg;
+};
+
+struct nouveau_fb_tile {
+	struct nouveau_mm_node *tag;
+	u32 addr;
+	u32 limit;
+	u32 pitch;
+	u32 zcomp;
+};
+
+#ifndef XXX_THIS_IS_A_HACK
+struct nouveau_fb {
+	struct nouveau_subdev base;
+
+	bool (*memtype_valid)(struct nouveau_fb *, u32 memtype);
+
+	struct {
+		enum {
+			NV_MEM_TYPE_UNKNOWN = 0,
+			NV_MEM_TYPE_STOLEN,
+			NV_MEM_TYPE_SGRAM,
+			NV_MEM_TYPE_SDRAM,
+			NV_MEM_TYPE_DDR1,
+			NV_MEM_TYPE_DDR2,
+			NV_MEM_TYPE_DDR3,
+			NV_MEM_TYPE_GDDR2,
+			NV_MEM_TYPE_GDDR3,
+			NV_MEM_TYPE_GDDR4,
+			NV_MEM_TYPE_GDDR5
+		} type;
+		u64 stolen;
+		u64 size;
+		int ranks;
+
+		int  (*get)(struct nouveau_fb *, u64 size, u32 align,
+			    u32 size_nc, u32 type, struct nouveau_mem **);
+		void (*put)(struct nouveau_fb *, struct nouveau_mem **);
+	} ram;
+
+	struct nouveau_mm vram;
+	struct nouveau_mm tags;
+
+	struct {
+		struct nouveau_fb_tile region[16];
+		int regions;
+		void (*init)(struct nouveau_fb *, int i, u32 addr, u32 size,
+			     u32 pitch, u32 flags, struct nouveau_fb_tile *);
+		void (*fini)(struct nouveau_fb *, int i,
+			     struct nouveau_fb_tile *);
+		void (*prog)(struct nouveau_fb *, int i,
+			     struct nouveau_fb_tile *);
+	} tile;
+};
+
+static inline struct nouveau_fb *
+nouveau_fb(void *obj)
+{
+	return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_FB];
+}
+
+#define nouveau_fb_create(p,e,c,d)                                             \
+	nouveau_subdev_create((p), (e), (c), 0, "PFB", "fb", (d))
+int  nouveau_fb_created(struct nouveau_fb *);
+void nouveau_fb_destroy(struct nouveau_fb *);
+int  nouveau_fb_init(struct nouveau_fb *);
+#define nouveau_fb_fini(p,s)                                                   \
+	nouveau_subdev_fini(&(p)->base, (s))
+
+void _nouveau_fb_dtor(struct nouveau_object *);
+int  _nouveau_fb_init(struct nouveau_object *);
+#define _nouveau_fb_fini _nouveau_subdev_fini
+
+extern struct nouveau_oclass nv04_fb_oclass;
+extern struct nouveau_oclass nv10_fb_oclass;
+extern struct nouveau_oclass nv20_fb_oclass;
+extern struct nouveau_oclass nv30_fb_oclass;
+extern struct nouveau_oclass nv40_fb_oclass;
+extern struct nouveau_oclass nv50_fb_oclass;
+extern struct nouveau_oclass nvc0_fb_oclass;
+
+struct nouveau_bios;
+int  nouveau_fb_bios_memtype(struct nouveau_bios *);
+
+bool nv04_fb_memtype_valid(struct nouveau_fb *, u32 memtype);
+
+void nv10_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *);
+
+void nv30_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size,
+		       u32 pitch, u32 flags, struct nouveau_fb_tile *);
+void nv30_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *);
+
+void nv50_fb_vram_del(struct nouveau_fb *, struct nouveau_mem **);
+void nv50_fb_trap(struct nouveau_fb *, int display);
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h b/drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h
new file mode 100644
index 0000000..f351f63
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/ltcg.h
@@ -0,0 +1,33 @@
+#ifndef __NOUVEAU_LTCG_H__
+#define __NOUVEAU_LTCG_H__
+
+#include <core/subdev.h>
+#include <core/device.h>
+
+struct nouveau_ltcg {
+	struct nouveau_subdev base;
+};
+
+static inline struct nouveau_ltcg *
+nouveau_ltcg(void *obj)
+{
+	return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_LTCG];
+}
+
+#define nouveau_ltcg_create(p,e,o,d)                                           \
+	nouveau_subdev_create_((p), (e), (o), 0, "PLTCG", "level2",            \
+			       sizeof(**d), (void **)d)
+#define nouveau_ltcg_destroy(p)                                                \
+	nouveau_subdev_destroy(&(p)->base)
+#define nouveau_ltcg_init(p)                                                   \
+	nouveau_subdev_init(&(p)->base)
+#define nouveau_ltcg_fini(p,s)                                                 \
+	nouveau_subdev_fini(&(p)->base, (s))
+
+#define _nouveau_ltcg_dtor _nouveau_subdev_dtor
+#define _nouveau_ltcg_init _nouveau_subdev_init
+#define _nouveau_ltcg_fini _nouveau_subdev_fini
+
+extern struct nouveau_oclass nvc0_ltcg_oclass;
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/vm.h b/drivers/gpu/drm/nouveau/core/include/subdev/vm.h
index 7c1fc21..6d3764b 100644
--- a/drivers/gpu/drm/nouveau/core/include/subdev/vm.h
+++ b/drivers/gpu/drm/nouveau/core/include/subdev/vm.h
@@ -25,11 +25,10 @@
 #ifndef __NOUVEAU_VM_H__
 #define __NOUVEAU_VM_H__
 
-#include "drmP.h"
-
-#include "nouveau_drv.h"
 #include <core/mm.h>
 
+struct nouveau_mem;
+
 struct nouveau_vm_pgt {
 	struct nouveau_gpuobj *obj[2];
 	u32 refcount[2];
diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c
index aa43bd5..66b7156 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c
@@ -29,6 +29,7 @@
 #include <subdev/devinit.h>
 #include <subdev/mc.h>
 #include <subdev/timer.h>
+#include <subdev/fb.h>
 
 int
 nv04_identify(struct nouveau_device *device)
@@ -41,6 +42,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv04_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv04_fb_oclass;
 		break;
 	case 0x05:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -49,6 +51,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv05_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv04_fb_oclass;
 		break;
 	default:
 		nv_fatal(device, "unknown RIVA chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c
index 1926c33..cf81479 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c
@@ -30,6 +30,7 @@
 #include <subdev/devinit.h>
 #include <subdev/mc.h>
 #include <subdev/timer.h>
+#include <subdev/fb.h>
 
 int
 nv10_identify(struct nouveau_device *device)
@@ -43,6 +44,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
 		break;
 	case 0x15:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -52,6 +54,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
 		break;
 	case 0x16:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -61,6 +64,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
 		break;
 	case 0x1a:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -70,6 +74,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
 		break;
 	case 0x11:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -79,6 +84,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
 		break;
 	case 0x17:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -88,6 +94,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
 		break;
 	case 0x1f:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -97,6 +104,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
 		break;
 	case 0x18:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -106,6 +114,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv10_fb_oclass;
 		break;
 	default:
 		nv_fatal(device, "unknown Celsius chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c
index 88729c3..e97280c 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c
@@ -30,6 +30,7 @@
 #include <subdev/devinit.h>
 #include <subdev/mc.h>
 #include <subdev/timer.h>
+#include <subdev/fb.h>
 
 int
 nv20_identify(struct nouveau_device *device)
@@ -43,6 +44,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv20_fb_oclass;
 		break;
 	case 0x25:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -52,6 +54,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv20_fb_oclass;
 		break;
 	case 0x28:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -61,6 +64,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv20_fb_oclass;
 		break;
 	case 0x2a:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -70,6 +74,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv20_fb_oclass;
 		break;
 	default:
 		nv_fatal(device, "unknown Kelvin chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c
index 4feea88..ddd3ab6 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c
@@ -30,6 +30,7 @@
 #include <subdev/devinit.h>
 #include <subdev/mc.h>
 #include <subdev/timer.h>
+#include <subdev/fb.h>
 
 int
 nv30_identify(struct nouveau_device *device)
@@ -43,6 +44,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv30_fb_oclass;
 		break;
 	case 0x35:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -52,6 +54,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv30_fb_oclass;
 		break;
 	case 0x31:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -61,6 +64,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv30_fb_oclass;
 		break;
 	case 0x36:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -70,6 +74,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv30_fb_oclass;
 		break;
 	case 0x34:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -79,6 +84,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv30_fb_oclass;
 		break;
 	default:
 		nv_fatal(device, "unknown Rankine chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c
index 8f68361..c7ea921 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c
@@ -30,6 +30,7 @@
 #include <subdev/devinit.h>
 #include <subdev/mc.h>
 #include <subdev/timer.h>
+#include <subdev/fb.h>
 
 int
 nv40_identify(struct nouveau_device *device)
@@ -43,6 +44,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x41:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -52,6 +54,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x42:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -61,6 +64,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x43:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -70,6 +74,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x45:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -79,6 +84,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x47:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -88,6 +94,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x49:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -97,6 +104,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x4b:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -106,6 +114,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv04_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x44:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -115,6 +124,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x46:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -124,6 +134,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x4a:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -133,6 +144,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x4c:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -142,6 +154,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x4e:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -151,6 +164,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x63:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -160,6 +174,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x67:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -169,6 +184,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	case 0x68:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -178,6 +194,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv44_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv40_fb_oclass;
 		break;
 	default:
 		nv_fatal(device, "unknown Curie chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c
index e844525..e60cdf2 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c
@@ -30,6 +30,7 @@
 #include <subdev/devinit.h>
 #include <subdev/mc.h>
 #include <subdev/timer.h>
+#include <subdev/fb.h>
 
 int
 nv50_identify(struct nouveau_device *device)
@@ -43,6 +44,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0x84:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -52,6 +54,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0x86:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -61,6 +64,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0x92:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -70,6 +74,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0x94:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -79,6 +84,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0x96:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -88,6 +94,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv50_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0x98:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -97,6 +104,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0xa0:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -106,6 +114,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0xaa:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -115,6 +124,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0xac:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -124,6 +134,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0xa3:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -133,6 +144,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0xa5:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -142,6 +154,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0xa8:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -151,6 +164,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	case 0xaf:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -160,6 +174,7 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nv98_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nv50_fb_oclass;
 		break;
 	default:
 		nv_fatal(device, "unknown Tesla chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c
index 56d1950..8de6730 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c
@@ -30,6 +30,8 @@
 #include <subdev/devinit.h>
 #include <subdev/mc.h>
 #include <subdev/timer.h>
+#include <subdev/fb.h>
+#include <subdev/ltcg.h>
 
 int
 nvc0_identify(struct nouveau_device *device)
@@ -43,6 +45,8 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+		device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
 		break;
 	case 0xc4:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -52,6 +56,8 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+		device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
 		break;
 	case 0xc3:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -61,6 +67,8 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+		device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
 		break;
 	case 0xce:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -70,6 +78,8 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+		device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
 		break;
 	case 0xcf:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -79,6 +89,8 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+		device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
 		break;
 	case 0xc1:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -88,6 +100,8 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+		device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
 		break;
 	case 0xc8:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -97,6 +111,8 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+		device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
 		break;
 	case 0xd9:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -106,6 +122,8 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+		device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
 		break;
 	default:
 		nv_fatal(device, "unknown Fermi chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c
index ab5e052..919a102 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c
@@ -30,6 +30,8 @@
 #include <subdev/devinit.h>
 #include <subdev/mc.h>
 #include <subdev/timer.h>
+#include <subdev/fb.h>
+#include <subdev/ltcg.h>
 
 int
 nve0_identify(struct nouveau_device *device)
@@ -43,6 +45,8 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+		device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
 		break;
 	case 0xe7:
 		device->oclass[NVDEV_SUBDEV_VBIOS  ] = &nouveau_bios_oclass;
@@ -52,6 +56,8 @@
 		device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass;
 		device->oclass[NVDEV_SUBDEV_MC     ] = &nvc0_mc_oclass;
 		device->oclass[NVDEV_SUBDEV_TIMER  ] = &nv04_timer_oclass;
+		device->oclass[NVDEV_SUBDEV_FB     ] = &nvc0_fb_oclass;
+		device->oclass[NVDEV_SUBDEV_LTCG   ] = &nvc0_ltcg_oclass;
 		break;
 	default:
 		nv_fatal(device, "unknown Kepler chipset\n");
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/base.c b/drivers/gpu/drm/nouveau/core/subdev/fb/base.c
new file mode 100644
index 0000000..f0086de
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/base.c
@@ -0,0 +1,130 @@
+/*
+ * 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 "subdev/fb.h"
+#include "subdev/bios.h"
+#include "subdev/bios/bit.h"
+
+int
+nouveau_fb_bios_memtype(struct nouveau_bios *bios)
+{
+	struct bit_entry M;
+	u8 ramcfg;
+
+	ramcfg = (nv_rd32(bios, 0x101000) & 0x0000003c) >> 2;
+	if (!bit_entry(bios, 'M', &M) && M.version == 2 && M.length >= 5) {
+		u16 table   = nv_ro16(bios, M.offset + 3);
+		u8  version = nv_ro08(bios, table + 0);
+		u8  header  = nv_ro08(bios, table + 1);
+		u8  record  = nv_ro08(bios, table + 2);
+		u8  entries = nv_ro08(bios, table + 3);
+		if (table && version == 0x10 && ramcfg < entries) {
+			u16 entry = table + header + (ramcfg * record);
+			switch (nv_ro08(bios, entry) & 0x0f) {
+			case 0: return NV_MEM_TYPE_DDR2;
+			case 1: return NV_MEM_TYPE_DDR3;
+			case 2: return NV_MEM_TYPE_GDDR3;
+			case 3: return NV_MEM_TYPE_GDDR5;
+			default:
+				break;
+			}
+
+		}
+	}
+
+	return NV_MEM_TYPE_UNKNOWN;
+}
+
+int
+nouveau_fb_init(struct nouveau_fb *pfb)
+{
+	int ret, i;
+
+	ret = nouveau_subdev_init(&pfb->base);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < pfb->tile.regions; i++)
+		pfb->tile.prog(pfb, i, &pfb->tile.region[i]);
+
+	return 0;
+}
+
+int
+_nouveau_fb_init(struct nouveau_object *object)
+{
+	struct nouveau_fb *pfb = (void *)object;
+	return nouveau_fb_init(pfb);
+}
+
+void
+nouveau_fb_destroy(struct nouveau_fb *pfb)
+{
+	int i;
+
+	for (i = 0; i < pfb->tile.regions; i++)
+		pfb->tile.fini(pfb, i, &pfb->tile.region[i]);
+
+	if (pfb->tags.block_size)
+		nouveau_mm_fini(&pfb->tags);
+
+	if (pfb->vram.block_size)
+		nouveau_mm_fini(&pfb->vram);
+
+	nouveau_subdev_destroy(&pfb->base);
+}
+
+void
+_nouveau_fb_dtor(struct nouveau_object *object)
+{
+	struct nouveau_fb *pfb = (void *)object;
+	nouveau_fb_destroy(pfb);
+}
+
+int
+nouveau_fb_created(struct nouveau_fb *pfb)
+{
+	static const char *name[] = {
+		[NV_MEM_TYPE_UNKNOWN] = "unknown",
+		[NV_MEM_TYPE_STOLEN ] = "stolen system memory",
+		[NV_MEM_TYPE_SGRAM  ] = "SGRAM",
+		[NV_MEM_TYPE_SDRAM  ] = "SDRAM",
+		[NV_MEM_TYPE_DDR1   ] = "DDR1",
+		[NV_MEM_TYPE_DDR2   ] = "DDR2",
+		[NV_MEM_TYPE_DDR3   ] = "DDR3",
+		[NV_MEM_TYPE_GDDR2  ] = "GDDR2",
+		[NV_MEM_TYPE_GDDR3  ] = "GDDR3",
+		[NV_MEM_TYPE_GDDR4  ] = "GDDR4",
+		[NV_MEM_TYPE_GDDR5  ] = "GDDR5",
+	};
+
+	if (pfb->ram.size == 0) {
+		nv_fatal(pfb, "no vram detected!!\n");
+		return -ERANGE;
+	}
+
+	nv_info(pfb, "RAM type: %s\n", name[pfb->ram.type]);
+	nv_info(pfb, "RAM size: %d MiB\n", (int)(pfb->ram.size >> 20));
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c
index eef4844..eb06836 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c
@@ -1,55 +1,130 @@
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <nouveau_drm.h>
+/*
+ * 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
+ */
 
-int
-nv04_fb_vram_init(struct drm_device *dev)
+#include <subdev/fb.h>
+
+#define NV04_PFB_BOOT_0						0x00100000
+#	define NV04_PFB_BOOT_0_RAM_AMOUNT			0x00000003
+#	define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB			0x00000000
+#	define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB			0x00000001
+#	define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB			0x00000002
+#	define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB			0x00000003
+#	define NV04_PFB_BOOT_0_RAM_WIDTH_128			0x00000004
+#	define NV04_PFB_BOOT_0_RAM_TYPE				0x00000028
+#	define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT		0x00000000
+#	define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT		0x00000008
+#	define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK	0x00000010
+#	define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT		0x00000018
+#	define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT		0x00000020
+#	define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16		0x00000028
+#	define NV04_PFB_BOOT_0_UMA_ENABLE			0x00000100
+#	define NV04_PFB_BOOT_0_UMA_SIZE				0x0000f000
+#define NV04_PFB_CFG0						0x00100200
+
+struct nv04_fb_priv {
+	struct nouveau_fb base;
+};
+
+bool
+nv04_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	u32 boot0 = nv_rd32(dev, NV04_PFB_BOOT_0);
+	if (!(tile_flags & 0xff00))
+		return true;
 
+	return false;
+}
+
+static int
+nv04_fb_init(struct nouveau_object *object)
+{
+	struct nv04_fb_priv *priv = (void *)object;
+	int ret;
+
+	ret = nouveau_fb_init(&priv->base);
+	if (ret)
+		return ret;
+
+	/* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows
+	 * nvidia reading PFB_CFG_0, then writing back its original value.
+	 * (which was 0x701114 in this case)
+	 */
+	nv_wr32(priv, NV04_PFB_CFG0, 0x1114);
+	return 0;
+}
+
+static int
+nv04_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	     struct nouveau_oclass *oclass, void *data, u32 size,
+	     struct nouveau_object **pobject)
+{
+	struct nv04_fb_priv *priv;
+	u32 boot0;
+	int ret;
+
+	ret = nouveau_fb_create(parent, engine, oclass, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+
+	boot0 = nv_rd32(priv, NV04_PFB_BOOT_0);
 	if (boot0 & 0x00000100) {
-		dev_priv->vram_size  = ((boot0 >> 12) & 0xf) * 2 + 2;
-		dev_priv->vram_size *= 1024 * 1024;
+		priv->base.ram.size  = ((boot0 >> 12) & 0xf) * 2 + 2;
+		priv->base.ram.size *= 1024 * 1024;
 	} else {
 		switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) {
 		case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB:
-			dev_priv->vram_size = 32 * 1024 * 1024;
+			priv->base.ram.size = 32 * 1024 * 1024;
 			break;
 		case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB:
-			dev_priv->vram_size = 16 * 1024 * 1024;
+			priv->base.ram.size = 16 * 1024 * 1024;
 			break;
 		case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB:
-			dev_priv->vram_size = 8 * 1024 * 1024;
+			priv->base.ram.size = 8 * 1024 * 1024;
 			break;
 		case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB:
-			dev_priv->vram_size = 4 * 1024 * 1024;
+			priv->base.ram.size = 4 * 1024 * 1024;
 			break;
 		}
 	}
 
 	if ((boot0 & 0x00000038) <= 0x10)
-		dev_priv->vram_type = NV_MEM_TYPE_SGRAM;
+		priv->base.ram.type = NV_MEM_TYPE_SGRAM;
 	else
-		dev_priv->vram_type = NV_MEM_TYPE_SDRAM;
+		priv->base.ram.type = NV_MEM_TYPE_SDRAM;
 
-	return 0;
+
+	priv->base.memtype_valid = nv04_fb_memtype_valid;
+	return nouveau_fb_created(&priv->base);
 }
 
-int
-nv04_fb_init(struct drm_device *dev)
-{
-	/* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows
-	 * nvidia reading PFB_CFG_0, then writing back its original value.
-	 * (which was 0x701114 in this case)
-	 */
-
-	nv_wr32(dev, NV04_PFB_CFG0, 0x1114);
-	return 0;
-}
-
-void
-nv04_fb_takedown(struct drm_device *dev)
-{
-}
+struct nouveau_oclass
+nv04_fb_oclass = {
+	.handle = NV_SUBDEV(FB, 0x04),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv04_fb_ctor,
+		.dtor = _nouveau_fb_dtor,
+		.init = nv04_fb_init,
+		.fini = _nouveau_fb_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c
index 09af63b..f037a42 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c
@@ -1,104 +1,120 @@
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <nouveau_drm.h>
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL 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.
+ *
+ */
 
-void
-nv10_fb_init_tile_region(struct drm_device *dev, int i, uint32_t addr,
-			 uint32_t size, uint32_t pitch, uint32_t flags)
+#include <subdev/fb.h>
+
+struct nv10_fb_priv {
+	struct nouveau_fb base;
+};
+
+static void
+nv10_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
+		  u32 flags, struct nouveau_fb_tile *tile)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
-
 	tile->addr  = 0x80000000 | addr;
 	tile->limit = max(1u, addr + size) - 1;
 	tile->pitch = pitch;
 }
 
-void
-nv10_fb_free_tile_region(struct drm_device *dev, int i)
+static void
+nv10_fb_tile_fini(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
-
-	tile->addr = tile->limit = tile->pitch = tile->zcomp = 0;
+	tile->addr  = 0;
+	tile->limit = 0;
+	tile->pitch = 0;
+	tile->zcomp = 0;
 }
 
 void
-nv10_fb_set_tile_region(struct drm_device *dev, int i)
+nv10_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
-
-	nv_wr32(dev, NV10_PFB_TLIMIT(i), tile->limit);
-	nv_wr32(dev, NV10_PFB_TSIZE(i), tile->pitch);
-	nv_wr32(dev, NV10_PFB_TILE(i), tile->addr);
+	nv_wr32(pfb, 0x100244 + (i * 0x10), tile->limit);
+	nv_wr32(pfb, 0x100248 + (i * 0x10), tile->pitch);
+	nv_wr32(pfb, 0x100240 + (i * 0x10), tile->addr);
 }
 
-int
-nv1a_fb_vram_init(struct drm_device *dev)
+static int
+nv10_fb_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 pci_dev *bridge;
-	uint32_t mem, mib;
+	struct nouveau_device *device = nv_device(parent);
+	struct nv10_fb_priv *priv;
+	int ret;
 
-	bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1));
-	if (!bridge) {
-		NV_ERROR(dev, "no bridge device\n");
-		return 0;
-	}
+	ret = nouveau_fb_create(parent, engine, oclass, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
 
-	if (dev_priv->chipset == 0x1a) {
-		pci_read_config_dword(bridge, 0x7c, &mem);
-		mib = ((mem >> 6) & 31) + 1;
+	if (device->chipset == 0x1a ||  device->chipset == 0x1f) {
+		struct pci_dev *bridge;
+		u32 mem, mib;
+
+		bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1));
+		if (!bridge) {
+			nv_fatal(device, "no bridge device\n");
+			return 0;
+		}
+
+		if (device->chipset == 0x1a) {
+			pci_read_config_dword(bridge, 0x7c, &mem);
+			mib = ((mem >> 6) & 31) + 1;
+		} else {
+			pci_read_config_dword(bridge, 0x84, &mem);
+			mib = ((mem >> 4) & 127) + 1;
+		}
+
+		priv->base.ram.type = NV_MEM_TYPE_STOLEN;
+		priv->base.ram.size = mib * 1024 * 1024;
 	} else {
-		pci_read_config_dword(bridge, 0x84, &mem);
-		mib = ((mem >> 4) & 127) + 1;
+		u32 cfg0 = nv_rd32(priv, 0x100200);
+		if (cfg0 & 0x00000001)
+			priv->base.ram.type = NV_MEM_TYPE_DDR1;
+		else
+			priv->base.ram.type = NV_MEM_TYPE_SDRAM;
+
+		priv->base.ram.size = nv_rd32(priv, 0x10020c) & 0xff000000;
 	}
 
-	dev_priv->vram_size = mib * 1024 * 1024;
-	return 0;
+	priv->base.memtype_valid = nv04_fb_memtype_valid;
+	priv->base.tile.regions = 8;
+	priv->base.tile.init = nv10_fb_tile_init;
+	priv->base.tile.fini = nv10_fb_tile_fini;
+	priv->base.tile.prog = nv10_fb_tile_prog;
+	return nouveau_fb_created(&priv->base);
 }
 
-int
-nv10_fb_vram_init(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	u32 fifo_data = nv_rd32(dev, NV04_PFB_FIFO_DATA);
-	u32 cfg0 = nv_rd32(dev, 0x100200);
-
-	dev_priv->vram_size = fifo_data & NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK;
-
-	if (cfg0 & 0x00000001)
-		dev_priv->vram_type = NV_MEM_TYPE_DDR1;
-	else
-		dev_priv->vram_type = NV_MEM_TYPE_SDRAM;
-
-	return 0;
-}
-
-int
-nv10_fb_init(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	int i;
-
-	/* Turn all the tiling regions off. */
-	pfb->num_tiles = NV10_PFB_TILE__SIZE;
-	for (i = 0; i < pfb->num_tiles; i++)
-		pfb->set_tile_region(dev, i);
-
-	return 0;
-}
-
-void
-nv10_fb_takedown(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	int i;
-
-	for (i = 0; i < pfb->num_tiles; i++)
-		pfb->free_tile_region(dev, i);
-}
+struct nouveau_oclass
+nv10_fb_oclass = {
+	.handle = NV_SUBDEV(FB, 0x10),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv10_fb_ctor,
+		.dtor = _nouveau_fb_dtor,
+		.init = _nouveau_fb_init,
+		.fini = _nouveau_fb_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c
index a7f056c..4b3578f 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c
@@ -1,49 +1,41 @@
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <nouveau_drm.h>
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL 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.
+ *
+ */
 
-static struct drm_mm_node *
-nv20_fb_alloc_tag(struct drm_device *dev, uint32_t size)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	struct drm_mm_node *mem;
-	int ret;
+#include <subdev/fb.h>
 
-	ret = drm_mm_pre_get(&pfb->tag_heap);
-	if (ret)
-		return NULL;
-
-	spin_lock(&dev_priv->tile.lock);
-	mem = drm_mm_search_free(&pfb->tag_heap, size, 0, 0);
-	if (mem)
-		mem = drm_mm_get_block_atomic(mem, size, 0);
-	spin_unlock(&dev_priv->tile.lock);
-
-	return mem;
-}
+struct nv20_fb_priv {
+	struct nouveau_fb base;
+};
 
 static void
-nv20_fb_free_tag(struct drm_device *dev, struct drm_mm_node **pmem)
+nv20_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
+		  u32 flags, struct nouveau_fb_tile *tile)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct drm_mm_node *mem = *pmem;
-	if (mem) {
-		spin_lock(&dev_priv->tile.lock);
-		drm_mm_put_block(mem);
-		spin_unlock(&dev_priv->tile.lock);
-		*pmem = NULL;
-	}
-}
-
-void
-nv20_fb_init_tile_region(struct drm_device *dev, int i, uint32_t addr,
-			 uint32_t size, uint32_t pitch, uint32_t flags)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
-	int bpp = (flags & NOUVEAU_GEM_TILE_32BPP ? 32 : 16);
+	struct nouveau_device *device = nv_device(pfb);
+	int bpp = (flags & 2) ? 32 : 16;
 
 	tile->addr  = 0x00000001 | addr;
 	tile->limit = max(1u, addr + size) - 1;
@@ -53,20 +45,20 @@
 	 * compression meta-data (most likely just a bitmap determining
 	 * if a given tile is compressed or not).
 	 */
-	if (flags & NOUVEAU_GEM_TILE_ZETA) {
-		tile->tag_mem = nv20_fb_alloc_tag(dev, size / 256);
-		if (tile->tag_mem) {
+	size /= 256;
+	if (flags & 4) {
+		if (!nouveau_mm_head(&pfb->tags, 1, size, size, 1, &tile->tag)) {
 			/* Enable Z compression */
-			tile->zcomp = tile->tag_mem->start;
-			if (dev_priv->chipset >= 0x25) {
+			tile->zcomp = tile->tag->offset;
+			if (device->chipset >= 0x25) {
 				if (bpp == 16)
-					tile->zcomp |= NV25_PFB_ZCOMP_MODE_16;
+					tile->zcomp |= 0x00100000;
 				else
-					tile->zcomp |= NV25_PFB_ZCOMP_MODE_32;
+					tile->zcomp |= 0x00200000;
 			} else {
-				tile->zcomp |= NV20_PFB_ZCOMP_EN;
+				tile->zcomp |= 0x80000000;
 				if (bpp != 16)
-					tile->zcomp |= NV20_PFB_ZCOMP_MODE_32;
+					tile->zcomp |= 0x04000000;
 			}
 		}
 
@@ -74,75 +66,71 @@
 	}
 }
 
-void
-nv20_fb_free_tile_region(struct drm_device *dev, int i)
+static void
+nv20_fb_tile_fini(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
-
-	tile->addr = tile->limit = tile->pitch = tile->zcomp = 0;
-	nv20_fb_free_tag(dev, &tile->tag_mem);
+	tile->addr  = 0;
+	tile->limit = 0;
+	tile->pitch = 0;
+	tile->zcomp = 0;
+	nouveau_mm_free(&pfb->tags, &tile->tag);
 }
 
-void
-nv20_fb_set_tile_region(struct drm_device *dev, int i)
+static void
+nv20_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
-
-	nv_wr32(dev, NV10_PFB_TLIMIT(i), tile->limit);
-	nv_wr32(dev, NV10_PFB_TSIZE(i), tile->pitch);
-	nv_wr32(dev, NV10_PFB_TILE(i), tile->addr);
-	nv_wr32(dev, NV20_PFB_ZCOMP(i), tile->zcomp);
+	nv_wr32(pfb, 0x100244 + (i * 0x10), tile->limit);
+	nv_wr32(pfb, 0x100248 + (i * 0x10), tile->pitch);
+	nv_wr32(pfb, 0x100240 + (i * 0x10), tile->addr);
+	nv_wr32(pfb, 0x100300 + (i * 0x04), tile->zcomp);
 }
 
-int
-nv20_fb_vram_init(struct drm_device *dev)
+static int
+nv20_fb_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;
-	u32 mem_size = nv_rd32(dev, 0x10020c);
-	u32 pbus1218 = nv_rd32(dev, 0x001218);
+	struct nouveau_device *device = nv_device(parent);
+	struct nv20_fb_priv *priv;
+	u32 pbus1218;
+	int ret;
 
-	dev_priv->vram_size = mem_size & 0xff000000;
+	ret = nouveau_fb_create(parent, engine, oclass, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+
+	pbus1218 = nv_rd32(priv, 0x001218);
 	switch (pbus1218 & 0x00000300) {
-	case 0x00000000: dev_priv->vram_type = NV_MEM_TYPE_SDRAM; break;
-	case 0x00000100: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break;
-	case 0x00000200: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break;
-	case 0x00000300: dev_priv->vram_type = NV_MEM_TYPE_GDDR2; break;
+	case 0x00000000: priv->base.ram.type = NV_MEM_TYPE_SDRAM; break;
+	case 0x00000100: priv->base.ram.type = NV_MEM_TYPE_DDR1; break;
+	case 0x00000200: priv->base.ram.type = NV_MEM_TYPE_GDDR3; break;
+	case 0x00000300: priv->base.ram.type = NV_MEM_TYPE_GDDR2; break;
 	}
+	priv->base.ram.size = nv_rd32(priv, 0x10020c) & 0xff000000;
 
-	return 0;
-}
-
-int
-nv20_fb_init(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	int i;
-
-	if (dev_priv->chipset >= 0x25)
-		drm_mm_init(&pfb->tag_heap, 0, 64 * 1024);
+	if (device->chipset >= 0x25)
+		ret = nouveau_mm_init(&priv->base.tags, 0, 64 * 1024, 1);
 	else
-		drm_mm_init(&pfb->tag_heap, 0, 32 * 1024);
+		ret = nouveau_mm_init(&priv->base.tags, 0, 32 * 1024, 1);
+	if (ret)
+		return ret;
 
-	/* Turn all the tiling regions off. */
-	pfb->num_tiles = NV10_PFB_TILE__SIZE;
-	for (i = 0; i < pfb->num_tiles; i++)
-		pfb->set_tile_region(dev, i);
-
-	return 0;
+	priv->base.memtype_valid = nv04_fb_memtype_valid;
+	priv->base.tile.regions = 8;
+	priv->base.tile.init = nv20_fb_tile_init;
+	priv->base.tile.fini = nv20_fb_tile_fini;
+	priv->base.tile.prog = nv20_fb_tile_prog;
+	return nouveau_fb_created(&priv->base);
 }
 
-void
-nv20_fb_takedown(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	int i;
-
-	for (i = 0; i < pfb->num_tiles; i++)
-		pfb->free_tile_region(dev, i);
-
-	drm_mm_takedown(&pfb->tag_heap);
-}
+struct nouveau_oclass
+nv20_fb_oclass = {
+	.handle = NV_SUBDEV(FB, 0x20),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv20_fb_ctor,
+		.dtor = _nouveau_fb_dtor,
+		.init = _nouveau_fb_init,
+		.fini = _nouveau_fb_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c
index 30639a0..cba67bc 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c
@@ -24,50 +24,47 @@
  *
  */
 
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <nouveau_drm.h>
+#include <subdev/fb.h>
+
+struct nv30_fb_priv {
+	struct nouveau_fb base;
+};
 
 void
-nv30_fb_init_tile_region(struct drm_device *dev, int i, uint32_t addr,
-			 uint32_t size, uint32_t pitch, uint32_t flags)
+nv30_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch,
+		  u32 flags, struct nouveau_fb_tile *tile)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
-
 	tile->addr = addr | 1;
 	tile->limit = max(1u, addr + size) - 1;
 	tile->pitch = pitch;
 }
 
 void
-nv30_fb_free_tile_region(struct drm_device *dev, int i)
+nv30_fb_tile_fini(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
-
-	tile->addr = tile->limit = tile->pitch = 0;
+	tile->addr  = 0;
+	tile->limit = 0;
+	tile->pitch = 0;
 }
 
 static int
-calc_bias(struct drm_device *dev, int k, int i, int j)
+calc_bias(struct nv30_fb_priv *priv, int k, int i, int j)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	int b = (dev_priv->chipset > 0x30 ?
-		 nv_rd32(dev, 0x122c + 0x10 * k + 0x4 * j) >> (4 * (i ^ 1)) :
+	struct nouveau_device *device = nv_device(priv);
+	int b = (device->chipset > 0x30 ?
+		 nv_rd32(priv, 0x122c + 0x10 * k + 0x4 * j) >> (4 * (i ^ 1)) :
 		 0) & 0xf;
 
 	return 2 * (b & 0x8 ? b - 0x10 : b);
 }
 
 static int
-calc_ref(struct drm_device *dev, int l, int k, int i)
+calc_ref(struct nv30_fb_priv *priv, int l, int k, int i)
 {
 	int j, x = 0;
 
 	for (j = 0; j < 4; j++) {
-		int m = (l >> (8 * i) & 0xff) + calc_bias(dev, k, i, j);
+		int m = (l >> (8 * i) & 0xff) + calc_bias(priv, k, i, j);
 
 		x |= (0x80 | clamp(m, 0, 0x1f)) << (8 * j);
 	}
@@ -75,42 +72,77 @@
 	return x;
 }
 
-int
-nv30_fb_init(struct drm_device *dev)
+static int
+nv30_fb_init(struct nouveau_object *object)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	int i, j;
+	struct nouveau_device *device = nv_device(object);
+	struct nv30_fb_priv *priv = (void *)object;
+	int ret, i, j;
 
-	pfb->num_tiles = NV10_PFB_TILE__SIZE;
-
-	/* Turn all the tiling regions off. */
-	for (i = 0; i < pfb->num_tiles; i++)
-		pfb->set_tile_region(dev, i);
+	ret = nouveau_fb_init(&priv->base);
+	if (ret)
+		return ret;
 
 	/* Init the memory timing regs at 0x10037c/0x1003ac */
-	if (dev_priv->chipset == 0x30 ||
-	    dev_priv->chipset == 0x31 ||
-	    dev_priv->chipset == 0x35) {
+	if (device->chipset == 0x30 ||
+	    device->chipset == 0x31 ||
+	    device->chipset == 0x35) {
 		/* Related to ROP count */
-		int n = (dev_priv->chipset == 0x31 ? 2 : 4);
-		int l = nv_rd32(dev, 0x1003d0);
+		int n = (device->chipset == 0x31 ? 2 : 4);
+		int l = nv_rd32(priv, 0x1003d0);
 
 		for (i = 0; i < n; i++) {
 			for (j = 0; j < 3; j++)
-				nv_wr32(dev, 0x10037c + 0xc * i + 0x4 * j,
-					calc_ref(dev, l, 0, j));
+				nv_wr32(priv, 0x10037c + 0xc * i + 0x4 * j,
+					calc_ref(priv, l, 0, j));
 
 			for (j = 0; j < 2; j++)
-				nv_wr32(dev, 0x1003ac + 0x8 * i + 0x4 * j,
-					calc_ref(dev, l, 1, j));
+				nv_wr32(priv, 0x1003ac + 0x8 * i + 0x4 * j,
+					calc_ref(priv, l, 1, j));
 		}
 	}
 
 	return 0;
 }
 
-void
-nv30_fb_takedown(struct drm_device *dev)
+static int
+nv30_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	     struct nouveau_oclass *oclass, void *data, u32 size,
+	     struct nouveau_object **pobject)
 {
+	struct nv30_fb_priv *priv;
+	u32 pbus1218;
+	int ret;
+
+	ret = nouveau_fb_create(parent, engine, oclass, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+
+	pbus1218 = nv_rd32(priv, 0x001218);
+	switch (pbus1218 & 0x00000300) {
+	case 0x00000000: priv->base.ram.type = NV_MEM_TYPE_SDRAM; break;
+	case 0x00000100: priv->base.ram.type = NV_MEM_TYPE_DDR1; break;
+	case 0x00000200: priv->base.ram.type = NV_MEM_TYPE_GDDR3; break;
+	case 0x00000300: priv->base.ram.type = NV_MEM_TYPE_GDDR2; break;
+	}
+	priv->base.ram.size = nv_rd32(priv, 0x10020c) & 0xff000000;
+
+	priv->base.memtype_valid = nv04_fb_memtype_valid;
+	priv->base.tile.regions = 8;
+	priv->base.tile.init = nv30_fb_tile_init;
+	priv->base.tile.fini = nv30_fb_tile_fini;
+	priv->base.tile.prog = nv10_fb_tile_prog;
+	return nouveau_fb_created(&priv->base);
 }
+
+struct nouveau_oclass
+nv30_fb_oclass = {
+	.handle = NV_SUBDEV(FB, 0x30),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv30_fb_ctor,
+		.dtor = _nouveau_fb_dtor,
+		.init = nv30_fb_init,
+		.fini = _nouveau_fb_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c
index e08c2e1..84aa71c 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c
@@ -1,55 +1,83 @@
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <nouveau_drm.h>
+/*
+ * Copyright (C) 2010 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL 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.
+ *
+ */
 
-void
-nv40_fb_set_tile_region(struct drm_device *dev, int i)
+#include <subdev/fb.h>
+
+struct nv40_fb_priv {
+	struct nouveau_fb base;
+};
+
+static inline int
+nv44_graph_class(struct nouveau_device *device)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
+	if ((device->chipset & 0xf0) == 0x60)
+		return 1;
 
-	switch (dev_priv->chipset) {
-	case 0x40:
-		nv_wr32(dev, NV10_PFB_TLIMIT(i), tile->limit);
-		nv_wr32(dev, NV10_PFB_TSIZE(i), tile->pitch);
-		nv_wr32(dev, NV10_PFB_TILE(i), tile->addr);
-		break;
-
-	default:
-		nv_wr32(dev, NV40_PFB_TLIMIT(i), tile->limit);
-		nv_wr32(dev, NV40_PFB_TSIZE(i), tile->pitch);
-		nv_wr32(dev, NV40_PFB_TILE(i), tile->addr);
-		break;
-	}
+	return !(0x0baf & (1 << (device->chipset & 0x0f)));
 }
 
 static void
-nv40_fb_init_gart(struct drm_device *dev)
+nv40_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_gpuobj *gart = dev_priv->gart_info.sg_ctxdma;
+	nv_wr32(pfb, 0x100604 + (i * 0x10), tile->limit);
+	nv_wr32(pfb, 0x100608 + (i * 0x10), tile->pitch);
+	nv_wr32(pfb, 0x100600 + (i * 0x10), tile->addr);
+}
 
-	if (dev_priv->gart_info.type != NOUVEAU_GART_HW) {
-		nv_wr32(dev, 0x100800, 0x00000001);
+static void
+nv40_fb_init_gart(struct nv40_fb_priv *priv)
+{
+#if 0
+	struct nouveau_gpuobj *gart = ndev->gart_info.sg_ctxdma;
+
+	if (ndev->gart_info.type != NOUVEAU_GART_HW) {
+#endif
+		nv_wr32(priv, 0x100800, 0x00000001);
+#if 0
 		return;
 	}
 
-	nv_wr32(dev, 0x100800, gart->pinst | 0x00000002);
-	nv_mask(dev, 0x10008c, 0x00000100, 0x00000100);
-	nv_wr32(dev, 0x100820, 0x00000000);
+	nv_wr32(ndev, 0x100800, gart->pinst | 0x00000002);
+	nv_mask(ndev, 0x10008c, 0x00000100, 0x00000100);
+	nv_wr32(ndev, 0x100820, 0x00000000);
+#endif
 }
 
 static void
-nv44_fb_init_gart(struct drm_device *dev)
+nv44_fb_init_gart(struct nv40_fb_priv *priv)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_gpuobj *gart = dev_priv->gart_info.sg_ctxdma;
+#if 0
+	struct nouveau_gpuobj *gart = ndev->gart_info.sg_ctxdma;
 	u32 vinst;
 
-	if (dev_priv->gart_info.type != NOUVEAU_GART_HW) {
-		nv_wr32(dev, 0x100850, 0x80000000);
-		nv_wr32(dev, 0x100800, 0x00000001);
+	if (ndev->gart_info.type != NOUVEAU_GART_HW) {
+#endif
+		nv_wr32(priv, 0x100850, 0x80000000);
+		nv_wr32(priv, 0x100800, 0x00000001);
+#if 0
 		return;
 	}
 
@@ -57,24 +85,60 @@
 	 * must be allocated on 512KiB alignment, and not exceed
 	 * a total size of 512KiB for this to work correctly
 	 */
-	vinst  = nv_rd32(dev, 0x10020c);
+	vinst  = nv_rd32(ndev, 0x10020c);
 	vinst -= ((gart->pinst >> 19) + 1) << 19;
 
-	nv_wr32(dev, 0x100850, 0x80000000);
-	nv_wr32(dev, 0x100818, dev_priv->gart_info.dummy.addr);
+	nv_wr32(ndev, 0x100850, 0x80000000);
+	nv_wr32(ndev, 0x100818, ndev->gart_info.dummy.addr);
 
-	nv_wr32(dev, 0x100804, dev_priv->gart_info.aper_size);
-	nv_wr32(dev, 0x100850, 0x00008000);
-	nv_mask(dev, 0x10008c, 0x00000200, 0x00000200);
-	nv_wr32(dev, 0x100820, 0x00000000);
-	nv_wr32(dev, 0x10082c, 0x00000001);
-	nv_wr32(dev, 0x100800, vinst | 0x00000010);
+	nv_wr32(ndev, 0x100804, ndev->gart_info.aper_size);
+	nv_wr32(ndev, 0x100850, 0x00008000);
+	nv_mask(ndev, 0x10008c, 0x00000200, 0x00000200);
+	nv_wr32(ndev, 0x100820, 0x00000000);
+	nv_wr32(ndev, 0x10082c, 0x00000001);
+	nv_wr32(ndev, 0x100800, vinst | 0x00000010);
+#endif
 }
 
-int
-nv40_fb_vram_init(struct drm_device *dev)
+static int
+nv40_fb_init(struct nouveau_object *object)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv40_fb_priv *priv = (void *)object;
+	int ret;
+
+	ret = nouveau_fb_init(&priv->base);
+	if (ret)
+		return ret;
+
+	switch (nv_device(priv)->chipset) {
+	case 0x40:
+	case 0x45:
+		nv_mask(priv, 0x10033c, 0x00008000, 0x00000000);
+		break;
+	default:
+		if (nv44_graph_class(nv_device(priv)))
+			nv44_fb_init_gart(priv);
+		else
+			nv40_fb_init_gart(priv);
+		break;
+	}
+
+	return 0;
+}
+
+static int
+nv40_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	     struct nouveau_oclass *oclass, void *data, u32 size,
+	     struct nouveau_object **pobject)
+{
+	struct nouveau_device *device = nv_device(parent);
+	struct nv40_fb_priv *priv;
+	int ret;
+
+	ret = nouveau_fb_create(parent, engine, oclass, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
 
 	/* 0x001218 is actually present on a few other NV4X I looked at,
 	 * and even contains sane values matching 0x100474.  From looking
@@ -82,82 +146,73 @@
 	 * So, I chose to use the same regs I've seen NVIDIA reading around
 	 * the memory detection, hopefully that'll get us the right numbers
 	 */
-	if (dev_priv->chipset == 0x40) {
-		u32 pbus1218 = nv_rd32(dev, 0x001218);
+	if (device->chipset == 0x40) {
+		u32 pbus1218 = nv_rd32(priv, 0x001218);
 		switch (pbus1218 & 0x00000300) {
-		case 0x00000000: dev_priv->vram_type = NV_MEM_TYPE_SDRAM; break;
-		case 0x00000100: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break;
-		case 0x00000200: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break;
-		case 0x00000300: dev_priv->vram_type = NV_MEM_TYPE_DDR2; break;
+		case 0x00000000: priv->base.ram.type = NV_MEM_TYPE_SDRAM; break;
+		case 0x00000100: priv->base.ram.type = NV_MEM_TYPE_DDR1; break;
+		case 0x00000200: priv->base.ram.type = NV_MEM_TYPE_GDDR3; break;
+		case 0x00000300: priv->base.ram.type = NV_MEM_TYPE_DDR2; break;
 		}
 	} else
-	if (dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b) {
-		u32 pfb914 = nv_rd32(dev, 0x100914);
+	if (device->chipset == 0x49 || device->chipset == 0x4b) {
+		u32 pfb914 = nv_rd32(priv, 0x100914);
 		switch (pfb914 & 0x00000003) {
-		case 0x00000000: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break;
-		case 0x00000001: dev_priv->vram_type = NV_MEM_TYPE_DDR2; break;
-		case 0x00000002: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break;
+		case 0x00000000: priv->base.ram.type = NV_MEM_TYPE_DDR1; break;
+		case 0x00000001: priv->base.ram.type = NV_MEM_TYPE_DDR2; break;
+		case 0x00000002: priv->base.ram.type = NV_MEM_TYPE_GDDR3; break;
 		case 0x00000003: break;
 		}
 	} else
-	if (dev_priv->chipset != 0x4e) {
-		u32 pfb474 = nv_rd32(dev, 0x100474);
+	if (device->chipset != 0x4e) {
+		u32 pfb474 = nv_rd32(priv, 0x100474);
 		if (pfb474 & 0x00000004)
-			dev_priv->vram_type = NV_MEM_TYPE_GDDR3;
+			priv->base.ram.type = NV_MEM_TYPE_GDDR3;
 		if (pfb474 & 0x00000002)
-			dev_priv->vram_type = NV_MEM_TYPE_DDR2;
+			priv->base.ram.type = NV_MEM_TYPE_DDR2;
 		if (pfb474 & 0x00000001)
-			dev_priv->vram_type = NV_MEM_TYPE_DDR1;
+			priv->base.ram.type = NV_MEM_TYPE_DDR1;
 	} else {
-		dev_priv->vram_type = NV_MEM_TYPE_STOLEN;
+		priv->base.ram.type = NV_MEM_TYPE_STOLEN;
 	}
 
-	dev_priv->vram_size = nv_rd32(dev, 0x10020c) & 0xff000000;
-	return 0;
-}
+	priv->base.ram.size = nv_rd32(priv, 0x10020c) & 0xff000000;
 
-int
-nv40_fb_init(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	uint32_t tmp;
-	int i;
-
-	if (dev_priv->chipset != 0x40 && dev_priv->chipset != 0x45) {
-		if (nv44_graph_class(dev))
-			nv44_fb_init_gart(dev);
-		else
-			nv40_fb_init_gart(dev);
-	}
-
-	switch (dev_priv->chipset) {
+	priv->base.memtype_valid = nv04_fb_memtype_valid;
+	switch (device->chipset) {
 	case 0x40:
 	case 0x45:
-		tmp = nv_rd32(dev, NV10_PFB_CLOSE_PAGE2);
-		nv_wr32(dev, NV10_PFB_CLOSE_PAGE2, tmp & ~(1 << 15));
-		pfb->num_tiles = NV10_PFB_TILE__SIZE;
+		priv->base.tile.regions = 8;
 		break;
-	case 0x46: /* G72 */
-	case 0x47: /* G70 */
-	case 0x49: /* G71 */
-	case 0x4b: /* G73 */
-	case 0x4c: /* C51 (G7X version) */
-		pfb->num_tiles = NV40_PFB_TILE__SIZE_1;
+	case 0x46:
+	case 0x47:
+	case 0x49:
+	case 0x4b:
+	case 0x4c:
+		priv->base.tile.regions = 15;
 		break;
 	default:
-		pfb->num_tiles = NV40_PFB_TILE__SIZE_0;
+		priv->base.tile.regions = 12;
 		break;
 	}
+	priv->base.tile.init = nv30_fb_tile_init;
+	priv->base.tile.fini = nv30_fb_tile_fini;
+	if (device->chipset == 0x40)
+		priv->base.tile.prog = nv10_fb_tile_prog;
+	else
+		priv->base.tile.prog = nv40_fb_tile_prog;
 
-	/* Turn all the tiling regions off. */
-	for (i = 0; i < pfb->num_tiles; i++)
-		pfb->set_tile_region(dev, i);
-
-	return 0;
+	return nouveau_fb_created(&priv->base);
 }
 
-void
-nv40_fb_takedown(struct drm_device *dev)
-{
-}
+
+struct nouveau_oclass
+nv40_fb_oclass = {
+	.handle = NV_SUBDEV(FB, 0x40),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv40_fb_ctor,
+		.dtor = _nouveau_fb_dtor,
+		.init = nv40_fb_init,
+		.fini = _nouveau_fb_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
index a145b1c..eaf18fb 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c
@@ -1,119 +1,335 @@
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <nouveau_drm.h>
-#include <engine/fifo.h>
+/*
+ * 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/enum.h>
+
+#include <subdev/fb.h>
+#include <subdev/bios.h>
 
 struct nv50_fb_priv {
+	struct nouveau_fb base;
 	struct page *r100c08_page;
 	dma_addr_t r100c08;
 };
 
-static void
-nv50_fb_destroy(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	struct nv50_fb_priv *priv = pfb->priv;
+static int types[0x80] = {
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0,
+	1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0,
+	0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+	1, 0, 2, 0, 1, 0, 2, 0, 1, 1, 2, 2, 1, 1, 0, 0
+};
 
-	if (drm_mm_initialized(&pfb->tag_heap))
-		drm_mm_takedown(&pfb->tag_heap);
+static bool
+nv50_fb_memtype_valid(struct nouveau_fb *pfb, u32 memtype)
+{
+	return types[(memtype & 0xff00) >> 8] != 0;
+}
+
+static int
+nv50_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
+		 u32 memtype, struct nouveau_mem **pmem)
+{
+	struct nv50_fb_priv *priv = (void *)pfb;
+	struct nouveau_mm *heap = &priv->base.vram;
+	struct nouveau_mm *tags = &priv->base.tags;
+	struct nouveau_mm_node *r;
+	struct nouveau_mem *mem;
+	int comp = (memtype & 0x300) >> 8;
+	int type = (memtype & 0x07f);
+	int back = (memtype & 0x800);
+	int min, max, ret;
+
+	max = (size >> 12);
+	min = ncmin ? (ncmin >> 12) : max;
+	align >>= 12;
+
+	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+	if (!mem)
+		return -ENOMEM;
+
+	mutex_lock(&pfb->base.mutex);
+	if (comp) {
+		if (align == 16) {
+			int n = (max >> 4) * comp;
+
+			ret = nouveau_mm_head(tags, 1, n, n, 1, &mem->tag);
+			if (ret)
+				mem->tag = NULL;
+		}
+
+		if (unlikely(!mem->tag))
+			comp = 0;
+	}
+
+	INIT_LIST_HEAD(&mem->regions);
+	mem->memtype = (comp << 7) | type;
+	mem->size = max;
+
+	type = types[type];
+	do {
+		if (back)
+			ret = nouveau_mm_tail(heap, type, max, min, align, &r);
+		else
+			ret = nouveau_mm_head(heap, type, max, min, align, &r);
+		if (ret) {
+			mutex_unlock(&pfb->base.mutex);
+			pfb->ram.put(pfb, &mem);
+			return ret;
+		}
+
+		list_add_tail(&r->rl_entry, &mem->regions);
+		max -= r->length;
+	} while (max);
+	mutex_unlock(&pfb->base.mutex);
+
+	r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);
+	mem->offset = (u64)r->offset << 12;
+	*pmem = mem;
+	return 0;
+}
+
+void
+nv50_fb_vram_del(struct nouveau_fb *pfb, struct nouveau_mem **pmem)
+{
+	struct nv50_fb_priv *priv = (void *)pfb;
+	struct nouveau_mm_node *this;
+	struct nouveau_mem *mem;
+
+	mem = *pmem;
+	*pmem = NULL;
+	if (unlikely(mem == NULL))
+		return;
+
+	mutex_lock(&pfb->base.mutex);
+	while (!list_empty(&mem->regions)) {
+		this = list_first_entry(&mem->regions, typeof(*this), rl_entry);
+
+		list_del(&this->rl_entry);
+		nouveau_mm_free(&priv->base.vram, &this);
+	}
+
+	nouveau_mm_free(&priv->base.tags, &mem->tag);
+	mutex_unlock(&pfb->base.mutex);
+
+	kfree(mem);
+}
+
+static u32
+nv50_vram_rblock(struct nv50_fb_priv *priv)
+{
+	int i, parts, colbits, rowbitsa, rowbitsb, banks;
+	u64 rowsize, predicted;
+	u32 r0, r4, rt, ru, rblock_size;
+
+	r0 = nv_rd32(priv, 0x100200);
+	r4 = nv_rd32(priv, 0x100204);
+	rt = nv_rd32(priv, 0x100250);
+	ru = nv_rd32(priv, 0x001540);
+	nv_debug(priv, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru);
+
+	for (i = 0, parts = 0; i < 8; i++) {
+		if (ru & (0x00010000 << i))
+			parts++;
+	}
+
+	colbits  =  (r4 & 0x0000f000) >> 12;
+	rowbitsa = ((r4 & 0x000f0000) >> 16) + 8;
+	rowbitsb = ((r4 & 0x00f00000) >> 20) + 8;
+	banks    = 1 << (((r4 & 0x03000000) >> 24) + 2);
+
+	rowsize = parts * banks * (1 << colbits) * 8;
+	predicted = rowsize << rowbitsa;
+	if (r0 & 0x00000004)
+		predicted += rowsize << rowbitsb;
+
+	if (predicted != priv->base.ram.size) {
+		nv_warn(priv, "memory controller reports %d MiB VRAM\n",
+			(u32)(priv->base.ram.size >> 20));
+	}
+
+	rblock_size = rowsize;
+	if (rt & 1)
+		rblock_size *= 3;
+
+	nv_debug(priv, "rblock %d bytes\n", rblock_size);
+	return rblock_size;
+}
+
+static int
+nv50_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	     struct nouveau_oclass *oclass, void *data, u32 size,
+	     struct nouveau_object **pobject)
+{
+	struct nouveau_device *device = nv_device(parent);
+	struct nouveau_bios *bios = nouveau_bios(device);
+	const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
+	const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+	struct nv50_fb_priv *priv;
+	u32 tags;
+	int ret;
+
+	ret = nouveau_fb_create(parent, engine, oclass, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+
+	switch (nv_rd32(priv, 0x100714) & 0x00000007) {
+	case 0: priv->base.ram.type = NV_MEM_TYPE_DDR1; break;
+	case 1:
+		if (nouveau_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3)
+			priv->base.ram.type = NV_MEM_TYPE_DDR3;
+		else
+			priv->base.ram.type = NV_MEM_TYPE_DDR2;
+		break;
+	case 2: priv->base.ram.type = NV_MEM_TYPE_GDDR3; break;
+	case 3: priv->base.ram.type = NV_MEM_TYPE_GDDR4; break;
+	case 4: priv->base.ram.type = NV_MEM_TYPE_GDDR5; break;
+	default:
+		break;
+	}
+
+	priv->base.ram.size = nv_rd32(priv, 0x10020c);
+	priv->base.ram.size = (priv->base.ram.size & 0xffffff00) |
+			     ((priv->base.ram.size & 0x000000ff) << 32);
+
+	tags = nv_rd32(priv, 0x100320);
+	if (tags) {
+		ret = nouveau_mm_init(&priv->base.tags, 0, tags, 1);
+		if (ret)
+			return ret;
+
+		nv_debug(priv, "%d compression tags\n", tags);
+	}
+
+	size = (priv->base.ram.size >> 12) - rsvd_head - rsvd_tail;
+	switch (device->chipset) {
+	case 0xaa:
+	case 0xac:
+	case 0xaf: /* IGPs, no reordering, no real VRAM */
+		ret = nouveau_mm_init(&priv->base.vram, rsvd_head, size, 1);
+		if (ret)
+			return ret;
+
+		priv->base.ram.stolen = (u64)nv_rd32(priv, 0x100e10) << 12;
+		break;
+	default:
+		ret = nouveau_mm_init(&priv->base.vram, rsvd_head, size,
+				      nv50_vram_rblock(priv) >> 12);
+		if (ret)
+			return ret;
+
+		priv->base.ram.ranks = (nv_rd32(priv, 0x100200) & 0x4) ? 2 : 1;
+		break;
+	}
+
+	priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+	if (priv->r100c08_page) {
+		priv->r100c08 = pci_map_page(device->pdev, priv->r100c08_page,
+					     0, PAGE_SIZE,
+					     PCI_DMA_BIDIRECTIONAL);
+		if (pci_dma_mapping_error(device->pdev, priv->r100c08))
+			nv_warn(priv, "failed 0x100c08 page map\n");
+	} else {
+		nv_warn(priv, "failed 0x100c08 page alloc\n");
+	}
+
+	priv->base.memtype_valid = nv50_fb_memtype_valid;
+	priv->base.ram.get = nv50_fb_vram_new;
+	priv->base.ram.put = nv50_fb_vram_del;
+	return nouveau_fb_created(&priv->base);
+}
+
+static void
+nv50_fb_dtor(struct nouveau_object *object)
+{
+	struct nouveau_device *device = nv_device(object);
+	struct nv50_fb_priv *priv = (void *)object;
 
 	if (priv->r100c08_page) {
-		pci_unmap_page(dev->pdev, priv->r100c08, PAGE_SIZE,
+		pci_unmap_page(device->pdev, priv->r100c08, PAGE_SIZE,
 			       PCI_DMA_BIDIRECTIONAL);
 		__free_page(priv->r100c08_page);
 	}
 
-	kfree(priv);
-	pfb->priv = NULL;
+	nouveau_mm_fini(&priv->base.vram);
+	nouveau_fb_destroy(&priv->base);
 }
 
 static int
-nv50_fb_create(struct drm_device *dev)
+nv50_fb_init(struct nouveau_object *object)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	struct nv50_fb_priv *priv;
-	u32 tagmem;
+	struct nouveau_device *device = nv_device(object);
+	struct nv50_fb_priv *priv = (void *)object;
 	int ret;
 
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-	pfb->priv = priv;
-
-	priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
-	if (!priv->r100c08_page) {
-		nv50_fb_destroy(dev);
-		return -ENOMEM;
-	}
-
-	priv->r100c08 = pci_map_page(dev->pdev, priv->r100c08_page, 0,
-				     PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
-	if (pci_dma_mapping_error(dev->pdev, priv->r100c08)) {
-		nv50_fb_destroy(dev);
-		return -EFAULT;
-	}
-
-	tagmem = nv_rd32(dev, 0x100320);
-	NV_DEBUG(dev, "%d tags available\n", tagmem);
-	ret = drm_mm_init(&pfb->tag_heap, 0, tagmem);
-	if (ret) {
-		nv50_fb_destroy(dev);
+	ret = nouveau_fb_init(&priv->base);
+	if (ret)
 		return ret;
-	}
-
-	return 0;
-}
-
-int
-nv50_fb_init(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nv50_fb_priv *priv;
-	int ret;
-
-	if (!dev_priv->engine.fb.priv) {
-		ret = nv50_fb_create(dev);
-		if (ret)
-			return ret;
-	}
-	priv = dev_priv->engine.fb.priv;
 
 	/* Not a clue what this is exactly.  Without pointing it at a
 	 * scratch page, VRAM->GART blits with M2MF (as in DDX DFS)
 	 * cause IOMMU "read from address 0" errors (rh#561267)
 	 */
-	nv_wr32(dev, 0x100c08, priv->r100c08 >> 8);
+	nv_wr32(priv, 0x100c08, priv->r100c08 >> 8);
 
 	/* This is needed to get meaningful information from 100c90
 	 * on traps. No idea what these values mean exactly. */
-	switch (dev_priv->chipset) {
+	switch (device->chipset) {
 	case 0x50:
-		nv_wr32(dev, 0x100c90, 0x000707ff);
+		nv_wr32(priv, 0x100c90, 0x000707ff);
 		break;
 	case 0xa3:
 	case 0xa5:
 	case 0xa8:
-		nv_wr32(dev, 0x100c90, 0x000d0fff);
+		nv_wr32(priv, 0x100c90, 0x000d0fff);
 		break;
 	case 0xaf:
-		nv_wr32(dev, 0x100c90, 0x089d1fff);
+		nv_wr32(priv, 0x100c90, 0x089d1fff);
 		break;
 	default:
-		nv_wr32(dev, 0x100c90, 0x001d07ff);
+		nv_wr32(priv, 0x100c90, 0x001d07ff);
 		break;
 	}
 
 	return 0;
 }
 
-void
-nv50_fb_takedown(struct drm_device *dev)
-{
-	nv50_fb_destroy(dev);
-}
+struct nouveau_oclass
+nv50_fb_oclass = {
+	.handle = NV_SUBDEV(FB, 0x50),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nv50_fb_ctor,
+		.dtor = nv50_fb_dtor,
+		.init = nv50_fb_init,
+		.fini = _nouveau_fb_fini,
+	},
+};
 
 static struct nouveau_enum vm_dispatch_subclients[] = {
 	{ 0x00000000, "GRCTX", NULL },
@@ -211,47 +427,32 @@
 };
 
 void
-nv50_fb_vm_trap(struct drm_device *dev, int display)
+nv50_fb_trap(struct nouveau_fb *pfb, int display)
 {
-	struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO);
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_device *device = nv_device(pfb);
+	struct nv50_fb_priv *priv = (void *)pfb;
 	const struct nouveau_enum *en, *cl;
-	unsigned long flags;
-	u32 trap[6], idx, chinst;
+	u32 trap[6], idx, chan;
 	u8 st0, st1, st2, st3;
-	int i, ch;
+	int i;
 
-	idx = nv_rd32(dev, 0x100c90);
+	idx = nv_rd32(priv, 0x100c90);
 	if (!(idx & 0x80000000))
 		return;
 	idx &= 0x00ffffff;
 
 	for (i = 0; i < 6; i++) {
-		nv_wr32(dev, 0x100c90, idx | i << 24);
-		trap[i] = nv_rd32(dev, 0x100c94);
+		nv_wr32(priv, 0x100c90, idx | i << 24);
+		trap[i] = nv_rd32(priv, 0x100c94);
 	}
-	nv_wr32(dev, 0x100c90, idx | 0x80000000);
+	nv_wr32(priv, 0x100c90, idx | 0x80000000);
 
 	if (!display)
 		return;
 
-	/* lookup channel id */
-	chinst = (trap[2] << 16) | trap[1];
-	spin_lock_irqsave(&dev_priv->channels.lock, flags);
-	for (ch = 0; ch < pfifo->channels; ch++) {
-		struct nouveau_channel *chan = dev_priv->channels.ptr[ch];
-
-		if (!chan || !chan->ramin)
-			continue;
-
-		if (chinst == chan->ramin->vinst >> 12)
-			break;
-	}
-	spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
-
 	/* decode status bits into something more useful */
-	if (dev_priv->chipset  < 0xa3 ||
-	    dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) {
+	if (device->chipset  < 0xa3 ||
+	    device->chipset == 0xaa || device->chipset == 0xac) {
 		st0 = (trap[0] & 0x0000000f) >> 0;
 		st1 = (trap[0] & 0x000000f0) >> 4;
 		st2 = (trap[0] & 0x00000f00) >> 8;
@@ -262,10 +463,11 @@
 		st2 = (trap[0] & 0x00ff0000) >> 16;
 		st3 = (trap[0] & 0xff000000) >> 24;
 	}
+	chan = (trap[2] << 16) | trap[1];
 
-	NV_INFO(dev, "VM: trapped %s at 0x%02x%04x%04x on ch %d [0x%08x] ",
-		(trap[5] & 0x00000100) ? "read" : "write",
-		trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, ch, chinst);
+	nv_error(priv, "trapped %s at 0x%02x%04x%04x on channel 0x%08x ",
+		 (trap[5] & 0x00000100) ? "read" : "write",
+		 trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, chan);
 
 	en = nouveau_enum_find(vm_engine, st0);
 	if (en)
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50_vram.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50_vram.c
deleted file mode 100644
index 84b99b6..0000000
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50_vram.c
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright 2010 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 "drmP.h"
-#include "nouveau_drv.h"
-#include <core/mm.h>
-
-static int types[0x80] = {
-	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0,
-	1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 0,
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0,
-	0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 2, 2, 2, 2,
-	1, 0, 2, 0, 1, 0, 2, 0, 1, 1, 2, 2, 1, 1, 0, 0
-};
-
-bool
-nv50_vram_flags_valid(struct drm_device *dev, u32 tile_flags)
-{
-	int type = (tile_flags & NOUVEAU_GEM_TILE_LAYOUT_MASK) >> 8;
-
-	if (likely(type < ARRAY_SIZE(types) && types[type]))
-		return true;
-	return false;
-}
-
-void
-nv50_vram_del(struct drm_device *dev, struct nouveau_mem **pmem)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_mm *mm = &dev_priv->engine.vram.mm;
-	struct nouveau_mm_node *this;
-	struct nouveau_mem *mem;
-
-	mem = *pmem;
-	*pmem = NULL;
-	if (unlikely(mem == NULL))
-		return;
-
-	mutex_lock(&mm->mutex);
-	while (!list_empty(&mem->regions)) {
-		this = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);
-
-		list_del(&this->rl_entry);
-		nouveau_mm_free(mm, &this);
-	}
-
-	if (mem->tag) {
-		drm_mm_put_block(mem->tag);
-		mem->tag = NULL;
-	}
-	mutex_unlock(&mm->mutex);
-
-	kfree(mem);
-}
-
-int
-nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin,
-	      u32 memtype, struct nouveau_mem **pmem)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_mm *mm = &dev_priv->engine.vram.mm;
-	struct nouveau_mm_node *r;
-	struct nouveau_mem *mem;
-	int comp = (memtype & 0x300) >> 8;
-	int type = (memtype & 0x07f);
-	int back = (memtype & 0x800);
-	int ret;
-
-	size >>= 12;
-	align >>= 12;
-	ncmin >>= 12;
-	if (!ncmin)
-		ncmin = size;
-
-	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
-	if (!mem)
-		return -ENOMEM;
-
-	mutex_lock(&mm->mutex);
-	if (comp) {
-		if (align == 16) {
-			struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-			int n = (size >> 4) * comp;
-
-			mem->tag = drm_mm_search_free(&pfb->tag_heap, n, 0, 0);
-			if (mem->tag)
-				mem->tag = drm_mm_get_block(mem->tag, n, 0);
-		}
-
-		if (unlikely(!mem->tag))
-			comp = 0;
-	}
-
-	INIT_LIST_HEAD(&mem->regions);
-	mem->dev = dev_priv->dev;
-	mem->memtype = (comp << 7) | type;
-	mem->size = size;
-
-	type = types[type];
-	do {
-		if (back)
-			ret = nouveau_mm_tail(mm, type, size, ncmin, align, &r);
-		else
-			ret = nouveau_mm_head(mm, type, size, ncmin, align, &r);
-
-		if (ret) {
-			mutex_unlock(&mm->mutex);
-			nv50_vram_del(dev, &mem);
-			return ret;
-		}
-
-		list_add_tail(&r->rl_entry, &mem->regions);
-		size -= r->length;
-	} while (size);
-	mutex_unlock(&mm->mutex);
-
-	r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);
-	mem->offset = (u64)r->offset << 12;
-	*pmem = mem;
-	return 0;
-}
-
-static u32
-nv50_vram_rblock(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	int i, parts, colbits, rowbitsa, rowbitsb, banks;
-	u64 rowsize, predicted;
-	u32 r0, r4, rt, ru, rblock_size;
-
-	r0 = nv_rd32(dev, 0x100200);
-	r4 = nv_rd32(dev, 0x100204);
-	rt = nv_rd32(dev, 0x100250);
-	ru = nv_rd32(dev, 0x001540);
-	NV_DEBUG(dev, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru);
-
-	for (i = 0, parts = 0; i < 8; i++) {
-		if (ru & (0x00010000 << i))
-			parts++;
-	}
-
-	colbits  =  (r4 & 0x0000f000) >> 12;
-	rowbitsa = ((r4 & 0x000f0000) >> 16) + 8;
-	rowbitsb = ((r4 & 0x00f00000) >> 20) + 8;
-	banks    = 1 << (((r4 & 0x03000000) >> 24) + 2);
-
-	rowsize = parts * banks * (1 << colbits) * 8;
-	predicted = rowsize << rowbitsa;
-	if (r0 & 0x00000004)
-		predicted += rowsize << rowbitsb;
-
-	if (predicted != dev_priv->vram_size) {
-		NV_WARN(dev, "memory controller reports %dMiB VRAM\n",
-			(u32)(dev_priv->vram_size >> 20));
-		NV_WARN(dev, "we calculated %dMiB VRAM\n",
-			(u32)(predicted >> 20));
-	}
-
-	rblock_size = rowsize;
-	if (rt & 1)
-		rblock_size *= 3;
-
-	NV_DEBUG(dev, "rblock %d bytes\n", rblock_size);
-	return rblock_size;
-}
-
-int
-nv50_vram_init(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
-	const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
-	const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
-	u32 pfb714 = nv_rd32(dev, 0x100714);
-	u32 rblock, length;
-
-	switch (pfb714 & 0x00000007) {
-	case 0: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break;
-	case 1:
-		if (nouveau_mem_vbios_type(dev) == NV_MEM_TYPE_DDR3)
-			dev_priv->vram_type = NV_MEM_TYPE_DDR3;
-		else
-			dev_priv->vram_type = NV_MEM_TYPE_DDR2;
-		break;
-	case 2: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break;
-	case 3: dev_priv->vram_type = NV_MEM_TYPE_GDDR4; break;
-	case 4: dev_priv->vram_type = NV_MEM_TYPE_GDDR5; break;
-	default:
-		break;
-	}
-
-	dev_priv->vram_rank_B = !!(nv_rd32(dev, 0x100200) & 0x4);
-	dev_priv->vram_size  = nv_rd32(dev, 0x10020c);
-	dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32;
-	dev_priv->vram_size &= 0xffffffff00ULL;
-
-	/* IGPs, no funky reordering happens here, they don't have VRAM */
-	if (dev_priv->chipset == 0xaa ||
-	    dev_priv->chipset == 0xac ||
-	    dev_priv->chipset == 0xaf) {
-		dev_priv->vram_sys_base = (u64)nv_rd32(dev, 0x100e10) << 12;
-		rblock = 4096 >> 12;
-	} else {
-		rblock = nv50_vram_rblock(dev) >> 12;
-	}
-
-	length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail;
-
-	return nouveau_mm_init(&vram->mm, rsvd_head, length, rblock);
-}
-
-void
-nv50_vram_fini(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
-
-	nouveau_mm_fini(&vram->mm);
-}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
index f054331..9f59f2b 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2011 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,114 +22,224 @@
  * Authors: Ben Skeggs
  */
 
-#include "drmP.h"
-#include "drm.h"
-#include "nouveau_drv.h"
-#include <nouveau_drm.h>
+#include <subdev/fb.h>
+#include <subdev/bios.h>
 
 struct nvc0_fb_priv {
+	struct nouveau_fb base;
 	struct page *r100c10_page;
 	dma_addr_t r100c10;
 };
 
-static inline void
-nvc0_mfb_subp_isr(struct drm_device *dev, int unit, int subp)
-{
-	u32 subp_base = 0x141000 + (unit * 0x2000) + (subp * 0x400);
-	u32 stat = nv_rd32(dev, subp_base + 0x020);
+/* 0 = unsupported
+ * 1 = non-compressed
+ * 3 = compressed
+ */
+static const u8 types[256] = {
+	1, 1, 3, 3, 3, 3, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+	0, 1, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3,
+	3, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 1, 1, 1, 1, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
+	3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3,
+	3, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 3,
+	3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 0, 3, 0, 3, 3, 0,
+	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0
+};
 
-	if (stat) {
-		NV_INFO(dev, "PMFB%d_SUBP%d: 0x%08x\n", unit, subp, stat);
-		nv_wr32(dev, subp_base + 0x020, stat);
-	}
+static bool
+nvc0_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags)
+{
+	u8 memtype = (tile_flags & 0x0000ff00) >> 8;
+	return likely((types[memtype] == 1));
+}
+
+static int
+nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin,
+		 u32 memtype, struct nouveau_mem **pmem)
+{
+	struct nouveau_mm *mm = &pfb->vram;
+	struct nouveau_mm_node *r;
+	struct nouveau_mem *mem;
+	int type = (memtype & 0x0ff);
+	int back = (memtype & 0x800);
+	int ret;
+
+	size  >>= 12;
+	align >>= 12;
+	ncmin >>= 12;
+	if (!ncmin)
+		ncmin = size;
+
+	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+	if (!mem)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&mem->regions);
+	mem->memtype = type;
+	mem->size = size;
+
+	mutex_lock(&mm->mutex);
+	do {
+		if (back)
+			ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r);
+		else
+			ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r);
+		if (ret) {
+			mutex_unlock(&mm->mutex);
+			pfb->ram.put(pfb, &mem);
+			return ret;
+		}
+
+		list_add_tail(&r->rl_entry, &mem->regions);
+		size -= r->length;
+	} while (size);
+	mutex_unlock(&mm->mutex);
+
+	r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);
+	mem->offset = (u64)r->offset << 12;
+	*pmem = mem;
+	return 0;
+}
+
+static int
+nvc0_fb_init(struct nouveau_object *object)
+{
+	struct nvc0_fb_priv *priv = (void *)object;
+	int ret;
+
+	ret = nouveau_fb_init(&priv->base);
+	if (ret)
+		return ret;
+
+	nv_wr32(priv, 0x100c10, priv->r100c10 >> 8);
+	return 0;
 }
 
 static void
-nvc0_mfb_isr(struct drm_device *dev)
+nvc0_fb_dtor(struct nouveau_object *object)
 {
-	u32 units = nv_rd32(dev, 0x00017c);
-	while (units) {
-		u32 subp, unit = ffs(units) - 1;
-		for (subp = 0; subp < 2; subp++)
-			nvc0_mfb_subp_isr(dev, unit, subp);
-		units &= ~(1 << unit);
-	}
-
-	/* we do something horribly wrong and upset PMFB a lot, so mask off
-	 * interrupts from it after the first one until it's fixed
-	 */
-	nv_mask(dev, 0x000640, 0x02000000, 0x00000000);
-}
-
-static void
-nvc0_fb_destroy(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	struct nvc0_fb_priv *priv = pfb->priv;
-
-	nouveau_irq_unregister(dev, 25);
+	struct nouveau_device *device = nv_device(object);
+	struct nvc0_fb_priv *priv = (void *)object;
 
 	if (priv->r100c10_page) {
-		pci_unmap_page(dev->pdev, priv->r100c10, PAGE_SIZE,
+		pci_unmap_page(device->pdev, priv->r100c10, PAGE_SIZE,
 			       PCI_DMA_BIDIRECTIONAL);
 		__free_page(priv->r100c10_page);
 	}
 
-	kfree(priv);
-	pfb->priv = NULL;
+	nouveau_fb_destroy(&priv->base);
 }
 
 static int
-nvc0_fb_create(struct drm_device *dev)
+nvc0_vram_detect(struct nvc0_fb_priv *priv)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	struct nvc0_fb_priv *priv;
+	struct nouveau_bios *bios = nouveau_bios(priv);
+	struct nouveau_fb *pfb = &priv->base;
+	const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
+	const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+	u32 parts = nv_rd32(priv, 0x022438);
+	u32 pmask = nv_rd32(priv, 0x022554);
+	u32 bsize = nv_rd32(priv, 0x10f20c);
+	u32 offset, length;
+	bool uniform = true;
+	int ret, part;
 
-	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-	pfb->priv = priv;
+	nv_debug(priv, "0x100800: 0x%08x\n", nv_rd32(priv, 0x100800));
+	nv_debug(priv, "parts 0x%08x mask 0x%08x\n", parts, pmask);
 
-	priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
-	if (!priv->r100c10_page) {
-		nvc0_fb_destroy(dev);
-		return -ENOMEM;
+	priv->base.ram.type = nouveau_fb_bios_memtype(bios);
+	priv->base.ram.ranks = (nv_rd32(priv, 0x10f200) & 0x00000004) ? 2 : 1;
+
+	/* read amount of vram attached to each memory controller */
+	for (part = 0; part < parts; part++) {
+		if (!(pmask & (1 << part))) {
+			u32 psize = nv_rd32(priv, 0x11020c + (part * 0x1000));
+			if (psize != bsize) {
+				if (psize < bsize)
+					bsize = psize;
+				uniform = false;
+			}
+
+			nv_debug(priv, "%d: mem_amount 0x%08x\n", part, psize);
+			priv->base.ram.size += (u64)psize << 20;
+		}
 	}
 
-	priv->r100c10 = pci_map_page(dev->pdev, priv->r100c10_page, 0,
-				     PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
-	if (pci_dma_mapping_error(dev->pdev, priv->r100c10)) {
-		nvc0_fb_destroy(dev);
-		return -EFAULT;
+	/* if all controllers have the same amount attached, there's no holes */
+	if (uniform) {
+		offset = rsvd_head;
+		length = (priv->base.ram.size >> 12) - rsvd_head - rsvd_tail;
+		return nouveau_mm_init(&pfb->vram, offset, length, 1);
 	}
 
-	nouveau_irq_register(dev, 25, nvc0_mfb_isr);
+	/* otherwise, address lowest common amount from 0GiB */
+	ret = nouveau_mm_init(&pfb->vram, rsvd_head, (bsize << 8) * parts, 1);
+	if (ret)
+		return ret;
+
+	/* and the rest starting from (8GiB + common_size) */
+	offset = (0x0200000000ULL >> 12) + (bsize << 8);
+	length = (priv->base.ram.size >> 12) - (bsize << 8) - rsvd_tail;
+
+	ret = nouveau_mm_init(&pfb->vram, offset, length, 0);
+	if (ret) {
+		nouveau_mm_fini(&pfb->vram);
+		return ret;
+	}
+
 	return 0;
 }
 
-int
-nvc0_fb_init(struct drm_device *dev)
+static int
+nvc0_fb_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 nouveau_device *device = nv_device(parent);
 	struct nvc0_fb_priv *priv;
 	int ret;
 
-	if (!dev_priv->engine.fb.priv) {
-		ret = nvc0_fb_create(dev);
-		if (ret)
-			return ret;
-	}
-	priv = dev_priv->engine.fb.priv;
+	ret = nouveau_fb_create(parent, engine, oclass, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
 
-	nv_wr32(dev, 0x100c10, priv->r100c10 >> 8);
-	nv_mask(dev, 0x17e820, 0x00100000, 0x00000000); /* NV_PLTCG_INTR_EN */
-	return 0;
+	priv->base.memtype_valid = nvc0_fb_memtype_valid;
+	priv->base.ram.get = nvc0_fb_vram_new;
+	priv->base.ram.put = nv50_fb_vram_del;
+
+	ret = nvc0_vram_detect(priv);
+	if (ret)
+		return ret;
+
+	priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+	if (!priv->r100c10_page)
+		return -ENOMEM;
+
+	priv->r100c10 = pci_map_page(device->pdev, priv->r100c10_page, 0,
+				     PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+	if (pci_dma_mapping_error(device->pdev, priv->r100c10))
+		return -EFAULT;
+
+	return nouveau_fb_created(&priv->base);
 }
 
-void
-nvc0_fb_takedown(struct drm_device *dev)
-{
-	nvc0_fb_destroy(dev);
-}
+
+struct nouveau_oclass
+nvc0_fb_oclass = {
+	.handle = NV_SUBDEV(FB, 0xc0),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nvc0_fb_ctor,
+		.dtor = nvc0_fb_dtor,
+		.init = nvc0_fb_init,
+		.fini = _nouveau_fb_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0_vram.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0_vram.c
deleted file mode 100644
index f363234..0000000
--- a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0_vram.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2010 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 "drmP.h"
-#include "nouveau_drv.h"
-#include <core/mm.h>
-
-/* 0 = unsupported
- * 1 = non-compressed
- * 3 = compressed
- */
-static const u8 types[256] = {
-	1, 1, 3, 3, 3, 3, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0,
-	0, 1, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3,
-	3, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 1, 1, 1, 1, 0,
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 3, 3, 3, 3, 1, 1, 1, 1, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
-	3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3,
-	3, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 3,
-	3, 0, 3, 3, 3, 3, 3, 0, 0, 3, 0, 3, 0, 3, 3, 0,
-	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0
-};
-
-bool
-nvc0_vram_flags_valid(struct drm_device *dev, u32 tile_flags)
-{
-	u8 memtype = (tile_flags & NOUVEAU_GEM_TILE_LAYOUT_MASK) >> 8;
-	return likely((types[memtype] == 1));
-}
-
-int
-nvc0_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin,
-	      u32 memtype, struct nouveau_mem **pmem)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_mm *mm = &dev_priv->engine.vram.mm;
-	struct nouveau_mm_node *r;
-	struct nouveau_mem *mem;
-	int type = (memtype & 0x0ff);
-	int back = (memtype & 0x800);
-	int ret;
-
-	size  >>= 12;
-	align >>= 12;
-	ncmin >>= 12;
-	if (!ncmin)
-		ncmin = size;
-
-	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
-	if (!mem)
-		return -ENOMEM;
-
-	INIT_LIST_HEAD(&mem->regions);
-	mem->dev = dev_priv->dev;
-	mem->memtype = type;
-	mem->size = size;
-
-	mutex_lock(&mm->mutex);
-	do {
-		if (back)
-			ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r);
-		else
-			ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r);
-		if (ret) {
-			mutex_unlock(&mm->mutex);
-			nv50_vram_del(dev, &mem);
-			return ret;
-		}
-
-		list_add_tail(&r->rl_entry, &mem->regions);
-		size -= r->length;
-	} while (size);
-	mutex_unlock(&mm->mutex);
-
-	r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry);
-	mem->offset = (u64)r->offset << 12;
-	*pmem = mem;
-	return 0;
-}
-
-int
-nvc0_vram_init(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
-	const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
-	const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
-	u32 parts = nv_rd32(dev, 0x022438);
-	u32 pmask = nv_rd32(dev, 0x022554);
-	u32 bsize = nv_rd32(dev, 0x10f20c);
-	u32 offset, length;
-	bool uniform = true;
-	int ret, part;
-
-	NV_DEBUG(dev, "0x100800: 0x%08x\n", nv_rd32(dev, 0x100800));
-	NV_DEBUG(dev, "parts 0x%08x mask 0x%08x\n", parts, pmask);
-
-	dev_priv->vram_type = nouveau_mem_vbios_type(dev);
-	dev_priv->vram_rank_B = !!(nv_rd32(dev, 0x10f200) & 0x00000004);
-
-	/* read amount of vram attached to each memory controller */
-	for (part = 0; part < parts; part++) {
-		if (!(pmask & (1 << part))) {
-			u32 psize = nv_rd32(dev, 0x11020c + (part * 0x1000));
-			if (psize != bsize) {
-				if (psize < bsize)
-					bsize = psize;
-				uniform = false;
-			}
-
-			NV_DEBUG(dev, "%d: mem_amount 0x%08x\n", part, psize);
-			dev_priv->vram_size += (u64)psize << 20;
-		}
-	}
-
-	/* if all controllers have the same amount attached, there's no holes */
-	if (uniform) {
-		offset = rsvd_head;
-		length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail;
-		return nouveau_mm_init(&vram->mm, offset, length, 1);
-	}
-
-	/* otherwise, address lowest common amount from 0GiB */
-	ret = nouveau_mm_init(&vram->mm, rsvd_head, (bsize << 8) * parts, 1);
-	if (ret)
-		return ret;
-
-	/* and the rest starting from (8GiB + common_size) */
-	offset = (0x0200000000ULL >> 12) + (bsize << 8);
-	length = (dev_priv->vram_size >> 12) - (bsize << 8) - rsvd_tail;
-
-	ret = nouveau_mm_init(&vram->mm, offset, length, 0);
-	if (ret) {
-		nouveau_mm_fini(&vram->mm);
-		return ret;
-	}
-
-	return 0;
-}
diff --git a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c
index 5965eb7..24d077a 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c
@@ -310,8 +310,6 @@
 		 u32 size, u32 align)
 {
 	struct drm_device *dev = gpuobj->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
 	struct nv50_gpuobj_node *node = NULL;
 	int ret;
 
@@ -323,7 +321,7 @@
 	size  = (size + 4095) & ~4095;
 	align = max(align, (u32)4096);
 
-	ret = vram->get(dev, size, align, 0, 0x800, &node->vram);
+	ret = nvfb_vram_get(dev, size, align, 0, 0x800, &node->vram);
 	if (ret) {
 		kfree(node);
 		return ret;
@@ -339,7 +337,7 @@
 		ret = nouveau_vm_get(chan->vm, size, 12, flags,
 				     &node->chan_vma);
 		if (ret) {
-			vram->put(dev, &node->vram);
+			nvfb_vram_put(dev, &node->vram);
 			kfree(node);
 			return ret;
 		}
@@ -357,8 +355,6 @@
 nv50_instmem_put(struct nouveau_gpuobj *gpuobj)
 {
 	struct drm_device *dev = gpuobj->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
 	struct nv50_gpuobj_node *node;
 
 	node = gpuobj->node;
@@ -368,7 +364,7 @@
 		nouveau_vm_unmap(&node->chan_vma);
 		nouveau_vm_put(&node->chan_vma);
 	}
-	vram->put(dev, &node->vram);
+	nvfb_vram_put(dev, &node->vram);
 	kfree(node);
 }
 
diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c
new file mode 100644
index 0000000..7c2840c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c
@@ -0,0 +1,91 @@
+/*
+ * 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 <subdev/ltcg.h>
+
+struct nvc0_ltcg_priv {
+	struct nouveau_ltcg base;
+};
+
+static void
+nvc0_ltcg_subp_isr(struct nvc0_ltcg_priv *priv, int unit, int subp)
+{
+	u32 subp_base = 0x141000 + (unit * 0x2000) + (subp * 0x400);
+	u32 stat = nv_rd32(priv, subp_base + 0x020);
+
+	if (stat) {
+		nv_info(priv, "LTC%d_LTS%d: 0x%08x\n", unit, subp, stat);
+		nv_wr32(priv, subp_base + 0x020, stat);
+	}
+}
+
+static void
+nvc0_ltcg_intr(struct nouveau_subdev *subdev)
+{
+	struct nvc0_ltcg_priv *priv = (void *)subdev;
+	u32 units;
+
+	units = nv_rd32(priv, 0x00017c);
+	while (units) {
+		u32 subp, unit = ffs(units) - 1;
+		for (subp = 0; subp < 2; subp++)
+			nvc0_ltcg_subp_isr(priv, unit, subp);
+		units &= ~(1 << unit);
+	}
+
+	/* we do something horribly wrong and upset PMFB a lot, so mask off
+	 * interrupts from it after the first one until it's fixed
+	 */
+	nv_mask(priv, 0x000640, 0x02000000, 0x00000000);
+}
+
+static int
+nvc0_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
+	       struct nouveau_oclass *oclass, void *data, u32 size,
+	       struct nouveau_object **pobject)
+{
+	struct nvc0_ltcg_priv *priv;
+	int ret;
+
+	ret = nouveau_ltcg_create(parent, engine, oclass, &priv);
+	*pobject = nv_object(priv);
+	if (ret)
+		return ret;
+
+	nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */
+
+	nv_subdev(priv)->intr = nvc0_ltcg_intr;
+	return 0;
+}
+
+struct nouveau_oclass
+nvc0_ltcg_oclass = {
+	.handle = NV_SUBDEV(LTCG, 0xc0),
+	.ofuncs = &(struct nouveau_ofuncs) {
+		.ctor = nvc0_ltcg_ctor,
+		.dtor = _nouveau_ltcg_dtor,
+		.init = _nouveau_ltcg_init,
+		.fini = _nouveau_ltcg_fini,
+	},
+};
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
index 3fa39bb..bd9e9946 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
@@ -38,6 +38,7 @@
 	{ 0x00008000, NVDEV_ENGINE_BSP },
 	{ 0x00100000, NVDEV_SUBDEV_TIMER },
 	{ 0x00200000, NVDEV_SUBDEV_GPIO },
+	{ 0x02000000, NVDEV_SUBDEV_LTCG },
 	{ 0x04000000, NVDEV_ENGINE_DISP },
 	{ 0x80000000, NVDEV_ENGINE_SW },
 	{},
diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c
index eeac72f..7e46826d 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c
@@ -80,8 +80,8 @@
 
 	/* IGPs don't have real VRAM, re-target to stolen system memory */
 	target = 0;
-	if (dev_priv->vram_sys_base) {
-		phys += dev_priv->vram_sys_base;
+	if (nvfb_vram_sys_base(dev_priv->dev)) {
+		phys += nvfb_vram_sys_base(dev_priv->dev);
 		target = 3;
 	}
 
@@ -103,7 +103,7 @@
 		phys += block << (vma->node->type - 3);
 		cnt  -= block;
 		if (comp) {
-			u32 tag = mem->tag->start + ((delta >> 16) * comp);
+			u32 tag = mem->tag->offset + ((delta >> 16) * comp);
 			offset_h |= (tag << 17);
 			delta    += block << (vma->node->type - 3);
 		}
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index d952754..f63785c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -156,7 +156,7 @@
 set_placement_range(struct nouveau_bo *nvbo, uint32_t type)
 {
 	struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
-	int vram_pages = dev_priv->vram_size >> PAGE_SHIFT;
+	int vram_pages = nvfb_vram_size(dev_priv->dev) >> PAGE_SHIFT;
 
 	if (dev_priv->card_type == NV_10 &&
 	    nvbo->tile_mode && (type & TTM_PL_FLAG_VRAM) &&
diff --git a/drivers/gpu/drm/nouveau/nouveau_compat.c b/drivers/gpu/drm/nouveau/nouveau_compat.c
index 7880ebb..3d65c17 100644
--- a/drivers/gpu/drm/nouveau/nouveau_compat.c
+++ b/drivers/gpu/drm/nouveau/nouveau_compat.c
@@ -10,6 +10,7 @@
 #include <subdev/clock.h>
 #include <subdev/mc.h>
 #include <subdev/timer.h>
+#include <subdev/fb.h>
 
 void *nouveau_newpriv(struct drm_device *);
 
@@ -332,3 +333,108 @@
 	struct nouveau_timer *ptimer = nouveau_timer(drm->device);
 	return ptimer->read(ptimer);
 }
+
+int
+nvfb_tile_nr(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	return pfb->tile.regions;
+}
+
+struct nouveau_fb_tile *
+nvfb_tile(struct drm_device *dev, int i)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	return &pfb->tile.region[i];
+}
+
+void
+nvfb_tile_init(struct drm_device *dev, int i, u32 a, u32 b, u32 c, u32 d)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	pfb->tile.init(pfb, i, a, b, c, d, &pfb->tile.region[i]);
+}
+
+void
+nvfb_tile_fini(struct drm_device *dev, int i)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	pfb->tile.fini(pfb, i, &pfb->tile.region[i]);
+}
+
+void
+nvfb_tile_prog(struct drm_device *dev, int i)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	pfb->tile.prog(pfb, i, &pfb->tile.region[i]);
+}
+
+bool
+nvfb_flags_valid(struct drm_device *dev, u32 flags)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	return pfb->memtype_valid(pfb, flags);
+}
+
+int
+nvfb_vram_get(struct drm_device *dev, u64 size, u32 align, u32 ncmin,
+	      u32 memtype, struct nouveau_mem **pmem)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	int ret = pfb->ram.get(pfb, size, align, ncmin, memtype, pmem);
+	if (ret)
+		return ret;
+	(*pmem)->dev = dev;
+	return 0;
+}
+
+void
+nvfb_vram_put(struct drm_device *dev, struct nouveau_mem **pmem)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	pfb->ram.put(pfb, pmem);
+}
+
+
+u64 nvfb_vram_sys_base(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	return pfb->ram.stolen;
+}
+
+u64 nvfb_vram_size(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	return pfb->ram.size;
+}
+
+int nvfb_vram_type(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	return pfb->ram.type;
+}
+
+int nvfb_vram_rank_B(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	struct nouveau_fb *pfb = nouveau_fb(drm->device);
+	return pfb->ram.ranks > 1;
+}
+
+void
+nv50_fb_vm_trap(struct drm_device *dev, int disp)
+{
+	struct nouveau_drm *drm = nouveau_newpriv(dev);
+	nv50_fb_trap(nouveau_fb(drm->device), disp);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_compat.h b/drivers/gpu/drm/nouveau/nouveau_compat.h
index c9622eb..d047a20 100644
--- a/drivers/gpu/drm/nouveau/nouveau_compat.h
+++ b/drivers/gpu/drm/nouveau/nouveau_compat.h
@@ -62,4 +62,24 @@
 
 u64 nv_timer_read(struct drm_device *);
 
+int  nvfb_tile_nr(struct drm_device *);
+void nvfb_tile_init(struct drm_device *, int, u32, u32, u32, u32);
+void nvfb_tile_fini(struct drm_device *, int);
+void nvfb_tile_prog(struct drm_device *, int);
+
+struct nouveau_fb_tile *nvfb_tile(struct drm_device *, int);
+
+struct nouveau_mem;
+int nvfb_vram_get(struct drm_device *dev, u64 size, u32 align, u32 ncmin,
+		  u32 memtype, struct nouveau_mem **pmem);
+void nvfb_vram_put(struct drm_device *dev, struct nouveau_mem **pmem);
+bool nvfb_flags_valid(struct drm_device *dev, u32);
+
+u64 nvfb_vram_sys_base(struct drm_device *);
+u64 nvfb_vram_size(struct drm_device *);
+int nvfb_vram_type(struct drm_device *);
+int nvfb_vram_rank_B(struct drm_device *);
+
+void nv50_fb_vm_trap(struct drm_device *, int);
+
 #endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
index 188c92b..6564b54 100644
--- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c
+++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
@@ -137,9 +137,8 @@
 {
 	struct drm_info_node *node = (struct drm_info_node *) m->private;
 	struct drm_minor *minor = node->minor;
-	struct drm_nouveau_private *dev_priv = minor->dev->dev_private;
 
-	seq_printf(m, "VRAM total: %dKiB\n", (int)(dev_priv->vram_size >> 10));
+	seq_printf(m, "VRAM total: %dKiB\n", (int)(nvfb_vram_size(minor->dev) >> 10));
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
index 7223a4f..3f660a9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
@@ -252,7 +252,6 @@
 
 	NV_INFO(dev, "Reinitialising engines...\n");
 	engine->instmem.resume(dev);
-	engine->fb.init(dev);
 	for (i = 0; i < NVOBJ_ENGINE_NR; i++) {
 		if (dev_priv->eng[i])
 			dev_priv->eng[i]->init(dev, i);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index b459599..c7bc6ec 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -45,6 +45,23 @@
 #include "ttm/ttm_memory.h"
 #include "ttm/ttm_module.h"
 
+#define XXX_THIS_IS_A_HACK
+#include <subdev/fb.h>
+
+enum blah {
+	NV_MEM_TYPE_UNKNOWN = 0,
+	NV_MEM_TYPE_STOLEN,
+	NV_MEM_TYPE_SGRAM,
+	NV_MEM_TYPE_SDRAM,
+	NV_MEM_TYPE_DDR1,
+	NV_MEM_TYPE_DDR2,
+	NV_MEM_TYPE_DDR3,
+	NV_MEM_TYPE_GDDR2,
+	NV_MEM_TYPE_GDDR3,
+	NV_MEM_TYPE_GDDR4,
+	NV_MEM_TYPE_GDDR5
+};
+
 struct nouveau_fpriv {
 	spinlock_t lock;
 	struct list_head channels;
@@ -76,29 +93,8 @@
 #define NOUVEAU_MAX_CHANNEL_NR 4096
 #define NOUVEAU_MAX_TILE_NR 15
 
-struct nouveau_mem {
-	struct drm_device *dev;
-
-	struct nouveau_vma bar_vma;
-	struct nouveau_vma vma[2];
-	u8  page_shift;
-
-	struct drm_mm_node *tag;
-	struct list_head regions;
-	dma_addr_t *pages;
-	u32 memtype;
-	u64 offset;
-	u64 size;
-	struct sg_table *sg;
-};
-
 struct nouveau_tile_reg {
 	bool used;
-	uint32_t addr;
-	uint32_t limit;
-	uint32_t pitch;
-	uint32_t zcomp;
-	struct drm_mm_node *tag_mem;
 	struct nouveau_fence *fence;
 };
 
@@ -324,21 +320,6 @@
 	void	(*flush)(struct drm_device *);
 };
 
-struct nouveau_fb_engine {
-	int num_tiles;
-	struct drm_mm tag_heap;
-	void *priv;
-
-	int  (*init)(struct drm_device *dev);
-	void (*takedown)(struct drm_device *dev);
-
-	void (*init_tile_region)(struct drm_device *dev, int i,
-				 uint32_t addr, uint32_t size,
-				 uint32_t pitch, uint32_t flags);
-	void (*set_tile_region)(struct drm_device *dev, int i);
-	void (*free_tile_region)(struct drm_device *dev, int i);
-};
-
 struct nouveau_display_engine {
 	void *priv;
 	int (*early_init)(struct drm_device *);
@@ -519,24 +500,10 @@
 	int (*temp_get)(struct drm_device *);
 };
 
-struct nouveau_vram_engine {
-	struct nouveau_mm mm;
-
-	int  (*init)(struct drm_device *);
-	void (*takedown)(struct drm_device *dev);
-	int  (*get)(struct drm_device *, u64, u32 align, u32 size_nc,
-		    u32 type, struct nouveau_mem **);
-	void (*put)(struct drm_device *, struct nouveau_mem **);
-
-	bool (*flags_valid)(struct drm_device *, u32 tile_flags);
-};
-
 struct nouveau_engine {
 	struct nouveau_instmem_engine instmem;
-	struct nouveau_fb_engine      fb;
 	struct nouveau_display_engine display;
 	struct nouveau_pm_engine      pm;
-	struct nouveau_vram_engine    vram;
 };
 
 enum nv04_fp_display_regs {
@@ -714,24 +681,6 @@
 		spinlock_t lock;
 	} tile;
 
-	/* VRAM/fb configuration */
-	enum {
-		NV_MEM_TYPE_UNKNOWN = 0,
-		NV_MEM_TYPE_STOLEN,
-		NV_MEM_TYPE_SGRAM,
-		NV_MEM_TYPE_SDRAM,
-		NV_MEM_TYPE_DDR1,
-		NV_MEM_TYPE_DDR2,
-		NV_MEM_TYPE_DDR3,
-		NV_MEM_TYPE_GDDR2,
-		NV_MEM_TYPE_GDDR3,
-		NV_MEM_TYPE_GDDR4,
-		NV_MEM_TYPE_GDDR5
-	} vram_type;
-	uint64_t vram_size;
-	uint64_t vram_sys_base;
-	bool vram_rank_B;
-
 	uint64_t fb_available_size;
 	uint64_t fb_mappable_pages;
 	uint64_t fb_aper_free;
@@ -1047,55 +996,6 @@
 /* nouveau_hdmi.c */
 void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
 
-/* nv04_fb.c */
-extern int  nv04_fb_vram_init(struct drm_device *);
-extern int  nv04_fb_init(struct drm_device *);
-extern void nv04_fb_takedown(struct drm_device *);
-
-/* nv10_fb.c */
-extern int  nv10_fb_vram_init(struct drm_device *dev);
-extern int  nv1a_fb_vram_init(struct drm_device *dev);
-extern int  nv10_fb_init(struct drm_device *);
-extern void nv10_fb_takedown(struct drm_device *);
-extern void nv10_fb_init_tile_region(struct drm_device *dev, int i,
-				     uint32_t addr, uint32_t size,
-				     uint32_t pitch, uint32_t flags);
-extern void nv10_fb_set_tile_region(struct drm_device *dev, int i);
-extern void nv10_fb_free_tile_region(struct drm_device *dev, int i);
-
-/* nv20_fb.c */
-extern int  nv20_fb_vram_init(struct drm_device *dev);
-extern int  nv20_fb_init(struct drm_device *);
-extern void nv20_fb_takedown(struct drm_device *);
-extern void nv20_fb_init_tile_region(struct drm_device *dev, int i,
-				     uint32_t addr, uint32_t size,
-				     uint32_t pitch, uint32_t flags);
-extern void nv20_fb_set_tile_region(struct drm_device *dev, int i);
-extern void nv20_fb_free_tile_region(struct drm_device *dev, int i);
-
-/* nv30_fb.c */
-extern int  nv30_fb_init(struct drm_device *);
-extern void nv30_fb_takedown(struct drm_device *);
-extern void nv30_fb_init_tile_region(struct drm_device *dev, int i,
-				     uint32_t addr, uint32_t size,
-				     uint32_t pitch, uint32_t flags);
-extern void nv30_fb_free_tile_region(struct drm_device *dev, int i);
-
-/* nv40_fb.c */
-extern int  nv40_fb_vram_init(struct drm_device *dev);
-extern int  nv40_fb_init(struct drm_device *);
-extern void nv40_fb_takedown(struct drm_device *);
-extern void nv40_fb_set_tile_region(struct drm_device *dev, int i);
-
-/* nv50_fb.c */
-extern int  nv50_fb_init(struct drm_device *);
-extern void nv50_fb_takedown(struct drm_device *);
-extern void nv50_fb_vm_trap(struct drm_device *, int display);
-
-/* nvc0_fb.c */
-extern int  nvc0_fb_init(struct drm_device *);
-extern void nvc0_fb_takedown(struct drm_device *);
-
 /* nv04_graph.c */
 extern int  nv04_graph_create(struct drm_device *);
 extern int  nv04_graph_object_new(struct nouveau_channel *, int, u32, u16);
@@ -1488,18 +1388,6 @@
 	return !(0x0baf & (1 << (dev_priv->chipset & 0x0f)));
 }
 
-int  nv50_vram_init(struct drm_device *);
-void nv50_vram_fini(struct drm_device *);
-int  nv50_vram_new(struct drm_device *, u64 size, u32 align, u32 size_nc,
-		    u32 memtype, struct nouveau_mem **);
-void nv50_vram_del(struct drm_device *, struct nouveau_mem **);
-bool nv50_vram_flags_valid(struct drm_device *, u32 tile_flags);
-
-int  nvc0_vram_init(struct drm_device *);
-int  nvc0_vram_new(struct drm_device *, u64 size, u32 align, u32 ncmin,
-		    u32 memtype, struct nouveau_mem **);
-bool nvc0_vram_flags_valid(struct drm_device *, u32 tile_flags);
-
 /* memory type/access flags, do not match hardware values */
 #define NV_MEM_ACCESS_RO  1
 #define NV_MEM_ACCESS_WO  2
diff --git a/drivers/gpu/drm/nouveau/nouveau_fb.h b/drivers/gpu/drm/nouveau/nouveau_fb.h
index f3fb649..d767567 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fb.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fb.h
@@ -24,8 +24,8 @@
  *
  */
 
-#ifndef __NOUVEAU_FB_H__
-#define __NOUVEAU_FB_H__
+#ifndef __NOUVEAU_FRB_H__
+#define __NOUVEAU_FRB_H__
 
 struct nouveau_framebuffer {
 	struct drm_framebuffer base;
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 9b07803..f3f0b4c3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -475,9 +475,9 @@
 
 	drm_fb_helper_single_add_all_connectors(&nfbdev->helper);
 
-	if (dev_priv->vram_size <= 32 * 1024 * 1024)
+	if (nvfb_vram_size(dev) <= 32 * 1024 * 1024)
 		preferred_bpp = 8;
-	else if (dev_priv->vram_size <= 64 * 1024 * 1024)
+	else if (nvfb_vram_size(dev) <= 64 * 1024 * 1024)
 		preferred_bpp = 16;
 	else
 		preferred_bpp = 32;
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index c8bb9f2..96a34bc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -209,7 +209,7 @@
 
 	dev_priv->ttm.bdev.dev_mapping = dev->dev_mapping;
 
-	if (!dev_priv->engine.vram.flags_valid(dev, req->info.tile_flags)) {
+	if (!nvfb_flags_valid(dev, req->info.tile_flags)) {
 		NV_ERROR(dev, "bad page flags: 0x%08x\n", req->info.tile_flags);
 		return -EINVAL;
 	}
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 689d3e7..4aea1c4 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -48,21 +48,21 @@
 
 static void
 nv10_mem_update_tile_region(struct drm_device *dev,
-			    struct nouveau_tile_reg *tile, uint32_t addr,
+			    struct nouveau_tile_reg *tilereg, uint32_t addr,
 			    uint32_t size, uint32_t pitch, uint32_t flags)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
-	int i = tile - dev_priv->tile.reg, j;
+	int i = tilereg - dev_priv->tile.reg, j;
+	struct nouveau_fb_tile *tile = nvfb_tile(dev, i);
 	unsigned long save;
 
-	nouveau_fence_unref(&tile->fence);
+	nouveau_fence_unref(&tilereg->fence);
 
 	if (tile->pitch)
-		pfb->free_tile_region(dev, i);
+		nvfb_tile_fini(dev, i);
 
 	if (pitch)
-		pfb->init_tile_region(dev, i, addr, size, pitch, flags);
+		nvfb_tile_init(dev, i, addr, size, pitch, flags);
 
 	spin_lock_irqsave(&dev_priv->context_switch_lock, save);
 	nv_wr32(dev, NV03_PFIFO_CACHES, 0);
@@ -70,7 +70,7 @@
 
 	nouveau_wait_for_idle(dev);
 
-	pfb->set_tile_region(dev, i);
+	nvfb_tile_prog(dev, i);
 	for (j = 0; j < NVOBJ_ENGINE_NR; j++) {
 		if (dev_priv->eng[j] && dev_priv->eng[j]->set_tile_region)
 			dev_priv->eng[j]->set_tile_region(dev, i);
@@ -122,19 +122,17 @@
 nv10_mem_set_tiling(struct drm_device *dev, uint32_t addr, uint32_t size,
 		    uint32_t pitch, uint32_t flags)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
 	struct nouveau_tile_reg *tile, *found = NULL;
 	int i;
 
-	for (i = 0; i < pfb->num_tiles; i++) {
+	for (i = 0; i < nvfb_tile_nr(dev); i++) {
 		tile = nv10_mem_get_tile_region(dev, i);
 
 		if (pitch && !found) {
 			found = tile;
 			continue;
 
-		} else if (tile && tile->pitch) {
+		} else if (tile && nvfb_tile(dev, i)->pitch) {
 			/* Kill an unused tile region. */
 			nv10_mem_update_tile_region(dev, tile, 0, 0, 0, 0);
 		}
@@ -174,38 +172,11 @@
 	nouveau_sgdma_takedown(dev);
 }
 
-bool
-nouveau_mem_flags_valid(struct drm_device *dev, u32 tile_flags)
-{
-	if (!(tile_flags & NOUVEAU_GEM_TILE_LAYOUT_MASK))
-		return true;
-
-	return false;
-}
-
-static const struct vram_types {
-	int value;
-	const char *name;
-} vram_type_map[] = {
-	{ NV_MEM_TYPE_STOLEN , "stolen system memory" },
-	{ NV_MEM_TYPE_SGRAM  , "SGRAM" },
-	{ NV_MEM_TYPE_SDRAM  , "SDRAM" },
-	{ NV_MEM_TYPE_DDR1   , "DDR1" },
-	{ NV_MEM_TYPE_DDR2   , "DDR2" },
-	{ NV_MEM_TYPE_DDR3   , "DDR3" },
-	{ NV_MEM_TYPE_GDDR2  , "GDDR2" },
-	{ NV_MEM_TYPE_GDDR3  , "GDDR3" },
-	{ NV_MEM_TYPE_GDDR4  , "GDDR4" },
-	{ NV_MEM_TYPE_GDDR5  , "GDDR5" },
-	{ NV_MEM_TYPE_UNKNOWN, "unknown type" }
-};
-
 int
 nouveau_mem_vram_init(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
-	const struct vram_types *vram_type;
 	int ret, dma_bits;
 
 	dma_bits = 32;
@@ -243,27 +214,7 @@
 		return ret;
 	}
 
-	vram_type = vram_type_map;
-	while (vram_type->value != NV_MEM_TYPE_UNKNOWN) {
-		if (nouveau_vram_type) {
-			if (!strcasecmp(nouveau_vram_type, vram_type->name))
-				break;
-			dev_priv->vram_type = vram_type->value;
-		} else {
-			if (vram_type->value == dev_priv->vram_type)
-				break;
-		}
-		vram_type++;
-	}
-
-	NV_INFO(dev, "Detected %dMiB VRAM (%s)\n",
-		(int)(dev_priv->vram_size >> 20), vram_type->name);
-	if (dev_priv->vram_sys_base) {
-		NV_INFO(dev, "Stolen system memory at: 0x%010llx\n",
-			dev_priv->vram_sys_base);
-	}
-
-	dev_priv->fb_available_size = dev_priv->vram_size;
+	dev_priv->fb_available_size = nvfb_vram_size(dev);
 	dev_priv->fb_mappable_pages = dev_priv->fb_available_size;
 	if (dev_priv->fb_mappable_pages > pci_resource_len(dev->pdev, 1))
 		dev_priv->fb_mappable_pages = pci_resource_len(dev->pdev, 1);
@@ -364,7 +315,6 @@
 		     struct nouveau_pm_memtiming *boot,
 		     struct nouveau_pm_memtiming *t)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct bit_entry P;
 	uint8_t unk18 = 1, unk20 = 0, unk21 = 0, tmp7_3;
 
@@ -418,7 +368,7 @@
 		t->reg[7] = 0x4000202 | (e->tCL - 1) << 16;
 
 		/* XXX: P.version == 1 only has DDR2 and GDDR3? */
-		if (dev_priv->vram_type == NV_MEM_TYPE_DDR2) {
+		if (nvfb_vram_type(dev) == NV_MEM_TYPE_DDR2) {
 			t->reg[5] |= (e->tCL + 3) << 8;
 			t->reg[6] |= (t->tCWL - 2) << 8;
 			t->reg[8] |= (e->tCL - 4);
@@ -711,7 +661,7 @@
 		break;
 	}
 
-	switch (dev_priv->vram_type * !ret) {
+	switch (nvfb_vram_type(dev) * !ret) {
 	case NV_MEM_TYPE_GDDR3:
 		ret = nouveau_mem_gddr3_mr(dev, freq, e, len, boot, t);
 		break;
@@ -738,7 +688,7 @@
 		else
 			dll_off = !!(ramcfg[2] & 0x40);
 
-		switch (dev_priv->vram_type) {
+		switch (nvfb_vram_type(dev)) {
 		case NV_MEM_TYPE_GDDR3:
 			t->mr[1] &= ~0x00000040;
 			t->mr[1] |=  0x00000040 * dll_off;
@@ -804,7 +754,7 @@
 	t->odt = 0;
 	t->drive_strength = 0;
 
-	switch (dev_priv->vram_type) {
+	switch (nvfb_vram_type(dev)) {
 	case NV_MEM_TYPE_DDR3:
 		t->odt |= (t->mr[1] & 0x200) >> 7;
 	case NV_MEM_TYPE_DDR2:
@@ -831,7 +781,7 @@
 	u32 mr[3] = { info->mr[0], info->mr[1], info->mr[2] };
 	u32 mr1_dlloff;
 
-	switch (dev_priv->vram_type) {
+	switch (nvfb_vram_type(dev_priv->dev)) {
 	case NV_MEM_TYPE_DDR2:
 		tDLLK = 2000;
 		mr1_dlloff = 0x00000001;
@@ -852,7 +802,7 @@
 	}
 
 	/* fetch current MRs */
-	switch (dev_priv->vram_type) {
+	switch (nvfb_vram_type(dev_priv->dev)) {
 	case NV_MEM_TYPE_GDDR3:
 	case NV_MEM_TYPE_DDR3:
 		mr[2] = exec->mrg(exec, 2);
@@ -919,7 +869,7 @@
 		exec->mrs (exec, 0, info->mr[0] | 0x00000000);
 		exec->wait(exec, tMRD);
 		exec->wait(exec, tDLLK);
-		if (dev_priv->vram_type == NV_MEM_TYPE_GDDR3)
+		if (nvfb_vram_type(dev_priv->dev) == NV_MEM_TYPE_GDDR3)
 			exec->precharge(exec);
 	}
 
@@ -982,11 +932,10 @@
 			 struct ttm_mem_reg *mem)
 {
 	struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev);
-	struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
 	struct drm_device *dev = dev_priv->dev;
 
 	nouveau_mem_node_cleanup(mem->mm_node);
-	vram->put(dev, (struct nouveau_mem **)&mem->mm_node);
+	nvfb_vram_put(dev, (struct nouveau_mem **)&mem->mm_node);
 }
 
 static int
@@ -996,7 +945,6 @@
 			 struct ttm_mem_reg *mem)
 {
 	struct drm_nouveau_private *dev_priv = nouveau_bdev(man->bdev);
-	struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
 	struct drm_device *dev = dev_priv->dev;
 	struct nouveau_bo *nvbo = nouveau_bo(bo);
 	struct nouveau_mem *node;
@@ -1006,7 +954,7 @@
 	if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG)
 		size_nc = 1 << nvbo->page_shift;
 
-	ret = vram->get(dev, mem->num_pages << PAGE_SHIFT,
+	ret = nvfb_vram_get(dev, mem->num_pages << PAGE_SHIFT,
 			mem->page_alignment << PAGE_SHIFT, size_nc,
 			(nvbo->tile_flags >> 8) & 0x3ff, &node);
 	if (ret) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 3b11962..d8d9e5c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -61,8 +61,6 @@
 		engine->instmem.map		= nv04_instmem_map;
 		engine->instmem.unmap		= nv04_instmem_unmap;
 		engine->instmem.flush		= nv04_instmem_flush;
-		engine->fb.init			= nv04_fb_init;
-		engine->fb.takedown		= nv04_fb_takedown;
 		engine->display.early_init	= nv04_display_early_init;
 		engine->display.late_takedown	= nv04_display_late_takedown;
 		engine->display.create		= nv04_display_create;
@@ -72,9 +70,6 @@
 		engine->pm.clocks_get		= nv04_pm_clocks_get;
 		engine->pm.clocks_pre		= nv04_pm_clocks_pre;
 		engine->pm.clocks_set		= nv04_pm_clocks_set;
-		engine->vram.init		= nv04_fb_vram_init;
-		engine->vram.takedown		= nouveau_stub_takedown;
-		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
 	case 0x10:
 		engine->instmem.init		= nv04_instmem_init;
@@ -86,11 +81,6 @@
 		engine->instmem.map		= nv04_instmem_map;
 		engine->instmem.unmap		= nv04_instmem_unmap;
 		engine->instmem.flush		= nv04_instmem_flush;
-		engine->fb.init			= nv10_fb_init;
-		engine->fb.takedown		= nv10_fb_takedown;
-		engine->fb.init_tile_region	= nv10_fb_init_tile_region;
-		engine->fb.set_tile_region	= nv10_fb_set_tile_region;
-		engine->fb.free_tile_region	= nv10_fb_free_tile_region;
 		engine->display.early_init	= nv04_display_early_init;
 		engine->display.late_takedown	= nv04_display_late_takedown;
 		engine->display.create		= nv04_display_create;
@@ -100,13 +90,6 @@
 		engine->pm.clocks_get		= nv04_pm_clocks_get;
 		engine->pm.clocks_pre		= nv04_pm_clocks_pre;
 		engine->pm.clocks_set		= nv04_pm_clocks_set;
-		if (dev_priv->chipset == 0x1a ||
-		    dev_priv->chipset == 0x1f)
-			engine->vram.init	= nv1a_fb_vram_init;
-		else
-			engine->vram.init	= nv10_fb_vram_init;
-		engine->vram.takedown		= nouveau_stub_takedown;
-		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
 	case 0x20:
 		engine->instmem.init		= nv04_instmem_init;
@@ -118,11 +101,6 @@
 		engine->instmem.map		= nv04_instmem_map;
 		engine->instmem.unmap		= nv04_instmem_unmap;
 		engine->instmem.flush		= nv04_instmem_flush;
-		engine->fb.init			= nv20_fb_init;
-		engine->fb.takedown		= nv20_fb_takedown;
-		engine->fb.init_tile_region	= nv20_fb_init_tile_region;
-		engine->fb.set_tile_region	= nv20_fb_set_tile_region;
-		engine->fb.free_tile_region	= nv20_fb_free_tile_region;
 		engine->display.early_init	= nv04_display_early_init;
 		engine->display.late_takedown	= nv04_display_late_takedown;
 		engine->display.create		= nv04_display_create;
@@ -132,9 +110,6 @@
 		engine->pm.clocks_get		= nv04_pm_clocks_get;
 		engine->pm.clocks_pre		= nv04_pm_clocks_pre;
 		engine->pm.clocks_set		= nv04_pm_clocks_set;
-		engine->vram.init		= nv20_fb_vram_init;
-		engine->vram.takedown		= nouveau_stub_takedown;
-		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
 	case 0x30:
 		engine->instmem.init		= nv04_instmem_init;
@@ -146,11 +121,6 @@
 		engine->instmem.map		= nv04_instmem_map;
 		engine->instmem.unmap		= nv04_instmem_unmap;
 		engine->instmem.flush		= nv04_instmem_flush;
-		engine->fb.init			= nv30_fb_init;
-		engine->fb.takedown		= nv30_fb_takedown;
-		engine->fb.init_tile_region	= nv30_fb_init_tile_region;
-		engine->fb.set_tile_region	= nv10_fb_set_tile_region;
-		engine->fb.free_tile_region	= nv30_fb_free_tile_region;
 		engine->display.early_init	= nv04_display_early_init;
 		engine->display.late_takedown	= nv04_display_late_takedown;
 		engine->display.create		= nv04_display_create;
@@ -162,9 +132,6 @@
 		engine->pm.clocks_set		= nv04_pm_clocks_set;
 		engine->pm.voltage_get		= nouveau_voltage_gpio_get;
 		engine->pm.voltage_set		= nouveau_voltage_gpio_set;
-		engine->vram.init		= nv20_fb_vram_init;
-		engine->vram.takedown		= nouveau_stub_takedown;
-		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
 	case 0x40:
 	case 0x60:
@@ -177,11 +144,6 @@
 		engine->instmem.map		= nv04_instmem_map;
 		engine->instmem.unmap		= nv04_instmem_unmap;
 		engine->instmem.flush		= nv04_instmem_flush;
-		engine->fb.init			= nv40_fb_init;
-		engine->fb.takedown		= nv40_fb_takedown;
-		engine->fb.init_tile_region	= nv30_fb_init_tile_region;
-		engine->fb.set_tile_region	= nv40_fb_set_tile_region;
-		engine->fb.free_tile_region	= nv30_fb_free_tile_region;
 		engine->display.early_init	= nv04_display_early_init;
 		engine->display.late_takedown	= nv04_display_late_takedown;
 		engine->display.create		= nv04_display_create;
@@ -196,9 +158,6 @@
 		engine->pm.temp_get		= nv40_temp_get;
 		engine->pm.pwm_get		= nv40_pm_pwm_get;
 		engine->pm.pwm_set		= nv40_pm_pwm_set;
-		engine->vram.init		= nv40_fb_vram_init;
-		engine->vram.takedown		= nouveau_stub_takedown;
-		engine->vram.flags_valid	= nouveau_mem_flags_valid;
 		break;
 	case 0x50:
 	case 0x80: /* gotta love NVIDIA's consistency.. */
@@ -216,8 +175,6 @@
 			engine->instmem.flush	= nv50_instmem_flush;
 		else
 			engine->instmem.flush	= nv84_instmem_flush;
-		engine->fb.init			= nv50_fb_init;
-		engine->fb.takedown		= nv50_fb_takedown;
 		engine->display.early_init	= nv50_display_early_init;
 		engine->display.late_takedown	= nv50_display_late_takedown;
 		engine->display.create		= nv50_display_create;
@@ -253,11 +210,6 @@
 			engine->pm.temp_get	= nv40_temp_get;
 		engine->pm.pwm_get		= nv50_pm_pwm_get;
 		engine->pm.pwm_set		= nv50_pm_pwm_set;
-		engine->vram.init		= nv50_vram_init;
-		engine->vram.takedown		= nv50_vram_fini;
-		engine->vram.get		= nv50_vram_new;
-		engine->vram.put		= nv50_vram_del;
-		engine->vram.flags_valid	= nv50_vram_flags_valid;
 		break;
 	case 0xc0:
 		engine->instmem.init		= nvc0_instmem_init;
@@ -269,19 +221,12 @@
 		engine->instmem.map		= nv50_instmem_map;
 		engine->instmem.unmap		= nv50_instmem_unmap;
 		engine->instmem.flush		= nv84_instmem_flush;
-		engine->fb.init			= nvc0_fb_init;
-		engine->fb.takedown		= nvc0_fb_takedown;
 		engine->display.early_init	= nv50_display_early_init;
 		engine->display.late_takedown	= nv50_display_late_takedown;
 		engine->display.create		= nv50_display_create;
 		engine->display.destroy		= nv50_display_destroy;
 		engine->display.init		= nv50_display_init;
 		engine->display.fini		= nv50_display_fini;
-		engine->vram.init		= nvc0_vram_init;
-		engine->vram.takedown		= nv50_vram_fini;
-		engine->vram.get		= nvc0_vram_new;
-		engine->vram.put		= nv50_vram_del;
-		engine->vram.flags_valid	= nvc0_vram_flags_valid;
 		engine->pm.temp_get		= nv84_temp_get;
 		engine->pm.clocks_get		= nvc0_pm_clocks_get;
 		engine->pm.clocks_pre		= nvc0_pm_clocks_pre;
@@ -301,19 +246,12 @@
 		engine->instmem.map		= nv50_instmem_map;
 		engine->instmem.unmap		= nv50_instmem_unmap;
 		engine->instmem.flush		= nv84_instmem_flush;
-		engine->fb.init			= nvc0_fb_init;
-		engine->fb.takedown		= nvc0_fb_takedown;
 		engine->display.early_init	= nouveau_stub_init;
 		engine->display.late_takedown	= nouveau_stub_takedown;
 		engine->display.create		= nvd0_display_create;
 		engine->display.destroy		= nvd0_display_destroy;
 		engine->display.init		= nvd0_display_init;
 		engine->display.fini		= nvd0_display_fini;
-		engine->vram.init		= nvc0_vram_init;
-		engine->vram.takedown		= nv50_vram_fini;
-		engine->vram.get		= nvc0_vram_new;
-		engine->vram.put		= nv50_vram_del;
-		engine->vram.flags_valid	= nvc0_vram_flags_valid;
 		engine->pm.temp_get		= nv84_temp_get;
 		engine->pm.clocks_get		= nvc0_pm_clocks_get;
 		engine->pm.clocks_pre		= nvc0_pm_clocks_pre;
@@ -331,19 +269,12 @@
 		engine->instmem.map		= nv50_instmem_map;
 		engine->instmem.unmap		= nv50_instmem_unmap;
 		engine->instmem.flush		= nv84_instmem_flush;
-		engine->fb.init			= nvc0_fb_init;
-		engine->fb.takedown		= nvc0_fb_takedown;
 		engine->display.early_init	= nouveau_stub_init;
 		engine->display.late_takedown	= nouveau_stub_takedown;
 		engine->display.create		= nvd0_display_create;
 		engine->display.destroy		= nvd0_display_destroy;
 		engine->display.init		= nvd0_display_init;
 		engine->display.fini		= nvd0_display_fini;
-		engine->vram.init		= nvc0_vram_init;
-		engine->vram.takedown		= nv50_vram_fini;
-		engine->vram.get		= nvc0_vram_new;
-		engine->vram.put		= nv50_vram_del;
-		engine->vram.flags_valid	= nvc0_vram_flags_valid;
 		break;
 	default:
 		NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
@@ -488,18 +419,9 @@
 		nv_mask(dev, 0x00088080, 0x00000800, 0x00000000);
 	}
 
-	/* PFB */
-	ret = engine->fb.init(dev);
-	if (ret)
-		goto out_bios;
-
-	ret = engine->vram.init(dev);
-	if (ret)
-		goto out_fb;
-
 	ret = nouveau_gpuobj_init(dev);
 	if (ret)
-		goto out_vram;
+		goto out_bios;
 
 	ret = engine->instmem.init(dev);
 	if (ret)
@@ -734,10 +656,6 @@
 	engine->instmem.takedown(dev);
 out_gpuobj:
 	nouveau_gpuobj_takedown(dev);
-out_vram:
-	engine->vram.takedown(dev);
-out_fb:
-	engine->fb.takedown(dev);
 out_bios:
 	nouveau_bios_takedown(dev);
 out_display_early:
@@ -788,9 +706,6 @@
 	engine->instmem.takedown(dev);
 	nouveau_gpuobj_takedown(dev);
 
-	engine->vram.takedown(dev);
-	engine->fb.takedown(dev);
-
 	nouveau_bios_takedown(dev);
 	engine->display.late_takedown(dev);
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_util.c b/drivers/gpu/drm/nouveau/nouveau_util.c
index e51b515..6bff634 100644
--- a/drivers/gpu/drm/nouveau/nouveau_util.c
+++ b/drivers/gpu/drm/nouveau/nouveau_util.c
@@ -26,51 +26,12 @@
  */
 
 #include <linux/ratelimit.h>
-
 #include "nouveau_util.h"
 
+#include <core/enum.h>
+
 static DEFINE_RATELIMIT_STATE(nouveau_ratelimit_state, 3 * HZ, 20);
 
-void
-nouveau_bitfield_print(const struct nouveau_bitfield *bf, u32 value)
-{
-	while (bf->name) {
-		if (value & bf->mask) {
-			printk(" %s", bf->name);
-			value &= ~bf->mask;
-		}
-
-		bf++;
-	}
-
-	if (value)
-		printk(" (unknown bits 0x%08x)", value);
-}
-
-const struct nouveau_enum *
-nouveau_enum_find(const struct nouveau_enum *en, u32 value)
-{
-	while (en->name) {
-		if (en->value == value)
-			return en;
-		en++;
-	}
-
-	return NULL;
-}
-
-void
-nouveau_enum_print(const struct nouveau_enum *en, u32 value)
-{
-	en = nouveau_enum_find(en, value);
-	if (en) {
-		printk("%s", en->name);
-		return;
-	}
-
-	printk("(unknown enum 0x%08x)", value);
-}
-
 int
 nouveau_ratelimit(void)
 {
diff --git a/drivers/gpu/drm/nouveau/nouveau_util.h b/drivers/gpu/drm/nouveau/nouveau_util.h
index b97719f..1142937 100644
--- a/drivers/gpu/drm/nouveau/nouveau_util.h
+++ b/drivers/gpu/drm/nouveau/nouveau_util.h
@@ -28,21 +28,7 @@
 #ifndef __NOUVEAU_UTIL_H__
 #define __NOUVEAU_UTIL_H__
 
-struct nouveau_bitfield {
-	u32 mask;
-	const char *name;
-};
-
-struct nouveau_enum {
-	u32 value;
-	const char *name;
-	void *data;
-};
-
-void nouveau_bitfield_print(const struct nouveau_bitfield *, u32 value);
-void nouveau_enum_print(const struct nouveau_enum *, u32 value);
-const struct nouveau_enum *
-nouveau_enum_find(const struct nouveau_enum *, u32 value);
+#include <core/enum.h>
 
 int nouveau_ratelimit(void);
 
diff --git a/drivers/gpu/drm/nouveau/nv50_evo.c b/drivers/gpu/drm/nouveau/nv50_evo.c
index 5731cb3..7a14242 100644
--- a/drivers/gpu/drm/nouveau/nv50_evo.c
+++ b/drivers/gpu/drm/nouveau/nv50_evo.c
@@ -300,24 +300,24 @@
 
 	/* create some default objects for the scanout memtypes we support */
 	ret = nv50_evo_dmaobj_new(disp->master, NvEvoVRAM, 0x0000,
-				  0, dev_priv->vram_size, NULL);
+				  0, nvfb_vram_size(dev), NULL);
 	if (ret)
 		goto err;
 
 	ret = nv50_evo_dmaobj_new(disp->master, NvEvoVRAM_LP, 0x80000000,
-				  0, dev_priv->vram_size, NULL);
+				  0, nvfb_vram_size(dev), NULL);
 	if (ret)
 		goto err;
 
 	ret = nv50_evo_dmaobj_new(disp->master, NvEvoFB32, 0x80000000 |
 				  (dev_priv->chipset < 0xc0 ? 0x7a00 : 0xfe00),
-				  0, dev_priv->vram_size, NULL);
+				  0, nvfb_vram_size(dev), NULL);
 	if (ret)
 		goto err;
 
 	ret = nv50_evo_dmaobj_new(disp->master, NvEvoFB16, 0x80000000 |
 				  (dev_priv->chipset < 0xc0 ? 0x7000 : 0xfe00),
-				  0, dev_priv->vram_size, NULL);
+				  0, nvfb_vram_size(dev), NULL);
 	if (ret)
 		goto err;
 
@@ -352,21 +352,21 @@
 			goto err;
 
 		ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoVRAM_LP, 0x80000000,
-					  0, dev_priv->vram_size, NULL);
+					  0, nvfb_vram_size(dev), NULL);
 		if (ret)
 			goto err;
 
 		ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoFB32, 0x80000000 |
 					  (dev_priv->chipset < 0xc0 ?
 					  0x7a00 : 0xfe00),
-					  0, dev_priv->vram_size, NULL);
+					  0, nvfb_vram_size(dev), NULL);
 		if (ret)
 			goto err;
 
 		ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoFB16, 0x80000000 |
 					  (dev_priv->chipset < 0xc0 ?
 					  0x7000 : 0xfe00),
-					  0, dev_priv->vram_size, NULL);
+					  0, nvfb_vram_size(dev), NULL);
 		if (ret)
 			goto err;
 
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
index 378ca8c..142cd4e 100644
--- a/drivers/gpu/drm/nouveau/nv50_pm.c
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -471,17 +471,16 @@
 static void
 mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data)
 {
-	struct drm_nouveau_private *dev_priv = exec->dev->dev_private;
 	struct nv50_pm_state *info = exec->priv;
 	struct hwsq_ucode *hwsq = &info->mclk_hwsq;
 
 	if (mr <= 1) {
-		if (dev_priv->vram_rank_B)
+		if (nvfb_vram_rank_B(exec->dev))
 			hwsq_wr32(hwsq, 0x1002c8 + ((mr - 0) * 4), data);
 		hwsq_wr32(hwsq, 0x1002c0 + ((mr - 0) * 4), data);
 	} else
 	if (mr <= 3) {
-		if (dev_priv->vram_rank_B)
+		if (nvfb_vram_rank_B(exec->dev))
 			hwsq_wr32(hwsq, 0x1002e8 + ((mr - 2) * 4), data);
 		hwsq_wr32(hwsq, 0x1002e0 + ((mr - 2) * 4), data);
 	}
diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c
index 847c616..2f7ee50 100644
--- a/drivers/gpu/drm/nouveau/nva3_pm.c
+++ b/drivers/gpu/drm/nouveau/nva3_pm.c
@@ -361,15 +361,13 @@
 static void
 mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data)
 {
-	struct drm_nouveau_private *dev_priv = exec->dev->dev_private;
-
 	if (mr <= 1) {
-		if (dev_priv->vram_rank_B)
+		if (nvfb_vram_rank_B(exec->dev))
 			nv_wr32(exec->dev, 0x1002c8 + ((mr - 0) * 4), data);
 		nv_wr32(exec->dev, 0x1002c0 + ((mr - 0) * 4), data);
 	} else
 	if (mr <= 3) {
-		if (dev_priv->vram_rank_B)
+		if (nvfb_vram_rank_B(exec->dev))
 			nv_wr32(exec->dev, 0x1002e8 + ((mr - 2) * 4), data);
 		nv_wr32(exec->dev, 0x1002e0 + ((mr - 2) * 4), data);
 	}
diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c
index 4a21a72..a14711c 100644
--- a/drivers/gpu/drm/nouveau/nvc0_pm.c
+++ b/drivers/gpu/drm/nouveau/nvc0_pm.c
@@ -459,8 +459,7 @@
 mclk_mrg(struct nouveau_mem_exec_func *exec, int mr)
 {
 	struct drm_device *dev = exec->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	if (dev_priv->vram_type != NV_MEM_TYPE_GDDR5) {
+	if (nvfb_vram_type(dev) != NV_MEM_TYPE_GDDR5) {
 		if (mr <= 1)
 			return nv_rd32(dev, 0x10f300 + ((mr - 0) * 4));
 		return nv_rd32(dev, 0x10f320 + ((mr - 2) * 4));
@@ -478,16 +477,15 @@
 mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data)
 {
 	struct drm_device *dev = exec->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	if (dev_priv->vram_type != NV_MEM_TYPE_GDDR5) {
+	if (nvfb_vram_type(dev) != NV_MEM_TYPE_GDDR5) {
 		if (mr <= 1) {
 			nv_wr32(dev, 0x10f300 + ((mr - 0) * 4), data);
-			if (dev_priv->vram_rank_B)
+			if (nvfb_vram_rank_B(dev))
 				nv_wr32(dev, 0x10f308 + ((mr - 0) * 4), data);
 		} else
 		if (mr <= 3) {
 			nv_wr32(dev, 0x10f320 + ((mr - 2) * 4), data);
-			if (dev_priv->vram_rank_B)
+			if (nvfb_vram_rank_B(dev))
 				nv_wr32(dev, 0x10f328 + ((mr - 2) * 4), data);
 		}
 	} else {
diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
index d26dd02..8d9fc00 100644
--- a/drivers/gpu/drm/nouveau/nvd0_display.c
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -2077,7 +2077,7 @@
 
 		nv_wo32(disp->mem, dmao + 0x20, 0x00000049);
 		nv_wo32(disp->mem, dmao + 0x24, 0x00000000);
-		nv_wo32(disp->mem, dmao + 0x28, (dev_priv->vram_size - 1) >> 8);
+		nv_wo32(disp->mem, dmao + 0x28, (nvfb_vram_size(dev) - 1) >> 8);
 		nv_wo32(disp->mem, dmao + 0x2c, 0x00000000);
 		nv_wo32(disp->mem, dmao + 0x30, 0x00000000);
 		nv_wo32(disp->mem, dmao + 0x34, 0x00000000);
@@ -2087,7 +2087,7 @@
 
 		nv_wo32(disp->mem, dmao + 0x40, 0x00000009);
 		nv_wo32(disp->mem, dmao + 0x44, 0x00000000);
-		nv_wo32(disp->mem, dmao + 0x48, (dev_priv->vram_size - 1) >> 8);
+		nv_wo32(disp->mem, dmao + 0x48, (nvfb_vram_size(dev) - 1) >> 8);
 		nv_wo32(disp->mem, dmao + 0x4c, 0x00000000);
 		nv_wo32(disp->mem, dmao + 0x50, 0x00000000);
 		nv_wo32(disp->mem, dmao + 0x54, 0x00000000);
@@ -2097,7 +2097,7 @@
 
 		nv_wo32(disp->mem, dmao + 0x60, 0x0fe00009);
 		nv_wo32(disp->mem, dmao + 0x64, 0x00000000);
-		nv_wo32(disp->mem, dmao + 0x68, (dev_priv->vram_size - 1) >> 8);
+		nv_wo32(disp->mem, dmao + 0x68, (nvfb_vram_size(dev) - 1) >> 8);
 		nv_wo32(disp->mem, dmao + 0x6c, 0x00000000);
 		nv_wo32(disp->mem, dmao + 0x70, 0x00000000);
 		nv_wo32(disp->mem, dmao + 0x74, 0x00000000);