Эх сурвалжийг харах

drm/nv20-nv30/gr: move to exec engine interface

A bit of cleanup done along the way, but, like nv40/nv50, needs more.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Ben Skeggs 14 жил өмнө
parent
commit
a0b1de84fe

+ 1 - 7
drivers/gpu/drm/nouveau/nouveau_drv.h

@@ -1167,13 +1167,7 @@ extern struct nouveau_bitfield nv10_graph_intr[];
 extern struct nouveau_bitfield nv10_graph_nstatus[];
 
 /* nv20_graph.c */
-extern int  nv20_graph_create_context(struct nouveau_channel *);
-extern void nv20_graph_destroy_context(struct nouveau_channel *);
-extern int  nv20_graph_load_context(struct nouveau_channel *);
-extern int  nv20_graph_unload_context(struct drm_device *);
-extern int  nv20_graph_init(struct drm_device *);
-extern void nv20_graph_takedown(struct drm_device *);
-extern int  nv30_graph_init(struct drm_device *);
+extern int  nv20_graph_create(struct drm_device *);
 extern void nv20_graph_set_tile_region(struct drm_device *dev, int i);
 
 /* nv40_graph.c */

+ 14 - 18
drivers/gpu/drm/nouveau/nouveau_state.c

@@ -180,15 +180,10 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		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->graph.init		= nv20_graph_init;
-		engine->graph.takedown		= nv20_graph_takedown;
-		engine->graph.channel		= nv10_graph_channel;
-		engine->graph.create_context	= nv20_graph_create_context;
-		engine->graph.destroy_context	= nv20_graph_destroy_context;
-		engine->graph.fifo_access	= nv04_graph_fifo_access;
-		engine->graph.load_context	= nv20_graph_load_context;
-		engine->graph.unload_context	= nv20_graph_unload_context;
-		engine->graph.object_new	= nv04_graph_object_new;
+		engine->graph.init		= nouveau_stub_init;
+		engine->graph.takedown		= nouveau_stub_takedown;
+		engine->graph.channel		= nvc0_graph_channel;
+		engine->graph.fifo_access	= nvc0_graph_fifo_access;
 		engine->graph.set_tile_region	= nv20_graph_set_tile_region;
 		engine->fifo.channels		= 32;
 		engine->fifo.init		= nv10_fifo_init;
@@ -238,15 +233,10 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		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->graph.init		= nv30_graph_init;
-		engine->graph.takedown		= nv20_graph_takedown;
-		engine->graph.fifo_access	= nv04_graph_fifo_access;
-		engine->graph.channel		= nv10_graph_channel;
-		engine->graph.create_context	= nv20_graph_create_context;
-		engine->graph.destroy_context	= nv20_graph_destroy_context;
-		engine->graph.load_context	= nv20_graph_load_context;
-		engine->graph.unload_context	= nv20_graph_unload_context;
-		engine->graph.object_new	= nv04_graph_object_new;
+		engine->graph.init		= nouveau_stub_init;
+		engine->graph.takedown		= nouveau_stub_takedown;
+		engine->graph.channel		= nvc0_graph_channel;
+		engine->graph.fifo_access	= nvc0_graph_fifo_access;
 		engine->graph.set_tile_region	= nv20_graph_set_tile_region;
 		engine->fifo.channels		= 32;
 		engine->fifo.init		= nv10_fifo_init;
@@ -614,6 +604,10 @@ nouveau_card_init(struct drm_device *dev)
 		goto out_timer;
 
 	switch (dev_priv->card_type) {
+	case NV_20:
+	case NV_30:
+		nv20_graph_create(dev);
+		break;
 	case NV_40:
 		nv40_graph_create(dev);
 		break;
@@ -623,6 +617,8 @@ nouveau_card_init(struct drm_device *dev)
 	case NV_C0:
 		nvc0_graph_create(dev);
 		break;
+	default:
+		break;
 	}
 
 	switch (dev_priv->chipset) {

+ 224 - 287
drivers/gpu/drm/nouveau/nv20_graph.c

@@ -24,6 +24,14 @@
  *
  */
 
+struct nv20_graph_engine {
+	struct nouveau_exec_engine base;
+	struct nouveau_gpuobj *ctxtab;
+	void (*grctx_init)(struct nouveau_gpuobj *);
+	u32 grctx_size;
+	u32 grctx_user;
+};
+
 #define NV20_GRCTX_SIZE (3580*4)
 #define NV25_GRCTX_SIZE (3529*4)
 #define NV2A_GRCTX_SIZE (3500*4)
@@ -32,12 +40,54 @@
 #define NV34_GRCTX_SIZE    (18140)
 #define NV35_36_GRCTX_SIZE (22396)
 
-static int nv20_graph_register(struct drm_device *);
-static int nv30_graph_register(struct drm_device *);
-static void nv20_graph_isr(struct drm_device *);
+int
+nv20_graph_unload_context(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	struct nouveau_channel *chan;
+	struct nouveau_gpuobj *grctx;
+	u32 tmp;
+
+	chan = nv10_graph_channel(dev);
+	if (!chan)
+		return 0;
+	grctx = chan->engctx[NVOBJ_ENGINE_GR];
+
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, grctx->pinst >> 4);
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
+		     NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE);
+
+	nouveau_wait_for_idle(dev);
+
+	nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
+	tmp  = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
+	tmp |= (pfifo->channels - 1) << 24;
+	nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
+	return 0;
+}
+
+static void
+nv20_graph_rdi(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int i, writecount = 32;
+	uint32_t rdi_index = 0x2c80000;
+
+	if (dev_priv->chipset == 0x20) {
+		rdi_index = 0x3d0000;
+		writecount = 15;
+	}
+
+	nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, rdi_index);
+	for (i = 0; i < writecount; i++)
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA, 0);
+
+	nouveau_wait_for_idle(dev);
+}
 
 static void
-nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv20_graph_context_init(struct nouveau_gpuobj *ctx)
 {
 	int i;
 
@@ -87,7 +137,7 @@ nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
 }
 
 static void
-nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv25_graph_context_init(struct nouveau_gpuobj *ctx)
 {
 	int i;
 
@@ -146,7 +196,7 @@ nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
 }
 
 static void
-nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv2a_graph_context_init(struct nouveau_gpuobj *ctx)
 {
 	int i;
 
@@ -196,7 +246,7 @@ nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
 }
 
 static void
-nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv30_31_graph_context_init(struct nouveau_gpuobj *ctx)
 {
 	int i;
 
@@ -254,7 +304,7 @@ nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
 }
 
 static void
-nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv34_graph_context_init(struct nouveau_gpuobj *ctx)
 {
 	int i;
 
@@ -312,7 +362,7 @@ nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
 }
 
 static void
-nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+nv35_36_graph_context_init(struct nouveau_gpuobj *ctx)
 {
 	int i;
 
@@ -370,145 +420,54 @@ nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
 }
 
 int
-nv20_graph_create_context(struct nouveau_channel *chan)
+nv20_graph_context_new(struct nouveau_channel *chan, int engine)
 {
+	struct nv20_graph_engine *pgraph = nv_engine(chan->dev, engine);
+	struct nouveau_gpuobj *grctx = NULL;
 	struct drm_device *dev = chan->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
-	void (*ctx_init)(struct drm_device *, struct nouveau_gpuobj *);
-	unsigned int idoffs = 0x28;
 	int ret;
 
-	switch (dev_priv->chipset) {
-	case 0x20:
-		ctx_init = nv20_graph_context_init;
-		idoffs = 0;
-		break;
-	case 0x25:
-	case 0x28:
-		ctx_init = nv25_graph_context_init;
-		break;
-	case 0x2a:
-		ctx_init = nv2a_graph_context_init;
-		idoffs = 0;
-		break;
-	case 0x30:
-	case 0x31:
-		ctx_init = nv30_31_graph_context_init;
-		break;
-	case 0x34:
-		ctx_init = nv34_graph_context_init;
-		break;
-	case 0x35:
-	case 0x36:
-		ctx_init = nv35_36_graph_context_init;
-		break;
-	default:
-		BUG_ON(1);
-	}
-
-	ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 16,
-				 NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin_grctx);
+	ret = nouveau_gpuobj_new(dev, NULL, pgraph->grctx_size, 16,
+				 NVOBJ_FLAG_ZERO_ALLOC, &grctx);
 	if (ret)
 		return ret;
 
 	/* Initialise default context values */
