Merge branch 'upstream-gallium-0.1' into nouveau-gallium-0.1
[mesa.git] / src / mesa / drivers / dri / nouveau_winsys / nouveau_bo.c
index d31026b52d5200c0d8a0bbbe27792815631e1285..6887ffa6886394fb09e16498dc317063a641bd99 100644 (file)
 #include <stdint.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <assert.h>
 
 #include "nouveau_drmif.h"
 #include "nouveau_dma.h"
 #include "nouveau_local.h"
 
-int
-nouveau_bo_init(struct nouveau_device *dev)
+static void
+nouveau_mem_free(struct nouveau_device *dev, struct drm_nouveau_mem_alloc *ma,
+                void **map)
 {
-       return 0;
-}
+       struct nouveau_device_priv *nvdev = nouveau_device(dev);
+       struct drm_nouveau_mem_free mf;
 
-void
-nouveau_bo_takedown(struct nouveau_device *dev)
-{
+       if (map && *map) {
+               drmUnmap(*map, ma->size);
+               *map = NULL;
+       }
+
+       if (ma->size) {
+               mf.offset = ma->offset;
+               mf.flags = ma->flags;
+               drmCommandWrite(nvdev->fd, DRM_NOUVEAU_MEM_FREE,
+                               &mf, sizeof(mf));
+               ma->size = 0;
+       }
 }
 
 static int
