|
@@ -4585,7 +4585,9 @@ static void intel_update_watermarks(struct drm_device *dev)
|
|
|
|
|
|
static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
|
|
|
{
|
|
|
- return dev_priv->lvds_use_ssc && i915_panel_use_ssc
|
|
|
+ if (i915_panel_use_ssc >= 0)
|
|
|
+ return i915_panel_use_ssc != 0;
|
|
|
+ return dev_priv->lvds_use_ssc
|
|
|
&& !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
|
|
|
}
|
|
|
|
|
@@ -5108,36 +5110,52 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void ironlake_update_pch_refclk(struct drm_device *dev)
|
|
|
+/*
|
|
|
+ * Initialize reference clocks when the driver loads
|
|
|
+ */
|
|
|
+void ironlake_init_pch_refclk(struct drm_device *dev)
|
|
|
{
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
struct drm_mode_config *mode_config = &dev->mode_config;
|
|
|
- struct drm_crtc *crtc;
|
|
|
struct intel_encoder *encoder;
|
|
|
- struct intel_encoder *has_edp_encoder = NULL;
|
|
|
u32 temp;
|
|
|
bool has_lvds = false;
|
|
|
+ bool has_cpu_edp = false;
|
|
|
+ bool has_pch_edp = false;
|
|
|
+ bool has_panel = false;
|
|
|
+ bool has_ck505 = false;
|
|
|
+ bool can_ssc = false;
|
|
|
|
|
|
/* We need to take the global config into account */
|
|
|
- list_for_each_entry(crtc, &mode_config->crtc_list, head) {
|
|
|
- if (!crtc->enabled)
|
|
|
- continue;
|
|
|
-
|
|
|
- list_for_each_entry(encoder, &mode_config->encoder_list,
|
|
|
- base.head) {
|
|
|
- if (encoder->base.crtc != crtc)
|
|
|
- continue;
|
|
|
-
|
|
|
- switch (encoder->type) {
|
|
|
- case INTEL_OUTPUT_LVDS:
|
|
|
- has_lvds = true;
|
|
|
- case INTEL_OUTPUT_EDP:
|
|
|
- has_edp_encoder = encoder;
|
|
|
- break;
|
|
|
- }
|
|
|
+ list_for_each_entry(encoder, &mode_config->encoder_list,
|
|
|
+ base.head) {
|
|
|
+ switch (encoder->type) {
|
|
|
+ case INTEL_OUTPUT_LVDS:
|
|
|
+ has_panel = true;
|
|
|
+ has_lvds = true;
|
|
|
+ break;
|
|
|
+ case INTEL_OUTPUT_EDP:
|
|
|
+ has_panel = true;
|
|
|
+ if (intel_encoder_is_pch_edp(&encoder->base))
|
|
|
+ has_pch_edp = true;
|
|
|
+ else
|
|
|
+ has_cpu_edp = true;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (HAS_PCH_IBX(dev)) {
|
|
|
+ has_ck505 = dev_priv->display_clock_mode;
|
|
|
+ can_ssc = has_ck505;
|
|
|
+ } else {
|
|
|
+ has_ck505 = false;
|
|
|
+ can_ssc = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ DRM_DEBUG_KMS("has_panel %d has_lvds %d has_pch_edp %d has_cpu_edp %d has_ck505 %d\n",
|
|
|
+ has_panel, has_lvds, has_pch_edp, has_cpu_edp,
|
|
|
+ has_ck505);
|
|
|
+
|
|
|
/* Ironlake: try to setup display ref clock before DPLL
|
|
|
* enabling. This is only under driver's control after
|
|
|
* PCH B stepping, previous chipset stepping should be
|
|
@@ -5146,37 +5164,62 @@ static void ironlake_update_pch_refclk(struct drm_device *dev)
|
|
|
temp = I915_READ(PCH_DREF_CONTROL);
|
|
|
/* Always enable nonspread source */
|
|
|
temp &= ~DREF_NONSPREAD_SOURCE_MASK;
|
|
|
- temp |= DREF_NONSPREAD_SOURCE_ENABLE;
|
|
|
- temp &= ~DREF_SSC_SOURCE_MASK;
|
|
|
- temp |= DREF_SSC_SOURCE_ENABLE;
|
|
|
- I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
|
|
|
- POSTING_READ(PCH_DREF_CONTROL);
|
|
|
- udelay(200);
|
|
|
+ if (has_ck505)
|
|
|
+ temp |= DREF_NONSPREAD_CK505_ENABLE;
|
|
|
+ else
|
|
|
+ temp |= DREF_NONSPREAD_SOURCE_ENABLE;
|
|
|
|
|
|
- if (has_edp_encoder) {
|
|
|
- if (intel_panel_use_ssc(dev_priv)) {
|
|
|
- temp |= DREF_SSC1_ENABLE;
|
|
|
- I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
+ if (has_panel) {
|
|
|
+ temp &= ~DREF_SSC_SOURCE_MASK;
|
|
|
+ temp |= DREF_SSC_SOURCE_ENABLE;
|
|
|
|
|
|
- POSTING_READ(PCH_DREF_CONTROL);
|
|
|
- udelay(200);
|
|
|
+ /* SSC must be turned on before enabling the CPU output */
|
|
|
+ if (intel_panel_use_ssc(dev_priv) && can_ssc) {
|
|
|
+ DRM_DEBUG_KMS("Using SSC on panel\n");
|
|
|
+ temp |= DREF_SSC1_ENABLE;
|
|
|
}
|
|
|
+
|
|
|
+ /* Get SSC going before enabling the outputs */
|
|
|
+ I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
+ POSTING_READ(PCH_DREF_CONTROL);
|
|
|
+ udelay(200);
|
|
|
+
|
|
|
temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
|
|
|
|
|
|
/* Enable CPU source on CPU attached eDP */
|
|
|
- if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
|
|
|
- if (intel_panel_use_ssc(dev_priv))
|
|
|
+ if (has_cpu_edp) {
|
|
|
+ if (intel_panel_use_ssc(dev_priv) && can_ssc) {
|
|
|
+ DRM_DEBUG_KMS("Using SSC on eDP\n");
|
|
|
temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
|
|
|
+ }
|
|
|
else
|
|
|
temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
|
|
|
- } else {
|
|
|
- /* Enable SSC on PCH eDP if needed */
|
|
|
- if (intel_panel_use_ssc(dev_priv)) {
|
|
|
- DRM_ERROR("enabling SSC on PCH\n");
|
|
|
- temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
|
|
|
- }
|
|
|
- }
|
|
|
+ } else
|
|
|
+ temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
|
|
|
+
|
|
|
+ I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
+ POSTING_READ(PCH_DREF_CONTROL);
|
|
|
+ udelay(200);
|
|
|
+ } else {
|
|
|
+ DRM_DEBUG_KMS("Disabling SSC entirely\n");
|
|
|
+
|
|
|
+ temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
|
|
|
+
|
|
|
+ /* Turn off CPU output */
|
|
|
+ temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
|
|
|
+
|
|
|
+ I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
+ POSTING_READ(PCH_DREF_CONTROL);
|
|
|
+ udelay(200);
|
|
|
+
|
|
|
+ /* Turn off the SSC source */
|
|
|
+ temp &= ~DREF_SSC_SOURCE_MASK;
|
|
|
+ temp |= DREF_SSC_SOURCE_DISABLE;
|
|
|
+
|
|
|
+ /* Turn off SSC1 */
|
|
|
+ temp &= ~ DREF_SSC1_ENABLE;
|
|
|
+
|
|
|
I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
POSTING_READ(PCH_DREF_CONTROL);
|
|
|
udelay(200);
|
|
@@ -5242,16 +5285,10 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
num_connectors++;
|
|
|
}
|
|
|
|
|
|
- if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
|
|
|
- refclk = dev_priv->lvds_ssc_freq * 1000;
|
|
|
- DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
|
|
|
- refclk / 1000);
|
|
|
- } else {
|
|
|
- refclk = 96000;
|
|
|
- if (!has_edp_encoder ||
|
|
|
- intel_encoder_is_pch_edp(&has_edp_encoder->base))
|
|
|
- refclk = 120000; /* 120Mhz refclk */
|
|
|
- }
|
|
|
+ /*
|
|
|
+ * Every reference clock in a PCH system is 120MHz
|
|
|
+ */
|
|
|
+ refclk = 120000;
|
|
|
|
|
|
/*
|
|
|
* Returns a set of divisors for the desired target clock with the given
|
|
@@ -5378,8 +5415,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw,
|
|
|
&m_n);
|
|
|
|
|
|
- ironlake_update_pch_refclk(dev);
|
|
|
-
|
|
|
fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
|
|
|
if (has_reduced_clock)
|
|
|
fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 |
|
|
@@ -7376,6 +7411,9 @@ static void intel_setup_outputs(struct drm_device *dev)
|
|
|
|
|
|
/* disable all the possible outputs/crtcs before entering KMS mode */
|
|
|
drm_helper_disable_unused_functions(dev);
|
|
|
+
|
|
|
+ if (HAS_PCH_SPLIT(dev))
|
|
|
+ ironlake_init_pch_refclk(dev);
|
|
|
}
|
|
|
|
|
|
static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
|