drm/i915/icl+: Sanitize port to PLL mapping
[sfrench/cifs-2.6.git] / drivers / gpu / drm / i915 / intel_ddi.c
index 8761513f3532c5c4bfb56151833ab6217a5ab99f..3eea987e909b1f39ca4dbe907a3b3305cac56024 100644 (file)
@@ -642,7 +642,7 @@ skl_get_buf_trans_dp(struct drm_i915_private *dev_priv, int *n_entries)
 static const struct ddi_buf_trans *
 kbl_get_buf_trans_dp(struct drm_i915_private *dev_priv, int *n_entries)
 {
-       if (IS_KBL_ULX(dev_priv)) {
+       if (IS_KBL_ULX(dev_priv) || IS_AML_ULX(dev_priv)) {
                *n_entries = ARRAY_SIZE(kbl_y_ddi_translations_dp);
                return kbl_y_ddi_translations_dp;
        } else if (IS_KBL_ULT(dev_priv) || IS_CFL_ULT(dev_priv)) {
@@ -658,7 +658,7 @@ static const struct ddi_buf_trans *
 skl_get_buf_trans_edp(struct drm_i915_private *dev_priv, int *n_entries)
 {
        if (dev_priv->vbt.edp.low_vswing) {
-               if (IS_SKL_ULX(dev_priv) || IS_KBL_ULX(dev_priv)) {
+               if (IS_SKL_ULX(dev_priv) || IS_KBL_ULX(dev_priv) || IS_AML_ULX(dev_priv)) {
                        *n_entries = ARRAY_SIZE(skl_y_ddi_translations_edp);
                        return skl_y_ddi_translations_edp;
                } else if (IS_SKL_ULT(dev_priv) || IS_KBL_ULT(dev_priv) ||
@@ -680,7 +680,7 @@ skl_get_buf_trans_edp(struct drm_i915_private *dev_priv, int *n_entries)
 static const struct ddi_buf_trans *
 skl_get_buf_trans_hdmi(struct drm_i915_private *dev_priv, int *n_entries)
 {
-       if (IS_SKL_ULX(dev_priv) || IS_KBL_ULX(dev_priv)) {
+       if (IS_SKL_ULX(dev_priv) || IS_KBL_ULX(dev_priv) || IS_AML_ULX(dev_priv)) {
                *n_entries = ARRAY_SIZE(skl_y_ddi_translations_hdmi);
                return skl_y_ddi_translations_hdmi;
        } else {
@@ -916,7 +916,7 @@ static int intel_ddi_hdmi_level(struct drm_i915_private *dev_priv, enum port por
        level = dev_priv->vbt.ddi_port_info[port].hdmi_level_shift;
 
        if (IS_ICELAKE(dev_priv)) {
-               if (port == PORT_A || port == PORT_B)
+               if (intel_port_is_combophy(dev_priv, port))
                        icl_get_combo_buf_trans(dev_priv, port,
                                                INTEL_OUTPUT_HDMI, &n_entries);
                else
@@ -1060,10 +1060,10 @@ static uint32_t hsw_pll_to_ddi_pll_sel(const struct intel_shared_dpll *pll)
 }
 
 static uint32_t icl_pll_to_ddi_pll_sel(struct intel_encoder *encoder,
-                                      const struct intel_shared_dpll *pll)
+                                      const struct intel_crtc_state *crtc_state)
 {
-       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
-       int clock = crtc->config->port_clock;
+       const struct intel_shared_dpll *pll = crtc_state->shared_dpll;
+       int clock = crtc_state->port_clock;
        const enum intel_dpll_id id = pll->info->id;
 
        switch (id) {
@@ -1414,7 +1414,7 @@ static int cnl_calc_wrpll_link(struct drm_i915_private *dev_priv,
                break;
        }
 
-       ref_clock = dev_priv->cdclk.hw.ref;
+       ref_clock = cnl_hdmi_pll_ref_clock(dev_priv);
 
        dco_freq = (cfgcr0 & DPLL_CFGCR0_DCO_INTEGER_MASK) * ref_clock;
 
@@ -1427,6 +1427,81 @@ static int cnl_calc_wrpll_link(struct drm_i915_private *dev_priv,
        return dco_freq / (p0 * p1 * p2 * 5);
 }
 
+static int icl_calc_tbt_pll_link(struct drm_i915_private *dev_priv,
+                                enum port port)
+{
+       u32 val = I915_READ(DDI_CLK_SEL(port)) & DDI_CLK_SEL_MASK;
+
+       switch (val) {
+       case DDI_CLK_SEL_NONE:
+               return 0;
+       case DDI_CLK_SEL_TBT_162:
+               return 162000;
+       case DDI_CLK_SEL_TBT_270:
+               return 270000;
+       case DDI_CLK_SEL_TBT_540:
+               return 540000;
+       case DDI_CLK_SEL_TBT_810:
+               return 810000;
+       default:
+               MISSING_CASE(val);
+               return 0;
+       }
+}
+
+static int icl_calc_mg_pll_link(struct drm_i915_private *dev_priv,
+                               enum port port)
+{
+       u32 mg_pll_div0, mg_clktop_hsclkctl;
+       u32 m1, m2_int, m2_frac, div1, div2, refclk;
+       u64 tmp;
+
+       refclk = dev_priv->cdclk.hw.ref;
+
+       mg_pll_div0 = I915_READ(MG_PLL_DIV0(port));
+       mg_clktop_hsclkctl = I915_READ(MG_CLKTOP2_HSCLKCTL(port));
+
+       m1 = I915_READ(MG_PLL_DIV1(port)) & MG_PLL_DIV1_FBPREDIV_MASK;
+       m2_int = mg_pll_div0 & MG_PLL_DIV0_FBDIV_INT_MASK;
+       m2_frac = (mg_pll_div0 & MG_PLL_DIV0_FRACNEN_H) ?
+                 (mg_pll_div0 & MG_PLL_DIV0_FBDIV_FRAC_MASK) >>
+                 MG_PLL_DIV0_FBDIV_FRAC_SHIFT : 0;
+
+       switch (mg_clktop_hsclkctl & MG_CLKTOP2_HSCLKCTL_HSDIV_RATIO_MASK) {
+       case MG_CLKTOP2_HSCLKCTL_HSDIV_RATIO_2:
+               div1 = 2;
+               break;
+       case MG_CLKTOP2_HSCLKCTL_HSDIV_RATIO_3:
+               div1 = 3;
+               break;
+       case MG_CLKTOP2_HSCLKCTL_HSDIV_RATIO_5:
+               div1 = 5;
+               break;
+       case MG_CLKTOP2_HSCLKCTL_HSDIV_RATIO_7:
+               div1 = 7;
+               break;
+       default:
+               MISSING_CASE(mg_clktop_hsclkctl);
+               return 0;
+       }
+
+       div2 = (mg_clktop_hsclkctl & MG_CLKTOP2_HSCLKCTL_DSDIV_RATIO_MASK) >>
+               MG_CLKTOP2_HSCLKCTL_DSDIV_RATIO_SHIFT;
+       /* div2 value of 0 is same as 1 means no div */
+       if (div2 == 0)
+               div2 = 1;
+
+       /*
+        * Adjust the original formula to delay the division by 2^22 in order to
+        * minimize possible rounding errors.
+        */
+       tmp = (u64)m1 * m2_int * refclk +
+             (((u64)m1 * m2_frac * refclk) >> 22);
+       tmp = div_u64(tmp, 5 * div1 * div2);
+
+       return tmp;
+}
+
 static void ddi_dotclock_get(struct intel_crtc_state *pipe_config)
 {
        int dotclock;
@@ -1442,7 +1517,7 @@ static void ddi_dotclock_get(struct intel_crtc_state *pipe_config)
        else
                dotclock = pipe_config->port_clock;
 
-       if (pipe_config->ycbcr420)
+       if (pipe_config->output_format == INTEL_OUTPUT_FORMAT_YCBCR420)
                dotclock *= 2;
 
        if (pipe_config->pixel_multiplier)
@@ -1460,15 +1535,17 @@ static void icl_ddi_clock_get(struct intel_encoder *encoder,
        uint32_t pll_id;
 
        pll_id = intel_get_shared_dpll_id(dev_priv, pipe_config->shared_dpll);
-       if (port == PORT_A || port == PORT_B) {
+       if (intel_port_is_combophy(dev_priv, port)) {
                if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_HDMI))
                        link_clock = cnl_calc_wrpll_link(dev_priv, pll_id);
                else
                        link_clock = icl_calc_dp_combo_pll_link(dev_priv,
                                                                pll_id);
        } else {
-               /* FIXME - Add for MG PLL */
-               WARN(1, "MG PLL clock_get code not implemented yet\n");
+               if (pll_id == DPLL_ID_ICL_TBTPLL)
+                       link_clock = icl_calc_tbt_pll_link(dev_priv, port);
+               else
+                       link_clock = icl_calc_mg_pll_link(dev_priv, port);
        }
 
        pipe_config->port_clock = link_clock;
@@ -1660,16 +1737,16 @@ static void intel_ddi_clock_get(struct intel_encoder *encoder,
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
 
-       if (INTEL_GEN(dev_priv) <= 8)
-               hsw_ddi_clock_get(encoder, pipe_config);
-       else if (IS_GEN9_BC(dev_priv))
-               skl_ddi_clock_get(encoder, pipe_config);
-       else if (IS_GEN9_LP(dev_priv))
-               bxt_ddi_clock_get(encoder, pipe_config);
+       if (IS_ICELAKE(dev_priv))
+               icl_ddi_clock_get(encoder, pipe_config);
        else if (IS_CANNONLAKE(dev_priv))
                cnl_ddi_clock_get(encoder, pipe_config);
-       else if (IS_ICELAKE(dev_priv))
-               icl_ddi_clock_get(encoder, pipe_config);
+       else if (IS_GEN9_LP(dev_priv))
+               bxt_ddi_clock_get(encoder, pipe_config);
+       else if (IS_GEN9_BC(dev_priv))
+               skl_ddi_clock_get(encoder, pipe_config);
+       else if (INTEL_GEN(dev_priv) <= 8)
+               hsw_ddi_clock_get(encoder, pipe_config);
 }
 
 void intel_ddi_set_pipe_settings(const struct intel_crtc_state *crtc_state)
@@ -1707,6 +1784,13 @@ void intel_ddi_set_pipe_settings(const struct intel_crtc_state *crtc_state)
                break;
        }
 
+       /*
+        * As per DP 1.2 spec section 2.3.4.3 while sending
+        * YCBCR 444 signals we should program MSA MISC1/0 fields with
+        * colorspace information. The output colorspace encoding is BT601.
+        */
+       if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444)
+               temp |= TRANS_MSA_SAMPLING_444 | TRANS_MSA_CLRSP_YCBCR;
        I915_WRITE(TRANS_MSA_MISC(cpu_transcoder), temp);
 }
 
@@ -1998,9 +2082,9 @@ out:
 }
 
 static inline enum intel_display_power_domain
-intel_ddi_main_link_aux_domain(struct intel_dp *intel_dp)
+intel_ddi_main_link_aux_domain(struct intel_digital_port *dig_port)
 {
-       /* CNL HW requires corresponding AUX IOs to be powered up for PSR with
+       /* CNL+ HW requires corresponding AUX IOs to be powered up for PSR with
         * DC states enabled at the same time, while for driver initiated AUX
         * transfers we need the same AUX IOs to be powered but with DC states
         * disabled. Accordingly use the AUX power domain here which leaves DC
@@ -2012,13 +2096,14 @@ intel_ddi_main_link_aux_domain(struct intel_dp *intel_dp)
         * Note that PSR is enabled only on Port A even though this function
         * returns the correct domain for other ports too.
         */
-       return intel_dp->aux_ch == AUX_CH_A ? POWER_DOMAIN_AUX_IO_A :
-                                             intel_dp->aux_power_domain;
+       return dig_port->aux_ch == AUX_CH_A ? POWER_DOMAIN_AUX_IO_A :
+                                             intel_aux_power_domain(dig_port);
 }
 
 static u64 intel_ddi_get_power_domains(struct intel_encoder *encoder,
                                       struct intel_crtc_state *crtc_state)
 {
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        struct intel_digital_port *dig_port;
        u64 domains;
 
@@ -2033,12 +2118,13 @@ static u64 intel_ddi_get_power_domains(struct intel_encoder *encoder,
        dig_port = enc_to_dig_port(&encoder->base);
        domains = BIT_ULL(dig_port->ddi_io_power_domain);
 
-       /* AUX power is only needed for (e)DP mode, not for HDMI. */
-       if (intel_crtc_has_dp_encoder(crtc_state)) {
-               struct intel_dp *intel_dp = &dig_port->dp;
-
-               domains |= BIT_ULL(intel_ddi_main_link_aux_domain(intel_dp));
-       }
+       /*
+        * AUX power is only needed for (e)DP mode, and for HDMI mode on TC
+        * ports.
+        */
+       if (intel_crtc_has_dp_encoder(crtc_state) ||
+           intel_port_is_tc(dev_priv, encoder->port))
+               domains |= BIT_ULL(intel_ddi_main_link_aux_domain(dig_port));
 
        return domains;
 }
@@ -2158,7 +2244,7 @@ u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder)
        int n_entries;
 
        if (IS_ICELAKE(dev_priv)) {
-               if (port == PORT_A || port == PORT_B)
+               if (intel_port_is_combophy(dev_priv, port))
                        icl_get_combo_buf_trans(dev_priv, port, encoder->type,
                                                &n_entries);
                else
@@ -2468,16 +2554,137 @@ static void icl_combo_phy_ddi_vswing_sequence(struct intel_encoder *encoder,
        I915_WRITE(ICL_PORT_TX_DW5_GRP(port), val);
 }
 
-static void icl_ddi_vswing_sequence(struct intel_encoder *encoder, u32 level,
+static void icl_mg_phy_ddi_vswing_sequence(struct intel_encoder *encoder,
+                                          int link_clock,
+                                          u32 level)
+{
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       enum port port = encoder->port;
+       const struct icl_mg_phy_ddi_buf_trans *ddi_translations;
+       u32 n_entries, val;
+       int ln;
+
+       n_entries = ARRAY_SIZE(icl_mg_phy_ddi_translations);
+       ddi_translations = icl_mg_phy_ddi_translations;
+       /* The table does not have values for level 3 and level 9. */
+       if (level >= n_entries || level == 3 || level == 9) {
+               DRM_DEBUG_KMS("DDI translation not found for level %d. Using %d instead.",
+                             level, n_entries - 2);
+               level = n_entries - 2;
+       }
+
+       /* Set MG_TX_LINK_PARAMS cri_use_fs32 to 0. */
+       for (ln = 0; ln < 2; ln++) {
+               val = I915_READ(MG_TX1_LINK_PARAMS(port, ln));
+               val &= ~CRI_USE_FS32;
+               I915_WRITE(MG_TX1_LINK_PARAMS(port, ln), val);
+
+               val = I915_READ(MG_TX2_LINK_PARAMS(port, ln));
+               val &= ~CRI_USE_FS32;
+               I915_WRITE(MG_TX2_LINK_PARAMS(port, ln), val);
+       }
+
+       /* Program MG_TX_SWINGCTRL with values from vswing table */
+       for (ln = 0; ln < 2; ln++) {
+               val = I915_READ(MG_TX1_SWINGCTRL(port, ln));
+               val &= ~CRI_TXDEEMPH_OVERRIDE_17_12_MASK;
+               val |= CRI_TXDEEMPH_OVERRIDE_17_12(
+                       ddi_translations[level].cri_txdeemph_override_17_12);
+               I915_WRITE(MG_TX1_SWINGCTRL(port, ln), val);
+
+               val = I915_READ(MG_TX2_SWINGCTRL(port, ln));
+               val &= ~CRI_TXDEEMPH_OVERRIDE_17_12_MASK;
+               val |= CRI_TXDEEMPH_OVERRIDE_17_12(
+                       ddi_translations[level].cri_txdeemph_override_17_12);
+               I915_WRITE(MG_TX2_SWINGCTRL(port, ln), val);
+       }
+
+       /* Program MG_TX_DRVCTRL with values from vswing table */
+       for (ln = 0; ln < 2; ln++) {
+               val = I915_READ(MG_TX1_DRVCTRL(port, ln));
+               val &= ~(CRI_TXDEEMPH_OVERRIDE_11_6_MASK |
+                        CRI_TXDEEMPH_OVERRIDE_5_0_MASK);
+               val |= CRI_TXDEEMPH_OVERRIDE_5_0(
+                       ddi_translations[level].cri_txdeemph_override_5_0) |
+                       CRI_TXDEEMPH_OVERRIDE_11_6(
+                               ddi_translations[level].cri_txdeemph_override_11_6) |
+                       CRI_TXDEEMPH_OVERRIDE_EN;
+               I915_WRITE(MG_TX1_DRVCTRL(port, ln), val);
+
+               val = I915_READ(MG_TX2_DRVCTRL(port, ln));
+               val &= ~(CRI_TXDEEMPH_OVERRIDE_11_6_MASK |
+                        CRI_TXDEEMPH_OVERRIDE_5_0_MASK);
+               val |= CRI_TXDEEMPH_OVERRIDE_5_0(
+                       ddi_translations[level].cri_txdeemph_override_5_0) |
+                       CRI_TXDEEMPH_OVERRIDE_11_6(
+                               ddi_translations[level].cri_txdeemph_override_11_6) |
+                       CRI_TXDEEMPH_OVERRIDE_EN;
+               I915_WRITE(MG_TX2_DRVCTRL(port, ln), val);
+
+               /* FIXME: Program CRI_LOADGEN_SEL after the spec is updated */
+       }
+
+       /*
+        * Program MG_CLKHUB<LN, port being used> with value from frequency table
+        * In case of Legacy mode on MG PHY, both TX1 and TX2 enabled so use the
+        * values from table for which TX1 and TX2 enabled.
+        */
+       for (ln = 0; ln < 2; ln++) {
+               val = I915_READ(MG_CLKHUB(port, ln));
+               if (link_clock < 300000)
+                       val |= CFG_LOW_RATE_LKREN_EN;
+               else
+                       val &= ~CFG_LOW_RATE_LKREN_EN;
+               I915_WRITE(MG_CLKHUB(port, ln), val);
+       }
+
+       /* Program the MG_TX_DCC<LN, port being used> based on the link frequency */
+       for (ln = 0; ln < 2; ln++) {
+               val = I915_READ(MG_TX1_DCC(port, ln));
+               val &= ~CFG_AMI_CK_DIV_OVERRIDE_VAL_MASK;
+               if (link_clock <= 500000) {
+                       val &= ~CFG_AMI_CK_DIV_OVERRIDE_EN;
+               } else {
+                       val |= CFG_AMI_CK_DIV_OVERRIDE_EN |
+                               CFG_AMI_CK_DIV_OVERRIDE_VAL(1);
+               }
+               I915_WRITE(MG_TX1_DCC(port, ln), val);
+
+               val = I915_READ(MG_TX2_DCC(port, ln));
+               val &= ~CFG_AMI_CK_DIV_OVERRIDE_VAL_MASK;
+               if (link_clock <= 500000) {
+                       val &= ~CFG_AMI_CK_DIV_OVERRIDE_EN;
+               } else {
+                       val |= CFG_AMI_CK_DIV_OVERRIDE_EN |
+                               CFG_AMI_CK_DIV_OVERRIDE_VAL(1);
+               }
+               I915_WRITE(MG_TX2_DCC(port, ln), val);
+       }
+
+       /* Program MG_TX_PISO_READLOAD with values from vswing table */
+       for (ln = 0; ln < 2; ln++) {
+               val = I915_READ(MG_TX1_PISO_READLOAD(port, ln));
+               val |= CRI_CALCINIT;
+               I915_WRITE(MG_TX1_PISO_READLOAD(port, ln), val);
+
+               val = I915_READ(MG_TX2_PISO_READLOAD(port, ln));
+               val |= CRI_CALCINIT;
+               I915_WRITE(MG_TX2_PISO_READLOAD(port, ln), val);
+       }
+}
+
+static void icl_ddi_vswing_sequence(struct intel_encoder *encoder,
+                                   int link_clock,
+                                   u32 level,
                                    enum intel_output_type type)
 {
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        enum port port = encoder->port;
 
-       if (port == PORT_A || port == PORT_B)
+       if (intel_port_is_combophy(dev_priv, port))
                icl_combo_phy_ddi_vswing_sequence(encoder, level, type);
        else
-               /* Not Implemented Yet */
-               WARN_ON(1);
+               icl_mg_phy_ddi_vswing_sequence(encoder, link_clock, level);
 }
 
 static uint32_t translate_signal_level(int signal_levels)
@@ -2512,7 +2719,8 @@ u32 bxt_signal_levels(struct intel_dp *intel_dp)
        int level = intel_ddi_dp_level(intel_dp);
 
        if (IS_ICELAKE(dev_priv))
-               icl_ddi_vswing_sequence(encoder, level, encoder->type);
+               icl_ddi_vswing_sequence(encoder, intel_dp->link_rate,
+                                       level, encoder->type);
        else if (IS_CANNONLAKE(dev_priv))
                cnl_ddi_vswing_sequence(encoder, level, encoder->type);
        else
@@ -2534,6 +2742,21 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp)
        return DDI_BUF_TRANS_SELECT(level);
 }
 
+static inline
+uint32_t icl_dpclka_cfgcr0_clk_off(struct drm_i915_private *dev_priv,
+                                  enum port port)
+{
+       if (intel_port_is_combophy(dev_priv, port)) {
+               return ICL_DPCLKA_CFGCR0_DDI_CLK_OFF(port);
+       } else if (intel_port_is_tc(dev_priv, port)) {
+               enum tc_port tc_port = intel_port_to_tc(dev_priv, port);
+
+               return ICL_DPCLKA_CFGCR0_TC_CLK_OFF(tc_port);
+       }
+
+       return 0;
+}
+
 void icl_map_plls_to_ports(struct drm_crtc *crtc,
                           struct intel_crtc_state *crtc_state,
                           struct drm_atomic_state *old_state)
@@ -2557,16 +2780,16 @@ void icl_map_plls_to_ports(struct drm_crtc *crtc,
                mutex_lock(&dev_priv->dpll_lock);
 
                val = I915_READ(DPCLKA_CFGCR0_ICL);
-               WARN_ON((val & DPCLKA_CFGCR0_DDI_CLK_OFF(port)) == 0);
+               WARN_ON((val & icl_dpclka_cfgcr0_clk_off(dev_priv, port)) == 0);
 
-               if (port == PORT_A || port == PORT_B) {
+               if (intel_port_is_combophy(dev_priv, port)) {
                        val &= ~DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(port);
                        val |= DPCLKA_CFGCR0_DDI_CLK_SEL(pll->info->id, port);
                        I915_WRITE(DPCLKA_CFGCR0_ICL, val);
                        POSTING_READ(DPCLKA_CFGCR0_ICL);
                }
 
-               val &= ~DPCLKA_CFGCR0_DDI_CLK_OFF(port);
+               val &= ~icl_dpclka_cfgcr0_clk_off(dev_priv, port);
                I915_WRITE(DPCLKA_CFGCR0_ICL, val);
 
                mutex_unlock(&dev_priv->dpll_lock);
@@ -2594,17 +2817,41 @@ void icl_unmap_plls_to_ports(struct drm_crtc *crtc,
                mutex_lock(&dev_priv->dpll_lock);
                I915_WRITE(DPCLKA_CFGCR0_ICL,
                           I915_READ(DPCLKA_CFGCR0_ICL) |
-                          DPCLKA_CFGCR0_DDI_CLK_OFF(port));
+                          icl_dpclka_cfgcr0_clk_off(dev_priv, port));
                mutex_unlock(&dev_priv->dpll_lock);
        }
 }
 
+void icl_sanitize_encoder_pll_mapping(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       u32 val = I915_READ(DPCLKA_CFGCR0_ICL);
+       enum port port = encoder->port;
+       bool clk_enabled = !(val & icl_dpclka_cfgcr0_clk_off(dev_priv, port));
+
+       if (clk_enabled == !!encoder->base.crtc)
+               return;
+
+       /*
+        * Punt on the case now where clock is disabled, but the encoder is
+        * enabled, something else is really broken then.
+        */
+       if (WARN_ON(!clk_enabled))
+               return;
+
+       DRM_NOTE("Port %c is disabled but it has a mapped PLL, unmap it\n",
+                port_name(port));
+       val |= icl_dpclka_cfgcr0_clk_off(dev_priv, port);
+       I915_WRITE(DPCLKA_CFGCR0_ICL, val);
+}
+
 static void intel_ddi_clk_select(struct intel_encoder *encoder,
-                                const struct intel_shared_dpll *pll)
+                                const struct intel_crtc_state *crtc_state)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        enum port port = encoder->port;
        uint32_t val;
+       const struct intel_shared_dpll *pll = crtc_state->shared_dpll;
 
        if (WARN_ON(!pll))
                return;
@@ -2612,9 +2859,9 @@ static void intel_ddi_clk_select(struct intel_encoder *encoder,
        mutex_lock(&dev_priv->dpll_lock);
 
        if (IS_ICELAKE(dev_priv)) {
-               if (port >= PORT_C)
+               if (!intel_port_is_combophy(dev_priv, port))
                        I915_WRITE(DDI_CLK_SEL(port),
-                                  icl_pll_to_ddi_pll_sel(encoder, pll));
+                                  icl_pll_to_ddi_pll_sel(encoder, crtc_state));
        } else if (IS_CANNONLAKE(dev_priv)) {
                /* Configure DPCLKA_CFGCR0 to map the DPLL to the DDI. */
                val = I915_READ(DPCLKA_CFGCR0);
@@ -2654,7 +2901,7 @@ static void intel_ddi_clk_disable(struct intel_encoder *encoder)
        enum port port = encoder->port;
 
        if (IS_ICELAKE(dev_priv)) {
-               if (port >= PORT_C)
+               if (!intel_port_is_combophy(dev_priv, port))
                        I915_WRITE(DDI_CLK_SEL(port), DDI_CLK_SEL_NONE);
        } else if (IS_CANNONLAKE(dev_priv)) {
                I915_WRITE(DPCLKA_CFGCR0, I915_READ(DPCLKA_CFGCR0) |
@@ -2680,20 +2927,21 @@ static void intel_ddi_pre_enable_dp(struct intel_encoder *encoder,
 
        WARN_ON(is_mst && (port == PORT_A || port == PORT_E));
 
-       intel_display_power_get(dev_priv,
-                               intel_ddi_main_link_aux_domain(intel_dp));
-
        intel_dp_set_link_params(intel_dp, crtc_state->port_clock,
                                 crtc_state->lane_count, is_mst);
 
        intel_edp_panel_on(intel_dp);
 
-       intel_ddi_clk_select(encoder, crtc_state->shared_dpll);
+       intel_ddi_clk_select(encoder, crtc_state);
 
        intel_display_power_get(dev_priv, dig_port->ddi_io_power_domain);
 
+       icl_program_mg_dp_mode(intel_dp);
+       icl_disable_phy_clock_gating(dig_port);
+
        if (IS_ICELAKE(dev_priv))
-               icl_ddi_vswing_sequence(encoder, level, encoder->type);
+               icl_ddi_vswing_sequence(encoder, crtc_state->port_clock,
+                                       level, encoder->type);
        else if (IS_CANNONLAKE(dev_priv))
                cnl_ddi_vswing_sequence(encoder, level, encoder->type);
        else if (IS_GEN9_LP(dev_priv))
@@ -2708,7 +2956,10 @@ static void intel_ddi_pre_enable_dp(struct intel_encoder *encoder,
        if (port != PORT_A || INTEL_GEN(dev_priv) >= 9)
                intel_dp_stop_link_train(intel_dp);
 
-       intel_ddi_enable_pipe_clock(crtc_state);
+       icl_enable_phy_clock_gating(dig_port);
+
+       if (!is_mst)
+               intel_ddi_enable_pipe_clock(crtc_state);
 }
 
 static void intel_ddi_pre_enable_hdmi(struct intel_encoder *encoder,
@@ -2723,12 +2974,13 @@ static void intel_ddi_pre_enable_hdmi(struct intel_encoder *encoder,
        struct intel_digital_port *dig_port = enc_to_dig_port(&encoder->base);
 
        intel_dp_dual_mode_set_tmds_output(intel_hdmi, true);
-       intel_ddi_clk_select(encoder, crtc_state->shared_dpll);
+       intel_ddi_clk_select(encoder, crtc_state);
 
        intel_display_power_get(dev_priv, dig_port->ddi_io_power_domain);
 
        if (IS_ICELAKE(dev_priv))
-               icl_ddi_vswing_sequence(encoder, level, INTEL_OUTPUT_HDMI);
+               icl_ddi_vswing_sequence(encoder, crtc_state->port_clock,
+                                       level, INTEL_OUTPUT_HDMI);
        else if (IS_CANNONLAKE(dev_priv))
                cnl_ddi_vswing_sequence(encoder, level, INTEL_OUTPUT_HDMI);
        else if (IS_GEN9_LP(dev_priv))
@@ -2741,7 +2993,7 @@ static void intel_ddi_pre_enable_hdmi(struct intel_encoder *encoder,
 
        intel_ddi_enable_pipe_clock(crtc_state);
 
-       intel_dig_port->set_infoframes(&encoder->base,
+       intel_dig_port->set_infoframes(encoder,
                                       crtc_state->has_infoframe,
                                       crtc_state, conn_state);
 }
@@ -2771,10 +3023,22 @@ static void intel_ddi_pre_enable(struct intel_encoder *encoder,
 
        intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
 
-       if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
+       if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) {
                intel_ddi_pre_enable_hdmi(encoder, crtc_state, conn_state);
-       else
+       } else {
+               struct intel_lspcon *lspcon =
+                               enc_to_intel_lspcon(&encoder->base);
+
                intel_ddi_pre_enable_dp(encoder, crtc_state, conn_state);
+               if (lspcon->active) {
+                       struct intel_digital_port *dig_port =
+                                       enc_to_dig_port(&encoder->base);
+
+                       dig_port->set_infoframes(encoder,
+                                                crtc_state->has_infoframe,
+                                                crtc_state, conn_state);
+               }
+       }
 }
 
 static void intel_disable_ddi_buf(struct intel_encoder *encoder)
@@ -2810,14 +3074,14 @@ static void intel_ddi_post_disable_dp(struct intel_encoder *encoder,
        bool is_mst = intel_crtc_has_type(old_crtc_state,
                                          INTEL_OUTPUT_DP_MST);
 
-       intel_ddi_disable_pipe_clock(old_crtc_state);
-
-       /*
-        * Power down sink before disabling the port, otherwise we end
-        * up getting interrupts from the sink on detecting link loss.
-        */
-       if (!is_mst)
+       if (!is_mst) {
+               intel_ddi_disable_pipe_clock(old_crtc_state);
+               /*
+                * Power down sink before disabling the port, otherwise we end
+                * up getting interrupts from the sink on detecting link loss.
+                */
                intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
+       }
 
        intel_disable_ddi_buf(encoder);
 
@@ -2827,9 +3091,6 @@ static void intel_ddi_post_disable_dp(struct intel_encoder *encoder,
        intel_display_power_put(dev_priv, dig_port->ddi_io_power_domain);
 
        intel_ddi_clk_disable(encoder);
-
-       intel_display_power_put(dev_priv,
-                               intel_ddi_main_link_aux_domain(intel_dp));
 }
 
 static void intel_ddi_post_disable_hdmi(struct intel_encoder *encoder,
@@ -2840,7 +3101,7 @@ static void intel_ddi_post_disable_hdmi(struct intel_encoder *encoder,
        struct intel_digital_port *dig_port = enc_to_dig_port(&encoder->base);
        struct intel_hdmi *intel_hdmi = &dig_port->hdmi;
 
-       dig_port->set_infoframes(&encoder->base, false,
+       dig_port->set_infoframes(encoder, false,
                                 old_crtc_state, old_conn_state);
 
        intel_ddi_disable_pipe_clock(old_crtc_state);
@@ -3060,13 +3321,76 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
                intel_disable_ddi_dp(encoder, old_crtc_state, old_conn_state);
 }
 
-static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
-                                  const struct intel_crtc_state *pipe_config,
-                                  const struct drm_connector_state *conn_state)
+static void intel_ddi_set_fia_lane_count(struct intel_encoder *encoder,
+                                        const struct intel_crtc_state *pipe_config,
+                                        enum port port)
 {
-       uint8_t mask = pipe_config->lane_lat_optim_mask;
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_digital_port *dig_port = enc_to_dig_port(&encoder->base);
+       enum tc_port tc_port = intel_port_to_tc(dev_priv, port);
+       u32 val = I915_READ(PORT_TX_DFLEXDPMLE1);
+       bool lane_reversal = dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL;
+
+       val &= ~DFLEXDPMLE1_DPMLETC_MASK(tc_port);
+       switch (pipe_config->lane_count) {
+       case 1:
+               val |= (lane_reversal) ? DFLEXDPMLE1_DPMLETC_ML3(tc_port) :
+               DFLEXDPMLE1_DPMLETC_ML0(tc_port);
+               break;
+       case 2:
+               val |= (lane_reversal) ? DFLEXDPMLE1_DPMLETC_ML3_2(tc_port) :
+               DFLEXDPMLE1_DPMLETC_ML1_0(tc_port);
+               break;
+       case 4:
+               val |= DFLEXDPMLE1_DPMLETC_ML3_0(tc_port);
+               break;
+       default:
+               MISSING_CASE(pipe_config->lane_count);
+       }
+       I915_WRITE(PORT_TX_DFLEXDPMLE1, val);
+}
+
+static void
+intel_ddi_pre_pll_enable(struct intel_encoder *encoder,
+                        const struct intel_crtc_state *crtc_state,
+                        const struct drm_connector_state *conn_state)
+{
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_digital_port *dig_port = enc_to_dig_port(&encoder->base);
+       enum port port = encoder->port;
+
+       if (intel_crtc_has_dp_encoder(crtc_state) ||
+           intel_port_is_tc(dev_priv, encoder->port))
+               intel_display_power_get(dev_priv,
+                                       intel_ddi_main_link_aux_domain(dig_port));
 
-       bxt_ddi_phy_set_lane_optim_mask(encoder, mask);
+       if (IS_GEN9_LP(dev_priv))
+               bxt_ddi_phy_set_lane_optim_mask(encoder,
+                                               crtc_state->lane_lat_optim_mask);
+
+       /*
+        * Program the lane count for static/dynamic connections on Type-C ports.
+        * Skip this step for TBT.
+        */
+       if (dig_port->tc_type == TC_PORT_UNKNOWN ||
+           dig_port->tc_type == TC_PORT_TBT)
+               return;
+
+       intel_ddi_set_fia_lane_count(encoder, crtc_state, port);
+}
+
+static void
+intel_ddi_post_pll_disable(struct intel_encoder *encoder,
+                          const struct intel_crtc_state *crtc_state,
+                          const struct drm_connector_state *conn_state)
+{
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_digital_port *dig_port = enc_to_dig_port(&encoder->base);
+
+       if (intel_crtc_has_dp_encoder(crtc_state) ||
+           intel_port_is_tc(dev_priv, encoder->port))
+               intel_display_power_put(dev_priv,
+                                       intel_ddi_main_link_aux_domain(dig_port));
 }
 
 void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp)
@@ -3131,10 +3455,10 @@ static bool intel_ddi_is_audio_enabled(struct drm_i915_private *dev_priv,
 void intel_ddi_compute_min_voltage_level(struct drm_i915_private *dev_priv,
                                         struct intel_crtc_state *crtc_state)
 {
-       if (IS_CANNONLAKE(dev_priv) && crtc_state->port_clock > 594000)
-               crtc_state->min_voltage_level = 2;
-       else if (IS_ICELAKE(dev_priv) && crtc_state->port_clock > 594000)
+       if (IS_ICELAKE(dev_priv) && crtc_state->port_clock > 594000)
                crtc_state->min_voltage_level = 1;
+       else if (IS_CANNONLAKE(dev_priv) && crtc_state->port_clock > 594000)
+               crtc_state->min_voltage_level = 2;
 }
 
 void intel_ddi_get_config(struct intel_encoder *encoder,
@@ -3184,7 +3508,7 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
                pipe_config->has_hdmi_sink = true;
                intel_dig_port = enc_to_dig_port(&encoder->base);
 
-               if (intel_dig_port->infoframe_enabled(&encoder->base, pipe_config))
+               if (intel_dig_port->infoframe_enabled(encoder, pipe_config))
                        pipe_config->has_infoframe = true;
 
                if ((temp & TRANS_DDI_HDMI_SCRAMBLING_MASK) ==
@@ -3545,6 +3869,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
        struct intel_encoder *intel_encoder;
        struct drm_encoder *encoder;
        bool init_hdmi, init_dp, init_lspcon = false;
+       enum pipe pipe;
 
 
        init_hdmi = (dev_priv->vbt.ddi_port_info[port].supports_dvi ||
@@ -3583,8 +3908,8 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
        intel_encoder->compute_output_type = intel_ddi_compute_output_type;
        intel_encoder->compute_config = intel_ddi_compute_config;
        intel_encoder->enable = intel_enable_ddi;
-       if (IS_GEN9_LP(dev_priv))
-               intel_encoder->pre_pll_enable = bxt_ddi_pre_pll_enable;
+       intel_encoder->pre_pll_enable = intel_ddi_pre_pll_enable;
+       intel_encoder->post_pll_disable = intel_ddi_post_pll_disable;
        intel_encoder->pre_enable = intel_ddi_pre_enable;
        intel_encoder->disable = intel_disable_ddi;
        intel_encoder->post_disable = intel_ddi_post_disable;
@@ -3595,8 +3920,9 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
        intel_encoder->type = INTEL_OUTPUT_DDI;
        intel_encoder->power_domain = intel_port_to_power_domain(port);
        intel_encoder->port = port;
-       intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
        intel_encoder->cloneable = 0;
+       for_each_pipe(dev_priv, pipe)
+               intel_encoder->crtc_mask |= BIT(pipe);
 
        if (INTEL_GEN(dev_priv) >= 11)
                intel_dig_port->saved_port_bits = I915_READ(DDI_BUF_CTL(port)) &
@@ -3606,6 +3932,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
                        (DDI_BUF_PORT_REVERSAL | DDI_A_4_LANES);
        intel_dig_port->dp.output_reg = INVALID_MMIO_REG;
        intel_dig_port->max_lanes = intel_ddi_max_lanes(intel_dig_port);
+       intel_dig_port->aux_ch = intel_aux_ch(dev_priv, port);
 
        switch (port) {
        case PORT_A:
@@ -3636,8 +3963,6 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
                MISSING_CASE(port);
        }
 
-       intel_infoframe_init(intel_dig_port);
-
        if (init_dp) {
                if (!intel_ddi_init_dp_connector(intel_dig_port))
                        goto err;
@@ -3666,6 +3991,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
                                port_name(port));
        }
 
+       intel_infoframe_init(intel_dig_port);
        return;
 
 err: