]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
drm/i915/adl_p: Implement TC sequences
authorJosé Roberto de Souza <jose.souza@intel.com>
Wed, 19 May 2021 00:06:13 +0000 (17:06 -0700)
committerLucas De Marchi <lucas.demarchi@intel.com>
Thu, 20 May 2021 06:59:18 +0000 (23:59 -0700)
ADL-P have basically the same TC connection and disconnection
sequences as ICL and TGL, the major difference is the new registers.

So here adding functions without the icl prefix in the name and
making the new functions call the platform specific function to access
the correct register.

v2:
 - Retain DDI TC PHY ownership flag during modesetting.

BSpec: 55480
Cc: Imre Deak <imre.deak@intel.com>
Signed-off-by: José Roberto de Souza <jose.souza@intel.com>
Signed-off-by: Clinton Taylor <Clinton.A.Taylor@intel.com>
Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210519000625.3184321-6-lucas.demarchi@intel.com
drivers/gpu/drm/i915/display/intel_ddi.c
drivers/gpu/drm/i915/display/intel_tc.c
drivers/gpu/drm/i915/i915_reg.h

index eccbdd42d223fda64a4039e904cf6ff3334b01fa..38a4f251b9c9562eae941e6b97321e2839755755 100644 (file)
@@ -252,12 +252,18 @@ static u32 icl_pll_to_ddi_clk_sel(struct intel_encoder *encoder,
 static void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder,
                                      const struct intel_crtc_state *crtc_state)
 {
+       struct drm_i915_private *i915 = to_i915(encoder->base.dev);
        struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
        struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+       enum phy phy = intel_port_to_phy(i915, encoder->port);
 
        intel_dp->DP = dig_port->saved_port_bits |
                DDI_BUF_CTL_ENABLE | DDI_BUF_TRANS_SELECT(0);
        intel_dp->DP |= DDI_PORT_WIDTH(crtc_state->lane_count);
+
+       if (IS_ALDERLAKE_P(i915) &&
+           intel_phy_is_tc(i915, phy) && dig_port->tc_mode != TC_PORT_TBT_ALT)
+               intel_dp->DP |= DDI_BUF_CTL_TC_PHY_OWNERSHIP;
 }
 
 static int icl_calc_tbt_pll_link(struct drm_i915_private *dev_priv,
index e325463acddd7e6f2fd9daca062526ade1f81953..5f03215a03e4b9615b34755e16f324f14e3f1597 100644 (file)
@@ -205,7 +205,7 @@ static void tc_port_fixup_legacy_flag(struct intel_digital_port *dig_port,
        dig_port->tc_legacy_port = !dig_port->tc_legacy_port;
 }
 
-static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port)
+static u32 icl_tc_port_live_status_mask(struct intel_digital_port *dig_port)
 {
        struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
        struct intel_uncore *uncore = &i915->uncore;
@@ -238,6 +238,40 @@ static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port)
        return mask;
 }
 
+static u32 adl_tc_port_live_status_mask(struct intel_digital_port *dig_port)
+{
+       struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+       enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port);
+       u32 isr_bit = i915->hotplug.pch_hpd[dig_port->base.hpd_pin];
+       struct intel_uncore *uncore = &i915->uncore;
+       u32 val, mask = 0;
+
+       val = intel_uncore_read(uncore, TCSS_DDI_STATUS(tc_port));
+       if (val & TCSS_DDI_STATUS_HPD_LIVE_STATUS_ALT)
+               mask |= BIT(TC_PORT_DP_ALT);
+       if (val & TCSS_DDI_STATUS_HPD_LIVE_STATUS_TBT)
+               mask |= BIT(TC_PORT_TBT_ALT);
+
+       if (intel_uncore_read(uncore, SDEISR) & isr_bit)
+               mask |= BIT(TC_PORT_LEGACY);
+
+       /* The sink can be connected only in a single mode. */
+       if (!drm_WARN_ON(&i915->drm, hweight32(mask) > 1))
+               tc_port_fixup_legacy_flag(dig_port, mask);
+
+       return mask;
+}
+
+static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port)
+{
+       struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+
+       if (IS_ALDERLAKE_P(i915))
+               return adl_tc_port_live_status_mask(dig_port);
+
+       return icl_tc_port_live_status_mask(dig_port);
+}
+
 static bool icl_tc_phy_status_complete(struct intel_digital_port *dig_port)
 {
        struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
@@ -256,6 +290,33 @@ static bool icl_tc_phy_status_complete(struct intel_digital_port *dig_port)
        return val & DP_PHY_MODE_STATUS_COMPLETED(dig_port->tc_phy_fia_idx);
 }
 
