Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 26972b0a authored by Ville Syrjälä's avatar Ville Syrjälä Committed by Daniel Vetter
Browse files

drm/i915: Add per-pipe power wells for chv



CHV has a power well for each pipe. Add the code to deal with them.

The Punit in current hardware doesn't seem ready for this yet, so
leave it iffed out.

Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: default avatarImre Deak <imre.deak@intel.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent f07057d1
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -501,6 +501,18 @@
#define   DSPFREQSTAT_MASK			(0x3 << DSPFREQSTAT_SHIFT)
#define   DSPFREQGUAR_SHIFT			14
#define   DSPFREQGUAR_MASK			(0x3 << DSPFREQGUAR_SHIFT)
#define   _DP_SSC(val, pipe)			((val) << (2 * (pipe)))
#define   DP_SSC_MASK(pipe)			_DP_SSC(0x3, (pipe))
#define   DP_SSC_PWR_ON(pipe)			_DP_SSC(0x0, (pipe))
#define   DP_SSC_CLK_GATE(pipe)			_DP_SSC(0x1, (pipe))
#define   DP_SSC_RESET(pipe)			_DP_SSC(0x2, (pipe))
#define   DP_SSC_PWR_GATE(pipe)			_DP_SSC(0x3, (pipe))
#define   _DP_SSS(val, pipe)			((val) << (2 * (pipe) + 16))
#define   DP_SSS_MASK(pipe)			_DP_SSS(0x3, (pipe))
#define   DP_SSS_PWR_ON(pipe)			_DP_SSS(0x0, (pipe))
#define   DP_SSS_CLK_GATE(pipe)			_DP_SSS(0x1, (pipe))
#define   DP_SSS_RESET(pipe)			_DP_SSS(0x2, (pipe))
#define   DP_SSS_PWR_GATE(pipe)			_DP_SSS(0x3, (pipe))

/* See the PUNIT HAS v0.8 for the below bits */
enum punit_power_well {
+126 −0
Original line number Diff line number Diff line
@@ -6312,6 +6312,95 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv,
	vlv_set_power_well(dev_priv, power_well, false);
}

static bool chv_pipe_power_well_enabled(struct drm_i915_private *dev_priv,
					struct i915_power_well *power_well)
{
	enum pipe pipe = power_well->data;
	bool enabled;
	u32 state, ctrl;

	mutex_lock(&dev_priv->rps.hw_lock);

	state = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) & DP_SSS_MASK(pipe);
	/*
	 * We only ever set the power-on and power-gate states, anything
	 * else is unexpected.
	 */
	WARN_ON(state != DP_SSS_PWR_ON(pipe) && state != DP_SSS_PWR_GATE(pipe));
	enabled = state == DP_SSS_PWR_ON(pipe);

	/*
	 * A transient state at this point would mean some unexpected party
	 * is poking at the power controls too.
	 */
	ctrl = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) & DP_SSC_MASK(pipe);
	WARN_ON(ctrl << 16 != state);

	mutex_unlock(&dev_priv->rps.hw_lock);

	return enabled;
}

static void chv_set_pipe_power_well(struct drm_i915_private *dev_priv,
				    struct i915_power_well *power_well,
				    bool enable)
{
	enum pipe pipe = power_well->data;
	u32 state;
	u32 ctrl;

	state = enable ? DP_SSS_PWR_ON(pipe) : DP_SSS_PWR_GATE(pipe);

	mutex_lock(&dev_priv->rps.hw_lock);

#define COND \
	((vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ) & DP_SSS_MASK(pipe)) == state)

	if (COND)
		goto out;

	ctrl = vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ);
	ctrl &= ~DP_SSC_MASK(pipe);
	ctrl |= enable ? DP_SSC_PWR_ON(pipe) : DP_SSC_PWR_GATE(pipe);
	vlv_punit_write(dev_priv, PUNIT_REG_DSPFREQ, ctrl);

	if (wait_for(COND, 100))
		DRM_ERROR("timout setting power well state %08x (%08x)\n",
			  state,
			  vlv_punit_read(dev_priv, PUNIT_REG_DSPFREQ));

#undef COND

out:
	mutex_unlock(&dev_priv->rps.hw_lock);
}

static void chv_pipe_power_well_sync_hw(struct drm_i915_private *dev_priv,
					struct i915_power_well *power_well)
{
	chv_set_pipe_power_well(dev_priv, power_well, power_well->count > 0);
}

static void chv_pipe_power_well_enable(struct drm_i915_private *dev_priv,
				       struct i915_power_well *power_well)
{
	WARN_ON_ONCE(power_well->data != PIPE_A &&
		     power_well->data != PIPE_B &&
		     power_well->data != PIPE_C);

	chv_set_pipe_power_well(dev_priv, power_well, true);
}

static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv,
					struct i915_power_well *power_well)
{
	WARN_ON_ONCE(power_well->data != PIPE_A &&
		     power_well->data != PIPE_B &&
		     power_well->data != PIPE_C);

	chv_set_pipe_power_well(dev_priv, power_well, false);
}

static void check_power_well_state(struct drm_i915_private *dev_priv,
				   struct i915_power_well *power_well)
{
@@ -6503,6 +6592,18 @@ EXPORT_SYMBOL_GPL(i915_get_cdclk_freq);
	BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) |	\
	BIT(POWER_DOMAIN_INIT))

#define CHV_PIPE_A_POWER_DOMAINS (	\
	BIT(POWER_DOMAIN_PIPE_A) |	\
	BIT(POWER_DOMAIN_INIT))

#define CHV_PIPE_B_POWER_DOMAINS (	\
	BIT(POWER_DOMAIN_PIPE_B) |	\
	BIT(POWER_DOMAIN_INIT))

#define CHV_PIPE_C_POWER_DOMAINS (	\
	BIT(POWER_DOMAIN_PIPE_C) |	\
	BIT(POWER_DOMAIN_INIT))

#define CHV_DPIO_CMN_BC_POWER_DOMAINS (		\
	BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) |	\
	BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) |	\
@@ -6522,6 +6623,13 @@ static const struct i915_power_well_ops i9xx_always_on_power_well_ops = {
	.is_enabled = i9xx_always_on_power_well_enabled,
};

static const struct i915_power_well_ops chv_pipe_power_well_ops = {
	.sync_hw = chv_pipe_power_well_sync_hw,
	.enable = chv_pipe_power_well_enable,
	.disable = chv_pipe_power_well_disable,
	.is_enabled = chv_pipe_power_well_enabled,
};

static const struct i915_power_well_ops chv_dpio_cmn_power_well_ops = {
	.sync_hw = vlv_power_well_sync_hw,
	.enable = chv_dpio_cmn_power_well_enable,
@@ -6665,6 +6773,24 @@ static struct i915_power_well chv_power_wells[] = {
		.data = PUNIT_POWER_WELL_DISP2D,
		.ops = &vlv_display_power_well_ops,
	},
	{
		.name = "pipe-a",
		.domains = CHV_PIPE_A_POWER_DOMAINS,
		.data = PIPE_A,
		.ops = &chv_pipe_power_well_ops,
	},
	{
		.name = "pipe-b",
		.domains = CHV_PIPE_B_POWER_DOMAINS,
		.data = PIPE_B,
		.ops = &chv_pipe_power_well_ops,
	},
	{
		.name = "pipe-c",
		.domains = CHV_PIPE_C_POWER_DOMAINS,
		.data = PIPE_C,
		.ops = &chv_pipe_power_well_ops,
	},
#endif
	{
		.name = "dpio-common-bc",