[PATCH v3 1/4] drm/i915: Implements dynamic WOPCM partitioning.

Jackie Li yaodong.li at intel.com
Tue Dec 5 18:23:19 UTC 2017


Static WOPCM partitioning will lead to GuC loading failure
if the GuC/HuC firmware size exceeded current static 512KB
partition boundary.

This patch enables the dynamic calculation of the WOPCM aperture
used by GuC/HuC firmware. GuC WOPCM offset is  set to
HuC size + reserved WOPCM size. GuC WOPCM size is set to
total WOPCM size - GuC WOPCM offset - RC6CTX size. In this case,
GuC WOPCM offset will be updated based on the size of HuC firmware
while GuC WOPCM size will be set to use all the remaining WOPCM space.

v2:
 - Removed intel_wopcm_init (Ville/Sagar/Joonas)
 - Renamed and Moved the intel_wopcm_partition into intel_guc (Sagar)
 - Removed unnecessary function calls (Joonas)
 - Init GuC WOPCM partition as soon as firmware fetching is completed

v3:
 - Fixed indentation issues (Chris)
 - Moved GuC WOCPM partitioning code into intel_guc.c (Sagar)
 - Used inline function to avoid code duplication (Michal)

Cc: Michal Wajdeczko <michal.wajdeczko at intel.com>
Cc: Sagar Arun Kamble <sagar.a.kamble at intel.com>
Cc: Sujaritha Sundaresan <sujaritha.sundaresan at intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio at intel.com>
Cc: John Spotswood <john.a.spotswood at intel.com>
Cc: Oscar Mateo <oscar.mateo at intel.com>
Signed-off-by: Jackie Li <yaodong.li at intel.com>
---
 drivers/gpu/drm/i915/i915_gem_context.c     |  4 +-
 drivers/gpu/drm/i915/intel_guc.c            | 76 ++++++++++++++++++++++++-----
 drivers/gpu/drm/i915/intel_guc.h            | 25 +++++++---
 drivers/gpu/drm/i915/intel_guc_ct.c         |  4 +-
 drivers/gpu/drm/i915/intel_guc_fw.c         |  2 +-
 drivers/gpu/drm/i915/intel_guc_log.c        |  2 +-
 drivers/gpu/drm/i915/intel_guc_reg.h        | 18 +++++--
 drivers/gpu/drm/i915/intel_guc_submission.c | 16 +++---
 drivers/gpu/drm/i915/intel_huc.c            |  7 +--
 drivers/gpu/drm/i915/intel_uc.c             | 67 ++++++++++++++++++++++++-
 drivers/gpu/drm/i915/intel_uc_fw.c          | 11 +++--
 drivers/gpu/drm/i915/intel_uc_fw.h          |  8 +++
 12 files changed, 195 insertions(+), 45 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index ce3139e..01b8ecb 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -312,12 +312,12 @@ __create_hw_context(struct drm_i915_private *dev_priv,
 	ctx->desc_template =
 		default_desc_template(dev_priv, dev_priv->mm.aliasing_ppgtt);
 
-	/* GuC requires the ring to be placed above GUC_WOPCM_TOP. If GuC is not
+	/* GuC requires the ring to be placed above GuC WOPCM top. if GuC is not
 	 * present or not in use we still need a small bias as ring wraparound
 	 * at offset 0 sometimes hangs. No idea why.
 	 */
 	if (HAS_GUC(dev_priv) && i915_modparams.enable_guc_loading)
-		ctx->ggtt_offset_bias = GUC_WOPCM_TOP;
+		ctx->ggtt_offset_bias = dev_priv->guc.wopcm.top;
 	else
 		ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE;
 
diff --git a/drivers/gpu/drm/i915/intel_guc.c b/drivers/gpu/drm/i915/intel_guc.c
index d08e760..6d62428 100644
--- a/drivers/gpu/drm/i915/intel_guc.c
+++ b/drivers/gpu/drm/i915/intel_guc.c
@@ -129,8 +129,8 @@ void intel_guc_init_params(struct intel_guc *guc)
 
 	/* If GuC submission is enabled, set up additional parameters here */
 	if (i915_modparams.enable_guc_submission) {
-		u32 ads = guc_ggtt_offset(guc->ads_vma) >> PAGE_SHIFT;
-		u32 pgs = guc_ggtt_offset(dev_priv->guc.stage_desc_pool);
+		u32 ads = guc_ggtt_offset(guc, guc->ads_vma) >> PAGE_SHIFT;
+		u32 pgs = guc_ggtt_offset(guc, dev_priv->guc.stage_desc_pool);
 		u32 ctx_in_16 = GUC_MAX_STAGE_DESCRIPTORS / 16;
 
 		params[GUC_CTL_DEBUG] |= ads << GUC_ADS_ADDR_SHIFT;
@@ -278,7 +278,7 @@ int intel_guc_suspend(struct drm_i915_private *dev_priv)
 	data[0] = INTEL_GUC_ACTION_ENTER_S_STATE;
 	/* any value greater than GUC_POWER_D0 */
 	data[1] = GUC_POWER_D1;
-	data[2] = guc_ggtt_offset(guc->shared_data);
+	data[2] = guc_ggtt_offset(guc, guc->shared_data);
 
 	return intel_guc_send(guc, data, ARRAY_SIZE(data));
 }
@@ -301,7 +301,7 @@ int intel_guc_reset_engine(struct intel_guc *guc,
 	data[3] = 0;
 	data[4] = 0;
 	data[5] = guc->execbuf_client->stage_id;
-	data[6] = guc_ggtt_offset(guc->shared_data);
+	data[6] = guc_ggtt_offset(guc, guc->shared_data);
 
 	return intel_guc_send(guc, data, ARRAY_SIZE(data));
 }
@@ -323,7 +323,7 @@ int intel_guc_resume(struct drm_i915_private *dev_priv)
 
 	data[0] = INTEL_GUC_ACTION_EXIT_S_STATE;
 	data[1] = GUC_POWER_D0;
-	data[2] = guc_ggtt_offset(guc->shared_data);
+	data[2] = guc_ggtt_offset(guc, guc->shared_data);
 
 	return intel_guc_send(guc, data, ARRAY_SIZE(data));
 }
