drm/nouveau: implement devinit subdev, and new init table parser

v2:
- make sure not to execute display scripts unless resuming

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index c72d426..3b4e65d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -32,6 +32,7 @@
 #include <subdev/device.h>
 
 #include "nouveau_drm.h"
+#include "nouveau_agp.h"
 
 int __devinit nouveau_pci_probe(struct pci_dev *, const struct pci_device_id *);
 void nouveau_pci_remove(struct pci_dev *);
@@ -123,6 +124,30 @@
 	INIT_LIST_HEAD(&drm->clients);
 	drm->dev = dev;
 
+	/* make sure AGP controller is in a consistent state before we
+	 * (possibly) execute vbios init tables (see nouveau_agp.h)
+	 */
+	if (drm_pci_device_is_agp(dev) && dev->agp) {
+		/* dummy device object, doesn't init anything, but allows
+		 * agp code access to registers
+		 */
+		ret = nouveau_object_new(nv_object(drm), NVDRM_CLIENT,
+					 NVDRM_DEVICE, 0x0080,
+					 &(struct nv_device_class) {
+						.device = ~0,
+						.disable =
+						 ~(NV_DEVICE_DISABLE_MMIO |
+						   NV_DEVICE_DISABLE_IDENTIFY),
+						.debug0 = ~0,
+					 }, sizeof(struct nv_device_class),
+					 &drm->device);
+		if (ret)
+			return ret;
+
+		nouveau_agp_reset(drm);
+		nouveau_object_del(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE);
+	}
+
 	ret = nouveau_object_new(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE,
 				 0x0080, &(struct nv_device_class) {
 					.device = ~0,
@@ -133,6 +158,9 @@
 	if (ret)
 		goto fail_device;
 
+	/* initialise AGP */
+	nouveau_agp_init(drm);
+
 	ret = nouveau_load(dev, flags);
 	if (ret)
 		goto fail_device;
@@ -155,6 +183,8 @@
 	if (ret)
 		return ret;
 
+	nouveau_agp_fini(drm);
+
 	pci_set_drvdata(pdev, drm->client.base.device);
 	nouveau_cli_destroy(&drm->client);
 	return 0;
@@ -195,6 +225,8 @@
 	if (ret)
 		goto fail_client;
 
+	nouveau_agp_fini(drm);
+
 	pci_save_state(pdev);
 	if (pm_state.event == PM_EVENT_SUSPEND) {
 		pci_disable_device(pdev);
@@ -230,12 +262,16 @@
 		return ret;
 	pci_set_master(pdev);
 
+	nouveau_agp_reset(drm);
+
 	nouveau_client_init(&drm->client.base);
 
 	list_for_each_entry(cli, &drm->clients, head) {
 		nouveau_client_init(&cli->base);
 	}
 
+	nouveau_agp_init(drm);
+
 	return nouveau_pci_resume(pdev);
 }