summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandre Courbot <acourbot@nvidia.com>2015-04-27 17:25:11 +0900
committerAlexandre Courbot <acourbot@nvidia.com>2015-06-08 17:42:27 +0900
commita2c4aac405411b69913daf09cf4450f037d9e5fb (patch)
treeb0d9f549afea18fe1dc4d8677d39d73193ea195d
parent16942d593eb931b81f617a4b1a57fa9211ae2f92 (diff)
downloadnouveau-tegra-fixes.tar.gz
gr/gf100: wait for GR idle after GO_IDLE bundletegra-fixes
After submitting a GO_IDLE bundle, one must wait for GR to effectively be idle before submitting the next bundle. Failure to do so may result in undefined behavior in some rare cases. Signed-off-by: Alexandre Courbot <acourbot@nvidia.com> Reported-by: Kary Jin <karyj@nvidia.com>
-rw-r--r--drm/nouveau/nvkm/engine/gr/gf100.c37
-rw-r--r--drm/nouveau/nvkm/engine/gr/gf100.h1
2 files changed, 38 insertions, 0 deletions
diff --git a/drm/nouveau/nvkm/engine/gr/gf100.c b/drm/nouveau/nvkm/engine/gr/gf100.c
index 01efc2c96..ca11ddb6e 100644
--- a/drm/nouveau/nvkm/engine/gr/gf100.c
+++ b/drm/nouveau/nvkm/engine/gr/gf100.c
@@ -663,6 +663,37 @@ gf100_gr_zbc_init(struct gf100_gr_priv *priv)
gf100_gr_zbc_clear_depth(priv, index);
}
+/**
+ * Wait until GR goes idle. GR is considered idle if it is disabled by the
+ * MC (0x200) register, or GR is not busy and a context switch is not in
+ * progress.
+ */
+int
+gf100_gr_wait_idle(struct gf100_gr_priv *priv)
+{
+ unsigned long end_jiffies = jiffies + msecs_to_jiffies(2000);
+ bool gr_enabled, ctxsw_active, gr_busy;
+
+ do {
+ /*
+ * required to make sure FIFO_ENGINE_STATUS (0x2640) is
+ * up-to-date
+ */
+ nv_rd32(priv, 0x400700);
+
+ gr_enabled = nv_rd32(priv, 0x200) & 0x1000;
+ ctxsw_active = nv_rd32(priv, 0x2640) & 0x8000;
+ gr_busy = nv_rd32(priv, 0x40060c) & 0x1;
+
+ if (!gr_enabled || (!gr_busy && !ctxsw_active))
+ return 0;
+ } while (time_before(jiffies, end_jiffies));
+
+ nv_error(priv, "wait for idle timeout (en: %d, ctxsw: %d, busy: %d)\n",
+ gr_enabled, ctxsw_active, gr_busy);
+ return -EAGAIN;
+}
+
void
gf100_gr_mmio(struct gf100_gr_priv *priv, const struct gf100_gr_pack *p)
{
@@ -699,6 +730,12 @@ gf100_gr_icmd(struct gf100_gr_priv *priv, const struct gf100_gr_pack *p)
while (addr < next) {
nv_wr32(priv, 0x400200, addr);
+ /**
+ * Wait for GR to go idle after submitting a
+ * GO_IDLE bundle
+ */
+ if ((addr & 0xffff) == 0xe100)
+ gf100_gr_wait_idle(priv);
nv_wait(priv, 0x400700, 0x00000004, 0x00000000);
addr += init->pitch;
}
diff --git a/drm/nouveau/nvkm/engine/gr/gf100.h b/drm/nouveau/nvkm/engine/gr/gf100.h
index 8af1a89ed..c9533fdac 100644
--- a/drm/nouveau/nvkm/engine/gr/gf100.h
+++ b/drm/nouveau/nvkm/engine/gr/gf100.h
@@ -181,6 +181,7 @@ struct gf100_gr_oclass {
int ppc_nr;
};
+int gf100_gr_wait_idle(struct gf100_gr_priv *);
void gf100_gr_mmio(struct gf100_gr_priv *, const struct gf100_gr_pack *);
void gf100_gr_icmd(struct gf100_gr_priv *, const struct gf100_gr_pack *);
void gf100_gr_mthd(struct gf100_gr_priv *, const struct gf100_gr_pack *);