diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_cdclk.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_cdclk.c | 205 | 
1 files changed, 188 insertions, 17 deletions
| diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 868dd43a7542..c30cf8d2b835 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -24,8 +24,11 @@  #include <linux/time.h>  #include "intel_atomic.h" +#include "intel_atomic_plane.h" +#include "intel_audio.h"  #include "intel_bw.h"  #include "intel_cdclk.h" +#include "intel_crtc.h"  #include "intel_de.h"  #include "intel_display_types.h"  #include "intel_pcode.h" @@ -66,7 +69,7 @@ void intel_cdclk_get_cdclk(struct drm_i915_private *dev_priv,  	dev_priv->cdclk_funcs->get_cdclk(dev_priv, cdclk_config);  } -int intel_cdclk_bw_calc_min_cdclk(struct intel_atomic_state *state) +static int intel_cdclk_bw_calc_min_cdclk(struct intel_atomic_state *state)  {  	struct drm_i915_private *dev_priv = to_i915(state->base.dev);  	return dev_priv->cdclk_funcs->bw_calc_min_cdclk(state); @@ -1211,6 +1214,19 @@ static void skl_cdclk_uninit_hw(struct drm_i915_private *dev_priv)  	skl_set_cdclk(dev_priv, &cdclk_config, INVALID_PIPE);  } +static bool has_cdclk_squasher(struct drm_i915_private *i915) +{ +	return IS_DG2(i915); +} + +struct intel_cdclk_vals { +	u32 cdclk; +	u16 refclk; +	u16 waveform; +	u8 divider;	/* CD2X divider * 2 */ +	u8 ratio; +}; +  static const struct intel_cdclk_vals bxt_cdclk_table[] = {  	{ .refclk = 19200, .cdclk = 144000, .divider = 8, .ratio = 60 },  	{ .refclk = 19200, .cdclk = 288000, .divider = 4, .ratio = 60 }, @@ -1312,12 +1328,19 @@ static const struct intel_cdclk_vals adlp_cdclk_table[] = {  };  static const struct intel_cdclk_vals dg2_cdclk_table[] = { -	{ .refclk = 38400, .cdclk = 172800, .divider = 2, .ratio =  9 }, -	{ .refclk = 38400, .cdclk = 192000, .divider = 2, .ratio = 10 }, -	{ .refclk = 38400, .cdclk = 307200, .divider = 2, .ratio = 16 }, -	{ .refclk = 38400, .cdclk = 326400, .divider = 4, .ratio = 34 }, -	{ .refclk = 38400, .cdclk = 556800, .divider = 2, .ratio = 29 }, -	{ .refclk = 38400, .cdclk = 652800, .divider = 2, .ratio = 34 }, +	{ .refclk = 38400, .cdclk = 163200, .divider = 2, .ratio = 34, .waveform = 0x8888 }, +	{ .refclk = 38400, .cdclk = 204000, .divider = 2, .ratio = 34, .waveform = 0x9248 }, +	{ .refclk = 38400, .cdclk = 244800, .divider = 2, .ratio = 34, .waveform = 0xa4a4 }, +	{ .refclk = 38400, .cdclk = 285600, .divider = 2, .ratio = 34, .waveform = 0xa54a }, +	{ .refclk = 38400, .cdclk = 326400, .divider = 2, .ratio = 34, .waveform = 0xaaaa }, +	{ .refclk = 38400, .cdclk = 367200, .divider = 2, .ratio = 34, .waveform = 0xad5a }, +	{ .refclk = 38400, .cdclk = 408000, .divider = 2, .ratio = 34, .waveform = 0xb6b6 }, +	{ .refclk = 38400, .cdclk = 448800, .divider = 2, .ratio = 34, .waveform = 0xdbb6 }, +	{ .refclk = 38400, .cdclk = 489600, .divider = 2, .ratio = 34, .waveform = 0xeeee }, +	{ .refclk = 38400, .cdclk = 530400, .divider = 2, .ratio = 34, .waveform = 0xf7de }, +	{ .refclk = 38400, .cdclk = 571200, .divider = 2, .ratio = 34, .waveform = 0xfefe }, +	{ .refclk = 38400, .cdclk = 612000, .divider = 2, .ratio = 34, .waveform = 0xfffe }, +	{ .refclk = 38400, .cdclk = 652800, .divider = 2, .ratio = 34, .waveform = 0xffff },  	{}  }; @@ -1453,6 +1476,7 @@ static void bxt_de_pll_readout(struct drm_i915_private *dev_priv,  static void bxt_get_cdclk(struct drm_i915_private *dev_priv,  			  struct intel_cdclk_config *cdclk_config)  { +	u32 squash_ctl = 0;  	u32 divider;  	int div; @@ -1490,7 +1514,21 @@ static void bxt_get_cdclk(struct drm_i915_private *dev_priv,  		return;  	} -	cdclk_config->cdclk = DIV_ROUND_CLOSEST(cdclk_config->vco, div); +	if (has_cdclk_squasher(dev_priv)) +		squash_ctl = intel_de_read(dev_priv, CDCLK_SQUASH_CTL); + +	if (squash_ctl & CDCLK_SQUASH_ENABLE) { +		u16 waveform; +		int size; + +		size = REG_FIELD_GET(CDCLK_SQUASH_WINDOW_SIZE_MASK, squash_ctl) + 1; +		waveform = REG_FIELD_GET(CDCLK_SQUASH_WAVEFORM_MASK, squash_ctl) >> (16 - size); + +		cdclk_config->cdclk = DIV_ROUND_CLOSEST(hweight16(waveform) * +							cdclk_config->vco, size * div); +	} else { +		cdclk_config->cdclk = DIV_ROUND_CLOSEST(cdclk_config->vco, div); +	}   out:  	/* @@ -1625,6 +1663,26 @@ static u32 bxt_cdclk_cd2x_div_sel(struct drm_i915_private *dev_priv,  	}  } +static u32 cdclk_squash_waveform(struct drm_i915_private *dev_priv, +				 int cdclk) +{ +	const struct intel_cdclk_vals *table = dev_priv->cdclk.table; +	int i; + +	if (cdclk == dev_priv->cdclk.hw.bypass) +		return 0; + +	for (i = 0; table[i].refclk; i++) +		if (table[i].refclk == dev_priv->cdclk.hw.ref && +		    table[i].cdclk == cdclk) +			return table[i].waveform; + +	drm_WARN(&dev_priv->drm, 1, "cdclk %d not valid for refclk %u\n", +		 cdclk, dev_priv->cdclk.hw.ref); + +	return 0xffff; +} +  static void bxt_set_cdclk(struct drm_i915_private *dev_priv,  			  const struct intel_cdclk_config *cdclk_config,  			  enum pipe pipe) @@ -1632,6 +1690,8 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv,  	int cdclk = cdclk_config->cdclk;  	int vco = cdclk_config->vco;  	u32 val; +	u16 waveform; +	int clock;  	int ret;  	/* Inform power controller of upcoming frequency change. */ @@ -1675,7 +1735,24 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv,  			bxt_de_pll_enable(dev_priv, vco);  	} -	val = bxt_cdclk_cd2x_div_sel(dev_priv, cdclk, vco) | +	waveform = cdclk_squash_waveform(dev_priv, cdclk); + +	if (waveform) +		clock = vco / 2; +	else +		clock = cdclk; + +	if (has_cdclk_squasher(dev_priv)) { +		u32 squash_ctl = 0; + +		if (waveform) +			squash_ctl = CDCLK_SQUASH_ENABLE | +				CDCLK_SQUASH_WINDOW_SIZE(0xf) | waveform; + +		intel_de_write(dev_priv, CDCLK_SQUASH_CTL, squash_ctl); +	} + +	val = bxt_cdclk_cd2x_div_sel(dev_priv, clock, vco) |  		bxt_cdclk_cd2x_pipe(dev_priv, pipe) |  		skl_cdclk_decimal(cdclk); @@ -1689,7 +1766,7 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv,  	intel_de_write(dev_priv, CDCLK_CTL, val);  	if (pipe != INVALID_PIPE) -		intel_wait_for_vblank(dev_priv, pipe); +		intel_crtc_wait_for_next_vblank(intel_crtc_for_pipe(dev_priv, pipe));  	if (DISPLAY_VER(dev_priv) >= 11) {  		ret = sandybridge_pcode_write(dev_priv, SKL_PCODE_CDCLK_CONTROL, @@ -1727,7 +1804,7 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv,  static void bxt_sanitize_cdclk(struct drm_i915_private *dev_priv)  {  	u32 cdctl, expected; -	int cdclk, vco; +	int cdclk, clock, vco;  	intel_update_cdclk(dev_priv);  	intel_dump_cdclk_config(&dev_priv->cdclk.hw, "Current CDCLK"); @@ -1763,8 +1840,12 @@ static void bxt_sanitize_cdclk(struct drm_i915_private *dev_priv)  	expected = skl_cdclk_decimal(cdclk);  	/* Figure out what CD2X divider we should be using for this cdclk */ -	expected |= bxt_cdclk_cd2x_div_sel(dev_priv, -					   dev_priv->cdclk.hw.cdclk, +	if (has_cdclk_squasher(dev_priv)) +		clock = dev_priv->cdclk.hw.vco / 2; +	else +		clock = dev_priv->cdclk.hw.cdclk; + +	expected |= bxt_cdclk_cd2x_div_sel(dev_priv, clock,  					   dev_priv->cdclk.hw.vco);  	/* @@ -1880,6 +1961,25 @@ static bool intel_cdclk_can_crawl(struct drm_i915_private *dev_priv,  		a->ref == b->ref;  } +static bool intel_cdclk_can_squash(struct drm_i915_private *dev_priv, +				   const struct intel_cdclk_config *a, +				   const struct intel_cdclk_config *b) +{ +	/* +	 * FIXME should store a bit more state in intel_cdclk_config +	 * to differentiate squasher vs. cd2x divider properly. For +	 * the moment all platforms with squasher use a fixed cd2x +	 * divider. +	 */ +	if (!has_cdclk_squasher(dev_priv)) +		return false; + +	return a->cdclk != b->cdclk && +		a->vco != 0 && +		a->vco == b->vco && +		a->ref == b->ref; +} +  /**   * intel_cdclk_needs_modeset - Determine if changong between the CDCLK   *                             configurations requires a modeset on all pipes @@ -1917,7 +2017,17 @@ static bool intel_cdclk_can_cd2x_update(struct drm_i915_private *dev_priv,  	if (DISPLAY_VER(dev_priv) < 10 && !IS_BROXTON(dev_priv))  		return false; +	/* +	 * FIXME should store a bit more state in intel_cdclk_config +	 * to differentiate squasher vs. cd2x divider properly. For +	 * the moment all platforms with squasher use a fixed cd2x +	 * divider. +	 */ +	if (has_cdclk_squasher(dev_priv)) +		return false; +  	return a->cdclk != b->cdclk && +		a->vco != 0 &&  		a->vco == b->vco &&  		a->ref == b->ref;  } @@ -1975,6 +2085,8 @@ static void intel_set_cdclk(struct drm_i915_private *dev_priv,  		intel_psr_pause(intel_dp);  	} +	intel_audio_cdclk_change_pre(dev_priv); +  	/*  	 * Lock aux/gmbus while we change cdclk in case those  	 * functions use cdclk. Not all platforms/ports do, @@ -2003,6 +2115,8 @@ static void intel_set_cdclk(struct drm_i915_private *dev_priv,  		intel_psr_resume(intel_dp);  	} +	intel_audio_cdclk_change_post(dev_priv); +  	if (drm_WARN(&dev_priv->drm,  		     intel_cdclk_changed(&dev_priv->cdclk.hw, cdclk_config),  		     "cdclk state doesn't match!\n")) { @@ -2524,6 +2638,58 @@ intel_atomic_get_cdclk_state(struct intel_atomic_state *state)  	return to_intel_cdclk_state(cdclk_state);  } +int intel_cdclk_atomic_check(struct intel_atomic_state *state, +			     bool *need_cdclk_calc) +{ +	struct drm_i915_private *i915 = to_i915(state->base.dev); +	const struct intel_cdclk_state *old_cdclk_state; +	const struct intel_cdclk_state *new_cdclk_state; +	struct intel_plane_state *plane_state; +	struct intel_bw_state *new_bw_state; +	struct intel_plane *plane; +	int min_cdclk = 0; +	enum pipe pipe; +	int ret; +	int i; + +	/* +	 * active_planes bitmask has been updated, and potentially affected +	 * planes are part of the state. We can now compute the minimum cdclk +	 * for each plane. +	 */ +	for_each_new_intel_plane_in_state(state, plane, plane_state, i) { +		ret = intel_plane_calc_min_cdclk(state, plane, need_cdclk_calc); +		if (ret) +			return ret; +	} + +	old_cdclk_state = intel_atomic_get_old_cdclk_state(state); +	new_cdclk_state = intel_atomic_get_new_cdclk_state(state); + +	if (new_cdclk_state && +	    old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk) +		*need_cdclk_calc = true; + +	ret = intel_cdclk_bw_calc_min_cdclk(state); +	if (ret) +		return ret; + +	new_bw_state = intel_atomic_get_new_bw_state(state); + +	if (!new_cdclk_state || !new_bw_state) +		return 0; + +	for_each_pipe(i915, pipe) { +		min_cdclk = max(new_cdclk_state->min_cdclk[pipe], min_cdclk); + +		/* Currently do this change only if we need to increase */ +		if (new_bw_state->min_cdclk > min_cdclk) +			*need_cdclk_calc = true; +	} + +	return 0; +} +  int intel_cdclk_init(struct drm_i915_private *dev_priv)  {  	struct intel_cdclk_state *cdclk_state; @@ -2587,7 +2753,7 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state)  		struct intel_crtc_state *crtc_state;  		pipe = ilog2(new_cdclk_state->active_pipes); -		crtc = intel_get_crtc_for_pipe(dev_priv, pipe); +		crtc = intel_crtc_for_pipe(dev_priv, pipe);  		crtc_state = intel_atomic_get_crtc_state(&state->base, crtc);  		if (IS_ERR(crtc_state)) @@ -2597,9 +2763,14 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state)  			pipe = INVALID_PIPE;  	} -	if (intel_cdclk_can_crawl(dev_priv, -				  &old_cdclk_state->actual, -				  &new_cdclk_state->actual)) { +	if (intel_cdclk_can_squash(dev_priv, +				   &old_cdclk_state->actual, +				   &new_cdclk_state->actual)) { +		drm_dbg_kms(&dev_priv->drm, +			    "Can change cdclk via squasher\n"); +	} else if (intel_cdclk_can_crawl(dev_priv, +					 &old_cdclk_state->actual, +					 &new_cdclk_state->actual)) {  		drm_dbg_kms(&dev_priv->drm,  			    "Can change cdclk via crawl\n");  	} else if (pipe != INVALID_PIPE) { |