-	ctx_init(dev, chan->ramin_grctx);
+	pgraph->grctx_init(grctx);
 
 	/* nv20: nv_wo32(dev, chan->ramin_grctx->gpuobj, 10, chan->id<<24); */
-	nv_wo32(chan->ramin_grctx, idoffs,
-		(chan->id << 24) | 0x1); /* CTX_USER */
+	/* CTX_USER */
+	nv_wo32(grctx, pgraph->grctx_user, (chan->id << 24) | 0x1);
 
-	nv_wo32(pgraph->ctx_table, chan->id * 4, chan->ramin_grctx->pinst >> 4);
+	nv_wo32(pgraph->ctxtab, chan->id * 4, grctx->pinst >> 4);
+	chan->engctx[engine] = grctx;
 	return 0;
 }
 
 void
-nv20_graph_destroy_context(struct nouveau_channel *chan)
+nv20_graph_context_del(struct nouveau_channel *chan, int engine)
 {
+	struct nv20_graph_engine *pgraph = nv_engine(chan->dev, engine);
+	struct nouveau_gpuobj *grctx = chan->engctx[engine];
 	struct drm_device *dev = chan->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
 	unsigned long flags;
 
 	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
-	pgraph->fifo_access(dev, false);
+	nv04_graph_fifo_access(dev, false);
 
 	/* Unload the context if it's the currently active one */
-	if (pgraph->channel(dev) == chan)
-		pgraph->unload_context(dev);
+	if (nv10_graph_channel(dev) == chan)
+		nv20_graph_unload_context(dev);
 
-	pgraph->fifo_access(dev, true);
+	nv04_graph_fifo_access(dev, true);
 	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
 
 	/* Free the context resources */
-	nv_wo32(pgraph->ctx_table, chan->id * 4, 0);
-	nouveau_gpuobj_ref(NULL, &chan->ramin_grctx);
-}
-
-int
-nv20_graph_load_context(struct nouveau_channel *chan)
-{
-	struct drm_device *dev = chan->dev;
-	uint32_t inst;
+	nv_wo32(pgraph->ctxtab, chan->id * 4, 0);
 
-	if (!chan->ramin_grctx)
-		return -EINVAL;
-	inst = chan->ramin_grctx->pinst >> 4;
-
-	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
-	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
-		     NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD);
-	nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
-
-	nouveau_wait_for_idle(dev);
-	return 0;
-}
-
-int
-nv20_graph_unload_context(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
-	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
-	struct nouveau_channel *chan;
-	uint32_t inst, tmp;
-
-	chan = pgraph->channel(dev);
-	if (!chan)
-		return 0;
-	inst = chan->ramin_grctx->pinst >> 4;
-
-	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
-	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
-		     NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE);
-
-	nouveau_wait_for_idle(dev);
-
-	nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
-	tmp  = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
-	tmp |= (pfifo->channels - 1) << 24;
-	nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
-	return 0;
-}
-
-static void
-nv20_graph_rdi(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	int i, writecount = 32;
-	uint32_t rdi_index = 0x2c80000;
-
-	if (dev_priv->chipset == 0x20) {
-		rdi_index = 0x3d0000;
-		writecount = 15;
-	}
-
-	nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, rdi_index);
-	for (i = 0; i < writecount; i++)
-		nv_wr32(dev, NV10_PGRAPH_RDI_DATA, 0);
-
-	nouveau_wait_for_idle(dev);
+	nouveau_gpuobj_ref(NULL, &grctx);
+	chan->engctx[engine] = NULL;
 }
 
 void
@@ -536,56 +495,22 @@ nv20_graph_set_tile_region(struct drm_device *dev, int i)
 }
 
 int
