@@ -5312,8 +5312,12 @@
#define SPLL_PLL_ENABLE (1<<31)
#define SPLL_PLL_SSC (1<<28)
#define SPLL_PLL_NON_SSC (2<<28)
+#define SPLL_PLL_LCPLL (3<<28)
+#define SPLL_PLL_REF_MASK (3<<28)
#define SPLL_PLL_FREQ_810MHz (0<<26)
#define SPLL_PLL_FREQ_1350MHz (1<<26)
+#define SPLL_PLL_FREQ_2700MHz (2<<26)
+#define SPLL_PLL_FREQ_MASK (3<<26)
/* WRPLL */
#define WRPLL_CTL1 0x46040
@@ -5324,8 +5328,13 @@
#define WRPLL_PLL_SELECT_LCPLL_2700 (0x03<<28)
/* WRPLL divider programming */
#define WRPLL_DIVIDER_REFERENCE(x) ((x)<<0)
+#define WRPLL_DIVIDER_REF_MASK (0xff)
#define WRPLL_DIVIDER_POST(x) ((x)<<8)
+#define WRPLL_DIVIDER_POST_MASK (0x3f<<8)
+#define WRPLL_DIVIDER_POST_SHIFT 8
#define WRPLL_DIVIDER_FEEDBACK(x) ((x)<<16)
+#define WRPLL_DIVIDER_FB_SHIFT 16
+#define WRPLL_DIVIDER_FB_MASK (0xff<<16)
/* Port clock selection */
#define PORT_CLK_SEL_A 0x46100
@@ -5338,6 +5347,7 @@
#define PORT_CLK_SEL_WRPLL1 (4<<29)
#define PORT_CLK_SEL_WRPLL2 (5<<29)
#define PORT_CLK_SEL_NONE (7<<29)
+#define PORT_CLK_SEL_MASK (7<<29)
/* Transcoder clock selection */
#define TRANS_CLK_SEL_A 0x46140
@@ -633,6 +633,96 @@ static void wrpll_update_rnp(uint64_t freq2k, unsigned budget,
/* Otherwise a < c && b >= d, do nothing */
}
+static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv,
+ int reg)
+{
+ int refclk = LC_FREQ;
+ int n, p, r;
+ u32 wrpll;
+
+ wrpll = I915_READ(reg);
+ switch (wrpll & SPLL_PLL_REF_MASK) {
+ case SPLL_PLL_SSC:
+ case SPLL_PLL_NON_SSC:
+ /*
+ * We could calculate spread here, but our checking
+ * code only cares about 5% accuracy, and spread is a max of
+ * 0.5% downspread.
+ */
+ refclk = 135;
+ break;
+ case SPLL_PLL_LCPLL:
+ refclk = LC_FREQ;
+ break;
+ default:
+ WARN(1, "bad wrpll refclk\n");
+ return 0;
+ }
+
+ r = wrpll & WRPLL_DIVIDER_REF_MASK;
+ p = (wrpll & WRPLL_DIVIDER_POST_MASK) >> WRPLL_DIVIDER_POST_SHIFT;
+ n = (wrpll & WRPLL_DIVIDER_FB_MASK) >> WRPLL_DIVIDER_FB_SHIFT;
+
+ return (LC_FREQ * n) / (p * r);
+}
+
+static void intel_ddi_clock_get(struct intel_encoder *encoder,
+ struct intel_crtc_config *pipe_config)
+{
+ struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
+ enum port port = intel_ddi_get_encoder_port(encoder);
+ int link_clock = 0;
+ u32 val, pll;
+
+ val = I915_READ(PORT_CLK_SEL(port));
+ switch (val & PORT_CLK_SEL_MASK) {
+ case PORT_CLK_SEL_LCPLL_810:
+ link_clock = 81000;
+ break;
+ case PORT_CLK_SEL_LCPLL_1350:
+ link_clock = 135000;
+ break;
+ case PORT_CLK_SEL_LCPLL_2700:
+ link_clock = 270000;
+ break;
+ case PORT_CLK_SEL_WRPLL1:
+ link_clock = intel_ddi_calc_wrpll_link(dev_priv, WRPLL_CTL1);
+ break;
+ case PORT_CLK_SEL_WRPLL2:
+ link_clock = intel_ddi_calc_wrpll_link(dev_priv, WRPLL_CTL2);
+ break;
+ case PORT_CLK_SEL_SPLL:
+ pll = I915_READ(SPLL_CTL) & SPLL_PLL_FREQ_MASK;
+ if (pll == SPLL_PLL_FREQ_810MHz)
+ link_clock = 81000;
+ else if (pll == SPLL_PLL_FREQ_1350MHz)
+ link_clock = 135000;
+ else if (pll == SPLL_PLL_FREQ_2700MHz)
+ link_clock = 270000;
+ else {
+ WARN(1, "bad spll freq\n");
+ return;
+ }
+ break;
+ default:
+ WARN(1, "bad port clock sel\n");
+ return;
+ }
+
+ pipe_config->port_clock = link_clock * 2;
+
+ if (pipe_config->has_pch_encoder)
+ pipe_config->adjusted_mode.crtc_clock =
+ intel_dotclock_calculate(pipe_config->port_clock,
+ &pipe_config->fdi_m_n);
+ else if (pipe_config->has_dp_encoder)
+ pipe_config->adjusted_mode.crtc_clock =
+ intel_dotclock_calculate(pipe_config->port_clock,
+ &pipe_config->dp_m_n);
+ else
+ pipe_config->adjusted_mode.crtc_clock = pipe_config->port_clock;
+}
+
static void
intel_ddi_calculate_wrpll(int clock /* in Hz */,
unsigned *r2_out, unsigned *n2_out, unsigned *p_out)
@@ -1510,6 +1600,8 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
pipe_config->pipe_bpp, dev_priv->vbt.edp_bpp);
dev_priv->vbt.edp_bpp = pipe_config->pipe_bpp;
}
+
+ intel_ddi_clock_get(encoder, pipe_config);
}
static void intel_ddi_destroy(struct drm_encoder *encoder)