Merge branch 'upstream-gallium-0.1' into nouveau-gallium-0.1
[mesa.git] / src / mesa / drivers / dri / nouveau_winsys / nouveau_bo.c
index fd6d05b7fab1b66f1fe659fd33e0b991a77e6083..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 *userdev)
+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 *userdev)
-{
+       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 *bo, 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 *nv = nouveau_device(bo->base.device);
+       struct nouveau_device_priv *nvdev = nouveau_device(dev);
        int ret;
 
-       if (bo->drm.size && bo->drm.size != size) {
-               struct drm_nouveau_mem_free f;
-
-               if (bo->map) {
-                       drmUnmap(bo->map, bo->drm.size);
-                       bo->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;
                }
+       }
 
-               f.flags = bo->drm.flags;
-               f.offset = bo->drm.offset;
-               drmCommandWrite(nv->fd, DRM_NOUVEAU_MEM_FREE, &f, sizeof(f));
+       return 0;
+}
 
-               bo->drm.size = 0;
-       }
+static void
+nouveau_bo_tmp_del(void *priv)
+{
+       struct nouveau_resource *r = priv;
 
-       if (size && !bo->drm.size) {
-               if (flags) {
-                       bo->drm.flags = 0;
-                       if (flags & NOUVEAU_BO_VRAM)
-                               bo->drm.flags |= NOUVEAU_MEM_FB;
-                       if (flags & NOUVEAU_BO_GART)
-                               bo->drm.flags |= (NOUVEAU_MEM_AGP |
-                                                 NOUVEAU_MEM_PCI);
-                       bo->drm.flags |= NOUVEAU_MEM_MAPPED;
-               }
+       nouveau_fence_ref(NULL, (struct nouveau_fence **)&r->priv);
+       nouveau_resource_free(&r);
+}
 
-               bo->drm.size = size;
-               
-               ret = drmCommandWriteRead(nv->fd, DRM_NOUVEAU_MEM_ALLOC,
-                                         &bo->drm, sizeof(bo->drm));
-               if (ret) {
-                       free(bo);
-                       return ret;
-               }
+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;
 
-               ret = drmMap(nv->fd, bo->drm.map_handle, bo->drm.size,
-                            &bo->map);
-               if (ret) {
-                       bo->map = NULL;
-                       nouveau_bo_del((void *)&bo);
-                       return ret;
-               }
+       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);
 
-       return 0;
+       return r;
 }
 
 int