-nv20_graph_init(struct drm_device *dev)
+nv20_graph_init(struct drm_device *dev, int engine)
 {
+	struct nv20_graph_engine *pgraph = nv_engine(dev, engine);
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
 	uint32_t tmp, vramsz;
-	int ret, i;
-
-	switch (dev_priv->chipset) {
-	case 0x20:
-		pgraph->grctx_size = NV20_GRCTX_SIZE;
-		break;
-	case 0x25:
-	case 0x28:
-		pgraph->grctx_size = NV25_GRCTX_SIZE;
-		break;
-	case 0x2a:
-		pgraph->grctx_size = NV2A_GRCTX_SIZE;
-		break;
-	default:
-		NV_ERROR(dev, "unknown chipset, disabling acceleration\n");
-		pgraph->accel_blocked = true;
-		return 0;
-	}
+	int i;
 
 	nv_wr32(dev, NV03_PMC_ENABLE,
 		nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
 	nv_wr32(dev, NV03_PMC_ENABLE,
 		nv_rd32(dev, NV03_PMC_ENABLE) |  NV_PMC_ENABLE_PGRAPH);
 
-	if (!pgraph->ctx_table) {
-		/* Create Context Pointer Table */
-		ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16,
-					 NVOBJ_FLAG_ZERO_ALLOC,
-					 &pgraph->ctx_table);
-		if (ret)
-			return ret;
-	}
-
-	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
-		     pgraph->ctx_table->pinst >> 4);
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, pgraph->ctxtab->pinst >> 4);
 
 	nv20_graph_rdi(dev);
 
-	ret = nv20_graph_register(dev);
-	if (ret) {
-		nouveau_gpuobj_ref(NULL, &pgraph->ctx_table);
-		return ret;
-	}
-
-	nouveau_irq_register(dev, 12, nv20_graph_isr);
 	nv_wr32(dev, NV03_PGRAPH_INTR   , 0xFFFFFFFF);
 	nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
 
@@ -657,67 +582,20 @@ nv20_graph_init(struct drm_device *dev)
 	return 0;
 }
 
-void
-nv20_graph_takedown(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
-
-	nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
-	nouveau_irq_unregister(dev, 12);
-
-	nouveau_gpuobj_ref(NULL, &pgraph->ctx_table);
-}
-
 int
-nv30_graph_init(struct drm_device *dev)
+nv30_graph_init(struct drm_device *dev, int engine)
 {
+	struct nv20_graph_engine *pgraph = nv_engine(dev, engine);
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
-	int ret, i;
-
-	switch (dev_priv->chipset) {
-	case 0x30:
-	case 0x31:
-		pgraph->grctx_size = NV30_31_GRCTX_SIZE;
-		break;
-	case 0x34:
-		pgraph->grctx_size = NV34_GRCTX_SIZE;
-		break;
-	case 0x35:
-	case 0x36:
-		pgraph->grctx_size = NV35_36_GRCTX_SIZE;
-		break;
-	default:
-		NV_ERROR(dev, "unknown chipset, disabling acceleration\n");
-		pgraph->accel_blocked = true;
-		return 0;
-	}
+	int i;
 
 	nv_wr32(dev, NV03_PMC_ENABLE,
 		nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
 	nv_wr32(dev, NV03_PMC_ENABLE,
 		nv_rd32(dev, NV03_PMC_ENABLE) |  NV_PMC_ENABLE_PGRAPH);
 
-	if (!pgraph->ctx_table) {
-		/* Create Context Pointer Table */
-		ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16,
-					 NVOBJ_FLAG_ZERO_ALLOC,
-					 &pgraph->ctx_table);
-		if (ret)
-			return ret;
-	}
-
-	ret = nv30_graph_register(dev);
-	if (ret) {
-		nouveau_gpuobj_ref(NULL, &pgraph->ctx_table);
-		return ret;
-	}
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, pgraph->ctxtab->pinst >> 4);
 
-	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
-		     pgraph->ctx_table->pinst >> 4);
-
-	nouveau_irq_register(dev, 12, nv20_graph_isr);
 	nv_wr32(dev, NV03_PGRAPH_INTR   , 0xFFFFFFFF);
 	nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
 
@@ -775,85 +653,11 @@ nv30_graph_init(struct drm_device *dev)
 	return 0;
 }
 
