xserver-multidpi/hw/kdrive/ati/ati_dma.c
Eric Anholt 7cab70d1cb Improve error handling, especially in the DRI case. Do some FatalErrors
instead of ErrorFs for things that are really bad, and put limits on
    some loops. Now, sometimes instead of hanging the entire system, we
    (mostly-) cleanly drop to console when the card has hung.
2004-09-12 19:52:51 +00:00

1005 lines
25 KiB
C

/*
* Copyright © 2004 Eric Anholt
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Eric Anholt not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. Eric Anholt makes no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* ERIC ANHOLT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL ERIC ANHOLT BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Header$ */
#include "ati.h"
#include "ati_reg.h"
#include "ati_dma.h"
#include "ati_draw.h"
#ifdef USE_DRI
#include "radeon_common.h"
#include "r128_common.h"
#include "ati_sarea.h"
#endif /* USE_DRI */
#include "agp.h"
#define DEBUG_FIFO 0
extern CARD32 r128_cce_microcode[];
extern CARD32 radeon_cp_microcode[][2];
extern CARD32 r200_cp_microcode[][2];
extern CARD32 r300_cp_microcode[][2];
static void
ATIDebugFifo(ATIScreenInfo *atis)
{
ATICardInfo *atic = atis->atic;
char *mmio = atic->reg_base;
if (atic->is_radeon) {
ErrorF("RADEON_REG_CP_CSQ_CNTL: 0x%08x\n",
MMIO_IN32(mmio, RADEON_REG_CP_CSQ_CNTL));
ErrorF("RADEON_REG_CP_CSQ_STAT: 0x%08x\n",
MMIO_IN32(mmio, RADEON_REG_CP_CSQ_STAT));
ErrorF("RADEON_REG_RBBM_STATUS: 0x%08x\n",
MMIO_IN32(mmio, RADEON_REG_RBBM_STATUS));
ErrorF("RADEON_REG_RB2D_DSTCACHE_CTLSTAT: 0x%08x\n",
MMIO_IN32(mmio, RADEON_REG_RB2D_DSTCACHE_CTLSTAT));
} else {
ErrorF("R128_REG_PM4_BUFFER_CNTL: 0x%08x\n",
MMIO_IN32(mmio, R128_REG_PM4_BUFFER_CNTL));
ErrorF("R128_REG_PM4_STAT: 0x%08x\n",
MMIO_IN32(mmio, R128_REG_PM4_STAT));
ErrorF("R128_REG_GUI_STAT: 0x%08x\n",
MMIO_IN32(mmio, R128_REG_GUI_STAT));
ErrorF("R128_REG_PC_NGUI_CTLSTAT: 0x%08x\n",
MMIO_IN32(mmio, R128_REG_PC_NGUI_CTLSTAT));
}
}
static void
ATIUploadMicrocode(ATIScreenInfo *atis)
{
ATICardInfo *atic = atis->atic;
char *mmio = atic->reg_base;
int i;
MMIO_OUT32(mmio, ATI_REG_MICROCODE_RAM_ADDR, 0);
if (atic->is_radeon && atic->is_r300) {
for (i = 0; i < 256; i++) {
MMIO_OUT32(mmio, ATI_REG_MICROCODE_RAM_DATAH,
r300_cp_microcode[i][1]);
MMIO_OUT32(mmio, ATI_REG_MICROCODE_RAM_DATAL,
r300_cp_microcode[i][0]);
}
} else if (atic->is_radeon && atic->is_r200) {
for (i = 0; i < 256; i++) {
MMIO_OUT32(mmio, ATI_REG_MICROCODE_RAM_DATAH,
r200_cp_microcode[i][1]);
MMIO_OUT32(mmio, ATI_REG_MICROCODE_RAM_DATAL,
r200_cp_microcode[i][0]);
}
} else if (atic->is_radeon && atic->is_r100) {
for (i = 0; i < 256; i++) {
MMIO_OUT32(mmio, ATI_REG_MICROCODE_RAM_DATAH,
radeon_cp_microcode[i][1]);
MMIO_OUT32(mmio, ATI_REG_MICROCODE_RAM_DATAL,
radeon_cp_microcode[i][0]);
}
} else {
for (i = 0; i < 256; i++) {
MMIO_OUT32(mmio, ATI_REG_MICROCODE_RAM_DATAH,
r128_cce_microcode[i * 2]);
MMIO_OUT32(mmio, ATI_REG_MICROCODE_RAM_DATAL,
r128_cce_microcode[i * 2 + 1]);
}
}
}
/* Required when reading from video memory after acceleration to make sure all
* data has been flushed to video memory from the pixel cache.
*/
static void
ATIFlushPixelCache(ATIScreenInfo *atis)
{
ATICardInfo *atic = atis->atic;
char *mmio = atic->reg_base;
int tries;
CARD32 temp;
if (atic->is_radeon) {
temp = MMIO_IN32(mmio, RADEON_REG_RB2D_DSTCACHE_CTLSTAT);
temp |= RADEON_RB2D_DC_FLUSH_ALL;
MMIO_OUT32(mmio, RADEON_REG_RB2D_DSTCACHE_CTLSTAT, temp);
for (tries = 1000000; tries != 0; tries--) {
if ((MMIO_IN32(mmio, RADEON_REG_RB2D_DSTCACHE_CTLSTAT) &
RADEON_RB2D_DC_BUSY) == 0)
break;
}
} else {
temp = MMIO_IN32(mmio, R128_REG_PC_NGUI_CTLSTAT);
temp |= R128_PC_FLUSH_ALL;
MMIO_OUT32(mmio, R128_REG_PC_NGUI_CTLSTAT, temp);
for (tries = 1000000; tries != 0; tries--) {
if ((MMIO_IN32(mmio, R128_REG_PC_NGUI_CTLSTAT) &
R128_PC_BUSY) != R128_PC_BUSY)
break;
}
}
if (tries == 0)
ErrorF("Timeout flushing pixel cache.\n");
}
static void
ATIEngineReset(ATIScreenInfo *atis)
{
ATICardInfo *atic = atis->atic;
char *mmio = atic->reg_base;
CARD32 clockcntlindex, mclkcntl;
#if DEBUG_FIFO
ErrorF("Engine Reset!\n");
ATIDebugFifo(atis);
#endif
ATIFlushPixelCache(atis);
clockcntlindex = MMIO_IN32(mmio, ATI_REG_CLOCK_CNTL_INDEX);
if (atic->is_r300)
R300CGWorkaround(atis);
if (atic->is_radeon) {
CARD32 host_path_cntl;
mclkcntl = INPLL(mmio, RADEON_REG_MCLK_CNTL);
OUTPLL(mmio, RADEON_REG_MCLK_CNTL, mclkcntl |
RADEON_FORCEON_MCLKA |
RADEON_FORCEON_MCLKB |
RADEON_FORCEON_YCLKA |
RADEON_FORCEON_YCLKB |
RADEON_FORCEON_MC |
RADEON_FORCEON_AIC);
host_path_cntl = MMIO_IN32(mmio, RADEON_REG_HOST_PATH_CNTL);
if (atic->is_r300) {
MMIO_OUT32(mmio, RADEON_REG_RBBM_SOFT_RESET,
RADEON_SOFT_RESET_CP |
RADEON_SOFT_RESET_HI |
RADEON_SOFT_RESET_E2);
} else {
MMIO_OUT32(mmio, RADEON_REG_RBBM_SOFT_RESET,
RADEON_SOFT_RESET_CP |
RADEON_SOFT_RESET_HI |
RADEON_SOFT_RESET_SE |
RADEON_SOFT_RESET_RE |
RADEON_SOFT_RESET_PP |
RADEON_SOFT_RESET_E2 |
RADEON_SOFT_RESET_RB);
}
MMIO_IN32(mmio, RADEON_REG_RBBM_SOFT_RESET);
MMIO_OUT32(mmio, RADEON_REG_RBBM_SOFT_RESET, 0);
MMIO_OUT32(mmio, RADEON_REG_HOST_PATH_CNTL, host_path_cntl |
RADEON_HDP_SOFT_RESET);
MMIO_IN32(mmio, RADEON_REG_HOST_PATH_CNTL);
MMIO_OUT32(mmio, RADEON_REG_HOST_PATH_CNTL, host_path_cntl);
MMIO_OUT32(mmio, ATI_REG_CLOCK_CNTL_INDEX, clockcntlindex);
OUTPLL(mmio, RADEON_REG_MCLK_CNTL, mclkcntl);
if (atic->is_r300)
R300CGWorkaround(atis);
} else {
CARD32 temp;
mclkcntl = INPLL(mmio, R128_REG_MCLK_CNTL);
OUTPLL(mmio, R128_REG_MCLK_CNTL,
mclkcntl | R128_FORCE_GCP | R128_FORCE_PIPE3D_CP);
temp = MMIO_IN32(mmio, R128_REG_GEN_RESET_CNTL);
MMIO_OUT32(mmio, R128_REG_GEN_RESET_CNTL,
temp | R128_SOFT_RESET_GUI);
temp = MMIO_IN32(mmio, R128_REG_GEN_RESET_CNTL);
MMIO_OUT32(mmio, R128_REG_GEN_RESET_CNTL,
temp & ~R128_SOFT_RESET_GUI);
temp = MMIO_IN32(mmio, R128_REG_GEN_RESET_CNTL);
OUTPLL(mmio, R128_REG_MCLK_CNTL, mclkcntl);
MMIO_OUT32(mmio, ATI_REG_CLOCK_CNTL_INDEX, clockcntlindex);
}
}
static void
ATIWaitAvailMMIO(ATIScreenInfo *atis, int n)
{
ATICardInfo *atic = atis->atic;
char *mmio = atic->reg_base;
int tries;
if (atis->mmio_avail >= n) {
atis->mmio_avail -= n;
return;
}
if (atic->is_radeon) {
for (tries = 1000000; tries != 0 && atis->mmio_avail < n; tries--)
{
atis->mmio_avail = MMIO_IN32(mmio,
RADEON_REG_RBBM_STATUS) & RADEON_RBBM_FIFOCNT_MASK;
}
} else {
for (tries = 1000000; tries != 0 && atis->mmio_avail < n; tries--)
{
atis->mmio_avail = MMIO_IN32(mmio, R128_REG_GUI_STAT) &
0xfff;
}
}
if (tries == 0) {
ErrorF("Timeout waiting for %d MMIO slots.\n", n);
ATIEngineReset(atis);
}
atis->mmio_avail -= n;
}
static int
ATIGetAvailPrimary(ATIScreenInfo *atis)
{
ATICardInfo *atic = atis->atic;
char *mmio = atic->reg_base;
if (atic->is_radeon) {
int csq_stat, diff;
csq_stat = MMIO_IN32(mmio, RADEON_REG_CP_CSQ_STAT);
diff = ((csq_stat & RADEON_CSQ_WPTR_PRIMARY_MASK) >> 8) -
(csq_stat & RADEON_CSQ_RPTR_PRIMARY_MASK);
if (diff < 0)
return -diff;
else
return atis->cce_pri_size - diff;
} else {
return MMIO_IN32(mmio, R128_REG_PM4_STAT) &
R128_PM4_FIFOCNT_MASK;
}
}
static void
ATIWaitAvailPrimary(ATIScreenInfo *atis, int n)
{
int tries;
if (atis->cce_pri_avail >= n) {
atis->cce_pri_avail -= n;
return;
}
for (tries = 1000000; tries != 0 && atis->cce_pri_avail < n; tries--)
{
atis->cce_pri_avail = ATIGetAvailPrimary(atis);
if (atis->cce_pri_avail >= n)
break;
}
if (tries == 0) {
ErrorF("Timeout waiting for %d CCE slots (%d avail).\n", n,
atis->cce_pri_avail);
ATIEngineReset(atis);
}
atis->cce_pri_avail -= n;
}
void
ATIWaitIdle(ATIScreenInfo *atis)
{
ATICardInfo *atic = atis->atic;
int tries;
char *mmio = atic->reg_base;
RING_LOCALS;
if (atis->indirectBuffer != NULL)
ATIFlushIndirect(atis, 0);
#ifdef USE_DRI
if (atis->using_dri) {
int ret;
int cmd = (atic->is_radeon ? DRM_RADEON_CP_IDLE :
DRM_R128_CCE_IDLE);
for (tries = 100; tries != 0; tries--) {
ret = drmCommandNone(atic->drmFd, cmd);
if (ret != -EBUSY)
break;
}
if (tries == 0) {
ATIDebugFifo(atis);
FatalError("Timed out idling CCE (card hung)\n");
}
if (ret != 0)
ErrorF("Failed to idle DMA, returned %d\n", ret);
return;
}
#endif
if (atic->is_radeon && (atis->using_pseudo || atis->using_dma)) {
BEGIN_DMA(4);
OUT_REG(RADEON_REG_RB2D_DSTCACHE_CTLSTAT,
RADEON_RB2D_DC_FLUSH_ALL);
OUT_REG(ATI_REG_WAIT_UNTIL,
RADEON_WAIT_HOST_IDLECLEAN | RADEON_WAIT_2D_IDLECLEAN |
RADEON_WAIT_3D_IDLECLEAN);
END_DMA();
}
if (!atic->is_radeon && (atis->using_pseudo || atis->using_dma)) {
ATIWaitAvailPrimary(atis, atis->cce_pri_size);
for (tries = 1000000; tries != 0; tries--) {
if ((MMIO_IN32(mmio, R128_REG_PM4_STAT) &
(R128_PM4_BUSY | R128_PM4_GUI_ACTIVE)) == 0)
break;
}
if (tries == 0) {
ErrorF("Timeout idling CCE, resetting...\n");
ATIEngineReset(atis);
}
}
/* Radeon CP idle is the same as MMIO idle. */
if (atis->using_pio || atic->is_radeon) {
/* Empty the fifo */
ATIWaitAvailMMIO(atis, 64);
if (atic->is_radeon) {
for (tries = 1000000; tries != 0; tries--) {
if ((MMIO_IN32(mmio, RADEON_REG_RBBM_STATUS) &
RADEON_RBBM_ACTIVE) == 0)
break;
}
} else {
for (tries = 1000000; tries != 0; tries--) {
if ((MMIO_IN32(mmio, R128_REG_GUI_STAT) &
R128_GUI_ACTIVE) == 0)
break;
}
}
if (tries == 0) {
ErrorF("Timeout idling accelerator, resetting...\n");
ATIEngineReset(atis);
}
}
ATIFlushPixelCache(atis);
#if DEBUG_FIFO
ErrorF("Idle?\n");
ATIDebugFifo(atis);
#endif
}
dmaBuf *
ATIGetDMABuffer(ATIScreenInfo *atis)
{
dmaBuf *buf;
buf = (dmaBuf *)xalloc(sizeof(dmaBuf));
if (buf == NULL)
return NULL;
#ifdef USE_DRI
if (atis->using_dri) {
buf->drmBuf = ATIDRIGetBuffer(atis);
if (buf->drmBuf == NULL) {
xfree(buf);
return NULL;
}
buf->size = buf->drmBuf->total;
buf->used = buf->drmBuf->used;
buf->address = buf->drmBuf->address;
return buf;
}
#endif /* USE_DRI */
if (atis->using_dma)
buf->size = atis->ring_len / 2;
else
buf->size = 512 * 1024;
buf->address = xalloc(buf->size);
if (buf->address == NULL) {
xfree(buf);
return NULL;
}
buf->used = 0;
return buf;
}
/* Decode a type-3 packet into MMIO register writes. Only some type-3 packets
* supported, and only partially.
*/
static void
ATIDispatchPacket3MMIO(ATIScreenInfo *atis, CARD32 header, CARD32 *addr,
int count)
{
ATICardInfo *atic = atis->atic;
char *mmio = atic->reg_base;
CARD32 settings;
int i = 0;
settings = addr[i++];
if ((settings & ATI_GMC_SRC_PITCH_OFFSET_CNTL) != 0)
MMIO_OUT32(mmio, ATI_REG_SRC_PITCH_OFFSET, addr[i++]);
if ((settings & ATI_GMC_DST_PITCH_OFFSET_CNTL) != 0)
MMIO_OUT32(mmio, ATI_REG_DST_PITCH_OFFSET, addr[i++]);
if ((settings & ATI_GMC_BRUSH_MASK) == ATI_GMC_BRUSH_SOLID_COLOR)
MMIO_OUT32(mmio, ATI_REG_DP_BRUSH_FRGD_CLR, addr[i++]);
switch (header & (ATI_CCE_PACKETTYPE_MASK |
ATI_CCE_PACKET3_IT_OPCODE_MASK))
{
case ATI_CCE_PACKET3_PAINT_MULTI:
while (i < count) {
MMIO_OUT32(mmio, ATI_REG_DST_Y_X,
(addr[i] >> 16) | (addr[i] << 16));
i++;
MMIO_OUT32(mmio, ATI_REG_DST_HEIGHT_WIDTH,
(addr[i] >> 16) | (addr[i] << 16));
i++;
}
break;
case ATI_CCE_PACKET3_BITBLT_MULTI:
while (i < count) {
MMIO_OUT32(mmio, ATI_REG_SRC_Y_X,
(addr[i] >> 16) | (addr[i] << 16));
i++;
MMIO_OUT32(mmio, ATI_REG_DST_Y_X,
(addr[i] >> 16) | (addr[i] << 16));
i++;
MMIO_OUT32(mmio, ATI_REG_DST_HEIGHT_WIDTH,
(addr[i] >> 16) | (addr[i] << 16));
i++;
}
break;
default:
ErrorF("Unsupported packet: 0x%x\n", header);
}
}
/* Dispatch packets by decoding them and writing to registers. Doesn't support
* the type 3 packets.
*/
static void
ATIDispatchIndirectMMIO(ATIScreenInfo *atis)
{
ATICardInfo *atic = atis->atic;
dmaBuf *buf = atis->indirectBuffer;
char *mmio = atic->reg_base;
CARD32 *addr;
CARD32 reg;
int i, n, count;
addr = (CARD32 *)((char *)buf->address + atis->indirectStart);
count = (buf->used - atis->indirectStart) / 4;
for (i = 0; i < count; i++) {
CARD32 header = addr[i];
switch (header & ATI_CCE_PACKETTYPE_MASK)
{
case ATI_CCE_PACKET0:
n = ((header & ATI_CCE_PACKET0_COUNT_MASK) >> 16) + 1;
reg = (header & ATI_CCE_PACKET0_REG_MASK) << 2;
ATIWaitAvailMMIO(atis, n);
while (n > 0) {
i++;
MMIO_OUT32(mmio, reg, addr[i]);
if ((header & ATI_CCE_PACKET0_ONE_REG_WR) == 0)
reg += 4;
n--;
}
break;
case ATI_CCE_PACKET1:
reg = (header & ATI_CCE_PACKET1_REG_1) << 2;
MMIO_OUT32(mmio, reg, addr[++i]);
reg = ((header & ATI_CCE_PACKET1_REG_2) >>
ATI_CCE_PACKET1_REG_2_SHIFT) << 2;
MMIO_OUT32(mmio, reg, addr[++i]);
break;
case ATI_CCE_PACKET2:
/* PACKET2 is a no-op packet. */
break;
case ATI_CCE_PACKET3:
n = ((header & ATI_CCE_PACKET3_COUNT_MASK) >> 16) + 1;
ATIDispatchPacket3MMIO(atis, header, &addr[i], n);
i += n;
break;
default:
ErrorF("Unsupported packet: 0x%x\n", addr[i]);
}
}
}
/* Dispatch packets by sending them through the MMIO aperture. */
static void
R128DispatchIndirectPDMA(ATIScreenInfo *atis)
{
ATICardInfo *atic = atis->atic;
dmaBuf *buf = atis->indirectBuffer;
char *mmio = atic->reg_base;
CARD32 *addr;
int count;
addr = (CARD32 *)((char *)buf->address + atis->indirectStart);
count = (buf->used - atis->indirectStart) / 4;
while (count > 1) {
ATIWaitAvailPrimary(atis, 2);
MMIO_OUT32(mmio, R128_REG_PM4_FIFO_DATA_EVEN, *addr++);
MMIO_OUT32(mmio, R128_REG_PM4_FIFO_DATA_ODD, *addr++);
count -= 2;
}
/* Submit last DWORD if necessary. */
if (count != 0) {
ATIWaitAvailPrimary(atis, 2);
MMIO_OUT32(mmio, R128_REG_PM4_FIFO_DATA_EVEN, *addr++);
MMIO_OUT32(mmio, R128_REG_PM4_FIFO_DATA_ODD, ATI_CCE_PACKET2);
}
}
/* Dispatch packets by sending them through the MMIO aperture, using the
* primary CCE ring. */
static void
RadeonDispatchIndirectPDMA(ATIScreenInfo *atis)
{
ATICardInfo *atic = atis->atic;
dmaBuf *buf = atis->indirectBuffer;
char *mmio = atic->reg_base;
CARD32 *addr;
int count, avail, reg, i;
addr = (CARD32 *)((char *)buf->address + atis->indirectStart);
count = (buf->used - atis->indirectStart) / 4;
reg = RADEON_REG_CSQ_APER_PRIMARY;
while (count > 0) {
avail = ATIGetAvailPrimary(atis);
for (i = 0; i < min(count, avail); i++) {
MMIO_OUT32(mmio, reg, *addr++);
if (reg == RADEON_REG_CSQ_APER_PRIMARY_END)
reg = RADEON_REG_CSQ_APER_PRIMARY;
else
reg += 4;
}
count -= i;
}
}
/* Dispatch packets by writing them to the (primary) ring buffer, which happens
* to be in framebuffer memory.
*/
static void
R128DispatchIndirectDMA(ATIScreenInfo *atis)
{
ATICardInfo *atic = atis->atic;
dmaBuf *buf = atis->indirectBuffer;
char *mmio = atic->reg_base;
CARD32 *addr;
int count, ring_count;
addr = (CARD32 *)((char *)buf->address + atis->indirectStart);
count = (buf->used - atis->indirectStart) / 4;
ring_count = atis->ring_len / 4;
while (count > 0) {
int tries = 0;
atis->ring_addr[atis->ring_write++] = *addr++;
if (atis->ring_write >= ring_count)
atis->ring_write = 0;
while (atis->ring_write == atis->ring_read) {
atis->ring_read = MMIO_IN32(mmio, ATI_REG_CCE_RPTR);
if (tries++ == 1000000) {
ErrorF("Timeout submitting packets, resetting...\n");
ATIEngineReset(atis);
}
}
count--;
}
/* Workaround for some early Rage 128 ASIC spins where the CCE parser
* may read up to 32 DWORDS beyond the end of the ring buffer memory
* before wrapping around, if the ring buffer was empty and a <32 DWORD
* packet that wraps around the end of the ring buffer is submitted.
* To work around that, copy the beginning of the ring buffer past the
* end if that may happen.
*/
if (atis->ring_write < 32)
memcpy(atis->ring_addr + ring_count, atis->ring_addr, 32 * 4);
/* Update write pointer */
MMIO_OUT32(mmio, ATI_REG_CCE_WPTR, atis->ring_write);
}
void
ATIFlushIndirect(ATIScreenInfo *atis, Bool discard)
{
ATICardInfo *atic = atis->atic;
dmaBuf *buf = atis->indirectBuffer;
if ((atis->indirectStart == buf->used) && !discard)
return;
#if DEBUG_FIFO
ErrorF("Dispatching %d DWORDS\n", (buf->used - atis->indirectStart) /
4);
#endif
#ifdef USE_DRI
if (atis->using_dri) {
buf->drmBuf->used = buf->used;
ATIDRIDispatchIndirect(atis, discard);
if (discard) {
buf->drmBuf = ATIDRIGetBuffer(atis);
buf->size = buf->drmBuf->total;
buf->used = buf->drmBuf->used;
buf->address = buf->drmBuf->address;
atis->indirectStart = 0;
} else {
/* Start on a double word boundary */
atis->indirectStart = buf->used = (buf->used + 7) & ~7;
}
return;
}
#endif /* USE_DRI */
if (atis->using_dma && !atic->is_radeon)
R128DispatchIndirectDMA(atis);
else if (atis->using_pseudo) {
if (atic->is_radeon)
RadeonDispatchIndirectPDMA(atis);
else
R128DispatchIndirectPDMA(atis);
} else
ATIDispatchIndirectMMIO(atis);
buf->used = 0;
atis->indirectStart = 0;
}
static Bool
ATIInitAGP(ScreenPtr pScreen, int size)
{
KdScreenPriv(pScreen);
ATIScreenInfo(pScreenPriv);
ATICardInfo(pScreenPriv);
AgpInfoPtr agp_info;
int screennum = atis->screen->mynum;
if (atic->is_radeon)
return FALSE;
if (!KdAgpGARTSupported())
return FALSE;
if (!KdAcquireGART(screennum))
return FALSE;
atis->agp_key = KdAllocateGARTMemory(screennum, size, 0, NULL);
if (atis->agp_key == -1) {
ErrorF("Failed to allocate %dKB GART memory\n", size/1024);
KdReleaseGART(screennum);
return FALSE;
}
if (!KdBindGARTMemory(screennum, atis->agp_key, 0)) {
ErrorF("Failed to bind GART memory\n");
KdReleaseGART(screennum);
return FALSE;
}
agp_info = KdGetAGPInfo(screennum);
if (agp_info == NULL) {
KdUnbindGARTMemory(screennum, atis->agp_key);
KdReleaseGART(screennum);
return FALSE;
}
atis->agp_addr = KdMapDevice(agp_info->base, agp_info->size);
if (atis->agp_addr == NULL) {
ErrorF("Failed to map GART memory\n");
KdUnbindGARTMemory(screennum, atis->agp_key);
KdReleaseGART(screennum);
free(agp_info);
return FALSE;
}
KdSetMappedMode(agp_info->base, agp_info->size,
KD_MAPPED_MODE_FRAMEBUFFER);
atis->agp_size = size;
free(agp_info);
return TRUE;
}
static void
ATIFiniAGP(ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
ATIScreenInfo(pScreenPriv);
int screennum = atis->screen->mynum;
KdUnbindGARTMemory(screennum, atis->agp_key);
KdReleaseGART(screennum);
atis->agp_addr = NULL;
atis->agp_size = 0;
}
static Bool
ATIPseudoDMAInit(ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
ATIScreenInfo(pScreenPriv);
ATICardInfo(pScreenPriv);
char *mmio = atic->reg_base;
if (atic->is_r200 || atic->is_r300)
return FALSE;
ATIUploadMicrocode(atis);
ATIEngineReset(atis);
if (atic->is_radeon) {
MMIO_OUT32(mmio, RADEON_REG_CP_CSQ_CNTL,
RADEON_CSQ_PRIPIO_INDDIS);
atis->cce_pri_size = MMIO_IN32(mmio, RADEON_REG_CP_CSQ_CNTL) &
RADEON_CSQ_CNT_PRIMARY_MASK;
MMIO_OUT32(mmio, RADEON_REG_ME_CNTL, RADEON_ME_MODE_FREE_RUN);
} else {
MMIO_OUT32(mmio, R128_REG_PM4_BUFFER_CNTL, R128_PM4_192PIO |
R128_PM4_BUFFER_CNTL_NOUPDATE);
atis->cce_pri_size = 192;
MMIO_OUT32(mmio, R128_REG_PM4_MICRO_CNTL,
R128_PM4_MICRO_FREERUN);
}
return TRUE;
}
static Bool
ATIPseudoDMAFini(ScreenPtr pScreen)
{ KdScreenPriv(pScreen);
ATIScreenInfo(pScreenPriv);
ATICardInfo(pScreenPriv);
char *mmio = atic->reg_base;
if (atic->is_radeon) {
MMIO_OUT32(mmio, RADEON_REG_ME_CNTL, 0);
MMIO_OUT32(mmio, RADEON_REG_CP_CSQ_CNTL,
RADEON_CSQ_PRIDIS_INDDIS);
} else {
MMIO_OUT32(mmio, R128_REG_PM4_MICRO_CNTL, 0);
MMIO_OUT32(mmio, R128_REG_PM4_BUFFER_CNTL,
R128_PM4_NONPM4 | R128_PM4_BUFFER_CNTL_NOUPDATE);
}
atis->cce_pri_size = 0;
ATIEngineReset(atis);
return TRUE;
}
static Bool
ATIDMAInit(ScreenPtr pScreen, Bool use_agp)
{
KdScreenPriv(pScreen);
ATIScreenInfo(pScreenPriv);
ATICardInfo(pScreenPriv);
char *mmio = atic->reg_base;
int dma_offset;
CARD32 tmp;
/* XXX: Not for radeons. Yet? */
if (atic->is_radeon)
return FALSE;
if (use_agp) {
if (1)
return FALSE; /* XXX */
/* Allocate a 1MB AGP space, but only use 128k + 128 for DMA.
* XXX: Should use the rest for things like scratch space.
*/
if (!ATIInitAGP(pScreen, 1024 * 1024))
return FALSE;
atis->ring_addr = atis->agp_addr;
atis->ring_len = 128 * 1024;
dma_offset = R128_AGP_OFFSET;
} else {
if (1)
return FALSE; /* XXX */
/* Allocate a 128K buffer, plus 32 DWORDS to give space for the
* R128 ASIC bug workaround.
*/
atis->dma_space = KdOffscreenAlloc(pScreen, 128 * 1024 + 128,
128, TRUE, NULL, NULL);
if (atis->dma_space == NULL)
return FALSE;
atis->ring_addr = (CARD32 *)(atis->dma_space->offset +
pScreenPriv->screen->memory_base);
atis->ring_len = 128 * 1024;
dma_offset = atis->dma_space->offset;
}
ATIUploadMicrocode(atis);
ATIEngineReset(atis);
atis->ring_read = 0;
atis->ring_write = 0;
tmp = MMIO_IN32(mmio, ATI_REG_BUS_CNTL);
MMIO_OUT32(mmio, ATI_REG_BUS_CNTL, tmp & ~ATI_BUS_MASTER_DIS);
MMIO_OUT32(mmio, ATI_REG_CCE_RB_BASE, dma_offset);
MMIO_OUT32(mmio, ATI_REG_CCE_WPTR, atis->ring_write);
MMIO_OUT32(mmio, ATI_REG_CCE_RPTR, atis->ring_read);
MMIO_OUT32(mmio, ATI_REG_CCE_RPTR_ADDR, 0 /* XXX? */);
if (atic->is_radeon) {
MMIO_OUT32(mmio, RADEON_REG_CP_CSQ_CNTL,
RADEON_CSQ_PRIBM_INDBM);
atis->cce_pri_size = MMIO_IN32(mmio, RADEON_REG_CP_CSQ_CNTL) &
RADEON_CSQ_CNT_PRIMARY_MASK;
MMIO_OUT32(mmio, RADEON_REG_ME_CNTL, RADEON_ME_MODE_FREE_RUN);
} else {
MMIO_OUT32(mmio, R128_REG_PM4_BUFFER_WM_CNTL,
((R128_WATERMARK_L/4) << R128_WMA_SHIFT) |
((R128_WATERMARK_M/4) << R128_WMB_SHIFT) |
((R128_WATERMARK_N/4) << R128_WMC_SHIFT) |
((R128_WATERMARK_K/64) << R128_WB_WM_SHIFT));
/* The sample code reads from an undocumneted register
* (PM4_BUFFER_ADDR). Perhaps it's a write posting thing? Do
* a read in case that's it.
*/
MMIO_IN32(mmio, R128_REG_PM4_BUFFER_CNTL);
if (use_agp) {
/* XXX Magic num */
MMIO_OUT32(mmio, R128_REG_PCI_GART_PAGE, 1);
MMIO_OUT32(mmio, R128_REG_PM4_BUFFER_CNTL,
ATILog2(atis->ring_len) |
R128_PM4_192BM |
R128_PM4_BUFFER_CNTL_NOUPDATE);
} else {
MMIO_OUT32(mmio, R128_REG_PM4_BUFFER_CNTL,
ATILog2(atis->ring_len) |
R128_PM4_192BM |
R128_PM4_BUFFER_CNTL_NOUPDATE |
R128_PM4_IN_FRAME_BUFFER);
}
atis->cce_pri_size = 192;
MMIO_IN32(mmio, R128_REG_PM4_BUFFER_CNTL);
MMIO_OUT32(mmio, R128_REG_PM4_MICRO_CNTL,
R128_PM4_MICRO_FREERUN);
}
return TRUE;
}
static Bool
ATIDMAFini(ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
ATIScreenInfo(pScreenPriv);
ATICardInfo(pScreenPriv);
char *mmio = atic->reg_base;
if (atic->is_radeon) {
MMIO_OUT32(mmio, RADEON_REG_ME_CNTL, 0);
MMIO_OUT32(mmio, RADEON_REG_CP_CSQ_CNTL,
RADEON_CSQ_PRIDIS_INDDIS);
} else {
MMIO_OUT32(mmio, ATI_REG_CCE_WPTR,
atis->ring_write | R128_PM4_BUFFER_DL_DONE);
MMIO_OUT32(mmio, R128_REG_PM4_MICRO_CNTL, 0);
MMIO_OUT32(mmio, R128_REG_PM4_BUFFER_CNTL,
R128_PM4_NONPM4 | R128_PM4_BUFFER_CNTL_NOUPDATE);
}
atis->cce_pri_size = 0;
ATIEngineReset(atis);
if (atis->using_agp)
ATIFiniAGP(pScreen);
else
KdOffscreenFree(pScreen, atis->dma_space);
return TRUE;
}
void
ATIDMASetup(ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
ATICardInfo(pScreenPriv);
ATIScreenInfo(pScreenPriv);
#ifdef USE_DRI
if (atis->using_dri)
ATIDRIDMAStart(pScreen);
#endif /* USE_DRI */
if (!atis->using_dri) {
atis->using_agp = FALSE;
if (atic->is_agp && ATIDMAInit(pScreen, TRUE)) {
atis->using_agp = TRUE;
atis->using_dma = TRUE;
} else if (ATIDMAInit(pScreen, FALSE)) {
atis->using_agp = FALSE;
atis->using_dma = TRUE;
} else if (ATIPseudoDMAInit(pScreen))
atis->using_pseudo = TRUE;
else
atis->using_pio = TRUE;
}
atis->indirectBuffer = ATIGetDMABuffer(atis);
if (atis->indirectBuffer == FALSE)
FatalError("Failed to allocate DMA buffer.\n");
if (atis->using_dri)
ErrorF("Initialized %s DRI DMA\n",
atis->using_agp ? "AGP" : "PCI");
else if (atis->using_dma && atis->using_agp)
ErrorF("Initialized AGP DMA\n");
else if (atis->using_dma)
ErrorF("Initialized framebuffer pseudo-DMA\n");
else if (atis->using_pseudo)
ErrorF("Initialized pseudo-DMA\n");
else if (atis->using_pio)
ErrorF("Initialized PIO\n");
}
void
ATIDMATeardown(ScreenPtr pScreen)
{
KdScreenPriv(pScreen);
ATIScreenInfo(pScreenPriv);
ATIWaitIdle(atis);
#ifdef USE_DRI
if (atis->using_dri)
ATIDRIDMAStop(pScreen);
#endif /* USE_DRI */
if (atis->using_dma)
ATIDMAFini(pScreen);
if (atis->using_pseudo)
ATIPseudoDMAFini(pScreen);
if (atis->using_pio || atis->using_pseudo || atis->using_dma) {
xfree(atis->indirectBuffer->address);
xfree(atis->indirectBuffer);
}
atis->indirectBuffer = NULL;
atis->using_pio = FALSE;
atis->using_pseudo = FALSE;
atis->using_dma = FALSE;
atis->using_agp = FALSE;
}