drm/libdrm/nouveau/nouveau_dma.c

216 lines
5.3 KiB
C

/*
* 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 <stdint.h>
#include <assert.h>
#include <errno.h>
#include "nouveau_drmif.h"
#include "nouveau_dma.h"
static inline uint32_t
READ_GET(struct nouveau_channel_priv *nvchan)
{
return *nvchan->get;
}
static inline void
WRITE_PUT(struct nouveau_channel_priv *nvchan, uint32_t val)
{
uint32_t put = ((val << 2) + nvchan->dma->base);
volatile int dum;
NOUVEAU_DMA_BARRIER;
dum = READ_GET(nvchan);
*nvchan->put = put;
nvchan->dma->put = val;
#ifdef NOUVEAU_DMA_TRACE
printf("WRITE_PUT %d/0x%08x\n", nvchan->drm.channel, put);
#endif
NOUVEAU_DMA_BARRIER;
}
static inline int
LOCAL_GET(struct nouveau_dma_priv *dma, uint32_t *val)
{
uint32_t get = *val;
if (get >= dma->base && get <= (dma->base + (dma->max << 2))) {
*val = (get - dma->base) >> 2;
return 1;
}
return 0;
}
void
nouveau_dma_channel_init(struct nouveau_channel *chan)
{
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
int i;
nvchan->dma = &nvchan->struct_dma;
nvchan->dma->base = nvchan->drm.put_base;
nvchan->dma->cur = nvchan->dma->put = 0;
nvchan->dma->max = (nvchan->drm.cmdbuf_size >> 2) - 2;
nvchan->dma->free = nvchan->dma->max - nvchan->dma->cur;
RING_SPACE_CH(chan, RING_SKIPS);
for (i = 0; i < RING_SKIPS; i++)
OUT_RING_CH(chan, 0);
}
#define CHECK_TIMEOUT() do { \
if ((NOUVEAU_TIME_MSEC() - t_start) > NOUVEAU_DMA_TIMEOUT) \
return - EBUSY; \
} while(0)
int
nouveau_dma_wait(struct nouveau_channel *chan, int size)
{
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
struct nouveau_dma_priv *dma = nvchan->dma;
uint32_t get, t_start;
FIRE_RING_CH(chan);
t_start = NOUVEAU_TIME_MSEC();
while (dma->free < size) {
CHECK_TIMEOUT();
get = READ_GET(nvchan);
if (!LOCAL_GET(dma, &get))
continue;
if (dma->put >= get) {
dma->free = dma->max - dma->cur;
if (dma->free < size) {
#ifdef NOUVEAU_DMA_DEBUG
dma->push_free = 1;
#endif
OUT_RING_CH(chan, 0x20000000 | dma->base);
if (get <= RING_SKIPS) {
/*corner case - will be idle*/
if (dma->put <= RING_SKIPS)
WRITE_PUT(nvchan,
RING_SKIPS + 1);
do {
CHECK_TIMEOUT();
get = READ_GET(nvchan);
if (!LOCAL_GET(dma, &get))
get = 0;
} while (get <= RING_SKIPS);
}
WRITE_PUT(nvchan, RING_SKIPS);
dma->cur = dma->put = RING_SKIPS;
dma->free = get - (RING_SKIPS + 1);
}
} else {
dma->free = get - dma->cur - 1;
}
}
return 0;
}
#ifdef NOUVEAU_DMA_DUMP_POSTRELOC_PUSHBUF
static void
nouveau_dma_parse_pushbuf(struct nouveau_channel *chan, int get, int put)
{
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
unsigned mthd_count = 0;
while (get != put) {
uint32_t gpuget = (get << 2) + nvchan->drm.put_base;
uint32_t data;
if (get < 0 || get >= nvchan->drm.cmdbuf_size)
assert(0);
data = nvchan->pushbuf[get++];
if (mthd_count) {
printf("0x%08x 0x%08x\n", gpuget, data);
mthd_count--;
continue;
}
switch (data & 0x60000000) {
case 0x00000000:
mthd_count = (data >> 18) & 0x7ff;
printf("0x%08x 0x%08x MTHD "
"Sc %d Mthd 0x%04x Size %d\n",
gpuget, data, (data>>13) & 7, data & 0x1ffc,
mthd_count);
break;
case 0x20000000:
get = (data & 0x1ffffffc) >> 2;
printf("0x%08x 0x%08x JUMP 0x%08x\n",
gpuget, data, data & 0x1ffffffc);
continue;
case 0x40000000:
mthd_count = (data >> 18) & 0x7ff;
printf("0x%08x 0x%08x NINC "
"Sc %d Mthd 0x%04x Size %d\n",
gpuget, data, (data>>13) & 7, data & 0x1ffc,
mthd_count);
break;
case 0x60000000:
/* DMA_OPCODE_CALL apparently, doesn't seem to work on
* my NV40 at least..
*/
/* fall-through */
default:
printf("DMA_PUSHER 0x%08x 0x%08x\n", gpuget, data);
assert(0);
}
}
}
#endif
void
nouveau_dma_kickoff(struct nouveau_channel *chan)
{
struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
struct nouveau_dma_priv *dma = nvchan->dma;
if (dma->cur == dma->put)
return;
#ifdef NOUVEAU_DMA_DEBUG
if (dma->push_free) {
printf("Packet incomplete: %d left\n", dma->push_free);
return;
}
#endif
#ifdef NOUVEAU_DMA_DUMP_POSTRELOC_PUSHBUF
nouveau_dma_parse_pushbuf(chan, dma->put, dma->cur);
#endif
WRITE_PUT(nvchan, dma->cur);
}