0001
0002
0003
0004
0005
0006 #include "intel_combo_phy.h"
0007 #include "intel_combo_phy_regs.h"
0008 #include "intel_de.h"
0009 #include "intel_display_types.h"
0010
0011 #define for_each_combo_phy(__dev_priv, __phy) \
0012 for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++) \
0013 for_each_if(intel_phy_is_combo(__dev_priv, __phy))
0014
0015 #define for_each_combo_phy_reverse(__dev_priv, __phy) \
0016 for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) \
0017 for_each_if(intel_phy_is_combo(__dev_priv, __phy))
0018
0019 enum {
0020 PROCMON_0_85V_DOT_0,
0021 PROCMON_0_95V_DOT_0,
0022 PROCMON_0_95V_DOT_1,
0023 PROCMON_1_05V_DOT_0,
0024 PROCMON_1_05V_DOT_1,
0025 };
0026
0027 static const struct icl_procmon {
0028 const char *name;
0029 u32 dw1, dw9, dw10;
0030 } icl_procmon_values[] = {
0031 [PROCMON_0_85V_DOT_0] = {
0032 .name = "0.85V dot0 (low-voltage)",
0033 .dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96,
0034 },
0035 [PROCMON_0_95V_DOT_0] = {
0036 .name = "0.95V dot0",
0037 .dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB,
0038 },
0039 [PROCMON_0_95V_DOT_1] = {
0040 .name = "0.95V dot1",
0041 .dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5,
0042 },
0043 [PROCMON_1_05V_DOT_0] = {
0044 .name = "1.05V dot0",
0045 .dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1,
0046 },
0047 [PROCMON_1_05V_DOT_1] = {
0048 .name = "1.05V dot1",
0049 .dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1,
0050 },
0051 };
0052
0053 static const struct icl_procmon *
0054 icl_get_procmon_ref_values(struct drm_i915_private *dev_priv, enum phy phy)
0055 {
0056 const struct icl_procmon *procmon;
0057 u32 val;
0058
0059 val = intel_de_read(dev_priv, ICL_PORT_COMP_DW3(phy));
0060 switch (val & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) {
0061 default:
0062 MISSING_CASE(val);
0063 fallthrough;
0064 case VOLTAGE_INFO_0_85V | PROCESS_INFO_DOT_0:
0065 procmon = &icl_procmon_values[PROCMON_0_85V_DOT_0];
0066 break;
0067 case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_0:
0068 procmon = &icl_procmon_values[PROCMON_0_95V_DOT_0];
0069 break;
0070 case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_1:
0071 procmon = &icl_procmon_values[PROCMON_0_95V_DOT_1];
0072 break;
0073 case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_0:
0074 procmon = &icl_procmon_values[PROCMON_1_05V_DOT_0];
0075 break;
0076 case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_1:
0077 procmon = &icl_procmon_values[PROCMON_1_05V_DOT_1];
0078 break;
0079 }
0080
0081 return procmon;
0082 }
0083
0084 static void icl_set_procmon_ref_values(struct drm_i915_private *dev_priv,
0085 enum phy phy)
0086 {
0087 const struct icl_procmon *procmon;
0088 u32 val;
0089
0090 procmon = icl_get_procmon_ref_values(dev_priv, phy);
0091
0092 val = intel_de_read(dev_priv, ICL_PORT_COMP_DW1(phy));
0093 val &= ~((0xff << 16) | 0xff);
0094 val |= procmon->dw1;
0095 intel_de_write(dev_priv, ICL_PORT_COMP_DW1(phy), val);
0096
0097 intel_de_write(dev_priv, ICL_PORT_COMP_DW9(phy), procmon->dw9);
0098 intel_de_write(dev_priv, ICL_PORT_COMP_DW10(phy), procmon->dw10);
0099 }
0100
0101 static bool check_phy_reg(struct drm_i915_private *dev_priv,
0102 enum phy phy, i915_reg_t reg, u32 mask,
0103 u32 expected_val)
0104 {
0105 u32 val = intel_de_read(dev_priv, reg);
0106
0107 if ((val & mask) != expected_val) {
0108 drm_dbg(&dev_priv->drm,
0109 "Combo PHY %c reg %08x state mismatch: "
0110 "current %08x mask %08x expected %08x\n",
0111 phy_name(phy),
0112 reg.reg, val, mask, expected_val);
0113 return false;
0114 }
0115
0116 return true;
0117 }
0118
0119 static bool icl_verify_procmon_ref_values(struct drm_i915_private *dev_priv,
0120 enum phy phy)
0121 {
0122 const struct icl_procmon *procmon;
0123 bool ret;
0124
0125 procmon = icl_get_procmon_ref_values(dev_priv, phy);
0126
0127 drm_dbg_kms(&dev_priv->drm,
0128 "Combo PHY %c Voltage/Process Info : %s\n",
0129 phy_name(phy), procmon->name);
0130
0131 ret = check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW1(phy),
0132 (0xff << 16) | 0xff, procmon->dw1);
0133 ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW9(phy),
0134 -1U, procmon->dw9);
0135 ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW10(phy),
0136 -1U, procmon->dw10);
0137
0138 return ret;
0139 }
0140
0141 static bool has_phy_misc(struct drm_i915_private *i915, enum phy phy)
0142 {
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152 if (IS_ALDERLAKE_S(i915))
0153 return phy == PHY_A;
0154 else if (IS_JSL_EHL(i915) ||
0155 IS_ROCKETLAKE(i915) ||
0156 IS_DG1(i915))
0157 return phy < PHY_C;
0158
0159 return true;
0160 }
0161
0162 static bool icl_combo_phy_enabled(struct drm_i915_private *dev_priv,
0163 enum phy phy)
0164 {
0165
0166 if (!has_phy_misc(dev_priv, phy))
0167 return intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT;
0168 else
0169 return !(intel_de_read(dev_priv, ICL_PHY_MISC(phy)) &
0170 ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN) &&
0171 (intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT);
0172 }
0173
0174 static bool ehl_vbt_ddi_d_present(struct drm_i915_private *i915)
0175 {
0176 bool ddi_a_present = intel_bios_is_port_present(i915, PORT_A);
0177 bool ddi_d_present = intel_bios_is_port_present(i915, PORT_D);
0178 bool dsi_present = intel_bios_is_dsi_present(i915, NULL);
0179
0180
0181
0182
0183
0184
0185
0186 if (ddi_d_present && !ddi_a_present && !dsi_present)
0187 return true;
0188
0189
0190
0191
0192
0193
0194 if (ddi_d_present)
0195 drm_err(&i915->drm,
0196 "VBT claims to have both internal and external displays on PHY A. Configuring for internal.\n");
0197
0198 return false;
0199 }
0200
0201 static bool phy_is_master(struct drm_i915_private *dev_priv, enum phy phy)
0202 {
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219 if (phy == PHY_A)
0220 return true;
0221 else if (IS_ALDERLAKE_S(dev_priv))
0222 return phy == PHY_D;
0223 else if (IS_DG1(dev_priv) || IS_ROCKETLAKE(dev_priv))
0224 return phy == PHY_C;
0225
0226 return false;
0227 }
0228
0229 static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv,
0230 enum phy phy)
0231 {
0232 bool ret = true;
0233 u32 expected_val = 0;
0234
0235 if (!icl_combo_phy_enabled(dev_priv, phy))
0236 return false;
0237
0238 if (DISPLAY_VER(dev_priv) >= 12) {
0239 ret &= check_phy_reg(dev_priv, phy, ICL_PORT_TX_DW8_LN(0, phy),
0240 ICL_PORT_TX_DW8_ODCC_CLK_SEL |
0241 ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK,
0242 ICL_PORT_TX_DW8_ODCC_CLK_SEL |
0243 ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2);
0244
0245 ret &= check_phy_reg(dev_priv, phy, ICL_PORT_PCS_DW1_LN(0, phy),
0246 DCC_MODE_SELECT_MASK,
0247 DCC_MODE_SELECT_CONTINUOSLY);
0248 }
0249
0250 ret &= icl_verify_procmon_ref_values(dev_priv, phy);
0251
0252 if (phy_is_master(dev_priv, phy)) {
0253 ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW8(phy),
0254 IREFGEN, IREFGEN);
0255
0256 if (IS_JSL_EHL(dev_priv)) {
0257 if (ehl_vbt_ddi_d_present(dev_priv))
0258 expected_val = ICL_PHY_MISC_MUX_DDID;
0259
0260 ret &= check_phy_reg(dev_priv, phy, ICL_PHY_MISC(phy),
0261 ICL_PHY_MISC_MUX_DDID,
0262 expected_val);
0263 }
0264 }
0265
0266 ret &= check_phy_reg(dev_priv, phy, ICL_PORT_CL_DW5(phy),
0267 CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
0268
0269 return ret;
0270 }
0271
0272 void intel_combo_phy_power_up_lanes(struct drm_i915_private *dev_priv,
0273 enum phy phy, bool is_dsi,
0274 int lane_count, bool lane_reversal)
0275 {
0276 u8 lane_mask;
0277 u32 val;
0278
0279 if (is_dsi) {
0280 drm_WARN_ON(&dev_priv->drm, lane_reversal);
0281
0282 switch (lane_count) {
0283 case 1:
0284 lane_mask = PWR_DOWN_LN_3_1_0;
0285 break;
0286 case 2:
0287 lane_mask = PWR_DOWN_LN_3_1;
0288 break;
0289 case 3:
0290 lane_mask = PWR_DOWN_LN_3;
0291 break;
0292 default:
0293 MISSING_CASE(lane_count);
0294 fallthrough;
0295 case 4:
0296 lane_mask = PWR_UP_ALL_LANES;
0297 break;
0298 }
0299 } else {
0300 switch (lane_count) {
0301 case 1:
0302 lane_mask = lane_reversal ? PWR_DOWN_LN_2_1_0 :
0303 PWR_DOWN_LN_3_2_1;
0304 break;
0305 case 2:
0306 lane_mask = lane_reversal ? PWR_DOWN_LN_1_0 :
0307 PWR_DOWN_LN_3_2;
0308 break;
0309 default:
0310 MISSING_CASE(lane_count);
0311 fallthrough;
0312 case 4:
0313 lane_mask = PWR_UP_ALL_LANES;
0314 break;
0315 }
0316 }
0317
0318 val = intel_de_read(dev_priv, ICL_PORT_CL_DW10(phy));
0319 val &= ~PWR_DOWN_LN_MASK;
0320 val |= lane_mask;
0321 intel_de_write(dev_priv, ICL_PORT_CL_DW10(phy), val);
0322 }
0323
0324 static void icl_combo_phys_init(struct drm_i915_private *dev_priv)
0325 {
0326 enum phy phy;
0327
0328 for_each_combo_phy(dev_priv, phy) {
0329 u32 val;
0330
0331 if (icl_combo_phy_verify_state(dev_priv, phy)) {
0332 drm_dbg(&dev_priv->drm,
0333 "Combo PHY %c already enabled, won't reprogram it.\n",
0334 phy_name(phy));
0335 continue;
0336 }
0337
0338 if (!has_phy_misc(dev_priv, phy))
0339 goto skip_phy_misc;
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349 val = intel_de_read(dev_priv, ICL_PHY_MISC(phy));
0350 if (IS_JSL_EHL(dev_priv) && phy == PHY_A) {
0351 val &= ~ICL_PHY_MISC_MUX_DDID;
0352
0353 if (ehl_vbt_ddi_d_present(dev_priv))
0354 val |= ICL_PHY_MISC_MUX_DDID;
0355 }
0356
0357 val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
0358 intel_de_write(dev_priv, ICL_PHY_MISC(phy), val);
0359
0360 skip_phy_misc:
0361 if (DISPLAY_VER(dev_priv) >= 12) {
0362 val = intel_de_read(dev_priv, ICL_PORT_TX_DW8_LN(0, phy));
0363 val &= ~ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK;
0364 val |= ICL_PORT_TX_DW8_ODCC_CLK_SEL;
0365 val |= ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2;
0366 intel_de_write(dev_priv, ICL_PORT_TX_DW8_GRP(phy), val);
0367
0368 val = intel_de_read(dev_priv, ICL_PORT_PCS_DW1_LN(0, phy));
0369 val &= ~DCC_MODE_SELECT_MASK;
0370 val |= DCC_MODE_SELECT_CONTINUOSLY;
0371 intel_de_write(dev_priv, ICL_PORT_PCS_DW1_GRP(phy), val);
0372 }
0373
0374 icl_set_procmon_ref_values(dev_priv, phy);
0375
0376 if (phy_is_master(dev_priv, phy)) {
0377 val = intel_de_read(dev_priv, ICL_PORT_COMP_DW8(phy));
0378 val |= IREFGEN;
0379 intel_de_write(dev_priv, ICL_PORT_COMP_DW8(phy), val);
0380 }
0381
0382 val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy));
0383 val |= COMP_INIT;
0384 intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy), val);
0385
0386 val = intel_de_read(dev_priv, ICL_PORT_CL_DW5(phy));
0387 val |= CL_POWER_DOWN_ENABLE;
0388 intel_de_write(dev_priv, ICL_PORT_CL_DW5(phy), val);
0389 }
0390 }
0391
0392 static void icl_combo_phys_uninit(struct drm_i915_private *dev_priv)
0393 {
0394 enum phy phy;
0395
0396 for_each_combo_phy_reverse(dev_priv, phy) {
0397 u32 val;
0398
0399 if (phy == PHY_A &&
0400 !icl_combo_phy_verify_state(dev_priv, phy)) {
0401 if (IS_TIGERLAKE(dev_priv) || IS_DG1(dev_priv)) {
0402
0403
0404
0405
0406
0407 drm_dbg_kms(&dev_priv->drm,
0408 "Combo PHY %c HW state changed unexpectedly\n",
0409 phy_name(phy));
0410 } else {
0411 drm_warn(&dev_priv->drm,
0412 "Combo PHY %c HW state changed unexpectedly\n",
0413 phy_name(phy));
0414 }
0415 }
0416
0417 if (!has_phy_misc(dev_priv, phy))
0418 goto skip_phy_misc;
0419
0420 val = intel_de_read(dev_priv, ICL_PHY_MISC(phy));
0421 val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
0422 intel_de_write(dev_priv, ICL_PHY_MISC(phy), val);
0423
0424 skip_phy_misc:
0425 val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy));
0426 val &= ~COMP_INIT;
0427 intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy), val);
0428 }
0429 }
0430
0431 void intel_combo_phy_init(struct drm_i915_private *i915)
0432 {
0433 icl_combo_phys_init(i915);
0434 }
0435
0436 void intel_combo_phy_uninit(struct drm_i915_private *i915)
0437 {
0438 icl_combo_phys_uninit(i915);
0439 }