@@ -336,7 +336,7 @@ int intel_guc_resume(struct drm_i915_private *dev_priv)
  * This is a wrapper to create an object for use with the GuC. In order to
  * use it inside the GuC, an object needs to be pinned lifetime, so we allocate
  * both some backing storage and a range inside the Global GTT. We must pin
- * it in the GGTT somewhere other than than [0, GUC_WOPCM_TOP) because that
+ * it in the GGTT somewhere other than than [0, GuC WOPCM top) because that
  * range is reserved inside GuC.
  *
  * Return:	A i915_vma if successful, otherwise an ERR_PTR.
@@ -357,7 +357,8 @@ struct i915_vma *intel_guc_allocate_vma(struct intel_guc *guc, u32 size)
 		goto err;
 
 	ret = i915_vma_pin(vma, 0, PAGE_SIZE,
-			   PIN_GLOBAL | PIN_OFFSET_BIAS | GUC_WOPCM_TOP);
+			   PIN_GLOBAL | PIN_OFFSET_BIAS |
+			   dev_priv->guc.wopcm.top);
 	if (ret) {
 		vma = ERR_PTR(ret);
 		goto err;
@@ -370,13 +371,64 @@ struct i915_vma *intel_guc_allocate_vma(struct intel_guc *guc, u32 size)
 	return vma;
 }
 
