drm/nouveau: Provide a means to have arbitrary work run on fence completion.
authorFrancisco Jerez <currojerez@riseup.net>
Tue, 21 Sep 2010 18:49:39 +0000 (20:49 +0200)
committerBen Skeggs <bskeggs@redhat.com>
Mon, 4 Oct 2010 23:59:12 +0000 (09:59 +1000)
Signed-off-by: Francisco Jerez <currojerez@riseup.net>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_fence.c

index 9a9066f1fbcccde5835a8e8ceeba11848100dfe1..d4f049d42edeb2a459b9bcb0abb0533bb7a783f0 100644 (file)
@@ -1243,6 +1243,9 @@ extern void nouveau_fence_update(struct nouveau_channel *);
 extern int nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **,
                             bool emit);
 extern int nouveau_fence_emit(struct nouveau_fence *);
+extern void nouveau_fence_work(struct nouveau_fence *fence,
+                              void (*work)(void *priv, bool signalled),
+                              void *priv);
 struct nouveau_channel *nouveau_fence_channel(struct nouveau_fence *);
 extern bool nouveau_fence_signalled(void *obj, void *arg);
 extern int nouveau_fence_wait(void *obj, void *arg, bool lazy, bool intr);
index 62f13189698af7ef1c1c8b9a60eb8f35d424b23a..fbb2c3b26239b4690615e6290bf0dcb26614b3d4 100644 (file)
@@ -39,6 +39,9 @@ struct nouveau_fence {
 
        uint32_t sequence;
        bool signalled;
+
+       void (*work)(void *priv, bool signalled);
+       void *priv;
 };
 
 static inline struct nouveau_fence *
@@ -78,6 +81,10 @@ nouveau_fence_update(struct nouveau_channel *chan)
                sequence = fence->sequence;
                fence->signalled = true;
                list_del(&fence->entry);
+
+               if (unlikely(fence->work))
+                       fence->work(fence->priv, true);
+
                kref_put(&fence->refcount, nouveau_fence_del);
 
                if (sequence == chan->fence.sequence_ack)
@@ -147,6 +154,25 @@ nouveau_fence_emit(struct nouveau_fence *fence)
        return 0;
 }
 
+void
+nouveau_fence_work(struct nouveau_fence *fence,
+                  void (*work)(void *priv, bool signalled),
+                  void *priv)
+{
+       BUG_ON(fence->work);
+
+       spin_lock(&fence->channel->fence.lock);
+
+       if (fence->signalled) {
+               work(priv, true);
+       } else {
+               fence->work = work;
+               fence->priv = priv;
+       }
+
+       spin_unlock(&fence->channel->fence.lock);
+}
+
 void
 nouveau_fence_unref(void **sync_obj)
 {
@@ -268,6 +294,10 @@ nouveau_fence_channel_fini(struct nouveau_channel *chan)
        list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {
                fence->signalled = true;
                list_del(&fence->entry);
+
+               if (unlikely(fence->work))
+                       fence->work(fence->priv, false);
+
                kref_put(&fence->refcount, nouveau_fence_del);
        }
 }