1*9e5fbd4fSriastradh /* $NetBSD: intel_tc.c,v 1.3 2021/12/19 11:49:11 riastradh Exp $ */
24e390cabSriastradh
34e390cabSriastradh // SPDX-License-Identifier: MIT
44e390cabSriastradh /*
54e390cabSriastradh * Copyright © 2019 Intel Corporation
64e390cabSriastradh */
74e390cabSriastradh
84e390cabSriastradh #include <sys/cdefs.h>
9*9e5fbd4fSriastradh __KERNEL_RCSID(0, "$NetBSD: intel_tc.c,v 1.3 2021/12/19 11:49:11 riastradh Exp $");
104e390cabSriastradh
114e390cabSriastradh #include "i915_drv.h"
124e390cabSriastradh #include "intel_display.h"
134e390cabSriastradh #include "intel_display_types.h"
144e390cabSriastradh #include "intel_dp_mst.h"
154e390cabSriastradh #include "intel_tc.h"
164e390cabSriastradh
17*9e5fbd4fSriastradh #include <linux/nbsd-namespace.h>
18*9e5fbd4fSriastradh
tc_port_mode_name(enum tc_port_mode mode)194e390cabSriastradh static const char *tc_port_mode_name(enum tc_port_mode mode)
204e390cabSriastradh {
214e390cabSriastradh static const char * const names[] = {
224e390cabSriastradh [TC_PORT_TBT_ALT] = "tbt-alt",
234e390cabSriastradh [TC_PORT_DP_ALT] = "dp-alt",
244e390cabSriastradh [TC_PORT_LEGACY] = "legacy",
254e390cabSriastradh };
264e390cabSriastradh
274e390cabSriastradh if (WARN_ON(mode >= ARRAY_SIZE(names)))
284e390cabSriastradh mode = TC_PORT_TBT_ALT;
294e390cabSriastradh
304e390cabSriastradh return names[mode];
314e390cabSriastradh }
324e390cabSriastradh
334e390cabSriastradh static void
tc_port_load_fia_params(struct drm_i915_private * i915,struct intel_digital_port * dig_port)344e390cabSriastradh tc_port_load_fia_params(struct drm_i915_private *i915,
354e390cabSriastradh struct intel_digital_port *dig_port)
364e390cabSriastradh {
374e390cabSriastradh enum port port = dig_port->base.port;
384e390cabSriastradh enum tc_port tc_port = intel_port_to_tc(i915, port);
394e390cabSriastradh u32 modular_fia;
404e390cabSriastradh
414e390cabSriastradh if (INTEL_INFO(i915)->display.has_modular_fia) {
424e390cabSriastradh modular_fia = intel_uncore_read(&i915->uncore,
434e390cabSriastradh PORT_TX_DFLEXDPSP(FIA1));
444e390cabSriastradh modular_fia &= MODULAR_FIA_MASK;
454e390cabSriastradh } else {
464e390cabSriastradh modular_fia = 0;
474e390cabSriastradh }
484e390cabSriastradh
494e390cabSriastradh /*
504e390cabSriastradh * Each Modular FIA instance houses 2 TC ports. In SOC that has more
514e390cabSriastradh * than two TC ports, there are multiple instances of Modular FIA.
524e390cabSriastradh */
534e390cabSriastradh if (modular_fia) {
544e390cabSriastradh dig_port->tc_phy_fia = tc_port / 2;
554e390cabSriastradh dig_port->tc_phy_fia_idx = tc_port % 2;
564e390cabSriastradh } else {
574e390cabSriastradh dig_port->tc_phy_fia = FIA1;
584e390cabSriastradh dig_port->tc_phy_fia_idx = tc_port;
594e390cabSriastradh }
604e390cabSriastradh }
614e390cabSriastradh
intel_tc_port_get_lane_mask(struct intel_digital_port * dig_port)624e390cabSriastradh u32 intel_tc_port_get_lane_mask(struct intel_digital_port *dig_port)
634e390cabSriastradh {
644e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
654e390cabSriastradh struct intel_uncore *uncore = &i915->uncore;
664e390cabSriastradh u32 lane_mask;
674e390cabSriastradh
684e390cabSriastradh lane_mask = intel_uncore_read(uncore,
694e390cabSriastradh PORT_TX_DFLEXDPSP(dig_port->tc_phy_fia));
704e390cabSriastradh
714e390cabSriastradh WARN_ON(lane_mask == 0xffffffff);
724e390cabSriastradh
734e390cabSriastradh lane_mask &= DP_LANE_ASSIGNMENT_MASK(dig_port->tc_phy_fia_idx);
744e390cabSriastradh return lane_mask >> DP_LANE_ASSIGNMENT_SHIFT(dig_port->tc_phy_fia_idx);
754e390cabSriastradh }
764e390cabSriastradh
intel_tc_port_get_pin_assignment_mask(struct intel_digital_port * dig_port)774e390cabSriastradh u32 intel_tc_port_get_pin_assignment_mask(struct intel_digital_port *dig_port)
784e390cabSriastradh {
794e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
804e390cabSriastradh struct intel_uncore *uncore = &i915->uncore;
814e390cabSriastradh u32 pin_mask;
824e390cabSriastradh
834e390cabSriastradh pin_mask = intel_uncore_read(uncore,
844e390cabSriastradh PORT_TX_DFLEXPA1(dig_port->tc_phy_fia));
854e390cabSriastradh
864e390cabSriastradh WARN_ON(pin_mask == 0xffffffff);
874e390cabSriastradh
884e390cabSriastradh return (pin_mask & DP_PIN_ASSIGNMENT_MASK(dig_port->tc_phy_fia_idx)) >>
894e390cabSriastradh DP_PIN_ASSIGNMENT_SHIFT(dig_port->tc_phy_fia_idx);
904e390cabSriastradh }
914e390cabSriastradh
intel_tc_port_fia_max_lane_count(struct intel_digital_port * dig_port)924e390cabSriastradh int intel_tc_port_fia_max_lane_count(struct intel_digital_port *dig_port)
934e390cabSriastradh {
944e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
954e390cabSriastradh intel_wakeref_t wakeref;
964e390cabSriastradh u32 lane_mask;
974e390cabSriastradh
984e390cabSriastradh if (dig_port->tc_mode != TC_PORT_DP_ALT)
994e390cabSriastradh return 4;
1004e390cabSriastradh
1014e390cabSriastradh lane_mask = 0;
1024e390cabSriastradh with_intel_display_power(i915, POWER_DOMAIN_DISPLAY_CORE, wakeref)
1034e390cabSriastradh lane_mask = intel_tc_port_get_lane_mask(dig_port);
1044e390cabSriastradh
1054e390cabSriastradh switch (lane_mask) {
1064e390cabSriastradh default:
1074e390cabSriastradh MISSING_CASE(lane_mask);
1084e390cabSriastradh /* fall-through */
1094e390cabSriastradh case 0x1:
1104e390cabSriastradh case 0x2:
1114e390cabSriastradh case 0x4:
1124e390cabSriastradh case 0x8:
1134e390cabSriastradh return 1;
1144e390cabSriastradh case 0x3:
1154e390cabSriastradh case 0xc:
1164e390cabSriastradh return 2;
1174e390cabSriastradh case 0xf:
1184e390cabSriastradh return 4;
1194e390cabSriastradh }
1204e390cabSriastradh }
1214e390cabSriastradh
intel_tc_port_set_fia_lane_count(struct intel_digital_port * dig_port,int required_lanes)1224e390cabSriastradh void intel_tc_port_set_fia_lane_count(struct intel_digital_port *dig_port,
1234e390cabSriastradh int required_lanes)
1244e390cabSriastradh {
1254e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
1264e390cabSriastradh bool lane_reversal = dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL;
1274e390cabSriastradh struct intel_uncore *uncore = &i915->uncore;
1284e390cabSriastradh u32 val;
1294e390cabSriastradh
1304e390cabSriastradh WARN_ON(lane_reversal && dig_port->tc_mode != TC_PORT_LEGACY);
1314e390cabSriastradh
1324e390cabSriastradh val = intel_uncore_read(uncore,
1334e390cabSriastradh PORT_TX_DFLEXDPMLE1(dig_port->tc_phy_fia));
1344e390cabSriastradh val &= ~DFLEXDPMLE1_DPMLETC_MASK(dig_port->tc_phy_fia_idx);
1354e390cabSriastradh
1364e390cabSriastradh switch (required_lanes) {
1374e390cabSriastradh case 1:
1384e390cabSriastradh val |= lane_reversal ?
1394e390cabSriastradh DFLEXDPMLE1_DPMLETC_ML3(dig_port->tc_phy_fia_idx) :
1404e390cabSriastradh DFLEXDPMLE1_DPMLETC_ML0(dig_port->tc_phy_fia_idx);
1414e390cabSriastradh break;
1424e390cabSriastradh case 2:
1434e390cabSriastradh val |= lane_reversal ?
1444e390cabSriastradh DFLEXDPMLE1_DPMLETC_ML3_2(dig_port->tc_phy_fia_idx) :
1454e390cabSriastradh DFLEXDPMLE1_DPMLETC_ML1_0(dig_port->tc_phy_fia_idx);
1464e390cabSriastradh break;
1474e390cabSriastradh case 4:
1484e390cabSriastradh val |= DFLEXDPMLE1_DPMLETC_ML3_0(dig_port->tc_phy_fia_idx);
1494e390cabSriastradh break;
1504e390cabSriastradh default:
1514e390cabSriastradh MISSING_CASE(required_lanes);
1524e390cabSriastradh }
1534e390cabSriastradh
1544e390cabSriastradh intel_uncore_write(uncore,
1554e390cabSriastradh PORT_TX_DFLEXDPMLE1(dig_port->tc_phy_fia), val);
1564e390cabSriastradh }
1574e390cabSriastradh
tc_port_fixup_legacy_flag(struct intel_digital_port * dig_port,u32 live_status_mask)1584e390cabSriastradh static void tc_port_fixup_legacy_flag(struct intel_digital_port *dig_port,
1594e390cabSriastradh u32 live_status_mask)
1604e390cabSriastradh {
1614e390cabSriastradh u32 valid_hpd_mask;
1624e390cabSriastradh
1634e390cabSriastradh if (dig_port->tc_legacy_port)
1644e390cabSriastradh valid_hpd_mask = BIT(TC_PORT_LEGACY);
1654e390cabSriastradh else
1664e390cabSriastradh valid_hpd_mask = BIT(TC_PORT_DP_ALT) |
1674e390cabSriastradh BIT(TC_PORT_TBT_ALT);
1684e390cabSriastradh
1694e390cabSriastradh if (!(live_status_mask & ~valid_hpd_mask))
1704e390cabSriastradh return;
1714e390cabSriastradh
1724e390cabSriastradh /* If live status mismatches the VBT flag, trust the live status. */
1734e390cabSriastradh DRM_ERROR("Port %s: live status %08x mismatches the legacy port flag, fix flag\n",
1744e390cabSriastradh dig_port->tc_port_name, live_status_mask);
1754e390cabSriastradh
1764e390cabSriastradh dig_port->tc_legacy_port = !dig_port->tc_legacy_port;
1774e390cabSriastradh }
1784e390cabSriastradh
tc_port_live_status_mask(struct intel_digital_port * dig_port)1794e390cabSriastradh static u32 tc_port_live_status_mask(struct intel_digital_port *dig_port)
1804e390cabSriastradh {
1814e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
1824e390cabSriastradh enum tc_port tc_port = intel_port_to_tc(i915, dig_port->base.port);
1834e390cabSriastradh struct intel_uncore *uncore = &i915->uncore;
1844e390cabSriastradh u32 mask = 0;
1854e390cabSriastradh u32 val;
1864e390cabSriastradh
1874e390cabSriastradh val = intel_uncore_read(uncore,
1884e390cabSriastradh PORT_TX_DFLEXDPSP(dig_port->tc_phy_fia));
1894e390cabSriastradh
1904e390cabSriastradh if (val == 0xffffffff) {
1914e390cabSriastradh DRM_DEBUG_KMS("Port %s: PHY in TCCOLD, nothing connected\n",
1924e390cabSriastradh dig_port->tc_port_name);
1934e390cabSriastradh return mask;
1944e390cabSriastradh }
1954e390cabSriastradh
1964e390cabSriastradh if (val & TC_LIVE_STATE_TBT(dig_port->tc_phy_fia_idx))
1974e390cabSriastradh mask |= BIT(TC_PORT_TBT_ALT);
1984e390cabSriastradh if (val & TC_LIVE_STATE_TC(dig_port->tc_phy_fia_idx))
1994e390cabSriastradh mask |= BIT(TC_PORT_DP_ALT);
2004e390cabSriastradh
2014e390cabSriastradh if (intel_uncore_read(uncore, SDEISR) & SDE_TC_HOTPLUG_ICP(tc_port))
2024e390cabSriastradh mask |= BIT(TC_PORT_LEGACY);
2034e390cabSriastradh
2044e390cabSriastradh /* The sink can be connected only in a single mode. */
2054e390cabSriastradh if (!WARN_ON(hweight32(mask) > 1))
2064e390cabSriastradh tc_port_fixup_legacy_flag(dig_port, mask);
2074e390cabSriastradh
2084e390cabSriastradh return mask;
2094e390cabSriastradh }
2104e390cabSriastradh
icl_tc_phy_status_complete(struct intel_digital_port * dig_port)2114e390cabSriastradh static bool icl_tc_phy_status_complete(struct intel_digital_port *dig_port)
2124e390cabSriastradh {
2134e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
2144e390cabSriastradh struct intel_uncore *uncore = &i915->uncore;
2154e390cabSriastradh u32 val;
2164e390cabSriastradh
2174e390cabSriastradh val = intel_uncore_read(uncore,
2184e390cabSriastradh PORT_TX_DFLEXDPPMS(dig_port->tc_phy_fia));
2194e390cabSriastradh if (val == 0xffffffff) {
2204e390cabSriastradh DRM_DEBUG_KMS("Port %s: PHY in TCCOLD, assuming not complete\n",
2214e390cabSriastradh dig_port->tc_port_name);
2224e390cabSriastradh return false;
2234e390cabSriastradh }
2244e390cabSriastradh
2254e390cabSriastradh return val & DP_PHY_MODE_STATUS_COMPLETED(dig_port->tc_phy_fia_idx);
2264e390cabSriastradh }
2274e390cabSriastradh
icl_tc_phy_set_safe_mode(struct intel_digital_port * dig_port,bool enable)2284e390cabSriastradh static bool icl_tc_phy_set_safe_mode(struct intel_digital_port *dig_port,
2294e390cabSriastradh bool enable)
2304e390cabSriastradh {
2314e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
2324e390cabSriastradh struct intel_uncore *uncore = &i915->uncore;
2334e390cabSriastradh u32 val;
2344e390cabSriastradh
2354e390cabSriastradh val = intel_uncore_read(uncore,
2364e390cabSriastradh PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia));
2374e390cabSriastradh if (val == 0xffffffff) {
2384e390cabSriastradh DRM_DEBUG_KMS("Port %s: PHY in TCCOLD, can't set safe-mode to %s\n",
2394e390cabSriastradh dig_port->tc_port_name,
2404e390cabSriastradh enableddisabled(enable));
2414e390cabSriastradh
2424e390cabSriastradh return false;
2434e390cabSriastradh }
2444e390cabSriastradh
2454e390cabSriastradh val &= ~DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx);
2464e390cabSriastradh if (!enable)
2474e390cabSriastradh val |= DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx);
2484e390cabSriastradh
2494e390cabSriastradh intel_uncore_write(uncore,
2504e390cabSriastradh PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia), val);
2514e390cabSriastradh
2524e390cabSriastradh if (enable && wait_for(!icl_tc_phy_status_complete(dig_port), 10))
2534e390cabSriastradh DRM_DEBUG_KMS("Port %s: PHY complete clear timed out\n",
2544e390cabSriastradh dig_port->tc_port_name);
2554e390cabSriastradh
2564e390cabSriastradh return true;
2574e390cabSriastradh }
2584e390cabSriastradh
icl_tc_phy_is_in_safe_mode(struct intel_digital_port * dig_port)2594e390cabSriastradh static bool icl_tc_phy_is_in_safe_mode(struct intel_digital_port *dig_port)
2604e390cabSriastradh {
2614e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
2624e390cabSriastradh struct intel_uncore *uncore = &i915->uncore;
2634e390cabSriastradh u32 val;
2644e390cabSriastradh
2654e390cabSriastradh val = intel_uncore_read(uncore,
2664e390cabSriastradh PORT_TX_DFLEXDPCSSS(dig_port->tc_phy_fia));
2674e390cabSriastradh if (val == 0xffffffff) {
2684e390cabSriastradh DRM_DEBUG_KMS("Port %s: PHY in TCCOLD, assume safe mode\n",
2694e390cabSriastradh dig_port->tc_port_name);
2704e390cabSriastradh return true;
2714e390cabSriastradh }
2724e390cabSriastradh
2734e390cabSriastradh return !(val & DP_PHY_MODE_STATUS_NOT_SAFE(dig_port->tc_phy_fia_idx));
2744e390cabSriastradh }
2754e390cabSriastradh
2764e390cabSriastradh /*
2774e390cabSriastradh * This function implements the first part of the Connect Flow described by our
2784e390cabSriastradh * specification, Gen11 TypeC Programming chapter. The rest of the flow (reading
2794e390cabSriastradh * lanes, EDID, etc) is done as needed in the typical places.
2804e390cabSriastradh *
2814e390cabSriastradh * Unlike the other ports, type-C ports are not available to use as soon as we
2824e390cabSriastradh * get a hotplug. The type-C PHYs can be shared between multiple controllers:
2834e390cabSriastradh * display, USB, etc. As a result, handshaking through FIA is required around
2844e390cabSriastradh * connect and disconnect to cleanly transfer ownership with the controller and
2854e390cabSriastradh * set the type-C power state.
2864e390cabSriastradh */
icl_tc_phy_connect(struct intel_digital_port * dig_port,int required_lanes)2874e390cabSriastradh static void icl_tc_phy_connect(struct intel_digital_port *dig_port,
2884e390cabSriastradh int required_lanes)
2894e390cabSriastradh {
2904e390cabSriastradh int max_lanes;
2914e390cabSriastradh
2924e390cabSriastradh if (!icl_tc_phy_status_complete(dig_port)) {
2934e390cabSriastradh DRM_DEBUG_KMS("Port %s: PHY not ready\n",
2944e390cabSriastradh dig_port->tc_port_name);
2954e390cabSriastradh goto out_set_tbt_alt_mode;
2964e390cabSriastradh }
2974e390cabSriastradh
2984e390cabSriastradh if (!icl_tc_phy_set_safe_mode(dig_port, false) &&
2994e390cabSriastradh !WARN_ON(dig_port->tc_legacy_port))
3004e390cabSriastradh goto out_set_tbt_alt_mode;
3014e390cabSriastradh
3024e390cabSriastradh max_lanes = intel_tc_port_fia_max_lane_count(dig_port);
3034e390cabSriastradh if (dig_port->tc_legacy_port) {
3044e390cabSriastradh WARN_ON(max_lanes != 4);
3054e390cabSriastradh dig_port->tc_mode = TC_PORT_LEGACY;
3064e390cabSriastradh
3074e390cabSriastradh return;
3084e390cabSriastradh }
3094e390cabSriastradh
3104e390cabSriastradh /*
3114e390cabSriastradh * Now we have to re-check the live state, in case the port recently
3124e390cabSriastradh * became disconnected. Not necessary for legacy mode.
3134e390cabSriastradh */
3144e390cabSriastradh if (!(tc_port_live_status_mask(dig_port) & BIT(TC_PORT_DP_ALT))) {
3154e390cabSriastradh DRM_DEBUG_KMS("Port %s: PHY sudden disconnect\n",
3164e390cabSriastradh dig_port->tc_port_name);
3174e390cabSriastradh goto out_set_safe_mode;
3184e390cabSriastradh }
3194e390cabSriastradh
3204e390cabSriastradh if (max_lanes < required_lanes) {
3214e390cabSriastradh DRM_DEBUG_KMS("Port %s: PHY max lanes %d < required lanes %d\n",
3224e390cabSriastradh dig_port->tc_port_name,
3234e390cabSriastradh max_lanes, required_lanes);
3244e390cabSriastradh goto out_set_safe_mode;
3254e390cabSriastradh }
3264e390cabSriastradh
3274e390cabSriastradh dig_port->tc_mode = TC_PORT_DP_ALT;
3284e390cabSriastradh
3294e390cabSriastradh return;
3304e390cabSriastradh
3314e390cabSriastradh out_set_safe_mode:
3324e390cabSriastradh icl_tc_phy_set_safe_mode(dig_port, true);
3334e390cabSriastradh out_set_tbt_alt_mode:
3344e390cabSriastradh dig_port->tc_mode = TC_PORT_TBT_ALT;
3354e390cabSriastradh }
3364e390cabSriastradh
3374e390cabSriastradh /*
3384e390cabSriastradh * See the comment at the connect function. This implements the Disconnect
3394e390cabSriastradh * Flow.
3404e390cabSriastradh */
icl_tc_phy_disconnect(struct intel_digital_port * dig_port)3414e390cabSriastradh static void icl_tc_phy_disconnect(struct intel_digital_port *dig_port)
3424e390cabSriastradh {
3434e390cabSriastradh switch (dig_port->tc_mode) {
3444e390cabSriastradh case TC_PORT_LEGACY:
3454e390cabSriastradh /* Nothing to do, we never disconnect from legacy mode */
3464e390cabSriastradh break;
3474e390cabSriastradh case TC_PORT_DP_ALT:
3484e390cabSriastradh icl_tc_phy_set_safe_mode(dig_port, true);
3494e390cabSriastradh dig_port->tc_mode = TC_PORT_TBT_ALT;
3504e390cabSriastradh break;
3514e390cabSriastradh case TC_PORT_TBT_ALT:
3524e390cabSriastradh /* Nothing to do, we stay in TBT-alt mode */
3534e390cabSriastradh break;
3544e390cabSriastradh default:
3554e390cabSriastradh MISSING_CASE(dig_port->tc_mode);
3564e390cabSriastradh }
3574e390cabSriastradh }
3584e390cabSriastradh
icl_tc_phy_is_connected(struct intel_digital_port * dig_port)3594e390cabSriastradh static bool icl_tc_phy_is_connected(struct intel_digital_port *dig_port)
3604e390cabSriastradh {
3614e390cabSriastradh if (!icl_tc_phy_status_complete(dig_port)) {
3624e390cabSriastradh DRM_DEBUG_KMS("Port %s: PHY status not complete\n",
3634e390cabSriastradh dig_port->tc_port_name);
3644e390cabSriastradh return dig_port->tc_mode == TC_PORT_TBT_ALT;
3654e390cabSriastradh }
3664e390cabSriastradh
3674e390cabSriastradh if (icl_tc_phy_is_in_safe_mode(dig_port)) {
3684e390cabSriastradh DRM_DEBUG_KMS("Port %s: PHY still in safe mode\n",
3694e390cabSriastradh dig_port->tc_port_name);
3704e390cabSriastradh
3714e390cabSriastradh return false;
3724e390cabSriastradh }
3734e390cabSriastradh
3744e390cabSriastradh return dig_port->tc_mode == TC_PORT_DP_ALT ||
3754e390cabSriastradh dig_port->tc_mode == TC_PORT_LEGACY;
3764e390cabSriastradh }
3774e390cabSriastradh
3784e390cabSriastradh static enum tc_port_mode
intel_tc_port_get_current_mode(struct intel_digital_port * dig_port)3794e390cabSriastradh intel_tc_port_get_current_mode(struct intel_digital_port *dig_port)
3804e390cabSriastradh {
3814e390cabSriastradh u32 live_status_mask = tc_port_live_status_mask(dig_port);
3824e390cabSriastradh bool in_safe_mode = icl_tc_phy_is_in_safe_mode(dig_port);
3834e390cabSriastradh enum tc_port_mode mode;
3844e390cabSriastradh
3854e390cabSriastradh if (in_safe_mode || WARN_ON(!icl_tc_phy_status_complete(dig_port)))
3864e390cabSriastradh return TC_PORT_TBT_ALT;
3874e390cabSriastradh
3884e390cabSriastradh mode = dig_port->tc_legacy_port ? TC_PORT_LEGACY : TC_PORT_DP_ALT;
3894e390cabSriastradh if (live_status_mask) {
3904e390cabSriastradh enum tc_port_mode live_mode = fls(live_status_mask) - 1;
3914e390cabSriastradh
3924e390cabSriastradh if (!WARN_ON(live_mode == TC_PORT_TBT_ALT))
3934e390cabSriastradh mode = live_mode;
3944e390cabSriastradh }
3954e390cabSriastradh
3964e390cabSriastradh return mode;
3974e390cabSriastradh }
3984e390cabSriastradh
3994e390cabSriastradh static enum tc_port_mode
intel_tc_port_get_target_mode(struct intel_digital_port * dig_port)4004e390cabSriastradh intel_tc_port_get_target_mode(struct intel_digital_port *dig_port)
4014e390cabSriastradh {
4024e390cabSriastradh u32 live_status_mask = tc_port_live_status_mask(dig_port);
4034e390cabSriastradh
4044e390cabSriastradh if (live_status_mask)
4054e390cabSriastradh return fls(live_status_mask) - 1;
4064e390cabSriastradh
4074e390cabSriastradh return icl_tc_phy_status_complete(dig_port) &&
4084e390cabSriastradh dig_port->tc_legacy_port ? TC_PORT_LEGACY :
4094e390cabSriastradh TC_PORT_TBT_ALT;
4104e390cabSriastradh }
4114e390cabSriastradh
intel_tc_port_reset_mode(struct intel_digital_port * dig_port,int required_lanes)4124e390cabSriastradh static void intel_tc_port_reset_mode(struct intel_digital_port *dig_port,
4134e390cabSriastradh int required_lanes)
4144e390cabSriastradh {
4154e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
4164e390cabSriastradh enum tc_port_mode old_tc_mode = dig_port->tc_mode;
4174e390cabSriastradh
4184e390cabSriastradh intel_display_power_flush_work(i915);
4194e390cabSriastradh WARN_ON(intel_display_power_is_enabled(i915,
4204e390cabSriastradh intel_aux_power_domain(dig_port)));
4214e390cabSriastradh
4224e390cabSriastradh icl_tc_phy_disconnect(dig_port);
4234e390cabSriastradh icl_tc_phy_connect(dig_port, required_lanes);
4244e390cabSriastradh
4254e390cabSriastradh DRM_DEBUG_KMS("Port %s: TC port mode reset (%s -> %s)\n",
4264e390cabSriastradh dig_port->tc_port_name,
4274e390cabSriastradh tc_port_mode_name(old_tc_mode),
4284e390cabSriastradh tc_port_mode_name(dig_port->tc_mode));
4294e390cabSriastradh }
4304e390cabSriastradh
4314e390cabSriastradh static void
intel_tc_port_link_init_refcount(struct intel_digital_port * dig_port,int refcount)4324e390cabSriastradh intel_tc_port_link_init_refcount(struct intel_digital_port *dig_port,
4334e390cabSriastradh int refcount)
4344e390cabSriastradh {
4354e390cabSriastradh WARN_ON(dig_port->tc_link_refcount);
4364e390cabSriastradh dig_port->tc_link_refcount = refcount;
4374e390cabSriastradh }
4384e390cabSriastradh
intel_tc_port_sanitize(struct intel_digital_port * dig_port)4394e390cabSriastradh void intel_tc_port_sanitize(struct intel_digital_port *dig_port)
4404e390cabSriastradh {
4414e390cabSriastradh struct intel_encoder *encoder = &dig_port->base;
4424e390cabSriastradh int active_links = 0;
4434e390cabSriastradh
4444e390cabSriastradh mutex_lock(&dig_port->tc_lock);
4454e390cabSriastradh
4464e390cabSriastradh dig_port->tc_mode = intel_tc_port_get_current_mode(dig_port);
4474e390cabSriastradh if (dig_port->dp.is_mst)
4484e390cabSriastradh active_links = intel_dp_mst_encoder_active_links(dig_port);
4494e390cabSriastradh else if (encoder->base.crtc)
4504e390cabSriastradh active_links = to_intel_crtc(encoder->base.crtc)->active;
4514e390cabSriastradh
4524e390cabSriastradh if (active_links) {
4534e390cabSriastradh if (!icl_tc_phy_is_connected(dig_port))
4544e390cabSriastradh DRM_DEBUG_KMS("Port %s: PHY disconnected with %d active link(s)\n",
4554e390cabSriastradh dig_port->tc_port_name, active_links);
4564e390cabSriastradh intel_tc_port_link_init_refcount(dig_port, active_links);
4574e390cabSriastradh
4584e390cabSriastradh goto out;
4594e390cabSriastradh }
4604e390cabSriastradh
4614e390cabSriastradh if (dig_port->tc_legacy_port)
4624e390cabSriastradh icl_tc_phy_connect(dig_port, 1);
4634e390cabSriastradh
4644e390cabSriastradh out:
4654e390cabSriastradh DRM_DEBUG_KMS("Port %s: sanitize mode (%s)\n",
4664e390cabSriastradh dig_port->tc_port_name,
4674e390cabSriastradh tc_port_mode_name(dig_port->tc_mode));
4684e390cabSriastradh
4694e390cabSriastradh mutex_unlock(&dig_port->tc_lock);
4704e390cabSriastradh }
4714e390cabSriastradh
intel_tc_port_needs_reset(struct intel_digital_port * dig_port)4724e390cabSriastradh static bool intel_tc_port_needs_reset(struct intel_digital_port *dig_port)
4734e390cabSriastradh {
4744e390cabSriastradh return intel_tc_port_get_target_mode(dig_port) != dig_port->tc_mode;
4754e390cabSriastradh }
4764e390cabSriastradh
4774e390cabSriastradh /*
4784e390cabSriastradh * The type-C ports are different because even when they are connected, they may
4794e390cabSriastradh * not be available/usable by the graphics driver: see the comment on
4804e390cabSriastradh * icl_tc_phy_connect(). So in our driver instead of adding the additional
4814e390cabSriastradh * concept of "usable" and make everything check for "connected and usable" we
4824e390cabSriastradh * define a port as "connected" when it is not only connected, but also when it
4834e390cabSriastradh * is usable by the rest of the driver. That maintains the old assumption that
4844e390cabSriastradh * connected ports are usable, and avoids exposing to the users objects they
4854e390cabSriastradh * can't really use.
4864e390cabSriastradh */
intel_tc_port_connected(struct intel_digital_port * dig_port)4874e390cabSriastradh bool intel_tc_port_connected(struct intel_digital_port *dig_port)
4884e390cabSriastradh {
4894e390cabSriastradh bool is_connected;
4904e390cabSriastradh
4914e390cabSriastradh intel_tc_port_lock(dig_port);
4924e390cabSriastradh is_connected = tc_port_live_status_mask(dig_port) &
4934e390cabSriastradh BIT(dig_port->tc_mode);
4944e390cabSriastradh intel_tc_port_unlock(dig_port);
4954e390cabSriastradh
4964e390cabSriastradh return is_connected;
4974e390cabSriastradh }
4984e390cabSriastradh
__intel_tc_port_lock(struct intel_digital_port * dig_port,int required_lanes)4994e390cabSriastradh static void __intel_tc_port_lock(struct intel_digital_port *dig_port,
5004e390cabSriastradh int required_lanes)
5014e390cabSriastradh {
5024e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
5034e390cabSriastradh intel_wakeref_t wakeref;
5044e390cabSriastradh
5054e390cabSriastradh wakeref = intel_display_power_get(i915, POWER_DOMAIN_DISPLAY_CORE);
5064e390cabSriastradh
5074e390cabSriastradh mutex_lock(&dig_port->tc_lock);
5084e390cabSriastradh
5094e390cabSriastradh if (!dig_port->tc_link_refcount &&
5104e390cabSriastradh intel_tc_port_needs_reset(dig_port))
5114e390cabSriastradh intel_tc_port_reset_mode(dig_port, required_lanes);
5124e390cabSriastradh
5134e390cabSriastradh WARN_ON(dig_port->tc_lock_wakeref);
5144e390cabSriastradh dig_port->tc_lock_wakeref = wakeref;
5154e390cabSriastradh }
5164e390cabSriastradh
intel_tc_port_lock(struct intel_digital_port * dig_port)5174e390cabSriastradh void intel_tc_port_lock(struct intel_digital_port *dig_port)
5184e390cabSriastradh {
5194e390cabSriastradh __intel_tc_port_lock(dig_port, 1);
5204e390cabSriastradh }
5214e390cabSriastradh
intel_tc_port_unlock(struct intel_digital_port * dig_port)5224e390cabSriastradh void intel_tc_port_unlock(struct intel_digital_port *dig_port)
5234e390cabSriastradh {
5244e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
5254e390cabSriastradh intel_wakeref_t wakeref = fetch_and_zero(&dig_port->tc_lock_wakeref);
5264e390cabSriastradh
5274e390cabSriastradh mutex_unlock(&dig_port->tc_lock);
5284e390cabSriastradh
5294e390cabSriastradh intel_display_power_put_async(i915, POWER_DOMAIN_DISPLAY_CORE,
5304e390cabSriastradh wakeref);
5314e390cabSriastradh }
5324e390cabSriastradh
intel_tc_port_ref_held(struct intel_digital_port * dig_port)5334e390cabSriastradh bool intel_tc_port_ref_held(struct intel_digital_port *dig_port)
5344e390cabSriastradh {
5354e390cabSriastradh return mutex_is_locked(&dig_port->tc_lock) ||
5364e390cabSriastradh dig_port->tc_link_refcount;
5374e390cabSriastradh }
5384e390cabSriastradh
intel_tc_port_get_link(struct intel_digital_port * dig_port,int required_lanes)5394e390cabSriastradh void intel_tc_port_get_link(struct intel_digital_port *dig_port,
5404e390cabSriastradh int required_lanes)
5414e390cabSriastradh {
5424e390cabSriastradh __intel_tc_port_lock(dig_port, required_lanes);
5434e390cabSriastradh dig_port->tc_link_refcount++;
5444e390cabSriastradh intel_tc_port_unlock(dig_port);
5454e390cabSriastradh }
5464e390cabSriastradh
intel_tc_port_put_link(struct intel_digital_port * dig_port)5474e390cabSriastradh void intel_tc_port_put_link(struct intel_digital_port *dig_port)
5484e390cabSriastradh {
5494e390cabSriastradh mutex_lock(&dig_port->tc_lock);
5504e390cabSriastradh dig_port->tc_link_refcount--;
5514e390cabSriastradh mutex_unlock(&dig_port->tc_lock);
5524e390cabSriastradh }
5534e390cabSriastradh
intel_tc_port_init(struct intel_digital_port * dig_port,bool is_legacy)5544e390cabSriastradh void intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy)
5554e390cabSriastradh {
5564e390cabSriastradh struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
5574e390cabSriastradh enum port port = dig_port->base.port;
5584e390cabSriastradh enum tc_port tc_port = intel_port_to_tc(i915, port);
5594e390cabSriastradh
5604e390cabSriastradh if (WARN_ON(tc_port == PORT_TC_NONE))
5614e390cabSriastradh return;
5624e390cabSriastradh
5634e390cabSriastradh snprintf(dig_port->tc_port_name, sizeof(dig_port->tc_port_name),
5644e390cabSriastradh "%c/TC#%d", port_name(port), tc_port + 1);
5654e390cabSriastradh
5664e390cabSriastradh mutex_init(&dig_port->tc_lock);
5674e390cabSriastradh dig_port->tc_legacy_port = is_legacy;
5684e390cabSriastradh dig_port->tc_link_refcount = 0;
5694e390cabSriastradh tc_port_load_fia_params(i915, dig_port);
5704e390cabSriastradh }
571