[PATCH 14/19] WIP: vlv shared dpll
Ander Conselvan de Oliveira
ander.conselvan.de.oliveira at intel.com
Tue May 3 08:37:54 UTC 2016
---
drivers/gpu/drm/i915/i915_drv.h | 3 +-
drivers/gpu/drm/i915/intel_display.c | 131 +++++++++++------------
drivers/gpu/drm/i915/intel_dpio_phy.c | 7 +-
drivers/gpu/drm/i915/intel_dpll_mgr.c | 195 ++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_dpll_mgr.h | 17 +++
5 files changed, 279 insertions(+), 74 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 87bded2..50142ea 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3612,7 +3612,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 e65a073..658a3ad 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 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);
@@ -6129,8 +6067,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);
@@ -6257,7 +6195,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);
}
@@ -7242,6 +7180,7 @@ static void chv_compute_dpll(struct intel_crtc *crtc,
int vlv_force_pll_on(struct drm_device *dev, enum pipe pipe,
const struct 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;
@@ -7257,8 +7196,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);
@@ -7276,10 +7219,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,
@@ -7760,6 +7709,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)
{
@@ -7787,6 +7768,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));
@@ -7801,6 +7783,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;
}
@@ -15782,6 +15772,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 3e53907..c97a743 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 b57d7ba..d7b7b87 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,127 @@ 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 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;
+
+ 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);
+
+ 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 +1907,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 +1933,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 a60576a..e344533 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.4.11
More information about the Intel-gfx-trybot
mailing list