nouveau: use "indirect" push buffers

This commit is contained in:
Ben Skeggs
2007-12-09 20:38:08 +11:00
parent b8965bee40
commit 62bbf8db87
11 changed files with 474 additions and 67 deletions
@@ -17,9 +17,11 @@ DRIVER_SOURCES = \
nouveau_context.c \
nouveau_device.c \
nouveau_dma.c \
nouveau_fence.c \
nouveau_grobj.c \
nouveau_lock.c \
nouveau_notifier.c \
nouveau_pushbuf.c \
nouveau_screen.c \
nouveau_swapbuffers.c \
nouveau_winsys.c \
@@ -91,6 +91,7 @@ nouveau_channel_alloc(struct nouveau_device *userdev, uint32_t fb_ctxdma,
malloc(sizeof(struct nouveau_bo_reloc) * chan->max_relocs);
nouveau_dma_channel_init(&chan->base);
nouveau_pushbuf_init(&chan->base);
*userchan = &chan->base;
return 0;
@@ -44,6 +44,7 @@ struct nouveau_context {
struct nouveau_bo *reloc_head;
/* Hardware context */
uint32_t *pushbuf;
struct nouveau_channel *channel;
struct nouveau_notifier *sync_notifier;
struct nouveau_grobj *NvNull;
@@ -98,4 +99,7 @@ extern void UNLOCK_HARDWARE(struct nouveau_context *);
extern int nouveau_surface_init_nv04(struct nouveau_context *);
extern int nouveau_surface_init_nv50(struct nouveau_context *);
extern uint32_t *nouveau_pipe_dma_beginp(struct nouveau_grobj *, int, int);
extern void nouveau_pipe_dma_kickoff(struct nouveau_channel *);
#endif
@@ -141,35 +141,6 @@ nouveau_dma_kickoff(struct nouveau_channel *userchan)
if (chan->dma.cur == chan->dma.put)
return;
if (chan->num_relocs) {
nouveau_bo_validate(userchan);
for (i = 0; i < chan->num_relocs; i++) {
struct nouveau_bo_reloc *r = &chan->relocs[i];
uint32_t push;
if (r->flags & NOUVEAU_BO_LOW) {
push = r->bo->base.offset + r->data;
} else
if (r->flags & NOUVEAU_BO_HIGH) {
push = (r->bo->base.offset + r->data) >> 32;
} else {
push = r->data;
}
if (r->flags & NOUVEAU_BO_OR) {
if (r->bo->base.flags & NOUVEAU_BO_VRAM)
push |= r->vor;
else
push |= r->tor;
}
*r->ptr = push;
}
chan->num_relocs = 0;
}
#ifdef NOUVEAU_DMA_DEBUG
if (chan->dma.push_free) {
NOUVEAU_ERR("Packet incomplete: %d left\n", chan->dma.push_free);
@@ -178,8 +149,20 @@ nouveau_dma_kickoff(struct nouveau_channel *userchan)
#endif
#ifdef NOUVEAU_DMA_DUMP_POSTRELOC_PUSHBUF
for (i = chan->dma.put; i < chan->dma.cur; i++)
NOUVEAU_MSG("0x%08x\n", chan->pushbuf[i]);
for (i = chan->dma.put; i < chan->dma.cur; i++) {
NOUVEAU_MSG("0x%08x 0x%08x\n", (i<<2)+chan->dma.base,
chan->pushbuf[i]);
if ((chan->pushbuf[i] & 0xf0000000) == 0x20000000) {
int n = (((chan->pushbuf[i] & 0x0fffffff) -
chan->dma.base) / 4);
do {
NOUVEAU_MSG("\t0x%08x 0x%08x\n",
(n<<2)+chan->dma.base,
chan->pushbuf[n]);
} while ((chan->pushbuf[n++]&0xf0000000) != 0x20000000);
}
}
#endif
put_offset = (chan->dma.cur << 2) + chan->dma.base;
@@ -32,6 +32,7 @@
#include "pipe/nouveau/nouveau_grobj.h"
#include "pipe/nouveau/nouveau_notifier.h"
#include "pipe/nouveau/nouveau_bo.h"
#include "pipe/nouveau/nouveau_resource.h"
struct nouveau_device_priv {
struct nouveau_device base;
@@ -59,6 +60,61 @@ nouveau_device_get_param(struct nouveau_device *, uint64_t param, uint64_t *v);
extern int
nouveau_device_set_param(struct nouveau_device *, uint64_t param, uint64_t val);
struct nouveau_fence {
struct nouveau_channel *channel;
};
struct nouveau_fence_priv {
struct nouveau_fence base;
int refcount;
struct nouveau_fence *next;
uint32_t sequence;
int emitted;
int signalled;
};
#define nouveau_fence(n) ((struct nouveau_fence_priv *)(n))
extern int
nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **);
extern int
nouveau_fence_ref(struct nouveau_fence *, struct nouveau_fence **);
extern void
nouveau_fence_del(struct nouveau_fence **);
extern void
nouveau_fence_emit(struct nouveau_fence *);
extern int
nouveau_fence_wait(struct nouveau_fence **);
extern void
nouveau_fence_flush(struct nouveau_channel *);
struct nouveau_pushbuf {
struct nouveau_channel *channel;
unsigned remaining;
uint32_t *cur;
};
struct nouveau_pushbuf_priv {
struct nouveau_pushbuf base;
struct nouveau_pushbuf *next;
struct nouveau_resource *res;
struct nouveau_fence *fence;
};
#define nouveau_pushbuf(n) ((struct nouveau_pushbuf_priv *)(n))
extern int
nouveau_pushbuf_init(struct nouveau_channel *);
extern int
nouveau_pushbuf_flush(struct nouveau_channel *);
struct nouveau_channel_priv {
struct nouveau_channel base;
@@ -95,6 +151,14 @@ struct nouveau_channel_priv {
struct nouveau_bo_reloc *relocs;
int num_relocs;
int max_relocs;
struct nouveau_fence *fence_head;
struct nouveau_fence *fence_tail;
uint32_t fence_sequence;
struct nouveau_resource *pb_heap;
struct nouveau_pushbuf *pb_head;
struct nouveau_pushbuf *pb_tail;
};
#define nouveau_channel(n) ((struct nouveau_channel_priv *)(n))
@@ -148,6 +212,8 @@ nouveau_notifier_wait_status(struct nouveau_notifier *, int id, int status,
struct nouveau_bo_priv {
struct nouveau_bo base;
struct nouveau_fence *fence;
struct drm_nouveau_mem_alloc drm;
void *map;
@@ -203,4 +269,14 @@ nouveau_bo_emit_reloc(struct nouveau_channel *chan, void *ptr,
extern void
nouveau_bo_validate(struct nouveau_channel *);
extern int
nouveau_resource_init(struct nouveau_resource **heap, int size);
extern int
nouveau_resource_alloc(struct nouveau_resource *heap, int size, void *priv,
struct nouveau_resource **);
extern void
nouveau_resource_free(struct nouveau_resource **);
#endif
@@ -0,0 +1,135 @@
/*
* Copyright 2007 Nouveau Project
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdlib.h>
#include <errno.h>
#include "nouveau_drmif.h"
#include "nouveau_dma.h"
#include "nouveau_local.h"
int
nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **fence)
{
struct nouveau_fence_priv *nvfence;
if (!chan || !fence || *fence)
return -EINVAL;
nvfence = calloc(1, sizeof(struct nouveau_fence_priv));
if (!nvfence)
return -ENOMEM;
nvfence->base.channel = chan;
nvfence->refcount = 1;
*fence = &nvfence->base;
return 0;
}
int
nouveau_fence_ref(struct nouveau_fence *ref, struct nouveau_fence **fence)
{
struct nouveau_fence_priv *nvfence;
if (!ref || !fence || *fence)
return -EINVAL;
nvfence = nouveau_fence(ref);
nvfence->refcount++;
*fence = &nvfence->base;
return 0;
}
void
nouveau_fence_del(struct nouveau_fence **fence)
{
struct nouveau_fence_priv *nvfence;
if (!fence || !*fence)
return;
nvfence = nouveau_fence(*fence);
*fence = NULL;
if (--nvfence->refcount <= 0) {
if (nvfence->emitted && !nvfence->signalled)
nouveau_fence_wait((void *)&nvfence);
free(nvfence);
}
}
void
nouveau_fence_emit(struct nouveau_fence *fence)
{
struct nouveau_channel_priv *nvchan = nouveau_channel(fence->channel);
struct nouveau_fence_priv *nvfence = nouveau_fence(fence);
nvfence->emitted = 1;
nvfence->sequence = ++nvchan->fence_sequence;
if (nvfence->sequence == 0xffffffff)
NOUVEAU_ERR("AII wrap unhandled\n");
BEGIN_RING_CH(&nvchan->base, nvchan->subchannel[0].grobj, 0x50, 1);
OUT_RING_CH (&nvchan->base, nvfence->sequence);
if (nvchan->fence_tail) {
nouveau_fence(nvchan->fence_tail)->next = fence;
} else {
nvchan->fence_head = fence;
}
nvchan->fence_tail = fence;
}
void
nouveau_fence_flush(struct nouveau_channel *chan)
{
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
struct nouveau_fence_priv *nvfence = nouveau_fence(nvchan->fence_head);
uint32_t sequence = *nvchan->ref_cnt;
while (nvchan->fence_head && nvfence->sequence <= sequence) {
nvfence->signalled = 1;
nvchan->fence_head = nvfence->next;
if (nvchan->fence_head == NULL)
nvchan->fence_tail = NULL;
nvfence = nouveau_fence(nvchan->fence_head);
}
}
int
nouveau_fence_wait(struct nouveau_fence **fence)
{
struct nouveau_fence_priv *nvfence;
if (!fence || !*fence)
return -EINVAL;
nvfence = nouveau_fence(*fence);
if (nvfence->emitted) {
while (!nvfence->signalled)
nouveau_fence_flush(nvfence->base.channel);
}
nouveau_fence_del(fence);
return 0;
}
@@ -26,15 +26,16 @@
/* Push buffer access macros */
#define BEGIN_RING(obj,mthd,size) do { \
BEGIN_RING_CH(nv->channel, nv->obj, (mthd), (size)); \
nv->pushbuf = nouveau_pipe_dma_beginp(nv->obj, (mthd), (size)); \
} while(0)
#define OUT_RING(data) do { \
OUT_RING_CH(nv->channel, (data)); \
(*nv->pushbuf++) = (data); \
} while(0)
#define OUT_RINGp(src,size) do { \
OUT_RINGp_CH(nv->channel, (src), (size)); \
memcpy(nv->pushbuf, (src), (size)<<2); \
nv->pushbuf += (size); \
} while(0)
#define OUT_RINGf(data) do { \
@@ -43,18 +44,13 @@
OUT_RING(c.u); \
} while(0)
#define WAIT_RING(size) do { \
WAIT_RING_CH(nv->channel, (size)); \
} while(0)
#define FIRE_RING() do { \
FIRE_RING_CH(nv->channel); \
nouveau_pipe_dma_kickoff(nv->channel); \
} while(0)
#define OUT_RELOC(bo,data,flags,vor,tor) do { \
struct nouveau_channel_priv *chan = nouveau_channel(nv->channel); \
nouveau_bo_emit_reloc(nv->channel, &chan->pushbuf[chan->dma.cur], \
(void*)(bo), (data), (flags), (vor), (tor)); \
nouveau_bo_emit_reloc(nv->channel, nv->pushbuf, (void*)(bo), (data), \
(flags), (vor), (tor)); \
OUT_RING(0); \
} while(0)
@@ -0,0 +1,177 @@
/*
* Copyright 2007 Nouveau Project
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include "nouveau_drmif.h"
#include "nouveau_dma.h"
int
nouveau_pushbuf_init(struct nouveau_channel *chan)
{
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
if (!nvchan)
return -EINVAL;
/* Everything except first 4KiB of the push buffer is managed by us */
if (nouveau_resource_init(&nvchan->pb_heap,
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;
return 0;
}
/* This would be our TTM "superioctl" */
int
nouveau_pushbuf_flush(struct nouveau_channel *chan)
{
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(nvchan->pb_tail);
int i, ret;
if (!nvpb)
goto out_realloc;
if (nvpb->base.remaining == nvpb->res->size / 4)
return 0;
ret = nouveau_fence_new(chan, &nvpb->fence);
if (ret)
return ret;
/* Validate buffers */
nouveau_bo_validate(chan);
/* Apply relocations */
if (nvchan->num_relocs) {
for (i = 0; i < nvchan->num_relocs; i++) {
struct nouveau_bo_reloc *r = &nvchan->relocs[i];
uint32_t push;
if (r->flags & NOUVEAU_BO_LOW) {
push = r->bo->base.offset + r->data;
} else
if (r->flags & NOUVEAU_BO_HIGH) {
push = (r->bo->base.offset + r->data) >> 32;
} else {
push = r->data;
}
if (r->flags & NOUVEAU_BO_OR) {
if (r->bo->base.flags & NOUVEAU_BO_VRAM)
push |= r->vor;
else
push |= r->tor;
}
*r->ptr = push;
}
nvchan->num_relocs = 0;
}
/* Emit JMP to indirect pushbuf */
if (nvchan->dma.free < 1)
WAIT_RING_CH(chan, 1);
nvchan->dma.free -= 1;
OUT_RING_CH(chan, 0x20000000 | (nvpb->res->start + 4096));
/* Add JMP back to master pushbuf from indirect pushbuf */
(*nvpb->base.cur++) =
0x20000000 | ((nvchan->dma.cur << 2) + nvchan->dma.base);
/* Fence */
nouveau_fence_emit(nvpb->fence);
/* Kickoff */
FIRE_RING_CH(chan);
/* Allocate space for next push buffer */
out_realloc:
nvpb = calloc(1, sizeof(struct nouveau_pushbuf_priv));
if (!nvpb)
return -ENOMEM;
if (nouveau_resource_alloc(nvchan->pb_heap, 0x2000, NULL, &nvpb->res)) {
struct nouveau_pushbuf_priv *e;
int nr = 0;
/* Update fences */
nouveau_fence_flush(chan);
/* Free any push buffers that have already been executed */
e = nouveau_pushbuf(nvchan->pb_head);
while (e && e->fence) {
if (!e->fence || !nouveau_fence(e->fence)->signalled)
break;
nouveau_fence_del(&e->fence);
nouveau_resource_free(&e->res);
nr++;
nvchan->pb_head = e->next;
if (nvchan->pb_head == NULL)
nvchan->pb_tail = NULL;
free(e);
e = nouveau_pushbuf(nvchan->pb_head);
}
/* We didn't free any buffers above. As a last resort, busy
* wait on the oldest buffer becoming available.
*/
if (!nr) {
e = nouveau_pushbuf(nvchan->pb_head);
nouveau_fence_wait(&e->fence);
nouveau_resource_free(&e->res);
nvchan->pb_head = e->next;
if (nvchan->pb_head == NULL)
nvchan->pb_tail = NULL;
free(e);
}
if (nouveau_resource_alloc(nvchan->pb_heap, 0x2000, nvpb,
&nvpb->res))
assert(0);
}
nvpb->base.channel = chan;
nvpb->base.remaining = nvpb->res->size / 4;
nvpb->base.cur = &nvchan->pushbuf[(nvpb->res->start + 4096)/4];
if (nvchan->pb_tail) {
nouveau_pushbuf(nvchan->pb_tail)->next = & nvpb->base;
} else {
nvchan->pb_head = &nvpb->base;
}
nvchan->pb_tail = &nvpb->base;
return 0;
}
@@ -5,7 +5,7 @@
#include "pipe/nouveau/nouveau_winsys.h"
static int
int
nouveau_resource_init(struct nouveau_resource **heap, int size)
{
struct nouveau_resource *r;
@@ -20,7 +20,7 @@ nouveau_resource_init(struct nouveau_resource **heap, int size)
return 0;
}
static int
int
nouveau_resource_alloc(struct nouveau_resource *heap, int size, void *priv,
struct nouveau_resource **res)
{
@@ -58,7 +58,7 @@ nouveau_resource_alloc(struct nouveau_resource *heap, int size, void *priv,
return 1;
}
static void
void
nouveau_resource_free(struct nouveau_resource **res)
{
struct nouveau_resource *r;
@@ -108,25 +108,31 @@ nouveau_pipe_grobj_alloc(struct nouveau_winsys *nvws, int grclass,
grclass, grobj);
}
static uint32_t *
uint32_t *
nouveau_pipe_dma_beginp(struct nouveau_grobj *grobj, int mthd, int size)
{
struct nouveau_channel_priv *chan = nouveau_channel(grobj->channel);
struct nouveau_channel_priv *nvchan = nouveau_channel(grobj->channel);
uint32_t *pushbuf;
BEGIN_RING_CH(&chan->base, grobj, mthd, size);
pushbuf = &chan->pushbuf[chan->dma.cur];
chan->dma.cur += size;
#ifdef NOUVEAU_DMA_DEBUG
chan->dma.push_free -= size;
#endif
if (!nvchan->pb_tail || nvchan->pb_tail->remaining < (size + 1))
nouveau_pushbuf_flush(grobj->channel);
if (grobj->bound == NOUVEAU_GROBJ_UNBOUND)
nouveau_dma_subc_bind(grobj);
nvchan->subchannel[grobj->subc].seq = nvchan->subc_sequence++;
pushbuf = nvchan->pb_tail->cur;
nvchan->pb_tail->cur += (size + 1);
nvchan->pb_tail->remaining -= (size + 1);
(*pushbuf++) = ((grobj->subc << 13) | (size << 18) | mthd);
return pushbuf;
}
static void
nouveau_pipe_dma_kickoff(struct nouveau_channel *userchan)
void
nouveau_pipe_dma_kickoff(struct nouveau_channel *chan)
{
FIRE_RING_CH(userchan);
nouveau_pushbuf_flush(chan);
}
static int
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright 2007 Nouveau Project
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef __NOUVEAU_RESOURCE_H__
#define __NOUVEAU_RESOURCE_H__
struct nouveau_resource {
struct nouveau_resource *prev;
struct nouveau_resource *next;
int in_use;
void *priv;
unsigned int start;
unsigned int size;
};
#endif
+1 -11
View File
@@ -10,17 +10,7 @@
#include "pipe/nouveau/nouveau_class.h"
#include "pipe/nouveau/nouveau_grobj.h"
#include "pipe/nouveau/nouveau_notifier.h"
struct nouveau_resource {
struct nouveau_resource *prev;
struct nouveau_resource *next;
boolean in_use;
void *priv;
uint start;
uint size;
};
#include "pipe/nouveau/nouveau_resource.h"
struct nouveau_winsys {
struct nouveau_context *nv;