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