reading a register/attempting DMA. Now it'll wait for a certain number of seconds rather than a certain number of times through the loop before deciding that it's timed out and resetting the hardware. Also, add more timeout handling, and reset the draw state after resetting the engine.
1048 lines
26 KiB
C
1048 lines
26 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 <sys/time.h>
|
|
|
|
#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;
|
|
CARD32 temp;
|
|
TIMEOUT_LOCALS;
|
|
|
|
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);
|
|
|
|
WHILE_NOT_TIMEOUT(.2) {
|
|
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);
|
|
|
|
WHILE_NOT_TIMEOUT(.2) {
|
|
if ((MMIO_IN32(mmio, R128_REG_PC_NGUI_CTLSTAT) &
|
|
R128_PC_BUSY) != R128_PC_BUSY)
|
|
break;
|
|
}
|
|
}
|
|
if (TIMEDOUT())
|
|
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_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);
|
|
}
|
|
#ifdef USE_DRI
|
|
if (atis->using_dri) {
|
|
ATIDRIDMAReset(atis);
|
|
ATIDRIDMAStart(atis);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
ATIWaitAvailMMIO(ATIScreenInfo *atis, int n)
|
|
{
|
|
ATICardInfo *atic = atis->atic;
|
|
char *mmio = atic->reg_base;
|
|
TIMEOUT_LOCALS;
|
|
|
|
if (atis->mmio_avail >= n) {
|
|
atis->mmio_avail -= n;
|
|
return;
|
|
}
|
|
if (atic->is_radeon) {
|
|
WHILE_NOT_TIMEOUT(.2) {
|
|
atis->mmio_avail = MMIO_IN32(mmio,
|
|
RADEON_REG_RBBM_STATUS) & RADEON_RBBM_FIFOCNT_MASK;
|
|
if (atis->mmio_avail >= n)
|
|
break;
|
|
}
|
|
} else {
|
|
WHILE_NOT_TIMEOUT(.2) {
|
|
atis->mmio_avail = MMIO_IN32(mmio, R128_REG_GUI_STAT) &
|
|
0xfff;
|
|
if (atis->mmio_avail >= n)
|
|
break;
|
|
}
|
|
}
|
|
if (TIMEDOUT()) {
|
|
ErrorF("Timeout waiting for %d MMIO slots.\n", n);
|
|
ATIEngineReset(atis);
|
|
ATIDrawSetup(atis->screen->pScreen);
|
|
}
|
|
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);
|
|
if (atic->is_r200)
|
|
diff = ((csq_stat & R200_CSQ_WPTR_PRIMARY_MASK) >> 9) -
|
|
(csq_stat & R200_CSQ_RPTR_PRIMARY_MASK);
|
|
else
|
|
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)
|
|
{
|
|
TIMEOUT_LOCALS;
|
|
|
|
if (atis->cce_pri_avail >= n) {
|
|
atis->cce_pri_avail -= n;
|
|
return;
|
|
}
|
|
|
|
WHILE_NOT_TIMEOUT(.2) {
|
|
if (atis->cce_pri_avail >= n)
|
|
break;
|
|
atis->cce_pri_avail = ATIGetAvailPrimary(atis);
|
|
if (atis->cce_pri_avail >= n)
|
|
break;
|
|
}
|
|
if (TIMEDOUT()) {
|
|
ErrorF("Timeout waiting for %d CCE slots (%d avail).\n", n,
|
|
atis->cce_pri_avail);
|
|
ATIEngineReset(atis);
|
|
ATIDrawSetup(atis->screen->pScreen);
|
|
}
|
|
atis->cce_pri_avail -= n;
|
|
}
|
|
|
|
void
|
|
ATIWaitIdle(ATIScreenInfo *atis)
|
|
{
|
|
ATICardInfo *atic = atis->atic;
|
|
char *mmio = atic->reg_base;
|
|
RING_LOCALS;
|
|
TIMEOUT_LOCALS;
|
|
|
|
if (atis->indirectBuffer != NULL)
|
|
ATIFlushIndirect(atis, 0);
|
|
|
|
#ifdef USE_DRI
|
|
if (atis->using_dri) {
|
|
int ret = 0;
|
|
int cmd = (atic->is_radeon ? DRM_RADEON_CP_IDLE :
|
|
DRM_R128_CCE_IDLE);
|
|
WHILE_NOT_TIMEOUT(2) {
|
|
ret = drmCommandNone(atic->drmFd, cmd);
|
|
if (ret != -EBUSY)
|
|
break;
|
|
}
|
|
if (TIMEDOUT()) {
|
|
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);
|
|
|
|
WHILE_NOT_TIMEOUT(.2) {
|
|
if ((MMIO_IN32(mmio, R128_REG_PM4_STAT) &
|
|
(R128_PM4_BUSY | R128_PM4_GUI_ACTIVE)) == 0)
|
|
break;
|
|
}
|
|
if (TIMEDOUT()) {
|
|
ErrorF("Timeout idling CCE, resetting...\n");
|
|
ATIEngineReset(atis);
|
|
ATIDrawSetup(atis->screen->pScreen);
|
|
}
|
|
}
|
|
|
|
/* 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) {
|
|
WHILE_NOT_TIMEOUT(.2) {
|
|
if ((MMIO_IN32(mmio, RADEON_REG_RBBM_STATUS) &
|
|
RADEON_RBBM_ACTIVE) == 0)
|
|
break;
|
|
}
|
|
} else {
|
|
WHILE_NOT_TIMEOUT(.2) {
|
|
if ((MMIO_IN32(mmio, R128_REG_GUI_STAT) &
|
|
R128_GUI_ACTIVE) == 0)
|
|
break;
|
|
}
|
|
}
|
|
if (TIMEDOUT()) {
|
|
ErrorF("Timeout idling accelerator, resetting...\n");
|
|
ATIEngineReset(atis);
|
|
ATIDrawSetup(atis->screen->pScreen);
|
|
}
|
|
}
|
|
|
|
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;
|
|
TIMEOUT_LOCALS;
|
|
|
|
addr = (CARD32 *)((char *)buf->address + atis->indirectStart);
|
|
count = (buf->used - atis->indirectStart) / 4;
|
|
|
|
reg = RADEON_REG_CSQ_APER_PRIMARY;
|
|
WHILE_NOT_TIMEOUT(3) {
|
|
/* 3 seconds is empirical, using render_bench on an r100. */
|
|
if (count <= 0)
|
|
break;
|
|
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;
|
|
}
|
|
if (TIMEDOUT()) {
|
|
ErrorF("Timeout submitting packets, resetting...\n");
|
|
ATIEngineReset(atis);
|
|
ATIDrawSetup(atis->screen->pScreen);
|
|
}
|
|
}
|
|
|
|
|
|
/* 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;
|
|
TIMEOUT_LOCALS;
|
|
|
|
addr = (CARD32 *)((char *)buf->address + atis->indirectStart);
|
|
count = (buf->used - atis->indirectStart) / 4;
|
|
ring_count = atis->ring_len / 4;
|
|
|
|
WHILE_NOT_TIMEOUT(.2) {
|
|
if (count <= 0)
|
|
break;
|
|
|
|
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);
|
|
}
|
|
count--;
|
|
}
|
|
if (TIMEDOUT()) {
|
|
ErrorF("Timeout submitting packets, resetting...\n");
|
|
ATIEngineReset(atis);
|
|
ATIDrawSetup(atis->screen->pScreen);
|
|
}
|
|
|
|
/* 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_r300)
|
|
return FALSE;
|
|
|
|
ATIUploadMicrocode(atis);
|
|
ATIEngineReset(atis);
|
|
|
|
if (atic->is_r200) {
|
|
MMIO_OUT32(mmio, RADEON_REG_CP_CSQ_CNTL,
|
|
RADEON_CSQ_PRIPIO_INDDIS);
|
|
atis->cce_pri_size = MMIO_IN32(mmio, RADEON_REG_CP_CSQ_CNTL) &
|
|
R200_CSQ_CNT_PRIMARY_MASK;
|
|
MMIO_OUT32(mmio, RADEON_REG_ME_CNTL, RADEON_ME_MODE_FREE_RUN);
|
|
} 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_r200) {
|
|
MMIO_OUT32(mmio, RADEON_REG_CP_CSQ_CNTL,
|
|
RADEON_CSQ_PRIBM_INDBM);
|
|
atis->cce_pri_size = MMIO_IN32(mmio, RADEON_REG_CP_CSQ_CNTL) &
|
|
R200_CSQ_CNT_PRIMARY_MASK;
|
|
MMIO_OUT32(mmio, RADEON_REG_ME_CNTL, RADEON_ME_MODE_FREE_RUN);
|
|
} else 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(atis);
|
|
#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(atis);
|
|
#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;
|
|
}
|
|
|