xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/i915/display/intel_tc.c (revision 9e5fbd4f7f25d0a357b482f4591196ee62d90ed5)
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