diff mbox series

drm/i915: Add Wa_14010733141

Message ID 20210401162818.211784-1-aditya.swarup@intel.com (mailing list archive)
State New, archived
Headers show
Series drm/i915: Add Wa_14010733141 | expand

Commit Message

Aditya Swarup April 1, 2021, 4:28 p.m. UTC
The WA requires the following procedure for VDBox SFC reset:

If (MFX-SFC usage is 1) {
	1.Issue a MFX-SFC forced lock
	2.Wait for MFX-SFC forced lock ack
	3.Check the MFX-SFC usage bit
	If (MFX-SFC usage bit is 1)
		Reset VDBOX and SFC
	else
		Reset VDBOX
	Release the force lock MFX-SFC
}
else if(HCP+SFC usage is 1) {
	1.Issue a VE-SFC forced lock
	2.Wait for SFC forced lock ack
	3.Check the VE-SFC usage bit
	If (VE-SFC usage bit is 1)
		Reset VDBOX
	else
		Reset VDBOX and SFC
	Release the force lock VE-SFC.
}
else
	Reset VDBOX

- Restructure: the changes to the original code flow should stay
  relatively minimal; we only need to do an extra HCP check after the
  usual VD-MFX check and, if true, switch the register/bit we're
  performing the lock on.(MattR)

Bspec: 52890, 53509

Co-developed-by: Matt Roper <matthew.d.roper@intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Matt Roper <matthew.d.roper@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Lucas De Marchi <lucas.demarchi@intel.com>
Signed-off-by: Aditya Swarup <aditya.swarup@intel.com>
Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
---
 drivers/gpu/drm/i915/gt/intel_reset.c | 194 +++++++++++++++++---------
 drivers/gpu/drm/i915/i915_reg.h       |   6 +
 2 files changed, 137 insertions(+), 63 deletions(-)

Comments

Daniele Ceraolo Spurio April 7, 2021, 2:01 a.m. UTC | #1
On 4/1/2021 9:28 AM, Aditya Swarup wrote:
> The WA requires the following procedure for VDBox SFC reset:
>
> If (MFX-SFC usage is 1) {
> 	1.Issue a MFX-SFC forced lock
> 	2.Wait for MFX-SFC forced lock ack
> 	3.Check the MFX-SFC usage bit
> 	If (MFX-SFC usage bit is 1)
> 		Reset VDBOX and SFC
> 	else
> 		Reset VDBOX
> 	Release the force lock MFX-SFC
> }
> else if(HCP+SFC usage is 1) {
> 	1.Issue a VE-SFC forced lock
> 	2.Wait for SFC forced lock ack
> 	3.Check the VE-SFC usage bit
> 	If (VE-SFC usage bit is 1)
> 		Reset VDBOX
> 	else
> 		Reset VDBOX and SFC
> 	Release the force lock VE-SFC.
> }
> else
> 	Reset VDBOX
>
> - Restructure: the changes to the original code flow should stay
>    relatively minimal; we only need to do an extra HCP check after the
>    usual VD-MFX check and, if true, switch the register/bit we're
>    performing the lock on.(MattR)
>
> Bspec: 52890, 53509
>
> Co-developed-by: Matt Roper <matthew.d.roper@intel.com>
> Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> Cc: Matt Roper <matthew.d.roper@intel.com>
> Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
> Cc: Lucas De Marchi <lucas.demarchi@intel.com>
> Signed-off-by: Aditya Swarup <aditya.swarup@intel.com>
> Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
> ---
>   drivers/gpu/drm/i915/gt/intel_reset.c | 194 +++++++++++++++++---------
>   drivers/gpu/drm/i915/i915_reg.h       |   6 +
>   2 files changed, 137 insertions(+), 63 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c
> index a377c4588aaa..bcb3d864db11 100644
> --- a/drivers/gpu/drm/i915/gt/intel_reset.c
> +++ b/drivers/gpu/drm/i915/gt/intel_reset.c
> @@ -338,15 +338,69 @@ static int gen6_reset_engines(struct intel_gt *gt,
>   	return gen6_hw_domain_reset(gt, hw_mask);
>   }
>   
> -static int gen11_lock_sfc(struct intel_engine_cs *engine, u32 *hw_mask)
> +static struct intel_engine_cs *find_sfc_paired_vecs_engine(struct intel_engine_cs *engine)
> +{
> +	int vecs_id;
> +
> +	GEM_BUG_ON(engine->class != VIDEO_DECODE_CLASS);
> +
> +	vecs_id = _VECS((engine->instance) / 2);
> +
> +	return engine->gt->engine[vecs_id];
> +}
> +
> +struct sfc_lock_data {
> +	i915_reg_t lock_reg;
> +	i915_reg_t ack_reg;
> +	i915_reg_t usage_reg;
> +	u32 lock_bit;
> +	u32 ack_bit;
> +	u32 usage_bit;
> +	u32 reset_bit;
> +};
> +
> +static void get_sfc_forced_lock_data(struct intel_engine_cs *engine,
> +				     struct sfc_lock_data *sfc_lock)
> +{
> +	switch (engine->class) {
> +	default:
> +		MISSING_CASE(engine->class);
> +		fallthrough;
> +	case VIDEO_DECODE_CLASS:
> +		sfc_lock->lock_reg = GEN11_VCS_SFC_FORCED_LOCK(engine);
> +		sfc_lock->lock_bit = GEN11_VCS_SFC_FORCED_LOCK_BIT;
> +
> +		sfc_lock->ack_reg = GEN11_VCS_SFC_LOCK_STATUS(engine);
> +		sfc_lock->ack_bit  = GEN11_VCS_SFC_LOCK_ACK_BIT;
> +
> +		sfc_lock->usage_reg = GEN11_VCS_SFC_LOCK_STATUS(engine);
> +		sfc_lock->usage_bit = GEN11_VCS_SFC_USAGE_BIT;
> +		sfc_lock->reset_bit = GEN11_VCS_SFC_RESET_BIT(engine->instance);
> +
> +		break;
> +	case VIDEO_ENHANCEMENT_CLASS:
> +		sfc_lock->lock_reg = GEN11_VECS_SFC_FORCED_LOCK(engine);
> +		sfc_lock->lock_bit = GEN11_VECS_SFC_FORCED_LOCK_BIT;
> +
> +		sfc_lock->ack_reg = GEN11_VECS_SFC_LOCK_ACK(engine);
> +		sfc_lock->ack_bit  = GEN11_VECS_SFC_LOCK_ACK_BIT;
> +
> +		sfc_lock->usage_reg = GEN11_VECS_SFC_USAGE(engine);
> +		sfc_lock->usage_bit = GEN11_VECS_SFC_USAGE_BIT;
> +		sfc_lock->reset_bit = GEN11_VECS_SFC_RESET_BIT(engine->instance);
> +
> +		break;
> +	}
> +}
> +
> +static int gen11_lock_sfc(struct intel_engine_cs *engine,
> +			  u32 *reset_mask,
> +			  u32 *unlock_mask)
>   {
>   	struct intel_uncore *uncore = engine->uncore;
>   	u8 vdbox_sfc_access = engine->gt->info.vdbox_sfc_access;
> -	i915_reg_t sfc_forced_lock, sfc_forced_lock_ack;
> -	u32 sfc_forced_lock_bit, sfc_forced_lock_ack_bit;
> -	i915_reg_t sfc_usage;
> -	u32 sfc_usage_bit;
> -	u32 sfc_reset_bit;
> +	struct sfc_lock_data sfc_lock;
> +	bool lock_obtained, lock_to_other = false;
>   	int ret;
>   
>   	switch (engine->class) {
> @@ -354,53 +408,72 @@ static int gen11_lock_sfc(struct intel_engine_cs *engine, u32 *hw_mask)
>   		if ((BIT(engine->instance) & vdbox_sfc_access) == 0)
>   			return 0;
>   
> -		sfc_forced_lock = GEN11_VCS_SFC_FORCED_LOCK(engine);
> -		sfc_forced_lock_bit = GEN11_VCS_SFC_FORCED_LOCK_BIT;
> -
> -		sfc_forced_lock_ack = GEN11_VCS_SFC_LOCK_STATUS(engine);
> -		sfc_forced_lock_ack_bit  = GEN11_VCS_SFC_LOCK_ACK_BIT;
> +		fallthrough;
> +	case VIDEO_ENHANCEMENT_CLASS:
> +		get_sfc_forced_lock_data(engine, &sfc_lock);
>   
> -		sfc_usage = GEN11_VCS_SFC_LOCK_STATUS(engine);
> -		sfc_usage_bit = GEN11_VCS_SFC_USAGE_BIT;
> -		sfc_reset_bit = GEN11_VCS_SFC_RESET_BIT(engine->instance);
>   		break;
> +	default:
> +		return 0;
> +	}
>   
> -	case VIDEO_ENHANCEMENT_CLASS:
> -		sfc_forced_lock = GEN11_VECS_SFC_FORCED_LOCK(engine);
> -		sfc_forced_lock_bit = GEN11_VECS_SFC_FORCED_LOCK_BIT;
> +	if (!(intel_uncore_read_fw(uncore, sfc_lock.usage_reg) & sfc_lock.usage_bit)) {
> +		struct intel_engine_cs *paired_vecs;
>   
> -		sfc_forced_lock_ack = GEN11_VECS_SFC_LOCK_ACK(engine);
> -		sfc_forced_lock_ack_bit  = GEN11_VECS_SFC_LOCK_ACK_BIT;
> +		if (engine->class != VIDEO_DECODE_CLASS ||
> +		    !IS_GEN(engine->i915, 12))
> +			return 0;
>   
> -		sfc_usage = GEN11_VECS_SFC_USAGE(engine);
> -		sfc_usage_bit = GEN11_VECS_SFC_USAGE_BIT;
> -		sfc_reset_bit = GEN11_VECS_SFC_RESET_BIT(engine->instance);
> -		break;
> +		/*
> +		 * Wa_14010733141
> +		 *
> +		 * If the VCS-MFX isn't using the SFC, we also need to check
> +		 * whether VCS-HCP is using it.  If so, we need to issue a *VE*
> +		 * forced lock on the VE engine that shares the same SFC.
> +		 */
> +		if (!(intel_uncore_read_fw(uncore,
> +					   GEN12_HCP_SFC_LOCK_STATUS(engine)) &
> +		      GEN12_HCP_SFC_USAGE_BIT))
> +			return 0;
>   
> -	default:
> -		return 0;
> +		paired_vecs = find_sfc_paired_vecs_engine(engine);
> +		get_sfc_forced_lock_data(paired_vecs, &sfc_lock);
> +		lock_to_other = true;
> +		*unlock_mask |= BIT(paired_vecs->id);

nit: could use paired_vecs->engine_mask directly here instead of BIT(...)

Reviewed-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>

Daniele

> +	} else {
> +		*unlock_mask |= engine->mask;
>   	}
>   
>   	/*
> -	 * If the engine is using a SFC, tell the engine that a software reset
> +	 * If the engine is using an SFC, tell the engine that a software reset
>   	 * is going to happen. The engine will then try to force lock the SFC.
>   	 * If SFC ends up being locked to the engine we want to reset, we have
>   	 * to reset it as well (we will unlock it once the reset sequence is
>   	 * completed).
>   	 */
> -	if (!(intel_uncore_read_fw(uncore, sfc_usage) & sfc_usage_bit))
> -		return 0;
> -
> -	rmw_set_fw(uncore, sfc_forced_lock, sfc_forced_lock_bit);
> +	rmw_set_fw(uncore, sfc_lock.lock_reg, sfc_lock.lock_bit);
>   
>   	ret = __intel_wait_for_register_fw(uncore,
> -					   sfc_forced_lock_ack,
> -					   sfc_forced_lock_ack_bit,
> -					   sfc_forced_lock_ack_bit,
> +					   sfc_lock.ack_reg,
> +					   sfc_lock.ack_bit,
> +					   sfc_lock.ack_bit,
>   					   1000, 0, NULL);
>   
> -	/* Was the SFC released while we were trying to lock it? */
> -	if (!(intel_uncore_read_fw(uncore, sfc_usage) & sfc_usage_bit))
> +	/*
> +	 * Was the SFC released while we were trying to lock it?
> +	 *
> +	 * We should reset both the engine and the SFC if:
> +	 *  - We were locking the SFC to this engine and the lock succeeded
> +	 *       OR
> +	 *  - We were locking the SFC to a different engine (Wa_14010733141)
> +	 *    but the SFC was released before the lock was obtained.
> +	 *
> +	 * Otherwise we need only reset the engine by itself and we can
> +	 * leave the SFC alone.
> +	 */
> +	lock_obtained = (intel_uncore_read_fw(uncore, sfc_lock.usage_reg) &
> +			sfc_lock.usage_bit) != 0;
> +	if (lock_obtained == lock_to_other)
>   		return 0;
>   
>   	if (ret) {
> @@ -408,7 +481,7 @@ static int gen11_lock_sfc(struct intel_engine_cs *engine, u32 *hw_mask)
>   		return ret;
>   	}
>   
> -	*hw_mask |= sfc_reset_bit;
> +	*reset_mask |= sfc_lock.reset_bit;
>   	return 0;
>   }
>   
> @@ -416,28 +489,19 @@ static void gen11_unlock_sfc(struct intel_engine_cs *engine)
>   {
>   	struct intel_uncore *uncore = engine->uncore;
>   	u8 vdbox_sfc_access = engine->gt->info.vdbox_sfc_access;
> -	i915_reg_t sfc_forced_lock;
> -	u32 sfc_forced_lock_bit;
> -
> -	switch (engine->class) {
> -	case VIDEO_DECODE_CLASS:
> -		if ((BIT(engine->instance) & vdbox_sfc_access) == 0)
> -			return;
> -
> -		sfc_forced_lock = GEN11_VCS_SFC_FORCED_LOCK(engine);
> -		sfc_forced_lock_bit = GEN11_VCS_SFC_FORCED_LOCK_BIT;
> -		break;
> +	struct sfc_lock_data sfc_lock = {};
>   
> -	case VIDEO_ENHANCEMENT_CLASS:
> -		sfc_forced_lock = GEN11_VECS_SFC_FORCED_LOCK(engine);
> -		sfc_forced_lock_bit = GEN11_VECS_SFC_FORCED_LOCK_BIT;
> -		break;
> +	if (engine->class != VIDEO_DECODE_CLASS &&
> +	    engine->class != VIDEO_ENHANCEMENT_CLASS)
> +		return;
>   
> -	default:
> +	if (engine->class == VIDEO_DECODE_CLASS &&
> +	    (BIT(engine->instance) & vdbox_sfc_access) == 0)
>   		return;
> -	}
>   
> -	rmw_clear_fw(uncore, sfc_forced_lock, sfc_forced_lock_bit);
> +	get_sfc_forced_lock_data(engine, &sfc_lock);
> +
> +	rmw_clear_fw(uncore, sfc_lock.lock_reg, sfc_lock.lock_bit);
>   }
>   
>   static int gen11_reset_engines(struct intel_gt *gt,
> @@ -456,23 +520,23 @@ static int gen11_reset_engines(struct intel_gt *gt,
>   	};
>   	struct intel_engine_cs *engine;
>   	intel_engine_mask_t tmp;
> -	u32 hw_mask;
> +	u32 reset_mask, unlock_mask = 0;
>   	int ret;
>   
>   	if (engine_mask == ALL_ENGINES) {
> -		hw_mask = GEN11_GRDOM_FULL;
> +		reset_mask = GEN11_GRDOM_FULL;
>   	} else {
> -		hw_mask = 0;
> +		reset_mask = 0;
>   		for_each_engine_masked(engine, gt, engine_mask, tmp) {
>   			GEM_BUG_ON(engine->id >= ARRAY_SIZE(hw_engine_mask));
> -			hw_mask |= hw_engine_mask[engine->id];
> -			ret = gen11_lock_sfc(engine, &hw_mask);
> +			reset_mask |= hw_engine_mask[engine->id];
> +			ret = gen11_lock_sfc(engine, &reset_mask, &unlock_mask);
>   			if (ret)
>   				goto sfc_unlock;
>   		}
>   	}
>   
> -	ret = gen6_hw_domain_reset(gt, hw_mask);
> +	ret = gen6_hw_domain_reset(gt, reset_mask);
>   
>   sfc_unlock:
>   	/*
> @@ -480,10 +544,14 @@ static int gen11_reset_engines(struct intel_gt *gt,
>   	 * gen11_lock_sfc to make sure that we clean properly if something
>   	 * wrong happened during the lock (e.g. lock acquired after timeout
>   	 * expiration).
> +	 *
> +	 * Due to Wa_14010733141, we may have locked an SFC to an engine that
> +	 * wasn't being reset.  So instead of calling gen11_unlock_sfc()
> +	 * on engine_mask, we instead call it on the mask of engines that our
> +	 * gen11_lock_sfc() calls told us actually had locks attempted.
>   	 */
> -	if (engine_mask != ALL_ENGINES)
> -		for_each_engine_masked(engine, gt, engine_mask, tmp)
> -			gen11_unlock_sfc(engine);
> +	for_each_engine_masked(engine, gt, unlock_mask, tmp)
> +		gen11_unlock_sfc(engine);
>   
>   	return ret;
>   }
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index cbf7a60afe54..f265733979ed 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -416,6 +416,12 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
>   #define GEN11_VECS_SFC_USAGE(engine)		_MMIO((engine)->mmio_base + 0x2014)
>   #define   GEN11_VECS_SFC_USAGE_BIT		(1 << 0)
>   
> +#define GEN12_HCP_SFC_FORCED_LOCK(engine)	_MMIO((engine)->mmio_base + 0x2910)
> +#define   GEN12_HCP_SFC_FORCED_LOCK_BIT		REG_BIT(0)
> +#define GEN12_HCP_SFC_LOCK_STATUS(engine)	_MMIO((engine)->mmio_base + 0x2914)
> +#define   GEN12_HCP_SFC_LOCK_ACK_BIT		REG_BIT(1)
> +#define   GEN12_HCP_SFC_USAGE_BIT			REG_BIT(0)
> +
>   #define GEN12_SFC_DONE(n)		_MMIO(0x1cc00 + (n) * 0x100)
>   #define GEN12_SFC_DONE_MAX		4
>
diff mbox series

