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