-nouveau_bo_realloc_gpu(struct nouveau_bo_priv *nvbo, uint32_t flags, int size)
+nouveau_mem_alloc(struct nouveau_device *dev, unsigned size, unsigned align,
+                 uint32_t flags, struct drm_nouveau_mem_alloc *ma, void **map)
 {
-       struct nouveau_device_priv *nvdev = nouveau_device(nvbo->base.device);
+       struct nouveau_device_priv *nvdev = nouveau_device(dev);
        int ret;
 
-       if (nvbo->drm.size && nvbo->drm.size != size) {
-               struct drm_nouveau_mem_free f;
-
-               if (nvbo->map) {
-                       drmUnmap(nvbo->map, nvbo->drm.size);
-                       nvbo->map = NULL;
+       ma->alignment = align;
+       ma->size = size;
+       ma->flags = flags;
+       if (map)
+               ma->flags |= NOUVEAU_MEM_MAPPED;
+       ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_MEM_ALLOC, ma,
+                                 sizeof(struct drm_nouveau_mem_alloc));
+       if (ret)
+               return ret;
+
+       if (map) {
+               ret = drmMap(nvdev->fd, ma->map_handle, ma->size, map);
+               if (ret) {
+                       *map = NULL;
+                       nouveau_mem_free(dev, ma, map);
+                       return ret;
                }
+       }
+
+       return 0;
+}
 
-               f.flags = nvbo->drm.flags;
-               f.offset = nvbo->drm.offset;
-               drmCommandWrite(nvdev->fd, DRM_NOUVEAU_MEM_FREE, &f, sizeof(f));
+static void
+nouveau_bo_tmp_del(void *priv)
+{
+       struct nouveau_resource *r = priv;
+
+       nouveau_fence_ref(NULL, (struct nouveau_fence **)&r->priv);
+       nouveau_resource_free(&r);
+}
+
+static struct nouveau_resource *
+nouveau_bo_tmp(struct nouveau_channel *chan, unsigned size,
+              struct nouveau_fence *fence)
+{
+       struct nouveau_device_priv *nvdev = nouveau_device(chan->device);
+       struct nouveau_resource *r = NULL;
+       struct nouveau_fence *ref = NULL;
 
-               nvbo->drm.size = 0;
+       if (fence)
+               nouveau_fence_ref(fence, &ref);
+       else
+               nouveau_fence_new(chan, &ref);
+       assert(ref);
+
+       while (nouveau_resource_alloc(nvdev->sa_heap, size, ref, &r)) {
+               nouveau_fence_flush(chan);
        }
+       nouveau_fence_signal_cb(ref, nouveau_bo_tmp_del, r);
 
-       if (size && !nvbo->drm.size) {
-               if (flags) {
-                       nvbo->drm.flags = 0;
-                       if (flags & NOUVEAU_BO_VRAM)
-                               nvbo->drm.flags |= NOUVEAU_MEM_FB;
-                       if (flags & NOUVEAU_BO_GART)
-                               nvbo->drm.flags |= (NOUVEAU_MEM_AGP |
-                                                   NOUVEAU_MEM_PCI);
-                       nvbo->drm.flags |= NOUVEAU_MEM_MAPPED;
-               }
+       return r;
+}
 
-               nvbo->drm.size = size;
-               
-               ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_MEM_ALLOC,
-                                         &nvbo->drm, sizeof(nvbo->drm));
-               if (ret) {
-                       free(nvbo);
-                       return ret;
-               }
+int
+nouveau_bo_init(struct nouveau_device *dev)
+{
+       struct nouveau_device_priv *nvdev = nouveau_device(dev);
+       int ret;
 
-               ret = drmMap(nvdev->fd, nvbo->drm.map_handle, nvbo->drm.size,
-                            &nvbo->map);
-               if (ret) {
-                       nvbo->map = NULL;
-                       nouveau_bo_del((void *)&nvbo);
-                       return ret;
-               }
+       ret = nouveau_mem_alloc(dev, 128*1024, 0, NOUVEAU_MEM_AGP |
+                               NOUVEAU_MEM_PCI, &nvdev->sa, &nvdev->sa_map);
+       if (ret)
+               return ret;
+
+       ret = nouveau_resource_init(&nvdev->sa_heap, 0, nvdev->sa.size);
+       if (ret) {
+               nouveau_mem_free(dev, &nvdev->sa, &nvdev->sa_map);
+               return ret;
        }
 
        return 0;
 }
 
+void
+nouveau_bo_takedown(struct nouveau_device *dev)
+{
+       struct nouveau_device_priv *nvdev = nouveau_device(dev);
+
+       nouveau_mem_free(dev, &nvdev->sa, &nvdev->sa_map);
+}
+
 int
 nouveau_bo_new(struct nouveau_device *dev, uint32_t flags, int align,
               int size, struct nouveau_bo **bo)
@@ -106,26 +152,17 @@ nouveau_bo_new(struct nouveau_device *dev, uint32_t flags, int align,
        if (!nvbo)
                return -ENOMEM;
        nvbo->base.device = dev;
+       nvbo->base.size = size;
+       nvbo->base.handle = bo_to_ptr(nvbo);
        nvbo->drm.alignment = align;
+       nvbo->refcount = 1;
 
-       if (flags & NOUVEAU_BO_PIN) {
-               ret = nouveau_bo_realloc_gpu(nvbo, flags, size);
-               if (ret) {
-                       free(nvbo);
-                       return ret;
-               }       
-       } else {
-               nvbo->sysmem = malloc(size);
-               if (!nvbo->sysmem) {
-                       free(nvbo);
-                       return -ENOMEM;
-               }
+       ret = nouveau_bo_set_status(&nvbo->base, flags);
+       if (ret) {
+               free(nvbo);
+               return ret;
        }
 
-       nvbo->base.size = size;
-       nvbo->base.offset = nvbo->drm.offset;
-       nvbo->base.handle = bo_to_ptr(nvbo);
-       nvbo->refcount = 1;
        *bo = &nvbo->base;
        return 0;
 }
@@ -169,29 +206,6 @@ nouveau_bo_ref(struct nouveau_device *dev, uint64_t handle,
        return 0;
 }
 
-int
-nouveau_bo_resize(struct nouveau_bo *bo, int size)
-{
-       struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
-       int ret;
-
-       if (!nvbo || nvbo->user)
-               return -EINVAL;
-
-       if (nvbo->sysmem) {
-               nvbo->sysmem = realloc(nvbo->sysmem, size);
-               if (!nvbo->sysmem)
-                       return -ENOMEM;
-       } else {
-               ret = nouveau_bo_realloc_gpu(nvbo, 0, size);
-               if (ret)
-                       return ret;
-       }
-
-       nvbo->base.size = size;
-       return 0;
-}
-
 void
 nouveau_bo_del(struct nouveau_bo **bo)
 {
@@ -207,8 +221,7 @@ nouveau_bo_del(struct nouveau_bo **bo)
 
        if (nvbo->fence)
                nouveau_fence_wait(&nvbo->fence);
-
-       nouveau_bo_realloc_gpu(nvbo, 0, 0);
+       nouveau_mem_free(nvbo->base.device, &nvbo->drm, &nvbo->map);
        if (nvbo->sysmem && !nvbo->user)
                free(nvbo->sysmem);
        free(nvbo);
@@ -222,6 +235,11 @@ nouveau_bo_map(struct nouveau_bo *bo, uint32_t flags)
        if (!nvbo)
                return -EINVAL;
 
+       if (flags & NOUVEAU_BO_WR)
+               nouveau_fence_wait(&nvbo->fence);
+       else
+               nouveau_fence_wait(&nvbo->wr_fence);
+
        if (nvbo->sysmem)
                bo->map = nvbo->sysmem;
        else
@@ -245,38 +263,140 @@ nouveau_bo_upload(struct nouveau_bo_priv *nvbo)
 }
 
 int
-nouveau_bo_validate(struct nouveau_channel *chan, struct nouveau_bo *bo,
-                   struct nouveau_fence *fence, uint32_t flags)
+nouveau_bo_set_status(struct nouveau_bo *bo, uint32_t flags)
 {
        struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+       struct drm_nouveau_mem_alloc new;
+       void *new_map = NULL, *new_sysmem = NULL;
+       unsigned new_flags = 0, ret;
 
-       if (!nvbo->drm.size) {
-               nouveau_bo_realloc_gpu(nvbo, flags, nvbo->base.size);
-               nouveau_bo_upload(nvbo);
-       } else
-       if (nvbo->user) {
-               nouveau_bo_upload(nvbo);
-       } else
-       if (nvbo->base.map) {
-               nouveau_bo_upload(nvbo);
-               nvbo->sync_hack = 1;
+       assert(!bo->map);
+
+       /* Check current memtype vs requested, if they match do nothing */
+       if ((nvbo->drm.flags & NOUVEAU_MEM_FB) && (flags & NOUVEAU_BO_VRAM))
+               return 0;
+       if ((nvbo->drm.flags & NOUVEAU_MEM_AGP) && (flags & NOUVEAU_BO_GART))
+               return 0;
+       if (nvbo->drm.size == 0 && nvbo->sysmem && (flags & NOUVEAU_BO_LOCAL))
+               return 0;
+
+       memset(&new, 0x00, sizeof(new));
+
+       /* Allocate new memory */
+       if (flags & NOUVEAU_BO_VRAM)
+               new_flags |= NOUVEAU_MEM_FB;
+       else
+       if (flags & NOUVEAU_BO_GART)
+               new_flags |= (NOUVEAU_MEM_AGP | NOUVEAU_MEM_PCI);
+
+       if (new_flags) {
+               ret = nouveau_mem_alloc(bo->device, bo->size,
+                                       nvbo->drm.alignment, new_flags,
+                                       &new, &new_map);
+               if (ret)
+                       return ret;
+       } else {
+               new_sysmem = malloc(bo->size);
        }
 
-       if (!nvbo->user && !nvbo->base.map) {
-               free(nvbo->sysmem);
-               nvbo->sysmem = NULL;
+       /* Copy old -> new */
+       /*XXX: use M2MF */
+       if (nvbo->sysmem || nvbo->map) {
+               nouveau_bo_map(bo, NOUVEAU_BO_RD);
+               memcpy(new_map, bo->map, bo->size);
+               nouveau_bo_unmap(bo);
        }
 
+       /* Free old memory */
        if (nvbo->fence)
-               nouveau_fence_del(&nvbo->fence);
-       nouveau_fence_ref(fence, &nvbo->fence);
+               nouveau_fence_wait(&nvbo->fence);
+       nouveau_mem_free(bo->device, &nvbo->drm, &nvbo->map);
+       if (nvbo->sysmem)
+               free(nvbo->sysmem);
 
-       nvbo->base.offset = nvbo->drm.offset;
-       if (nvbo->drm.flags & NOUVEAU_MEM_AGP)
-               nvbo->base.flags = NOUVEAU_BO_GART;
+       nvbo->drm = new;
+       nvbo->map = new_map;
+       nvbo->sysmem = new_sysmem;
+       bo->flags = flags;
+       bo->offset = nvbo->drm.offset;
+       return 0;
+}
+
+static int
+nouveau_bo_validate_user(struct nouveau_channel *chan, struct nouveau_bo *bo,
+                        struct nouveau_fence *fence, uint32_t flags)
+{
+       struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
+       struct nouveau_device_priv *nvdev = nouveau_device(chan->device);
+       struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+       struct nouveau_resource *r;
+
+       if (nvchan->user_charge + bo->size > nvdev->sa.size)
+               return 1;
+       nvchan->user_charge += bo->size;
+
+       if (!(flags & NOUVEAU_BO_GART))
+               return 1;
+
+       r = nouveau_bo_tmp(chan, bo->size, fence);
+       if (!r)
+               return 1;
+
+       memcpy(nvdev->sa_map + r->start, nvbo->sysmem, bo->size);
+
+       nvbo->offset = nvdev->sa.offset + r->start;
+       nvbo->flags = NOUVEAU_BO_GART;
+       return 0;
+}
+
+static int
+nouveau_bo_validate_bo(struct nouveau_channel *chan, struct nouveau_bo *bo,
+                      struct nouveau_fence *fence, uint32_t flags)
+{
+       struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+       int ret;
+
+       ret = nouveau_bo_set_status(bo, flags);
+       if (ret)
+               return ret;
+
+       if (nvbo->user)
+               nouveau_bo_upload(nvbo);
+
+       nvbo->offset = nvbo->drm.offset;
+       if (nvbo->drm.flags & (NOUVEAU_MEM_AGP | NOUVEAU_MEM_PCI))
+               nvbo->flags = NOUVEAU_BO_GART;
        else
-               nvbo->base.flags = NOUVEAU_BO_VRAM;
+               nvbo->flags = NOUVEAU_BO_VRAM;
+
+       return 0;
+}
 
+int
+nouveau_bo_validate(struct nouveau_channel *chan, struct nouveau_bo *bo,
+                   struct nouveau_fence *fence, uint32_t flags)
+{
+       struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+       int ret;
+
+       assert(bo->map == NULL);
+
+       if (nvbo->user) {
+               ret = nouveau_bo_validate_user(chan, bo, fence, flags);
+               if (ret) {
+                       ret = nouveau_bo_validate_bo(chan, bo, fence, flags);
+                       if (ret)
+                               return ret;
+               }
+       } else {
+               ret = nouveau_bo_validate_bo(chan, bo, fence, flags);
+               if (ret)
+                       return ret;
+       }
+
+       if (flags & NOUVEAU_BO_WR)
+               nouveau_fence_ref(fence, &nvbo->wr_fence);
+       nouveau_fence_ref(fence, &nvbo->fence);
        return 0;
 }