[Intel-gfx] [PATCH 12/14] drm/i915: Add dkl phy pll calculations

José Roberto de Souza jose.souza at intel.com
Fri Sep 13 22:32:49 UTC 2019


The _pll_find_divisors and _calc_dkl_pll_state TGL versions are
similar to ICL ones but the BSpec algorithm conversion from real to
integer number plus the differences makes it easier and safer to
implement it in new functions.

BSpec: 49204

Signed-off-by: Vandita Kulkarni <vandita.kulkarni at intel.com>
Signed-off-by: José Roberto de Souza <jose.souza at intel.com>
---
 drivers/gpu/drm/i915/display/intel_ddi.c      |  29 ++-
 drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 205 +++++++++++++++++-
 2 files changed, 227 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index 981e24120a87..521e5b2e6f1c 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -1436,11 +1436,30 @@ static int icl_calc_mg_pll_link(struct drm_i915_private *dev_priv,
 
 	ref_clock = dev_priv->cdclk.hw.ref;
 
-	m1 = pll_state->mg_pll_div1 & MG_PLL_DIV1_FBPREDIV_MASK;
-	m2_int = pll_state->mg_pll_div0 & MG_PLL_DIV0_FBDIV_INT_MASK;
-	m2_frac = (pll_state->mg_pll_div0 & MG_PLL_DIV0_FRACNEN_H) ?
-		(pll_state->mg_pll_div0 & MG_PLL_DIV0_FBDIV_FRAC_MASK) >>
-		MG_PLL_DIV0_FBDIV_FRAC_SHIFT : 0;
+	if (INTEL_GEN(dev_priv) >= 12) {
+		m1 = pll_state->mg_pll_div0 & DKL_PLL_DIV0_FBPREDIV_MASK;
+		m1 = m1 >> DKL_PLL_DIV0_FBPREDIV_SHIFT;
+		m2_int = pll_state->mg_pll_div0 & DKL_PLL_DIV0_FBDIV_INT_MASK;
+
+		if (pll_state->mg_pll_bias & DKL_PLL_BIAS_FRAC_EN_H) {
+			m2_frac = pll_state->mg_pll_bias &
+				  DKL_PLL_BIAS_FBDIV_FRAC_MASK;
+			m2_frac = m2_frac >> DKL_PLL_BIAS_FBDIV_SHIFT;
+		} else {
+			m2_frac = 0;
+		}
+	} else {
+		m1 = pll_state->mg_pll_div1 & MG_PLL_DIV1_FBPREDIV_MASK;
+		m2_int = pll_state->mg_pll_div0 & MG_PLL_DIV0_FBDIV_INT_MASK;
+
+		if (pll_state->mg_pll_div0 & MG_PLL_DIV0_FRACNEN_H) {
+			m2_frac = pll_state->mg_pll_div0 &
+				  MG_PLL_DIV0_FBDIV_FRAC_MASK;
+			m2_frac = m2_frac >> MG_PLL_DIV0_FBDIV_FRAC_SHIFT;
+		} else {
+			m2_frac = 0;
+		}
+	}
 
 	switch (pll_state->mg_clktop2_hsclkctl &
 		MG_CLKTOP2_HSCLKCTL_HSDIV_RATIO_MASK) {
diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
index 9304606c1c0a..25be6229b122 100644
--- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
+++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
@@ -2605,6 +2605,202 @@ enum intel_dpll_id icl_tc_port_to_pll_id(enum tc_port tc_port)
 	return tc_port + DPLL_ID_ICL_MGPLL1;
 }
 
+static bool tgl_dkl_pll_find_divisors(int clock_khz, bool is_dp, bool use_ssc,
+				      u32 *target_dco_khz,
+				      struct intel_dpll_hw_state *state)
+{
+	u32 dco_min_freq, dco_max_freq;
+	int div1_vals[] = {7, 5, 3, 2};
+	int i, div2;
+
+	dco_min_freq = is_dp ? 8100000 : use_ssc ? 8000000 : 7992000;
+	dco_max_freq = is_dp ? 8100000 : 10000000;
+
+	for (i = 0; i < ARRAY_SIZE(div1_vals); i++) {
+		int div1 = div1_vals[i];
+
+		for (div2 = 10; div2 > 0; div2--) {
+			int dco = div1 * div2 * clock_khz * 5;
+			int inputsel, tlinedrv;
+			u32 hsdiv;
+
+			if (dco < dco_min_freq || dco > dco_max_freq)
+				continue;
+
+			state->mg_refclkin_ctl = MG_REFCLKIN_CTL_OD_2_MUX(1);
+			state->mg_clktop2_coreclkctl1 =
+					MG_CLKTOP2_CORECLKCTL1_A_DIVRATIO(5);
+
+			tlinedrv = div2 >= 2 ? 1 : 2;
+			inputsel = is_dp ? 0 : 1;
+
+			switch (div1) {
+			default:
+				MISSING_CASE(div1);
+				/* fall through */
+			case 2:
+				hsdiv = MG_CLKTOP2_HSCLKCTL_HSDIV_RATIO_2;
+				break;
+			case 3:
+				hsdiv = MG_CLKTOP2_HSCLKCTL_HSDIV_RATIO_3;
+				break;
+			case 5:
+				hsdiv = MG_CLKTOP2_HSCLKCTL_HSDIV_RATIO_5;
+				break;
+			case 7:
+				hsdiv = MG_CLKTOP2_HSCLKCTL_HSDIV_RATIO_7;
+				break;
+			}
+
+			*target_dco_khz = dco;
+
+			state->mg_clktop2_hsclkctl =
+				MG_CLKTOP2_HSCLKCTL_TLINEDRV_CLKSEL(tlinedrv) |
+				MG_CLKTOP2_HSCLKCTL_CORE_INPUTSEL(inputsel) |
+				hsdiv |
+				MG_CLKTOP2_HSCLKCTL_DSDIV_RATIO(div2);
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * The specification for this function uses real numbers, so the math had to be
+ * adapted to integer-only calculation, that's why it looks so different.
+ */
+static bool tgl_calc_dkl_pll_state(struct intel_crtc_state *crtc_state,
+				   struct intel_dpll_hw_state *pll_state)
+{
+	struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
+	int refclk_khz = dev_priv->cdclk.hw.ref;
+	int symbol_frequency = crtc_state->port_clock;
+	u32 dco_khz, m1div, m2div_int, m2div_rem, m2div_frac;
+	u32 iref_ndiv, iref_trim;
+	u32 prop_coeff, int_coeff;
+	u32 tdc_targetcnt, feedfwgain;
+	u64 ssc_stepsize, ssc_steplen, ssc_steplog;
+	u64 tmp;
+	bool use_ssc = true;
+	bool is_dp = !intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI);
+
+	memset(pll_state, 0, sizeof(*pll_state));
+
+	if (!tgl_dkl_pll_find_divisors(symbol_frequency, is_dp, use_ssc,
+				       &dco_khz, pll_state)) {
+		DRM_DEBUG_KMS("Failed to find divisors for clock %d\n",
+			      symbol_frequency);
+		return false;
+	}
+
+	m1div = 2;
+	m2div_int = dco_khz / (refclk_khz * m1div);
+	if (m2div_int > 255) {
+		DRM_DEBUG_KMS("Failed to find mdiv for clock %d\n",
+			      symbol_frequency);
+		return false;
+	}
+	m2div_rem = dco_khz % (refclk_khz * m1div);
+
+	tmp = (u64)m2div_rem * (1 << 22);
+	do_div(tmp, refclk_khz * m1div);
+	m2div_frac = tmp;
+
+	switch (refclk_khz) {
+	case 19200:
+		iref_ndiv = 1;
+		break;
+	case 24000:
+		iref_ndiv = 1;
+		break;
+	case 38400:
+		iref_ndiv = 2;
+		break;
+	default:
+		MISSING_CASE(refclk_khz);
+		return false;
+	}
+
+	/*
+	 * tdc_res = 0.000003
+	 * tdc_targetcnt = int(2 / (tdc_res * 8 * 50 * 1.1) / refclk_mhz + 0.5)
+	 *
+	 * The multiplication by 1000 is due to refclk MHz to KHz conversion. It
+	 * was supposed to be a division, but we rearranged the operations of
+	 * the formula to avoid early divisions so we don't multiply the
+	 * rounding errors.
+	 *
+	 * 0.000003 * 8 * 50 * 1.1 = 0.00132, also known as 132 / 100000, which
+	 * we also rearrange to work with integers.
+	 *
+	 * The 0.5 transformed to 5 results in a multiplication by 10 and the
+	 * last division by 10.
+	 */
+	tdc_targetcnt = (2 * 1000 * 100000 * 10 / (132 * refclk_khz) + 5) / 10;
+
+	/*
+	 * Here we divide dco_khz by 10 in order to allow the dividend to fit in
+	 * 32 bits. That's not a problem since we round the division down
+	 * anyway.
+	 */
+	feedfwgain = (use_ssc || m2div_rem > 0) ?
+		m1div * 1000000 * 100 / (dco_khz * 3 / 10) : 0;
+
+	tmp = refclk_khz / iref_ndiv;
+	if (tmp <= 19200)
+		iref_trim = 28;
+	else if (tmp <= 25000)
+		iref_trim = 25;
+	else
+		iref_trim = 24;
+
+	if (dco_khz >= 9000000) {
+		prop_coeff = 5;
+		int_coeff = 10;
+	} else {
+		prop_coeff = 4;
+		int_coeff = 8;
+	}
+
+	if (use_ssc) {
+		tmp = mul_u32_u32(dco_khz, 47 * 32);
+		do_div(tmp, refclk_khz * m1div * 10000);
+		ssc_stepsize = tmp;
+
+		tmp = mul_u32_u32(dco_khz, 1000);
+		ssc_steplen = DIV_ROUND_UP_ULL(tmp, 32 * 2 * 32);
+	} else {
+		ssc_stepsize = 0;
+		ssc_steplen = 0;
+	}
+	ssc_steplog = 4;
+
+	/* write pll_state calculations */
+	pll_state->mg_pll_div0 = DKL_PLL_DIV0_INTEG_COEFF(int_coeff) |
+				 DKL_PLL_DIV0_PROP_COEFF(prop_coeff) |
+				 DKL_PLL_DIV0_FBPREDIV(m1div) |
+				 DKL_PLL_DIV0_FBDIV_INT(m2div_int);
+
+	pll_state->mg_pll_div1 = DKL_PLL_DIV1_IREF_TRIM(iref_trim) |
+				 DKL_PLL_DIV1_TDC_TARGET_CNT(tdc_targetcnt);
+
+	pll_state->mg_pll_ssc = DKL_PLL_SSC_IREF_NDIV_RATIO(iref_ndiv) |
+				DKL_PLL_SSC_STEP_LEN(ssc_steplen) |
+				DKL_PLL_SSC_STEP_NUM(ssc_steplog) |
+				(use_ssc ? DKL_PLL_SSC_EN : 0);
+
+	pll_state->mg_pll_bias = (m2div_frac > 0 ? DKL_PLL_BIAS_FRAC_EN_H : 0) |
+				 DKL_PLL_BIAS_FBDIV_FRAC(m2div_frac);
+
+	pll_state->mg_pll_tdc_coldst_bias =
+			DKL_PLL_TDC_SSC_STEP_SIZE(ssc_stepsize) |
+			DKL_PLL_TDC_FEED_FWD_GAIN(feedfwgain);
+
+	return true;
+}
+
 static bool icl_mg_pll_find_divisors(int clock_khz, bool is_dp, bool use_ssc,
 				     u32 *target_dco_khz,
 				     struct intel_dpll_hw_state *state)
@@ -2932,6 +3128,7 @@ static bool icl_get_tc_phy_dplls(struct intel_atomic_state *state,
 		intel_atomic_get_new_crtc_state(state, crtc);
 	struct icl_port_dpll *port_dpll;
 	enum intel_dpll_id dpll_id;
+	bool r;
 
 	port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_DEFAULT];
 	if (!icl_calc_dpll_state(crtc_state, encoder, &port_dpll->hw_state)) {
@@ -2950,9 +3147,13 @@ static bool icl_get_tc_phy_dplls(struct intel_atomic_state *state,
 	intel_reference_shared_dpll(state, crtc,
 				    port_dpll->pll, &port_dpll->hw_state);
 
-
 	port_dpll = &crtc_state->icl_port_dplls[ICL_PORT_DPLL_MG_PHY];
-	if (!icl_calc_mg_pll_state(crtc_state, &port_dpll->hw_state)) {
+	if (INTEL_GEN(dev_priv) >= 12)
+		r = tgl_calc_dkl_pll_state(crtc_state, &port_dpll->hw_state);
+	else
+		r = icl_calc_mg_pll_state(crtc_state, &port_dpll->hw_state);
+
+	if (!r) {
 		DRM_DEBUG_KMS("Could not calculate MG PHY PLL state.\n");
 		goto err_unreference_tbt_pll;
 	}
-- 
2.23.0



More information about the Intel-gfx mailing list