#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;
+}
+
+static int
+nouveau_bo_realloc_gpu(struct nouveau_bo_priv *nvbo, uint32_t flags, int size)
+{
+ int ret;
- bo->drm.size = 0;
+ if (nvbo->drm.size && nvbo->drm.size != size) {
+ nouveau_mem_free(nvbo->base.device, &nvbo->drm, &nvbo->map);
}
- if (size && !bo->drm.size) {
+ if (size && !nvbo->drm.size) {
if (flags) {
- bo->drm.flags = 0;
+ nvbo->drm.flags = 0;
if (flags & NOUVEAU_BO_VRAM)
- bo->drm.flags |= NOUVEAU_MEM_FB;
+ nvbo->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;
+ nvbo->drm.flags |= (NOUVEAU_MEM_AGP |
+ NOUVEAU_MEM_PCI);
+ nvbo->drm.flags |= NOUVEAU_MEM_MAPPED;
}
- bo->drm.size = size;
-
- ret = drmCommandWriteRead(nv->fd, DRM_NOUVEAU_MEM_ALLOC,
- &bo->drm, sizeof(bo->drm));
+ ret = nouveau_mem_alloc(nvbo->base.device, size,
+ nvbo->drm.alignment, nvbo->drm.flags,
+ &nvbo->drm, &nvbo->map);
if (ret) {
- free(bo);
- return ret;
+ assert(0);
}
+ }
- 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;
- }
+ return 0;
+}
+
+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;
+
+ 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 r;
+}
+
+int
+nouveau_bo_init(struct nouveau_device *dev)
+{
+ struct nouveau_device_priv *nvdev = nouveau_device(dev);
+ int 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 *userdev, uint32_t flags, int align,
- 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->drm.alignment = align;
+ nvbo->base.device = dev;
+ nvbo->drm.alignment = align;
if (flags & NOUVEAU_BO_PIN) {
- ret = nouveau_bo_realloc_gpu(bo, flags, size);
+ ret = nouveau_bo_realloc_gpu(nvbo, flags, size);
if (ret) {
- free(bo);
+ free(nvbo);
return ret;
}
} else {
- bo->sysmem = malloc(size);
- if (!bo->sysmem) {
- free(bo);
+ nvbo->sysmem = malloc(size);
+ if (!nvbo->sysmem) {
+ free(nvbo);
return -ENOMEM;
}
}
- bo->base.size = size;
- bo->base.offset = bo->drm.offset;
- bo->base.handle = (unsigned long)bo;
- bo->refcount = 1;
- *userbo = &bo->base;
+ 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_user(struct nouveau_device *userdev, void *ptr, int size,
- struct nouveau_bo **userbo)
+nouveau_bo_user(struct nouveau_device *dev, void *ptr, int size,
+ struct nouveau_bo **bo)
{
- struct nouveau_bo_priv *bo;
+ struct nouveau_bo_priv *nvbo;
- if (!userdev || !userbo || *userbo)
+ if (!dev || !bo || *bo)
return -EINVAL;
- bo = calloc(1, sizeof(*bo));
- if (!bo)
+ nvbo = calloc(1, sizeof(*nvbo));
+ if (!nvbo)
return -ENOMEM;
- bo->base.device = userdev;
+ nvbo->base.device = dev;
- 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->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_ref(struct nouveau_device *userdev, uint64_t handle,
- struct nouveau_bo **userbo)
+nouveau_bo_ref(struct nouveau_device *dev, uint64_t handle,
+ struct nouveau_bo **bo)
{
- struct nouveau_bo_priv *bo = (void *)(unsigned long)handle;
+ struct nouveau_bo_priv *nvbo = ptr_to_bo(handle);
- if (!userdev || !userbo || *userbo)
+ if (!dev || !bo || *bo)
return -EINVAL;
- bo->refcount++;
- *userbo = &bo->base;
+ nvbo->refcount++;
+ *bo = &nvbo->base;
return 0;
}
int
-nouveau_bo_resize(struct nouveau_bo *userbo, int size)
+nouveau_bo_resize(struct nouveau_bo *bo, int size)
{
- struct nouveau_bo_priv *bo = nouveau_bo(userbo);
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
int ret;
- if (!bo || bo->user)
+ if (!nvbo || nvbo->user)
return -EINVAL;
- if (bo->sysmem) {
- bo->sysmem = realloc(bo->sysmem, size);
- if (!bo->sysmem)
+ if (nvbo->sysmem) {
+ nvbo->sysmem = realloc(nvbo->sysmem, size);
+ if (!nvbo->sysmem)
return -ENOMEM;
} else {
- ret = nouveau_bo_realloc_gpu(bo, 0, size);
+ ret = nouveau_bo_realloc_gpu(nvbo, 0, size);
if (ret)
return ret;
}
- bo->base.size = size;
+ nvbo->base.size = size;
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_bo_realloc_gpu(nvbo, 0, 0);
+ 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
+ nouveau_fence_wait(&nvbo->wr_fence);
+
+ if (nvbo->sysmem)
+ bo->map = nvbo->sysmem;
else
- userbo->map = bo->map;
+ 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;
}
-void
-nouveau_bo_emit_reloc(struct nouveau_channel *userchan, void *ptr,
- struct nouveau_bo *userbo, uint32_t data, uint32_t flags,
- uint32_t vor, uint32_t tor)
+static int
+nouveau_bo_upload(struct nouveau_bo_priv *nvbo)
{
- struct nouveau_channel_priv *chan = nouveau_channel(userchan);
- struct nouveau_bo_priv *bo = nouveau_bo(userbo);
- struct nouveau_bo_reloc *r;
- int i, on_list = 0;
-
- for (i = 0; i < chan->nr_buffers; i++) {
- if (chan->buffers[i].bo == bo) {
- on_list = 1;
- break;
- }
- }
+ if (nvbo->fence)
+ nouveau_fence_wait(&nvbo->fence);
+ memcpy(nvbo->map, nvbo->sysmem, nvbo->drm.size);
+ return 0;
+}
- if (i >= 128)
- return;
+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 (on_list) {
- chan->buffers[i].flags &= (flags | NOUVEAU_BO_RDWR);
- chan->buffers[i].flags |= (flags & NOUVEAU_BO_RDWR);
- } else {
- chan->buffers[i].bo = bo;
- chan->buffers[i].flags = flags;
- chan->nr_buffers++;
- }
+ 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;
- if (chan->num_relocs >= chan->max_relocs)
- FIRE_RING_CH(userchan);
- r = &chan->relocs[chan->num_relocs++];
+ memcpy(nvdev->sa_map + r->start, nvbo->sysmem, bo->size);
- r->ptr = ptr;
- r->bo = bo;
- r->data = data;
- r->flags = flags;
- r->vor = vor;
- r->tor = tor;
+ nvbo->offset = nvdev->sa.offset + r->start;
+ nvbo->flags = NOUVEAU_BO_GART;
+ return 0;
}
static int
-nouveau_bo_upload(struct nouveau_bo_priv *bo)
+nouveau_bo_validate_bo(struct nouveau_channel *chan, struct nouveau_bo *bo,
+ struct nouveau_fence *fence, uint32_t flags)
{
- memcpy(bo->map, bo->sysmem, bo->drm.size);
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+
+ if (!nvbo->drm.size) {
+ nouveau_bo_realloc_gpu(nvbo, flags, nvbo->base.size);
+ nouveau_bo_upload(nvbo);
+ if (!nvbo->user) {
+ free(nvbo->sysmem);
+ nvbo->sysmem = NULL;
+ }
+ } else
+ 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->flags = NOUVEAU_BO_VRAM;
+
return 0;
}
-void
-nouveau_bo_validate(struct nouveau_channel *userchan)
+int
+nouveau_bo_validate(struct nouveau_channel *chan, struct nouveau_bo *bo,
+ struct nouveau_fence *fence, uint32_t flags)
{
- struct nouveau_channel_priv *chan = nouveau_channel(userchan);
- int i;
-
- for (i = 0; i < chan->nr_buffers; i++) {
- struct nouveau_bo_priv *bo = chan->buffers[i].bo;
-
- if (!bo->drm.size) {
- nouveau_bo_realloc_gpu(bo, chan->buffers[i].flags,
- bo->base.size);
- nouveau_bo_upload(bo);
- } else
- if (bo->user || bo->base.map)
- nouveau_bo_upload(bo);
-
- if (!bo->user && !bo->base.map) {
- free(bo->sysmem);
- bo->sysmem = NULL;
- }
+ struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
+ int ret;
+ assert(bo->map == NULL);
- bo->base.offset = bo->drm.offset;
- if (bo->drm.flags & NOUVEAU_MEM_AGP)
- bo->base.flags = NOUVEAU_BO_GART;
- else
- bo->base.flags = NOUVEAU_BO_VRAM;
+ 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;
}
- chan->nr_buffers = 0;
+
+ if (flags & NOUVEAU_BO_WR)
+ nouveau_fence_ref(fence, &nvbo->wr_fence);
+ nouveau_fence_ref(fence, &nvbo->fence);
+ return 0;
}