Merge branch 'upstream-gallium-0.1' into nouveau-gallium-0.1
[mesa.git] / src / mesa / drivers / dri / nouveau_winsys / nouveau_pushbuf.c
index a922300ff5e441235db3c497873096851f5a4446..7d5eddb92ff81bc41500125437f94f92294f09d6 100644 (file)
 #include "nouveau_drmif.h"
 #include "nouveau_dma.h"
 
+#define PB_BUFMGR_DWORDS   (4096 / 2)
+#define PB_MIN_USER_DWORDS  2048
+
+static int
+nouveau_pushbuf_space(struct nouveau_channel *chan, unsigned min)
+{
+       struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+       struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
+
+       assert((min + 1) <= nvchan->dma->max);
+
+       /* Wait for enough space in push buffer */
+       min = min < PB_MIN_USER_DWORDS ? PB_MIN_USER_DWORDS : min;
+       min += 1; /* a bit extra for the NOP */
+       if (nvchan->dma->free < min)
+               WAIT_RING_CH(chan, min);
+
+       /* Insert NOP, may turn into a jump later */
+       RING_SPACE_CH(chan, 1);
+       nvpb->nop_jump = nvchan->dma->cur;
+       OUT_RING_CH(chan, 0);
+
+       /* Any remaining space is available to the user */
+       nvpb->start = nvchan->dma->cur;
+       nvpb->size = nvchan->dma->free;
+       nvpb->base.channel = chan;
+       nvpb->base.remaining = nvpb->size;
+       nvpb->base.cur = &nvchan->pushbuf[nvpb->start];
+
+       return 0;
+}
+
 int
 nouveau_pushbuf_init(struct nouveau_channel *chan)
 {
        struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+       struct nouveau_dma_priv *m = &nvchan->dma_master;
+       struct nouveau_dma_priv *b = &nvchan->dma_bufmgr;
+       int i;
 
        if (!nvchan)
                return -EINVAL;
 
-       /* Everything except first 4KiB of the push buffer is managed by us */
-       if (nouveau_resource_init(&nvchan->pb_heap, 4096,
-                                 nvchan->drm.cmdbuf_size - 4096))
-               return -EINVAL;
-
-       /* Shrink master ring to 4KiB */
-       assert(nvchan->dma.cur <= (4096/4));
-       nvchan->dma.max = (4096 / 4) - 2;
-       nvchan->dma.free = nvchan->dma.max - nvchan->dma.cur;
+       /* Reassign last bit of push buffer for a "separate" bufmgr
+        * ring buffer
+        */
+       m->max -= PB_BUFMGR_DWORDS;
+       m->free -= PB_BUFMGR_DWORDS;
+
+       b->base = m->base + ((m->max + 2) << 2);
+       b->max = PB_BUFMGR_DWORDS - 2;
+       b->cur = b->put = 0;
+       b->free = b->max - b->cur;
+
+       /* Some NOPs just to be safe
+        *XXX: RING_SKIPS
+        */
+       nvchan->dma = b;
+       RING_SPACE_CH(chan, 8);
+       for (i = 0; i < 8; i++)
+               OUT_RING_CH(chan, 0);
+       nvchan->dma = m;
+
+       nouveau_pushbuf_space(chan, 0);
+       chan->pushbuf = &nvchan->pb.base;
 
        return 0;
 }
 
-static void
-nouveau_pushbuf_fence_signalled(void *priv)
+static uint32_t
+nouveau_pushbuf_calc_reloc(struct nouveau_bo *bo,
+                          struct nouveau_pushbuf_reloc *r)
 {
-       struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(priv);
+       uint32_t push;
+
+       if (r->flags & NOUVEAU_BO_LOW) {
+               push = bo->offset + r->data;
+       } else
+       if (r->flags & NOUVEAU_BO_HIGH) {
+               push = (bo->offset + r->data) >> 32;
+       } else {
+               push = r->data;
+       }
+
+       if (r->flags & NOUVEAU_BO_OR) {
+               if (bo->flags & NOUVEAU_BO_VRAM)
+                       push |= r->vor;
+               else
+                       push |= r->tor;
+       }
 
-       nouveau_fence_del(&nvpb->fence);
-       nouveau_resource_free(&nvpb->res);
-       free(nvpb);
+       return push;
 }
 
 /* This would be our TTM "superioctl" */
 int
