blob: 1ad539d28582e7786eedbfa2e2d349d5ea0e2aab [file] [log] [blame]
#include <pipe/p_defines.h>
#include <pipe/p_screen.h>
#include <pipe/p_state.h>
#include <util/u_memory.h>
#include <errno.h>
#include "nouveau/nouveau_bo.h"
#include "nouveau_winsys.h"
#include "nouveau_screen.h"
static const char *
nouveau_screen_get_name(struct pipe_screen *pscreen)
{
struct nouveau_device *dev = nouveau_screen(pscreen)->device;
static char buffer[128];
snprintf(buffer, sizeof(buffer), "NV%02X", dev->chipset);
return buffer;
}
static const char *
nouveau_screen_get_vendor(struct pipe_screen *pscreen)
{
return "nouveau";
}
static struct pipe_buffer *
nouveau_screen_bo_skel(struct pipe_screen *pscreen, struct nouveau_bo *bo,
unsigned alignment, unsigned usage, unsigned size)
{
struct pipe_buffer *pb;
pb = CALLOC(1, sizeof(struct pipe_buffer)+sizeof(struct nouveau_bo *));
if (!pb) {
nouveau_bo_ref(NULL, &bo);
return NULL;
}
pipe_reference_init(&pb->reference, 1);
pb->screen = pscreen;
pb->alignment = alignment;
pb->usage = usage;
pb->size = size;
*(struct nouveau_bo **)(pb + 1) = bo;
return pb;
}
static struct pipe_buffer *
nouveau_screen_bo_new(struct pipe_screen *pscreen, unsigned alignment,
unsigned usage, unsigned size)
{
struct nouveau_device *dev = nouveau_screen(pscreen)->device;
struct nouveau_bo *bo = NULL;
uint32_t flags = NOUVEAU_BO_MAP;
int ret;
if (usage & NOUVEAU_BUFFER_USAGE_TRANSFER)
flags |= NOUVEAU_BO_GART;
else
if (usage & PIPE_BUFFER_USAGE_VERTEX) {
if (pscreen->get_param(pscreen, NOUVEAU_CAP_HW_VTXBUF))
flags |= NOUVEAU_BO_GART;
} else
if (usage & PIPE_BUFFER_USAGE_INDEX) {
if (pscreen->get_param(pscreen, NOUVEAU_CAP_HW_IDXBUF))
flags |= NOUVEAU_BO_GART;
}
if (usage & PIPE_BUFFER_USAGE_PIXEL) {
if (usage & NOUVEAU_BUFFER_USAGE_TEXTURE)
flags |= NOUVEAU_BO_GART;
if (!(usage & PIPE_BUFFER_USAGE_CPU_READ_WRITE))
flags |= NOUVEAU_BO_VRAM;
if (dev->chipset == 0x50 || dev->chipset >= 0x80) {
flags |= NOUVEAU_BO_TILED;
if (usage & NOUVEAU_BUFFER_USAGE_ZETA)
flags |= NOUVEAU_BO_ZTILE;
}
}
ret = nouveau_bo_new(dev, flags, alignment, size, &bo);
if (ret)
return NULL;
return nouveau_screen_bo_skel(pscreen, bo, alignment, usage, size);
}
static struct pipe_buffer *
nouveau_screen_bo_user(struct pipe_screen *pscreen, void *ptr, unsigned bytes)
{
struct nouveau_device *dev = nouveau_screen(pscreen)->device;
struct nouveau_bo *bo = NULL;
int ret;
ret = nouveau_bo_user(dev, ptr, bytes, &bo);
if (ret)
return NULL;
return nouveau_screen_bo_skel(pscreen, bo, 0, 0, bytes);
}
static inline uint32_t
nouveau_screen_map_flags(unsigned pipe)
{
uint32_t flags = 0;
if (pipe & PIPE_BUFFER_USAGE_CPU_READ)
flags |= NOUVEAU_BO_RD;
if (pipe & PIPE_BUFFER_USAGE_CPU_WRITE)
flags |= NOUVEAU_BO_WR;
if (pipe & PIPE_BUFFER_USAGE_DISCARD)
flags |= NOUVEAU_BO_INVAL;
if (pipe & PIPE_BUFFER_USAGE_DONTBLOCK)
flags |= NOUVEAU_BO_NOWAIT;
else
if (pipe & 0 /*PIPE_BUFFER_USAGE_UNSYNCHRONIZED*/)
flags |= NOUVEAU_BO_NOSYNC;
return flags;
}
static void *
nouveau_screen_bo_map(struct pipe_screen *pscreen, struct pipe_buffer *pb,
unsigned usage)
{
struct nouveau_bo *bo = nouveau_bo(pb);
struct nouveau_screen *nscreen = nouveau_screen(pscreen);
int ret;
if (nscreen->pre_pipebuffer_map_callback) {
ret = nscreen->pre_pipebuffer_map_callback(pscreen, pb, usage);
if (ret) {
debug_printf("pre_pipebuffer_map_callback failed %d\n",
ret);
return NULL;
}
}
ret = nouveau_bo_map(bo, nouveau_screen_map_flags(usage));
if (ret) {
debug_printf("map failed: %d\n", ret);
return NULL;
}
return bo->map;
}
static void *
nouveau_screen_bo_map_range(struct pipe_screen *pscreen, struct pipe_buffer *pb,
unsigned offset, unsigned length, unsigned usage)
{
struct nouveau_bo *bo = nouveau_bo(pb);
struct nouveau_screen *nscreen = nouveau_screen(pscreen);
uint32_t flags = nouveau_screen_map_flags(usage);
int ret;
if (nscreen->pre_pipebuffer_map_callback) {
ret = nscreen->pre_pipebuffer_map_callback(pscreen, pb, usage);
if (ret) {
debug_printf("pre_pipebuffer_map_callback failed %d\n",
ret);
return NULL;
}
}
ret = nouveau_bo_map_range(bo, offset, length, flags);
if (ret) {
nouveau_bo_unmap(bo);
if (!(flags & NOUVEAU_BO_NOWAIT) || ret != -EBUSY)
debug_printf("map_range failed: %d\n", ret);
return NULL;
}
return (char *)bo->map - offset; /* why gallium? why? */
}
static void
nouveau_screen_bo_map_flush(struct pipe_screen *pscreen, struct pipe_buffer *pb,
unsigned offset, unsigned length)
{
struct nouveau_bo *bo = nouveau_bo(pb);
nouveau_bo_map_flush(bo, offset, length);
}
static void
nouveau_screen_bo_unmap(struct pipe_screen *pscreen, struct pipe_buffer *pb)
{
struct nouveau_bo *bo = nouveau_bo(pb);
nouveau_bo_unmap(bo);
}
static void
nouveau_screen_bo_del(struct pipe_buffer *pb)
{
struct nouveau_bo *bo = nouveau_bo(pb);
nouveau_bo_ref(NULL, &bo);
FREE(pb);
}
static void
nouveau_screen_fence_ref(struct pipe_screen *pscreen,
struct pipe_fence_handle **ptr,
struct pipe_fence_handle *pfence)
{
*ptr = pfence;
}
static int
nouveau_screen_fence_signalled(struct pipe_screen *screen,
struct pipe_fence_handle *pfence,
unsigned flags)
{
return 0;
}
static int
nouveau_screen_fence_finish(struct pipe_screen *screen,
struct pipe_fence_handle *pfence,
unsigned flags)
{
return 0;
}
int
nouveau_screen_init(struct nouveau_screen *screen, struct nouveau_device *dev)
{
struct pipe_screen *pscreen = &screen->base;
int ret;
ret = nouveau_channel_alloc(dev, 0xbeef0201, 0xbeef0202,
&screen->channel);
if (ret)
return ret;
screen->device = dev;
pscreen->get_name = nouveau_screen_get_name;
pscreen->get_vendor = nouveau_screen_get_vendor;
pscreen->buffer_create = nouveau_screen_bo_new;
pscreen->user_buffer_create = nouveau_screen_bo_user;
pscreen->buffer_map = nouveau_screen_bo_map;
pscreen->buffer_map_range = nouveau_screen_bo_map_range;
pscreen->buffer_flush_mapped_range = nouveau_screen_bo_map_flush;
pscreen->buffer_unmap = nouveau_screen_bo_unmap;
pscreen->buffer_destroy = nouveau_screen_bo_del;
pscreen->fence_reference = nouveau_screen_fence_ref;
pscreen->fence_signalled = nouveau_screen_fence_signalled;
pscreen->fence_finish = nouveau_screen_fence_finish;
return 0;
}
void
nouveau_screen_fini(struct nouveau_screen *screen)
{
struct pipe_winsys *ws = screen->base.winsys;
nouveau_channel_free(&screen->channel);
ws->destroy(ws);
}