xref: /linux/drivers/gpu/drm/i915/display/intel_pch.c (revision b08494a8f7416e5f09907318c5460ad6f6e2a548)
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  */
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
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 
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
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 
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