[PATCH RFC 102/111] staging: etnaviv: separate GPU pipes from execution state

Lucas Stach l.stach at pengutronix.de
Thu Apr 2 08:30:44 PDT 2015


While this isn't the case on i.MX6 a single GPU pipe can have
multiple rendering backend states, which can be selected by the
pipe switch command, so there is no strict mapping between the
user "pipes" and the PIPE_2D/PIPE_3D execution states.

We need to respect this in the public userspace API. The exported
pipes are the GC cores which may be able to support one or more
execution states. The information which execution states are supported
on a given pipe is available to userspace through the features0 param.

Userspace is responsible to choose a matching pipe when deciding which
pipe to use for a specific task.

The submit ioctl now takes one more parameter for userspace to specify
which execution state it expects a pipe to be in when starting execution
of the command buffers. This allows the kernel to insert pipe switch
commands only when really needed while maintaining separation between
processes by making sure that no process can leave the pipe behind in
an unexpected state, potentially hanging the next one to use the GPU.

Signed-off-by: Lucas Stach <l.stach at pengutronix.de>
---
 drivers/staging/etnaviv/etnaviv_buffer.c     | 57 +++++++++++++++++-----------
 drivers/staging/etnaviv/etnaviv_drv.c        |  1 +
 drivers/staging/etnaviv/etnaviv_drv.h        |  1 +
 drivers/staging/etnaviv/etnaviv_gem.h        |  1 +
 drivers/staging/etnaviv/etnaviv_gem_submit.c |  1 +
 drivers/staging/etnaviv/etnaviv_gpu.c        | 48 ++++-------------------
 drivers/staging/etnaviv/etnaviv_gpu.h        |  2 +-
 include/uapi/drm/etnaviv_drm.h               | 19 +++++-----
 8 files changed, 57 insertions(+), 73 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_buffer.c b/drivers/staging/etnaviv/etnaviv_buffer.c
index 05e0da28cc97..7c8014f07249 100644
--- a/drivers/staging/etnaviv/etnaviv_buffer.c
+++ b/drivers/staging/etnaviv/etnaviv_buffer.c
@@ -154,8 +154,6 @@ u32 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
 	/* initialize buffer */
 	buffer->offset = 0;
 
-	etnaviv_cmd_select_pipe(buffer, gpu->pipe);
-
 	CMD_WAIT(buffer);
 	CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + ((buffer->offset - 1) * 4));
 
@@ -179,21 +177,29 @@ 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, reserve_size;
+	u32 back, link_target, link_size, reserve_size, extra_size = 0;
 	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 (gpu->mmu->need_flush || gpu->switch_context) {
+		/* link command */
+		extra_size += 2;
+		/* flush command */
+		if (gpu->mmu->need_flush)
+			extra_size += 2;
+		/* pipe switch commands */
+		if (gpu->switch_context)
+			extra_size += 8;
+	}
+
+	reserve_size = 6 + extra_size;
 
 	/*
 	 * if we are going to completely overflow the buffer, we need to wrap.
@@ -207,10 +213,8 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	link_target = gpu_va(gpu, buffer) + buffer->offset * 4;
 	link_size = 6;
 
-	if (gpu->mmu->need_flush) {
-		/* Skip over the MMU flush and LINK instructions */
-		link_target += 4 * sizeof(uint32_t);
-	}
+	/* Skip over any extra instructions */
+	link_target += extra_size * sizeof(uint32_t);
 
 	/* update offset for every cmd stream */
 	for (i = submit->nr_cmds; i--; ) {
@@ -249,26 +253,33 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 		pr_info("event: %d\n", event);
 	}
 
-	if (gpu->mmu->need_flush) {
+	if (gpu->mmu->need_flush || gpu->switch_context) {
 		uint32_t new_target = gpu_va(gpu, buffer) + 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_UNK1 |
-			       VIVS_GL_FLUSH_MMU_FLUSH_UNK2 |
-			       VIVS_GL_FLUSH_MMU_FLUSH_PEMMU |
-			       VIVS_GL_FLUSH_MMU_FLUSH_UNK4);
+		if (gpu->mmu->need_flush) {
+			/* Add the MMU flush */
+			CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU,
+				       VIVS_GL_FLUSH_MMU_FLUSH_FEMMU |
+				       VIVS_GL_FLUSH_MMU_FLUSH_UNK1 |
+				       VIVS_GL_FLUSH_MMU_FLUSH_UNK2 |
+				       VIVS_GL_FLUSH_MMU_FLUSH_PEMMU |
+				       VIVS_GL_FLUSH_MMU_FLUSH_UNK4);
+
+			gpu->mmu->need_flush = false;
+		}
+
+		if (gpu->switch_context) {
+			etnaviv_cmd_select_pipe(buffer, submit->exec_state);
+			gpu->switch_context = false;
+		}
 
 		/* And the link to the first buffer */
 		CMD_LINK(buffer, link_size, link_target);
 
