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
= I915_READ(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
= I915_READ(ICL_PORT_COMP_DW1(phy
));
85 val
&= ~((0xff << 16) | 0xff);
87 I915_WRITE(ICL_PORT_COMP_DW1(phy
), val
);
89 I915_WRITE(ICL_PORT_COMP_DW9(phy
), procmon
->dw9
);
90 I915_WRITE(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
= I915_READ(reg
);
99 if ((val
& mask
) != expected_val
) {
100 DRM_DEBUG_DRIVER("Combo PHY %c reg %08x state mismatch: "
101 "current %08x mask %08x expected %08x\n",
103 reg
.reg
, val
, mask
, expected_val
);
110 static bool cnl_verify_procmon_ref_values(struct drm_i915_private
*dev_priv
,
113 const struct cnl_procmon
*procmon
;
116 procmon
= cnl_get_procmon_ref_values(dev_priv
, phy
);
118 ret
= check_phy_reg(dev_priv
, phy
, ICL_PORT_COMP_DW1(phy
),
119 (0xff << 16) | 0xff, procmon
->dw1
);
120 ret
&= check_phy_reg(dev_priv
, phy
, ICL_PORT_COMP_DW9(phy
),
122 ret
&= check_phy_reg(dev_priv
, phy
, ICL_PORT_COMP_DW10(phy
),
128 static bool cnl_combo_phy_enabled(struct drm_i915_private
*dev_priv
)
130 return !(I915_READ(CHICKEN_MISC_2
) & CNL_COMP_PWR_DOWN
) &&
131 (I915_READ(CNL_PORT_COMP_DW0
) & COMP_INIT
);
134 static bool cnl_combo_phy_verify_state(struct drm_i915_private
*dev_priv
)
136 enum phy phy
= PHY_A
;
139 if (!cnl_combo_phy_enabled(dev_priv
))
142 ret
= cnl_verify_procmon_ref_values(dev_priv
, phy
);
144 ret
&= check_phy_reg(dev_priv
, phy
, CNL_PORT_CL1CM_DW5
,
145 CL_POWER_DOWN_ENABLE
, CL_POWER_DOWN_ENABLE
);
150 static void cnl_combo_phys_init(struct drm_i915_private
*dev_priv
)
154 val
= I915_READ(CHICKEN_MISC_2
);
155 val
&= ~CNL_COMP_PWR_DOWN
;
156 I915_WRITE(CHICKEN_MISC_2
, val
);
158 /* Dummy PORT_A to get the correct CNL register from the ICL macro */
159 cnl_set_procmon_ref_values(dev_priv
, PHY_A
);
161 val
= I915_READ(CNL_PORT_COMP_DW0
);
163 I915_WRITE(CNL_PORT_COMP_DW0
, val
);
165 val
= I915_READ(CNL_PORT_CL1CM_DW5
);
166 val
|= CL_POWER_DOWN_ENABLE
;
167 I915_WRITE(CNL_PORT_CL1CM_DW5
, val
);
170 static void cnl_combo_phys_uninit(struct drm_i915_private
*dev_priv
)
174 if (!cnl_combo_phy_verify_state(dev_priv
))
175 DRM_WARN("Combo PHY HW state changed unexpectedly.\n");
177 val
= I915_READ(CHICKEN_MISC_2
);
178 val
|= CNL_COMP_PWR_DOWN
;
179 I915_WRITE(CHICKEN_MISC_2
, val
);
182 static bool icl_combo_phy_enabled(struct drm_i915_private
*dev_priv
,
185 /* The PHY C added by EHL has no PHY_MISC register */
186 if (IS_ELKHARTLAKE(dev_priv
) && phy
== PHY_C
)
187 return I915_READ(ICL_PORT_COMP_DW0(phy
)) & COMP_INIT
;
189 return !(I915_READ(ICL_PHY_MISC(phy
)) &
190 ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN
) &&
191 (I915_READ(ICL_PORT_COMP_DW0(phy
)) & COMP_INIT
);
194 static bool icl_combo_phy_verify_state(struct drm_i915_private
*dev_priv
,
199 if (!icl_combo_phy_enabled(dev_priv
, phy
))
202 ret
= cnl_verify_procmon_ref_values(dev_priv
, phy
);
205 ret
&= check_phy_reg(dev_priv
, phy
, ICL_PORT_COMP_DW8(phy
),
208 ret
&= check_phy_reg(dev_priv
, phy
, ICL_PORT_CL_DW5(phy
),
209 CL_POWER_DOWN_ENABLE
, CL_POWER_DOWN_ENABLE
);
214 void intel_combo_phy_power_up_lanes(struct drm_i915_private
*dev_priv
,
215 enum phy phy
, bool is_dsi
,
216 int lane_count
, bool lane_reversal
)
222 WARN_ON(lane_reversal
);
224 switch (lane_count
) {
226 lane_mask
= PWR_DOWN_LN_3_1_0
;
229 lane_mask
= PWR_DOWN_LN_3_1
;
232 lane_mask
= PWR_DOWN_LN_3
;
235 MISSING_CASE(lane_count
);
238 lane_mask
= PWR_UP_ALL_LANES
;
242 switch (lane_count
) {
244 lane_mask
= lane_reversal
? PWR_DOWN_LN_2_1_0
:
248 lane_mask
= lane_reversal
? PWR_DOWN_LN_1_0
:
252 MISSING_CASE(lane_count
);
255 lane_mask
= PWR_UP_ALL_LANES
;
260 val
= I915_READ(ICL_PORT_CL_DW10(phy
));
261 val
&= ~PWR_DOWN_LN_MASK
;
262 val
|= lane_mask
<< PWR_DOWN_LN_SHIFT
;
263 I915_WRITE(ICL_PORT_CL_DW10(phy
), val
);
266 static u32
ehl_combo_phy_a_mux(struct drm_i915_private
*i915
, u32 val
)
268 bool ddi_a_present
= i915
->vbt
.ddi_port_info
[PORT_A
].child
!= NULL
;
269 bool ddi_d_present
= i915
->vbt
.ddi_port_info
[PORT_D
].child
!= NULL
;
270 bool dsi_present
= intel_bios_is_dsi_present(i915
, NULL
);
273 * VBT's 'dvo port' field for child devices references the DDI, not
274 * the PHY. So if combo PHY A is wired up to drive an external
275 * display, we should see a child device present on PORT_D and
276 * nothing on PORT_A and no DSI.
278 if (ddi_d_present
&& !ddi_a_present
&& !dsi_present
)
279 return val
| ICL_PHY_MISC_MUX_DDID
;
282 * If we encounter a VBT that claims to have an external display on
283 * DDI-D _and_ an internal display on DDI-A/DSI leave an error message
284 * in the log and let the internal display win.
287 DRM_ERROR("VBT claims to have both internal and external displays on PHY A. Configuring for internal.\n");
289 return val
& ~ICL_PHY_MISC_MUX_DDID
;
292 static void icl_combo_phys_init(struct drm_i915_private
*dev_priv
)
296 for_each_combo_phy(dev_priv
, phy
) {
299 if (icl_combo_phy_verify_state(dev_priv
, phy
)) {
300 DRM_DEBUG_DRIVER("Combo PHY %c already enabled, won't reprogram it.\n",
306 * Although EHL adds a combo PHY C, there's no PHY_MISC
307 * register for it and no need to program the
308 * DE_IO_COMP_PWR_DOWN setting on PHY C.
310 if (IS_ELKHARTLAKE(dev_priv
) && phy
== PHY_C
)
314 * EHL's combo PHY A can be hooked up to either an external
315 * display (via DDI-D) or an internal display (via DDI-A or
316 * the DSI DPHY). This is a motherboard design decision that
317 * can't be changed on the fly, so initialize the PHY's mux
318 * based on whether our VBT indicates the presence of any
319 * "internal" child devices.
321 val
= I915_READ(ICL_PHY_MISC(phy
));
322 if (IS_ELKHARTLAKE(dev_priv
) && phy
== PHY_A
)
323 val
= ehl_combo_phy_a_mux(dev_priv
, val
);
324 val
&= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN
;
325 I915_WRITE(ICL_PHY_MISC(phy
), val
);
328 cnl_set_procmon_ref_values(dev_priv
, phy
);
331 val
= I915_READ(ICL_PORT_COMP_DW8(phy
));
333 I915_WRITE(ICL_PORT_COMP_DW8(phy
), val
);
336 val
= I915_READ(ICL_PORT_COMP_DW0(phy
));
338 I915_WRITE(ICL_PORT_COMP_DW0(phy
), val
);
340 val
= I915_READ(ICL_PORT_CL_DW5(phy
));
341 val
|= CL_POWER_DOWN_ENABLE
;
342 I915_WRITE(ICL_PORT_CL_DW5(phy
), val
);
346 static void icl_combo_phys_uninit(struct drm_i915_private
*dev_priv
)
350 for_each_combo_phy_reverse(dev_priv
, phy
) {
354 !icl_combo_phy_verify_state(dev_priv
, phy
))
355 DRM_WARN("Combo PHY %c HW state changed unexpectedly\n",
359 * Although EHL adds a combo PHY C, there's no PHY_MISC
360 * register for it and no need to program the
361 * DE_IO_COMP_PWR_DOWN setting on PHY C.
363 if (IS_ELKHARTLAKE(dev_priv
) && phy
== PHY_C
)
366 val
= I915_READ(ICL_PHY_MISC(phy
));
367 val
|= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN
;
368 I915_WRITE(ICL_PHY_MISC(phy
), val
);
371 val
= I915_READ(ICL_PORT_COMP_DW0(phy
));
373 I915_WRITE(ICL_PORT_COMP_DW0(phy
), val
);
377 void intel_combo_phy_init(struct drm_i915_private
*i915
)
379 if (INTEL_GEN(i915
) >= 11)
380 icl_combo_phys_init(i915
);
381 else if (IS_CANNONLAKE(i915
))
382 cnl_combo_phys_init(i915
);
385 void intel_combo_phy_uninit(struct drm_i915_private
*i915
)
387 if (INTEL_GEN(i915
) >= 11)
388 icl_combo_phys_uninit(i915
);
389 else if (IS_CANNONLAKE(i915
))
390 cnl_combo_phys_uninit(i915
);