-u32 intel_guc_wopcm_size(struct drm_i915_private *dev_priv)
+static inline u32 guc_reserved_wopcm_size(struct intel_guc *guc)
 {
-	u32 wopcm_size = GUC_WOPCM_TOP;
+	struct drm_i915_private *i915 = guc_to_i915(guc);
 
 	/* On BXT, the top of WOPCM is reserved for RC6 context */
-	if (IS_GEN9_LP(dev_priv))
-		wopcm_size -= BXT_GUC_WOPCM_RC6_RESERVED;
+	if (IS_GEN9_LP(i915))
+		return BXT_WOPCM_RC6_RESERVED;
 
-	return wopcm_size;
+	return 0;
+}
+
+/*
+ * intel_guc_wopcm_init() - Initialize the GuC WOPCM partition.
+ * @guc: intel guc.
+ * @guc_size: size of GuC firmware.
+ * @huc_size: size of HuC firmware.
+ *
+ * This function tries to initialize the WOPCM partition based on HuC firmware
+ * size and the reserved WOPCM memory size.
+ *
+ * Return: 0 on success, non-zero error code on failure.
+ */
+int intel_guc_init_wopcm_partition(struct intel_guc *guc, u32 guc_size,
+				   u32 huc_size)
+{
+	u32 reserved = guc_reserved_wopcm_size(guc);
+	u32 offset, top, size;
+
+	GEM_BUG_ON(!guc_size);
+
+	if (reserved >= WOPCM_DEFAULT_SIZE)
+		return -E2BIG;
+
+	offset = huc_size + WOPCM_RESERVED_SIZE;
+	if (offset >= WOPCM_DEFAULT_SIZE)
+		return -E2BIG;
+
+	/* Hardware requires GuC WOPCM offset needs to be 16K aligned. */
+	offset = ALIGN(offset, WOPCM_OFFSET_ALIGNMENT);
+	if ((offset + reserved) >= WOPCM_DEFAULT_SIZE)
+		return -E2BIG;
+
+	top = WOPCM_DEFAULT_SIZE - offset;
+	size = top - reserved;
+
+	/*
+	 * GuC size needs to be less than or equal to GuC WOPCM size.
+	 * Need extra 8K stack for GuC.
+	 */
+	if ((guc_size + GUC_WOPCM_STACK_RESERVED) > size)
+		return -E2BIG;
+
+	guc->wopcm.offset = offset;
+	guc->wopcm.top = top;
+	guc->wopcm.size = size;
+
+	DRM_DEBUG_DRIVER("GuC WOPCM offset %dKB, size %dKB, top %dKB\n",
+			 offset >> 10, size >> 10, top >> 10);
+
+	return 0;
 }
diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h
index 5985672..4550de1 100644
--- a/drivers/gpu/drm/i915/intel_guc.h
+++ b/drivers/gpu/drm/i915/intel_guc.h
@@ -39,6 +39,12 @@ struct guc_preempt_work {
 	struct intel_engine_cs *engine;
 };
 
+struct guc_wopcm_partition {
+	u32 offset;
+	u32 size;
+	u32 top;
+};
+
 /*
  * Top level structure of GuC. It handles firmware loading and manages client
  * pool and doorbells. intel_guc owns a intel_guc_client to replace the legacy
@@ -46,6 +52,7 @@ struct guc_preempt_work {
  */
 struct intel_guc {
 	struct intel_uc_fw fw;
+	struct guc_wopcm_partition wopcm;
 	struct intel_guc_log log;
 	struct intel_guc_ct ct;
 
@@ -101,18 +108,19 @@ static inline void intel_guc_notify(struct intel_guc *guc)
 }
 
 /*
- * GuC does not allow any gfx GGTT address that falls into range [0, WOPCM_TOP),
- * which is reserved for Boot ROM, SRAM and WOPCM. Currently this top address is
- * 512K. In order to exclude 0-512K address space from GGTT, all gfx objects
- * used by GuC is pinned with PIN_OFFSET_BIAS along with size of WOPCM.
+ * GuC does not allow any gfx GGTT address that falls into range
+ * [0, GuC WOPCM top), which is reserved for Boot ROM, SRAM and WOPCM.
+ * All gfx objects used by GuC is pinned with PIN_OFFSET_BIAS along with
+ * top of WOPCM.
  */
-static inline u32 guc_ggtt_offset(struct i915_vma *vma)
+static inline u32 guc_ggtt_offset(struct intel_guc *guc, struct i915_vma *vma)
 {
+	u32 top = guc->wopcm.top;
 	u32 offset = i915_ggtt_offset(vma);
 
-	GEM_BUG_ON(offset < GUC_WOPCM_TOP);
+	GEM_BUG_ON(!top);
+	GEM_BUG_ON(offset < top);
 	GEM_BUG_ON(range_overflows_t(u64, offset, vma->size, GUC_GGTT_TOP));
-
 	return offset;
 }
 
@@ -126,6 +134,7 @@ int intel_guc_auth_huc(struct intel_guc *guc, u32 rsa_offset);
 int intel_guc_suspend(struct drm_i915_private *dev_priv);
 int intel_guc_resume(struct drm_i915_private *dev_priv);
 struct i915_vma *intel_guc_allocate_vma(struct intel_guc *guc, u32 size);
-u32 intel_guc_wopcm_size(struct drm_i915_private *dev_priv);
+int intel_guc_init_wopcm_partition(struct intel_guc *guc, u32 guc_size,
+				   u32 huc_size);
 
 #endif
diff --git a/drivers/gpu/drm/i915/intel_guc_ct.c b/drivers/gpu/drm/i915/intel_guc_ct.c
index 24ad557..f3e3f16 100644
--- a/drivers/gpu/drm/i915/intel_guc_ct.c
+++ b/drivers/gpu/drm/i915/intel_guc_ct.c
@@ -156,7 +156,7 @@ static int ctch_init(struct intel_guc *guc,
 		err = PTR_ERR(blob);
 		goto err_vma;
 	}
-	DRM_DEBUG_DRIVER("CT: vma base=%#x\n", guc_ggtt_offset(ctch->vma));
+	DRM_DEBUG_DRIVER("CT: vma base=%#x\n", guc_ggtt_offset(guc, ctch->vma));
 
 	/* store pointers to desc and cmds */
 	for (i = 0; i < ARRAY_SIZE(ctch->ctbs); i++) {
@@ -202,7 +202,7 @@ static int ctch_open(struct intel_guc *guc,
 	}
 
 	/* vma should be already allocated and map'ed */
-	base = guc_ggtt_offset(ctch->vma);
+	base = guc_ggtt_offset(guc, ctch->vma);
 
 	/* (re)initialize descriptors
 	 * cmds buffers are in the second half of the blob page
diff --git a/drivers/gpu/drm/i915/intel_guc_fw.c b/drivers/gpu/drm/i915/intel_guc_fw.c
index 89862fa..5b7a7b6 100644
--- a/drivers/gpu/drm/i915/intel_guc_fw.c
+++ b/drivers/gpu/drm/i915/intel_guc_fw.c
@@ -165,7 +165,7 @@ static int guc_xfer_ucode(struct intel_guc *guc, struct i915_vma *vma)
 	I915_WRITE(DMA_COPY_SIZE, guc_fw->header_size + guc_fw->ucode_size);
 
 	/* Set the source address for the new blob */
-	offset = guc_ggtt_offset(vma) + guc_fw->header_offset;
+	offset = guc_ggtt_offset(guc, vma) + guc_fw->header_offset;
 	I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset));
 	I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF);
 
