diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index 648e753..546404e 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -312,12 +312,13 @@ __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 (USES_GUC(dev_priv))
-		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 d9bc2a9..ecd5da2 100644
--- a/drivers/gpu/drm/i915/intel_guc.c
+++ b/drivers/gpu/drm/i915/intel_guc.c
@@ -65,6 +65,7 @@ void intel_guc_init_early(struct intel_guc *guc)
 	intel_guc_fw_init_early(guc);
 	intel_guc_ct_init_early(&guc->ct);
 	intel_guc_log_init_early(guc);
+	intel_guc_wopcm_init_early(&guc->wopcm);
 
 	mutex_init(&guc->send_mutex);
 	guc->send = intel_guc_send_nop;
@@ -478,7 +479,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.
@@ -499,7 +500,7 @@ 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 | guc->wopcm.top);
 	if (ret) {
 		vma = ERR_PTR(ret);
 		goto err;
diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h
index 50be6de..06f315e 100644
--- a/drivers/gpu/drm/i915/intel_guc.h
+++ b/drivers/gpu/drm/i915/intel_guc.h
@@ -49,6 +49,7 @@ struct intel_guc {
 	struct intel_uc_fw fw;
 	struct intel_guc_log log;
 	struct intel_guc_ct ct;
+	struct intel_guc_wopcm wopcm;
 
 	/* Log snapshot if GuC errors during load */
 	struct drm_i915_gem_object *load_err_log;
@@ -109,10 +110,10 @@ static inline void intel_guc_notify(struct intel_guc *guc)
  * @guc: intel guc.
  * @vma: i915 graphics virtual memory area.
  *
- * 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.
  *
  * Return: GGTT offset that meets the GuC gfx address requirement.
  */
@@ -121,7 +122,8 @@ static inline u32 intel_guc_ggtt_offset(struct intel_guc *guc,
 {
 	u32 offset = i915_ggtt_offset(vma);
 
-	GEM_BUG_ON(offset < GUC_WOPCM_TOP);
+	GEM_BUG_ON(!guc->wopcm.valid);
+	GEM_BUG_ON(offset < guc->wopcm.top);
 	GEM_BUG_ON(range_overflows_t(u64, offset, vma->size, GUC_GGTT_TOP));
 
 	return offset;
diff --git a/drivers/gpu/drm/i915/intel_guc_wopcm.c b/drivers/gpu/drm/i915/intel_guc_wopcm.c
index 73be9af..1555e79 100644
--- a/drivers/gpu/drm/i915/intel_guc_wopcm.c
+++ b/drivers/gpu/drm/i915/intel_guc_wopcm.c
@@ -25,22 +25,116 @@
 #include "intel_guc_wopcm.h"
 #include "i915_drv.h"
 
-/*
- * intel_guc_wopcm_size() - Get the size of GuC WOPCM.
+static inline u32 guc_reserved_wopcm_size(struct intel_guc *guc)
+{
+	struct drm_i915_private *i915 = guc_to_i915(guc);
+
+	if (IS_GEN9_LP(i915))
+		return BXT_GUC_WOPCM_RC6_RESERVED;
+
+	return 0;
+}
+
+static inline int gen9_guc_wopcm_size_check(struct drm_i915_private *i915)
+{
+	struct intel_guc_wopcm *wopcm = &i915->guc.wopcm;
+	u32 guc_wopcm_start;
+	u32 delta;
+
+	/*
+	 * GuC WOPCM size is at least 4 bytes larger than the offset from WOPCM
+	 * base (GuC WOPCM offset from WOPCM base + GEN9_GUC_WOPCM_OFFSET) due
+	 * to hardware limitation on Gen9.
+	 */
+	guc_wopcm_start = wopcm->offset + GEN9_GUC_WOPCM_OFFSET;
+	if (unlikely(guc_wopcm_start > wopcm->size))
+		return -E2BIG;
+
+	delta = wopcm->size - guc_wopcm_start;
+	if (unlikely(delta < GEN9_GUC_WOPCM_DELTA))
+		return -E2BIG;
+
+	return 0;
+}
+
+static inline int guc_wopcm_check_hw_restrictions(struct intel_guc *guc)
+{
+	struct drm_i915_private *i915 = guc_to_i915(guc);
+
+	if (IS_GEN9(i915))
+		return gen9_guc_wopcm_size_check(i915);
+
+	return 0;
+}
+
+/**
+ * intel_guc_wopcm_init() - Initialize the GuC WOPCM..
  * @guc: intel guc.
+ * @guc_fw_size: size of GuC firmware.
+ * @huc_fw_size: size of HuC firmware.
  *
- * Get the platform specific GuC WOPCM size.
+ * Calculate the GuC WOPCM offset and size based on GuC and HuC firmware sizes.
+ * This function will to set the GuC WOPCM size to the size of maximum WOPCM
+ * available for GuC. This function will also enforce platform dependent
+ * hardware restrictions on GuC WOPCM offset and size. It will fail the GuC
+ * WOPCM init if any of these checks were failed, so that the following GuC
+ * firmware uploading would be aborted.
  *
- * Return: size of the GuC WOPCM.
+ * Return: 0 on success, non-zero error code on failure.
  */
-u32 intel_guc_wopcm_size(struct intel_guc *guc)
+int intel_guc_wopcm_init(struct intel_guc *guc, u32 guc_fw_size,
+			 u32 huc_fw_size)
 {
-	struct drm_i915_private *i915 = guc_to_i915(guc);
-	u32 size = GUC_WOPCM_TOP;
+	u32 reserved = guc_reserved_wopcm_size(guc);
+	u32 offset, size, top;
+	int err;
 
-	/* On BXT, the top of WOPCM is reserved for RC6 context */
-	if (IS_GEN9_LP(i915))
-		size -= BXT_GUC_WOPCM_RC6_RESERVED;
+	if (guc->wopcm.valid)
+		return 0;
+
+	if (!guc_fw_size)
+		return -EINVAL;
+
+	if (reserved >= WOPCM_DEFAULT_SIZE)
+		return -E2BIG;
+
+	offset = huc_fw_size + WOPCM_RESERVED_SIZE;
+	if (offset >= WOPCM_DEFAULT_SIZE)
+		return -E2BIG;
+
+	/* Hardware requires GuC WOPCM offset to be 16K aligned. */
+	offset = ALIGN(offset, GUC_WOPCM_OFFSET_ALIGNMENT);
+	if ((offset + reserved) >= WOPCM_DEFAULT_SIZE)
+		return -E2BIG;
+
+	top = WOPCM_DEFAULT_SIZE - offset;
+	size = top - reserved;
+
+	/* GuC WOPCM size must be multiple of 4K pages */
+	size &= PAGE_MASK;
+
+	/*
+	 * GuC firmware size needs to be less than or equal to the size of the
+	 * available GuC WOPCM (total available GuC WOPCM size - reserved size).
+	 * Need extra 8K stack for GuC firmware.
+	 */
+	reserved = GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED;
+	if ((guc_fw_size + reserved) > size)
+		return -E2BIG;
+
+	guc->wopcm.offset = offset;
+	guc->wopcm.size = size;
+	guc->wopcm.top = top;
+
+	/* Check platform specific restrictions */
+	err = guc_wopcm_check_hw_restrictions(guc);
+	if (err)
+		return err;
+
+	guc->wopcm.valid = 1;
+
+	DRM_DEBUG_DRIVER("GuC WOPCM offset %dKB, size %dKB, top %dKB\n",
+			 offset >> 10, size >> 10, top >> 10);
 
-	return size;
+	return 0;
 }
diff --git a/drivers/gpu/drm/i915/intel_guc_wopcm.h b/drivers/gpu/drm/i915/intel_guc_wopcm.h
index 53de931..28e4103 100644
--- a/drivers/gpu/drm/i915/intel_guc_wopcm.h
+++ b/drivers/gpu/drm/i915/intel_guc_wopcm.h
@@ -29,11 +29,67 @@
 
 struct intel_guc;
 
-#define GUC_WOPCM_OFFSET_VALUE		0x80000	/* 512KB */
-/* 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  */
+/* Default WOPCM size 1MB. */
+#define WOPCM_DEFAULT_SIZE		(0x1 << 20)
+/* The initial 16KB WOPCM is reserved. */
+#define WOPCM_RESERVED_SIZE		(0x4000)
 
-u32 intel_guc_wopcm_size(struct intel_guc *guc);
+/* GUC WOPCM offset needs to be 16KB aligned. */
+#define GUC_WOPCM_OFFSET_ALIGNMENT	(0x4000)
+/* 16KB reserved at the beginning of GuC WOPCM. */
+#define GUC_WOPCM_RESERVED		(0x4000)
+/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */
+#define GUC_WOPCM_STACK_RESERVED	(0x2000)
+/* 24KB at the end of GuC WOPCM is reserved for RC6 CTX on BXT. */
+#define BXT_GUC_WOPCM_RC6_RESERVED	(0x6000)
+
+#define GEN9_GUC_WOPCM_DELTA		4
+/**
+ * GuC WOPCM starts at 144KB (GUC_WOPCM_RESERVED + 128KB reserved for GuC
+ * firmware loading) from GuC WOPCM offset on BXT.
+ */
+#define GEN9_GUC_WOPCM_OFFSET		(0x24000)
+
+/**
+ * intel_guc_wopcm - GuC WOPCM related settings.
+ * @offset: GuC WOPCM offset from the WOPCM base.
+ * @size: size of GuC WOPCM for GuC firmware.
+ * @top: start of the non-GuC WOPCM memory.
+ * @valid: whether this structure contains valid (1-valid, 0-invalid) info.
+ *
+ * We simply use this structure to track the GuC use of WOPCM. The layout of
+ * WOPCM would be defined by writing to GuC WOPCM offset and size registers as
+ * shown below.
+ *
+ * +-----------+<-- top
+ * |###########|
+ * +-----------+<-- size (GuC_WOPCM_SIZE register)
+ * | GuC WOPCM |
+ * +-----------+<-- offset (DMA_GUC_WOPCM_OFFSET register)
+ * |###########|
+ * +-----------+<-- WOPCM base
+ */
+struct intel_guc_wopcm {
+	u32 offset;
+	u32 size;
+	u32 top;
+	u32 valid;
+};
+
+/**
+ * intel_guc_wopcm_init_early() - Early initialization of the GuC WOPCM.
+ * @wopcm: GuC WOPCM.
+ *
+ * Setup the GuC WOPCM top to the top of the overall WOPCM. This will guarantee
+ * that the allocation of the GuC accessible objects won't fall into WOPCM when
+ * GuC partition isn't present.
+ *
+ */
+static inline void intel_guc_wopcm_init_early(struct intel_guc_wopcm *wopcm)
+{
+	wopcm->top = WOPCM_DEFAULT_SIZE;
+}
+
+int intel_guc_wopcm_init(struct intel_guc *guc, u32 guc_size, u32 huc_size);
 
 #endif
diff --git a/drivers/gpu/drm/i915/intel_huc.c b/drivers/gpu/drm/i915/intel_huc.c
index aed9c1c..dc6a6c6 100644
--- a/drivers/gpu/drm/i915/intel_huc.c
+++ b/drivers/gpu/drm/i915/intel_huc.c
@@ -206,7 +206,7 @@ int intel_huc_auth(struct intel_huc *huc)
 		return -ENOEXEC;
 
 	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)) {
 		ret = PTR_ERR(vma);
 		DRM_ERROR("HuC: Failed to pin huc fw object %d\n", ret);
diff --git a/drivers/gpu/drm/i915/intel_uc.c b/drivers/gpu/drm/i915/intel_uc.c
index 1b2831b..2292f31 100644
--- a/drivers/gpu/drm/i915/intel_uc.c
+++ b/drivers/gpu/drm/i915/intel_uc.c
@@ -283,6 +283,9 @@ void intel_uc_fini_misc(struct drm_i915_private *dev_priv)
 int intel_uc_init(struct drm_i915_private *dev_priv)
 {
 	struct intel_guc *guc = &dev_priv->guc;
+	struct intel_huc *huc = &dev_priv->huc;
+	u32 guc_fw_size = intel_uc_fw_get_size(&guc->fw);
+	u32 huc_fw_size = intel_uc_fw_get_size(&huc->fw);
 	int ret;
 
 	if (!USES_GUC(dev_priv))
@@ -291,6 +294,10 @@ int intel_uc_init(struct drm_i915_private *dev_priv)
 	if (!HAS_GUC(dev_priv))
 		return -ENODEV;
 
+	ret = intel_guc_wopcm_init(guc, guc_fw_size, huc_fw_size);
+	if (ret)
+		return ret;
+
 	ret = intel_guc_init(guc);
 	if (ret)
 		return ret;
@@ -340,9 +347,9 @@ int intel_uc_init_hw(struct drm_i915_private *dev_priv)
 	gen9_reset_guc_interrupts(dev_priv);
 
 	/* init WOPCM */
-	I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(guc));
+	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 24945cf..791263a 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->guc)) {
+	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 d5fd460..298d475 100644
--- a/drivers/gpu/drm/i915/intel_uc_fw.h
+++ b/drivers/gpu/drm/i915/intel_uc_fw.h
@@ -115,6 +115,22 @@ static inline bool intel_uc_fw_is_selected(struct intel_uc_fw *uc_fw)
 	return uc_fw->path != NULL;
 }
 
+/**
+ * intel_uc_fw_get_size() - Get the size of the firmware.
+ * @uc_fw: intel_uc_fw structure.
+ *
+ * Get the size of the firmware that will be placed in WOPCM.
+ *
+ * Return: Zero on invalid firmware status. actual size on success.
+ */
+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,
