[PATCH RFC 045/111] staging: etnaviv: add support to insert a MMU flush into GPU stream

Lucas Stach l.stach at pengutronix.de
Thu Apr 2 08:29:47 PDT 2015


From: Russell King <rmk+kernel at arm.linux.org.uk>

Add a flag to indicate that the GPU MMU needs to be flushed before
executing the next set of command buffers.  This is necessary to
ensure that the GPU sees updated page table entries which may have
been modified by GEM.

It is expected that userspace will have flushed the caches at the
end of the previous command buffers, so there will be no cache
writebacks pending.

Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
---
 drivers/staging/etnaviv/etnaviv_buffer.c | 64 +++++++++++++++++++++++++-------
 drivers/staging/etnaviv/etnaviv_mmu.h    |  1 +
 2 files changed, 51 insertions(+), 14 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 026489baeda7..96661e513d7d 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -17,6 +17,7 @@
 
 #include "etnaviv_gpu.h"
 #include "etnaviv_gem.h"
+#include "etnaviv_mmu.h"
 
 #include "common.xml.h"
 #include "state.xml.h"
@@ -162,34 +163,38 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_gem_object *buffer = to_etnaviv_bo(gpu->buffer);
 	struct etnaviv_gem_object *cmd;
 	u32 *lw = buffer->vaddr + ((buffer->offset - 4) * 4);
-	u32 back, link_target, link_size;
+	u32 back, link_target, link_size, reserve_size;
 	u32 i;
 
 	if (drm_debug & DRM_UT_DRIVER)
 		etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
 
+	reserve_size = 6;
+
+	/*
+	 * If we need to flush the MMU prior to submitting this buffer, we
+	 * will need to append a mmu flush load state, followed by a new
+	 * link to this buffer - a total of four additional words.
+	 */
+	if (gpu->mmu->need_flush)
+		reserve_size += 4;
+
 	/*
 	 * if we are going to completely overflow the buffer, we need to wrap.
 	 */
-	if (buffer->offset + 6 > buffer->base.size / sizeof(uint32_t))
+	if (buffer->offset + reserve_size >
+	    buffer->base.size / sizeof(uint32_t))
 		buffer->offset = 0;
 
 	/* save offset back into main buffer */
-	back = buffer->offset;
+	back = buffer->offset + reserve_size - 6;
 	link_target = buffer->paddr + buffer->offset * 4;
 	link_size = 6;
 
-	/* Save the event and buffer position of the new event trigger */
-	gpu->event[event].fence = submit->fence;
-	gpu->event[event].ring_pos = buffer->offset;
-
-	/* trigger event */
-	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
-		       VIVS_GL_EVENT_FROM_PE);
-
-	/* append WAIT/LINK to main buffer */
-	CMD_WAIT(buffer);
-	CMD_LINK(buffer, 2, buffer->paddr + ((buffer->offset - 1) * 4));
+	if (gpu->mmu->need_flush) {
+		/* Skip over the MMU flush and LINK instructions */
+		link_target += 4 * sizeof(uint32_t);
+	}
 
 	/* update offset for every cmd stream */
 	for (i = submit->nr_cmds; i--; ) {
@@ -228,6 +233,37 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 		pr_info("event: %d\n", event);
 	}
 
+	if (gpu->mmu->need_flush) {
+		uint32_t new_target = buffer->paddr + buffer->offset *
+					sizeof(uint32_t);
+
+		/* Add the MMU flush */
+		CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU,
+			       VIVS_GL_FLUSH_MMU_FLUSH_FEMMU |
+			       VIVS_GL_FLUSH_MMU_FLUSH_PEMMU);
+
+		/* And the link to the first buffer */
+		CMD_LINK(buffer, link_size, link_target);
+
+		/* Update the link target to point to the flush */
+		link_target = new_target;
+		link_size = 4;
+
+		gpu->mmu->need_flush = false;
+	}
+
+	/* Save the event and buffer position of the new event trigger */
+	gpu->event[event].fence = submit->fence;
+	gpu->event[event].ring_pos = buffer->offset;
+
+	/* trigger event */
+	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
+		       VIVS_GL_EVENT_FROM_PE);
+
+	/* append WAIT/LINK to main buffer */
+	CMD_WAIT(buffer);
+	CMD_LINK(buffer, 2, buffer->paddr + ((buffer->offset - 1) * 4));
+
 	/* Change WAIT into a LINK command; write the address first. */
 	*(lw + 1) = link_target;
 	mb();
diff --git a/drivers/staging/etnaviv/etnaviv_mmu.h b/drivers/staging/etnaviv/etnaviv_mmu.h
index 7b97ef35d290..b3a0e3c98372 100644
--- a/drivers/staging/etnaviv/etnaviv_mmu.h
+++ b/drivers/staging/etnaviv/etnaviv_mmu.h
@@ -23,6 +23,7 @@
 struct etnaviv_iommu {
 	struct drm_device *dev;
 	struct iommu_domain *domain;
+	bool need_flush;
 };
 
 int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names,
-- 
2.1.4



More information about the dri-devel mailing list