summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux-core/xgi_cmdlist.c64
-rw-r--r--linux-core/xgi_drv.c8
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);
}