diff --git a/drivers/gpu/drm/i915/intel_guc_log.c b/drivers/gpu/drm/i915/intel_guc_log.c
index 76d3eb1..3e41d71 100644
--- a/drivers/gpu/drm/i915/intel_guc_log.c
+++ b/drivers/gpu/drm/i915/intel_guc_log.c
@@ -572,7 +572,7 @@ int intel_guc_log_create(struct intel_guc *guc)
 		(GUC_LOG_ISR_PAGES << GUC_LOG_ISR_SHIFT) |
 		(GUC_LOG_CRASH_PAGES << GUC_LOG_CRASH_SHIFT);
 
-	offset = guc_ggtt_offset(vma) >> PAGE_SHIFT; /* in pages */
+	offset = guc_ggtt_offset(guc, vma) >> PAGE_SHIFT; /* in pages */
 	guc->log.flags = (offset << GUC_LOG_BUF_ADDR_SHIFT) | flags;
 
 	return 0;
diff --git a/drivers/gpu/drm/i915/intel_guc_reg.h b/drivers/gpu/drm/i915/intel_guc_reg.h
index 19a9247..419d922 100644
--- a/drivers/gpu/drm/i915/intel_guc_reg.h
+++ b/drivers/gpu/drm/i915/intel_guc_reg.h
@@ -68,7 +68,6 @@
 #define DMA_GUC_WOPCM_OFFSET		_MMIO(0xc340)
 #define   HUC_LOADING_AGENT_VCR		  (0<<1)
 #define   HUC_LOADING_AGENT_GUC		  (1<<1)
-#define   GUC_WOPCM_OFFSET_VALUE	  0x80000	/* 512KB */
 #define GUC_MAX_IDLE_COUNT		_MMIO(0xC3E4)
 
 #define HUC_STATUS2             _MMIO(0xD3B0)
@@ -76,13 +75,24 @@
 
 /* Defines WOPCM space available to GuC firmware */
 #define GUC_WOPCM_SIZE			_MMIO(0xc050)
-/* GuC addresses below GUC_WOPCM_TOP don't map through the GTT */
-#define   GUC_WOPCM_TOP			  (0x80 << 12)	/* 512KB */
-#define   BXT_GUC_WOPCM_RC6_RESERVED	  (0x10 << 12)	/* 64KB  */
 
 /* GuC addresses above GUC_GGTT_TOP also don't map through the GTT */
 #define GUC_GGTT_TOP			0xFEE00000
 
+/* Default WOPCM size 1MB */
+#define WOPCM_DEFAULT_SIZE		(0x1 << 20)
+/* Reserved WOPCM size 16KB */
+#define WOPCM_RESERVED_SIZE		(0x4000)
+/* GUC WOPCM Offset need to be 16KB aligned */
+#define WOPCM_OFFSET_ALIGNMENT		(0x4000)
+/* 8KB stack reserved for GuC FW*/
+#define GUC_WOPCM_STACK_RESERVED	(0x2000)
+/* 24KB WOPCM reserved for RC6 CTX on BXT */
+#define BXT_WOPCM_RC6_RESERVED		(0x6000)
+
+#define GEN9_GUC_WOPCM_DELTA		4
+#define GEN9_GUC_WOPCM_OFFSET		(0x24000)
+
 #define GEN8_GT_PM_CONFIG		_MMIO(0x138140)
 #define GEN9LP_GT_PM_CONFIG		_MMIO(0x138140)
 #define GEN9_GT_PM_CONFIG		_MMIO(0x13816c)
