/* stops a fifo */ void nouveau_channel_free(struct nouveau_channel *chan) { struct drm_device *dev = chan->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; unsigned long flags; int ret; NV_INFO(dev, "%s: freeing fifo %d\n", __func__, chan->id); nouveau_debugfs_channel_fini(chan); /* Give outstanding push buffers a chance to complete */ spin_lock_irqsave(&chan->fence.lock, flags); nouveau_fence_update(chan); spin_unlock_irqrestore(&chan->fence.lock, flags); if (chan->fence.sequence != chan->fence.sequence_ack) { struct nouveau_fence *fence = NULL; ret = nouveau_fence_new(chan, &fence, true); if (ret == 0) { ret = nouveau_fence_wait(fence, NULL, false, false); nouveau_fence_unref((void *)&fence); } if (ret) NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id); } /* 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_fini(chan); /* Ensure the channel is no longer active on the GPU */ pfifo->reassign(dev, false); pgraph->fifo_access(dev, false); if (pgraph->channel(dev) == chan) pgraph->unload_context(dev); pgraph->destroy_context(chan); pgraph->fifo_access(dev, true); if (pfifo->channel_id(dev) == chan->id) { pfifo->disable(dev); pfifo->unload_context(dev); pfifo->enable(dev); } pfifo->destroy_context(chan); pfifo->reassign(dev, true); /* Release the channel's resources */ nouveau_gpuobj_ref_del(dev, &chan->pushbuf); if (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); if (chan->user) iounmap(chan->user); dev_priv->fifos[chan->id] = NULL; dev_priv->fifo_alloc_count--; kfree(chan); }
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); }