+static bool adl_tc_phy_status_complete(struct intel_digital_port *dig_port)
+{
+       struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+       struct intel_uncore *uncore = &i915->uncore;
+       u32 val;
+
+       val = intel_uncore_read(uncore, TCSS_DDI_STATUS(dig_port->tc_phy_fia_idx));
+       if (val == 0xffffffff) {
+               drm_dbg_kms(&i915->drm,
+                           "Port %s: PHY in TCCOLD, assuming not complete\n",
+                           dig_port->tc_port_name);
+               return false;
+       }
+
+       return val & TCSS_DDI_STATUS_READY;
+}
+
+static bool tc_phy_status_complete(struct intel_digital_port *dig_port)
+{
+       struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+
+       if (IS_ALDERLAKE_P(i915))
+               return adl_tc_phy_status_complete(dig_port);
+
+       return icl_tc_phy_status_complete(dig_port);
+}
+
 static bool icl_tc_phy_take_ownership(struct intel_digital_port *dig_port,
                                      bool take)
 {
@@ -280,7 +341,7 @@ static bool icl_tc_phy_take_ownership(struct intel_digital_port *dig_port,
        intel_uncore_write(uncore,
                           PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia), val);
 
-       if (!take && wait_for(!icl_tc_phy_status_complete(dig_port), 10))
+       if (!take && wait_for(!tc_phy_status_complete(dig_port), 10))
                drm_dbg_kms(&i915->drm,
                            "Port %s: PHY complete clear timed out\n",
                            dig_port->tc_port_name);
@@ -288,6 +349,34 @@ static bool icl_tc_phy_take_ownership(struct intel_digital_port *dig_port,
        return true;
 }
 
+static bool adl_tc_phy_take_ownership(struct intel_digital_port *dig_port,
+                                     bool take)
+{
+       struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+       struct intel_uncore *uncore = &i915->uncore;
+       enum port port = dig_port->base.port;
+       u32 val;
+
+       val = intel_uncore_read(uncore, DDI_BUF_CTL(port));
+       if (take)
+               val |= DDI_BUF_CTL_TC_PHY_OWNERSHIP;
+       else
+               val &= ~DDI_BUF_CTL_TC_PHY_OWNERSHIP;
+       intel_uncore_write(uncore, DDI_BUF_CTL(port), val);
+
+       return true;
+}
+
+static bool tc_phy_take_ownership(struct intel_digital_port *dig_port, bool take)
+{
+       struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+
+       if (IS_ALDERLAKE_P(i915))
+               return adl_tc_phy_take_ownership(dig_port, take);
+
+       return icl_tc_phy_take_ownership(dig_port, take);
+}
+
 static bool icl_tc_phy_is_owned(struct intel_digital_port *dig_port)
 {
        struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
@@ -306,6 +395,27 @@ static bool icl_tc_phy_is_owned(struct intel_digital_port *dig_port)
        return val & DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx);
 }
 
+static bool adl_tc_phy_is_owned(struct intel_digital_port *dig_port)
+{
+       struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+       struct intel_uncore *uncore = &i915->uncore;
+       enum port port = dig_port->base.port;
+       u32 val;
+
+       val = intel_uncore_read(uncore, DDI_BUF_CTL(port));
+       return val & DDI_BUF_CTL_TC_PHY_OWNERSHIP;
+}
+
+static bool tc_phy_is_owned(struct intel_digital_port *dig_port)
+{
+       struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
+
+       if (IS_ALDERLAKE_P(i915))
+               return adl_tc_phy_is_owned(dig_port);
+
+       return icl_tc_phy_is_owned(dig_port);
+}
+
 /*
  * This function implements the first part of the Connect Flow described by our
  * specification, Gen11 TypeC Programming chapter. The rest of the flow (reading
@@ -323,13 +433,13 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port,
        struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
        int max_lanes;
 
-       if (!icl_tc_phy_status_complete(dig_port)) {
+       if (!tc_phy_status_complete(dig_port)) {
                drm_dbg_kms(&i915->drm, "Port %s: PHY not ready\n",
                            dig_port->tc_port_name);
                goto out_set_tbt_alt_mode;
        }
 
-       if (!icl_tc_phy_take_ownership(dig_port, true) &&
+       if (!tc_phy_take_ownership(dig_port, true) &&
            !drm_WARN_ON(&i915->drm, dig_port->tc_legacy_port))
                goto out_set_tbt_alt_mode;
 
@@ -364,7 +474,7 @@ static void icl_tc_phy_connect(struct intel_digital_port *dig_port,
        return;
 
 out_release_phy:
-       icl_tc_phy_take_ownership(dig_port, false);
+       tc_phy_take_ownership(dig_port, false);
 out_set_tbt_alt_mode:
        dig_port->tc_mode = TC_PORT_TBT_ALT;
 }
@@ -380,7 +490,7 @@ static void icl_tc_phy_disconnect(struct intel_digital_port *dig_port)
                /* Nothing to do, we never disconnect from legacy mode */
                break;
        case TC_PORT_DP_ALT:
-               icl_tc_phy_take_ownership(dig_port, false);
+               tc_phy_take_ownership(dig_port, false);
                dig_port->tc_mode = TC_PORT_TBT_ALT;
                break;
        case TC_PORT_TBT_ALT:
@@ -395,13 +505,13 @@ static bool icl_tc_phy_is_connected(struct intel_digital_port *dig_port)
 {
        struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
 
-       if (!icl_tc_phy_status_complete(dig_port)) {
+       if (!tc_phy_status_complete(dig_port)) {
                drm_dbg_kms(&i915->drm, "Port %s: PHY status not complete\n",
                            dig_port->tc_port_name);
                return dig_port->tc_mode == TC_PORT_TBT_ALT;
        }
 
-       if (!icl_tc_phy_is_owned(dig_port)) {
+       if (!tc_phy_is_owned(dig_port)) {
                drm_dbg_kms(&i915->drm, "Port %s: PHY not owned\n",
                            dig_port->tc_port_name);
 
@@ -419,8 +529,8 @@ intel_tc_port_get_current_mode(struct intel_digital_port *dig_port)
        u32 live_status_mask = tc_port_live_status_mask(dig_port);
        enum tc_port_mode mode;
 
-       if (!icl_tc_phy_is_owned(dig_port) ||
-           drm_WARN_ON(&i915->drm, !icl_tc_phy_status_complete(dig_port)))
+       if (!tc_phy_is_owned(dig_port) ||
+           drm_WARN_ON(&i915->drm, !tc_phy_status_complete(dig_port)))
                return TC_PORT_TBT_ALT;
 
        mode = dig_port->tc_legacy_port ? TC_PORT_LEGACY : TC_PORT_DP_ALT;
@@ -442,7 +552,7 @@ intel_tc_port_get_target_mode(struct intel_digital_port *dig_port)
        if (live_status_mask)
                return fls(live_status_mask) - 1;
 
-       return icl_tc_phy_status_complete(dig_port) &&
+       return tc_phy_status_complete(dig_port) &&
               dig_port->tc_legacy_port ? TC_PORT_LEGACY :
                                          TC_PORT_TBT_ALT;
 }
index 388f2977d899c4598570ee38b1bc67832235dada..b370143a9d270a1e50867799959236df5c380106 100644 (file)
@@ -10143,6 +10143,7 @@ enum skl_power_gate {
 #define  DDI_BUF_EMP_MASK                      (0xf << 24)
 #define  DDI_BUF_PORT_REVERSAL                 (1 << 16)
 #define  DDI_BUF_IS_IDLE                       (1 << 7)
+#define  DDI_BUF_CTL_TC_PHY_OWNERSHIP          REG_BIT(6)
 #define  DDI_A_4_LANES                         (1 << 4)
 #define  DDI_PORT_WIDTH(width)                 (((width) - 1) << 1)
 #define  DDI_PORT_WIDTH_MASK                   (7 << 1)
@@ -12578,6 +12579,15 @@ enum skl_power_gate {
 #define   DP_PIN_ASSIGNMENT_MASK(idx)          (0xf << ((idx) * 4))
 #define   DP_PIN_ASSIGNMENT(idx, x)            ((x) << ((idx) * 4))
 
+#define _TCSS_DDI_STATUS_1                     0x161500
+#define _TCSS_DDI_STATUS_2                     0x161504
+#define TCSS_DDI_STATUS(tc)                    _MMIO(_PICK_EVEN(tc, \
+                                                                _TCSS_DDI_STATUS_1, \
+                                                                _TCSS_DDI_STATUS_2))
+#define  TCSS_DDI_STATUS_READY                 REG_BIT(2)
+#define  TCSS_DDI_STATUS_HPD_LIVE_STATUS_TBT   REG_BIT(1)
+#define  TCSS_DDI_STATUS_HPD_LIVE_STATUS_ALT   REG_BIT(0)
+
 /* This register controls the Display State Buffer (DSB) engines. */
 #define _DSBSL_INSTANCE_BASE           0x70B00
 #define DSBSL_INSTANCE(pipe, id)       (_DSBSL_INSTANCE_BASE + \