1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright 2019 Intel Corporation. |
4 | */ |
5 | |
6 | #include "i915_drv.h" |
7 | #include "i915_utils.h" |
8 | #include "intel_pch.h" |
9 | |
10 | /* Map PCH device id to PCH type, or PCH_NONE if unknown. */ |
11 | static enum intel_pch |
12 | intel_pch_type(const struct drm_i915_private *dev_priv, unsigned short id) |
13 | { |
14 | switch (id) { |
15 | case INTEL_PCH_IBX_DEVICE_ID_TYPE: |
16 | drm_dbg_kms(&dev_priv->drm, "Found Ibex Peak PCH\n" ); |
17 | drm_WARN_ON(&dev_priv->drm, GRAPHICS_VER(dev_priv) != 5); |
18 | return PCH_IBX; |
19 | case INTEL_PCH_CPT_DEVICE_ID_TYPE: |
20 | drm_dbg_kms(&dev_priv->drm, "Found CougarPoint PCH\n" ); |
21 | drm_WARN_ON(&dev_priv->drm, |
22 | GRAPHICS_VER(dev_priv) != 6 && !IS_IVYBRIDGE(dev_priv)); |
23 | return PCH_CPT; |
24 | case INTEL_PCH_PPT_DEVICE_ID_TYPE: |
25 | drm_dbg_kms(&dev_priv->drm, "Found PantherPoint PCH\n" ); |
26 | drm_WARN_ON(&dev_priv->drm, |
27 | GRAPHICS_VER(dev_priv) != 6 && !IS_IVYBRIDGE(dev_priv)); |
28 | /* PPT is CPT compatible */ |
29 | return PCH_CPT; |
30 | case INTEL_PCH_LPT_DEVICE_ID_TYPE: |
31 | drm_dbg_kms(&dev_priv->drm, "Found LynxPoint PCH\n" ); |
32 | drm_WARN_ON(&dev_priv->drm, |
33 | !IS_HASWELL(dev_priv) && !IS_BROADWELL(dev_priv)); |
34 | drm_WARN_ON(&dev_priv->drm, |
35 | IS_HASWELL_ULT(dev_priv) || IS_BROADWELL_ULT(dev_priv)); |
36 | return PCH_LPT; |
37 | case INTEL_PCH_LPT_LP_DEVICE_ID_TYPE: |
38 | drm_dbg_kms(&dev_priv->drm, "Found LynxPoint LP PCH\n" ); |
39 | drm_WARN_ON(&dev_priv->drm, |
40 | !IS_HASWELL(dev_priv) && !IS_BROADWELL(dev_priv)); |
41 | drm_WARN_ON(&dev_priv->drm, |
42 | !IS_HASWELL_ULT(dev_priv) && !IS_BROADWELL_ULT(dev_priv)); |
43 | return PCH_LPT; |
44 | case INTEL_PCH_WPT_DEVICE_ID_TYPE: |
45 | drm_dbg_kms(&dev_priv->drm, "Found WildcatPoint PCH\n" ); |
46 | drm_WARN_ON(&dev_priv->drm, |
47 | !IS_HASWELL(dev_priv) && !IS_BROADWELL(dev_priv)); |
48 | drm_WARN_ON(&dev_priv->drm, |
49 | IS_HASWELL_ULT(dev_priv) || IS_BROADWELL_ULT(dev_priv)); |
50 | /* WPT is LPT compatible */ |
51 | return PCH_LPT; |
52 | case INTEL_PCH_WPT_LP_DEVICE_ID_TYPE: |
53 | drm_dbg_kms(&dev_priv->drm, "Found WildcatPoint LP PCH\n" ); |
54 | drm_WARN_ON(&dev_priv->drm, |
55 | !IS_HASWELL(dev_priv) && !IS_BROADWELL(dev_priv)); |
56 | drm_WARN_ON(&dev_priv->drm, |
57 | !IS_HASWELL_ULT(dev_priv) && !IS_BROADWELL_ULT(dev_priv)); |
58 | /* WPT is LPT compatible */ |
59 | return PCH_LPT; |
60 | case INTEL_PCH_SPT_DEVICE_ID_TYPE: |
61 | drm_dbg_kms(&dev_priv->drm, "Found SunrisePoint PCH\n" ); |
62 | drm_WARN_ON(&dev_priv->drm, |
63 | !IS_SKYLAKE(dev_priv) && !IS_KABYLAKE(dev_priv)); |
64 | return PCH_SPT; |
65 | case INTEL_PCH_SPT_LP_DEVICE_ID_TYPE: |
66 | drm_dbg_kms(&dev_priv->drm, "Found SunrisePoint LP PCH\n" ); |
67 | drm_WARN_ON(&dev_priv->drm, |
68 | !IS_SKYLAKE(dev_priv) && |
69 | !IS_KABYLAKE(dev_priv) && |
70 | !IS_COFFEELAKE(dev_priv) && |
71 | !IS_COMETLAKE(dev_priv)); |
72 | return PCH_SPT; |
73 | case INTEL_PCH_KBP_DEVICE_ID_TYPE: |
74 | drm_dbg_kms(&dev_priv->drm, "Found Kaby Lake PCH (KBP)\n" ); |
75 | drm_WARN_ON(&dev_priv->drm, |
76 | !IS_SKYLAKE(dev_priv) && |
77 | !IS_KABYLAKE(dev_priv) && |
78 | !IS_COFFEELAKE(dev_priv) && |
79 | !IS_COMETLAKE(dev_priv)); |
80 | /* KBP is SPT compatible */ |
81 | return PCH_SPT; |
82 | case INTEL_PCH_CNP_DEVICE_ID_TYPE: |
83 | drm_dbg_kms(&dev_priv->drm, "Found Cannon Lake PCH (CNP)\n" ); |
84 | drm_WARN_ON(&dev_priv->drm, |
85 | !IS_COFFEELAKE(dev_priv) && |
86 | !IS_COMETLAKE(dev_priv)); |
87 | return PCH_CNP; |
88 | case INTEL_PCH_CNP_LP_DEVICE_ID_TYPE: |
89 | drm_dbg_kms(&dev_priv->drm, |
90 | "Found Cannon Lake LP PCH (CNP-LP)\n" ); |
91 | drm_WARN_ON(&dev_priv->drm, |
92 | !IS_COFFEELAKE(dev_priv) && |
93 | !IS_COMETLAKE(dev_priv)); |
94 | return PCH_CNP; |
95 | case INTEL_PCH_CMP_DEVICE_ID_TYPE: |
96 | case INTEL_PCH_CMP2_DEVICE_ID_TYPE: |
97 | drm_dbg_kms(&dev_priv->drm, "Found Comet Lake PCH (CMP)\n" ); |
98 | drm_WARN_ON(&dev_priv->drm, |
99 | !IS_COFFEELAKE(dev_priv) && |
100 | !IS_COMETLAKE(dev_priv) && |
101 | !IS_ROCKETLAKE(dev_priv)); |
102 | /* CMP is CNP compatible */ |
103 | return PCH_CNP; |
104 | case INTEL_PCH_CMP_V_DEVICE_ID_TYPE: |
105 | drm_dbg_kms(&dev_priv->drm, "Found Comet Lake V PCH (CMP-V)\n" ); |
106 | drm_WARN_ON(&dev_priv->drm, |
107 | !IS_COFFEELAKE(dev_priv) && |
108 | !IS_COMETLAKE(dev_priv)); |
109 | /* CMP-V is based on KBP, which is SPT compatible */ |
110 | return PCH_SPT; |
111 | case INTEL_PCH_ICP_DEVICE_ID_TYPE: |
112 | case INTEL_PCH_ICP2_DEVICE_ID_TYPE: |
113 | drm_dbg_kms(&dev_priv->drm, "Found Ice Lake PCH\n" ); |
114 | drm_WARN_ON(&dev_priv->drm, !IS_ICELAKE(dev_priv)); |
115 | return PCH_ICP; |
116 | case INTEL_PCH_MCC_DEVICE_ID_TYPE: |
117 | drm_dbg_kms(&dev_priv->drm, "Found Mule Creek Canyon PCH\n" ); |
118 | drm_WARN_ON(&dev_priv->drm, !(IS_JASPERLAKE(dev_priv) || |
119 | IS_ELKHARTLAKE(dev_priv))); |
120 | /* MCC is TGP compatible */ |
121 | return PCH_TGP; |
122 | case INTEL_PCH_TGP_DEVICE_ID_TYPE: |
123 | case INTEL_PCH_TGP2_DEVICE_ID_TYPE: |
124 | drm_dbg_kms(&dev_priv->drm, "Found Tiger Lake LP PCH\n" ); |
125 | drm_WARN_ON(&dev_priv->drm, !IS_TIGERLAKE(dev_priv) && |
126 | !IS_ROCKETLAKE(dev_priv) && |
127 | !IS_GEN9_BC(dev_priv)); |
128 | return PCH_TGP; |
129 | case INTEL_PCH_JSP_DEVICE_ID_TYPE: |
130 | drm_dbg_kms(&dev_priv->drm, "Found Jasper Lake PCH\n" ); |
131 | drm_WARN_ON(&dev_priv->drm, !(IS_JASPERLAKE(dev_priv) || |
132 | IS_ELKHARTLAKE(dev_priv))); |
133 | /* JSP is ICP compatible */ |
134 | return PCH_ICP; |
135 | case INTEL_PCH_ADP_DEVICE_ID_TYPE: |
136 | case INTEL_PCH_ADP2_DEVICE_ID_TYPE: |
137 | case INTEL_PCH_ADP3_DEVICE_ID_TYPE: |
138 | case INTEL_PCH_ADP4_DEVICE_ID_TYPE: |
139 | drm_dbg_kms(&dev_priv->drm, "Found Alder Lake PCH\n" ); |
140 | drm_WARN_ON(&dev_priv->drm, !IS_ALDERLAKE_S(dev_priv) && |
141 | !IS_ALDERLAKE_P(dev_priv)); |
142 | return PCH_ADP; |
143 | default: |
144 | return PCH_NONE; |
145 | } |
146 | } |
147 | |
148 | static bool intel_is_virt_pch(unsigned short id, |
149 | unsigned short svendor, unsigned short sdevice) |
150 | { |
151 | return (id == INTEL_PCH_P2X_DEVICE_ID_TYPE || |
152 | id == INTEL_PCH_P3X_DEVICE_ID_TYPE || |
153 | (id == INTEL_PCH_QEMU_DEVICE_ID_TYPE && |
154 | svendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET && |
155 | sdevice == PCI_SUBDEVICE_ID_QEMU)); |
156 | } |
157 | |
158 | static void |
159 | intel_virt_detect_pch(const struct drm_i915_private *dev_priv, |
160 | unsigned short *pch_id, enum intel_pch *pch_type) |
161 | { |
162 | unsigned short id = 0; |
163 | |
164 | /* |
165 | * In a virtualized passthrough environment we can be in a |
166 | * setup where the ISA bridge is not able to be passed through. |
167 | * In this case, a south bridge can be emulated and we have to |
168 | * make an educated guess as to which PCH is really there. |
169 | */ |
170 | |
171 | if (IS_ALDERLAKE_S(dev_priv) || IS_ALDERLAKE_P(dev_priv)) |
172 | id = INTEL_PCH_ADP_DEVICE_ID_TYPE; |
173 | else if (IS_TIGERLAKE(dev_priv) || IS_ROCKETLAKE(dev_priv)) |
174 | id = INTEL_PCH_TGP_DEVICE_ID_TYPE; |
175 | else if (IS_JASPERLAKE(dev_priv) || IS_ELKHARTLAKE(dev_priv)) |
176 | id = INTEL_PCH_MCC_DEVICE_ID_TYPE; |
177 | else if (IS_ICELAKE(dev_priv)) |
178 | id = INTEL_PCH_ICP_DEVICE_ID_TYPE; |
179 | else if (IS_COFFEELAKE(dev_priv) || |
180 | IS_COMETLAKE(dev_priv)) |
181 | id = INTEL_PCH_CNP_DEVICE_ID_TYPE; |
182 | else if (IS_KABYLAKE(dev_priv) || IS_SKYLAKE(dev_priv)) |
183 | id = INTEL_PCH_SPT_DEVICE_ID_TYPE; |
184 | else if (IS_HASWELL_ULT(dev_priv) || IS_BROADWELL_ULT(dev_priv)) |
185 | id = INTEL_PCH_LPT_LP_DEVICE_ID_TYPE; |
186 | else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) |
187 | id = INTEL_PCH_LPT_DEVICE_ID_TYPE; |
188 | else if (GRAPHICS_VER(dev_priv) == 6 || IS_IVYBRIDGE(dev_priv)) |
189 | id = INTEL_PCH_CPT_DEVICE_ID_TYPE; |
190 | else if (GRAPHICS_VER(dev_priv) == 5) |
191 | id = INTEL_PCH_IBX_DEVICE_ID_TYPE; |
192 | |
193 | if (id) |
194 | drm_dbg_kms(&dev_priv->drm, "Assuming PCH ID %04x\n" , id); |
195 | else |
196 | drm_dbg_kms(&dev_priv->drm, "Assuming no PCH\n" ); |
197 | |
198 | *pch_type = intel_pch_type(dev_priv, id); |
199 | |
200 | /* Sanity check virtual PCH id */ |
201 | if (drm_WARN_ON(&dev_priv->drm, |
202 | id && *pch_type == PCH_NONE)) |
203 | id = 0; |
204 | |
205 | *pch_id = id; |
206 | } |
207 | |
208 | void intel_detect_pch(struct drm_i915_private *dev_priv) |
209 | { |
210 | struct pci_dev *pch = NULL; |
211 | unsigned short id; |
212 | enum intel_pch pch_type; |
213 | |
214 | /* |
215 | * South display engine on the same PCI device: just assign the fake |
216 | * PCH. |
217 | */ |
218 | if (DISPLAY_VER(dev_priv) >= 20) { |
219 | dev_priv->pch_type = PCH_LNL; |
220 | return; |
221 | } else if (IS_METEORLAKE(dev_priv)) { |
222 | /* |
223 | * Both north display and south display are on the SoC die. |
224 | * The real PCH is uninvolved in display. |
225 | */ |
226 | dev_priv->pch_type = PCH_MTL; |
227 | return; |
228 | } else if (IS_DG2(dev_priv)) { |
229 | dev_priv->pch_type = PCH_DG2; |
230 | return; |
231 | } else if (IS_DG1(dev_priv)) { |
232 | dev_priv->pch_type = PCH_DG1; |
233 | return; |
234 | } |
235 | |
236 | /* |
237 | * The reason to probe ISA bridge instead of Dev31:Fun0 is to |
238 | * make graphics device passthrough work easy for VMM, that only |
239 | * need to expose ISA bridge to let driver know the real hardware |
240 | * underneath. This is a requirement from virtualization team. |
241 | * |
242 | * In some virtualized environments (e.g. XEN), there is irrelevant |
243 | * ISA bridge in the system. To work reliably, we should scan trhough |
244 | * all the ISA bridge devices and check for the first match, instead |
245 | * of only checking the first one. |
246 | */ |
247 | while ((pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, from: pch))) { |
248 | if (pch->vendor != PCI_VENDOR_ID_INTEL) |
249 | continue; |
250 | |
251 | id = pch->device & INTEL_PCH_DEVICE_ID_MASK; |
252 | |
253 | pch_type = intel_pch_type(dev_priv, id); |
254 | if (pch_type != PCH_NONE) { |
255 | dev_priv->pch_type = pch_type; |
256 | dev_priv->pch_id = id; |
257 | break; |
258 | } else if (intel_is_virt_pch(id, svendor: pch->subsystem_vendor, |
259 | sdevice: pch->subsystem_device)) { |
260 | intel_virt_detect_pch(dev_priv, pch_id: &id, pch_type: &pch_type); |
261 | dev_priv->pch_type = pch_type; |
262 | dev_priv->pch_id = id; |
263 | break; |
264 | } |
265 | } |
266 | |
267 | /* |
268 | * Use PCH_NOP (PCH but no South Display) for PCH platforms without |
269 | * display. |
270 | */ |
271 | if (pch && !HAS_DISPLAY(dev_priv)) { |
272 | drm_dbg_kms(&dev_priv->drm, |
273 | "Display disabled, reverting to NOP PCH\n" ); |
274 | dev_priv->pch_type = PCH_NOP; |
275 | dev_priv->pch_id = 0; |
276 | } else if (!pch) { |
277 | if (i915_run_as_guest() && HAS_DISPLAY(dev_priv)) { |
278 | intel_virt_detect_pch(dev_priv, pch_id: &id, pch_type: &pch_type); |
279 | dev_priv->pch_type = pch_type; |
280 | dev_priv->pch_id = id; |
281 | } else { |
282 | drm_dbg_kms(&dev_priv->drm, "No PCH found.\n" ); |
283 | } |
284 | } |
285 | |
286 | pci_dev_put(dev: pch); |
287 | } |
288 | |