From patchwork Sat May 8 02:27:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Matt Roper X-Patchwork-Id: 12245643 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1BB9DC433B4 for ; Sat, 8 May 2021 02:28:58 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id C8EF26144F for ; Sat, 8 May 2021 02:28:57 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org C8EF26144F Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=intel.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=intel-gfx-bounces@lists.freedesktop.org Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5DF156EEB3; Sat, 8 May 2021 02:28:36 +0000 (UTC) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by gabe.freedesktop.org (Postfix) with ESMTPS id 9F7F06E829 for ; Sat, 8 May 2021 02:28:31 +0000 (UTC) IronPort-SDR: XsNLOHWwG5++/FEHg7XFsabnuuGSnMjNAE8f3sgcpYFvyirCedOZO9wGAvr7hIhYQEFile0rQq UEJpQtbfvOHg== X-IronPort-AV: E=McAfee;i="6200,9189,9977"; a="262790153" X-IronPort-AV: E=Sophos;i="5.82,282,1613462400"; d="scan'208";a="262790153" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 May 2021 19:28:30 -0700 IronPort-SDR: g1QatPwTPN6daPNkkSAARb1bv8P822iXbC+EyFUJ5nYKqAIBdWKplwytBa/sSHJjaKDLqEis/W IJx7rAbN5AAg== X-IronPort-AV: E=Sophos;i="5.82,282,1613462400"; d="scan'208";a="533910103" Received: from mdroper-desk1.fm.intel.com ([10.1.27.168]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 May 2021 19:28:28 -0700 From: Matt Roper To: intel-gfx@lists.freedesktop.org Date: Fri, 7 May 2021 19:27:55 -0700 Message-Id: <20210508022820.780227-24-matthew.d.roper@intel.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: <20210508022820.780227-1-matthew.d.roper@intel.com> References: <20210508022820.780227-1-matthew.d.roper@intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH v3 23/48] drm/i915/adl_p: Implement TC sequences X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" From: José Roberto de Souza 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 Signed-off-by: José Roberto de Souza Signed-off-by: Clinton Taylor Signed-off-by: Matt Roper --- drivers/gpu/drm/i915/display/intel_ddi.c | 6 ++ drivers/gpu/drm/i915/display/intel_tc.c | 132 +++++++++++++++++++++-- drivers/gpu/drm/i915/i915_reg.h | 10 ++ 3 files changed, 137 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index d37b01b889c0..1a21879016e1 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -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, diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 2af4d8d5a983..261c0498ec1f 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -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; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index a0baa8fff527..93405171c7b9 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -10157,6 +10157,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) @@ -12590,6 +12591,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 + \