1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright 2025 Intel Corporation.
4 */
5
6 #include <drm/drm_print.h>
7
8 #include "i915_utils.h"
9 #include "intel_display_core.h"
10 #include "intel_pch.h"
11
12 #define INTEL_PCH_DEVICE_ID_MASK 0xff80
13 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00
14 #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00
15 #define INTEL_PCH_PPT_DEVICE_ID_TYPE 0x1e00
16 #define INTEL_PCH_LPT_DEVICE_ID_TYPE 0x8c00
17 #define INTEL_PCH_LPT_LP_DEVICE_ID_TYPE 0x9c00
18 #define INTEL_PCH_WPT_DEVICE_ID_TYPE 0x8c80
19 #define INTEL_PCH_WPT_LP_DEVICE_ID_TYPE 0x9c80
20 #define INTEL_PCH_SPT_DEVICE_ID_TYPE 0xA100
21 #define INTEL_PCH_SPT_LP_DEVICE_ID_TYPE 0x9D00
22 #define INTEL_PCH_KBP_DEVICE_ID_TYPE 0xA280
23 #define INTEL_PCH_CNP_DEVICE_ID_TYPE 0xA300
24 #define INTEL_PCH_CNP_LP_DEVICE_ID_TYPE 0x9D80
25 #define INTEL_PCH_CMP_DEVICE_ID_TYPE 0x0280
26 #define INTEL_PCH_CMP2_DEVICE_ID_TYPE 0x0680
27 #define INTEL_PCH_CMP_V_DEVICE_ID_TYPE 0xA380
28 #define INTEL_PCH_ICP_DEVICE_ID_TYPE 0x3480
29 #define INTEL_PCH_ICP2_DEVICE_ID_TYPE 0x3880
30 #define INTEL_PCH_MCC_DEVICE_ID_TYPE 0x4B00
31 #define INTEL_PCH_TGP_DEVICE_ID_TYPE 0xA080
32 #define INTEL_PCH_TGP2_DEVICE_ID_TYPE 0x4380
33 #define INTEL_PCH_JSP_DEVICE_ID_TYPE 0x4D80
34 #define INTEL_PCH_ADP_DEVICE_ID_TYPE 0x7A80
35 #define INTEL_PCH_ADP2_DEVICE_ID_TYPE 0x5180
36 #define INTEL_PCH_ADP3_DEVICE_ID_TYPE 0x7A00
37 #define INTEL_PCH_ADP4_DEVICE_ID_TYPE 0x5480
38 #define INTEL_PCH_P2X_DEVICE_ID_TYPE 0x7100
39 #define INTEL_PCH_P3X_DEVICE_ID_TYPE 0x7000
40 #define INTEL_PCH_QEMU_DEVICE_ID_TYPE 0x2900 /* qemu q35 has 2918 */
41
42 /*
43 * Check for platforms where the south display is on the same PCI device or SoC
44 * die as the north display. The PCH (if it even exists) is not involved in
45 * display. Return a fake PCH type for south display handling on these
46 * platforms, without actually detecting the PCH, and PCH_NONE otherwise.
47 */
intel_pch_fake_for_south_display(struct intel_display * display)48 static enum intel_pch intel_pch_fake_for_south_display(struct intel_display *display)
49 {
50 enum intel_pch pch_type = PCH_NONE;
51
52 if (DISPLAY_VER(display) >= 20)
53 pch_type = PCH_LNL;
54 else if (display->platform.battlemage || display->platform.meteorlake)
55 pch_type = PCH_MTL;
56 else if (display->platform.dg2)
57 pch_type = PCH_DG2;
58 else if (display->platform.dg1)
59 pch_type = PCH_DG1;
60
61 return pch_type;
62 }
63
64 /* Map PCH device id to PCH type, or PCH_NONE if unknown. */
65 static enum intel_pch
intel_pch_type(const struct intel_display * display,unsigned short id)66 intel_pch_type(const struct intel_display *display, unsigned short id)
67 {
68 switch (id) {
69 case INTEL_PCH_IBX_DEVICE_ID_TYPE:
70 drm_dbg_kms(display->drm, "Found Ibex Peak PCH\n");
71 drm_WARN_ON(display->drm, DISPLAY_VER(display) != 5);
72 return PCH_IBX;
73 case INTEL_PCH_CPT_DEVICE_ID_TYPE:
74 drm_dbg_kms(display->drm, "Found CougarPoint PCH\n");
75 drm_WARN_ON(display->drm,
76 DISPLAY_VER(display) != 6 &&
77 !display->platform.ivybridge);
78 return PCH_CPT;
79 case INTEL_PCH_PPT_DEVICE_ID_TYPE:
80 drm_dbg_kms(display->drm, "Found PantherPoint PCH\n");
81 drm_WARN_ON(display->drm,
82 DISPLAY_VER(display) != 6 &&
83 !display->platform.ivybridge);
84 /* PPT is CPT compatible */
85 return PCH_CPT;
86 case INTEL_PCH_LPT_DEVICE_ID_TYPE:
87 drm_dbg_kms(display->drm, "Found LynxPoint PCH\n");
88 drm_WARN_ON(display->drm,
89 !display->platform.haswell &&
90 !display->platform.broadwell);
91 drm_WARN_ON(display->drm,
92 display->platform.haswell_ult ||
93 display->platform.broadwell_ult);
94 return PCH_LPT_H;
95 case INTEL_PCH_LPT_LP_DEVICE_ID_TYPE:
96 drm_dbg_kms(display->drm, "Found LynxPoint LP PCH\n");
97 drm_WARN_ON(display->drm,
98 !display->platform.haswell &&
99 !display->platform.broadwell);
100 drm_WARN_ON(display->drm,
101 !display->platform.haswell_ult &&
102 !display->platform.broadwell_ult);
103 return PCH_LPT_LP;
104 case INTEL_PCH_WPT_DEVICE_ID_TYPE:
105 drm_dbg_kms(display->drm, "Found WildcatPoint PCH\n");
106 drm_WARN_ON(display->drm,
107 !display->platform.haswell &&
108 !display->platform.broadwell);
109 drm_WARN_ON(display->drm,
110 display->platform.haswell_ult ||
111 display->platform.broadwell_ult);
112 /* WPT is LPT compatible */
113 return PCH_LPT_H;
114 case INTEL_PCH_WPT_LP_DEVICE_ID_TYPE:
115 drm_dbg_kms(display->drm, "Found WildcatPoint LP PCH\n");
116 drm_WARN_ON(display->drm,
117 !display->platform.haswell &&
118 !display->platform.broadwell);
119 drm_WARN_ON(display->drm,
120 !display->platform.haswell_ult &&
121 !display->platform.broadwell_ult);
122 /* WPT is LPT compatible */
123 return PCH_LPT_LP;
124 case INTEL_PCH_SPT_DEVICE_ID_TYPE:
125 drm_dbg_kms(display->drm, "Found SunrisePoint PCH\n");
126 drm_WARN_ON(display->drm,
127 !display->platform.skylake &&
128 !display->platform.kabylake &&
129 !display->platform.coffeelake);
130 return PCH_SPT;
131 case INTEL_PCH_SPT_LP_DEVICE_ID_TYPE:
132 drm_dbg_kms(display->drm, "Found SunrisePoint LP PCH\n");
133 drm_WARN_ON(display->drm,
134 !display->platform.skylake &&
135 !display->platform.kabylake &&
136 !display->platform.coffeelake &&
137 !display->platform.cometlake);
138 return PCH_SPT;
139 case INTEL_PCH_KBP_DEVICE_ID_TYPE:
140 drm_dbg_kms(display->drm, "Found Kaby Lake PCH (KBP)\n");
141 drm_WARN_ON(display->drm,
142 !display->platform.skylake &&
143 !display->platform.kabylake &&
144 !display->platform.coffeelake &&
145 !display->platform.cometlake);
146 /* KBP is SPT compatible */
147 return PCH_SPT;
148 case INTEL_PCH_CNP_DEVICE_ID_TYPE:
149 drm_dbg_kms(display->drm, "Found Cannon Lake PCH (CNP)\n");
150 drm_WARN_ON(display->drm,
151 !display->platform.coffeelake &&
152 !display->platform.cometlake);
153 return PCH_CNP;
154 case INTEL_PCH_CNP_LP_DEVICE_ID_TYPE:
155 drm_dbg_kms(display->drm,
156 "Found Cannon Lake LP PCH (CNP-LP)\n");
157 drm_WARN_ON(display->drm,
158 !display->platform.coffeelake &&
159 !display->platform.cometlake);
160 return PCH_CNP;
161 case INTEL_PCH_CMP_DEVICE_ID_TYPE:
162 case INTEL_PCH_CMP2_DEVICE_ID_TYPE:
163 drm_dbg_kms(display->drm, "Found Comet Lake PCH (CMP)\n");
164 drm_WARN_ON(display->drm,
165 !display->platform.coffeelake &&
166 !display->platform.cometlake &&
167 !display->platform.rocketlake);
168 /* CMP is CNP compatible */
169 return PCH_CNP;
170 case INTEL_PCH_CMP_V_DEVICE_ID_TYPE:
171 drm_dbg_kms(display->drm, "Found Comet Lake V PCH (CMP-V)\n");
172 drm_WARN_ON(display->drm,
173 !display->platform.coffeelake &&
174 !display->platform.cometlake);
175 /* CMP-V is based on KBP, which is SPT compatible */
176 return PCH_SPT;
177 case INTEL_PCH_ICP_DEVICE_ID_TYPE:
178 case INTEL_PCH_ICP2_DEVICE_ID_TYPE:
179 drm_dbg_kms(display->drm, "Found Ice Lake PCH\n");
180 drm_WARN_ON(display->drm, !display->platform.icelake);
181 return PCH_ICP;
182 case INTEL_PCH_MCC_DEVICE_ID_TYPE:
183 drm_dbg_kms(display->drm, "Found Mule Creek Canyon PCH\n");
184 drm_WARN_ON(display->drm, !(display->platform.jasperlake ||
185 display->platform.elkhartlake));
186 /* MCC is TGP compatible */
187 return PCH_TGP;
188 case INTEL_PCH_TGP_DEVICE_ID_TYPE:
189 case INTEL_PCH_TGP2_DEVICE_ID_TYPE:
190 drm_dbg_kms(display->drm, "Found Tiger Lake LP PCH\n");
191 drm_WARN_ON(display->drm, !display->platform.tigerlake &&
192 !display->platform.rocketlake &&
193 !display->platform.skylake &&
194 !display->platform.kabylake &&
195 !display->platform.coffeelake &&
196 !display->platform.cometlake);
197 return PCH_TGP;
198 case INTEL_PCH_JSP_DEVICE_ID_TYPE:
199 drm_dbg_kms(display->drm, "Found Jasper Lake PCH\n");
200 drm_WARN_ON(display->drm, !(display->platform.jasperlake ||
201 display->platform.elkhartlake));
202 /* JSP is ICP compatible */
203 return PCH_ICP;
204 case INTEL_PCH_ADP_DEVICE_ID_TYPE:
205 case INTEL_PCH_ADP2_DEVICE_ID_TYPE:
206 case INTEL_PCH_ADP3_DEVICE_ID_TYPE:
207 case INTEL_PCH_ADP4_DEVICE_ID_TYPE:
208 drm_dbg_kms(display->drm, "Found Alder Lake PCH\n");
209 drm_WARN_ON(display->drm, !display->platform.alderlake_s &&
210 !display->platform.alderlake_p);
211 return PCH_ADP;
212 default:
213 return PCH_NONE;
214 }
215 }
216
intel_is_virt_pch(unsigned short id,unsigned short svendor,unsigned short sdevice)217 static bool intel_is_virt_pch(unsigned short id,
218 unsigned short svendor, unsigned short sdevice)
219 {
220 return (id == INTEL_PCH_P2X_DEVICE_ID_TYPE ||
221 id == INTEL_PCH_P3X_DEVICE_ID_TYPE ||
222 (id == INTEL_PCH_QEMU_DEVICE_ID_TYPE &&
223 svendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET &&
224 sdevice == PCI_SUBDEVICE_ID_QEMU));
225 }
226
227 static void
intel_virt_detect_pch(const struct intel_display * display,unsigned short * pch_id,enum intel_pch * pch_type)228 intel_virt_detect_pch(const struct intel_display *display,
229 unsigned short *pch_id, enum intel_pch *pch_type)
230 {
231 unsigned short id = 0;
232
233 /*
234 * In a virtualized passthrough environment we can be in a
235 * setup where the ISA bridge is not able to be passed through.
236 * In this case, a south bridge can be emulated and we have to
237 * make an educated guess as to which PCH is really there.
238 */
239
240 if (display->platform.alderlake_s || display->platform.alderlake_p)
241 id = INTEL_PCH_ADP_DEVICE_ID_TYPE;
242 else if (display->platform.tigerlake || display->platform.rocketlake)
243 id = INTEL_PCH_TGP_DEVICE_ID_TYPE;
244 else if (display->platform.jasperlake || display->platform.elkhartlake)
245 id = INTEL_PCH_MCC_DEVICE_ID_TYPE;
246 else if (display->platform.icelake)
247 id = INTEL_PCH_ICP_DEVICE_ID_TYPE;
248 else if (display->platform.coffeelake ||
249 display->platform.cometlake)
250 id = INTEL_PCH_CNP_DEVICE_ID_TYPE;
251 else if (display->platform.kabylake || display->platform.skylake)
252 id = INTEL_PCH_SPT_DEVICE_ID_TYPE;
253 else if (display->platform.haswell_ult ||
254 display->platform.broadwell_ult)
255 id = INTEL_PCH_LPT_LP_DEVICE_ID_TYPE;
256 else if (display->platform.haswell || display->platform.broadwell)
257 id = INTEL_PCH_LPT_DEVICE_ID_TYPE;
258 else if (DISPLAY_VER(display) == 6 || display->platform.ivybridge)
259 id = INTEL_PCH_CPT_DEVICE_ID_TYPE;
260 else if (DISPLAY_VER(display) == 5)
261 id = INTEL_PCH_IBX_DEVICE_ID_TYPE;
262
263 if (id)
264 drm_dbg_kms(display->drm, "Assuming PCH ID %04x\n", id);
265 else
266 drm_dbg_kms(display->drm, "Assuming no PCH\n");
267
268 *pch_type = intel_pch_type(display, id);
269
270 /* Sanity check virtual PCH id */
271 if (drm_WARN_ON(display->drm,
272 id && *pch_type == PCH_NONE))
273 id = 0;
274
275 *pch_id = id;
276 }
277
intel_pch_detect(struct intel_display * display)278 void intel_pch_detect(struct intel_display *display)
279 {
280 struct pci_dev *pch = NULL;
281 unsigned short id;
282 enum intel_pch pch_type;
283
284 pch_type = intel_pch_fake_for_south_display(display);
285 if (pch_type != PCH_NONE) {
286 display->pch_type = pch_type;
287 drm_dbg_kms(display->drm,
288 "PCH not involved in display, using fake PCH type %d for south display\n",
289 pch_type);
290 return;
291 }
292
293 /*
294 * The reason to probe ISA bridge instead of Dev31:Fun0 is to
295 * make graphics device passthrough work easy for VMM, that only
296 * need to expose ISA bridge to let driver know the real hardware
297 * underneath. This is a requirement from virtualization team.
298 *
299 * In some virtualized environments (e.g. XEN), there is irrelevant
300 * ISA bridge in the system. To work reliably, we should scan through
301 * all the ISA bridge devices and check for the first match, instead
302 * of only checking the first one.
303 */
304 while ((pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, pch))) {
305 if (pch->vendor != PCI_VENDOR_ID_INTEL)
306 continue;
307
308 id = pch->device & INTEL_PCH_DEVICE_ID_MASK;
309
310 pch_type = intel_pch_type(display, id);
311 if (pch_type != PCH_NONE) {
312 display->pch_type = pch_type;
313 break;
314 } else if (intel_is_virt_pch(id, pch->subsystem_vendor,
315 pch->subsystem_device)) {
316 intel_virt_detect_pch(display, &id, &pch_type);
317 display->pch_type = pch_type;
318 break;
319 }
320 }
321
322 /*
323 * Use PCH_NOP (PCH but no South Display) for PCH platforms without
324 * display.
325 */
326 if (pch && !HAS_DISPLAY(display)) {
327 drm_dbg_kms(display->drm,
328 "Display disabled, reverting to NOP PCH\n");
329 display->pch_type = PCH_NOP;
330 } else if (!pch) {
331 if (i915_run_as_guest() && HAS_DISPLAY(display)) {
332 intel_virt_detect_pch(display, &id, &pch_type);
333 display->pch_type = pch_type;
334 } else {
335 drm_dbg_kms(display->drm, "No PCH found.\n");
336 }
337 }
338
339 pci_dev_put(pch);
340 }
341