xref: /openbsd-src/sys/dev/pci/drm/i915/display/intel_combo_phy.c (revision f005ef32267c16bdb134f0e9fa4477dbe07c263a)
1c349dbc7Sjsg // SPDX-License-Identifier: MIT
2c349dbc7Sjsg /*
3c349dbc7Sjsg  * Copyright © 2018 Intel Corporation
4c349dbc7Sjsg  */
5c349dbc7Sjsg 
6*f005ef32Sjsg #include "i915_reg.h"
7c349dbc7Sjsg #include "intel_combo_phy.h"
81bb76ff1Sjsg #include "intel_combo_phy_regs.h"
95ca02815Sjsg #include "intel_de.h"
10c349dbc7Sjsg #include "intel_display_types.h"
11c349dbc7Sjsg 
12c349dbc7Sjsg #define for_each_combo_phy(__dev_priv, __phy) \
13c349dbc7Sjsg 	for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++)	\
14c349dbc7Sjsg 		for_each_if(intel_phy_is_combo(__dev_priv, __phy))
15c349dbc7Sjsg 
16c349dbc7Sjsg #define for_each_combo_phy_reverse(__dev_priv, __phy) \
17c349dbc7Sjsg 	for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) \
18c349dbc7Sjsg 		for_each_if(intel_phy_is_combo(__dev_priv, __phy))
19c349dbc7Sjsg 
20c349dbc7Sjsg enum {
21c349dbc7Sjsg 	PROCMON_0_85V_DOT_0,
22c349dbc7Sjsg 	PROCMON_0_95V_DOT_0,
23c349dbc7Sjsg 	PROCMON_0_95V_DOT_1,
24c349dbc7Sjsg 	PROCMON_1_05V_DOT_0,
25c349dbc7Sjsg 	PROCMON_1_05V_DOT_1,
26c349dbc7Sjsg };
27c349dbc7Sjsg 
285ca02815Sjsg static const struct icl_procmon {
291bb76ff1Sjsg 	const char *name;
30c349dbc7Sjsg 	u32 dw1, dw9, dw10;
315ca02815Sjsg } icl_procmon_values[] = {
321bb76ff1Sjsg 	[PROCMON_0_85V_DOT_0] = {
331bb76ff1Sjsg 		.name = "0.85V dot0 (low-voltage)",
341bb76ff1Sjsg 		.dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96,
351bb76ff1Sjsg 	},
361bb76ff1Sjsg 	[PROCMON_0_95V_DOT_0] = {
371bb76ff1Sjsg 		.name = "0.95V dot0",
381bb76ff1Sjsg 		.dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB,
391bb76ff1Sjsg 	},
401bb76ff1Sjsg 	[PROCMON_0_95V_DOT_1] = {
411bb76ff1Sjsg 		.name = "0.95V dot1",
421bb76ff1Sjsg 		.dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5,
431bb76ff1Sjsg 	},
441bb76ff1Sjsg 	[PROCMON_1_05V_DOT_0] = {
451bb76ff1Sjsg 		.name = "1.05V dot0",
461bb76ff1Sjsg 		.dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1,
471bb76ff1Sjsg 	},
481bb76ff1Sjsg 	[PROCMON_1_05V_DOT_1] = {
491bb76ff1Sjsg 		.name = "1.05V dot1",
501bb76ff1Sjsg 		.dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1,
511bb76ff1Sjsg 	},
52c349dbc7Sjsg };
53c349dbc7Sjsg 
545ca02815Sjsg static const struct icl_procmon *
icl_get_procmon_ref_values(struct drm_i915_private * dev_priv,enum phy phy)555ca02815Sjsg icl_get_procmon_ref_values(struct drm_i915_private *dev_priv, enum phy phy)
56c349dbc7Sjsg {
57c349dbc7Sjsg 	u32 val;
58c349dbc7Sjsg 
59c349dbc7Sjsg 	val = intel_de_read(dev_priv, ICL_PORT_COMP_DW3(phy));
60c349dbc7Sjsg 	switch (val & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) {
61c349dbc7Sjsg 	default:
62c349dbc7Sjsg 		MISSING_CASE(val);
63ad8b1aafSjsg 		fallthrough;
64c349dbc7Sjsg 	case VOLTAGE_INFO_0_85V | PROCESS_INFO_DOT_0:
65*f005ef32Sjsg 		return &icl_procmon_values[PROCMON_0_85V_DOT_0];
66c349dbc7Sjsg 	case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_0:
67*f005ef32Sjsg 		return &icl_procmon_values[PROCMON_0_95V_DOT_0];
68c349dbc7Sjsg 	case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_1:
69*f005ef32Sjsg 		return &icl_procmon_values[PROCMON_0_95V_DOT_1];
70c349dbc7Sjsg 	case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_0:
71*f005ef32Sjsg 		return &icl_procmon_values[PROCMON_1_05V_DOT_0];
72c349dbc7Sjsg 	case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_1:
73*f005ef32Sjsg 		return &icl_procmon_values[PROCMON_1_05V_DOT_1];
74c349dbc7Sjsg 	}
75c349dbc7Sjsg }
76c349dbc7Sjsg 
icl_set_procmon_ref_values(struct drm_i915_private * dev_priv,enum phy phy)775ca02815Sjsg static void icl_set_procmon_ref_values(struct drm_i915_private *dev_priv,
78c349dbc7Sjsg 				       enum phy phy)
79c349dbc7Sjsg {
805ca02815Sjsg 	const struct icl_procmon *procmon;
81c349dbc7Sjsg 
825ca02815Sjsg 	procmon = icl_get_procmon_ref_values(dev_priv, phy);
83c349dbc7Sjsg 
84*f005ef32Sjsg 	intel_de_rmw(dev_priv, ICL_PORT_COMP_DW1(phy),
85*f005ef32Sjsg 		     (0xff << 16) | 0xff, procmon->dw1);
86c349dbc7Sjsg 
87c349dbc7Sjsg 	intel_de_write(dev_priv, ICL_PORT_COMP_DW9(phy), procmon->dw9);
88c349dbc7Sjsg 	intel_de_write(dev_priv, ICL_PORT_COMP_DW10(phy), procmon->dw10);
89c349dbc7Sjsg }
90c349dbc7Sjsg 
check_phy_reg(struct drm_i915_private * dev_priv,enum phy phy,i915_reg_t reg,u32 mask,u32 expected_val)91c349dbc7Sjsg static bool check_phy_reg(struct drm_i915_private *dev_priv,
92c349dbc7Sjsg 			  enum phy phy, i915_reg_t reg, u32 mask,
93c349dbc7Sjsg 			  u32 expected_val)
94c349dbc7Sjsg {
95c349dbc7Sjsg 	u32 val = intel_de_read(dev_priv, reg);
96c349dbc7Sjsg 
97c349dbc7Sjsg 	if ((val & mask) != expected_val) {
98c349dbc7Sjsg 		drm_dbg(&dev_priv->drm,
99c349dbc7Sjsg 			"Combo PHY %c reg %08x state mismatch: "
100c349dbc7Sjsg 			"current %08x mask %08x expected %08x\n",
101c349dbc7Sjsg 			phy_name(phy),
102c349dbc7Sjsg 			reg.reg, val, mask, expected_val);
103c349dbc7Sjsg 		return false;
104c349dbc7Sjsg 	}
105c349dbc7Sjsg 
106c349dbc7Sjsg 	return true;
107c349dbc7Sjsg }
108c349dbc7Sjsg 
icl_verify_procmon_ref_values(struct drm_i915_private * dev_priv,enum phy phy)1095ca02815Sjsg static bool icl_verify_procmon_ref_values(struct drm_i915_private *dev_priv,
110c349dbc7Sjsg 					  enum phy phy)
111c349dbc7Sjsg {
1125ca02815Sjsg 	const struct icl_procmon *procmon;
113c349dbc7Sjsg 	bool ret;
114c349dbc7Sjsg 
1155ca02815Sjsg 	procmon = icl_get_procmon_ref_values(dev_priv, phy);
116c349dbc7Sjsg 
1171bb76ff1Sjsg 	drm_dbg_kms(&dev_priv->drm,
1181bb76ff1Sjsg 		    "Combo PHY %c Voltage/Process Info : %s\n",
1191bb76ff1Sjsg 		    phy_name(phy), procmon->name);
1201bb76ff1Sjsg 
121c349dbc7Sjsg 	ret = check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW1(phy),
122c349dbc7Sjsg 			    (0xff << 16) | 0xff, procmon->dw1);
123c349dbc7Sjsg 	ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW9(phy),
124c349dbc7Sjsg 			     -1U, procmon->dw9);
125c349dbc7Sjsg 	ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW10(phy),
126c349dbc7Sjsg 			     -1U, procmon->dw10);
127c349dbc7Sjsg 
128c349dbc7Sjsg 	return ret;
129c349dbc7Sjsg }
130c349dbc7Sjsg 
has_phy_misc(struct drm_i915_private * i915,enum phy phy)131ad8b1aafSjsg static bool has_phy_misc(struct drm_i915_private *i915, enum phy phy)
132ad8b1aafSjsg {
133ad8b1aafSjsg 	/*
134ad8b1aafSjsg 	 * Some platforms only expect PHY_MISC to be programmed for PHY-A and
135ad8b1aafSjsg 	 * PHY-B and may not even have instances of the register for the
136ad8b1aafSjsg 	 * other combo PHY's.
1375ca02815Sjsg 	 *
1385ca02815Sjsg 	 * ADL-S technically has three instances of PHY_MISC, but only requires
1395ca02815Sjsg 	 * that we program it for PHY A.
140ad8b1aafSjsg 	 */
1415ca02815Sjsg 
1425ca02815Sjsg 	if (IS_ALDERLAKE_S(i915))
1435ca02815Sjsg 		return phy == PHY_A;
144*f005ef32Sjsg 	else if ((IS_JASPERLAKE(i915) || IS_ELKHARTLAKE(i915)) ||
1455ca02815Sjsg 		 IS_ROCKETLAKE(i915) ||
1465ca02815Sjsg 		 IS_DG1(i915))
147ad8b1aafSjsg 		return phy < PHY_C;
148ad8b1aafSjsg 
149ad8b1aafSjsg 	return true;
150ad8b1aafSjsg }
151ad8b1aafSjsg 
icl_combo_phy_enabled(struct drm_i915_private * dev_priv,enum phy phy)152c349dbc7Sjsg static bool icl_combo_phy_enabled(struct drm_i915_private *dev_priv,
153c349dbc7Sjsg 				  enum phy phy)
154c349dbc7Sjsg {
155c349dbc7Sjsg 	/* The PHY C added by EHL has no PHY_MISC register */
156ad8b1aafSjsg 	if (!has_phy_misc(dev_priv, phy))
157c349dbc7Sjsg 		return intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT;
158c349dbc7Sjsg 	else
159c349dbc7Sjsg 		return !(intel_de_read(dev_priv, ICL_PHY_MISC(phy)) &
160c349dbc7Sjsg 			 ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN) &&
161c349dbc7Sjsg 			(intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT);
162c349dbc7Sjsg }
163c349dbc7Sjsg 
ehl_vbt_ddi_d_present(struct drm_i915_private * i915)164c349dbc7Sjsg static bool ehl_vbt_ddi_d_present(struct drm_i915_private *i915)
165c349dbc7Sjsg {
166c349dbc7Sjsg 	bool ddi_a_present = intel_bios_is_port_present(i915, PORT_A);
167c349dbc7Sjsg 	bool ddi_d_present = intel_bios_is_port_present(i915, PORT_D);
168c349dbc7Sjsg 	bool dsi_present = intel_bios_is_dsi_present(i915, NULL);
169c349dbc7Sjsg 
170c349dbc7Sjsg 	/*
171c349dbc7Sjsg 	 * VBT's 'dvo port' field for child devices references the DDI, not
172c349dbc7Sjsg 	 * the PHY.  So if combo PHY A is wired up to drive an external
173c349dbc7Sjsg 	 * display, we should see a child device present on PORT_D and
174c349dbc7Sjsg 	 * nothing on PORT_A and no DSI.
175c349dbc7Sjsg 	 */
176c349dbc7Sjsg 	if (ddi_d_present && !ddi_a_present && !dsi_present)
177c349dbc7Sjsg 		return true;
178c349dbc7Sjsg 
179c349dbc7Sjsg 	/*
180c349dbc7Sjsg 	 * If we encounter a VBT that claims to have an external display on
181c349dbc7Sjsg 	 * DDI-D _and_ an internal display on DDI-A/DSI leave an error message
182c349dbc7Sjsg 	 * in the log and let the internal display win.
183c349dbc7Sjsg 	 */
184c349dbc7Sjsg 	if (ddi_d_present)
185c349dbc7Sjsg 		drm_err(&i915->drm,
186c349dbc7Sjsg 			"VBT claims to have both internal and external displays on PHY A.  Configuring for internal.\n");
187c349dbc7Sjsg 
188c349dbc7Sjsg 	return false;
189c349dbc7Sjsg }
190c349dbc7Sjsg 
phy_is_master(struct drm_i915_private * dev_priv,enum phy phy)191ad8b1aafSjsg static bool phy_is_master(struct drm_i915_private *dev_priv, enum phy phy)
192ad8b1aafSjsg {
193ad8b1aafSjsg 	/*
194ad8b1aafSjsg 	 * Certain PHYs are connected to compensation resistors and act
195ad8b1aafSjsg 	 * as masters to other PHYs.
196ad8b1aafSjsg 	 *
197ad8b1aafSjsg 	 * ICL,TGL:
198ad8b1aafSjsg 	 *   A(master) -> B(slave), C(slave)
1995ca02815Sjsg 	 * RKL,DG1:
200ad8b1aafSjsg 	 *   A(master) -> B(slave)
201ad8b1aafSjsg 	 *   C(master) -> D(slave)
2025ca02815Sjsg 	 * ADL-S:
2035ca02815Sjsg 	 *   A(master) -> B(slave), C(slave)
2045ca02815Sjsg 	 *   D(master) -> E(slave)
205ad8b1aafSjsg 	 *
206ad8b1aafSjsg 	 * We must set the IREFGEN bit for any PHY acting as a master
207ad8b1aafSjsg 	 * to another PHY.
208ad8b1aafSjsg 	 */
2095ca02815Sjsg 	if (phy == PHY_A)
210ad8b1aafSjsg 		return true;
2115ca02815Sjsg 	else if (IS_ALDERLAKE_S(dev_priv))
2125ca02815Sjsg 		return phy == PHY_D;
2135ca02815Sjsg 	else if (IS_DG1(dev_priv) || IS_ROCKETLAKE(dev_priv))
2145ca02815Sjsg 		return phy == PHY_C;
215ad8b1aafSjsg 
2165ca02815Sjsg 	return false;
217ad8b1aafSjsg }
218ad8b1aafSjsg 
icl_combo_phy_verify_state(struct drm_i915_private * dev_priv,enum phy phy)219c349dbc7Sjsg static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv,
220c349dbc7Sjsg 				       enum phy phy)
221c349dbc7Sjsg {
222ad8b1aafSjsg 	bool ret = true;
223c349dbc7Sjsg 	u32 expected_val = 0;
224c349dbc7Sjsg 
225c349dbc7Sjsg 	if (!icl_combo_phy_enabled(dev_priv, phy))
226c349dbc7Sjsg 		return false;
227c349dbc7Sjsg 
2285ca02815Sjsg 	if (DISPLAY_VER(dev_priv) >= 12) {
2291bb76ff1Sjsg 		ret &= check_phy_reg(dev_priv, phy, ICL_PORT_TX_DW8_LN(0, phy),
230ad8b1aafSjsg 				     ICL_PORT_TX_DW8_ODCC_CLK_SEL |
231ad8b1aafSjsg 				     ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK,
232ad8b1aafSjsg 				     ICL_PORT_TX_DW8_ODCC_CLK_SEL |
233ad8b1aafSjsg 				     ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2);
234c349dbc7Sjsg 
2351bb76ff1Sjsg 		ret &= check_phy_reg(dev_priv, phy, ICL_PORT_PCS_DW1_LN(0, phy),
236*f005ef32Sjsg 				     DCC_MODE_SELECT_MASK, RUN_DCC_ONCE);
237ad8b1aafSjsg 	}
238ad8b1aafSjsg 
2395ca02815Sjsg 	ret &= icl_verify_procmon_ref_values(dev_priv, phy);
240ad8b1aafSjsg 
241ad8b1aafSjsg 	if (phy_is_master(dev_priv, phy)) {
242c349dbc7Sjsg 		ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW8(phy),
243c349dbc7Sjsg 				     IREFGEN, IREFGEN);
244c349dbc7Sjsg 
245*f005ef32Sjsg 		if (IS_JASPERLAKE(dev_priv) || IS_ELKHARTLAKE(dev_priv)) {
246c349dbc7Sjsg 			if (ehl_vbt_ddi_d_present(dev_priv))
247c349dbc7Sjsg 				expected_val = ICL_PHY_MISC_MUX_DDID;
248c349dbc7Sjsg 
249c349dbc7Sjsg 			ret &= check_phy_reg(dev_priv, phy, ICL_PHY_MISC(phy),
250c349dbc7Sjsg 					     ICL_PHY_MISC_MUX_DDID,
251c349dbc7Sjsg 					     expected_val);
252c349dbc7Sjsg 		}
253c349dbc7Sjsg 	}
254c349dbc7Sjsg 
255c349dbc7Sjsg 	ret &= check_phy_reg(dev_priv, phy, ICL_PORT_CL_DW5(phy),
256c349dbc7Sjsg 			     CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
257c349dbc7Sjsg 
258c349dbc7Sjsg 	return ret;
259c349dbc7Sjsg }
260c349dbc7Sjsg 
intel_combo_phy_power_up_lanes(struct drm_i915_private * dev_priv,enum phy phy,bool is_dsi,int lane_count,bool lane_reversal)261c349dbc7Sjsg void intel_combo_phy_power_up_lanes(struct drm_i915_private *dev_priv,
262c349dbc7Sjsg 				    enum phy phy, bool is_dsi,
263c349dbc7Sjsg 				    int lane_count, bool lane_reversal)
264c349dbc7Sjsg {
265c349dbc7Sjsg 	u8 lane_mask;
266c349dbc7Sjsg 
267c349dbc7Sjsg 	if (is_dsi) {
268c349dbc7Sjsg 		drm_WARN_ON(&dev_priv->drm, lane_reversal);
269c349dbc7Sjsg 
270c349dbc7Sjsg 		switch (lane_count) {
271c349dbc7Sjsg 		case 1:
272c349dbc7Sjsg 			lane_mask = PWR_DOWN_LN_3_1_0;
273c349dbc7Sjsg 			break;
274c349dbc7Sjsg 		case 2:
275c349dbc7Sjsg 			lane_mask = PWR_DOWN_LN_3_1;
276c349dbc7Sjsg 			break;
277c349dbc7Sjsg 		case 3:
278c349dbc7Sjsg 			lane_mask = PWR_DOWN_LN_3;
279c349dbc7Sjsg 			break;
280c349dbc7Sjsg 		default:
281c349dbc7Sjsg 			MISSING_CASE(lane_count);
282ad8b1aafSjsg 			fallthrough;
283c349dbc7Sjsg 		case 4:
284c349dbc7Sjsg 			lane_mask = PWR_UP_ALL_LANES;
285c349dbc7Sjsg 			break;
286c349dbc7Sjsg 		}
287c349dbc7Sjsg 	} else {
288c349dbc7Sjsg 		switch (lane_count) {
289c349dbc7Sjsg 		case 1:
290c349dbc7Sjsg 			lane_mask = lane_reversal ? PWR_DOWN_LN_2_1_0 :
291c349dbc7Sjsg 						    PWR_DOWN_LN_3_2_1;
292c349dbc7Sjsg 			break;
293c349dbc7Sjsg 		case 2:
294c349dbc7Sjsg 			lane_mask = lane_reversal ? PWR_DOWN_LN_1_0 :
295c349dbc7Sjsg 						    PWR_DOWN_LN_3_2;
296c349dbc7Sjsg 			break;
297c349dbc7Sjsg 		default:
298c349dbc7Sjsg 			MISSING_CASE(lane_count);
299ad8b1aafSjsg 			fallthrough;
300c349dbc7Sjsg 		case 4:
301c349dbc7Sjsg 			lane_mask = PWR_UP_ALL_LANES;
302c349dbc7Sjsg 			break;
303c349dbc7Sjsg 		}
304c349dbc7Sjsg 	}
305c349dbc7Sjsg 
306*f005ef32Sjsg 	intel_de_rmw(dev_priv, ICL_PORT_CL_DW10(phy),
307*f005ef32Sjsg 		     PWR_DOWN_LN_MASK, lane_mask);
308c349dbc7Sjsg }
309c349dbc7Sjsg 
icl_combo_phys_init(struct drm_i915_private * dev_priv)310c349dbc7Sjsg static void icl_combo_phys_init(struct drm_i915_private *dev_priv)
311c349dbc7Sjsg {
312c349dbc7Sjsg 	enum phy phy;
313c349dbc7Sjsg 
314c349dbc7Sjsg 	for_each_combo_phy(dev_priv, phy) {
315c349dbc7Sjsg 		u32 val;
316c349dbc7Sjsg 
317c349dbc7Sjsg 		if (icl_combo_phy_verify_state(dev_priv, phy)) {
318c349dbc7Sjsg 			drm_dbg(&dev_priv->drm,
319c349dbc7Sjsg 				"Combo PHY %c already enabled, won't reprogram it.\n",
320c349dbc7Sjsg 				phy_name(phy));
321c349dbc7Sjsg 			continue;
322c349dbc7Sjsg 		}
323c349dbc7Sjsg 
324ad8b1aafSjsg 		if (!has_phy_misc(dev_priv, phy))
325c349dbc7Sjsg 			goto skip_phy_misc;
326c349dbc7Sjsg 
327c349dbc7Sjsg 		/*
328c349dbc7Sjsg 		 * EHL's combo PHY A can be hooked up to either an external
329c349dbc7Sjsg 		 * display (via DDI-D) or an internal display (via DDI-A or
330c349dbc7Sjsg 		 * the DSI DPHY).  This is a motherboard design decision that
331c349dbc7Sjsg 		 * can't be changed on the fly, so initialize the PHY's mux
332c349dbc7Sjsg 		 * based on whether our VBT indicates the presence of any
333c349dbc7Sjsg 		 * "internal" child devices.
334c349dbc7Sjsg 		 */
335c349dbc7Sjsg 		val = intel_de_read(dev_priv, ICL_PHY_MISC(phy));
336*f005ef32Sjsg 		if ((IS_JASPERLAKE(dev_priv) || IS_ELKHARTLAKE(dev_priv)) &&
337*f005ef32Sjsg 		    phy == PHY_A) {
338c349dbc7Sjsg 			val &= ~ICL_PHY_MISC_MUX_DDID;
339c349dbc7Sjsg 
340c349dbc7Sjsg 			if (ehl_vbt_ddi_d_present(dev_priv))
341c349dbc7Sjsg 				val |= ICL_PHY_MISC_MUX_DDID;
342c349dbc7Sjsg 		}
343c349dbc7Sjsg 
344c349dbc7Sjsg 		val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
345c349dbc7Sjsg 		intel_de_write(dev_priv, ICL_PHY_MISC(phy), val);
346c349dbc7Sjsg 
347c349dbc7Sjsg skip_phy_misc:
3485ca02815Sjsg 		if (DISPLAY_VER(dev_priv) >= 12) {
3491bb76ff1Sjsg 			val = intel_de_read(dev_priv, ICL_PORT_TX_DW8_LN(0, phy));
350ad8b1aafSjsg 			val &= ~ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK;
351ad8b1aafSjsg 			val |= ICL_PORT_TX_DW8_ODCC_CLK_SEL;
352ad8b1aafSjsg 			val |= ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2;
353ad8b1aafSjsg 			intel_de_write(dev_priv, ICL_PORT_TX_DW8_GRP(phy), val);
354ad8b1aafSjsg 
3551bb76ff1Sjsg 			val = intel_de_read(dev_priv, ICL_PORT_PCS_DW1_LN(0, phy));
356ad8b1aafSjsg 			val &= ~DCC_MODE_SELECT_MASK;
357*f005ef32Sjsg 			val |= RUN_DCC_ONCE;
358ad8b1aafSjsg 			intel_de_write(dev_priv, ICL_PORT_PCS_DW1_GRP(phy), val);
359ad8b1aafSjsg 		}
360ad8b1aafSjsg 
3615ca02815Sjsg 		icl_set_procmon_ref_values(dev_priv, phy);
362c349dbc7Sjsg 
363*f005ef32Sjsg 		if (phy_is_master(dev_priv, phy))
364*f005ef32Sjsg 			intel_de_rmw(dev_priv, ICL_PORT_COMP_DW8(phy),
365*f005ef32Sjsg 				     0, IREFGEN);
366c349dbc7Sjsg 
367*f005ef32Sjsg 		intel_de_rmw(dev_priv, ICL_PORT_COMP_DW0(phy), 0, COMP_INIT);
368*f005ef32Sjsg 		intel_de_rmw(dev_priv, ICL_PORT_CL_DW5(phy),
369*f005ef32Sjsg 			     0, CL_POWER_DOWN_ENABLE);
370c349dbc7Sjsg 	}
371c349dbc7Sjsg }
372c349dbc7Sjsg 
icl_combo_phys_uninit(struct drm_i915_private * dev_priv)373c349dbc7Sjsg static void icl_combo_phys_uninit(struct drm_i915_private *dev_priv)
374c349dbc7Sjsg {
375c349dbc7Sjsg 	enum phy phy;
376c349dbc7Sjsg 
377c349dbc7Sjsg 	for_each_combo_phy_reverse(dev_priv, phy) {
378c349dbc7Sjsg 		if (phy == PHY_A &&
3795ca02815Sjsg 		    !icl_combo_phy_verify_state(dev_priv, phy)) {
3805ca02815Sjsg 			if (IS_TIGERLAKE(dev_priv) || IS_DG1(dev_priv)) {
3815ca02815Sjsg 				/*
3825ca02815Sjsg 				 * A known problem with old ifwi:
3835ca02815Sjsg 				 * https://gitlab.freedesktop.org/drm/intel/-/issues/2411
3845ca02815Sjsg 				 * Suppress the warning for CI. Remove ASAP!
3855ca02815Sjsg 				 */
3865ca02815Sjsg 				drm_dbg_kms(&dev_priv->drm,
3875ca02815Sjsg 					    "Combo PHY %c HW state changed unexpectedly\n",
3885ca02815Sjsg 					    phy_name(phy));
3895ca02815Sjsg 			} else {
390c349dbc7Sjsg 				drm_warn(&dev_priv->drm,
391c349dbc7Sjsg 					 "Combo PHY %c HW state changed unexpectedly\n",
392c349dbc7Sjsg 					 phy_name(phy));
3935ca02815Sjsg 			}
3945ca02815Sjsg 		}
395c349dbc7Sjsg 
396ad8b1aafSjsg 		if (!has_phy_misc(dev_priv, phy))
397c349dbc7Sjsg 			goto skip_phy_misc;
398c349dbc7Sjsg 
399*f005ef32Sjsg 		intel_de_rmw(dev_priv, ICL_PHY_MISC(phy), 0,
400*f005ef32Sjsg 			     ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN);
401c349dbc7Sjsg 
402c349dbc7Sjsg skip_phy_misc:
403*f005ef32Sjsg 		intel_de_rmw(dev_priv, ICL_PORT_COMP_DW0(phy), COMP_INIT, 0);
404c349dbc7Sjsg 	}
405c349dbc7Sjsg }
406c349dbc7Sjsg 
intel_combo_phy_init(struct drm_i915_private * i915)407c349dbc7Sjsg void intel_combo_phy_init(struct drm_i915_private *i915)
408c349dbc7Sjsg {
409c349dbc7Sjsg 	icl_combo_phys_init(i915);
410c349dbc7Sjsg }
411c349dbc7Sjsg 
intel_combo_phy_uninit(struct drm_i915_private * i915)412c349dbc7Sjsg void intel_combo_phy_uninit(struct drm_i915_private *i915)
413c349dbc7Sjsg {
414c349dbc7Sjsg 	icl_combo_phys_uninit(i915);
415c349dbc7Sjsg }
416