1 // SPDX-License-Identifier: MIT
3 * Copyright © 2018 Intel Corporation
6 #include "intel_combo_phy.h"
7 #include "intel_display_types.h"
9 #define for_each_combo_phy(__dev_priv, __phy) \
10 for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++) \
11 for_each_if(intel_phy_is_combo(__dev_priv, __phy))
13 #define for_each_combo_phy_reverse(__dev_priv, __phy) \
14 for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) \
15 for_each_if(intel_phy_is_combo(__dev_priv, __phy))
25 static const struct cnl_procmon
{
27 } cnl_procmon_values
[] = {
28 [PROCMON_0_85V_DOT_0
] =
29 { .dw1
= 0x00000000, .dw9
= 0x62AB67BB, .dw10
= 0x51914F96, },
30 [PROCMON_0_95V_DOT_0
] =
31 { .dw1
= 0x00000000, .dw9
= 0x86E172C7, .dw10
= 0x77CA5EAB, },
32 [PROCMON_0_95V_DOT_1
] =
33 { .dw1
= 0x00000000, .dw9
= 0x93F87FE1, .dw10
= 0x8AE871C5, },
34 [PROCMON_1_05V_DOT_0
] =
35 { .dw1
= 0x00000000, .dw9
= 0x98FA82DD, .dw10
= 0x89E46DC1, },
36 [PROCMON_1_05V_DOT_1
] =
37 { .dw1
= 0x00440000, .dw9
= 0x9A00AB25, .dw10
= 0x8AE38FF1, },
41 * CNL has just one set of registers, while gen11 has a set for each combo PHY.
42 * The CNL registers are equivalent to the gen11 PHY A registers, that's why we
43 * call the ICL macros even though the function has CNL on its name.
45 static const struct cnl_procmon
*
46 cnl_get_procmon_ref_values(struct drm_i915_private
*dev_priv
, enum phy phy
)
48 const struct cnl_procmon
*procmon
;
51 val
= intel_de_read(dev_priv
, ICL_PORT_COMP_DW3(phy
));
52 switch (val
& (PROCESS_INFO_MASK
| VOLTAGE_INFO_MASK
)) {
56 case VOLTAGE_INFO_0_85V
| PROCESS_INFO_DOT_0
:
57 procmon
= &cnl_procmon_values
[PROCMON_0_85V_DOT_0
];
59 case VOLTAGE_INFO_0_95V
| PROCESS_INFO_DOT_0
:
60 procmon
= &cnl_procmon_values
[PROCMON_0_95V_DOT_0
];
62 case VOLTAGE_INFO_0_95V
| PROCESS_INFO_DOT_1
:
63 procmon
= &cnl_procmon_values
[PROCMON_0_95V_DOT_1
];
65 case VOLTAGE_INFO_1_05V
| PROCESS_INFO_DOT_0
:
66 procmon
= &cnl_procmon_values
[PROCMON_1_05V_DOT_0
];
68 case VOLTAGE_INFO_1_05V
| PROCESS_INFO_DOT_1
:
69 procmon
= &cnl_procmon_values
[PROCMON_1_05V_DOT_1
];
76 static void cnl_set_procmon_ref_values(struct drm_i915_private
*dev_priv
,
79 const struct cnl_procmon
*procmon
;
82 procmon
= cnl_get_procmon_ref_values(dev_priv
, phy
);
84 val
= intel_de_read(dev_priv
, ICL_PORT_COMP_DW1(phy
));
85 val
&= ~((0xff << 16) | 0xff);
87 intel_de_write(dev_priv
, ICL_PORT_COMP_DW1(phy
), val
);
89 intel_de_write(dev_priv
, ICL_PORT_COMP_DW9(phy
), procmon
->dw9
);
90 intel_de_write(dev_priv
, ICL_PORT_COMP_DW10(phy
), procmon
->dw10
);
93 static bool check_phy_reg(struct drm_i915_private
*dev_priv
,
94 enum phy phy
, i915_reg_t reg
, u32 mask
,
97 u32 val
= intel_de_read(dev_priv
, reg
);
99 if ((val
& mask
) != expected_val
) {
100 drm_dbg(&dev_priv
->drm
,
101 "Combo PHY %c reg %08x state mismatch: "
102 "current %08x mask %08x expected %08x\n",
104 reg
.reg
, val
, mask
, expected_val
);
111 static bool cnl_verify_procmon_ref_values(struct drm_i915_private
*dev_priv
,
114 const struct cnl_procmon
*procmon
;
117 procmon
= cnl_get_procmon_ref_values(dev_priv
, phy
);
119 ret
= check_phy_reg(dev_priv
, phy
, ICL_PORT_COMP_DW1(phy
),
120 (0xff << 16) | 0xff, procmon
->dw1
);
121 ret
&= check_phy_reg(dev_priv
, phy
, ICL_PORT_COMP_DW9(phy
),
123 ret
&= check_phy_reg(dev_priv
, phy
, ICL_PORT_COMP_DW10(phy
),
129 static bool cnl_combo_phy_enabled(struct drm_i915_private
*dev_priv
)
131 return !(intel_de_read(dev_priv
, CHICKEN_MISC_2
) & CNL_COMP_PWR_DOWN
) &&
132 (intel_de_read(dev_priv
, CNL_PORT_COMP_DW0
) & COMP_INIT
);
135 static bool cnl_combo_phy_verify_state(struct drm_i915_private
*dev_priv
)
137 enum phy phy
= PHY_A
;
140 if (!cnl_combo_phy_enabled(dev_priv
))
143 ret
= cnl_verify_procmon_ref_values(dev_priv
, phy
);
145 ret
&= check_phy_reg(dev_priv
, phy
, CNL_PORT_CL1CM_DW5
,
146 CL_POWER_DOWN_ENABLE
, CL_POWER_DOWN_ENABLE
);
151 static void cnl_combo_phys_init(struct drm_i915_private
*dev_priv
)
155 val
= intel_de_read(dev_priv
, CHICKEN_MISC_2
);
156 val
&= ~CNL_COMP_PWR_DOWN
;
157 intel_de_write(dev_priv
, CHICKEN_MISC_2
, val
);
159 /* Dummy PORT_A to get the correct CNL register from the ICL macro */
160 cnl_set_procmon_ref_values(dev_priv
, PHY_A
);
162 val
= intel_de_read(dev_priv
, CNL_PORT_COMP_DW0
);
164 intel_de_write(dev_priv
, CNL_PORT_COMP_DW0
, val
);
166 val
= intel_de_read(dev_priv
, CNL_PORT_CL1CM_DW5
);
167 val
|= CL_POWER_DOWN_ENABLE
;
168 intel_de_write(dev_priv
, CNL_PORT_CL1CM_DW5
, val
);
171 static void cnl_combo_phys_uninit(struct drm_i915_private
*dev_priv
)
175 if (!cnl_combo_phy_verify_state(dev_priv
))
176 drm_warn(&dev_priv
->drm
,
177 "Combo PHY HW state changed unexpectedly.\n");
179 val
= intel_de_read(dev_priv
, CHICKEN_MISC_2
);
180 val
|= CNL_COMP_PWR_DOWN
;
181 intel_de_write(dev_priv
, CHICKEN_MISC_2
, val
);
184 static bool has_phy_misc(struct drm_i915_private
*i915
, enum phy phy
)
187 * Some platforms only expect PHY_MISC to be programmed for PHY-A and
188 * PHY-B and may not even have instances of the register for the
191 if (IS_JSL_EHL(i915
) ||
192 IS_ROCKETLAKE(i915
) ||
199 static bool icl_combo_phy_enabled(struct drm_i915_private
*dev_priv
,
202 /* The PHY C added by EHL has no PHY_MISC register */
203 if (!has_phy_misc(dev_priv
, phy
))
204 return intel_de_read(dev_priv
, ICL_PORT_COMP_DW0(phy
)) & COMP_INIT
;
206 return !(intel_de_read(dev_priv
, ICL_PHY_MISC(phy
)) &
207 ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN
) &&
208 (intel_de_read(dev_priv
, ICL_PORT_COMP_DW0(phy
)) & COMP_INIT
);
211 static bool ehl_vbt_ddi_d_present(struct drm_i915_private
*i915
)
213 bool ddi_a_present
= intel_bios_is_port_present(i915
, PORT_A
);
214 bool ddi_d_present
= intel_bios_is_port_present(i915
, PORT_D
);
215 bool dsi_present
= intel_bios_is_dsi_present(i915
, NULL
);
218 * VBT's 'dvo port' field for child devices references the DDI, not
219 * the PHY. So if combo PHY A is wired up to drive an external
220 * display, we should see a child device present on PORT_D and
221 * nothing on PORT_A and no DSI.
223 if (ddi_d_present
&& !ddi_a_present
&& !dsi_present
)
227 * If we encounter a VBT that claims to have an external display on
228 * DDI-D _and_ an internal display on DDI-A/DSI leave an error message
229 * in the log and let the internal display win.
233 "VBT claims to have both internal and external displays on PHY A. Configuring for internal.\n");
238 static bool phy_is_master(struct drm_i915_private
*dev_priv
, enum phy phy
)
241 * Certain PHYs are connected to compensation resistors and act
242 * as masters to other PHYs.
245 * A(master) -> B(slave), C(slave)
247 * A(master) -> B(slave)
248 * C(master) -> D(slave)
250 * We must set the IREFGEN bit for any PHY acting as a master
253 if ((IS_DG1(dev_priv
) || IS_ROCKETLAKE(dev_priv
)) && phy
== PHY_C
)
259 static bool icl_combo_phy_verify_state(struct drm_i915_private
*dev_priv
,
263 u32 expected_val
= 0;
265 if (!icl_combo_phy_enabled(dev_priv
, phy
))
268 if (INTEL_GEN(dev_priv
) >= 12) {
269 ret
&= check_phy_reg(dev_priv
, phy
, ICL_PORT_TX_DW8_LN0(phy
),
270 ICL_PORT_TX_DW8_ODCC_CLK_SEL
|
271 ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK
,
272 ICL_PORT_TX_DW8_ODCC_CLK_SEL
|
273 ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2
);
275 ret
&= check_phy_reg(dev_priv
, phy
, ICL_PORT_PCS_DW1_LN0(phy
),
276 DCC_MODE_SELECT_MASK
,
277 DCC_MODE_SELECT_CONTINUOSLY
);
280 ret
&= cnl_verify_procmon_ref_values(dev_priv
, phy
);
282 if (phy_is_master(dev_priv
, phy
)) {
283 ret
&= check_phy_reg(dev_priv
, phy
, ICL_PORT_COMP_DW8(phy
),
286 if (IS_JSL_EHL(dev_priv
)) {
287 if (ehl_vbt_ddi_d_present(dev_priv
))
288 expected_val
= ICL_PHY_MISC_MUX_DDID
;
290 ret
&= check_phy_reg(dev_priv
, phy
, ICL_PHY_MISC(phy
),
291 ICL_PHY_MISC_MUX_DDID
,
296 ret
&= check_phy_reg(dev_priv
, phy
, ICL_PORT_CL_DW5(phy
),
297 CL_POWER_DOWN_ENABLE
, CL_POWER_DOWN_ENABLE
);
302 void intel_combo_phy_power_up_lanes(struct drm_i915_private
*dev_priv
,
303 enum phy phy
, bool is_dsi
,
304 int lane_count
, bool lane_reversal
)
310 drm_WARN_ON(&dev_priv
->drm
, lane_reversal
);
312 switch (lane_count
) {
314 lane_mask
= PWR_DOWN_LN_3_1_0
;
317 lane_mask
= PWR_DOWN_LN_3_1
;
320 lane_mask
= PWR_DOWN_LN_3
;
323 MISSING_CASE(lane_count
);
326 lane_mask
= PWR_UP_ALL_LANES
;
330 switch (lane_count
) {
332 lane_mask
= lane_reversal
? PWR_DOWN_LN_2_1_0
:
336 lane_mask
= lane_reversal
? PWR_DOWN_LN_1_0
:
340 MISSING_CASE(lane_count
);
343 lane_mask
= PWR_UP_ALL_LANES
;
348 val
= intel_de_read(dev_priv
, ICL_PORT_CL_DW10(phy
));
349 val
&= ~PWR_DOWN_LN_MASK
;
350 val
|= lane_mask
<< PWR_DOWN_LN_SHIFT
;
351 intel_de_write(dev_priv
, ICL_PORT_CL_DW10(phy
), val
);
354 static void icl_combo_phys_init(struct drm_i915_private
*dev_priv
)
358 for_each_combo_phy(dev_priv
, phy
) {
361 if (icl_combo_phy_verify_state(dev_priv
, phy
)) {
362 drm_dbg(&dev_priv
->drm
,
363 "Combo PHY %c already enabled, won't reprogram it.\n",
368 if (!has_phy_misc(dev_priv
, phy
))
372 * EHL's combo PHY A can be hooked up to either an external
373 * display (via DDI-D) or an internal display (via DDI-A or
374 * the DSI DPHY). This is a motherboard design decision that
375 * can't be changed on the fly, so initialize the PHY's mux
376 * based on whether our VBT indicates the presence of any
377 * "internal" child devices.
379 val
= intel_de_read(dev_priv
, ICL_PHY_MISC(phy
));
380 if (IS_JSL_EHL(dev_priv
) && phy
== PHY_A
) {
381 val
&= ~ICL_PHY_MISC_MUX_DDID
;
383 if (ehl_vbt_ddi_d_present(dev_priv
))
384 val
|= ICL_PHY_MISC_MUX_DDID
;
387 val
&= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN
;
388 intel_de_write(dev_priv
, ICL_PHY_MISC(phy
), val
);
391 if (INTEL_GEN(dev_priv
) >= 12) {
392 val
= intel_de_read(dev_priv
, ICL_PORT_TX_DW8_LN0(phy
));
393 val
&= ~ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK
;
394 val
|= ICL_PORT_TX_DW8_ODCC_CLK_SEL
;
395 val
|= ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2
;
396 intel_de_write(dev_priv
, ICL_PORT_TX_DW8_GRP(phy
), val
);
398 val
= intel_de_read(dev_priv
, ICL_PORT_PCS_DW1_LN0(phy
));
399 val
&= ~DCC_MODE_SELECT_MASK
;
400 val
|= DCC_MODE_SELECT_CONTINUOSLY
;
401 intel_de_write(dev_priv
, ICL_PORT_PCS_DW1_GRP(phy
), val
);
404 cnl_set_procmon_ref_values(dev_priv
, phy
);
406 if (phy_is_master(dev_priv
, phy
)) {
407 val
= intel_de_read(dev_priv
, ICL_PORT_COMP_DW8(phy
));
409 intel_de_write(dev_priv
, ICL_PORT_COMP_DW8(phy
), val
);
412 val
= intel_de_read(dev_priv
, ICL_PORT_COMP_DW0(phy
));
414 intel_de_write(dev_priv
, ICL_PORT_COMP_DW0(phy
), val
);
416 val
= intel_de_read(dev_priv
, ICL_PORT_CL_DW5(phy
));
417 val
|= CL_POWER_DOWN_ENABLE
;
418 intel_de_write(dev_priv
, ICL_PORT_CL_DW5(phy
), val
);
422 static void icl_combo_phys_uninit(struct drm_i915_private
*dev_priv
)
426 for_each_combo_phy_reverse(dev_priv
, phy
) {
430 !icl_combo_phy_verify_state(dev_priv
, phy
))
431 drm_warn(&dev_priv
->drm
,
432 "Combo PHY %c HW state changed unexpectedly\n",
435 if (!has_phy_misc(dev_priv
, phy
))
438 val
= intel_de_read(dev_priv
, ICL_PHY_MISC(phy
));
439 val
|= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN
;
440 intel_de_write(dev_priv
, ICL_PHY_MISC(phy
), val
);
443 val
= intel_de_read(dev_priv
, ICL_PORT_COMP_DW0(phy
));
445 intel_de_write(dev_priv
, ICL_PORT_COMP_DW0(phy
), val
);
449 void intel_combo_phy_init(struct drm_i915_private
*i915
)
451 if (INTEL_GEN(i915
) >= 11)
452 icl_combo_phys_init(i915
);
453 else if (IS_CANNONLAKE(i915
))
454 cnl_combo_phys_init(i915
);
457 void intel_combo_phy_uninit(struct drm_i915_private
*i915
)
459 if (INTEL_GEN(i915
) >= 11)
460 icl_combo_phys_uninit(i915
);
461 else if (IS_CANNONLAKE(i915
))
462 cnl_combo_phys_uninit(i915
);