1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright 2025 Intel Corporation. 4 */ 5 6 #include <drm/drm_print.h> 7 8 #include "intel_de.h" 9 #include "intel_display.h" 10 #include "intel_display_regs.h" 11 #include "intel_display_core.h" 12 #include "intel_display_utils.h" 13 #include "intel_pch.h" 14 15 #define INTEL_PCH_DEVICE_ID_MASK 0xff80 16 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 17 #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00 18 #define INTEL_PCH_PPT_DEVICE_ID_TYPE 0x1e00 19 #define INTEL_PCH_LPT_DEVICE_ID_TYPE 0x8c00 20 #define INTEL_PCH_LPT_LP_DEVICE_ID_TYPE 0x9c00 21 #define INTEL_PCH_WPT_DEVICE_ID_TYPE 0x8c80 22 #define INTEL_PCH_WPT_LP_DEVICE_ID_TYPE 0x9c80 23 #define INTEL_PCH_SPT_DEVICE_ID_TYPE 0xA100 24 #define INTEL_PCH_SPT_LP_DEVICE_ID_TYPE 0x9D00 25 #define INTEL_PCH_KBP_DEVICE_ID_TYPE 0xA280 26 #define INTEL_PCH_CNP_DEVICE_ID_TYPE 0xA300 27 #define INTEL_PCH_CNP_LP_DEVICE_ID_TYPE 0x9D80 28 #define INTEL_PCH_CMP_DEVICE_ID_TYPE 0x0280 29 #define INTEL_PCH_CMP2_DEVICE_ID_TYPE 0x0680 30 #define INTEL_PCH_CMP_V_DEVICE_ID_TYPE 0xA380 31 #define INTEL_PCH_ICP_DEVICE_ID_TYPE 0x3480 32 #define INTEL_PCH_ICP2_DEVICE_ID_TYPE 0x3880 33 #define INTEL_PCH_MCC_DEVICE_ID_TYPE 0x4B00 34 #define INTEL_PCH_TGP_DEVICE_ID_TYPE 0xA080 35 #define INTEL_PCH_TGP2_DEVICE_ID_TYPE 0x4380 36 #define INTEL_PCH_JSP_DEVICE_ID_TYPE 0x4D80 37 #define INTEL_PCH_ADP_DEVICE_ID_TYPE 0x7A80 38 #define INTEL_PCH_ADP2_DEVICE_ID_TYPE 0x5180 39 #define INTEL_PCH_ADP3_DEVICE_ID_TYPE 0x7A00 40 #define INTEL_PCH_ADP4_DEVICE_ID_TYPE 0x5480 41 #define INTEL_PCH_P2X_DEVICE_ID_TYPE 0x7100 42 #define INTEL_PCH_P3X_DEVICE_ID_TYPE 0x7000 43 #define INTEL_PCH_QEMU_DEVICE_ID_TYPE 0x2900 /* qemu q35 has 2918 */ 44 45 /* 46 * Check for platforms where the south display is on the same PCI device or SoC 47 * die as the north display. The PCH (if it even exists) is not involved in 48 * display. Return a fake PCH type for south display handling on these 49 * platforms, without actually detecting the PCH, and PCH_NONE otherwise. 50 */ 51 static enum intel_pch intel_pch_fake_for_south_display(struct intel_display *display) 52 { 53 enum intel_pch pch_type = PCH_NONE; 54 55 if (DISPLAY_VER(display) >= 20) 56 pch_type = PCH_LNL; 57 else if (display->platform.battlemage || display->platform.meteorlake) 58 pch_type = PCH_MTL; 59 else if (display->platform.dg2) 60 pch_type = PCH_DG2; 61 else if (display->platform.dg1) 62 pch_type = PCH_DG1; 63 64 return pch_type; 65 } 66 67 /* Map PCH device id to PCH type, or PCH_NONE if unknown. */ 68 static enum intel_pch 69 intel_pch_type(const struct intel_display *display, unsigned short id) 70 { 71 switch (id) { 72 case INTEL_PCH_IBX_DEVICE_ID_TYPE: 73 drm_dbg_kms(display->drm, "Found Ibex Peak PCH\n"); 74 drm_WARN_ON(display->drm, DISPLAY_VER(display) != 5); 75 return PCH_IBX; 76 case INTEL_PCH_CPT_DEVICE_ID_TYPE: 77 drm_dbg_kms(display->drm, "Found CougarPoint PCH\n"); 78 drm_WARN_ON(display->drm, 79 DISPLAY_VER(display) != 6 && 80 !display->platform.ivybridge); 81 return PCH_CPT; 82 case INTEL_PCH_PPT_DEVICE_ID_TYPE: 83 drm_dbg_kms(display->drm, "Found PantherPoint PCH\n"); 84 drm_WARN_ON(display->drm, 85 DISPLAY_VER(display) != 6 && 86 !display->platform.ivybridge); 87 /* PPT is CPT compatible */ 88 return PCH_CPT; 89 case INTEL_PCH_LPT_DEVICE_ID_TYPE: 90 drm_dbg_kms(display->drm, "Found LynxPoint PCH\n"); 91 drm_WARN_ON(display->drm, 92 !display->platform.haswell && 93 !display->platform.broadwell); 94 drm_WARN_ON(display->drm, 95 display->platform.haswell_ult || 96 display->platform.broadwell_ult); 97 return PCH_LPT_H; 98 case INTEL_PCH_LPT_LP_DEVICE_ID_TYPE: 99 drm_dbg_kms(display->drm, "Found LynxPoint LP PCH\n"); 100 drm_WARN_ON(display->drm, 101 !display->platform.haswell && 102 !display->platform.broadwell); 103 drm_WARN_ON(display->drm, 104 !display->platform.haswell_ult && 105 !display->platform.broadwell_ult); 106 return PCH_LPT_LP; 107 case INTEL_PCH_WPT_DEVICE_ID_TYPE: 108 drm_dbg_kms(display->drm, "Found WildcatPoint PCH\n"); 109 drm_WARN_ON(display->drm, 110 !display->platform.haswell && 111 !display->platform.broadwell); 112 drm_WARN_ON(display->drm, 113 display->platform.haswell_ult || 114 display->platform.broadwell_ult); 115 /* WPT is LPT compatible */ 116 return PCH_LPT_H; 117 case INTEL_PCH_WPT_LP_DEVICE_ID_TYPE: 118 drm_dbg_kms(display->drm, "Found WildcatPoint LP PCH\n"); 119 drm_WARN_ON(display->drm, 120 !display->platform.haswell && 121 !display->platform.broadwell); 122 drm_WARN_ON(display->drm, 123 !display->platform.haswell_ult && 124 !display->platform.broadwell_ult); 125 /* WPT is LPT compatible */ 126 return PCH_LPT_LP; 127 case INTEL_PCH_SPT_DEVICE_ID_TYPE: 128 drm_dbg_kms(display->drm, "Found SunrisePoint PCH\n"); 129 drm_WARN_ON(display->drm, 130 !display->platform.skylake && 131 !display->platform.kabylake && 132 !display->platform.coffeelake); 133 return PCH_SPT; 134 case INTEL_PCH_SPT_LP_DEVICE_ID_TYPE: 135 drm_dbg_kms(display->drm, "Found SunrisePoint LP PCH\n"); 136 drm_WARN_ON(display->drm, 137 !display->platform.skylake && 138 !display->platform.kabylake && 139 !display->platform.coffeelake && 140 !display->platform.cometlake); 141 return PCH_SPT; 142 case INTEL_PCH_KBP_DEVICE_ID_TYPE: 143 drm_dbg_kms(display->drm, "Found Kaby Lake PCH (KBP)\n"); 144 drm_WARN_ON(display->drm, 145 !display->platform.skylake && 146 !display->platform.kabylake && 147 !display->platform.coffeelake && 148 !display->platform.cometlake); 149 /* KBP is SPT compatible */ 150 return PCH_SPT; 151 case INTEL_PCH_CNP_DEVICE_ID_TYPE: 152 drm_dbg_kms(display->drm, "Found Cannon Lake PCH (CNP)\n"); 153 drm_WARN_ON(display->drm, 154 !display->platform.coffeelake && 155 !display->platform.cometlake); 156 return PCH_CNP; 157 case INTEL_PCH_CNP_LP_DEVICE_ID_TYPE: 158 drm_dbg_kms(display->drm, 159 "Found Cannon Lake LP PCH (CNP-LP)\n"); 160 drm_WARN_ON(display->drm, 161 !display->platform.coffeelake && 162 !display->platform.cometlake); 163 return PCH_CNP; 164 case INTEL_PCH_CMP_DEVICE_ID_TYPE: 165 case INTEL_PCH_CMP2_DEVICE_ID_TYPE: 166 drm_dbg_kms(display->drm, "Found Comet Lake PCH (CMP)\n"); 167 drm_WARN_ON(display->drm, 168 !display->platform.coffeelake && 169 !display->platform.cometlake && 170 !display->platform.rocketlake); 171 /* CMP is CNP compatible */ 172 return PCH_CNP; 173 case INTEL_PCH_CMP_V_DEVICE_ID_TYPE: 174 drm_dbg_kms(display->drm, "Found Comet Lake V PCH (CMP-V)\n"); 175 drm_WARN_ON(display->drm, 176 !display->platform.coffeelake && 177 !display->platform.cometlake); 178 /* CMP-V is based on KBP, which is SPT compatible */ 179 return PCH_SPT; 180 case INTEL_PCH_ICP_DEVICE_ID_TYPE: 181 case INTEL_PCH_ICP2_DEVICE_ID_TYPE: 182 drm_dbg_kms(display->drm, "Found Ice Lake PCH\n"); 183 drm_WARN_ON(display->drm, !display->platform.icelake); 184 return PCH_ICP; 185 case INTEL_PCH_MCC_DEVICE_ID_TYPE: 186 drm_dbg_kms(display->drm, "Found Mule Creek Canyon PCH\n"); 187 drm_WARN_ON(display->drm, !(display->platform.jasperlake || 188 display->platform.elkhartlake)); 189 /* MCC is TGP compatible */ 190 return PCH_TGP; 191 case INTEL_PCH_TGP_DEVICE_ID_TYPE: 192 case INTEL_PCH_TGP2_DEVICE_ID_TYPE: 193 drm_dbg_kms(display->drm, "Found Tiger Lake LP PCH\n"); 194 drm_WARN_ON(display->drm, !display->platform.tigerlake && 195 !display->platform.rocketlake && 196 !display->platform.skylake && 197 !display->platform.kabylake && 198 !display->platform.coffeelake && 199 !display->platform.cometlake); 200 return PCH_TGP; 201 case INTEL_PCH_JSP_DEVICE_ID_TYPE: 202 drm_dbg_kms(display->drm, "Found Jasper Lake PCH\n"); 203 drm_WARN_ON(display->drm, !(display->platform.jasperlake || 204 display->platform.elkhartlake)); 205 /* JSP is ICP compatible */ 206 return PCH_ICP; 207 case INTEL_PCH_ADP_DEVICE_ID_TYPE: 208 case INTEL_PCH_ADP2_DEVICE_ID_TYPE: 209 case INTEL_PCH_ADP3_DEVICE_ID_TYPE: 210 case INTEL_PCH_ADP4_DEVICE_ID_TYPE: 211 drm_dbg_kms(display->drm, "Found Alder Lake PCH\n"); 212 drm_WARN_ON(display->drm, !display->platform.alderlake_s && 213 !display->platform.alderlake_p); 214 return PCH_ADP; 215 default: 216 return PCH_NONE; 217 } 218 } 219 220 static void intel_pch_ibx_init_clock_gating(struct intel_display *display) 221 { 222 /* 223 * On Ibex Peak and Cougar Point, we need to disable clock 224 * gating for the panel power sequencer or it will fail to 225 * start up when no ports are active. 226 */ 227 intel_de_write(display, SOUTH_DSPCLK_GATE_D, 228 PCH_DPLSUNIT_CLOCK_GATE_DISABLE); 229 } 230 231 static void intel_pch_cpt_init_clock_gating(struct intel_display *display) 232 { 233 enum pipe pipe; 234 u32 val; 235 236 /* 237 * On Ibex Peak and Cougar Point, we need to disable clock 238 * gating for the panel power sequencer or it will fail to 239 * start up when no ports are active. 240 */ 241 intel_de_write(display, SOUTH_DSPCLK_GATE_D, 242 PCH_DPLSUNIT_CLOCK_GATE_DISABLE | 243 PCH_DPLUNIT_CLOCK_GATE_DISABLE | 244 PCH_CPUNIT_CLOCK_GATE_DISABLE); 245 intel_de_rmw(display, SOUTH_CHICKEN2, 0, DPLS_EDP_PPS_FIX_DIS); 246 247 /* The below fixes the weird display corruption, a few pixels shifted 248 * downward, on (only) LVDS of some HP laptops with IVY. 249 */ 250 for_each_pipe(display, pipe) { 251 val = intel_de_read(display, TRANS_CHICKEN2(pipe)); 252 val |= TRANS_CHICKEN2_TIMING_OVERRIDE; 253 val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED; 254 if (display->vbt.fdi_rx_polarity_inverted) 255 val |= TRANS_CHICKEN2_FDI_POLARITY_REVERSED; 256 val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_COUNTER; 257 val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_MODESWITCH; 258 intel_de_write(display, TRANS_CHICKEN2(pipe), val); 259 } 260 261 /* WADP0ClockGatingDisable */ 262 for_each_pipe(display, pipe) 263 intel_de_write(display, TRANS_CHICKEN1(pipe), 264 TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); 265 } 266 267 static void intel_pch_lpt_init_clock_gating(struct intel_display *display) 268 { 269 /* 270 * TODO: this bit should only be enabled when really needed, then 271 * disabled when not needed anymore in order to save power. 272 */ 273 if (HAS_PCH_LPT_LP(display)) 274 intel_de_rmw(display, SOUTH_DSPCLK_GATE_D, 0, 275 PCH_LP_PARTITION_LEVEL_DISABLE); 276 277 /* WADPOClockGatingDisable:hsw */ 278 intel_de_rmw(display, TRANS_CHICKEN1(PIPE_A), 0, 279 TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); 280 } 281 282 static void intel_pch_cnp_init_clock_gating(struct intel_display *display) 283 { 284 /* Display WA #1181 WaSouthDisplayDisablePWMCGEGating: cnp */ 285 intel_de_rmw(display, SOUTH_DSPCLK_GATE_D, 0, 286 CNP_PWM_CGE_GATING_DISABLE); 287 } 288 289 void intel_pch_init_clock_gating(struct intel_display *display) 290 { 291 switch (INTEL_PCH_TYPE(display)) { 292 case PCH_IBX: 293 intel_pch_ibx_init_clock_gating(display); 294 break; 295 case PCH_CPT: 296 intel_pch_cpt_init_clock_gating(display); 297 break; 298 case PCH_LPT_H: 299 case PCH_LPT_LP: 300 intel_pch_lpt_init_clock_gating(display); 301 break; 302 case PCH_CNP: 303 intel_pch_cnp_init_clock_gating(display); 304 break; 305 default: 306 break; 307 } 308 } 309 310 static bool intel_is_virt_pch(unsigned short id, 311 unsigned short svendor, unsigned short sdevice) 312 { 313 return (id == INTEL_PCH_P2X_DEVICE_ID_TYPE || 314 id == INTEL_PCH_P3X_DEVICE_ID_TYPE || 315 (id == INTEL_PCH_QEMU_DEVICE_ID_TYPE && 316 svendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET && 317 sdevice == PCI_SUBDEVICE_ID_QEMU)); 318 } 319 320 static void 321 intel_virt_detect_pch(const struct intel_display *display, 322 unsigned short *pch_id, enum intel_pch *pch_type) 323 { 324 unsigned short id = 0; 325 326 /* 327 * In a virtualized passthrough environment we can be in a 328 * setup where the ISA bridge is not able to be passed through. 329 * In this case, a south bridge can be emulated and we have to 330 * make an educated guess as to which PCH is really there. 331 */ 332 333 if (display->platform.alderlake_s || display->platform.alderlake_p) 334 id = INTEL_PCH_ADP_DEVICE_ID_TYPE; 335 else if (display->platform.tigerlake || display->platform.rocketlake) 336 id = INTEL_PCH_TGP_DEVICE_ID_TYPE; 337 else if (display->platform.jasperlake || display->platform.elkhartlake) 338 id = INTEL_PCH_MCC_DEVICE_ID_TYPE; 339 else if (display->platform.icelake) 340 id = INTEL_PCH_ICP_DEVICE_ID_TYPE; 341 else if (display->platform.coffeelake || 342 display->platform.cometlake) 343 id = INTEL_PCH_CNP_DEVICE_ID_TYPE; 344 else if (display->platform.kabylake || display->platform.skylake) 345 id = INTEL_PCH_SPT_DEVICE_ID_TYPE; 346 else if (display->platform.haswell_ult || 347 display->platform.broadwell_ult) 348 id = INTEL_PCH_LPT_LP_DEVICE_ID_TYPE; 349 else if (display->platform.haswell || display->platform.broadwell) 350 id = INTEL_PCH_LPT_DEVICE_ID_TYPE; 351 else if (DISPLAY_VER(display) == 6 || display->platform.ivybridge) 352 id = INTEL_PCH_CPT_DEVICE_ID_TYPE; 353 else if (DISPLAY_VER(display) == 5) 354 id = INTEL_PCH_IBX_DEVICE_ID_TYPE; 355 356 if (id) 357 drm_dbg_kms(display->drm, "Assuming PCH ID %04x\n", id); 358 else 359 drm_dbg_kms(display->drm, "Assuming no PCH\n"); 360 361 *pch_type = intel_pch_type(display, id); 362 363 /* Sanity check virtual PCH id */ 364 if (drm_WARN_ON(display->drm, 365 id && *pch_type == PCH_NONE)) 366 id = 0; 367 368 *pch_id = id; 369 } 370 371 void intel_pch_detect(struct intel_display *display) 372 { 373 struct pci_dev *pch = NULL; 374 unsigned short id; 375 enum intel_pch pch_type; 376 377 pch_type = intel_pch_fake_for_south_display(display); 378 if (pch_type != PCH_NONE) { 379 display->pch_type = pch_type; 380 drm_dbg_kms(display->drm, 381 "PCH not involved in display, using fake PCH type %d for south display\n", 382 pch_type); 383 return; 384 } 385 386 /* 387 * The reason to probe ISA bridge instead of Dev31:Fun0 is to 388 * make graphics device passthrough work easy for VMM, that only 389 * need to expose ISA bridge to let driver know the real hardware 390 * underneath. This is a requirement from virtualization team. 391 * 392 * In some virtualized environments (e.g. XEN), there is irrelevant 393 * ISA bridge in the system. To work reliably, we should scan through 394 * all the ISA bridge devices and check for the first match, instead 395 * of only checking the first one. 396 */ 397 while ((pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, pch))) { 398 if (pch->vendor != PCI_VENDOR_ID_INTEL) 399 continue; 400 401 id = pch->device & INTEL_PCH_DEVICE_ID_MASK; 402 403 pch_type = intel_pch_type(display, id); 404 if (pch_type != PCH_NONE) { 405 display->pch_type = pch_type; 406 break; 407 } else if (intel_is_virt_pch(id, pch->subsystem_vendor, 408 pch->subsystem_device)) { 409 intel_virt_detect_pch(display, &id, &pch_type); 410 display->pch_type = pch_type; 411 break; 412 } 413 } 414 415 /* 416 * Use PCH_NOP (PCH but no South Display) for PCH platforms without 417 * display. 418 */ 419 if (pch && !HAS_DISPLAY(display)) { 420 drm_dbg_kms(display->drm, 421 "Display disabled, reverting to NOP PCH\n"); 422 display->pch_type = PCH_NOP; 423 } else if (!pch) { 424 if (intel_display_run_as_guest(display) && HAS_DISPLAY(display)) { 425 intel_virt_detect_pch(display, &id, &pch_type); 426 display->pch_type = pch_type; 427 } else { 428 drm_dbg_kms(display->drm, "No PCH found.\n"); 429 } 430 } 431 432 pci_dev_put(pch); 433 } 434