int nv20_graph_unload_context(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; struct nouveau_channel *chan; uint32_t inst, tmp; chan = pgraph->channel(dev); if (!chan) return 0; inst = chan->ramin_grctx->pinst >> 4; nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER, NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE); nouveau_wait_for_idle(dev); nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000); tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff; tmp |= (pfifo->channels - 1) << 24; nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp); return 0; }
static int nv50_graph_unload_context(struct drm_device *dev) { uint32_t inst; inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR); if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED)) return 0; inst &= NV50_PGRAPH_CTXCTL_CUR_INSTANCE; nouveau_wait_for_idle(dev); nv_wr32(dev, 0x400784, inst); nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x20); nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 0x01); nouveau_wait_for_idle(dev); nv_wr32(dev, NV50_PGRAPH_CTXCTL_CUR, inst); return 0; }
static void nv20_graph_rdi(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; int i, writecount = 32; uint32_t rdi_index = 0x2c80000; if (dev_priv->chipset == 0x20) { rdi_index = 0x3d0000; writecount = 15; } NV_WRITE(NV10_PGRAPH_RDI_INDEX, rdi_index); for (i = 0; i < writecount; i++) NV_WRITE(NV10_PGRAPH_RDI_DATA, 0); nouveau_wait_for_idle(dev); }
int nv20_graph_save_context(struct nouveau_channel *chan) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; uint32_t inst; if (!chan->ramin_grctx) return -EINVAL; inst = chan->ramin_grctx->instance >> 4; NV_WRITE(NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); NV_WRITE(NV20_PGRAPH_CHANNEL_CTX_XFER, NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE); nouveau_wait_for_idle(dev); return 0; }
int nv20_graph_load_context(struct nouveau_channel *chan) { struct drm_device *dev = chan->dev; uint32_t inst; if (!chan->ramin_grctx) return -EINVAL; inst = chan->ramin_grctx->pinst >> 4; nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER, NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD); nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100); nouveau_wait_for_idle(dev); return 0; }
static int nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst) { uint32_t fifo = nv_rd32(dev, 0x400500); nv_wr32(dev, 0x400500, fifo & ~1); nv_wr32(dev, 0x400784, inst); nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x40); nv_wr32(dev, 0x400320, nv_rd32(dev, 0x400320) | 0x11); nv_wr32(dev, 0x400040, 0xffffffff); (void)nv_rd32(dev, 0x400040); nv_wr32(dev, 0x400040, 0x00000000); nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 1); if (nouveau_wait_for_idle(dev)) nv_wr32(dev, 0x40032c, inst | (1<<31)); nv_wr32(dev, 0x400500, fifo); return 0; }
static int nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save) { uint32_t old_cp, tv = 1000, tmp; int i; old_cp = nv_rd32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER); nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0310); tmp |= save ? NV40_PGRAPH_CTXCTL_0310_XFER_SAVE : NV40_PGRAPH_CTXCTL_0310_XFER_LOAD; nv_wr32(dev, NV40_PGRAPH_CTXCTL_0310, tmp); tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0304); tmp |= NV40_PGRAPH_CTXCTL_0304_XFER_CTX; nv_wr32(dev, NV40_PGRAPH_CTXCTL_0304, tmp); nouveau_wait_for_idle(dev); for (i = 0; i < tv; i++) { if (nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C) == 0) break; } nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, old_cp); if (i == tv) { uint32_t ucstat = nv_rd32(dev, NV40_PGRAPH_CTXCTL_UCODE_STAT); NV_ERROR(dev, "Failed: Instance=0x%08x Save=%d\n", inst, save); NV_ERROR(dev, "IP: 0x%02x, Opcode: 0x%08x\n", ucstat >> NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT, ucstat & NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK); NV_ERROR(dev, "0x40030C = 0x%08x\n", nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C)); return -EBUSY; }
void nouveau_channel_put_unlocked(struct nouveau_channel **pchan) { struct nouveau_channel *chan = *pchan; struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; struct nouveau_crypt_engine *pcrypt = &dev_priv->engine.crypt; unsigned long flags; /* decrement the refcount, and we're done if there's still refs */ if (likely(!atomic_dec_and_test(&chan->users))) { nouveau_channel_ref(NULL, pchan); return; } /* noone wants the channel anymore */ NV_DEBUG(dev, "freeing channel %d\n", chan->id); nouveau_debugfs_channel_fini(chan); /* give it chance to idle */ nouveau_channel_idle(chan); /* ensure all outstanding fences are signaled. they should be if the * above attempts at idling were OK, but if we failed this'll tell TTM * we're done with the buffers. */ nouveau_fence_channel_fini(chan); /* boot it off the hardware */ pfifo->reassign(dev, false); /* We want to give pgraph a chance to idle and get rid of all * potential errors. We need to do this without the context * switch lock held, otherwise the irq handler is unable to * process them. */ if (pgraph->channel(dev) == chan) nouveau_wait_for_idle(dev); /* destroy the engine specific contexts */ pfifo->destroy_context(chan); pgraph->destroy_context(chan); if (pcrypt->destroy_context) pcrypt->destroy_context(chan); pfifo->reassign(dev, true); /* aside from its resources, the channel should now be dead, * remove it from the channel list */ spin_lock_irqsave(&dev_priv->channels.lock, flags); nouveau_channel_ref(NULL, &dev_priv->channels.ptr[chan->id]); spin_unlock_irqrestore(&dev_priv->channels.lock, flags); /* destroy any resources the channel owned */ nouveau_gpuobj_ref(NULL, &chan->pushbuf); if (chan->pushbuf_bo) { nouveau_bo_unmap(chan->pushbuf_bo); nouveau_bo_unpin(chan->pushbuf_bo); nouveau_bo_ref(NULL, &chan->pushbuf_bo); } nouveau_gpuobj_channel_takedown(chan); nouveau_notifier_takedown_channel(chan); nouveau_channel_ref(NULL, pchan); }