diff --git a/drivers/gpu/drm/i915/intel_guc_submission.c b/drivers/gpu/drm/i915/intel_guc_submission.c
index 912ff14..7a2128a 100644
--- a/drivers/gpu/drm/i915/intel_guc_submission.c
+++ b/drivers/gpu/drm/i915/intel_guc_submission.c
@@ -402,7 +402,8 @@ static void guc_stage_desc_init(struct intel_guc *guc,
 
 		/* The state page is after PPHWSP */
 		lrc->ring_lrca =
-			guc_ggtt_offset(ce->state) + LRC_STATE_PN * PAGE_SIZE;
+			guc_ggtt_offset(guc, ce->state) +
+			LRC_STATE_PN * PAGE_SIZE;
 
 		/* XXX: In direct submission, the GuC wants the HW context id
 		 * here. In proxy submission, it wants the stage id
@@ -410,7 +411,7 @@ static void guc_stage_desc_init(struct intel_guc *guc,
 		lrc->context_id = (client->stage_id << GUC_ELC_CTXID_OFFSET) |
 				(guc_engine_id << GUC_ELC_ENGINE_OFFSET);
 
-		lrc->ring_begin = guc_ggtt_offset(ce->ring->vma);
+		lrc->ring_begin = guc_ggtt_offset(guc, ce->ring->vma);
 		lrc->ring_end = lrc->ring_begin + ce->ring->size - 1;
 		lrc->ring_next_free_location = lrc->ring_begin;
 		lrc->ring_current_tail_pointer_value = 0;
@@ -426,7 +427,7 @@ static void guc_stage_desc_init(struct intel_guc *guc,
 	 * The doorbell, process descriptor, and workqueue are all parts
 	 * of the client object, which the GuC will reference via the GGTT
 	 */
-	gfx_addr = guc_ggtt_offset(client->vma);
+	gfx_addr = guc_ggtt_offset(guc, client->vma);
 	desc->db_trigger_phy = sg_dma_address(client->vma->pages->sgl) +
 				client->doorbell_offset;
 	desc->db_trigger_cpu = ptr_to_u64(__get_doorbell(client));
@@ -649,7 +650,7 @@ static void inject_preempt_context(struct work_struct *work)
 	data[3] = engine->guc_id;
 	data[4] = guc->execbuf_client->priority;
 	data[5] = guc->execbuf_client->stage_id;
-	data[6] = guc_ggtt_offset(guc->shared_data);
+	data[6] = guc_ggtt_offset(guc, guc->shared_data);
 
 	if (WARN_ON(intel_guc_send(guc, data, ARRAY_SIZE(data)))) {
 		execlists_clear_active(&engine->execlists,
@@ -1186,8 +1187,9 @@ static int guc_ads_create(struct intel_guc *guc)
 	 * because our GuC shared data is there.
 	 */
 	blob->ads.golden_context_lrca =
-		guc_ggtt_offset(dev_priv->kernel_context->engine[RCS].state) +
-		skipped_offset;
+		guc_ggtt_offset(guc,
+				dev_priv->kernel_context->engine[RCS].state) +
+				skipped_offset;
 
 	/*
 	 * The GuC expects us to exclude the portion of the context image that
@@ -1199,7 +1201,7 @@ static int guc_ads_create(struct intel_guc *guc)
 		blob->ads.eng_state_size[engine->guc_id] =
 			engine->context_size - skipped_size;
 
-	base = guc_ggtt_offset(vma);
+	base = guc_ggtt_offset(guc, vma);
 	blob->ads.scheduler_policies = base + ptr_offset(blob, policies);
 	blob->ads.reg_state_buffer = base + ptr_offset(blob, reg_state_buffer);
 	blob->ads.reg_state_addr = base + ptr_offset(blob, reg_state);
diff --git a/drivers/gpu/drm/i915/intel_huc.c b/drivers/gpu/drm/i915/intel_huc.c
index 98d1725..e1c3bf7 100644
--- a/drivers/gpu/drm/i915/intel_huc.c
+++ b/drivers/gpu/drm/i915/intel_huc.c
@@ -134,7 +134,7 @@ static int huc_ucode_xfer(struct intel_uc_fw *huc_fw, struct i915_vma *vma)
 	intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
 
 	/* Set the source address for the uCode */
-	offset = guc_ggtt_offset(vma) + huc_fw->header_offset;
+	offset = guc_ggtt_offset(&dev_priv->guc, vma) + huc_fw->header_offset;
 	I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset));
 	I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF);
 