-nouveau_pushbuf_flush(struct nouveau_channel *chan)
+nouveau_pushbuf_flush(struct nouveau_channel *chan, unsigned min)
 {
        struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
-       struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(nvchan->pb_tail);
+       struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
        struct nouveau_pushbuf_bo *pbbo;
        struct nouveau_fence *fence = NULL;
-       int sync_hack = 0;
        int ret;
 
-       if (!nvpb)
-               goto out_realloc;
-
-       if (nvpb->base.remaining == nvpb->res->size / 4)
+       if (nvpb->base.remaining == nvpb->size)
                return 0;
-       nvchan->pb_tail = NULL;
+
+       nvpb->size -= nvpb->base.remaining;
+       nvchan->dma->cur += nvpb->size;
+       nvchan->dma->free -= nvpb->size;
+       assert(nvchan->dma->cur <= nvchan->dma->max);
 
        ret = nouveau_fence_new(chan, &fence);
        if (ret)
                return ret;
 
+       nvchan->dma = &nvchan->dma_bufmgr;
+       nvchan->pushbuf[nvpb->nop_jump] = 0x20000000 |
+               (nvchan->dma->base + (nvchan->dma->cur << 2));
+
        /* Validate buffers + apply relocations */
+       nvchan->user_charge = 0;
        while ((pbbo = ptr_to_pbbo(nvpb->buffers))) {
                struct nouveau_pushbuf_reloc *r;
                struct nouveau_bo *bo = &ptr_to_bo(pbbo->handle)->base;
@@ -88,29 +156,22 @@ nouveau_pushbuf_flush(struct nouveau_channel *chan)
                ret = nouveau_bo_validate(chan, bo, fence, pbbo->flags);
                assert (ret == 0);
 
-               sync_hack |= nouveau_bo(bo)->sync_hack;
-               nouveau_bo(bo)->sync_hack = 0;
-
-               while ((r = ptr_to_pbrel(pbbo->relocs))) {
-                       uint32_t push;
-
-                       if (r->flags & NOUVEAU_BO_LOW) {
-                               push = bo->offset + r->data;
-                       } else
-                       if (r->flags & NOUVEAU_BO_HIGH) {
-                               push = (bo->offset + r->data) >> 32;
-                       } else {
-                               push = r->data;
+               if (bo->offset == nouveau_bo(bo)->offset &&
+                   bo->flags == nouveau_bo(bo)->flags) {
+                       while ((r = ptr_to_pbrel(pbbo->relocs))) {
+                               pbbo->relocs = r->next;
+                               free(r);
                        }
 
-                       if (r->flags & NOUVEAU_BO_OR) {
-                               if (bo->flags & NOUVEAU_BO_VRAM)
-                                       push |= r->vor;
-                               else
-                                       push |= r->tor;
-                       }
+                       nvpb->buffers = pbbo->next;
+                       free(pbbo);
+                       continue;
+               }
+               bo->offset = nouveau_bo(bo)->offset;
+               bo->flags = nouveau_bo(bo)->flags;
 
-                       *r->ptr = push;
+               while ((r = ptr_to_pbrel(pbbo->relocs))) {
+                       *r->ptr = nouveau_pushbuf_calc_reloc(bo, r);
                        pbbo->relocs = r->next;
                        free(r);
                }
@@ -120,49 +181,19 @@ nouveau_pushbuf_flush(struct nouveau_channel *chan)
        }
        nvpb->nr_buffers = 0;
 
-       /* Emit JMP to indirect pushbuf */
-       if (nvchan->dma.free < 1)
-               WAIT_RING_CH(chan, 1);
-       nvchan->dma.free -= 1;
-#ifdef NOUVEAU_DMA_DEBUG
-       nvchan->dma.push_free = 1;
-#endif
-       OUT_RING_CH(chan, 0x20000000 | nvpb->res->start);
-
-       /* Add JMP back to master pushbuf from indirect pushbuf */
-       (*nvpb->base.cur++) =
-               0x20000000 | ((nvchan->dma.cur << 2) + nvchan->dma.base);
-
-       /* Fence */
-       nvpb->fence = fence;
-       nouveau_fence_signal_cb(nvpb->fence, nouveau_pushbuf_fence_signalled,
-                               nvpb);
-       nouveau_fence_emit(nvpb->fence);
-
-       /* Kickoff */
-       FIRE_RING_CH(chan);
+       /* Switch back to user's ring */
+       RING_SPACE_CH(chan, 1);
+       OUT_RING_CH(chan, 0x20000000 | ((nvpb->start << 2) +
+                                       nvchan->dma_master.base));
+       nvchan->dma = &nvchan->dma_master;
 
-       if (sync_hack) {
-               struct nouveau_fence *f = NULL;
-               nouveau_fence_ref(nvpb->fence, &f);
-               nouveau_fence_wait(&f);
-       }
+       /* Fence + kickoff */
+       nouveau_fence_emit(fence);
+       FIRE_RING_CH(chan);
+       nouveau_fence_ref(NULL, &fence);
 
        /* Allocate space for next push buffer */
-out_realloc:
-       nvpb = calloc(1, sizeof(struct nouveau_pushbuf_priv));
-       if (!nvpb)
-               return -ENOMEM;
-
-       while (nouveau_resource_alloc(nvchan->pb_heap, 0x2000, NULL,
-                                     &nvpb->res)) {
-               nouveau_fence_flush(chan);
-       }
-
-       nvpb->base.channel = chan;
-       nvpb->base.remaining = nvpb->res->size / 4;
-       nvpb->base.cur = &nvchan->pushbuf[nvpb->res->start/4];
-       nvchan->pb_tail = &nvpb->base;
+       assert(!nouveau_pushbuf_space(chan, min));
 
        return 0;
 }
@@ -170,8 +201,7 @@ out_realloc:
 static struct nouveau_pushbuf_bo *
 nouveau_pushbuf_emit_buffer(struct nouveau_channel *chan, struct nouveau_bo *bo)
 {
-       struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
-       struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(nvchan->pb_tail);
+       struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf);
        struct nouveau_pushbuf_bo *pbbo = ptr_to_pbbo(nvpb->buffers);
 
        while (pbbo) {
@@ -222,6 +252,10 @@ nouveau_pushbuf_emit_reloc(struct nouveau_channel *chan, void *ptr,
        r->vor = vor;
        r->tor = tor;
 
+       if (flags & NOUVEAU_BO_DUMMY)
+               *(uint32_t *)ptr = 0;
+       else
+               *(uint32_t *)ptr = nouveau_pushbuf_calc_reloc(bo, r);
        return 0;
 }