diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_pm.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_pm.c | 143 | 
1 files changed, 120 insertions, 23 deletions
| diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 434b1f8b7fe3..3edba7fd0c49 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4717,6 +4717,10 @@ static const struct dbuf_slice_conf_entry dg2_allowed_dbufs[] = {  };  static const struct dbuf_slice_conf_entry adlp_allowed_dbufs[] = { +	/* +	 * Keep the join_mbus cases first so check_mbus_joined() +	 * will prefer them over the !join_mbus cases. +	 */  	{  		.active_pipes = BIT(PIPE_A),  		.dbuf_mask = { @@ -4732,6 +4736,20 @@ static const struct dbuf_slice_conf_entry adlp_allowed_dbufs[] = {  		.join_mbus = true,  	},  	{ +		.active_pipes = BIT(PIPE_A), +		.dbuf_mask = { +			[PIPE_A] = BIT(DBUF_S1) | BIT(DBUF_S2), +		}, +		.join_mbus = false, +	}, +	{ +		.active_pipes = BIT(PIPE_B), +		.dbuf_mask = { +			[PIPE_B] = BIT(DBUF_S3) | BIT(DBUF_S4), +		}, +		.join_mbus = false, +	}, +	{  		.active_pipes = BIT(PIPE_A) | BIT(PIPE_B),  		.dbuf_mask = {  			[PIPE_A] = BIT(DBUF_S1) | BIT(DBUF_S2), @@ -4847,13 +4865,14 @@ static bool adlp_check_mbus_joined(u8 active_pipes)  	return check_mbus_joined(active_pipes, adlp_allowed_dbufs);  } -static u8 compute_dbuf_slices(enum pipe pipe, u8 active_pipes, +static u8 compute_dbuf_slices(enum pipe pipe, u8 active_pipes, bool join_mbus,  			      const struct dbuf_slice_conf_entry *dbuf_slices)  {  	int i;  	for (i = 0; i < dbuf_slices[i].active_pipes; i++) { -		if (dbuf_slices[i].active_pipes == active_pipes) +		if (dbuf_slices[i].active_pipes == active_pipes && +		    dbuf_slices[i].join_mbus == join_mbus)  			return dbuf_slices[i].dbuf_mask[pipe];  	}  	return 0; @@ -4864,7 +4883,7 @@ static u8 compute_dbuf_slices(enum pipe pipe, u8 active_pipes,   * returns correspondent DBuf slice mask as stated in BSpec for particular   * platform.   */ -static u8 icl_compute_dbuf_slices(enum pipe pipe, u8 active_pipes) +static u8 icl_compute_dbuf_slices(enum pipe pipe, u8 active_pipes, bool join_mbus)  {  	/*  	 * FIXME: For ICL this is still a bit unclear as prev BSpec revision @@ -4878,37 +4897,41 @@ static u8 icl_compute_dbuf_slices(enum pipe pipe, u8 active_pipes)  	 * still here - we will need it once those additional constraints  	 * pop up.  	 */ -	return compute_dbuf_slices(pipe, active_pipes, icl_allowed_dbufs); +	return compute_dbuf_slices(pipe, active_pipes, join_mbus, +				   icl_allowed_dbufs);  } -static u8 tgl_compute_dbuf_slices(enum pipe pipe, u8 active_pipes) +static u8 tgl_compute_dbuf_slices(enum pipe pipe, u8 active_pipes, bool join_mbus)  { -	return compute_dbuf_slices(pipe, active_pipes, tgl_allowed_dbufs); +	return compute_dbuf_slices(pipe, active_pipes, join_mbus, +				   tgl_allowed_dbufs);  } -static u32 adlp_compute_dbuf_slices(enum pipe pipe, u32 active_pipes) +static u8 adlp_compute_dbuf_slices(enum pipe pipe, u8 active_pipes, bool join_mbus)  { -	return compute_dbuf_slices(pipe, active_pipes, adlp_allowed_dbufs); +	return compute_dbuf_slices(pipe, active_pipes, join_mbus, +				   adlp_allowed_dbufs);  } -static u32 dg2_compute_dbuf_slices(enum pipe pipe, u32 active_pipes) +static u8 dg2_compute_dbuf_slices(enum pipe pipe, u8 active_pipes, bool join_mbus)  { -	return compute_dbuf_slices(pipe, active_pipes, dg2_allowed_dbufs); +	return compute_dbuf_slices(pipe, active_pipes, join_mbus, +				   dg2_allowed_dbufs);  } -static u8 skl_compute_dbuf_slices(struct intel_crtc *crtc, u8 active_pipes) +static u8 skl_compute_dbuf_slices(struct intel_crtc *crtc, u8 active_pipes, bool join_mbus)  {  	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);  	enum pipe pipe = crtc->pipe;  	if (IS_DG2(dev_priv)) -		return dg2_compute_dbuf_slices(pipe, active_pipes); +		return dg2_compute_dbuf_slices(pipe, active_pipes, join_mbus);  	else if (IS_ALDERLAKE_P(dev_priv)) -		return adlp_compute_dbuf_slices(pipe, active_pipes); +		return adlp_compute_dbuf_slices(pipe, active_pipes, join_mbus);  	else if (DISPLAY_VER(dev_priv) == 12) -		return tgl_compute_dbuf_slices(pipe, active_pipes); +		return tgl_compute_dbuf_slices(pipe, active_pipes, join_mbus);  	else if (DISPLAY_VER(dev_priv) == 11) -		return icl_compute_dbuf_slices(pipe, active_pipes); +		return icl_compute_dbuf_slices(pipe, active_pipes, join_mbus);  	/*  	 * For anything else just return one slice yet.  	 * Should be extended for other platforms. @@ -6127,11 +6150,16 @@ skl_compute_ddb(struct intel_atomic_state *state)  			return ret;  	} +	if (IS_ALDERLAKE_P(dev_priv)) +		new_dbuf_state->joined_mbus = +			adlp_check_mbus_joined(new_dbuf_state->active_pipes); +  	for_each_intel_crtc(&dev_priv->drm, crtc) {  		enum pipe pipe = crtc->pipe;  		new_dbuf_state->slices[pipe] = -			skl_compute_dbuf_slices(crtc, new_dbuf_state->active_pipes); +			skl_compute_dbuf_slices(crtc, new_dbuf_state->active_pipes, +						new_dbuf_state->joined_mbus);  		if (old_dbuf_state->slices[pipe] == new_dbuf_state->slices[pipe])  			continue; @@ -6143,9 +6171,6 @@ skl_compute_ddb(struct intel_atomic_state *state)  	new_dbuf_state->enabled_slices = intel_dbuf_enabled_slices(new_dbuf_state); -	if (IS_ALDERLAKE_P(dev_priv)) -		new_dbuf_state->joined_mbus = adlp_check_mbus_joined(new_dbuf_state->active_pipes); -  	if (old_dbuf_state->enabled_slices != new_dbuf_state->enabled_slices ||  	    old_dbuf_state->joined_mbus != new_dbuf_state->joined_mbus) {  		ret = intel_atomic_serialize_global_state(&new_dbuf_state->base); @@ -6626,6 +6651,7 @@ void skl_wm_get_hw_state(struct drm_i915_private *dev_priv)  		enum pipe pipe = crtc->pipe;  		unsigned int mbus_offset;  		enum plane_id plane_id; +		u8 slices;  		skl_pipe_wm_get_hw_state(crtc, &crtc_state->wm.skl.optimal);  		crtc_state->wm.skl.raw = crtc_state->wm.skl.optimal; @@ -6645,19 +6671,22 @@ void skl_wm_get_hw_state(struct drm_i915_private *dev_priv)  			skl_ddb_entry_union(&dbuf_state->ddb[pipe], ddb_uv);  		} -		dbuf_state->slices[pipe] = -			skl_compute_dbuf_slices(crtc, dbuf_state->active_pipes); -  		dbuf_state->weight[pipe] = intel_crtc_ddb_weight(crtc_state);  		/*  		 * Used for checking overlaps, so we need absolute  		 * offsets instead of MBUS relative offsets.  		 */ -		mbus_offset = mbus_ddb_offset(dev_priv, dbuf_state->slices[pipe]); +		slices = skl_compute_dbuf_slices(crtc, dbuf_state->active_pipes, +						 dbuf_state->joined_mbus); +		mbus_offset = mbus_ddb_offset(dev_priv, slices);  		crtc_state->wm.skl.ddb.start = mbus_offset + dbuf_state->ddb[pipe].start;  		crtc_state->wm.skl.ddb.end = mbus_offset + dbuf_state->ddb[pipe].end; +		/* The slices actually used by the planes on the pipe */ +		dbuf_state->slices[pipe] = +			skl_ddb_dbuf_slice_mask(dev_priv, &crtc_state->wm.skl.ddb); +  		drm_dbg_kms(&dev_priv->drm,  			    "[CRTC:%d:%s] dbuf slices 0x%x, ddb (%d - %d), active pipes 0x%x, mbus joined: %s\n",  			    crtc->base.base.id, crtc->base.name, @@ -6669,6 +6698,74 @@ void skl_wm_get_hw_state(struct drm_i915_private *dev_priv)  	dbuf_state->enabled_slices = dev_priv->dbuf.enabled_slices;  } +static bool skl_dbuf_is_misconfigured(struct drm_i915_private *i915) +{ +	const struct intel_dbuf_state *dbuf_state = +		to_intel_dbuf_state(i915->dbuf.obj.state); +	struct skl_ddb_entry entries[I915_MAX_PIPES] = {}; +	struct intel_crtc *crtc; + +	for_each_intel_crtc(&i915->drm, crtc) { +		const struct intel_crtc_state *crtc_state = +			to_intel_crtc_state(crtc->base.state); + +		entries[crtc->pipe] = crtc_state->wm.skl.ddb; +	} + +	for_each_intel_crtc(&i915->drm, crtc) { +		const struct intel_crtc_state *crtc_state = +			to_intel_crtc_state(crtc->base.state); +		u8 slices; + +		slices = skl_compute_dbuf_slices(crtc, dbuf_state->active_pipes, +						 dbuf_state->joined_mbus); +		if (dbuf_state->slices[crtc->pipe] & ~slices) +			return true; + +		if (skl_ddb_allocation_overlaps(&crtc_state->wm.skl.ddb, entries, +						I915_MAX_PIPES, crtc->pipe)) +			return true; +	} + +	return false; +} + +void skl_wm_sanitize(struct drm_i915_private *i915) +{ +	struct intel_crtc *crtc; + +	/* +	 * On TGL/RKL (at least) the BIOS likes to assign the planes +	 * to the wrong DBUF slices. This will cause an infinite loop +	 * in skl_commit_modeset_enables() as it can't find a way to +	 * transition between the old bogus DBUF layout to the new +	 * proper DBUF layout without DBUF allocation overlaps between +	 * the planes (which cannot be allowed or else the hardware +	 * may hang). If we detect a bogus DBUF layout just turn off +	 * all the planes so that skl_commit_modeset_enables() can +	 * simply ignore them. +	 */ +	if (!skl_dbuf_is_misconfigured(i915)) +		return; + +	drm_dbg_kms(&i915->drm, "BIOS has misprogrammed the DBUF, disabling all planes\n"); + +	for_each_intel_crtc(&i915->drm, crtc) { +		struct intel_plane *plane = to_intel_plane(crtc->base.primary); +		const struct intel_plane_state *plane_state = +			to_intel_plane_state(plane->base.state); +		struct intel_crtc_state *crtc_state = +			to_intel_crtc_state(crtc->base.state); + +		if (plane_state->uapi.visible) +			intel_plane_disable_noatomic(crtc, plane); + +		drm_WARN_ON(&i915->drm, crtc_state->active_planes != 0); + +		memset(&crtc_state->wm.skl.ddb, 0, sizeof(crtc_state->wm.skl.ddb)); +	} +} +  static void ilk_pipe_wm_get_hw_state(struct intel_crtc *crtc)  {  	struct drm_device *dev = crtc->base.dev; |