diff options
Diffstat (limited to 'xorg-server/hw/kdrive/ati/ati_dma.c')
-rw-r--r-- | xorg-server/hw/kdrive/ati/ati_dma.c | 1037 |
1 files changed, 1037 insertions, 0 deletions
diff --git a/xorg-server/hw/kdrive/ati/ati_dma.c b/xorg-server/hw/kdrive/ati/ati_dma.c new file mode 100644 index 000000000..b97d79b25 --- /dev/null +++ b/xorg-server/hw/kdrive/ati/ati_dma.c @@ -0,0 +1,1037 @@ +/* + * 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. + */ + +#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]; + +#if DEBUG_FIFO +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_RB3D_DSTCACHE_CTLSTAT: 0x%08x\n", + MMIO_IN32(mmio, RADEON_REG_RB3D_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)); + } +} +#endif + +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_RB3D_DSTCACHE_CTLSTAT); + temp |= RADEON_RB3D_DC_FLUSH_ALL; + MMIO_OUT32(mmio, RADEON_REG_RB3D_DSTCACHE_CTLSTAT, temp); + + WHILE_NOT_TIMEOUT(.2) { + if ((MMIO_IN32(mmio, RADEON_REG_RB3D_DSTCACHE_CTLSTAT) & + RADEON_RB3D_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; + 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)) { + 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); + } else 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; +} + |