Commit 0a960996 authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/kms/nv50-: implement proper push buffer control logic

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: default avatarBen Skeggs <bskeggs@redhat.com>
parent ae09163a
...@@ -41,6 +41,8 @@ ...@@ -41,6 +41,8 @@
#include <drm/drm_scdc_helper.h> #include <drm/drm_scdc_helper.h>
#include <drm/drm_vblank.h> #include <drm/drm_vblank.h>
#include <nvif/push507c.h>
#include <nvif/class.h> #include <nvif/class.h>
#include <nvif/cl0002.h> #include <nvif/cl0002.h>
#include <nvif/cl5070.h> #include <nvif/cl5070.h>
...@@ -48,6 +50,8 @@ ...@@ -48,6 +50,8 @@
#include <nvif/event.h> #include <nvif/event.h>
#include <nvif/timer.h> #include <nvif/timer.h>
#include <nvhw/class/cl507c.h>
#include "nouveau_drv.h" #include "nouveau_drv.h"
#include "nouveau_dma.h" #include "nouveau_dma.h"
#include "nouveau_gem.h" #include "nouveau_gem.h"
...@@ -120,21 +124,93 @@ static void ...@@ -120,21 +124,93 @@ static void
nv50_dmac_kick(struct nvif_push *push) nv50_dmac_kick(struct nvif_push *push)
{ {
struct nv50_dmac *dmac = container_of(push, typeof(*dmac), _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 static int
nv50_dmac_wait(struct nvif_push *push, u32 size) nv50_dmac_wait(struct nvif_push *push, u32 size)
{ {
struct nv50_dmac *dmac = container_of(push, typeof(*dmac), _push); struct nv50_dmac *dmac = container_of(push, typeof(*dmac), _push);
u32 *ptr = evo_wait(dmac, size); int free;
if (!ptr)
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; return -ETIMEDOUT;
}
push->bgn = ptr; push->bgn = dmac->_push.mem.object.map.ptr;
push->cur = ptr; push->bgn = push->bgn + dmac->cur;
push->end = ptr + size; push->cur = push->bgn;
push->end = push->cur + free;
return 0; return 0;
} }
...@@ -171,6 +247,10 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, ...@@ -171,6 +247,10 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
dmac->_push.wait = nv50_dmac_wait; dmac->_push.wait = nv50_dmac_wait;
dmac->_push.kick = nv50_dmac_kick; dmac->_push.kick = nv50_dmac_kick;
dmac->push = &dmac->_push; 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); args->pushbuf = nvif_handle(&dmac->_push.mem.object);
...@@ -209,69 +289,6 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, ...@@ -209,69 +289,6 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
return ret; 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 * Output path helpers
*****************************************************************************/ *****************************************************************************/
......
...@@ -73,6 +73,10 @@ struct nv50_dmac { ...@@ -73,6 +73,10 @@ struct nv50_dmac {
* grabbed by evo_wait (if the pushbuf reservation is successful) and * grabbed by evo_wait (if the pushbuf reservation is successful) and
* dropped again by evo_kick. */ * dropped again by evo_kick. */
struct mutex lock; struct mutex lock;
u32 cur;
u32 put;
u32 max;
}; };
struct nv50_outp_atom { struct nv50_outp_atom {
......
...@@ -116,6 +116,19 @@ struct nvif_mclass { ...@@ -116,6 +116,19 @@ struct nvif_mclass {
_cid; \ _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*/ /*XXX*/
#include <core/object.h> #include <core/object.h>
#define nvxx_object(a) ({ \ #define nvxx_object(a) ({ \
......
...@@ -21,6 +21,8 @@ struct nv50_disp_chan { ...@@ -21,6 +21,8 @@ struct nv50_disp_chan {
struct nvkm_memory *memory; struct nvkm_memory *memory;
u64 push; u64 push;
u32 suspend_put;
}; };
struct nv50_disp_chan_func { struct nv50_disp_chan_func {
......
...@@ -182,6 +182,8 @@ gf119_disp_core_fini(struct nv50_disp_chan *chan) ...@@ -182,6 +182,8 @@ gf119_disp_core_fini(struct nv50_disp_chan *chan)
nvkm_error(subdev, "core fini: %08x\n", nvkm_error(subdev, "core fini: %08x\n",
nvkm_rd32(device, 0x610490)); nvkm_rd32(device, 0x610490));
} }
chan->suspend_put = nvkm_rd32(device, 0x640000);
} }
static int static int
...@@ -195,7 +197,7 @@ gf119_disp_core_init(struct nv50_disp_chan *chan) ...@@ -195,7 +197,7 @@ gf119_disp_core_init(struct nv50_disp_chan *chan)
nvkm_wr32(device, 0x610498, 0x00010000); nvkm_wr32(device, 0x610498, 0x00010000);
nvkm_wr32(device, 0x61049c, 0x00000001); nvkm_wr32(device, 0x61049c, 0x00000001);
nvkm_mask(device, 0x610490, 0x00000010, 0x00000010); nvkm_mask(device, 0x610490, 0x00000010, 0x00000010);
nvkm_wr32(device, 0x640000, 0x00000000); nvkm_wr32(device, 0x640000, chan->suspend_put);
nvkm_wr32(device, 0x610490, 0x01000013); nvkm_wr32(device, 0x610490, 0x01000013);
/* wait for it to go inactive */ /* wait for it to go inactive */
......
...@@ -36,7 +36,7 @@ gp102_disp_core_init(struct nv50_disp_chan *chan) ...@@ -36,7 +36,7 @@ gp102_disp_core_init(struct nv50_disp_chan *chan)
nvkm_wr32(device, 0x611498, 0x00010000); nvkm_wr32(device, 0x611498, 0x00010000);
nvkm_wr32(device, 0x61149c, 0x00000001); nvkm_wr32(device, 0x61149c, 0x00000001);
nvkm_mask(device, 0x610490, 0x00000010, 0x00000010); nvkm_mask(device, 0x610490, 0x00000010, 0x00000010);
nvkm_wr32(device, 0x640000, 0x00000000); nvkm_wr32(device, 0x640000, chan->suspend_put);
nvkm_wr32(device, 0x610490, 0x01000013); nvkm_wr32(device, 0x610490, 0x01000013);
/* wait for it to go inactive */ /* wait for it to go inactive */
......
...@@ -167,6 +167,7 @@ gv100_disp_core_fini(struct nv50_disp_chan *chan) ...@@ -167,6 +167,7 @@ gv100_disp_core_fini(struct nv50_disp_chan *chan)
nvkm_mask(device, 0x6104e0, 0x00000010, 0x00000000); nvkm_mask(device, 0x6104e0, 0x00000010, 0x00000000);
gv100_disp_core_idle(chan); gv100_disp_core_idle(chan);
nvkm_mask(device, 0x6104e0, 0x00000002, 0x00000000); nvkm_mask(device, 0x6104e0, 0x00000002, 0x00000000);
chan->suspend_put = nvkm_rd32(device, 0x680000);
} }
static int static int
...@@ -181,7 +182,7 @@ gv100_disp_core_init(struct nv50_disp_chan *chan) ...@@ -181,7 +182,7 @@ gv100_disp_core_init(struct nv50_disp_chan *chan)
nvkm_wr32(device, 0x610b2c, 0x00000040); nvkm_wr32(device, 0x610b2c, 0x00000040);
nvkm_mask(device, 0x6104e0, 0x00000010, 0x00000010); nvkm_mask(device, 0x6104e0, 0x00000010, 0x00000010);
nvkm_wr32(device, 0x680000, 0x00000000); nvkm_wr32(device, 0x680000, chan->suspend_put);
nvkm_wr32(device, 0x6104e0, 0x00000013); nvkm_wr32(device, 0x6104e0, 0x00000013);
return gv100_disp_core_idle(chan); return gv100_disp_core_idle(chan);
} }
......
...@@ -179,6 +179,8 @@ nv50_disp_core_fini(struct nv50_disp_chan *chan) ...@@ -179,6 +179,8 @@ nv50_disp_core_fini(struct nv50_disp_chan *chan)
nvkm_error(subdev, "core fini: %08x\n", nvkm_error(subdev, "core fini: %08x\n",
nvkm_rd32(device, 0x610200)); nvkm_rd32(device, 0x610200));
} }
chan->suspend_put = nvkm_rd32(device, 0x640000);
} }
static int static int
...@@ -198,7 +200,7 @@ nv50_disp_core_init(struct nv50_disp_chan *chan) ...@@ -198,7 +200,7 @@ nv50_disp_core_init(struct nv50_disp_chan *chan)
nvkm_wr32(device, 0x610208, 0x00010000); nvkm_wr32(device, 0x610208, 0x00010000);
nvkm_wr32(device, 0x61020c, 0x00000000); nvkm_wr32(device, 0x61020c, 0x00000000);
nvkm_mask(device, 0x610200, 0x00000010, 0x00000010); nvkm_mask(device, 0x610200, 0x00000010, 0x00000010);
nvkm_wr32(device, 0x640000, 0x00000000); nvkm_wr32(device, 0x640000, chan->suspend_put);
nvkm_wr32(device, 0x610200, 0x01000013); nvkm_wr32(device, 0x610200, 0x01000013);
/* wait for it to go inactive */ /* wait for it to go inactive */
......
...@@ -53,6 +53,8 @@ gf119_disp_dmac_fini(struct nv50_disp_chan *chan) ...@@ -53,6 +53,8 @@ gf119_disp_dmac_fini(struct nv50_disp_chan *chan)
nvkm_error(subdev, "ch %d fini: %08x\n", user, nvkm_error(subdev, "ch %d fini: %08x\n", user,
nvkm_rd32(device, 0x610490 + (ctrl * 0x10))); nvkm_rd32(device, 0x610490 + (ctrl * 0x10)));
} }
chan->suspend_put = nvkm_rd32(device, 0x640000 + (ctrl * 0x1000));
} }
static int static int
...@@ -68,7 +70,7 @@ gf119_disp_dmac_init(struct nv50_disp_chan *chan) ...@@ -68,7 +70,7 @@ gf119_disp_dmac_init(struct nv50_disp_chan *chan)
nvkm_wr32(device, 0x610498 + (ctrl * 0x0010), 0x00010000); nvkm_wr32(device, 0x610498 + (ctrl * 0x0010), 0x00010000);
nvkm_wr32(device, 0x61049c + (ctrl * 0x0010), 0x00000001); nvkm_wr32(device, 0x61049c + (ctrl * 0x0010), 0x00000001);
nvkm_mask(device, 0x610490 + (ctrl * 0x0010), 0x00000010, 0x00000010); 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); nvkm_wr32(device, 0x610490 + (ctrl * 0x0010), 0x00000013);
/* wait for it to go inactive */ /* wait for it to go inactive */
......
...@@ -38,7 +38,7 @@ gp102_disp_dmac_init(struct nv50_disp_chan *chan) ...@@ -38,7 +38,7 @@ gp102_disp_dmac_init(struct nv50_disp_chan *chan)
nvkm_wr32(device, 0x611498 + (ctrl * 0x0010), 0x00010000); nvkm_wr32(device, 0x611498 + (ctrl * 0x0010), 0x00010000);
nvkm_wr32(device, 0x61149c + (ctrl * 0x0010), 0x00000001); nvkm_wr32(device, 0x61149c + (ctrl * 0x0010), 0x00000001);
nvkm_mask(device, 0x610490 + (ctrl * 0x0010), 0x00000010, 0x00000010); 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); nvkm_wr32(device, 0x610490 + (ctrl * 0x0010), 0x00000013);
/* wait for it to go inactive */ /* wait for it to go inactive */
......
...@@ -50,10 +50,12 @@ void ...@@ -50,10 +50,12 @@ void
gv100_disp_dmac_fini(struct nv50_disp_chan *chan) gv100_disp_dmac_fini(struct nv50_disp_chan *chan)
{ {
struct nvkm_device *device = chan->disp->base.engine.subdev.device; 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; const u32 coff = chan->chid.ctrl * 0x04;
nvkm_mask(device, 0x6104e0 + coff, 0x00000010, 0x00000000); nvkm_mask(device, 0x6104e0 + coff, 0x00000010, 0x00000000);
gv100_disp_dmac_idle(chan); gv100_disp_dmac_idle(chan);
nvkm_mask(device, 0x6104e0 + coff, 0x00000002, 0x00000000); nvkm_mask(device, 0x6104e0 + coff, 0x00000002, 0x00000000);
chan->suspend_put = nvkm_rd32(device, 0x690000 + uoff);
} }
int int
...@@ -71,7 +73,7 @@ gv100_disp_dmac_init(struct nv50_disp_chan *chan) ...@@ -71,7 +73,7 @@ gv100_disp_dmac_init(struct nv50_disp_chan *chan)
nvkm_wr32(device, 0x610b2c + poff, 0x00000040); nvkm_wr32(device, 0x610b2c + poff, 0x00000040);
nvkm_mask(device, 0x6104e0 + coff, 0x00000010, 0x00000010); 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); nvkm_wr32(device, 0x6104e0 + coff, 0x00000013);
return gv100_disp_dmac_idle(chan); return gv100_disp_dmac_idle(chan);
} }
...@@ -94,6 +94,8 @@ nv50_disp_dmac_fini(struct nv50_disp_chan *chan) ...@@ -94,6 +94,8 @@ nv50_disp_dmac_fini(struct nv50_disp_chan *chan)
nvkm_error(subdev, "ch %d fini timeout, %08x\n", user, nvkm_error(subdev, "ch %d fini timeout, %08x\n", user,
nvkm_rd32(device, 0x610200 + (ctrl * 0x10))); nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));
} }
chan->suspend_put = nvkm_rd32(device, 0x640000 + (ctrl * 0x1000));
} }
static int static int
...@@ -109,7 +111,7 @@ nv50_disp_dmac_init(struct nv50_disp_chan *chan) ...@@ -109,7 +111,7 @@ nv50_disp_dmac_init(struct nv50_disp_chan *chan)
nvkm_wr32(device, 0x610208 + (ctrl * 0x0010), 0x00010000); nvkm_wr32(device, 0x610208 + (ctrl * 0x0010), 0x00010000);
nvkm_wr32(device, 0x61020c + (ctrl * 0x0010), ctrl); nvkm_wr32(device, 0x61020c + (ctrl * 0x0010), ctrl);
nvkm_mask(device, 0x610200 + (ctrl * 0x0010), 0x00000010, 0x00000010); 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); nvkm_wr32(device, 0x610200 + (ctrl * 0x0010), 0x00000013);
/* wait for it to go inactive */ /* wait for it to go inactive */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment