[PATCH 11/18] WIP: vlv shared dpll

Ander Conselvan de Oliveira ander.conselvan.de.oliveira at intel.com
Fri May 13 09:04:57 UTC 2016


---
 drivers/gpu/drm/i915/i915_drv.h       |   3 +-
 drivers/gpu/drm/i915/intel_display.c  | 156 +++++++++++++-------------
 drivers/gpu/drm/i915/intel_dpio_phy.c |   7 +-
 drivers/gpu/drm/i915/intel_dpll_mgr.c | 200 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_dpll_mgr.h |  17 +++
 5 files changed, 303 insertions(+), 80 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 643b081..54cf381 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3637,7 +3637,8 @@ void vlv_set_phy_signal_level(struct intel_encoder *encoder,
 void vlv_phy_pre_pll_enable(struct intel_encoder *encoder);
 void vlv_phy_pre_encoder_enable(struct intel_encoder *encoder);
 void vlv_phy_reset_lanes(struct intel_encoder *encoder);
-void vlv_phy_prepare_pll(struct intel_crtc *crtc, u32 bestn,
+void vlv_phy_prepare_pll(struct drm_i915_private *dev_priv,
+			 enum pipe pipe, u32 bestn,
 			 u32 bestm1, u32 bestm2, u32 bestp1, u32 bestp2,
 			 u32 lpf, bool use_ssc_source);
 void vlv_phy_read_dividers(struct drm_i915_private *dev_priv,
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 3a84732e..0222fc3 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -1523,52 +1523,6 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv,
 	assert_pch_hdmi_disabled(dev_priv, pipe, PCH_HDMID);
 }
 
-static void _vlv_enable_pll(struct intel_crtc *crtc,
-			    const struct intel_crtc_state *pipe_config)
-{
-	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
-	enum pipe pipe = crtc->pipe;
-
-	I915_WRITE(DPLL(pipe), pipe_config->dpll_hw_state.dpll);
-	POSTING_READ(DPLL(pipe));
-	udelay(150);
-
-	if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1))
-		DRM_ERROR("DPLL %d failed to lock\n", pipe);
-}
-
-static void vlv_enable_pll(struct intel_crtc *crtc,
-			   const struct intel_crtc_state *pipe_config)
-{
-	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
-	enum pipe pipe = crtc->pipe;
-
-	/* Enable Refclk */
-	I915_WRITE(DPLL(pipe),
-		   pipe_config->dpll_hw_state.dpll &
-		   ~(DPLL_VCO_ENABLE | DPLL_EXT_BUFFER_ENABLE_VLV));
-
-	/* No need to actually set up the DPLL with DSI */
-	if ((pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE) != 0) {
-		const struct intel_dpll *dividers =
-			&pipe_config->dpll_hw_state.dividers;
-
-		vlv_phy_prepare_pll(crtc, dividers->n,
-				    dividers->m1, dividers->m2,
-				    dividers->p1, dividers->p2,
-				    pipe_config->dpll_hw_state.lpf,
-				    pipe_config->dpll_hw_state.ssc);
-	}
-
-	assert_pipe_disabled(dev_priv, pipe);
-
-	/* PLL is protected by panel, make sure we can write it */
-	assert_panel_unlocked(dev_priv, pipe);
-
-	if (pipe_config->dpll_hw_state.dpll & DPLL_VCO_ENABLE)
-		_vlv_enable_pll(crtc, pipe_config);
-}
-
 
 static void _chv_enable_pll(struct intel_crtc *crtc,
 			    const struct intel_crtc_state *pipe_config)
@@ -1743,22 +1697,6 @@ static void i9xx_disable_pll(struct intel_crtc *crtc)
 	POSTING_READ(DPLL(pipe));
 }
 
-static void vlv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
-{
-	u32 val;
-
-	/* Make sure the pipe isn't still relying on us */
-	assert_pipe_disabled(dev_priv, pipe);
-
-	val = DPLL_INTEGRATED_REF_CLK_VLV |
-		DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS;
-	if (pipe != PIPE_A)
-		val |= DPLL_INTEGRATED_CRI_CLK_VLV;
-
-	I915_WRITE(DPLL(pipe), val);
-	POSTING_READ(DPLL(pipe));
-}
-
 static void chv_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
 {
 	enum dpio_channel port = vlv_pipe_to_channel(pipe);
@@ -6127,8 +6065,8 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
 
 	if (IS_CHERRYVIEW(dev))
 		chv_enable_pll(intel_crtc, intel_crtc->config);
-	else
-		vlv_enable_pll(intel_crtc, intel_crtc->config);
+	else if (!pipe_config->has_dsi_encoder)
+		intel_enable_shared_dpll(intel_crtc);
 
 	vlv_crtc_set_pixel_multiplier(intel_crtc);
 
@@ -6255,7 +6193,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
 		if (IS_CHERRYVIEW(dev))
 			chv_disable_pll(dev_priv, pipe);
 		else if (IS_VALLEYVIEW(dev))
-			vlv_disable_pll(dev_priv, pipe);
+			intel_disable_shared_dpll(intel_crtc);
 		else
 			i9xx_disable_pll(intel_crtc);
 	}
@@ -7240,6 +7178,7 @@ static void chv_compute_dpll(struct intel_crtc *crtc,
 int vlv_force_pll_on(struct drm_device *dev, enum pipe pipe,
 		     const struct intel_dpll *dpll)
 {
+	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct intel_crtc *crtc =
 		to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe));
 	struct intel_crtc_state *pipe_config;
@@ -7255,8 +7194,12 @@ int vlv_force_pll_on(struct drm_device *dev, enum pipe pipe,
 		chv_compute_dpll(crtc, pipe_config);
 		chv_enable_pll(crtc, pipe_config);
 	} else {
+		struct intel_shared_dpll *pll =
+			intel_get_shared_dpll_by_id(dev_priv, pipe);
+
 		vlv_compute_dpll(crtc, pipe_config);
-		vlv_enable_pll(crtc, pipe_config);
+		intel_shared_dpll_force_enable(dev_priv, pll,
+					       &pipe_config->dpll_hw_state);
 	}
 
 	kfree(pipe_config);
@@ -7274,10 +7217,16 @@ int vlv_force_pll_on(struct drm_device *dev, enum pipe pipe,
  */
 void vlv_force_pll_off(struct drm_device *dev, enum pipe pipe)
 {
-	if (IS_CHERRYVIEW(dev))
+	struct drm_i915_private *dev_priv = to_i915(dev);
+
+	if (IS_CHERRYVIEW(dev)) {
 		chv_disable_pll(to_i915(dev), pipe);
-	else
-		vlv_disable_pll(to_i915(dev), pipe);
+	} else {
+		struct intel_shared_dpll *pll =
+			intel_get_shared_dpll_by_id(dev_priv, pipe);
+
+		intel_shared_dpll_force_disable(dev_priv, pll);
+	}
 }
 
 static void i9xx_compute_dpll(struct intel_crtc *crtc,
@@ -7758,6 +7707,38 @@ static int i9xx_crtc_compute_clock(struct intel_crtc *crtc,
 	return 0;
 }
 
+static struct intel_encoder *
+find_digital_encoder(struct intel_crtc *crtc,
+		     struct intel_crtc_state *crtc_state)
+{
+	struct intel_encoder *encoder;
+	struct drm_atomic_state *state;
+	struct drm_connector *connector;
+	struct drm_connector_state *connector_state;
+	int i;
+
+	state = crtc_state->base.state;
+
+	for_each_connector_in_state(state, connector, connector_state, i) {
+		if (connector_state->crtc != crtc_state->base.crtc)
+			continue;
+
+		encoder = to_intel_encoder(connector_state->best_encoder);
+
+		switch (encoder->type) {
+		case INTEL_OUTPUT_DISPLAYPORT:
+		case INTEL_OUTPUT_EDP:
+		case INTEL_OUTPUT_HDMI:
+		case INTEL_OUTPUT_DSI:
+			return encoder;
+		default:
+			break;
+		}
+	}
+
+	return NULL;
+}
+
 static int chv_crtc_compute_clock(struct intel_crtc *crtc,
 				  struct intel_crtc_state *crtc_state)
 {
@@ -7785,6 +7766,7 @@ static int vlv_crtc_compute_clock(struct intel_crtc *crtc,
 {
 	int refclk = 100000;
 	const struct intel_limit *limit = &intel_limits_vlv;
+	struct intel_shared_dpll *pll;
 
 	memset(&crtc_state->dpll_hw_state, 0,
 	       sizeof(crtc_state->dpll_hw_state));
@@ -7799,6 +7781,14 @@ static int vlv_crtc_compute_clock(struct intel_crtc *crtc,
 
 	vlv_compute_dpll(crtc, crtc_state);
 
+	pll = intel_get_shared_dpll(crtc, crtc_state,
+				    find_digital_encoder(crtc, crtc_state));
+	if (!crtc_state->has_dsi_encoder && !pll) {
+		DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n",
+				 pipe_name(crtc->pipe));
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
@@ -8002,14 +7992,27 @@ static bool vlv_get_pipe_config(struct intel_crtc *crtc,
 	pipe_config->pixel_multiplier =
 		((tmp & DPLL_MD_UDI_MULTIPLIER_MASK)
 		 >> DPLL_MD_UDI_MULTIPLIER_SHIFT) + 1;
-	pipe_config->dpll_hw_state.dpll_md = tmp;
 
-	pipe_config->dpll_hw_state.dpll = I915_READ(DPLL(crtc->pipe));
+	if (IS_VALLEYVIEW(dev_priv)) {
+		struct intel_shared_dpll *pll =
+			intel_get_shared_dpll_by_id(dev_priv, crtc->pipe);
+		bool enabled;
+
+		enabled = pll->funcs.get_hw_state(dev_priv, pll,
+						  &pipe_config->dpll_hw_state);
 
-	/* Mask out read-only status bits. */
-	pipe_config->dpll_hw_state.dpll &= ~(DPLL_LOCK_VLV |
-					     DPLL_PORTC_READY_MASK |
-					     DPLL_PORTB_READY_MASK);
+		if (enabled)
+			pipe_config->shared_dpll = pll;
+	} else {
+		pipe_config->dpll_hw_state.dpll_md = tmp;
+
+		pipe_config->dpll_hw_state.dpll = I915_READ(DPLL(crtc->pipe));
+
+		/* Mask out read-only status bits. */
+		pipe_config->dpll_hw_state.dpll &= ~(DPLL_LOCK_VLV |
+						     DPLL_PORTC_READY_MASK |
+						     DPLL_PORTB_READY_MASK);
+	}
 
 	if (IS_CHERRYVIEW(dev))
 		chv_crtc_clock_get(crtc, pipe_config);
@@ -15788,6 +15791,9 @@ intel_modeset_setup_hw_state(struct drm_device *dev)
 	for (i = 0; i < dev_priv->num_shared_dpll; i++) {
 		struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i];
 
+		if (pll->funcs.sanitize)
+			pll->funcs.sanitize(dev_priv, pll);
+
 		if (!pll->on || pll->active_mask)
 			continue;
 
diff --git a/drivers/gpu/drm/i915/intel_dpio_phy.c b/drivers/gpu/drm/i915/intel_dpio_phy.c
index 0428788..e844325 100644
--- a/drivers/gpu/drm/i915/intel_dpio_phy.c
+++ b/drivers/gpu/drm/i915/intel_dpio_phy.c
@@ -589,12 +589,11 @@ static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv,
 	vlv_dpio_write(dev_priv, pipe, VLV_REF_DW13, reg_val);
 }
 
-void vlv_phy_prepare_pll(struct intel_crtc *crtc, u32 bestn,
-			 u32 bestm1, u32 bestm2, u32 bestp1, u32 bestp2,
+void vlv_phy_prepare_pll(struct drm_i915_private *dev_priv, enum pipe pipe,
+			 u32 bestn, u32 bestm1, u32 bestm2,
+			 u32 bestp1, u32 bestp2,
 			 u32 lpf, bool use_ssc_source)
 {
-	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
-	int pipe = crtc->pipe;
 	u32 mdiv;
 	u32 coreclk, reg_val;
 
diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.c b/drivers/gpu/drm/i915/intel_dpll_mgr.c
index 3499ed2..413b8e2 100644
--- a/drivers/gpu/drm/i915/intel_dpll_mgr.c
+++ b/drivers/gpu/drm/i915/intel_dpll_mgr.c
@@ -185,6 +185,67 @@ out:
 	mutex_unlock(&dev_priv->dpll_lock);
 }
 
+/** intel_shared_dpll_force_enable - Force enable a pll with given state
+ * @dev_priv: i915 private structure
+ * @pll: pll to force enable
+ * @hw_state: hw state with wich the pll should be enabled
+ *
+ * This function enables the given pll with the provided hw state. It is
+ * intended to be used for short operations that require it to be running
+ * where doing a full modeset to enable it is not possible.
+ *
+ * The dpll_lock is acquired by this fuction, but only released by calling
+ * intel_shared_dpll_force_disable().
+ *
+ * Return: true if the pll was enabled and a call to
+ * intel_shared_dpll_force_disable() should succeed; false otherwise.
+ */
+bool intel_shared_dpll_force_enable(struct drm_i915_private *dev_priv,
+				    struct intel_shared_dpll *pll,
+				    struct intel_dpll_hw_state *hw_state)
+{
+	mutex_lock(&dev_priv->dpll_lock);
+
+	if (pll->active_mask != 0 || pll->on) {
+		DRM_DEBUG_KMS("Can't force enable pll %s, already enabled\n",
+			      pll->name);
+		goto error;
+	}
+
+	memcpy(&pll->config.hw_state, hw_state, sizeof *hw_state);
+
+	DRM_DEBUG_KMS("force enabling %s\n", pll->name);
+	pll->funcs.enable(dev_priv, pll);
+	pll->on = true;
+
+	return true;
+
+error:
+	mutex_unlock(&dev_priv->dpll_lock);
+	return false;
+}
+
+/** intel_shared_dpll_force_disable -- disable a force enabled pll
+ * @dev_priv: i915 private structure
+ * @pll: pll to disable
+ * @hw_state: previous state of the pll
+ *
+ * Disables a pll wich was enabled with intel_shared_dpll_force_enable(),
+ * and release the dpll_lock.
+ */
+void intel_shared_dpll_force_disable(struct drm_i915_private *dev_priv,
+				     struct intel_shared_dpll *pll)
+{
+	assert_shared_dpll_enabled(dev_priv, pll);
+	WARN_ON(!pll->on);
+
+	DRM_DEBUG_KMS("force disabling %s\n", pll->name);
+	pll->funcs.disable(dev_priv, pll);
+	pll->on = false;
+
+	mutex_unlock(&dev_priv->dpll_lock);
+}
+
 static struct intel_shared_dpll *
 intel_find_shared_dpll(struct intel_crtc *crtc,
 		       struct intel_crtc_state *crtc_state,
@@ -1659,6 +1720,132 @@ static void intel_ddi_pll_init(struct drm_device *dev)
 	}
 }
 
+/* vlv */
+static u32 vlv_dpll_reg_value(struct drm_i915_private *dev_priv,
+			      struct intel_shared_dpll *pll,
+			      bool enabled)
+{
+	enum pipe pipe = pll->id;
+
+	/*
+	 * The refclk should be kept enabled since it is required when driving
+	 * a DSI output.
+	 */
+	u32 dpll =
+		DPLL_INTEGRATED_REF_CLK_VLV |
+		DPLL_REF_CLK_ENABLE_VLV |
+		DPLL_VGA_MODE_DIS;
+
+	if (pipe != PIPE_A)
+		dpll |= DPLL_INTEGRATED_CRI_CLK_VLV;
+
+	if (enabled)
+		dpll |= DPLL_VCO_ENABLE | DPLL_EXT_BUFFER_ENABLE_VLV;
+
+	return dpll;
+}
+
+static void vlv_sanitize_pll(struct drm_i915_private *dev_priv,
+			     struct intel_shared_dpll *pll)
+{
+	enum pipe pipe = pll->id;
+	u32 dpll = vlv_dpll_reg_value(dev_priv, pll, pll->on);
+
+	I915_WRITE(DPLL(pipe), dpll);
+	POSTING_READ(DPLL(pipe));
+}
+
+static void vlv_pll_enable(struct drm_i915_private *dev_priv,
+			   struct intel_shared_dpll *pll)
+{
+	enum pipe pipe = pll->id;
+	struct intel_dpll_hw_state *hw_state = &pll->config.hw_state;
+	const struct intel_dpll *dividers = &hw_state->dividers;
+
+	WARN_ON((hw_state->dpll & DPLL_VCO_ENABLE) == 0);
+
+	vlv_phy_prepare_pll(dev_priv, pipe, dividers->n,
+			    dividers->m1, dividers->m2,
+			    dividers->p1, dividers->p2,
+			    hw_state->lpf,
+			    hw_state->ssc);
+
+	assert_pipe_disabled(dev_priv, pipe);
+
+	/* PLL is protected by panel, make sure we can write it */
+	assert_panel_unlocked(dev_priv, pipe);
+
+	I915_WRITE(DPLL(pipe), pll->config.hw_state.dpll);
+	POSTING_READ(DPLL(pipe));
+	udelay(150);
+
+	if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1))
+		DRM_ERROR("DPLL %d failed to lock\n", pipe);
+}
+
+
+static void vlv_pll_disable(struct drm_i915_private *dev_priv,
+			    struct intel_shared_dpll *pll)
+{
+	enum pipe pipe = pll->id;
+	u32 val;
+
+	/* Make sure the pipe isn't still relying on us */
+	assert_pipe_disabled(dev_priv, pipe);
+
+	val = vlv_dpll_reg_value(dev_priv, pll, false);
+	I915_WRITE(DPLL(pipe), val);
+	POSTING_READ(DPLL(pipe));
+}
+
+static bool vlv_pll_get_hw_state(struct drm_i915_private *dev_priv,
+				 struct intel_shared_dpll *pll,
+				 struct intel_dpll_hw_state *hw_state)
+{
+	enum pipe pipe = pll->id;
+	u32 read_only_bits =
+		DPLL_LOCK_VLV | DPLL_PORTC_READY_MASK | DPLL_PORTB_READY_MASK;
+
+	if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
+		return false;
+
+	hw_state->dpll = I915_READ(DPLL(pipe)) & ~read_only_bits;
+
+	if (hw_state->dpll & DPLL_VCO_ENABLE)
+		vlv_phy_read_dividers(dev_priv, pipe, &hw_state->dividers);
+
+	intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
+
+	return hw_state->dpll & DPLL_VCO_ENABLE;
+}
+
+static const struct intel_shared_dpll_funcs vlv_pll_funcs = {
+	.enable = vlv_pll_enable,
+	.disable = vlv_pll_disable,
+	.get_hw_state = vlv_pll_get_hw_state,
+	.sanitize = vlv_sanitize_pll,
+};
+
+static struct intel_shared_dpll *
+vlv_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
+	     struct intel_encoder *encoder)
+{
+	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+	struct intel_shared_dpll *pll;
+
+	if (encoder->type == INTEL_OUTPUT_DSI) {
+		return NULL;
+	}
+
+	pll = intel_get_shared_dpll_by_id(dev_priv, crtc->pipe);
+
+	intel_reference_shared_dpll(pll, crtc_state);
+
+	return pll;
+}
+
+/* --- */
+
 struct dpll_info {
 	const char *name;
 	const int id;
@@ -1725,6 +1912,17 @@ static const struct intel_dpll_mgr bxt_pll_mgr = {
 	.get_dpll = bxt_get_dpll,
 };
 
+static const struct dpll_info vlv_plls[] = {
+	{ "PIPE A PLL", PIPE_A, &vlv_pll_funcs, 0 },
+	{ "PIPE B PLL", PIPE_B, &vlv_pll_funcs, 0 },
+	{ NULL, -1, NULL, },
+};
+
+static const struct intel_dpll_mgr vlv_pll_mgr = {
+	.dpll_info = vlv_plls,
+	.get_dpll = vlv_get_dpll,
+};
+
 void intel_shared_dpll_init(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1740,6 +1938,8 @@ void intel_shared_dpll_init(struct drm_device *dev)
 		dpll_mgr = &hsw_pll_mgr;
 	else if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
 		dpll_mgr = &pch_pll_mgr;
+	else if (IS_VALLEYVIEW(dev))
+		dpll_mgr = &vlv_pll_mgr;
 
 	if (!dpll_mgr) {
 		dev_priv->num_shared_dpll = 0;
diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.h b/drivers/gpu/drm/i915/intel_dpll_mgr.h
index ebbf65c..f33a765 100644
--- a/drivers/gpu/drm/i915/intel_dpll_mgr.h
+++ b/drivers/gpu/drm/i915/intel_dpll_mgr.h
@@ -128,6 +128,18 @@ struct intel_shared_dpll_funcs {
 	bool (*get_hw_state)(struct drm_i915_private *dev_priv,
 			     struct intel_shared_dpll *pll,
 			     struct intel_dpll_hw_state *hw_state);
+
+	/**
+	 * @sanitize:
+	 *
+	 * If non-NULL, this hook will be called from hardware state read
+	 * out. It can be used to update the hw state into a sane state
+	 * form sw tracking perspective. For example, if the rest of the
+	 * code assumes a register bit is always enabled, this would be
+	 * the place to enable it in case it was disabled by the BIOS.
+	 */
+	void (*sanitize)(struct drm_i915_private *dev_priv,
+			 struct intel_shared_dpll *pll);
 };
 
 struct intel_shared_dpll {
@@ -178,5 +190,10 @@ void intel_disable_shared_dpll(struct intel_crtc *crtc);
 void intel_shared_dpll_commit(struct drm_atomic_state *state);
 void intel_shared_dpll_init(struct drm_device *dev);
 
+bool intel_shared_dpll_force_enable(struct drm_i915_private *dev_priv,
+				    struct intel_shared_dpll *pll,
+				    struct intel_dpll_hw_state *hw_state);
+void intel_shared_dpll_force_disable(struct drm_i915_private *dev_priv,
+				     struct intel_shared_dpll *pll);
 
 #endif /* _INTEL_DPLL_MGR_H_ */
-- 
2.5.5



More information about the Intel-gfx-trybot mailing list