-nouveau_bo_new(struct nouveau_device *userdev, uint32_t flags, int align,
-              int size, struct nouveau_bo **userbo)
+nouveau_bo_init(struct nouveau_device *dev)
 {
-       struct nouveau_bo_priv *bo;
+       struct nouveau_device_priv *nvdev = nouveau_device(dev);
        int ret;
 
-       if (!userdev || !userbo || *userbo)
-               return -EINVAL;
+       ret = nouveau_mem_alloc(dev, 128*1024, 0, NOUVEAU_MEM_AGP |
+                               NOUVEAU_MEM_PCI, &nvdev->sa, &nvdev->sa_map);
+       if (ret)
+               return ret;
 
-       bo = calloc(1, sizeof(*bo));
-       if (!bo)
-               return -ENOMEM;
-       bo->base.device = userdev;
-       bo->drm.alignment = align;
-
-       if (flags & NOUVEAU_BO_PIN) {
-               ret = nouveau_bo_realloc_gpu(bo, flags, size);
-               if (ret) {
-                       free(bo);
-                       return ret;
-               }       
-       } else {
-               bo->sysmem = malloc(size);
-               if (!bo->sysmem) {
-                       free(bo);
-                       return -ENOMEM;
-               }
+       ret = nouveau_resource_init(&nvdev->sa_heap, 0, nvdev->sa.size);
+       if (ret) {
+               nouveau_mem_free(dev, &nvdev->sa, &nvdev->sa_map);
+               return ret;
        }
 
-       bo->base.size = size;
-       bo->base.offset = bo->drm.offset;
-       bo->base.handle = (unsigned long)bo;
-       bo->refcount = 1;
-       *userbo = &bo->base;
        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_user(struct nouveau_device *userdev, void *ptr, int size,
-               struct nouveau_bo **userbo)
+nouveau_bo_new(struct nouveau_device *dev, uint32_t flags, int align,
+              int size, struct nouveau_bo **bo)
 {
-       struct nouveau_bo_priv *bo;
+       struct nouveau_bo_priv *nvbo;
+       int ret;
 
-       if (!userdev || !userbo || *userbo)
+       if (!dev || !bo || *bo)
                return -EINVAL;
 
-       bo = calloc(1, sizeof(*bo));
-       if (!bo)
+       nvbo = calloc(1, sizeof(struct nouveau_bo_priv));
+       if (!nvbo)
                return -ENOMEM;
-       bo->base.device = userdev;
-       
-       bo->sysmem = ptr;
-       bo->user = 1;
-
-       bo->base.size = size;
-       bo->base.offset = bo->drm.offset;
-       bo->base.handle = (unsigned long)bo;
-       bo->refcount = 1;
-       *userbo = &bo->base;
+       nvbo->base.device = dev;
+       nvbo->base.size = size;
+       nvbo->base.handle = bo_to_ptr(nvbo);
+       nvbo->drm.alignment = align;
+       nvbo->refcount = 1;
+
+       ret = nouveau_bo_set_status(&nvbo->base, flags);
+       if (ret) {
+               free(nvbo);
+               return ret;
+       }
+
+       *bo = &nvbo->base;
        return 0;
 }
 
 int
-nouveau_bo_ref(struct nouveau_device *userdev, uint64_t handle,
-              struct nouveau_bo **userbo)
+nouveau_bo_user(struct nouveau_device *dev, void *ptr, int size,
+               struct nouveau_bo **bo)
 {
-       struct nouveau_bo_priv *bo = (void *)(unsigned long)handle;
+       struct nouveau_bo_priv *nvbo;
 
-       if (!userdev || !userbo || *userbo)
+       if (!dev || !bo || *bo)
                return -EINVAL;
 
-       bo->refcount++;
-       *userbo = &bo->base;
+       nvbo = calloc(1, sizeof(*nvbo));
+       if (!nvbo)
+               return -ENOMEM;
+       nvbo->base.device = dev;
+       
+       nvbo->sysmem = ptr;
+       nvbo->user = 1;
+
+       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;
 }
 
 int
-nouveau_bo_resize(struct nouveau_bo *userbo, int size)
+nouveau_bo_ref(struct nouveau_device *dev, uint64_t handle,
+              struct nouveau_bo **bo)
 {
-       struct nouveau_bo_priv *bo = nouveau_bo(userbo);
-       int ret;
+       struct nouveau_bo_priv *nvbo = ptr_to_bo(handle);
 
-       if (!bo || bo->user)
+       if (!dev || !bo || *bo)
                return -EINVAL;
 
-       if (bo->sysmem) {
-               bo->sysmem = realloc(bo->sysmem, size);
-               if (!bo->sysmem)
-                       return -ENOMEM;
-       } else {
-               ret = nouveau_bo_realloc_gpu(bo, 0, size);
-               if (ret)
-                       return ret;
-       }
-
-       bo->base.size = size;
+       nvbo->refcount++;
+       *bo = &nvbo->base;
        return 0;
 }
 
 void
-nouveau_bo_del(struct nouveau_bo **userbo)
+nouveau_bo_del(struct nouveau_bo **bo)
 {
-       struct nouveau_bo_priv *bo;
+       struct nouveau_bo_priv *nvbo;
 
-       if (!userbo || !*userbo)
+       if (!bo || !*bo)
                return;
-       bo = nouveau_bo(*userbo);
-       *userbo = NULL;
+       nvbo = nouveau_bo(*bo);
+       *bo = NULL;
 
-       if (--bo->refcount)
+       if (--nvbo->refcount)
                return;
 
-       nouveau_bo_realloc_gpu(bo, 0, 0);
-       if (bo->sysmem && !bo->user)
-               free(bo->sysmem);
-       free(bo);
+       if (nvbo->fence)
+               nouveau_fence_wait(&nvbo->fence);
+       nouveau_mem_free(nvbo->base.device, &nvbo->drm, &nvbo->map);
+       if (nvbo->sysmem && !nvbo->user)
+               free(nvbo->sysmem);
+       free(nvbo);
 }
 
 int
-nouveau_bo_map(struct nouveau_bo *userbo, uint32_t flags)
+nouveau_bo_map(struct nouveau_bo *bo, uint32_t flags)
 {
-       struct nouveau_bo_priv *bo = nouveau_bo(userbo);
+       struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
 
-       if (!bo)
+       if (!nvbo)
                return -EINVAL;
 
-       if (bo->sysmem)
-               userbo->map = bo->sysmem;
+       if (flags & NOUVEAU_BO_WR)
+               nouveau_fence_wait(&nvbo->fence);
        else
-               userbo->map = bo->map;
+               nouveau_fence_wait(&nvbo->wr_fence);
+
+       if (nvbo->sysmem)
+               bo->map = nvbo->sysmem;
+       else
+               bo->map = nvbo->map;
        return 0;
 }
 
 void
-nouveau_bo_unmap(struct nouveau_bo *userbo)
+nouveau_bo_unmap(struct nouveau_bo *bo)
 {
-       userbo->map = NULL;
+       bo->map = NULL;
 }
 
 static int
-nouveau_bo_upload(struct nouveau_bo_priv *bo)
+nouveau_bo_upload(struct nouveau_bo_priv *nvbo)
 {
-       memcpy(bo->map, bo->sysmem, bo->drm.size);
+       if (nvbo->fence)
+               nouveau_fence_wait(&nvbo->fence);
+       memcpy(nvbo->map, nvbo->sysmem, nvbo->drm.size);
        return 0;
 }
 
 int
-nouveau_bo_validate(struct nouveau_channel *chan, struct nouveau_bo *bo,
-                   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 || nvbo->base.map)
-               nouveau_bo_upload(nvbo);
+       assert(!bo->map);
 
-       if (!nvbo->user && !nvbo->base.map) {
-               free(nvbo->sysmem);
-               nvbo->sysmem = NULL;
+       /* 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);
        }
 
-       nvbo->base.offset = nvbo->drm.offset;
-       if (nvbo->drm.flags & NOUVEAU_MEM_AGP)
-               nvbo->base.flags = NOUVEAU_BO_GART;
+       /* 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_wait(&nvbo->fence);
+       nouveau_mem_free(bo->device, &nvbo->drm, &nvbo->map);
+       if (nvbo->sysmem)
+               free(nvbo->sysmem);
+
+       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;
 }