-		/* Update the link target to point to the flush */
+		/* Update the link target to point to above instructions */
 		link_target = new_target;
-		link_size = 4;
-
-		gpu->mmu->need_flush = false;
+		link_size = extra_size;
 	}
 
 	/* Save the event and buffer position of the new event trigger */
diff --git a/drivers/staging/etnaviv/etnaviv_drv.c b/drivers/staging/etnaviv/etnaviv_drv.c
index eade6010ce42..828ed8ce347f 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.c
+++ b/drivers/staging/etnaviv/etnaviv_drv.c
@@ -134,6 +134,7 @@ static int etnaviv_load(struct drm_device *dev, unsigned long flags)
 	init_waitqueue_head(&priv->fence_event);
 
 	INIT_LIST_HEAD(&priv->inactive_list);
+	priv->num_gpus = 0;
 
 	platform_set_drvdata(pdev, dev);
 
diff --git a/drivers/staging/etnaviv/etnaviv_drv.h b/drivers/staging/etnaviv/etnaviv_drv.h
index cf7e6f758dd7..4dfcd03c80ef 100644
--- a/drivers/staging/etnaviv/etnaviv_drv.h
+++ b/drivers/staging/etnaviv/etnaviv_drv.h
@@ -51,6 +51,7 @@ struct etnaviv_file_private {
 };
 
 struct etnaviv_drm_private {
+	int num_gpus;
 	struct etnaviv_gpu *gpu[ETNA_MAX_PIPES];
 	struct etnaviv_file_private *lastctx;
 
diff --git a/drivers/staging/etnaviv/etnaviv_gem.h b/drivers/staging/etnaviv/etnaviv_gem.h
index b0e5e968912a..6e0822674c8e 100644
--- a/drivers/staging/etnaviv/etnaviv_gem.h
+++ b/drivers/staging/etnaviv/etnaviv_gem.h
@@ -108,6 +108,7 @@ static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj)
 struct etnaviv_gem_submit {
 	struct drm_device *dev;
 	struct etnaviv_gpu *gpu;
+	uint32_t exec_state;
 	struct list_head bo_list;
 	struct ww_acquire_ctx ticket;
 	uint32_t fence;
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index 965096be5219..9061f5f7ecc6 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -328,6 +328,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		ret = -ENOMEM;
 		goto out;
 	}
+	submit->exec_state = args->exec_state;
 
 	ret = submit_lookup_objects(submit, args, file);
 	if (ret)
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index df393b9ecbf8..abadfecb447d 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -27,7 +27,7 @@
 #include "cmdstream.xml.h"
 
 static const struct platform_device_id gpu_ids[] = {
-	{ .name = "etnaviv-gpu,2d", .driver_data = ETNA_PIPE_2D, },
+	{ .name = "etnaviv-gpu,2d" },
 	{ },
 };
 
@@ -878,8 +878,10 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 
 	gpu->submitted_fence = submit->fence;
 
-	if (priv->lastctx != ctx)
+	if (priv->lastctx != ctx) {
 		gpu->mmu->need_flush = true;
+		gpu->switch_context = true;
+	}
 
 	etnaviv_buffer_queue(gpu, event, submit);
 
@@ -1041,21 +1043,8 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 	struct drm_device *drm = data;
 	struct etnaviv_drm_private *priv = drm->dev_private;
 	struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
-	int idx = gpu->pipe;
 	int ret;
 
-	dev_info(dev, "pre gpu[idx]: %p\n", priv->gpu[idx]);
-
-	if (priv->gpu[idx] == NULL) {
-		dev_info(dev, "adding core @idx %d\n", idx);
-		priv->gpu[idx] = gpu;
-	} else {
-		dev_err(dev, "failed to add core @idx %d\n", idx);
-		goto fail;
-	}
-
-	dev_info(dev, "post gpu[idx]: %p\n", priv->gpu[idx]);
-
 #ifdef CONFIG_PM
 	ret = pm_runtime_get_sync(gpu->dev);
 #else
@@ -1073,12 +1062,12 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 	setup_timer(&gpu->hangcheck_timer, hangcheck_handler,
 			(unsigned long)gpu);
 
+	priv->gpu[priv->num_gpus++] = gpu;
+
 	pm_runtime_mark_last_busy(gpu->dev);
 	pm_runtime_put_autosuspend(gpu->dev);
 
 	return 0;
-fail:
-	return -1;
 }
 
 static void etnaviv_gpu_unbind(struct device *dev, struct device *master,
@@ -1119,23 +1108,13 @@ static const struct component_ops gpu_ops = {
 
 static const struct of_device_id etnaviv_gpu_match[] = {
 	{
-		.compatible = "vivante,vivante-gpu-2d",
-		.data = (void *)ETNA_PIPE_2D
+		.compatible = "vivante,gc"
 	},
-	{
-		.compatible = "vivante,vivante-gpu-3d",
-		.data = (void *)ETNA_PIPE_3D
-	},
-	{
-		.compatible = "vivante,vivante-gpu-vg",
-		.data = (void *)ETNA_PIPE_VG
-	},
-	{ }
+	{ /* sentinel */ }
 };
 
 static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 {
-	const struct of_device_id *match;
 	struct device *dev = &pdev->dev;
 	struct etnaviv_gpu *gpu;
 	int err = 0;
@@ -1144,17 +1123,6 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 	if (!gpu)
 		return -ENOMEM;
 
-	if (pdev->dev.of_node) {
-		match = of_match_device(etnaviv_gpu_match, &pdev->dev);
-		if (!match)
-			return -EINVAL;
-		gpu->pipe = (int)match->data;
-	} else if (pdev->id_entry) {
-		gpu->pipe = pdev->id_entry->driver_data;
-	} else {
-		return -EINVAL;
-	}
-
 	gpu->dev = &pdev->dev;
 
 	/*
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index 9feab07da457..9465f7f56cdf 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -88,8 +88,8 @@ struct etnaviv_gpu {
 	struct drm_device *drm;
 	struct device *dev;
 	struct etnaviv_chip_identity identity;
-	int pipe;
 	bool initialized;
+	bool switch_context;
 
 	/* 'ring'-buffer: */
 	struct drm_gem_object *buffer;
diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h
index 52c6989ad93f..dfd51fcd56d6 100644
--- a/include/uapi/drm/etnaviv_drm.h
+++ b/include/uapi/drm/etnaviv_drm.h
@@ -34,12 +34,6 @@
  *     fields.. so that has to be somehow ok.
  */
 
-#define ETNA_PIPE_3D      0x00
-#define ETNA_PIPE_2D      0x01
-#define ETNA_PIPE_VG      0x02
-
-#define ETNA_MAX_PIPES    3
-
 /* timeouts are specified in clock-monotonic absolute times (to simplify
  * restarting interrupted ioctls).  The following struct is logically the
  * same as 'struct timespec' but 32/64b ABI safe.
@@ -70,8 +64,10 @@ struct drm_etnaviv_timespec {
 
 /* #define MSM_PARAM_GMEM_SIZE  0x02 */
 
+#define ETNA_MAX_PIPES 4
+
 struct drm_etnaviv_param {
-	uint32_t pipe;           /* in, ETNA_PIPE_x */
+	uint32_t pipe;           /* in */
 	uint32_t param;          /* in, ETNAVIV_PARAM_x */
 	uint64_t value;          /* out (get_param) or in (set_param) */
 };
@@ -182,11 +178,16 @@ struct drm_etnaviv_gem_submit_bo {
  * one or more cmdstream buffers.  This allows for conditional execution
  * (context-restore), and IB buffers needed for per tile/bin draw cmds.
  */
+#define ETNA_PIPE_3D      0x00
+#define ETNA_PIPE_2D      0x01
+#define ETNA_PIPE_VG      0x02
 struct drm_etnaviv_gem_submit {
-	uint32_t pipe;           /* in, ETNA_PIPE_x */
+	uint32_t pipe;           /* in */
+	uint32_t exec_state;     /* in, initial execution state (ETNA_PIPE_x) */
 	uint32_t fence;          /* out */
 	uint32_t nr_bos;         /* in, number of submit_bo's */
 	uint32_t nr_cmds;        /* in, number of submit_cmd's */
+	uint32_t pad;
 	uint64_t bos;            /* in, ptr to array of submit_bo's */
 	uint64_t cmds;           /* in, ptr to array of submit_cmd's */
 };
@@ -199,7 +200,7 @@ struct drm_etnaviv_gem_submit {
  * APIs without requiring a dummy bo to synchronize on.
  */
 struct drm_etnaviv_wait_fence {
-	uint32_t pipe;           /* in, ETNA_PIPE_x */
+	uint32_t pipe;           /* in */
 	uint32_t fence;          /* in */
 	struct drm_etnaviv_timespec timeout;   /* in */
 };
-- 
2.1.4



More information about the dri-devel mailing list