drm/nouveau/kms/nv50-: implement proper push buffer control logic
authorBen Skeggs <bskeggs@redhat.com>
Tue, 21 Jul 2020 01:34:07 +0000 (11:34 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 24 Jul 2020 08:50:56 +0000 (18:50 +1000)
We had a, what was supposed to be temporary, hack in the KMS code where we'd
completely drain an EVO/NVD channel's push buffer when wrapping to the start
again, instead of treating it as a ring buffer.

Let's fix that, finally.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
12 files changed:
drivers/gpu/drm/nouveau/dispnv50/disp.c
drivers/gpu/drm/nouveau/dispnv50/disp.h
drivers/gpu/drm/nouveau/include/nvif/object.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/coregf119.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/coregp102.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/coregv100.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/corenv50.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/dmacgf119.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/dmacgp102.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/dmacgv100.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/dmacnv50.c

index 368a2a9..0b3751c 100644 (file)
@@ -41,6 +41,8 @@
 #include <drm/drm_scdc_helper.h>
 #include <drm/drm_vblank.h>
 
+#include <nvif/push507c.h>
+
 #include <nvif/class.h>
 #include <nvif/cl0002.h>
 #include <nvif/cl5070.h>
@@ -48,6 +50,8 @@
 #include <nvif/event.h>
 #include <nvif/timer.h>
 
+#include <nvhw/class/cl507c.h>
+
 #include "nouveau_drv.h"
 #include "nouveau_dma.h"
 #include "nouveau_gem.h"
@@ -120,21 +124,93 @@ static void
 nv50_dmac_kick(struct nvif_push *push)
 {
        struct nv50_dmac *dmac = container_of(push, typeof(*dmac), _push);
-       evo_kick(push->cur, dmac);
-       push->bgn = push->cur = push->end;
+
+       dmac->cur = push->cur - (u32 *)dmac->_push.mem.object.map.ptr;
+       if (dmac->put != dmac->cur) {
+               /* Push buffer fetches are not coherent with BAR1, we need to ensure
+                * writes have been flushed right through to VRAM before writing PUT.
+                */
+               if (dmac->push->mem.type & NVIF_MEM_VRAM) {
+                       struct nvif_device *device = dmac->base.device;
+                       nvif_wr32(&device->object, 0x070000, 0x00000001);
+                       nvif_msec(device, 2000,
+                               if (!(nvif_rd32(&device->object, 0x070000) & 0x00000002))
+                                       break;
+                       );
+               }
+
+               NVIF_WV32(&dmac->base.user, NV507C, PUT, PTR, dmac->cur);
+               dmac->put = dmac->cur;
+       }
+
+       push->bgn = push->cur;
+}
+
+static int
+nv50_dmac_free(struct nv50_dmac *dmac)
+{
+       u32 get = NVIF_RV32(&dmac->base.user, NV507C, GET, PTR);
+       if (get > dmac->cur) /* NVIDIA stay 5 away from GET, do the same. */
+               return get - dmac->cur - 5;
+       return dmac->max - dmac->cur;
+}
+
+static int
+nv50_dmac_wind(struct nv50_dmac *dmac)
+{
+       /* Wait for GET to depart from the beginning of the push buffer to
+        * prevent writing PUT == GET, which would be ignored by HW.
+        */
+       u32 get = NVIF_RV32(&dmac->base.user, NV507C, GET, PTR);
+       if (get == 0) {
+               /* Corner-case, HW idle, but non-committed work pending. */
+               if (dmac->put == 0)
+                       nv50_dmac_kick(dmac->push);
+
+               if (nvif_msec(dmac->base.device, 2000,
+                       if (NVIF_TV32(&dmac->base.user, NV507C, GET, PTR, >, 0))
+                               break;
+               ) < 0)
+                       return -ETIMEDOUT;
+       }
+
+       PUSH_RSVD(dmac->push, PUSH_JUMP(dmac->push, 0));
+       dmac->cur = 0;
+       return 0;
 }
 
 static int
 nv50_dmac_wait(struct nvif_push *push, u32 size)
 {
        struct nv50_dmac *dmac = container_of(push, typeof(*dmac), _push);
-       u32 *ptr = evo_wait(dmac, size);
-       if (!ptr)
+       int free;
+
+       if (WARN_ON(size > dmac->max))
+               return -EINVAL;
+
+       dmac->cur = push->cur - (u32 *)dmac->_push.mem.object.map.ptr;
+       if (dmac->cur + size >= dmac->max) {
+               int ret = nv50_dmac_wind(dmac);
+               if (ret)
+                       return ret;
+
+               push->cur = dmac->_push.mem.object.map.ptr;
+               push->cur = push->cur + dmac->cur;
+               nv50_dmac_kick(push);
+       }
+
+       if (nvif_msec(dmac->base.device, 2000,
+               if ((free = nv50_dmac_free(dmac)) >= size)
+                       break;
+       ) < 0) {
+               WARN_ON(1);
                return -ETIMEDOUT;
+       }
 
-       push->bgn = ptr;
-       push->cur = ptr;
-       push->end = ptr + size;
+       push->bgn = dmac->_push.mem.object.map.ptr;
+       push->bgn = push->bgn + dmac->cur;
+       push->cur = push->bgn;
+       push->end = push->cur + free;
        return 0;
 }
 
@@ -171,6 +247,10 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
        dmac->_push.wait = nv50_dmac_wait;
        dmac->_push.kick = nv50_dmac_kick;
        dmac->push = &dmac->_push;
+       dmac->push->bgn = dmac->_push.mem.object.map.ptr;
+       dmac->push->cur = dmac->push->bgn;
+       dmac->push->end = dmac->push->bgn;
+       dmac->max = 0x1000/4 - 1;
 
        args->pushbuf = nvif_handle(&dmac->_push.mem.object);
 
@@ -209,69 +289,6 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
        return ret;
 }
 
