/* * 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 #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; }