-static int
-nv20_graph_register(struct drm_device *dev)
-{
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-
-	if (dev_priv->engine.graph.registered)
-		return 0;
-
-	NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */
-	NVOBJ_CLASS(dev, 0x0030, GR); /* null */
-	NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */
-	NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */
-	NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */
-	NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */
-	NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */
-	NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */
-	NVOBJ_CLASS(dev, 0x0043, GR); /* rop */
-	NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */
-	NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */
-	NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */
-	NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */
-	NVOBJ_CLASS(dev, 0x009e, GR); /* swzsurf */
-	NVOBJ_CLASS(dev, 0x0096, GR); /* celcius */
-
-	/* kelvin */
-	if (dev_priv->chipset < 0x25)
-		NVOBJ_CLASS(dev, 0x0097, GR);
-	else
-		NVOBJ_CLASS(dev, 0x0597, GR);
-
-	/* nvsw */
-	NVOBJ_CLASS(dev, 0x506e, SW);
-	NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
-
-	dev_priv->engine.graph.registered = true;
-	return 0;
-}
-
-static int
-nv30_graph_register(struct drm_device *dev)
+int
+nv20_graph_fini(struct drm_device *dev, int engine)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-
-	if (dev_priv->engine.graph.registered)
-		return 0;
-
-	NVOBJ_CLASS(dev, 0x506e, SW); /* nvsw */
-	NVOBJ_CLASS(dev, 0x0030, GR); /* null */
-	NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */
-	NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */
-	NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */
-	NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */
-	NVOBJ_CLASS(dev, 0x038a, GR); /* ifc (nv30) */
-	NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */
-	NVOBJ_CLASS(dev, 0x0389, GR); /* sifm (nv30) */
-	NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */
-	NVOBJ_CLASS(dev, 0x0362, GR); /* surf2d (nv30) */
-	NVOBJ_CLASS(dev, 0x0043, GR); /* rop */
-	NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */
-	NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */
-	NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */
-	NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */
-	NVOBJ_CLASS(dev, 0x039e, GR); /* swzsurf */
-
-	/* rankine */
-	if (0x00000003 & (1 << (dev_priv->chipset & 0x0f)))
-		NVOBJ_CLASS(dev, 0x0397, GR);
-	else
-	if (0x00000010 & (1 << (dev_priv->chipset & 0x0f)))
-		NVOBJ_CLASS(dev, 0x0697, GR);
-	else
-	if (0x000001e0 & (1 << (dev_priv->chipset & 0x0f)))
-		NVOBJ_CLASS(dev, 0x0497, GR);
-
-	/* nvsw */
-	NVOBJ_CLASS(dev, 0x506e, SW);
-	NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
-
-	dev_priv->engine.graph.registered = true;
+	nv20_graph_unload_context(dev);
+	nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0x00000000);
 	return 0;
 }
 
@@ -897,3 +701,136 @@ nv20_graph_isr(struct drm_device *dev)
 		}
 	}
 }
