drm/nouveau/gr/gf100: wait for GR idle after GO_IDLE bundle

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>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
Alexandre Courbot 2015-04-27 17:25:11 +09:00 committed by Ben Skeggs
parent 19bf09cecf
commit 4a8cf4513d
2 changed files with 38 additions and 0 deletions

View File

@ -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;
}

View File

@ -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 *);