@@ -202,7 +202,7 @@ void intel_huc_auth(struct intel_huc *huc)
 		return;
 
 	vma = i915_gem_object_ggtt_pin(huc->fw.obj, NULL, 0, 0,
-				PIN_OFFSET_BIAS | GUC_WOPCM_TOP);
+				       PIN_OFFSET_BIAS | guc->wopcm.top);
 	if (IS_ERR(vma)) {
 		DRM_ERROR("failed to pin huc fw object %d\n",
 				(int)PTR_ERR(vma));
@@ -210,7 +210,8 @@ void intel_huc_auth(struct intel_huc *huc)
 	}
 
 	ret = intel_guc_auth_huc(guc,
-				 guc_ggtt_offset(vma) + huc->fw.rsa_offset);
+				 guc_ggtt_offset(guc, vma) +
+				 huc->fw.rsa_offset);
 	if (ret) {
 		DRM_ERROR("HuC: GuC did not ack Auth request %d\n", ret);
 		goto out;
diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c
index 1e2a30a..4f964b1 100644
--- a/drivers/gpu/drm/i915/intel_uc.c
+++ b/drivers/gpu/drm/i915/intel_uc.c
@@ -86,10 +86,73 @@ void intel_uc_init_early(struct drm_i915_private *dev_priv)
 	intel_guc_init_early(&dev_priv->guc);
 }
 
+static inline int gen9_wocpm_size_check(struct drm_i915_private *i915)
+{
+	struct guc_wopcm_partition *wp = &i915->guc.wopcm;
+	u32 wopcm_base;
+	u32 delta;
+
+	/*
+	 * Check hardware restriction on Gen9
+	 * GuC WOPCM size is at least 4 bytes larger than GuC WOPCM base due
+	 * to hardware limitation on Gen9.
+	 */
+	wopcm_base = wp->offset + GEN9_GUC_WOPCM_OFFSET;
+	if (unlikely(wopcm_base > wp->size))
+		return -E2BIG;
+
+	delta = wp->size - wopcm_base;
+	if (unlikely(delta < GEN9_GUC_WOPCM_DELTA))
+		return -E2BIG;
+
+	return 0;
+}
+
+static int intel_uc_wopcm_partition_init(struct drm_i915_private *i915)
+{
+	struct intel_guc *guc = &i915->guc;
+	struct intel_huc *huc = &i915->huc;
+	u32 guc_size = intel_uc_fw_get_size(&guc->fw);
+	u32 huc_size = intel_uc_fw_get_size(&huc->fw);
+	int err;
+
+	if (!i915_modparams.enable_guc_loading)
+		return 0;
+
+	/* GuC firmware is not present, do not need to proceed */
+	if (!guc_size)
+		goto fail;
+
+	err = intel_guc_init_wopcm_partition(guc, guc_size, huc_size);
+	if (err)
+		goto fail;
+
+	/* Platform specific restrictions */
+	if (IS_GEN9(i915)) {
+		err = gen9_wocpm_size_check(i915);
+		if (err)
+			goto fail;
+	}
+
+	return 0;
+
+fail:
+	DRM_NOTE("WOPCM partitioning failed. Falling back to execlist mode\n");
+
+	/* GuC submission won't work without valid GuC WOPCM partition */
+	if (i915_modparams.enable_guc_submission)
+		i915_modparams.enable_guc_submission = 0;
+
+	i915_modparams.enable_guc_loading = 0;
+
+	return err;
+}
+
 void intel_uc_init_fw(struct drm_i915_private *dev_priv)
 {
 	intel_uc_fw_fetch(dev_priv, &dev_priv->huc.fw);
 	intel_uc_fw_fetch(dev_priv, &dev_priv->guc.fw);
+	intel_uc_wopcm_partition_init(dev_priv);
 }
 
 void intel_uc_fini_fw(struct drm_i915_private *dev_priv)
