summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nouveau_drm.c
diff options
context:
space:
mode:
authorBen Skeggs2012-07-20 00:17:34 +0200
committerBen Skeggs2012-10-03 05:12:56 +0200
commitebb945a94bba2ce8dff7b0942ff2b3f2a52a0a69 (patch)
tree07cad59be501458e6ae1304b7c0352e322ac3387 /drivers/gpu/drm/nouveau/nouveau_drm.c
parentdrm/nvc0-/gr: generate grctx template at init time, not first context ctor (diff)
downloadkernel-qcow2-linux-ebb945a94bba2ce8dff7b0942ff2b3f2a52a0a69.tar.gz
kernel-qcow2-linux-ebb945a94bba2ce8dff7b0942ff2b3f2a52a0a69.tar.xz
kernel-qcow2-linux-ebb945a94bba2ce8dff7b0942ff2b3f2a52a0a69.zip
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>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_drm.c')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c211
1 files changed, 204 insertions, 7 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 3b4e65d5122b..92ecf50a39d3 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 @@ void __exit nouveau_exit(struct pci_driver *);
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 @@ MODULE_PARM_DESC(debug, "debug string to pass to driver core");
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 @@ static void
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 @@ nouveau_drm_probe(struct pci_dev *pdev, const struct pci_device_id *pent)
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 @@ int
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 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
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 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
}, 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 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
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 @@ nouveau_drm_unload(struct drm_device *dev)
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 @@ nouveau_drm_unload(struct drm_device *dev)
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 @@ nouveau_drm_suspend(struct pci_dev *pdev, pm_message_t pm_state)
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 @@ nouveau_drm_resume(struct pci_dev *pdev)
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_drm_resume(struct pci_dev *pdev)
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[] = {
{