-/******************************************************************************
- * EVO channel helpers
- *****************************************************************************/
-static void
-evo_flush(struct nv50_dmac *dmac)
-{
-       /* Push buffer fetches are not coherent with BAR1, we need to ensure
-        * writes have been flushed right through to VRAM before writing PUT.
-        */
-       if (dmac->push->mem.type & NVIF_MEM_VRAM) {
-               struct nvif_device *device = dmac->base.device;
-               nvif_wr32(&device->object, 0x070000, 0x00000001);
-               nvif_msec(device, 2000,
-                       if (!(nvif_rd32(&device->object, 0x070000) & 0x00000002))
-                               break;
-               );
-       }
-}
-
-u32 *
-evo_wait(struct nv50_dmac *evoc, int nr)
-{
-       struct nv50_dmac *dmac = evoc;
-       struct nvif_device *device = dmac->base.device;
-       u32 put;
-
-       if (dmac->push->cur != dmac->push->bgn)
-               PUSH_KICK(dmac->push);
-
-       put = nvif_rd32(&dmac->base.user, 0x0000) / 4;
-
-       mutex_lock(&dmac->lock);
-       if (put + nr >= (PAGE_SIZE / 4) - 8) {
-               dmac->ptr[put] = 0x20000000;
-               evo_flush(dmac);
-
-               nvif_wr32(&dmac->base.user, 0x0000, 0x00000000);
-               if (nvif_msec(device, 2000,
-                       if (!nvif_rd32(&dmac->base.user, 0x0004))
-                               break;
-               ) < 0) {
-                       mutex_unlock(&dmac->lock);
-                       pr_err("nouveau: evo channel stalled\n");
-                       return NULL;
-               }
-
-               put = 0;
-       }
-
-       return dmac->ptr + put;
-}
-
-void
-evo_kick(u32 *push, struct nv50_dmac *evoc)
-{
-       struct nv50_dmac *dmac = evoc;
-
-       evo_flush(dmac);
-
-       nvif_wr32(&dmac->base.user, 0x0000, (push - dmac->ptr) << 2);
-       mutex_unlock(&dmac->lock);
-}
-
 /******************************************************************************
  * Output path helpers
  *****************************************************************************/
index fbf573a..92bddc0 100644 (file)
@@ -73,6 +73,10 @@ struct nv50_dmac {
         * grabbed by evo_wait (if the pushbuf reservation is successful) and
         * dropped again by evo_kick. */
        struct mutex lock;
+
+       u32 cur;
+       u32 put;
+       u32 max;
 };
 
 struct nv50_outp_atom {
index a39d2bf..1e4c158 100644 (file)
@@ -116,6 +116,19 @@ struct nvif_mclass {
        _cid;                                                                  \
 })
 