Patch

diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c
index a377c4588aaa..bcb3d864db11 100644
--- a/drivers/gpu/drm/i915/gt/intel_reset.c
+++ b/drivers/gpu/drm/i915/gt/intel_reset.c
@@ -338,15 +338,69 @@  static int gen6_reset_engines(struct intel_gt *gt,
 	return gen6_hw_domain_reset(gt, hw_mask);
 }
 
-static int gen11_lock_sfc(struct intel_engine_cs *engine, u32 *hw_mask)
+static struct intel_engine_cs *find_sfc_paired_vecs_engine(struct intel_engine_cs *engine)
+{
+	int vecs_id;
+
+	GEM_BUG_ON(engine->class != VIDEO_DECODE_CLASS);
+
+	vecs_id = _VECS((engine->instance) / 2);
+
+	return engine->gt->engine[vecs_id];
+}
+
+struct sfc_lock_data {
+	i915_reg_t lock_reg;
+	i915_reg_t ack_reg;
+	i915_reg_t usage_reg;
+	u32 lock_bit;
+	u32 ack_bit;
+	u32 usage_bit;
+	u32 reset_bit;
+};
+
+static void get_sfc_forced_lock_data(struct intel_engine_cs *engine,
+				     struct sfc_lock_data *sfc_lock)
+{
+	switch (engine->class) {
+	default:
+		MISSING_CASE(engine->class);
+		fallthrough;
+	case VIDEO_DECODE_CLASS:
+		sfc_lock->lock_reg = GEN11_VCS_SFC_FORCED_LOCK(engine);
+		sfc_lock->lock_bit = GEN11_VCS_SFC_FORCED_LOCK_BIT;
+
+		sfc_lock->ack_reg = GEN11_VCS_SFC_LOCK_STATUS(engine);
+		sfc_lock->ack_bit  = GEN11_VCS_SFC_LOCK_ACK_BIT;
+
+		sfc_lock->usage_reg = GEN11_VCS_SFC_LOCK_STATUS(engine);
+		sfc_lock->usage_bit = GEN11_VCS_SFC_USAGE_BIT;
+		sfc_lock->reset_bit = GEN11_VCS_SFC_RESET_BIT(engine->instance);
+
+		break;
+	case VIDEO_ENHANCEMENT_CLASS:
+		sfc_lock->lock_reg = GEN11_VECS_SFC_FORCED_LOCK(engine);
+		sfc_lock->lock_bit = GEN11_VECS_SFC_FORCED_LOCK_BIT;
+
+		sfc_lock->ack_reg = GEN11_VECS_SFC_LOCK_ACK(engine);
+		sfc_lock->ack_bit  = GEN11_VECS_SFC_LOCK_ACK_BIT;
+
+		sfc_lock->usage_reg = GEN11_VECS_SFC_USAGE(engine);
+		sfc_lock->usage_bit = GEN11_VECS_SFC_USAGE_BIT;
+		sfc_lock->reset_bit = GEN11_VECS_SFC_RESET_BIT(engine->instance);
+
+		break;
+	}
+}
+
+static int gen11_lock_sfc(struct intel_engine_cs *engine,
+			  u32 *reset_mask,
+			  u32 *unlock_mask)
 {
 	struct intel_uncore *uncore = engine->uncore;
 	u8 vdbox_sfc_access = engine->gt->info.vdbox_sfc_access;
-	i915_reg_t sfc_forced_lock, sfc_forced_lock_ack;
-	u32 sfc_forced_lock_bit, sfc_forced_lock_ack_bit;
-	i915_reg_t sfc_usage;
-	u32 sfc_usage_bit;
-	u32 sfc_reset_bit;
+	struct sfc_lock_data sfc_lock;
+	bool lock_obtained, lock_to_other = false;
 	int ret;
 
 	switch (engine->class) {
@@ -354,53 +408,72 @@  static int gen11_lock_sfc(struct intel_engine_cs *engine, u32 *hw_mask)
 		if ((BIT(engine->instance) & vdbox_sfc_access) == 0)
 			return 0;
 
-		sfc_forced_lock = GEN11_VCS_SFC_FORCED_LOCK(engine);
-		sfc_forced_lock_bit = GEN11_VCS_SFC_FORCED_LOCK_BIT;
-
-		sfc_forced_lock_ack = GEN11_VCS_SFC_LOCK_STATUS(engine);
-		sfc_forced_lock_ack_bit  = GEN11_VCS_SFC_LOCK_ACK_BIT;
+		fallthrough;
+	case VIDEO_ENHANCEMENT_CLASS:
+		get_sfc_forced_lock_data(engine, &sfc_lock);
 
-		sfc_usage = GEN11_VCS_SFC_LOCK_STATUS(engine);
-		sfc_usage_bit = GEN11_VCS_SFC_USAGE_BIT;
-		sfc_reset_bit = GEN11_VCS_SFC_RESET_BIT(engine->instance);
 		break;
+	default:
+		return 0;
+	}
 
-	case VIDEO_ENHANCEMENT_CLASS:
-		sfc_forced_lock = GEN11_VECS_SFC_FORCED_LOCK(engine);
-		sfc_forced_lock_bit = GEN11_VECS_SFC_FORCED_LOCK_BIT;
+	if (!(intel_uncore_read_fw(uncore, sfc_lock.usage_reg) & sfc_lock.usage_bit)) {
+		struct intel_engine_cs *paired_vecs;
 
-		sfc_forced_lock_ack = GEN11_VECS_SFC_LOCK_ACK(engine);
-		sfc_forced_lock_ack_bit  = GEN11_VECS_SFC_LOCK_ACK_BIT;
+		if (engine->class != VIDEO_DECODE_CLASS ||
+		    !IS_GEN(engine->i915, 12))
+			return 0;
 
-		sfc_usage = GEN11_VECS_SFC_USAGE(engine);
-		sfc_usage_bit = GEN11_VECS_SFC_USAGE_BIT;
-		sfc_reset_bit = GEN11_VECS_SFC_RESET_BIT(engine->instance);
-		break;
+		/*
+		 * Wa_14010733141
+		 *
+		 * If the VCS-MFX isn't using the SFC, we also need to check
+		 * whether VCS-HCP is using it.  If so, we need to issue a *VE*
+		 * forced lock on the VE engine that shares the same SFC.
+		 */
+		if (!(intel_uncore_read_fw(uncore,
+					   GEN12_HCP_SFC_LOCK_STATUS(engine)) &
+		      GEN12_HCP_SFC_USAGE_BIT))
+			return 0;
 
-	default:
-		return 0;
+		paired_vecs = find_sfc_paired_vecs_engine(engine);
+		get_sfc_forced_lock_data(paired_vecs, &sfc_lock);
+		lock_to_other = true;
+		*unlock_mask |= BIT(paired_vecs->id);
+	} else {
+		*unlock_mask |= engine->mask;
 	}
 
 	/*
-	 * If the engine is using a SFC, tell the engine that a software reset
+	 * If the engine is using an SFC, tell the engine that a software reset
 	 * is going to happen. The engine will then try to force lock the SFC.
 	 * If SFC ends up being locked to the engine we want to reset, we have
 	 * to reset it as well (we will unlock it once the reset sequence is
 	 * completed).
 	 */
-	if (!(intel_uncore_read_fw(uncore, sfc_usage) & sfc_usage_bit))
-		return 0;
-
-	rmw_set_fw(uncore, sfc_forced_lock, sfc_forced_lock_bit);
+	rmw_set_fw(uncore, sfc_lock.lock_reg, sfc_lock.lock_bit);
 
 	ret = __intel_wait_for_register_fw(uncore,
-					   sfc_forced_lock_ack,
-					   sfc_forced_lock_ack_bit,
-					   sfc_forced_lock_ack_bit,
+					   sfc_lock.ack_reg,
+					   sfc_lock.ack_bit,
+					   sfc_lock.ack_bit,
 					   1000, 0, NULL);
 
-	/* Was the SFC released while we were trying to lock it? */
-	if (!(intel_uncore_read_fw(uncore, sfc_usage) & sfc_usage_bit))
+	/*
+	 * Was the SFC released while we were trying to lock it?
+	 *
+	 * We should reset both the engine and the SFC if:
+	 *  - We were locking the SFC to this engine and the lock succeeded
+	 *       OR
+	 *  - We were locking the SFC to a different engine (Wa_14010733141)
+	 *    but the SFC was released before the lock was obtained.
+	 *
+	 * Otherwise we need only reset the engine by itself and we can
+	 * leave the SFC alone.
+	 */
+	lock_obtained = (intel_uncore_read_fw(uncore, sfc_lock.usage_reg) &
+			sfc_lock.usage_bit) != 0;
+	if (lock_obtained == lock_to_other)
 		return 0;
 
 	if (ret) {
@@ -408,7 +481,7 @@  static int gen11_lock_sfc(struct intel_engine_cs *engine, u32 *hw_mask)
 		return ret;
 	}
 
-	*hw_mask |= sfc_reset_bit;
+	*reset_mask |= sfc_lock.reset_bit;
 	return 0;
 }
 
