@@ -11032,6 +11032,21 @@ static void hsw_get_ddi_pll(struct drm_i915_private *dev_priv, enum port port,
pipe_config->shared_dpll = intel_get_shared_dpll_by_id(dev_priv, id);
}
+static bool transcoder_ddi_func_is_enabled(struct drm_i915_private *dev_priv,
+ enum transcoder cpu_transcoder)
+{
+ enum intel_display_power_domain power_domain;
+ intel_wakeref_t wakeref;
+ u32 tmp = 0;
+
+ power_domain = POWER_DOMAIN_TRANSCODER(cpu_transcoder);
+
+ with_intel_display_power_if_enabled(dev_priv, power_domain, wakeref)
+ tmp = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder));
+
+ return tmp & TRANS_DDI_FUNC_ENABLE;
+}
+
static u8 hsw_panel_transcoders(struct drm_i915_private *i915)
{
u8 panel_transcoder_mask = BIT(TRANSCODER_EDP);
@@ -11042,58 +11057,39 @@ static u8 hsw_panel_transcoders(struct drm_i915_private *i915)
return panel_transcoder_mask;
}
-static bool hsw_get_transcoder_state(struct intel_crtc *crtc,
- struct intel_crtc_state *pipe_config,
- u64 *power_domain_mask,
- intel_wakeref_t *wakerefs)
+static u8 hsw_enabled_transcoders(struct intel_crtc *crtc)
{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = to_i915(dev);
- enum intel_display_power_domain power_domain;
u8 panel_transcoder_mask = hsw_panel_transcoders(dev_priv);
- unsigned long enabled_panel_transcoders = 0;
- enum transcoder panel_transcoder;
- intel_wakeref_t wf;
- u32 tmp;
-
- /*
- * The pipe->transcoder mapping is fixed with the exception of the eDP
- * and DSI transcoders handled below.
- */
- pipe_config->cpu_transcoder = (enum transcoder) crtc->pipe;
+ enum transcoder cpu_transcoder;
+ u8 enabled_transcoders = 0;
/*
* XXX: Do intel_display_power_get_if_enabled before reading this (for
* consistency and less surprising code; it's in always on power).
*/
- for_each_cpu_transcoder_masked(dev_priv, panel_transcoder,
+ for_each_cpu_transcoder_masked(dev_priv, cpu_transcoder,
panel_transcoder_mask) {
- bool force_thru = false;
+ enum intel_display_power_domain power_domain;
+ intel_wakeref_t wakeref;
enum pipe trans_pipe;
+ u32 tmp = 0;
+
+ power_domain = POWER_DOMAIN_TRANSCODER(cpu_transcoder);
+ with_intel_display_power_if_enabled(dev_priv, power_domain, wakeref)
+ tmp = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(cpu_transcoder));
- tmp = intel_de_read(dev_priv,
- TRANS_DDI_FUNC_CTL(panel_transcoder));
if (!(tmp & TRANS_DDI_FUNC_ENABLE))
continue;
- /*
- * Log all enabled ones, only use the first one.
- *
- * FIXME: This won't work for two separate DSI displays.
- */
- enabled_panel_transcoders |= BIT(panel_transcoder);
- if (enabled_panel_transcoders != BIT(panel_transcoder))
- continue;
-
switch (tmp & TRANS_DDI_EDP_INPUT_MASK) {
default:
drm_WARN(dev, 1,
"unknown pipe linked to transcoder %s\n",
- transcoder_name(panel_transcoder));
+ transcoder_name(cpu_transcoder));
fallthrough;
case TRANS_DDI_EDP_INPUT_A_ONOFF:
- force_thru = true;
- fallthrough;
case TRANS_DDI_EDP_INPUT_A_ON:
trans_pipe = PIPE_A;
break;
@@ -11108,17 +11104,76 @@ static bool hsw_get_transcoder_state(struct intel_crtc *crtc,
break;
}
- if (trans_pipe == crtc->pipe) {
- pipe_config->cpu_transcoder = panel_transcoder;
- pipe_config->pch_pfit.force_thru = force_thru;
- }
+ if (trans_pipe == crtc->pipe)
+ enabled_transcoders |= BIT(cpu_transcoder);
}
+ cpu_transcoder = (enum transcoder) crtc->pipe;
+ if (transcoder_ddi_func_is_enabled(dev_priv, cpu_transcoder))
+ enabled_transcoders |= BIT(cpu_transcoder);
+
+ return enabled_transcoders;
+}
+
+static bool has_edp_transcoders(u8 enabled_transcoders)
+{
+ return enabled_transcoders & BIT(TRANSCODER_EDP);
+}
+
+static bool has_dsi_transcoders(u8 enabled_transcoders)
+{
+ return enabled_transcoders & (BIT(TRANSCODER_DSI_0) |
+ BIT(TRANSCODER_DSI_1));
+}
+
+static bool has_pipe_transcoders(u8 enabled_transcoders)
+{
+ return enabled_transcoders & ~(BIT(TRANSCODER_EDP) |
+ BIT(TRANSCODER_DSI_0) |
+ BIT(TRANSCODER_DSI_1));
+}
+
+static void assert_enabled_transcoders(struct drm_i915_private *i915,
+ u8 enabled_transcoders)
+{
+ /* Only one type of transcoder please */
+ drm_WARN_ON(&i915->drm,
+ has_edp_transcoders(enabled_transcoders) +
+ has_dsi_transcoders(enabled_transcoders) +
+ has_pipe_transcoders(enabled_transcoders) > 1);
+
+ /* Only DSI transcoders can be ganged */
+ drm_WARN_ON(&i915->drm,
+ !has_dsi_transcoders(enabled_transcoders) &&
+ !is_power_of_2(enabled_transcoders));
+}
+
+static bool hsw_get_transcoder_state(struct intel_crtc *crtc,
+ struct intel_crtc_state *pipe_config,
+ u64 *power_domain_mask,
+ intel_wakeref_t *wakerefs)
+{
+ struct drm_device *dev = crtc->base.dev;
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ enum intel_display_power_domain power_domain;
+ unsigned long enabled_transcoders;
+ intel_wakeref_t wf;
+ u32 tmp;
+
+ enabled_transcoders = hsw_enabled_transcoders(crtc);
+ if (!enabled_transcoders)
+ return false;
+
+ assert_enabled_transcoders(dev_priv, enabled_transcoders);
+
/*
- * Valid combos: none, eDP, DSI0, DSI1, DSI0+DSI1
+ * With the exception of DSI we should only ever have
+ * a single enabled transcoder. With DSI let's just
+ * pick the first one.
+ *
+ * FIXME: This won't work for two separate DSI displays.
*/
- drm_WARN_ON(dev, (enabled_panel_transcoders & BIT(TRANSCODER_EDP)) &&
- enabled_panel_transcoders != BIT(TRANSCODER_EDP));
+ pipe_config->cpu_transcoder = ffs(enabled_transcoders) - 1;
power_domain = POWER_DOMAIN_TRANSCODER(pipe_config->cpu_transcoder);
drm_WARN_ON(dev, *power_domain_mask & BIT_ULL(power_domain));
@@ -11127,6 +11182,13 @@ static bool hsw_get_transcoder_state(struct intel_crtc *crtc,
if (!wf)
return false;
+ if (hsw_panel_transcoders(dev_priv) & BIT(pipe_config->cpu_transcoder)) {
+ tmp = intel_de_read(dev_priv, TRANS_DDI_FUNC_CTL(pipe_config->cpu_transcoder));
+
+ if ((tmp & TRANS_DDI_EDP_INPUT_MASK) == TRANS_DDI_EDP_INPUT_A_ONOFF)
+ pipe_config->pch_pfit.force_thru = true;
+ }
+
wakerefs[power_domain] = wf;
*power_domain_mask |= BIT_ULL(power_domain);