+#define NVIF_RD32_(p,o,dr)   nvif_rd32((p), (o) + (dr))
+#define NVIF_WR32_(p,o,dr,f) nvif_wr32((p), (o) + (dr), (f))
+#define NVIF_RD32(p,A...) DRF_RD(NVIF_RD32_,                  (p), 0, ##A)
+#define NVIF_RV32(p,A...) DRF_RV(NVIF_RD32_,                  (p), 0, ##A)
+#define NVIF_TV32(p,A...) DRF_TV(NVIF_RD32_,                  (p), 0, ##A)
+#define NVIF_TD32(p,A...) DRF_TD(NVIF_RD32_,                  (p), 0, ##A)
+#define NVIF_WR32(p,A...) DRF_WR(            NVIF_WR32_,      (p), 0, ##A)
+#define NVIF_WV32(p,A...) DRF_WV(            NVIF_WR32_,      (p), 0, ##A)
+#define NVIF_WD32(p,A...) DRF_WD(            NVIF_WR32_,      (p), 0, ##A)
+#define NVIF_MR32(p,A...) DRF_MR(NVIF_RD32_, NVIF_WR32_, u32, (p), 0, ##A)
+#define NVIF_MV32(p,A...) DRF_MV(NVIF_RD32_, NVIF_WR32_, u32, (p), 0, ##A)
+#define NVIF_MD32(p,A...) DRF_MD(NVIF_RD32_, NVIF_WR32_, u32, (p), 0, ##A)
+
 /*XXX*/
 #include <core/object.h>
 #define nvxx_object(a) ({                                                      \
index e55054b..9cf2cfe 100644 (file)
@@ -21,6 +21,8 @@ struct nv50_disp_chan {
 
        struct nvkm_memory *memory;
        u64 push;
+
+       u32 suspend_put;
 };
 
 struct nv50_disp_chan_func {
index d162b9c..689e3cd 100644 (file)
@@ -182,6 +182,8 @@ gf119_disp_core_fini(struct nv50_disp_chan *chan)
                nvkm_error(subdev, "core fini: %08x\n",
                           nvkm_rd32(device, 0x610490));
        }
+
+       chan->suspend_put = nvkm_rd32(device, 0x640000);
 }
 
 static int
@@ -195,7 +197,7 @@ gf119_disp_core_init(struct nv50_disp_chan *chan)
        nvkm_wr32(device, 0x610498, 0x00010000);
        nvkm_wr32(device, 0x61049c, 0x00000001);
        nvkm_mask(device, 0x610490, 0x00000010, 0x00000010);
-       nvkm_wr32(device, 0x640000, 0x00000000);
+       nvkm_wr32(device, 0x640000, chan->suspend_put);
        nvkm_wr32(device, 0x610490, 0x01000013);
 
        /* wait for it to go inactive */
index 5b7f993..1b435be 100644 (file)
@@ -36,7 +36,7 @@ gp102_disp_core_init(struct nv50_disp_chan *chan)
        nvkm_wr32(device, 0x611498, 0x00010000);
        nvkm_wr32(device, 0x61149c, 0x00000001);
        nvkm_mask(device, 0x610490, 0x00000010, 0x00000010);
-       nvkm_wr32(device, 0x640000, 0x00000000);
+       nvkm_wr32(device, 0x640000, chan->suspend_put);
        nvkm_wr32(device, 0x610490, 0x01000013);
 
        /* wait for it to go inactive */
index 4592d0e..e20a48f 100644 (file)
@@ -167,6 +167,7 @@ gv100_disp_core_fini(struct nv50_disp_chan *chan)
        nvkm_mask(device, 0x6104e0, 0x00000010, 0x00000000);
        gv100_disp_core_idle(chan);
        nvkm_mask(device, 0x6104e0, 0x00000002, 0x00000000);
+       chan->suspend_put = nvkm_rd32(device, 0x680000);
 }
 
 static int
@@ -181,7 +182,7 @@ gv100_disp_core_init(struct nv50_disp_chan *chan)
        nvkm_wr32(device, 0x610b2c, 0x00000040);
 
        nvkm_mask(device, 0x6104e0, 0x00000010, 0x00000010);
-       nvkm_wr32(device, 0x680000, 0x00000000);
+       nvkm_wr32(device, 0x680000, chan->suspend_put);
        nvkm_wr32(device, 0x6104e0, 0x00000013);
        return gv100_disp_core_idle(chan);
 }
index 55db9a2..660310b 100644 (file)
@@ -179,6 +179,8 @@ nv50_disp_core_fini(struct nv50_disp_chan *chan)
                nvkm_error(subdev, "core fini: %08x\n",
                           nvkm_rd32(device, 0x610200));
        }
+
+       chan->suspend_put = nvkm_rd32(device, 0x640000);
 }
 
 static int
@@ -198,7 +200,7 @@ nv50_disp_core_init(struct nv50_disp_chan *chan)
        nvkm_wr32(device, 0x610208, 0x00010000);
        nvkm_wr32(device, 0x61020c, 0x00000000);
        nvkm_mask(device, 0x610200, 0x00000010, 0x00000010);
