@@ -4527,8 +4527,15 @@
#define LCPLL_CTL 0x130040
#define LCPLL_PLL_DISABLE (1<<31)
#define LCPLL_PLL_LOCK (1<<30)
+#define LCPLL_CLK_FREQ_MASK (3<<26)
+#define LCPLL_CLK_FREQ_450 (0<<26)
+#define LCPLL_CLK_FREQ_540 (1<<26)
#define LCPLL_CD_CLOCK_DISABLE (1<<25)
#define LCPLL_CD2X_CLOCK_DISABLE (1<<23)
+#define LCPLL_CD_SOURCE_FCLK (1<<21)
+
+#define CDCLK_FREQ 0x46200
+#define CDCLK_FREQ_MASK 0x3FF
/* Pipe WM_LINETIME - watermark line time */
#define PIPE_WM_LINETIME_A 0x45270
@@ -682,12 +682,6 @@ void intel_ddi_mode_set(struct drm_encoder *encoder,
DRM_DEBUG_KMS("WR PLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n",
crtc->mode.clock, p, n2, r2);
- /* Enable LCPLL if disabled */
- temp = I915_READ(LCPLL_CTL);
- if (temp & LCPLL_PLL_DISABLE)
- I915_WRITE(LCPLL_CTL,
- temp & ~LCPLL_PLL_DISABLE);
-
/* Configure WR PLL 1, program the correct divider values for
* the desired frequency and wait for warmup */
I915_WRITE(WRPLL_CTL1,
@@ -817,3 +811,43 @@ void intel_disable_ddi(struct intel_encoder *encoder)
I915_WRITE(DDI_BUF_CTL(port), temp);
}
+
+void intel_ddi_pll_init(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t lcpll_val, clk_val, temp;
+ bool lcpll_needs_change = false;
+
+ /* Check the LCPLL state and fix it if needed. */
+ lcpll_val = I915_READ(LCPLL_CTL);
+ clk_val = I915_READ(CDCLK_FREQ) & CDCLK_FREQ_MASK;
+ DRM_DEBUG_KMS("CDCLK running at %dMHz\n", clk_val + 1);
+
+ temp = lcpll_val & LCPLL_CLK_FREQ_MASK;
+ if ((clk_val == 449 && (temp != LCPLL_CLK_FREQ_450)) ||
+ (clk_val == 539 && (temp != LCPLL_CLK_FREQ_540))) {
+ DRM_ERROR("LCPLL and CDCLK frequencies don't match\n");
+ lcpll_needs_change = true;
+
+ lcpll_val &= ~LCPLL_CLK_FREQ_MASK;
+ if (clk_val == 449)
+ lcpll_val |= LCPLL_CLK_FREQ_450;
+ else
+ lcpll_val |= LCPLL_CLK_FREQ_540;
+ }
+
+ if (lcpll_val & LCPLL_CD_SOURCE_FCLK) {
+ DRM_ERROR("CDCLK source is not LCPLL\n");
+ lcpll_needs_change = true;
+ lcpll_val &= ~LCPLL_CD_SOURCE_FCLK;
+ }
+
+ if (lcpll_val & LCPLL_PLL_DISABLE) {
+ DRM_ERROR("LCPLL is disabled\n");
+ lcpll_needs_change = true;
+ lcpll_val &= ~LCPLL_PLL_DISABLE;
+ }
+
+ if (lcpll_needs_change)
+ I915_WRITE(LCPLL_CTL, lcpll_val);
+}
@@ -7477,6 +7477,12 @@ static const struct drm_crtc_funcs intel_crtc_funcs = {
.page_flip = intel_crtc_page_flip,
};
+static void intel_cpu_pll_init(struct drm_device *dev)
+{
+ if (IS_HASWELL(dev))
+ intel_ddi_pll_init(dev);
+}
+
static void intel_pch_pll_init(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
@@ -8085,6 +8091,7 @@ void intel_modeset_init(struct drm_device *dev)
DRM_DEBUG_KMS("plane %d init failed: %d\n", i, ret);
}
+ intel_cpu_pll_init(dev);
intel_pch_pll_init(dev);
/* Just disable it once at startup */
@@ -580,5 +580,6 @@ extern bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
extern void intel_ddi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
+extern void intel_ddi_pll_init(struct drm_device *dev);
#endif /* __INTEL_DRV_H__ */