+
+static void
+nv20_graph_destroy(struct drm_device *dev, int engine)
+{
+	struct nv20_graph_engine *pgraph = nv_engine(dev, engine);
+
+	nouveau_irq_unregister(dev, 12);
+	nouveau_gpuobj_ref(NULL, &pgraph->ctxtab);
+
+	NVOBJ_ENGINE_DEL(dev, GR);
+	kfree(pgraph);
+}
+
+int
+nv20_graph_create(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv20_graph_engine *pgraph;
+	int ret;
+
+	pgraph = kzalloc(sizeof(*pgraph), GFP_KERNEL);
+	if (!pgraph)
+		return -ENOMEM;
+
+	pgraph->base.destroy = nv20_graph_destroy;
+	pgraph->base.fini = nv20_graph_fini;
+	pgraph->base.context_new = nv20_graph_context_new;
+	pgraph->base.context_del = nv20_graph_context_del;
+	pgraph->base.object_new = nv04_graph_object_new;
+
+	pgraph->grctx_user = 0x0028;
+	if (dev_priv->card_type == NV_20) {
+		pgraph->base.init = nv20_graph_init;
+		switch (dev_priv->chipset) {
+		case 0x20:
+			pgraph->grctx_init = nv20_graph_context_init;
+			pgraph->grctx_size = NV20_GRCTX_SIZE;
+			pgraph->grctx_user = 0x0000;
+			break;
+		case 0x25:
+		case 0x28:
+			pgraph->grctx_init = nv25_graph_context_init;
+			pgraph->grctx_size = NV25_GRCTX_SIZE;
+			break;
+		case 0x2a:
+			pgraph->grctx_init = nv2a_graph_context_init;
+			pgraph->grctx_size = NV2A_GRCTX_SIZE;
+			pgraph->grctx_user = 0x0000;
+			break;
+		default:
+			NV_ERROR(dev, "unknown nv20, disabling acceleration\n");
+			dev_priv->engine.graph.accel_blocked = true;
+			return 0;
+		}
+	} else {
+		pgraph->base.init = nv30_graph_init;
+		switch (dev_priv->chipset) {
+		case 0x30:
+		case 0x31:
+			pgraph->grctx_init = nv30_31_graph_context_init;
+			pgraph->grctx_size = NV30_31_GRCTX_SIZE;
+			break;
+		case 0x34:
+			pgraph->grctx_init = nv34_graph_context_init;
+			pgraph->grctx_size = NV34_GRCTX_SIZE;
+			break;
+		case 0x35:
+		case 0x36:
+			pgraph->grctx_init = nv35_36_graph_context_init;
+			pgraph->grctx_size = NV35_36_GRCTX_SIZE;
+			break;
+		default:
+			NV_ERROR(dev, "unknown nv30, disabling acceleration\n");
+			dev_priv->engine.graph.accel_blocked = true;
+			return 0;
+		}
+	}
+
+	/* Create Context Pointer Table */
+	ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16, NVOBJ_FLAG_ZERO_ALLOC,
+				 &pgraph->ctxtab);
+	if (ret) {
+		kfree(pgraph);
+		return ret;
+	}
+
+	NVOBJ_ENGINE_ADD(dev, GR, &pgraph->base);
+	nouveau_irq_register(dev, 12, nv20_graph_isr);
+
+	/* nvsw */
+	NVOBJ_CLASS(dev, 0x506e, SW);
+	NVOBJ_MTHD (dev, 0x506e, 0x0500, nv04_graph_mthd_page_flip);
+
+	NVOBJ_CLASS(dev, 0x0030, GR); /* null */
+	NVOBJ_CLASS(dev, 0x0039, GR); /* m2mf */
+	NVOBJ_CLASS(dev, 0x004a, GR); /* gdirect */
+	NVOBJ_CLASS(dev, 0x009f, GR); /* imageblit (nv12) */
+	NVOBJ_CLASS(dev, 0x008a, GR); /* ifc */
+	NVOBJ_CLASS(dev, 0x0089, GR); /* sifm */
+	NVOBJ_CLASS(dev, 0x0062, GR); /* surf2d */
+	NVOBJ_CLASS(dev, 0x0043, GR); /* rop */
+	NVOBJ_CLASS(dev, 0x0012, GR); /* beta1 */
+	NVOBJ_CLASS(dev, 0x0072, GR); /* beta4 */
+	NVOBJ_CLASS(dev, 0x0019, GR); /* cliprect */
+	NVOBJ_CLASS(dev, 0x0044, GR); /* pattern */
+	if (dev_priv->card_type == NV_20) {
+		NVOBJ_CLASS(dev, 0x009e, GR); /* swzsurf */
+		NVOBJ_CLASS(dev, 0x0096, GR); /* celcius */
+
+		/* kelvin */
+		if (dev_priv->chipset < 0x25)
+			NVOBJ_CLASS(dev, 0x0097, GR);
+		else
+			NVOBJ_CLASS(dev, 0x0597, GR);
+	} else {
+		NVOBJ_CLASS(dev, 0x038a, GR); /* ifc (nv30) */
+		NVOBJ_CLASS(dev, 0x0389, GR); /* sifm (nv30) */
+		NVOBJ_CLASS(dev, 0x0362, GR); /* surf2d (nv30) */
+		NVOBJ_CLASS(dev, 0x039e, GR); /* swzsurf */
+
+		/* rankine */
+		if (0x00000003 & (1 << (dev_priv->chipset & 0x0f)))
+			NVOBJ_CLASS(dev, 0x0397, GR);
+		else
+		if (0x00000010 & (1 << (dev_priv->chipset & 0x0f)))
+			NVOBJ_CLASS(dev, 0x0697, GR);
+		else
+		if (0x000001e0 & (1 << (dev_priv->chipset & 0x0f)))
+			NVOBJ_CLASS(dev, 0x0497, GR);
+	}
+
+	return 0;
+}