1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2018 Intel Corporation
4  */
5 
6 #include "intel_combo_phy.h"
7 #include "intel_de.h"
8 #include "intel_display_types.h"
9 
10 #define for_each_combo_phy(__dev_priv, __phy) \
11 	for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++)	\
12 		for_each_if(intel_phy_is_combo(__dev_priv, __phy))
13 
14 #define for_each_combo_phy_reverse(__dev_priv, __phy) \
15 	for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) \
16 		for_each_if(intel_phy_is_combo(__dev_priv, __phy))
17 
18 enum {
19 	PROCMON_0_85V_DOT_0,
20 	PROCMON_0_95V_DOT_0,
21 	PROCMON_0_95V_DOT_1,
22 	PROCMON_1_05V_DOT_0,
23 	PROCMON_1_05V_DOT_1,
24 };
25 
26 static const struct cnl_procmon {
27 	u32 dw1, dw9, dw10;
28 } cnl_procmon_values[] = {
29 	[PROCMON_0_85V_DOT_0] =
30 		{ .dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96, },
31 	[PROCMON_0_95V_DOT_0] =
32 		{ .dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB, },
33 	[PROCMON_0_95V_DOT_1] =
34 		{ .dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5, },
35 	[PROCMON_1_05V_DOT_0] =
36 		{ .dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1, },
37 	[PROCMON_1_05V_DOT_1] =
38 		{ .dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1, },
39 };
40 
41 /*
42  * CNL has just one set of registers, while gen11 has a set for each combo PHY.
43  * The CNL registers are equivalent to the gen11 PHY A registers, that's why we
44  * call the ICL macros even though the function has CNL on its name.
45  */
46 static const struct cnl_procmon *
47 cnl_get_procmon_ref_values(struct drm_i915_private *dev_priv, enum phy phy)
48 {
49 	const struct cnl_procmon *procmon;
50 	u32 val;
51 
52 	val = intel_de_read(dev_priv, ICL_PORT_COMP_DW3(phy));
53 	switch (val & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) {
54 	default:
55 		MISSING_CASE(val);
56 		fallthrough;
57 	case VOLTAGE_INFO_0_85V | PROCESS_INFO_DOT_0:
58 		procmon = &cnl_procmon_values[PROCMON_0_85V_DOT_0];
59 		break;
60 	case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_0:
61 		procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_0];
62 		break;
63 	case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_1:
64 		procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_1];
65 		break;
66 	case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_0:
67 		procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_0];
68 		break;
69 	case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_1:
70 		procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_1];
71 		break;
72 	}
73 
74 	return procmon;
75 }
76 
77 static void cnl_set_procmon_ref_values(struct drm_i915_private *dev_priv,
78 				       enum phy phy)
79 {
80 	const struct cnl_procmon *procmon;
81 	u32 val;
82 
83 	procmon = cnl_get_procmon_ref_values(dev_priv, phy);
84 
85 	val = intel_de_read(dev_priv, ICL_PORT_COMP_DW1(phy));
86 	val &= ~((0xff << 16) | 0xff);
87 	val |= procmon->dw1;
88 	intel_de_write(dev_priv, ICL_PORT_COMP_DW1(phy), val);
89 
90 	intel_de_write(dev_priv, ICL_PORT_COMP_DW9(phy), procmon->dw9);
91 	intel_de_write(dev_priv, ICL_PORT_COMP_DW10(phy), procmon->dw10);
92 }
93 
94 static bool check_phy_reg(struct drm_i915_private *dev_priv,
95 			  enum phy phy, i915_reg_t reg, u32 mask,
96 			  u32 expected_val)
97 {
98 	u32 val = intel_de_read(dev_priv, reg);
99 
100 	if ((val & mask) != expected_val) {
101 		drm_dbg(&dev_priv->drm,
102 			"Combo PHY %c reg %08x state mismatch: "
103 			"current %08x mask %08x expected %08x\n",
104 			phy_name(phy),
105 			reg.reg, val, mask, expected_val);
106 		return false;
107 	}
108 
109 	return true;
110 }
111 
112 static bool cnl_verify_procmon_ref_values(struct drm_i915_private *dev_priv,
113 					  enum phy phy)
114 {
115 	const struct cnl_procmon *procmon;
116 	bool ret;
117 
118 	procmon = cnl_get_procmon_ref_values(dev_priv, phy);
119 
120 	ret = check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW1(phy),
121 			    (0xff << 16) | 0xff, procmon->dw1);
122 	ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW9(phy),
123 			     -1U, procmon->dw9);
124 	ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW10(phy),
125 			     -1U, procmon->dw10);
126 
127 	return ret;
128 }
129 
130 static bool cnl_combo_phy_enabled(struct drm_i915_private *dev_priv)
131 {
132 	return !(intel_de_read(dev_priv, CHICKEN_MISC_2) & CNL_COMP_PWR_DOWN) &&
133 		(intel_de_read(dev_priv, CNL_PORT_COMP_DW0) & COMP_INIT);
134 }
135 
136 static bool cnl_combo_phy_verify_state(struct drm_i915_private *dev_priv)
137 {
138 	enum phy phy = PHY_A;
139 	bool ret;
140 
141 	if (!cnl_combo_phy_enabled(dev_priv))
142 		return false;
143 
144 	ret = cnl_verify_procmon_ref_values(dev_priv, phy);
145 
146 	ret &= check_phy_reg(dev_priv, phy, CNL_PORT_CL1CM_DW5,
147 			     CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
148 
149 	return ret;
150 }
151 
152 static void cnl_combo_phys_init(struct drm_i915_private *dev_priv)
153 {
154 	u32 val;
155 
156 	val = intel_de_read(dev_priv, CHICKEN_MISC_2);
157 	val &= ~CNL_COMP_PWR_DOWN;
158 	intel_de_write(dev_priv, CHICKEN_MISC_2, val);
159 
160 	/* Dummy PORT_A to get the correct CNL register from the ICL macro */
161 	cnl_set_procmon_ref_values(dev_priv, PHY_A);
162 
163 	val = intel_de_read(dev_priv, CNL_PORT_COMP_DW0);
164 	val |= COMP_INIT;
165 	intel_de_write(dev_priv, CNL_PORT_COMP_DW0, val);
166 
167 	val = intel_de_read(dev_priv, CNL_PORT_CL1CM_DW5);
168 	val |= CL_POWER_DOWN_ENABLE;
169 	intel_de_write(dev_priv, CNL_PORT_CL1CM_DW5, val);
170 }
171 
172 static void cnl_combo_phys_uninit(struct drm_i915_private *dev_priv)
173 {
174 	u32 val;
175 
176 	if (!cnl_combo_phy_verify_state(dev_priv))
177 		drm_warn(&dev_priv->drm,
178 			 "Combo PHY HW state changed unexpectedly.\n");
179 
180 	val = intel_de_read(dev_priv, CHICKEN_MISC_2);
181 	val |= CNL_COMP_PWR_DOWN;
182 	intel_de_write(dev_priv, CHICKEN_MISC_2, val);
183 }
184 
185 static bool has_phy_misc(struct drm_i915_private *i915, enum phy phy)
186 {
187 	/*
188 	 * Some platforms only expect PHY_MISC to be programmed for PHY-A and
189 	 * PHY-B and may not even have instances of the register for the
190 	 * other combo PHY's.
191 	 *
192 	 * ADL-S technically has three instances of PHY_MISC, but only requires
193 	 * that we program it for PHY A.
194 	 */
195 
196 	if (IS_ALDERLAKE_S(i915))
197 		return phy == PHY_A;
198 	else if (IS_JSL_EHL(i915) ||
199 		 IS_ROCKETLAKE(i915) ||
200 		 IS_DG1(i915))
201 		return phy < PHY_C;
202 
203 	return true;
204 }
205 
206 static bool icl_combo_phy_enabled(struct drm_i915_private *dev_priv,
207 				  enum phy phy)
208 {
209 	/* The PHY C added by EHL has no PHY_MISC register */
210 	if (!has_phy_misc(dev_priv, phy))
211 		return intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT;
212 	else
213 		return !(intel_de_read(dev_priv, ICL_PHY_MISC(phy)) &
214 			 ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN) &&
215 			(intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT);
216 }
217 
218 static bool ehl_vbt_ddi_d_present(struct drm_i915_private *i915)
219 {
220 	bool ddi_a_present = intel_bios_is_port_present(i915, PORT_A);
221 	bool ddi_d_present = intel_bios_is_port_present(i915, PORT_D);
222 	bool dsi_present = intel_bios_is_dsi_present(i915, NULL);
223 
224 	/*
225 	 * VBT's 'dvo port' field for child devices references the DDI, not
226 	 * the PHY.  So if combo PHY A is wired up to drive an external
227 	 * display, we should see a child device present on PORT_D and
228 	 * nothing on PORT_A and no DSI.
229 	 */
230 	if (ddi_d_present && !ddi_a_present && !dsi_present)
231 		return true;
232 
233 	/*
234 	 * If we encounter a VBT that claims to have an external display on
235 	 * DDI-D _and_ an internal display on DDI-A/DSI leave an error message
236 	 * in the log and let the internal display win.
237 	 */
238 	if (ddi_d_present)
239 		drm_err(&i915->drm,
240 			"VBT claims to have both internal and external displays on PHY A.  Configuring for internal.\n");
241 
242 	return false;
243 }
244 
245 static bool phy_is_master(struct drm_i915_private *dev_priv, enum phy phy)
246 {
247 	/*
248 	 * Certain PHYs are connected to compensation resistors and act
249 	 * as masters to other PHYs.
250 	 *
251 	 * ICL,TGL:
252 	 *   A(master) -> B(slave), C(slave)
253 	 * RKL,DG1:
254 	 *   A(master) -> B(slave)
255 	 *   C(master) -> D(slave)
256 	 * ADL-S:
257 	 *   A(master) -> B(slave), C(slave)
258 	 *   D(master) -> E(slave)
259 	 *
260 	 * We must set the IREFGEN bit for any PHY acting as a master
261 	 * to another PHY.
262 	 */
263 	if (phy == PHY_A)
264 		return true;
265 	else if (IS_ALDERLAKE_S(dev_priv))
266 		return phy == PHY_D;
267 	else if (IS_DG1(dev_priv) || IS_ROCKETLAKE(dev_priv))
268 		return phy == PHY_C;
269 
270 	return false;
271 }
272 
273 static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv,
274 				       enum phy phy)
275 {
276 	bool ret = true;
277 	u32 expected_val = 0;
278 
279 	if (!icl_combo_phy_enabled(dev_priv, phy))
280 		return false;
281 
282 	if (DISPLAY_VER(dev_priv) >= 12) {
283 		ret &= check_phy_reg(dev_priv, phy, ICL_PORT_TX_DW8_LN0(phy),
284 				     ICL_PORT_TX_DW8_ODCC_CLK_SEL |
285 				     ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK,
286 				     ICL_PORT_TX_DW8_ODCC_CLK_SEL |
287 				     ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2);
288 
289 		ret &= check_phy_reg(dev_priv, phy, ICL_PORT_PCS_DW1_LN0(phy),
290 				     DCC_MODE_SELECT_MASK,
291 				     DCC_MODE_SELECT_CONTINUOSLY);
292 	}
293 
294 	ret &= cnl_verify_procmon_ref_values(dev_priv, phy);
295 
296 	if (phy_is_master(dev_priv, phy)) {
297 		ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW8(phy),
298 				     IREFGEN, IREFGEN);
299 
300 		if (IS_JSL_EHL(dev_priv)) {
301 			if (ehl_vbt_ddi_d_present(dev_priv))
302 				expected_val = ICL_PHY_MISC_MUX_DDID;
303 
304 			ret &= check_phy_reg(dev_priv, phy, ICL_PHY_MISC(phy),
305 					     ICL_PHY_MISC_MUX_DDID,
306 					     expected_val);
307 		}
308 	}
309 
310 	ret &= check_phy_reg(dev_priv, phy, ICL_PORT_CL_DW5(phy),
311 			     CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
312 
313 	return ret;
314 }
315 
316 void intel_combo_phy_power_up_lanes(struct drm_i915_private *dev_priv,
317 				    enum phy phy, bool is_dsi,
318 				    int lane_count, bool lane_reversal)
319 {
320 	u8 lane_mask;
321 	u32 val;
322 
323 	if (is_dsi) {
324 		drm_WARN_ON(&dev_priv->drm, lane_reversal);
325 
326 		switch (lane_count) {
327 		case 1:
328 			lane_mask = PWR_DOWN_LN_3_1_0;
329 			break;
330 		case 2:
331 			lane_mask = PWR_DOWN_LN_3_1;
332 			break;
333 		case 3:
334 			lane_mask = PWR_DOWN_LN_3;
335 			break;
336 		default:
337 			MISSING_CASE(lane_count);
338 			fallthrough;
339 		case 4:
340 			lane_mask = PWR_UP_ALL_LANES;
341 			break;
342 		}
343 	} else {
344 		switch (lane_count) {
345 		case 1:
346 			lane_mask = lane_reversal ? PWR_DOWN_LN_2_1_0 :
347 						    PWR_DOWN_LN_3_2_1;
348 			break;
349 		case 2:
350 			lane_mask = lane_reversal ? PWR_DOWN_LN_1_0 :
351 						    PWR_DOWN_LN_3_2;
352 			break;
353 		default:
354 			MISSING_CASE(lane_count);
355 			fallthrough;
356 		case 4:
357 			lane_mask = PWR_UP_ALL_LANES;
358 			break;
359 		}
360 	}
361 
362 	val = intel_de_read(dev_priv, ICL_PORT_CL_DW10(phy));
363 	val &= ~PWR_DOWN_LN_MASK;
364 	val |= lane_mask << PWR_DOWN_LN_SHIFT;
365 	intel_de_write(dev_priv, ICL_PORT_CL_DW10(phy), val);
366 }
367 
368 static void icl_combo_phys_init(struct drm_i915_private *dev_priv)
369 {
370 	enum phy phy;
371 
372 	for_each_combo_phy(dev_priv, phy) {
373 		u32 val;
374 
375 		if (icl_combo_phy_verify_state(dev_priv, phy)) {
376 			drm_dbg(&dev_priv->drm,
377 				"Combo PHY %c already enabled, won't reprogram it.\n",
378 				phy_name(phy));
379 			continue;
380 		}
381 
382 		if (!has_phy_misc(dev_priv, phy))
383 			goto skip_phy_misc;
384 
385 		/*
386 		 * EHL's combo PHY A can be hooked up to either an external
387 		 * display (via DDI-D) or an internal display (via DDI-A or
388 		 * the DSI DPHY).  This is a motherboard design decision that
389 		 * can't be changed on the fly, so initialize the PHY's mux
390 		 * based on whether our VBT indicates the presence of any
391 		 * "internal" child devices.
392 		 */
393 		val = intel_de_read(dev_priv, ICL_PHY_MISC(phy));
394 		if (IS_JSL_EHL(dev_priv) && phy == PHY_A) {
395 			val &= ~ICL_PHY_MISC_MUX_DDID;
396 
397 			if (ehl_vbt_ddi_d_present(dev_priv))
398 				val |= ICL_PHY_MISC_MUX_DDID;
399 		}
400 
401 		val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
402 		intel_de_write(dev_priv, ICL_PHY_MISC(phy), val);
403 
404 skip_phy_misc:
405 		if (DISPLAY_VER(dev_priv) >= 12) {
406 			val = intel_de_read(dev_priv, ICL_PORT_TX_DW8_LN0(phy));
407 			val &= ~ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK;
408 			val |= ICL_PORT_TX_DW8_ODCC_CLK_SEL;
409 			val |= ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2;
410 			intel_de_write(dev_priv, ICL_PORT_TX_DW8_GRP(phy), val);
411 
412 			val = intel_de_read(dev_priv, ICL_PORT_PCS_DW1_LN0(phy));
413 			val &= ~DCC_MODE_SELECT_MASK;
414 			val |= DCC_MODE_SELECT_CONTINUOSLY;
415 			intel_de_write(dev_priv, ICL_PORT_PCS_DW1_GRP(phy), val);
416 		}
417 
418 		cnl_set_procmon_ref_values(dev_priv, phy);
419 
420 		if (phy_is_master(dev_priv, phy)) {
421 			val = intel_de_read(dev_priv, ICL_PORT_COMP_DW8(phy));
422 			val |= IREFGEN;
423 			intel_de_write(dev_priv, ICL_PORT_COMP_DW8(phy), val);
424 		}
425 
426 		val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy));
427 		val |= COMP_INIT;
428 		intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy), val);
429 
430 		val = intel_de_read(dev_priv, ICL_PORT_CL_DW5(phy));
431 		val |= CL_POWER_DOWN_ENABLE;
432 		intel_de_write(dev_priv, ICL_PORT_CL_DW5(phy), val);
433 	}
434 }
435 
436 static void icl_combo_phys_uninit(struct drm_i915_private *dev_priv)
437 {
438 	enum phy phy;
439 
440 	for_each_combo_phy_reverse(dev_priv, phy) {
441 		u32 val;
442 
443 		if (phy == PHY_A &&
444 		    !icl_combo_phy_verify_state(dev_priv, phy)) {
445 			if (IS_TIGERLAKE(dev_priv) || IS_DG1(dev_priv)) {
446 				/*
447 				 * A known problem with old ifwi:
448 				 * https://gitlab.freedesktop.org/drm/intel/-/issues/2411
449 				 * Suppress the warning for CI. Remove ASAP!
450 				 */
451 				drm_dbg_kms(&dev_priv->drm,
452 					    "Combo PHY %c HW state changed unexpectedly\n",
453 					    phy_name(phy));
454 			} else {
455 				drm_warn(&dev_priv->drm,
456 					 "Combo PHY %c HW state changed unexpectedly\n",
457 					 phy_name(phy));
458 			}
459 		}
460 
461 		if (!has_phy_misc(dev_priv, phy))
462 			goto skip_phy_misc;
463 
464 		val = intel_de_read(dev_priv, ICL_PHY_MISC(phy));
465 		val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
466 		intel_de_write(dev_priv, ICL_PHY_MISC(phy), val);
467 
468 skip_phy_misc:
469 		val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy));
470 		val &= ~COMP_INIT;
471 		intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy), val);
472 	}
473 }
474 
475 void intel_combo_phy_init(struct drm_i915_private *i915)
476 {
477 	if (DISPLAY_VER(i915) >= 11)
478 		icl_combo_phys_init(i915);
479 	else if (IS_CANNONLAKE(i915))
480 		cnl_combo_phys_init(i915);
481 }
482 
483 void intel_combo_phy_uninit(struct drm_i915_private *i915)
484 {
485 	if (DISPLAY_VER(i915) >= 11)
486 		icl_combo_phys_uninit(i915);
487 	else if (IS_CANNONLAKE(i915))
488 		cnl_combo_phys_uninit(i915);
489 }
490