summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Skeggs <skeggsb@gmail.com>2008-03-07 15:08:59 +1100
committerBen Skeggs <skeggsb@gmail.com>2008-03-07 15:18:34 +1100
commit1ccccbd4ce3463edb459eb193feb572938fce19e (patch)
treeace23a5bff45a664b2e8f4242883398e1ed9db99
parentcd924de02927a091c517b0ac6b9cd8f065ce448c (diff)
nouveau: redo channel idle detection
Will hopefully work a bit better than previous code, which depended on knowing the channel's most recent PUT value. Some chips always return 0 on reading these regs, and currently userspace is the only other entity which knows the value.
-rw-r--r--shared-core/nouveau_fifo.c43
1 files changed, 29 insertions, 14 deletions
diff --git a/shared-core/nouveau_fifo.c b/shared-core/nouveau_fifo.c
index 8e93207e..d8fda277 100644
--- a/shared-core/nouveau_fifo.c
+++ b/shared-core/nouveau_fifo.c
@@ -390,6 +390,34 @@ nouveau_fifo_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
return 0;
}
+static int
+nouveau_channel_idle(struct nouveau_channel *chan)
+{
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_engine *engine = &dev_priv->Engine;
+ uint32_t caches;
+ int idle;
+
+ caches = NV_READ(NV03_PFIFO_CACHES);
+ NV_WRITE(NV03_PFIFO_CACHES, caches & ~1);
+
+ if (engine->fifo.channel_id(dev) != chan->id) {
+ struct nouveau_gpuobj *ramfc = chan->ramfc->gpuobj;
+
+ if (INSTANCE_RD(ramfc, 0) != INSTANCE_RD(ramfc, 1))
+ idle = 0;
+ else
+ idle = 1;
+ } else {
+ idle = (NV_READ(NV04_PFIFO_CACHE1_DMA_GET) ==
+ NV_READ(NV04_PFIFO_CACHE1_DMA_PUT));
+ }
+
+ NV_WRITE(NV03_PFIFO_CACHES, caches);
+ return idle;
+}
+
/* stops a fifo */
void nouveau_fifo_free(struct nouveau_channel *chan)
{
@@ -400,22 +428,9 @@ void nouveau_fifo_free(struct nouveau_channel *chan)
DRM_INFO("%s: freeing fifo %d\n", __func__, chan->id);
- /* Disable channel switching, if this channel isn't currenly
- * active re-enable it if there's still pending commands.
- * We really should do a manual context switch here, but I'm
- * not sure I trust our ability to do this reliably yet..
- */
- NV_WRITE(NV03_PFIFO_CACHES, 0);
- if (engine->fifo.channel_id(dev) != chan->id &&
- NV_READ(chan->get) != NV_READ(chan->put)) {
- NV_WRITE(NV03_PFIFO_CACHES, 1);
- }
-
/* Give the channel a chance to idle, wait 2s (hopefully) */
t_start = engine->timer.read(dev);
- while (NV_READ(chan->get) != NV_READ(chan->put) ||
- NV_READ(NV03_PFIFO_CACHE1_GET) !=
- NV_READ(NV03_PFIFO_CACHE1_PUT)) {
+ while (!nouveau_channel_idle(chan)) {
if (engine->timer.read(dev) - t_start > 2000000000ULL) {
DRM_ERROR("Failed to idle channel %d before destroy."
"Prepare for strangeness..\n", chan->id);