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

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

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

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

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

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

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

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 3b4e65d..92ecf50 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -27,12 +27,20 @@
 
 #include <core/device.h>
 #include <core/client.h>
+#include <core/gpuobj.h>
 #include <core/class.h>
 
 #include <subdev/device.h>
+#include <subdev/vm.h>
 
 #include "nouveau_drm.h"
+#include "nouveau_dma.h"
 #include "nouveau_agp.h"
+#include "nouveau_abi16.h"
+#include "nouveau_fbcon.h"
+#include "nouveau_fence.h"
+
+#include "nouveau_ttm.h"
 
 int __devinit nouveau_pci_probe(struct pci_dev *, const struct pci_device_id *);
 void nouveau_pci_remove(struct pci_dev *);
@@ -43,7 +51,6 @@
 
 int nouveau_load(struct drm_device *, unsigned long);
 int nouveau_unload(struct drm_device *);
-void *nouveau_newpriv(struct drm_device *);
 
 MODULE_PARM_DESC(config, "option string to pass to driver core");
 static char *nouveau_config;
@@ -53,6 +60,10 @@
 static char *nouveau_debug;
 module_param_named(debug, nouveau_debug, charp, 0400);
 
+MODULE_PARM_DESC(noaccel, "disable kernel/abi16 acceleration");
+static int nouveau_noaccel = 0;
+module_param_named(noaccel, nouveau_noaccel, int, 0400);
+
 static u64
 nouveau_name(struct pci_dev *pdev)
 {
@@ -82,17 +93,112 @@
 nouveau_cli_destroy(struct nouveau_cli *cli)
 {
 	struct nouveau_object *client = nv_object(cli);
+	nouveau_vm_ref(NULL, &cli->base.vm, NULL);
 	nouveau_client_fini(&cli->base, false);
 	atomic_set(&client->refcount, 1);
 	nouveau_object_ref(NULL, &client);
 }
 
+static void
+nouveau_accel_fini(struct nouveau_drm *drm)
+{
+	nouveau_gpuobj_ref(NULL, &drm->notify);
+	nouveau_channel_del(&drm->channel);
+	if (drm->fence)
+		nouveau_fence(drm)->dtor(drm);
+}
+
+static void
+nouveau_accel_init(struct nouveau_drm *drm)
+{
+	struct nouveau_device *device = nv_device(drm->device);
+	struct nouveau_object *object;
+	int ret;
+
+	if (nouveau_noaccel)
+		return;
+
+	/* initialise synchronisation routines */
+	if      (device->card_type < NV_10) ret = nv04_fence_create(drm);
+	else if (device->chipset   <  0x84) ret = nv10_fence_create(drm);
+	else if (device->card_type < NV_C0) ret = nv84_fence_create(drm);
+	else                                ret = nvc0_fence_create(drm);
+	if (ret) {
+		NV_ERROR(drm, "failed to initialise sync subsystem, %d\n", ret);
+		nouveau_accel_fini(drm);
+		return;
+	}
+
+	ret = nouveau_channel_new(drm, &drm->client, NVDRM_DEVICE, NVDRM_CHAN,
+				  NvDmaFB, NvDmaTT, &drm->channel);
+	if (ret) {
+		NV_ERROR(drm, "failed to create kernel channel, %d\n", ret);
+		nouveau_accel_fini(drm);
+		return;
+	}
+
+	if (device->card_type < NV_C0) {
+		ret = nouveau_gpuobj_new(drm->device, NULL, 32, 0, 0,
+					&drm->notify);
+		if (ret) {
+			NV_ERROR(drm, "failed to allocate notifier, %d\n", ret);
+			nouveau_accel_fini(drm);
+			return;
+		}
+
+		ret = nouveau_object_new(nv_object(drm),
+					 drm->channel->handle, NvNotify0,
+					 0x003d, &(struct nv_dma_class) {
+						.flags = NV_DMA_TARGET_VRAM |
+							 NV_DMA_ACCESS_RDWR,
+						.start = drm->notify->addr,
+						.limit = drm->notify->addr + 31
+						}, sizeof(struct nv_dma_class),
+					 &object);
+		if (ret) {
+			nouveau_accel_fini(drm);
+			return;
+		}
+	}
+
+
+	nouveau_bo_move_init(drm->channel);
+}
+
 static int __devinit
 nouveau_drm_probe(struct pci_dev *pdev, const struct pci_device_id *pent)
 {
 	struct nouveau_device *device;
+	struct apertures_struct *aper;
+	bool boot = false;
 	int ret;
 
+	/* remove conflicting drivers (vesafb, efifb etc) */
+	aper = alloc_apertures(3);
+	if (!aper)
+		return -ENOMEM;
+
+	aper->ranges[0].base = pci_resource_start(pdev, 1);
+	aper->ranges[0].size = pci_resource_len(pdev, 1);
+	aper->count = 1;
+
+	if (pci_resource_len(pdev, 2)) {
+		aper->ranges[aper->count].base = pci_resource_start(pdev, 2);
+		aper->ranges[aper->count].size = pci_resource_len(pdev, 2);
+		aper->count++;
+	}
+
+	if (pci_resource_len(pdev, 3)) {
+		aper->ranges[aper->count].base = pci_resource_start(pdev, 3);
+		aper->ranges[aper->count].size = pci_resource_len(pdev, 3);
+		aper->count++;
+	}
+
+#ifdef CONFIG_X86
+	boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
+#endif
+	remove_conflicting_framebuffers(aper, "nouveaufb", boot);
+
 	ret = nouveau_device_create(pdev, nouveau_name(pdev), pci_name(pdev),
 				    nouveau_config, nouveau_debug, &device);
 	if (ret)
@@ -102,7 +208,7 @@
 
 	ret = nouveau_pci_probe(pdev, pent);
 	if (ret) {
-		nouveau_device_destroy(&device);
+		nouveau_object_ref(NULL, (struct nouveau_object **)&device);
 		return ret;
 	}
 
@@ -113,6 +219,7 @@
 nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 {
 	struct pci_dev *pdev = dev->pdev;
+	struct nouveau_device *device;
 	struct nouveau_drm *drm;
 	int ret;
 
@@ -122,6 +229,7 @@
 		return ret;
 
 	INIT_LIST_HEAD(&drm->clients);
+	spin_lock_init(&drm->tile.lock);
 	drm->dev = dev;
 
 	/* make sure AGP controller is in a consistent state before we
@@ -142,7 +250,7 @@
 					 }, sizeof(struct nv_device_class),
 					 &drm->device);
 		if (ret)
-			return ret;
+			goto fail_device;
 
 		nouveau_agp_reset(drm);
 		nouveau_object_del(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE);
@@ -158,15 +266,32 @@
 	if (ret)
 		goto fail_device;
 
+	device = nv_device(drm->device);
+
 	/* initialise AGP */
 	nouveau_agp_init(drm);
 
-	ret = nouveau_load(dev, flags);
+	if (device->card_type >= NV_50) {
+		ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40),
+				     0x1000, &drm->client.base.vm);
+		if (ret)
+			goto fail_device;
+	}
+
+	ret = nouveau_ttm_init(drm);
 	if (ret)
 		goto fail_device;
 
+	ret = nouveau_load(dev, flags);
+	if (ret)
+		goto fail_load;
+
+	nouveau_accel_init(drm);
+	nouveau_fbcon_init(dev);
 	return 0;
 
+fail_load:
+	nouveau_ttm_fini(drm);
 fail_device:
 	nouveau_cli_destroy(&drm->client);
 	return ret;
@@ -179,10 +304,14 @@
 	struct pci_dev *pdev = dev->pdev;
 	int ret;
 
+	nouveau_fbcon_fini(dev);
+	nouveau_accel_fini(drm);
+
 	ret = nouveau_unload(dev);
 	if (ret)
 		return ret;
 
+	nouveau_ttm_fini(drm);
 	nouveau_agp_fini(drm);
 
 	pci_set_drvdata(pdev, drm->client.base.device);
@@ -193,10 +322,11 @@
 static void
 nouveau_drm_remove(struct pci_dev *pdev)
 {
-	struct nouveau_device *device;
+	struct nouveau_object *device;
 	nouveau_pci_remove(pdev);
 	device = pci_get_drvdata(pdev);
-	nouveau_device_destroy(&device);
+	nouveau_object_ref(NULL, &device);
+	nouveau_object_debug();
 }
 
 int
@@ -211,10 +341,23 @@
 	    pm_state.event == PM_EVENT_PRETHAW)
 		return 0;
 
