drm/nouveau: allow accelerated buffer moves even when gr isn't present
authorBen Skeggs <bskeggs@redhat.com>
Tue, 12 Feb 2019 12:28:13 +0000 (22:28 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Tue, 19 Feb 2019 22:59:59 +0000 (08:59 +1000)
There's no need to avoid using copy engines if gr init fails for some
reason (usually missing FW, or incomplete bring-up).

It's not terribly useful for an end-user, but it'll slightly speed up
suspend/resume when saving fb contents, and allow for host/ce code to
be validated.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_drm.c

index 50025e1ff857fe551a8b83be79ef099ed737b51b..c1e9069bd2ceef6955b35ded1d232f30247f8537 100644 (file)
@@ -282,18 +282,134 @@ done:
 }
 
 static void
-nouveau_accel_fini(struct nouveau_drm *drm)
+nouveau_accel_ce_fini(struct nouveau_drm *drm)
+{
+       nouveau_channel_idle(drm->cechan);
+       nvif_object_fini(&drm->ttm.copy);
+       nouveau_channel_del(&drm->cechan);
+}
+
+static void
+nouveau_accel_ce_init(struct nouveau_drm *drm)
+{
+       struct nvif_device *device = &drm->client.device;
+       int ret = 0;
+
+       /* Allocate channel that has access to a (preferably async) copy
+        * engine, to use for TTM buffer moves.
+        */
+       if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
+               ret = nouveau_channel_new(drm, device,
+                                         nvif_fifo_runlist_ce(device), 0,
+                                         true, &drm->cechan);
+       } else
+       if (device->info.chipset >= 0xa3 &&
+           device->info.chipset != 0xaa &&
+           device->info.chipset != 0xac) {
+               /* Prior to Kepler, there's only a single runlist, so all
+                * engines can be accessed from any channel.
+                *
+                * We still want to use a separate channel though.
+                */
+               ret = nouveau_channel_new(drm, device, NvDmaFB, NvDmaTT, false,
+                                         &drm->cechan);
+       }
+
+       if (ret)
+               NV_ERROR(drm, "failed to create ce channel, %d\n", ret);
+}
+
+static void
+nouveau_accel_gr_fini(struct nouveau_drm *drm)
 {
        nouveau_channel_idle(drm->channel);
        nvif_object_fini(&drm->ntfy);
        nvkm_gpuobj_del(&drm->notify);
        nvif_object_fini(&drm->nvsw);
        nouveau_channel_del(&drm->channel);
+}
 
-       nouveau_channel_idle(drm->cechan);
-       nvif_object_fini(&drm->ttm.copy);
-       nouveau_channel_del(&drm->cechan);
+static void
+nouveau_accel_gr_init(struct nouveau_drm *drm)
+{
+       struct nvif_device *device = &drm->client.device;
+       u32 arg0, arg1;
+       int ret;
+
+       /* Allocate channel that has access to the graphics engine. */
+       if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
+               arg0 = nvif_fifo_runlist(device, NV_DEVICE_INFO_ENGINE_GR);
+               arg1 = 1;
+       } else {
+               arg0 = NvDmaFB;
+               arg1 = NvDmaTT;
+       }
+
+       ret = nouveau_channel_new(drm, device, arg0, arg1, false,
+                                 &drm->channel);
+       if (ret) {
+               NV_ERROR(drm, "failed to create kernel channel, %d\n", ret);
+               nouveau_accel_gr_fini(drm);
+               return;
+       }
+
+       /* A SW class is used on pre-NV50 HW to assist with handling the
+        * synchronisation of page flips, as well as to implement fences
+        * on TNT/TNT2 HW that lacks any kind of support in host.
+        */
+       if (device->info.family < NV_DEVICE_INFO_V0_TESLA) {
+               ret = nvif_object_init(&drm->channel->user, NVDRM_NVSW,
+                                      nouveau_abi16_swclass(drm), NULL, 0,
+                                      &drm->nvsw);
+               if (ret == 0) {
+                       ret = RING_SPACE(drm->channel, 2);
+                       if (ret == 0) {
+                               BEGIN_NV04(drm->channel, NvSubSw, 0, 1);
+                               OUT_RING  (drm->channel, drm->nvsw.handle);
+                       }
+               }
+
+               if (ret) {
+                       NV_ERROR(drm, "failed to allocate sw class, %d\n", ret);
+                       nouveau_accel_gr_fini(drm);
+                       return;
+               }
+       }
+
+       /* NvMemoryToMemoryFormat requires a notifier ctxdma for some reason,
+        * even if notification is never requested, so, allocate a ctxdma on
+        * any GPU where it's possible we'll end up using M2MF for BO moves.
+        */
+       if (device->info.family < NV_DEVICE_INFO_V0_FERMI) {
+               ret = nvkm_gpuobj_new(nvxx_device(device), 32, 0, false, NULL,
+                                     &drm->notify);
+               if (ret) {
+                       NV_ERROR(drm, "failed to allocate notifier, %d\n", ret);
+                       nouveau_accel_gr_fini(drm);
+                       return;
+               }
+
+               ret = nvif_object_init(&drm->channel->user, NvNotify0,
+                                      NV_DMA_IN_MEMORY,
+                                      &(struct nv_dma_v0) {
+                                               .target = NV_DMA_V0_TARGET_VRAM,
+                                               .access = NV_DMA_V0_ACCESS_RDWR,
+                                               .start = drm->notify->addr,
+                                               .limit = drm->notify->addr + 31
+                                      }, sizeof(struct nv_dma_v0),
+                                      &drm->ntfy);
+               if (ret) {
+                       nouveau_accel_gr_fini(drm);
+                       return;
+               }
+       }
+}
 