@@ -174,9 +237,9 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv)
 	}
 
 	/* init WOPCM */
-	I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv));
+	I915_WRITE(GUC_WOPCM_SIZE, guc->wopcm.size);
 	I915_WRITE(DMA_GUC_WOPCM_OFFSET,
-		   GUC_WOPCM_OFFSET_VALUE | HUC_LOADING_AGENT_GUC);
+		   guc->wopcm.offset | HUC_LOADING_AGENT_GUC);
 
 	/* WaEnableuKernelHeaderValidFix:skl */
 	/* WaEnableGuCBootHashCheckNotSet:skl,bxt,kbl */
diff --git a/drivers/gpu/drm/i915/intel_uc_fw.c b/drivers/gpu/drm/i915/intel_uc_fw.c
index b376dd3..ea8c61a 100644
--- a/drivers/gpu/drm/i915/intel_uc_fw.c
+++ b/drivers/gpu/drm/i915/intel_uc_fw.c
@@ -95,9 +95,13 @@ void intel_uc_fw_fetch(struct drm_i915_private *dev_priv,
 	uc_fw->ucode_offset = uc_fw->header_offset + uc_fw->header_size;
 	uc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32);
 
-	/* Header and uCode will be loaded to WOPCM */
+	/*
+	 * Header and uCode will be loaded to WOPCM
+	 * Only check the size against the overall available WOPCM here. Will
+	 * continue to check the size during WOPCM partition calculation.
+	 */
 	size = uc_fw->header_size + uc_fw->ucode_size;
-	if (size > intel_guc_wopcm_size(dev_priv)) {
+	if (size > WOPCM_DEFAULT_SIZE) {
 		DRM_WARN("%s: Firmware is too large to fit in WOPCM\n",
 			 intel_uc_fw_type_repr(uc_fw->type));
 		err = -E2BIG;
@@ -207,6 +211,7 @@ int intel_uc_fw_upload(struct intel_uc_fw *uc_fw,
 		       int (*xfer)(struct intel_uc_fw *uc_fw,
 				   struct i915_vma *vma))
 {
+	struct drm_i915_private *i915 = to_i915(uc_fw->obj->base.dev);
 	struct i915_vma *vma;
 	int err;
 
@@ -230,7 +235,7 @@ int intel_uc_fw_upload(struct intel_uc_fw *uc_fw,
 	}
 
 	vma = i915_gem_object_ggtt_pin(uc_fw->obj, NULL, 0, 0,
-				       PIN_OFFSET_BIAS | GUC_WOPCM_TOP);
+				       PIN_OFFSET_BIAS | i915->guc.wopcm.top);
 	if (IS_ERR(vma)) {
 		err = PTR_ERR(vma);
 		DRM_DEBUG_DRIVER("%s fw ggtt-pin err=%d\n",
diff --git a/drivers/gpu/drm/i915/intel_uc_fw.h b/drivers/gpu/drm/i915/intel_uc_fw.h
index 5394d9d..01cf332 100644
--- a/drivers/gpu/drm/i915/intel_uc_fw.h
+++ b/drivers/gpu/drm/i915/intel_uc_fw.h
@@ -110,6 +110,14 @@ void intel_uc_fw_init(struct intel_uc_fw *uc_fw, enum intel_uc_fw_type type)
 	uc_fw->type = type;
 }
 
+static inline u32 intel_uc_fw_get_size(struct intel_uc_fw *uc_fw)
+{
+	if (uc_fw->fetch_status != INTEL_UC_FIRMWARE_SUCCESS)
+		return 0;
+
+	return uc_fw->header_size + uc_fw->ucode_size;
+}
+
 void intel_uc_fw_fetch(struct drm_i915_private *dev_priv,
 		       struct intel_uc_fw *uc_fw);
 int intel_uc_fw_upload(struct intel_uc_fw *uc_fw,
-- 
2.7.4



More information about the Intel-gfx-trybot mailing list