+	NV_INFO(drm, "suspending fbcon...\n");
+	nouveau_fbcon_set_suspend(dev, 1);
+
+	NV_INFO(drm, "suspending drm...\n");
 	ret = nouveau_pci_suspend(pdev, pm_state);
 	if (ret)
 		return ret;
 
+	NV_INFO(drm, "evicting buffers...\n");
+	ttm_bo_evict_mm(&drm->ttm.bdev, TTM_PL_VRAM);
+
+	if (drm->fence && nouveau_fence(drm)->suspend) {
+		if (!nouveau_fence(drm)->suspend(drm))
+			return -ENOMEM;
+	}
+
+	NV_INFO(drm, "suspending client object trees...\n");
 	list_for_each_entry(cli, &drm->clients, head) {
 		ret = nouveau_client_fini(&cli->base, true);
 		if (ret)
@@ -255,6 +398,7 @@
 	if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
 		return 0;
 
+	NV_INFO(drm, "re-enabling device...\n");
 	pci_set_power_state(pdev, PCI_D0);
 	pci_restore_state(pdev);
 	ret = pci_enable_device(pdev);
@@ -264,17 +408,70 @@
 
 	nouveau_agp_reset(drm);
 
+	NV_INFO(drm, "resuming client object trees...\n");
 	nouveau_client_init(&drm->client.base);
+	nouveau_agp_init(drm);
 
 	list_for_each_entry(cli, &drm->clients, head) {
 		nouveau_client_init(&cli->base);
 	}
 
-	nouveau_agp_init(drm);
+	if (drm->fence && nouveau_fence(drm)->resume)
+		nouveau_fence(drm)->resume(drm);
 
 	return nouveau_pci_resume(pdev);
 }
 
