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.
1005 lines
25 KiB
C
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;
|
|
}
|
|
|