+static void
+nouveau_accel_fini(struct nouveau_drm *drm)
+{
+       nouveau_accel_ce_fini(drm);
+       nouveau_accel_gr_fini(drm);
        if (drm->fence)
                nouveau_fence(drm)->dtor(drm);
 }
@@ -303,23 +419,16 @@ nouveau_accel_init(struct nouveau_drm *drm)
 {
        struct nvif_device *device = &drm->client.device;
        struct nvif_sclass *sclass;
-       u32 arg0, arg1;
        int ret, i, n;
 
        if (nouveau_noaccel)
                return;
 
+       /* Initialise global support for channels, and synchronisation. */
        ret = nouveau_channels_init(drm);
        if (ret)
                return;
 
-       if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_VOLTA) {
-               ret = nvif_user_init(device);
-               if (ret)
-                       return;
-       }
-
-       /* initialise synchronisation routines */
        /*XXX: this is crap, but the fence/channel stuff is a little
         *     backwards in some places.  this will be fixed.
         */
@@ -366,84 +475,18 @@ nouveau_accel_init(struct nouveau_drm *drm)
                return;
        }
 
-       if (device->info.family >= NV_DEVICE_INFO_V0_KEPLER) {
-               ret = nouveau_channel_new(drm, &drm->client.device,
-                                         nvif_fifo_runlist_ce(device), 0,
-                                         true, &drm->cechan);
-               if (ret)
-                       NV_ERROR(drm, "failed to create ce channel, %d\n", ret);
-
-               arg0 = nvif_fifo_runlist(device, NV_DEVICE_INFO_ENGINE_GR);
-               arg1 = 1;
-       } else
-       if (device->info.chipset >= 0xa3 &&
-           device->info.chipset != 0xaa &&
-           device->info.chipset != 0xac) {
-               ret = nouveau_channel_new(drm, &drm->client.device,
-                                         NvDmaFB, NvDmaTT, false,
-                                         &drm->cechan);
+       /* Volta requires access to a doorbell register for kickoff. */
+       if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_VOLTA) {
+               ret = nvif_user_init(device);
                if (ret)
-                       NV_ERROR(drm, "failed to create ce channel, %d\n", ret);
-
-               arg0 = NvDmaFB;
-               arg1 = NvDmaTT;
-       } else {
-               arg0 = NvDmaFB;
-               arg1 = NvDmaTT;
-       }
-
-       ret = nouveau_channel_new(drm, &drm->client.device,
-                                 arg0, arg1, false, &drm->channel);
-       if (ret) {
-               NV_ERROR(drm, "failed to create kernel channel, %d\n", ret);
-               nouveau_accel_fini(drm);
-               return;
-       }
-
-       if (device->info.family < NV_DEVICE_INFO_V0_TESLA) {
-               ret = nvif_object_init(&drm->channel->user, NVDRM_NVSW,
-                                      nouveau_abi16_swclass(drm), NULL, 0,
-                                      &drm->nvsw);
-               if (ret == 0) {
-                       ret = RING_SPACE(drm->channel, 2);
-                       if (ret == 0) {
-                               BEGIN_NV04(drm->channel, NvSubSw, 0, 1);
-                               OUT_RING  (drm->channel, drm->nvsw.handle);
-                       }
-               }
-
-               if (ret) {
-                       NV_ERROR(drm, "failed to allocate sw class, %d\n", ret);
-                       nouveau_accel_fini(drm);
-                       return;
-               }
-       }
-
-       if (device->info.family < NV_DEVICE_INFO_V0_FERMI) {
-               ret = nvkm_gpuobj_new(nvxx_device(&drm->client.device), 32, 0,
-                                     false, NULL, &drm->notify);
-               if (ret) {
-                       NV_ERROR(drm, "failed to allocate notifier, %d\n", ret);
-                       nouveau_accel_fini(drm);
-                       return;
-               }
-
-               ret = nvif_object_init(&drm->channel->user, NvNotify0,
-                                      NV_DMA_IN_MEMORY,
-                                      &(struct nv_dma_v0) {
-                                               .target = NV_DMA_V0_TARGET_VRAM,
-                                               .access = NV_DMA_V0_ACCESS_RDWR,
-                                               .start = drm->notify->addr,
-                                               .limit = drm->notify->addr + 31
-                                      }, sizeof(struct nv_dma_v0),
-                                      &drm->ntfy);
-               if (ret) {
-                       nouveau_accel_fini(drm);
                        return;
-               }
        }
 
+       /* Allocate channels we need to support various functions. */
+       nouveau_accel_gr_init(drm);
+       nouveau_accel_ce_init(drm);
 
+       /* Initialise accelerated TTM buffer moves. */
        nouveau_bo_move_init(drm);
 }