+int
+nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv)
+{
+	struct pci_dev *pdev = dev->pdev;
+	struct nouveau_drm *drm = nouveau_drm(dev);
+	struct nouveau_cli *cli;
+	int ret;
+
+	ret = nouveau_cli_create(pdev, fpriv->pid, sizeof(*cli), (void **)&cli);
+	if (ret)
+		return ret;
+
+	if (nv_device(drm->device)->card_type >= NV_50) {
+		ret = nouveau_vm_new(nv_device(drm->device), 0, (1ULL << 40),
+				     0x1000, &cli->base.vm);
+		if (ret) {
+			nouveau_cli_destroy(cli);
+			return ret;
+		}
+	}
+
+	fpriv->driver_priv = cli;
+
+	mutex_lock(&drm->client.mutex);
+	list_add(&cli->head, &drm->clients);
+	mutex_unlock(&drm->client.mutex);
+	return 0;
+}
+
+void
+nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
+{
+	struct nouveau_cli *cli = nouveau_cli(fpriv);
+	struct nouveau_drm *drm = nouveau_drm(dev);
+
+	if (cli->abi16)
+		nouveau_abi16_fini(cli->abi16);
+
+	mutex_lock(&drm->client.mutex);
+	list_del(&cli->head);
+	mutex_unlock(&drm->client.mutex);
+}
+
+void
+nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
+{
+	struct nouveau_cli *cli = nouveau_cli(fpriv);
+	nouveau_cli_destroy(cli);
+}
+
 static struct pci_device_id
 nouveau_drm_pci_table[] = {
 	{