-       nvkm_wr32(device, 0x640000, 0x00000000);
+       nvkm_wr32(device, 0x640000, chan->suspend_put);
        nvkm_wr32(device, 0x610200, 0x01000013);
 
        /* wait for it to go inactive */
index edf7dd0..76425e8 100644 (file)
@@ -53,6 +53,8 @@ gf119_disp_dmac_fini(struct nv50_disp_chan *chan)
                nvkm_error(subdev, "ch %d fini: %08x\n", user,
                           nvkm_rd32(device, 0x610490 + (ctrl * 0x10)));
        }
+
+       chan->suspend_put = nvkm_rd32(device, 0x640000 + (ctrl * 0x1000));
 }
 
 static int
@@ -68,7 +70,7 @@ gf119_disp_dmac_init(struct nv50_disp_chan *chan)
        nvkm_wr32(device, 0x610498 + (ctrl * 0x0010), 0x00010000);
        nvkm_wr32(device, 0x61049c + (ctrl * 0x0010), 0x00000001);
        nvkm_mask(device, 0x610490 + (ctrl * 0x0010), 0x00000010, 0x00000010);
-       nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), 0x00000000);
+       nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), chan->suspend_put);
        nvkm_wr32(device, 0x610490 + (ctrl * 0x0010), 0x00000013);
 
        /* wait for it to go inactive */
index f21a433..da258df 100644 (file)
@@ -38,7 +38,7 @@ gp102_disp_dmac_init(struct nv50_disp_chan *chan)
        nvkm_wr32(device, 0x611498 + (ctrl * 0x0010), 0x00010000);
        nvkm_wr32(device, 0x61149c + (ctrl * 0x0010), 0x00000001);
        nvkm_mask(device, 0x610490 + (ctrl * 0x0010), 0x00000010, 0x00000010);
-       nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), 0x00000000);
+       nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), chan->suspend_put);
        nvkm_wr32(device, 0x610490 + (ctrl * 0x0010), 0x00000013);
 
        /* wait for it to go inactive */
index eac0e42..fdb624a 100644 (file)
@@ -50,10 +50,12 @@ void
 gv100_disp_dmac_fini(struct nv50_disp_chan *chan)
 {
        struct nvkm_device *device = chan->disp->base.engine.subdev.device;
+       const u32 uoff = (chan->chid.ctrl - 1) * 0x1000;
        const u32 coff = chan->chid.ctrl * 0x04;
        nvkm_mask(device, 0x6104e0 + coff, 0x00000010, 0x00000000);
        gv100_disp_dmac_idle(chan);
        nvkm_mask(device, 0x6104e0 + coff, 0x00000002, 0x00000000);
+       chan->suspend_put = nvkm_rd32(device, 0x690000 + uoff);
 }
 
 int
@@ -71,7 +73,7 @@ gv100_disp_dmac_init(struct nv50_disp_chan *chan)
        nvkm_wr32(device, 0x610b2c + poff, 0x00000040);
 
        nvkm_mask(device, 0x6104e0 + coff, 0x00000010, 0x00000010);
-       nvkm_wr32(device, 0x690000 + uoff, 0x00000000);
+       nvkm_wr32(device, 0x690000 + uoff, chan->suspend_put);
        nvkm_wr32(device, 0x6104e0 + coff, 0x00000013);
        return gv100_disp_dmac_idle(chan);
 }
index 9e8a9d7..d0a7da9 100644 (file)
@@ -94,6 +94,8 @@ nv50_disp_dmac_fini(struct nv50_disp_chan *chan)
                nvkm_error(subdev, "ch %d fini timeout, %08x\n", user,
                           nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));
        }
+
+       chan->suspend_put = nvkm_rd32(device, 0x640000 + (ctrl * 0x1000));
 }
 
 static int
@@ -109,7 +111,7 @@ nv50_disp_dmac_init(struct nv50_disp_chan *chan)
        nvkm_wr32(device, 0x610208 + (ctrl * 0x0010), 0x00010000);
        nvkm_wr32(device, 0x61020c + (ctrl * 0x0010), ctrl);
        nvkm_mask(device, 0x610200 + (ctrl * 0x0010), 0x00000010, 0x00000010);
-       nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), 0x00000000);
+       nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), chan->suspend_put);
        nvkm_wr32(device, 0x610200 + (ctrl * 0x0010), 0x00000013);
 
        /* wait for it to go inactive */