@@ -416,28 +489,19 @@  static void gen11_unlock_sfc(struct intel_engine_cs *engine)
 {
 	struct intel_uncore *uncore = engine->uncore;
 	u8 vdbox_sfc_access = engine->gt->info.vdbox_sfc_access;
-	i915_reg_t sfc_forced_lock;
-	u32 sfc_forced_lock_bit;
-
-	switch (engine->class) {
-	case VIDEO_DECODE_CLASS:
-		if ((BIT(engine->instance) & vdbox_sfc_access) == 0)
-			return;
-
-		sfc_forced_lock = GEN11_VCS_SFC_FORCED_LOCK(engine);
-		sfc_forced_lock_bit = GEN11_VCS_SFC_FORCED_LOCK_BIT;
-		break;
+	struct sfc_lock_data sfc_lock = {};
 
-	case VIDEO_ENHANCEMENT_CLASS:
-		sfc_forced_lock = GEN11_VECS_SFC_FORCED_LOCK(engine);
-		sfc_forced_lock_bit = GEN11_VECS_SFC_FORCED_LOCK_BIT;
-		break;
+	if (engine->class != VIDEO_DECODE_CLASS &&
+	    engine->class != VIDEO_ENHANCEMENT_CLASS)
+		return;
 
-	default:
+	if (engine->class == VIDEO_DECODE_CLASS &&
+	    (BIT(engine->instance) & vdbox_sfc_access) == 0)
 		return;
-	}
 
-	rmw_clear_fw(uncore, sfc_forced_lock, sfc_forced_lock_bit);
+	get_sfc_forced_lock_data(engine, &sfc_lock);
+
+	rmw_clear_fw(uncore, sfc_lock.lock_reg, sfc_lock.lock_bit);
 }
 
 static int gen11_reset_engines(struct intel_gt *gt,
@@ -456,23 +520,23 @@  static int gen11_reset_engines(struct intel_gt *gt,
 	};
 	struct intel_engine_cs *engine;
 	intel_engine_mask_t tmp;
-	u32 hw_mask;
+	u32 reset_mask, unlock_mask = 0;
 	int ret;
 
 	if (engine_mask == ALL_ENGINES) {
-		hw_mask = GEN11_GRDOM_FULL;
+		reset_mask = GEN11_GRDOM_FULL;
 	} else {
-		hw_mask = 0;
+		reset_mask = 0;
 		for_each_engine_masked(engine, gt, engine_mask, tmp) {
 			GEM_BUG_ON(engine->id >= ARRAY_SIZE(hw_engine_mask));
-			hw_mask |= hw_engine_mask[engine->id];
-			ret = gen11_lock_sfc(engine, &hw_mask);
+			reset_mask |= hw_engine_mask[engine->id];
+			ret = gen11_lock_sfc(engine, &reset_mask, &unlock_mask);
 			if (ret)
 				goto sfc_unlock;
 		}
 	}
 
-	ret = gen6_hw_domain_reset(gt, hw_mask);
+	ret = gen6_hw_domain_reset(gt, reset_mask);
 
 sfc_unlock:
 	/*
@@ -480,10 +544,14 @@  static int gen11_reset_engines(struct intel_gt *gt,
 	 * gen11_lock_sfc to make sure that we clean properly if something
 	 * wrong happened during the lock (e.g. lock acquired after timeout
 	 * expiration).
+	 *
+	 * Due to Wa_14010733141, we may have locked an SFC to an engine that
+	 * wasn't being reset.  So instead of calling gen11_unlock_sfc()
+	 * on engine_mask, we instead call it on the mask of engines that our
+	 * gen11_lock_sfc() calls told us actually had locks attempted.
 	 */
-	if (engine_mask != ALL_ENGINES)
-		for_each_engine_masked(engine, gt, engine_mask, tmp)
-			gen11_unlock_sfc(engine);
+	for_each_engine_masked(engine, gt, unlock_mask, tmp)
+		gen11_unlock_sfc(engine);
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index cbf7a60afe54..f265733979ed 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -416,6 +416,12 @@  static inline bool i915_mmio_reg_valid(i915_reg_t reg)
 #define GEN11_VECS_SFC_USAGE(engine)		_MMIO((engine)->mmio_base + 0x2014)
 #define   GEN11_VECS_SFC_USAGE_BIT		(1 << 0)
 
+#define GEN12_HCP_SFC_FORCED_LOCK(engine)	_MMIO((engine)->mmio_base + 0x2910)
+#define   GEN12_HCP_SFC_FORCED_LOCK_BIT		REG_BIT(0)
+#define GEN12_HCP_SFC_LOCK_STATUS(engine)	_MMIO((engine)->mmio_base + 0x2914)
+#define   GEN12_HCP_SFC_LOCK_ACK_BIT		REG_BIT(1)
+#define   GEN12_HCP_SFC_USAGE_BIT			REG_BIT(0)
+
 #define GEN12_SFC_DONE(n)		_MMIO(0x1cc00 + (n) * 0x100)
 #define GEN12_SFC_DONE_MAX		4