diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_drm.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drm.c | 211 |
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 3b4e65d5122..92ecf50a39d 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[] = { { |