From 2fc697a7d270d57463eb5a16a0c65bd8e14c9893 Mon Sep 17 00:00:00 2001 From: Ian Romanick Date: Mon, 30 Jul 2007 10:20:15 -0700 Subject: Fix GE shut-down sequence. When the GE is shut down, an empty command packet without a begin-link must be sent. After this command is sent, wait for the hardware to go idle. Finally, turn off the GE and disable MMIO. --- linux-core/xgi_cmdlist.c | 64 +++++++++++++++++++++++++++++++----------------- linux-core/xgi_drv.c | 8 ++++-- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/linux-core/xgi_cmdlist.c b/linux-core/xgi_cmdlist.c index 1d0ee754..4bb147c4 100644 --- a/linux-core/xgi_cmdlist.c +++ b/linux-core/xgi_cmdlist.c @@ -29,7 +29,7 @@ #include "xgi_misc.h" #include "xgi_cmdlist.h" -static void addFlush2D(struct xgi_info * info); +static void xgi_emit_flush(struct xgi_info * info, bool link); static unsigned int get_batch_command(enum xgi_batch_type type); static void triggerHWCommandList(struct xgi_info * info); static void xgi_cmdlist_reset(struct xgi_info * info); @@ -120,7 +120,7 @@ int xgi_submit_cmdlist(struct drm_device * dev, void * data, DRM_DEBUG("info->cmdring.last_ptr != NULL\n"); if (pCmdInfo->type == BTYPE_3D) { - addFlush2D(info); + xgi_emit_flush(info, TRUE); } info->cmdring.last_ptr[1] = begin[1]; @@ -190,9 +190,18 @@ void xgi_cmdlist_reset(struct xgi_info * info) info->cmdring.ring_offset = 0; } + void xgi_cmdlist_cleanup(struct xgi_info * info) { if (info->cmdring.ring_hw_base != 0) { + /* If command lists have been issued, terminate the command + * list chain with a flush command. + */ + if (info->cmdring.last_ptr != NULL) { + xgi_emit_flush(info, FALSE); + xgi_waitfor_pci_idle(info); + } + xgi_pcie_free(info, info->cmdring.ring_gart_base, NULL); info->cmdring.ring_hw_base = 0; info->cmdring.ring_offset = 0; @@ -210,32 +219,43 @@ static void triggerHWCommandList(struct xgi_info * info) } -static void addFlush2D(struct xgi_info * info) +/** + * Emit a flush to the CRTL command stream. + * @info XGI info structure + * @link Emit (or don't emit) link information at start of flush command. + * + * This function assumes info->cmdring.ptr is non-NULL. + */ +static void xgi_emit_flush(struct xgi_info * info, bool link) { - u32 *flushBatchVirtAddr; - u32 flushBatchHWAddr; + static const u32 flush_command[8] = { + (0x10 << 24), + BEGIN_LINK_ENABLE_MASK | (0x00004), + 0x00000000, 0x00000000, + + /* Flush everything with the default 32 clock delay. + */ + 0x003fffff, 0x003fffff, 0x003fffff, 0x003fffff + }; + const unsigned int base = (link) ? 0 : 4; + const unsigned int flush_size = (8 - base) * sizeof(u32); + u32 *batch_addr; + u32 hw_addr; /* check buf is large enough to contain a new flush batch */ - if ((info->cmdring.ring_offset + 0x20) >= info->cmdring.size) { + if ((info->cmdring.ring_offset + flush_size) >= info->cmdring.size) { info->cmdring.ring_offset = 0; } - flushBatchHWAddr = info->cmdring.ring_hw_base + info->cmdring.ring_offset; - flushBatchVirtAddr = info->cmdring.ptr + hw_addr = info->cmdring.ring_hw_base + + info->cmdring.ring_offset; + batch_addr = info->cmdring.ptr + (info->cmdring.ring_offset / 4); - /* not using memcpy for I assume the address is discrete */ - *(flushBatchVirtAddr + 0) = 0x10000000; - *(flushBatchVirtAddr + 1) = 0x80000004; /* size = 0x04 dwords */ - *(flushBatchVirtAddr + 2) = 0x00000000; - *(flushBatchVirtAddr + 3) = 0x00000000; - *(flushBatchVirtAddr + 4) = FLUSH_2D; - *(flushBatchVirtAddr + 5) = FLUSH_2D; - *(flushBatchVirtAddr + 6) = FLUSH_2D; - *(flushBatchVirtAddr + 7) = FLUSH_2D; - - info->cmdring.last_ptr[1] = BEGIN_LINK_ENABLE_MASK + 0x08; - info->cmdring.last_ptr[2] = flushBatchHWAddr >> 4; + (void) memcpy(batch_addr, & flush_command[base], flush_size); + + info->cmdring.last_ptr[1] = BEGIN_LINK_ENABLE_MASK | (flush_size / 4); + info->cmdring.last_ptr[2] = hw_addr >> 4; info->cmdring.last_ptr[3] = 0; wmb(); info->cmdring.last_ptr[0] = (get_batch_command(BTYPE_CTRL) << 24) @@ -243,6 +263,6 @@ static void addFlush2D(struct xgi_info * info) triggerHWCommandList(info); - info->cmdring.ring_offset += 0x20; - info->cmdring.last_ptr = flushBatchVirtAddr; + info->cmdring.ring_offset += flush_size; + info->cmdring.last_ptr = (link) ? batch_addr : NULL; } diff --git a/linux-core/xgi_drv.c b/linux-core/xgi_drv.c index 0b094a31..201062ee 100644 --- a/linux-core/xgi_drv.c +++ b/linux-core/xgi_drv.c @@ -242,6 +242,12 @@ void xgi_driver_lastclose(struct drm_device * dev) struct xgi_info * info = dev->dev_private; if (info != NULL) { + if (info->mmio_map != NULL) { + xgi_cmdlist_cleanup(info); + xgi_disable_ge(info); + xgi_disable_mmio(info); + } + /* The core DRM lastclose routine will destroy all of our * mappings for us. NULL out the pointers here so that * xgi_bootstrap can do the right thing. @@ -250,8 +256,6 @@ void xgi_driver_lastclose(struct drm_device * dev) info->mmio_map = NULL; info->fb_map = NULL; - xgi_cmdlist_cleanup(info); - if (info->fb_heap.initialized) { xgi_mem_heap_cleanup(&info->fb_heap); } -- cgit v1.2.3