1 | /* |
2 | * Copyright 2008 Intel Corporation <hong.liu@intel.com> |
3 | * Copyright 2008 Red Hat <mjg@redhat.com> |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining |
6 | * a copy of this software and associated documentation files (the |
7 | * "Software"), to deal in the Software without restriction, including |
8 | * without limitation the rights to use, copy, modify, merge, publish, |
9 | * distribute, sub license, and/or sell copies of the Software, and to |
10 | * permit persons to whom the Software is furnished to do so, subject to |
11 | * the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice (including the |
14 | * next paragraph) shall be included in all copies or substantial |
15 | * portions of the Software. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
20 | * NON-INFRINGEMENT. IN NO EVENT SHALL INTEL AND/OR ITS SUPPLIERS BE |
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
24 | * SOFTWARE. |
25 | * |
26 | */ |
27 | |
28 | #include <linux/acpi.h> |
29 | #include <linux/dmi.h> |
30 | #include <linux/firmware.h> |
31 | #include <acpi/video.h> |
32 | |
33 | #include <drm/drm_edid.h> |
34 | |
35 | #include "i915_drv.h" |
36 | #include "intel_acpi.h" |
37 | #include "intel_backlight.h" |
38 | #include "intel_display_types.h" |
39 | #include "intel_opregion.h" |
40 | #include "intel_pci_config.h" |
41 | |
42 | #define 0 |
43 | #define OPREGION_ACPI_OFFSET 0x100 |
44 | #define ACPI_CLID 0x01ac /* current lid state indicator */ |
45 | #define ACPI_CDCK 0x01b0 /* current docking state indicator */ |
46 | #define OPREGION_SWSCI_OFFSET 0x200 |
47 | #define OPREGION_ASLE_OFFSET 0x300 |
48 | #define OPREGION_VBT_OFFSET 0x400 |
49 | #define OPREGION_ASLE_EXT_OFFSET 0x1C00 |
50 | |
51 | #define OPREGION_SIGNATURE "IntelGraphicsMem" |
52 | #define MBOX_ACPI BIT(0) /* Mailbox #1 */ |
53 | #define MBOX_SWSCI BIT(1) /* Mailbox #2 (obsolete from v2.x) */ |
54 | #define MBOX_ASLE BIT(2) /* Mailbox #3 */ |
55 | #define MBOX_ASLE_EXT BIT(4) /* Mailbox #5 */ |
56 | #define MBOX_BACKLIGHT BIT(5) /* Mailbox #2 (valid from v3.x) */ |
57 | |
58 | #define PCON_HEADLESS_SKU BIT(13) |
59 | |
60 | struct { |
61 | u8 [16]; |
62 | u32 ; |
63 | struct { |
64 | u8 ; |
65 | u8 ; |
66 | u8 ; |
67 | u8 ; |
68 | } __packed ; |
69 | u8 [32]; |
70 | u8 [16]; |
71 | u8 [16]; |
72 | u32 ; |
73 | u32 ; |
74 | u32 ; |
75 | u8 [32]; |
76 | u8 [124]; |
77 | } __packed; |
78 | |
79 | /* OpRegion mailbox #1: public ACPI methods */ |
80 | struct opregion_acpi { |
81 | u32 drdy; /* driver readiness */ |
82 | u32 csts; /* notification status */ |
83 | u32 cevt; /* current event */ |
84 | u8 rsvd1[20]; |
85 | u32 didl[8]; /* supported display devices ID list */ |
86 | u32 cpdl[8]; /* currently presented display list */ |
87 | u32 cadl[8]; /* currently active display list */ |
88 | u32 nadl[8]; /* next active devices list */ |
89 | u32 aslp; /* ASL sleep time-out */ |
90 | u32 tidx; /* toggle table index */ |
91 | u32 chpd; /* current hotplug enable indicator */ |
92 | u32 clid; /* current lid state*/ |
93 | u32 cdck; /* current docking state */ |
94 | u32 sxsw; /* Sx state resume */ |
95 | u32 evts; /* ASL supported events */ |
96 | u32 cnot; /* current OS notification */ |
97 | u32 nrdy; /* driver status */ |
98 | u32 did2[7]; /* extended supported display devices ID list */ |
99 | u32 cpd2[7]; /* extended attached display devices list */ |
100 | u8 rsvd2[4]; |
101 | } __packed; |
102 | |
103 | /* OpRegion mailbox #2: SWSCI */ |
104 | struct opregion_swsci { |
105 | u32 scic; /* SWSCI command|status|data */ |
106 | u32 parm; /* command parameters */ |
107 | u32 dslp; /* driver sleep time-out */ |
108 | u8 rsvd[244]; |
109 | } __packed; |
110 | |
111 | /* OpRegion mailbox #3: ASLE */ |
112 | struct opregion_asle { |
113 | u32 ardy; /* driver readiness */ |
114 | u32 aslc; /* ASLE interrupt command */ |
115 | u32 tche; /* technology enabled indicator */ |
116 | u32 alsi; /* current ALS illuminance reading */ |
117 | u32 bclp; /* backlight brightness to set */ |
118 | u32 pfit; /* panel fitting state */ |
119 | u32 cblv; /* current brightness level */ |
120 | u16 bclm[20]; /* backlight level duty cycle mapping table */ |
121 | u32 cpfm; /* current panel fitting mode */ |
122 | u32 epfm; /* enabled panel fitting modes */ |
123 | u8 plut[74]; /* panel LUT and identifier */ |
124 | u32 pfmb; /* PWM freq and min brightness */ |
125 | u32 cddv; /* color correction default values */ |
126 | u32 pcft; /* power conservation features */ |
127 | u32 srot; /* supported rotation angles */ |
128 | u32 iuer; /* IUER events */ |
129 | u64 fdss; |
130 | u32 fdsp; |
131 | u32 stat; |
132 | u64 rvda; /* Physical (2.0) or relative from opregion (2.1+) |
133 | * address of raw VBT data. */ |
134 | u32 rvds; /* Size of raw vbt data */ |
135 | u8 rsvd[58]; |
136 | } __packed; |
137 | |
138 | /* OpRegion mailbox #5: ASLE ext */ |
139 | struct opregion_asle_ext { |
140 | u32 phed; /* Panel Header */ |
141 | u8 bddc[256]; /* Panel EDID */ |
142 | u8 rsvd[764]; |
143 | } __packed; |
144 | |
145 | /* Driver readiness indicator */ |
146 | #define ASLE_ARDY_READY (1 << 0) |
147 | #define ASLE_ARDY_NOT_READY (0 << 0) |
148 | |
149 | /* ASLE Interrupt Command (ASLC) bits */ |
150 | #define ASLC_SET_ALS_ILLUM (1 << 0) |
151 | #define ASLC_SET_BACKLIGHT (1 << 1) |
152 | #define ASLC_SET_PFIT (1 << 2) |
153 | #define ASLC_SET_PWM_FREQ (1 << 3) |
154 | #define ASLC_SUPPORTED_ROTATION_ANGLES (1 << 4) |
155 | #define ASLC_BUTTON_ARRAY (1 << 5) |
156 | #define ASLC_CONVERTIBLE_INDICATOR (1 << 6) |
157 | #define ASLC_DOCKING_INDICATOR (1 << 7) |
158 | #define ASLC_ISCT_STATE_CHANGE (1 << 8) |
159 | #define ASLC_REQ_MSK 0x1ff |
160 | /* response bits */ |
161 | #define ASLC_ALS_ILLUM_FAILED (1 << 10) |
162 | #define ASLC_BACKLIGHT_FAILED (1 << 12) |
163 | #define ASLC_PFIT_FAILED (1 << 14) |
164 | #define ASLC_PWM_FREQ_FAILED (1 << 16) |
165 | #define ASLC_ROTATION_ANGLES_FAILED (1 << 18) |
166 | #define ASLC_BUTTON_ARRAY_FAILED (1 << 20) |
167 | #define ASLC_CONVERTIBLE_FAILED (1 << 22) |
168 | #define ASLC_DOCKING_FAILED (1 << 24) |
169 | #define ASLC_ISCT_STATE_FAILED (1 << 26) |
170 | |
171 | /* Technology enabled indicator */ |
172 | #define ASLE_TCHE_ALS_EN (1 << 0) |
173 | #define ASLE_TCHE_BLC_EN (1 << 1) |
174 | #define ASLE_TCHE_PFIT_EN (1 << 2) |
175 | #define ASLE_TCHE_PFMB_EN (1 << 3) |
176 | |
177 | /* ASLE backlight brightness to set */ |
178 | #define ASLE_BCLP_VALID (1<<31) |
179 | #define ASLE_BCLP_MSK (~(1<<31)) |
180 | |
181 | /* ASLE panel fitting request */ |
182 | #define ASLE_PFIT_VALID (1<<31) |
183 | #define ASLE_PFIT_CENTER (1<<0) |
184 | #define ASLE_PFIT_STRETCH_TEXT (1<<1) |
185 | #define ASLE_PFIT_STRETCH_GFX (1<<2) |
186 | |
187 | /* PWM frequency and minimum brightness */ |
188 | #define ASLE_PFMB_BRIGHTNESS_MASK (0xff) |
189 | #define ASLE_PFMB_BRIGHTNESS_VALID (1<<8) |
190 | #define ASLE_PFMB_PWM_MASK (0x7ffffe00) |
191 | #define ASLE_PFMB_PWM_VALID (1<<31) |
192 | |
193 | #define ASLE_CBLV_VALID (1<<31) |
194 | |
195 | /* IUER */ |
196 | #define ASLE_IUER_DOCKING (1 << 7) |
197 | #define ASLE_IUER_CONVERTIBLE (1 << 6) |
198 | #define ASLE_IUER_ROTATION_LOCK_BTN (1 << 4) |
199 | #define ASLE_IUER_VOLUME_DOWN_BTN (1 << 3) |
200 | #define ASLE_IUER_VOLUME_UP_BTN (1 << 2) |
201 | #define ASLE_IUER_WINDOWS_BTN (1 << 1) |
202 | #define ASLE_IUER_POWER_BTN (1 << 0) |
203 | |
204 | #define ASLE_PHED_EDID_VALID_MASK 0x3 |
205 | |
206 | /* Software System Control Interrupt (SWSCI) */ |
207 | #define SWSCI_SCIC_INDICATOR (1 << 0) |
208 | #define SWSCI_SCIC_MAIN_FUNCTION_SHIFT 1 |
209 | #define SWSCI_SCIC_MAIN_FUNCTION_MASK (0xf << 1) |
210 | #define SWSCI_SCIC_SUB_FUNCTION_SHIFT 8 |
211 | #define SWSCI_SCIC_SUB_FUNCTION_MASK (0xff << 8) |
212 | #define SWSCI_SCIC_EXIT_PARAMETER_SHIFT 8 |
213 | #define SWSCI_SCIC_EXIT_PARAMETER_MASK (0xff << 8) |
214 | #define SWSCI_SCIC_EXIT_STATUS_SHIFT 5 |
215 | #define SWSCI_SCIC_EXIT_STATUS_MASK (7 << 5) |
216 | #define SWSCI_SCIC_EXIT_STATUS_SUCCESS 1 |
217 | |
218 | #define SWSCI_FUNCTION_CODE(main, sub) \ |
219 | ((main) << SWSCI_SCIC_MAIN_FUNCTION_SHIFT | \ |
220 | (sub) << SWSCI_SCIC_SUB_FUNCTION_SHIFT) |
221 | |
222 | /* SWSCI: Get BIOS Data (GBDA) */ |
223 | #define SWSCI_GBDA 4 |
224 | #define SWSCI_GBDA_SUPPORTED_CALLS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 0) |
225 | #define SWSCI_GBDA_REQUESTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 1) |
226 | #define SWSCI_GBDA_BOOT_DISPLAY_PREF SWSCI_FUNCTION_CODE(SWSCI_GBDA, 4) |
227 | #define SWSCI_GBDA_PANEL_DETAILS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 5) |
228 | #define SWSCI_GBDA_TV_STANDARD SWSCI_FUNCTION_CODE(SWSCI_GBDA, 6) |
229 | #define SWSCI_GBDA_INTERNAL_GRAPHICS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 7) |
230 | #define SWSCI_GBDA_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_GBDA, 10) |
231 | |
232 | /* SWSCI: System BIOS Callbacks (SBCB) */ |
233 | #define SWSCI_SBCB 6 |
234 | #define SWSCI_SBCB_SUPPORTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 0) |
235 | #define SWSCI_SBCB_INIT_COMPLETION SWSCI_FUNCTION_CODE(SWSCI_SBCB, 1) |
236 | #define SWSCI_SBCB_PRE_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 3) |
237 | #define SWSCI_SBCB_POST_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 4) |
238 | #define SWSCI_SBCB_DISPLAY_SWITCH SWSCI_FUNCTION_CODE(SWSCI_SBCB, 5) |
239 | #define SWSCI_SBCB_SET_TV_FORMAT SWSCI_FUNCTION_CODE(SWSCI_SBCB, 6) |
240 | #define SWSCI_SBCB_ADAPTER_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 7) |
241 | #define SWSCI_SBCB_DISPLAY_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 8) |
242 | #define SWSCI_SBCB_SET_BOOT_DISPLAY SWSCI_FUNCTION_CODE(SWSCI_SBCB, 9) |
243 | #define SWSCI_SBCB_SET_PANEL_DETAILS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 10) |
244 | #define SWSCI_SBCB_SET_INTERNAL_GFX SWSCI_FUNCTION_CODE(SWSCI_SBCB, 11) |
245 | #define SWSCI_SBCB_POST_HIRES_TO_DOS_FS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 16) |
246 | #define SWSCI_SBCB_SUSPEND_RESUME SWSCI_FUNCTION_CODE(SWSCI_SBCB, 17) |
247 | #define SWSCI_SBCB_SET_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 18) |
248 | #define SWSCI_SBCB_POST_VBE_PM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 19) |
249 | #define SWSCI_SBCB_ENABLE_DISABLE_AUDIO SWSCI_FUNCTION_CODE(SWSCI_SBCB, 21) |
250 | |
251 | #define MAX_DSLP 1500 |
252 | |
253 | #define OPREGION_SIZE (8 * 1024) |
254 | |
255 | struct intel_opregion { |
256 | struct drm_i915_private *i915; |
257 | |
258 | struct opregion_header *; |
259 | struct opregion_acpi *acpi; |
260 | struct opregion_swsci *swsci; |
261 | u32 swsci_gbda_sub_functions; |
262 | u32 swsci_sbcb_sub_functions; |
263 | struct opregion_asle *asle; |
264 | struct opregion_asle_ext *asle_ext; |
265 | void *rvda; |
266 | void *vbt_firmware; |
267 | const void *vbt; |
268 | u32 vbt_size; |
269 | struct work_struct asle_work; |
270 | struct notifier_block acpi_notifier; |
271 | }; |
272 | |
273 | static int check_swsci_function(struct drm_i915_private *i915, u32 function) |
274 | { |
275 | struct intel_opregion *opregion = i915->display.opregion; |
276 | struct opregion_swsci *swsci; |
277 | u32 main_function, sub_function; |
278 | |
279 | if (!opregion) |
280 | return -ENODEV; |
281 | |
282 | swsci = opregion->swsci; |
283 | if (!swsci) |
284 | return -ENODEV; |
285 | |
286 | main_function = (function & SWSCI_SCIC_MAIN_FUNCTION_MASK) >> |
287 | SWSCI_SCIC_MAIN_FUNCTION_SHIFT; |
288 | sub_function = (function & SWSCI_SCIC_SUB_FUNCTION_MASK) >> |
289 | SWSCI_SCIC_SUB_FUNCTION_SHIFT; |
290 | |
291 | /* Check if we can call the function. See swsci_setup for details. */ |
292 | if (main_function == SWSCI_SBCB) { |
293 | if ((opregion->swsci_sbcb_sub_functions & |
294 | (1 << sub_function)) == 0) |
295 | return -EINVAL; |
296 | } else if (main_function == SWSCI_GBDA) { |
297 | if ((opregion->swsci_gbda_sub_functions & |
298 | (1 << sub_function)) == 0) |
299 | return -EINVAL; |
300 | } |
301 | |
302 | return 0; |
303 | } |
304 | |
305 | static int swsci(struct drm_i915_private *dev_priv, |
306 | u32 function, u32 parm, u32 *parm_out) |
307 | { |
308 | struct opregion_swsci *swsci; |
309 | struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); |
310 | u32 scic, dslp; |
311 | u16 swsci_val; |
312 | int ret; |
313 | |
314 | ret = check_swsci_function(i915: dev_priv, function); |
315 | if (ret) |
316 | return ret; |
317 | |
318 | swsci = dev_priv->display.opregion->swsci; |
319 | |
320 | /* Driver sleep timeout in ms. */ |
321 | dslp = swsci->dslp; |
322 | if (!dslp) { |
323 | /* The spec says 2ms should be the default, but it's too small |
324 | * for some machines. */ |
325 | dslp = 50; |
326 | } else if (dslp > MAX_DSLP) { |
327 | /* Hey bios, trust must be earned. */ |
328 | DRM_INFO_ONCE("ACPI BIOS requests an excessive sleep of %u ms, " |
329 | "using %u ms instead\n" , dslp, MAX_DSLP); |
330 | dslp = MAX_DSLP; |
331 | } |
332 | |
333 | /* The spec tells us to do this, but we are the only user... */ |
334 | scic = swsci->scic; |
335 | if (scic & SWSCI_SCIC_INDICATOR) { |
336 | drm_dbg(&dev_priv->drm, "SWSCI request already in progress\n" ); |
337 | return -EBUSY; |
338 | } |
339 | |
340 | scic = function | SWSCI_SCIC_INDICATOR; |
341 | |
342 | swsci->parm = parm; |
343 | swsci->scic = scic; |
344 | |
345 | /* Ensure SCI event is selected and event trigger is cleared. */ |
346 | pci_read_config_word(dev: pdev, SWSCI, val: &swsci_val); |
347 | if (!(swsci_val & SWSCI_SCISEL) || (swsci_val & SWSCI_GSSCIE)) { |
348 | swsci_val |= SWSCI_SCISEL; |
349 | swsci_val &= ~SWSCI_GSSCIE; |
350 | pci_write_config_word(dev: pdev, SWSCI, val: swsci_val); |
351 | } |
352 | |
353 | /* Use event trigger to tell bios to check the mail. */ |
354 | swsci_val |= SWSCI_GSSCIE; |
355 | pci_write_config_word(dev: pdev, SWSCI, val: swsci_val); |
356 | |
357 | /* Poll for the result. */ |
358 | #define C (((scic = swsci->scic) & SWSCI_SCIC_INDICATOR) == 0) |
359 | if (wait_for(C, dslp)) { |
360 | drm_dbg(&dev_priv->drm, "SWSCI request timed out\n" ); |
361 | return -ETIMEDOUT; |
362 | } |
363 | |
364 | scic = (scic & SWSCI_SCIC_EXIT_STATUS_MASK) >> |
365 | SWSCI_SCIC_EXIT_STATUS_SHIFT; |
366 | |
367 | /* Note: scic == 0 is an error! */ |
368 | if (scic != SWSCI_SCIC_EXIT_STATUS_SUCCESS) { |
369 | drm_dbg(&dev_priv->drm, "SWSCI request error %u\n" , scic); |
370 | return -EIO; |
371 | } |
372 | |
373 | if (parm_out) |
374 | *parm_out = swsci->parm; |
375 | |
376 | return 0; |
377 | |
378 | #undef C |
379 | } |
380 | |
381 | #define DISPLAY_TYPE_CRT 0 |
382 | #define DISPLAY_TYPE_TV 1 |
383 | #define DISPLAY_TYPE_EXTERNAL_FLAT_PANEL 2 |
384 | #define DISPLAY_TYPE_INTERNAL_FLAT_PANEL 3 |
385 | |
386 | int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder, |
387 | bool enable) |
388 | { |
389 | struct drm_i915_private *dev_priv = to_i915(dev: intel_encoder->base.dev); |
390 | u32 parm = 0; |
391 | u32 type = 0; |
392 | u32 port; |
393 | int ret; |
394 | |
395 | /* don't care about old stuff for now */ |
396 | if (!HAS_DDI(dev_priv)) |
397 | return 0; |
398 | |
399 | /* Avoid port out of bounds checks if SWSCI isn't there. */ |
400 | ret = check_swsci_function(i915: dev_priv, SWSCI_SBCB_DISPLAY_POWER_STATE); |
401 | if (ret) |
402 | return ret; |
403 | |
404 | if (intel_encoder->type == INTEL_OUTPUT_DSI) |
405 | port = 0; |
406 | else |
407 | port = intel_encoder->port; |
408 | |
409 | if (port == PORT_E) { |
410 | port = 0; |
411 | } else { |
412 | parm |= 1 << port; |
413 | port++; |
414 | } |
415 | |
416 | /* |
417 | * The port numbering and mapping here is bizarre. The now-obsolete |
418 | * swsci spec supports ports numbered [0..4]. Port E is handled as a |
419 | * special case, but port F and beyond are not. The functionality is |
420 | * supposed to be obsolete for new platforms. Just bail out if the port |
421 | * number is out of bounds after mapping. |
422 | */ |
423 | if (port > 4) { |
424 | drm_dbg_kms(&dev_priv->drm, |
425 | "[ENCODER:%d:%s] port %c (index %u) out of bounds for display power state notification\n" , |
426 | intel_encoder->base.base.id, intel_encoder->base.name, |
427 | port_name(intel_encoder->port), port); |
428 | return -EINVAL; |
429 | } |
430 | |
431 | if (!enable) |
432 | parm |= 4 << 8; |
433 | |
434 | switch (intel_encoder->type) { |
435 | case INTEL_OUTPUT_ANALOG: |
436 | type = DISPLAY_TYPE_CRT; |
437 | break; |
438 | case INTEL_OUTPUT_DDI: |
439 | case INTEL_OUTPUT_DP: |
440 | case INTEL_OUTPUT_HDMI: |
441 | case INTEL_OUTPUT_DP_MST: |
442 | type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL; |
443 | break; |
444 | case INTEL_OUTPUT_EDP: |
445 | case INTEL_OUTPUT_DSI: |
446 | type = DISPLAY_TYPE_INTERNAL_FLAT_PANEL; |
447 | break; |
448 | default: |
449 | drm_WARN_ONCE(&dev_priv->drm, 1, |
450 | "unsupported intel_encoder type %d\n" , |
451 | intel_encoder->type); |
452 | return -EINVAL; |
453 | } |
454 | |
455 | parm |= type << (16 + port * 3); |
456 | |
457 | return swsci(dev_priv, SWSCI_SBCB_DISPLAY_POWER_STATE, parm, NULL); |
458 | } |
459 | |
460 | static const struct { |
461 | pci_power_t pci_power_state; |
462 | u32 parm; |
463 | } power_state_map[] = { |
464 | { PCI_D0, 0x00 }, |
465 | { PCI_D1, 0x01 }, |
466 | { PCI_D2, 0x02 }, |
467 | { PCI_D3hot, 0x04 }, |
468 | { PCI_D3cold, 0x04 }, |
469 | }; |
470 | |
471 | int intel_opregion_notify_adapter(struct drm_i915_private *dev_priv, |
472 | pci_power_t state) |
473 | { |
474 | int i; |
475 | |
476 | if (!HAS_DDI(dev_priv)) |
477 | return 0; |
478 | |
479 | for (i = 0; i < ARRAY_SIZE(power_state_map); i++) { |
480 | if (state == power_state_map[i].pci_power_state) |
481 | return swsci(dev_priv, SWSCI_SBCB_ADAPTER_POWER_STATE, |
482 | parm: power_state_map[i].parm, NULL); |
483 | } |
484 | |
485 | return -EINVAL; |
486 | } |
487 | |
488 | static u32 asle_set_backlight(struct drm_i915_private *dev_priv, u32 bclp) |
489 | { |
490 | struct intel_connector *connector; |
491 | struct drm_connector_list_iter conn_iter; |
492 | struct opregion_asle *asle = dev_priv->display.opregion->asle; |
493 | |
494 | drm_dbg(&dev_priv->drm, "bclp = 0x%08x\n" , bclp); |
495 | |
496 | if (acpi_video_get_backlight_type() == acpi_backlight_native) { |
497 | drm_dbg_kms(&dev_priv->drm, |
498 | "opregion backlight request ignored\n" ); |
499 | return 0; |
500 | } |
501 | |
502 | if (!(bclp & ASLE_BCLP_VALID)) |
503 | return ASLC_BACKLIGHT_FAILED; |
504 | |
505 | bclp &= ASLE_BCLP_MSK; |
506 | if (bclp > 255) |
507 | return ASLC_BACKLIGHT_FAILED; |
508 | |
509 | drm_modeset_lock(lock: &dev_priv->drm.mode_config.connection_mutex, NULL); |
510 | |
511 | /* |
512 | * Update backlight on all connectors that support backlight (usually |
513 | * only one). |
514 | */ |
515 | drm_dbg_kms(&dev_priv->drm, "updating opregion backlight %d/255\n" , |
516 | bclp); |
517 | drm_connector_list_iter_begin(dev: &dev_priv->drm, iter: &conn_iter); |
518 | for_each_intel_connector_iter(connector, &conn_iter) |
519 | intel_backlight_set_acpi(conn_state: connector->base.state, level: bclp, max: 255); |
520 | drm_connector_list_iter_end(iter: &conn_iter); |
521 | asle->cblv = DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID; |
522 | |
523 | drm_modeset_unlock(lock: &dev_priv->drm.mode_config.connection_mutex); |
524 | |
525 | |
526 | return 0; |
527 | } |
528 | |
529 | static u32 asle_set_als_illum(struct drm_i915_private *dev_priv, u32 alsi) |
530 | { |
531 | /* alsi is the current ALS reading in lux. 0 indicates below sensor |
532 | range, 0xffff indicates above sensor range. 1-0xfffe are valid */ |
533 | drm_dbg(&dev_priv->drm, "Illum is not supported\n" ); |
534 | return ASLC_ALS_ILLUM_FAILED; |
535 | } |
536 | |
537 | static u32 asle_set_pwm_freq(struct drm_i915_private *dev_priv, u32 pfmb) |
538 | { |
539 | drm_dbg(&dev_priv->drm, "PWM freq is not supported\n" ); |
540 | return ASLC_PWM_FREQ_FAILED; |
541 | } |
542 | |
543 | static u32 asle_set_pfit(struct drm_i915_private *dev_priv, u32 pfit) |
544 | { |
545 | /* Panel fitting is currently controlled by the X code, so this is a |
546 | noop until modesetting support works fully */ |
547 | drm_dbg(&dev_priv->drm, "Pfit is not supported\n" ); |
548 | return ASLC_PFIT_FAILED; |
549 | } |
550 | |
551 | static u32 asle_set_supported_rotation_angles(struct drm_i915_private *dev_priv, u32 srot) |
552 | { |
553 | drm_dbg(&dev_priv->drm, "SROT is not supported\n" ); |
554 | return ASLC_ROTATION_ANGLES_FAILED; |
555 | } |
556 | |
557 | static u32 asle_set_button_array(struct drm_i915_private *dev_priv, u32 iuer) |
558 | { |
559 | if (!iuer) |
560 | drm_dbg(&dev_priv->drm, |
561 | "Button array event is not supported (nothing)\n" ); |
562 | if (iuer & ASLE_IUER_ROTATION_LOCK_BTN) |
563 | drm_dbg(&dev_priv->drm, |
564 | "Button array event is not supported (rotation lock)\n" ); |
565 | if (iuer & ASLE_IUER_VOLUME_DOWN_BTN) |
566 | drm_dbg(&dev_priv->drm, |
567 | "Button array event is not supported (volume down)\n" ); |
568 | if (iuer & ASLE_IUER_VOLUME_UP_BTN) |
569 | drm_dbg(&dev_priv->drm, |
570 | "Button array event is not supported (volume up)\n" ); |
571 | if (iuer & ASLE_IUER_WINDOWS_BTN) |
572 | drm_dbg(&dev_priv->drm, |
573 | "Button array event is not supported (windows)\n" ); |
574 | if (iuer & ASLE_IUER_POWER_BTN) |
575 | drm_dbg(&dev_priv->drm, |
576 | "Button array event is not supported (power)\n" ); |
577 | |
578 | return ASLC_BUTTON_ARRAY_FAILED; |
579 | } |
580 | |
581 | static u32 asle_set_convertible(struct drm_i915_private *dev_priv, u32 iuer) |
582 | { |
583 | if (iuer & ASLE_IUER_CONVERTIBLE) |
584 | drm_dbg(&dev_priv->drm, |
585 | "Convertible is not supported (clamshell)\n" ); |
586 | else |
587 | drm_dbg(&dev_priv->drm, |
588 | "Convertible is not supported (slate)\n" ); |
589 | |
590 | return ASLC_CONVERTIBLE_FAILED; |
591 | } |
592 | |
593 | static u32 asle_set_docking(struct drm_i915_private *dev_priv, u32 iuer) |
594 | { |
595 | if (iuer & ASLE_IUER_DOCKING) |
596 | drm_dbg(&dev_priv->drm, "Docking is not supported (docked)\n" ); |
597 | else |
598 | drm_dbg(&dev_priv->drm, |
599 | "Docking is not supported (undocked)\n" ); |
600 | |
601 | return ASLC_DOCKING_FAILED; |
602 | } |
603 | |
604 | static u32 asle_isct_state(struct drm_i915_private *dev_priv) |
605 | { |
606 | drm_dbg(&dev_priv->drm, "ISCT is not supported\n" ); |
607 | return ASLC_ISCT_STATE_FAILED; |
608 | } |
609 | |
610 | static void asle_work(struct work_struct *work) |
611 | { |
612 | struct intel_opregion *opregion = |
613 | container_of(work, struct intel_opregion, asle_work); |
614 | struct drm_i915_private *dev_priv = opregion->i915; |
615 | struct opregion_asle *asle = opregion->asle; |
616 | u32 aslc_stat = 0; |
617 | u32 aslc_req; |
618 | |
619 | if (!asle) |
620 | return; |
621 | |
622 | aslc_req = asle->aslc; |
623 | |
624 | if (!(aslc_req & ASLC_REQ_MSK)) { |
625 | drm_dbg(&dev_priv->drm, |
626 | "No request on ASLC interrupt 0x%08x\n" , aslc_req); |
627 | return; |
628 | } |
629 | |
630 | if (aslc_req & ASLC_SET_ALS_ILLUM) |
631 | aslc_stat |= asle_set_als_illum(dev_priv, alsi: asle->alsi); |
632 | |
633 | if (aslc_req & ASLC_SET_BACKLIGHT) |
634 | aslc_stat |= asle_set_backlight(dev_priv, bclp: asle->bclp); |
635 | |
636 | if (aslc_req & ASLC_SET_PFIT) |
637 | aslc_stat |= asle_set_pfit(dev_priv, pfit: asle->pfit); |
638 | |
639 | if (aslc_req & ASLC_SET_PWM_FREQ) |
640 | aslc_stat |= asle_set_pwm_freq(dev_priv, pfmb: asle->pfmb); |
641 | |
642 | if (aslc_req & ASLC_SUPPORTED_ROTATION_ANGLES) |
643 | aslc_stat |= asle_set_supported_rotation_angles(dev_priv, |
644 | srot: asle->srot); |
645 | |
646 | if (aslc_req & ASLC_BUTTON_ARRAY) |
647 | aslc_stat |= asle_set_button_array(dev_priv, iuer: asle->iuer); |
648 | |
649 | if (aslc_req & ASLC_CONVERTIBLE_INDICATOR) |
650 | aslc_stat |= asle_set_convertible(dev_priv, iuer: asle->iuer); |
651 | |
652 | if (aslc_req & ASLC_DOCKING_INDICATOR) |
653 | aslc_stat |= asle_set_docking(dev_priv, iuer: asle->iuer); |
654 | |
655 | if (aslc_req & ASLC_ISCT_STATE_CHANGE) |
656 | aslc_stat |= asle_isct_state(dev_priv); |
657 | |
658 | asle->aslc = aslc_stat; |
659 | } |
660 | |
661 | bool intel_opregion_asle_present(struct drm_i915_private *i915) |
662 | { |
663 | return i915->display.opregion && i915->display.opregion->asle; |
664 | } |
665 | |
666 | void intel_opregion_asle_intr(struct drm_i915_private *i915) |
667 | { |
668 | struct intel_opregion *opregion = i915->display.opregion; |
669 | |
670 | if (opregion && opregion->asle) |
671 | queue_work(wq: i915->unordered_wq, work: &opregion->asle_work); |
672 | } |
673 | |
674 | #define ACPI_EV_DISPLAY_SWITCH (1<<0) |
675 | #define ACPI_EV_LID (1<<1) |
676 | #define ACPI_EV_DOCK (1<<2) |
677 | |
678 | /* |
679 | * The only video events relevant to opregion are 0x80. These indicate either a |
680 | * docking event, lid switch or display switch request. In Linux, these are |
681 | * handled by the dock, button and video drivers. |
682 | */ |
683 | static int intel_opregion_video_event(struct notifier_block *nb, |
684 | unsigned long val, void *data) |
685 | { |
686 | struct intel_opregion *opregion = container_of(nb, struct intel_opregion, |
687 | acpi_notifier); |
688 | struct acpi_bus_event *event = data; |
689 | struct opregion_acpi *acpi; |
690 | int ret = NOTIFY_OK; |
691 | |
692 | if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0) |
693 | return NOTIFY_DONE; |
694 | |
695 | acpi = opregion->acpi; |
696 | |
697 | if (event->type == 0x80 && ((acpi->cevt & 1) == 0)) |
698 | ret = NOTIFY_BAD; |
699 | |
700 | acpi->csts = 0; |
701 | |
702 | return ret; |
703 | } |
704 | |
705 | /* |
706 | * Initialise the DIDL field in opregion. This passes a list of devices to |
707 | * the firmware. Values are defined by section B.4.2 of the ACPI specification |
708 | * (version 3) |
709 | */ |
710 | |
711 | static void set_did(struct intel_opregion *opregion, int i, u32 val) |
712 | { |
713 | if (i < ARRAY_SIZE(opregion->acpi->didl)) { |
714 | opregion->acpi->didl[i] = val; |
715 | } else { |
716 | i -= ARRAY_SIZE(opregion->acpi->didl); |
717 | |
718 | if (WARN_ON(i >= ARRAY_SIZE(opregion->acpi->did2))) |
719 | return; |
720 | |
721 | opregion->acpi->did2[i] = val; |
722 | } |
723 | } |
724 | |
725 | static void intel_didl_outputs(struct drm_i915_private *dev_priv) |
726 | { |
727 | struct intel_opregion *opregion = dev_priv->display.opregion; |
728 | struct intel_connector *connector; |
729 | struct drm_connector_list_iter conn_iter; |
730 | int i = 0, max_outputs; |
731 | |
732 | /* |
733 | * In theory, did2, the extended didl, gets added at opregion version |
734 | * 3.0. In practice, however, we're supposed to set it for earlier |
735 | * versions as well, since a BIOS that doesn't understand did2 should |
736 | * not look at it anyway. Use a variable so we can tweak this if a need |
737 | * arises later. |
738 | */ |
739 | max_outputs = ARRAY_SIZE(opregion->acpi->didl) + |
740 | ARRAY_SIZE(opregion->acpi->did2); |
741 | |
742 | intel_acpi_device_id_update(i915: dev_priv); |
743 | |
744 | drm_connector_list_iter_begin(dev: &dev_priv->drm, iter: &conn_iter); |
745 | for_each_intel_connector_iter(connector, &conn_iter) { |
746 | if (i < max_outputs) |
747 | set_did(opregion, i, val: connector->acpi_device_id); |
748 | i++; |
749 | } |
750 | drm_connector_list_iter_end(iter: &conn_iter); |
751 | |
752 | drm_dbg_kms(&dev_priv->drm, "%d outputs detected\n" , i); |
753 | |
754 | if (i > max_outputs) |
755 | drm_err(&dev_priv->drm, |
756 | "More than %d outputs in connector list\n" , |
757 | max_outputs); |
758 | |
759 | /* If fewer than max outputs, the list must be null terminated */ |
760 | if (i < max_outputs) |
761 | set_did(opregion, i, val: 0); |
762 | } |
763 | |
764 | static void intel_setup_cadls(struct drm_i915_private *dev_priv) |
765 | { |
766 | struct intel_opregion *opregion = dev_priv->display.opregion; |
767 | struct intel_connector *connector; |
768 | struct drm_connector_list_iter conn_iter; |
769 | int i = 0; |
770 | |
771 | /* |
772 | * Initialize the CADL field from the connector device ids. This is |
773 | * essentially the same as copying from the DIDL. Technically, this is |
774 | * not always correct as display outputs may exist, but not active. This |
775 | * initialization is necessary for some Clevo laptops that check this |
776 | * field before processing the brightness and display switching hotkeys. |
777 | * |
778 | * Note that internal panels should be at the front of the connector |
779 | * list already, ensuring they're not left out. |
780 | */ |
781 | drm_connector_list_iter_begin(dev: &dev_priv->drm, iter: &conn_iter); |
782 | for_each_intel_connector_iter(connector, &conn_iter) { |
783 | if (i >= ARRAY_SIZE(opregion->acpi->cadl)) |
784 | break; |
785 | opregion->acpi->cadl[i++] = connector->acpi_device_id; |
786 | } |
787 | drm_connector_list_iter_end(iter: &conn_iter); |
788 | |
789 | /* If fewer than 8 active devices, the list must be null terminated */ |
790 | if (i < ARRAY_SIZE(opregion->acpi->cadl)) |
791 | opregion->acpi->cadl[i] = 0; |
792 | } |
793 | |
794 | static void swsci_setup(struct drm_i915_private *dev_priv) |
795 | { |
796 | struct intel_opregion *opregion = dev_priv->display.opregion; |
797 | bool requested_callbacks = false; |
798 | u32 tmp; |
799 | |
800 | /* Sub-function code 0 is okay, let's allow them. */ |
801 | opregion->swsci_gbda_sub_functions = 1; |
802 | opregion->swsci_sbcb_sub_functions = 1; |
803 | |
804 | /* We use GBDA to ask for supported GBDA calls. */ |
805 | if (swsci(dev_priv, SWSCI_GBDA_SUPPORTED_CALLS, parm: 0, parm_out: &tmp) == 0) { |
806 | /* make the bits match the sub-function codes */ |
807 | tmp <<= 1; |
808 | opregion->swsci_gbda_sub_functions |= tmp; |
809 | } |
810 | |
811 | /* |
812 | * We also use GBDA to ask for _requested_ SBCB callbacks. The driver |
813 | * must not call interfaces that are not specifically requested by the |
814 | * bios. |
815 | */ |
816 | if (swsci(dev_priv, SWSCI_GBDA_REQUESTED_CALLBACKS, parm: 0, parm_out: &tmp) == 0) { |
817 | /* here, the bits already match sub-function codes */ |
818 | opregion->swsci_sbcb_sub_functions |= tmp; |
819 | requested_callbacks = true; |
820 | } |
821 | |
822 | /* |
823 | * But we use SBCB to ask for _supported_ SBCB calls. This does not mean |
824 | * the callback is _requested_. But we still can't call interfaces that |
825 | * are not requested. |
826 | */ |
827 | if (swsci(dev_priv, SWSCI_SBCB_SUPPORTED_CALLBACKS, parm: 0, parm_out: &tmp) == 0) { |
828 | /* make the bits match the sub-function codes */ |
829 | u32 low = tmp & 0x7ff; |
830 | u32 high = tmp & ~0xfff; /* bit 11 is reserved */ |
831 | tmp = (high << 4) | (low << 1) | 1; |
832 | |
833 | /* best guess what to do with supported wrt requested */ |
834 | if (requested_callbacks) { |
835 | u32 req = opregion->swsci_sbcb_sub_functions; |
836 | if ((req & tmp) != req) |
837 | drm_dbg(&dev_priv->drm, |
838 | "SWSCI BIOS requested (%08x) SBCB callbacks that are not supported (%08x)\n" , |
839 | req, tmp); |
840 | /* XXX: for now, trust the requested callbacks */ |
841 | /* opregion->swsci_sbcb_sub_functions &= tmp; */ |
842 | } else { |
843 | opregion->swsci_sbcb_sub_functions |= tmp; |
844 | } |
845 | } |
846 | |
847 | drm_dbg(&dev_priv->drm, |
848 | "SWSCI GBDA callbacks %08x, SBCB callbacks %08x\n" , |
849 | opregion->swsci_gbda_sub_functions, |
850 | opregion->swsci_sbcb_sub_functions); |
851 | } |
852 | |
853 | static int intel_no_opregion_vbt_callback(const struct dmi_system_id *id) |
854 | { |
855 | DRM_DEBUG_KMS("Falling back to manually reading VBT from " |
856 | "VBIOS ROM for %s\n" , id->ident); |
857 | return 1; |
858 | } |
859 | |
860 | static const struct dmi_system_id intel_no_opregion_vbt[] = { |
861 | { |
862 | .callback = intel_no_opregion_vbt_callback, |
863 | .ident = "ThinkCentre A57" , |
864 | .matches = { |
865 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO" ), |
866 | DMI_MATCH(DMI_PRODUCT_NAME, "97027RG" ), |
867 | }, |
868 | }, |
869 | { } |
870 | }; |
871 | |
872 | static int intel_load_vbt_firmware(struct drm_i915_private *dev_priv) |
873 | { |
874 | struct intel_opregion *opregion = dev_priv->display.opregion; |
875 | const struct firmware *fw = NULL; |
876 | const char *name = dev_priv->display.params.vbt_firmware; |
877 | int ret; |
878 | |
879 | if (!name || !*name) |
880 | return -ENOENT; |
881 | |
882 | ret = request_firmware(fw: &fw, name, device: dev_priv->drm.dev); |
883 | if (ret) { |
884 | drm_err(&dev_priv->drm, |
885 | "Requesting VBT firmware \"%s\" failed (%d)\n" , |
886 | name, ret); |
887 | return ret; |
888 | } |
889 | |
890 | if (intel_bios_is_valid_vbt(i915: dev_priv, buf: fw->data, size: fw->size)) { |
891 | opregion->vbt_firmware = kmemdup(p: fw->data, size: fw->size, GFP_KERNEL); |
892 | if (opregion->vbt_firmware) { |
893 | drm_dbg_kms(&dev_priv->drm, |
894 | "Found valid VBT firmware \"%s\"\n" , name); |
895 | opregion->vbt = opregion->vbt_firmware; |
896 | opregion->vbt_size = fw->size; |
897 | ret = 0; |
898 | } else { |
899 | ret = -ENOMEM; |
900 | } |
901 | } else { |
902 | drm_dbg_kms(&dev_priv->drm, "Invalid VBT firmware \"%s\"\n" , |
903 | name); |
904 | ret = -EINVAL; |
905 | } |
906 | |
907 | release_firmware(fw); |
908 | |
909 | return ret; |
910 | } |
911 | |
912 | int intel_opregion_setup(struct drm_i915_private *dev_priv) |
913 | { |
914 | struct intel_opregion *opregion; |
915 | struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); |
916 | u32 asls, mboxes; |
917 | char buf[sizeof(OPREGION_SIGNATURE)]; |
918 | int err = 0; |
919 | void *base; |
920 | const void *vbt; |
921 | u32 vbt_size; |
922 | |
923 | BUILD_BUG_ON(sizeof(struct opregion_header) != 0x100); |
924 | BUILD_BUG_ON(sizeof(struct opregion_acpi) != 0x100); |
925 | BUILD_BUG_ON(sizeof(struct opregion_swsci) != 0x100); |
926 | BUILD_BUG_ON(sizeof(struct opregion_asle) != 0x100); |
927 | BUILD_BUG_ON(sizeof(struct opregion_asle_ext) != 0x400); |
928 | |
929 | pci_read_config_dword(dev: pdev, ASLS, val: &asls); |
930 | drm_dbg(&dev_priv->drm, "graphic opregion physical addr: 0x%x\n" , |
931 | asls); |
932 | if (asls == 0) { |
933 | drm_dbg(&dev_priv->drm, "ACPI OpRegion not supported!\n" ); |
934 | return -ENOTSUPP; |
935 | } |
936 | |
937 | opregion = kzalloc(size: sizeof(*opregion), GFP_KERNEL); |
938 | if (!opregion) |
939 | return -ENOMEM; |
940 | |
941 | opregion->i915 = dev_priv; |
942 | dev_priv->display.opregion = opregion; |
943 | |
944 | INIT_WORK(&opregion->asle_work, asle_work); |
945 | |
946 | base = memremap(offset: asls, OPREGION_SIZE, flags: MEMREMAP_WB); |
947 | if (!base) { |
948 | err = -ENOMEM; |
949 | goto err_memremap; |
950 | } |
951 | |
952 | memcpy(buf, base, sizeof(buf)); |
953 | |
954 | if (memcmp(p: buf, OPREGION_SIGNATURE, size: 16)) { |
955 | drm_dbg(&dev_priv->drm, "opregion signature mismatch\n" ); |
956 | err = -EINVAL; |
957 | goto err_out; |
958 | } |
959 | opregion->header = base; |
960 | |
961 | drm_dbg(&dev_priv->drm, "ACPI OpRegion version %u.%u.%u\n" , |
962 | opregion->header->over.major, |
963 | opregion->header->over.minor, |
964 | opregion->header->over.revision); |
965 | |
966 | mboxes = opregion->header->mboxes; |
967 | if (mboxes & MBOX_ACPI) { |
968 | drm_dbg(&dev_priv->drm, "Public ACPI methods supported\n" ); |
969 | opregion->acpi = base + OPREGION_ACPI_OFFSET; |
970 | /* |
971 | * Indicate we handle monitor hotplug events ourselves so we do |
972 | * not need ACPI notifications for them. Disabling these avoids |
973 | * triggering the AML code doing the notifation, which may be |
974 | * broken as Windows also seems to disable these. |
975 | */ |
976 | opregion->acpi->chpd = 1; |
977 | } |
978 | |
979 | if (mboxes & MBOX_SWSCI) { |
980 | u8 major = opregion->header->over.major; |
981 | |
982 | if (major >= 3) { |
983 | drm_err(&dev_priv->drm, "SWSCI Mailbox #2 present for opregion v3.x, ignoring\n" ); |
984 | } else { |
985 | if (major >= 2) |
986 | drm_dbg(&dev_priv->drm, "SWSCI Mailbox #2 present for opregion v2.x\n" ); |
987 | drm_dbg(&dev_priv->drm, "SWSCI supported\n" ); |
988 | opregion->swsci = base + OPREGION_SWSCI_OFFSET; |
989 | swsci_setup(dev_priv); |
990 | } |
991 | } |
992 | |
993 | if (mboxes & MBOX_ASLE) { |
994 | drm_dbg(&dev_priv->drm, "ASLE supported\n" ); |
995 | opregion->asle = base + OPREGION_ASLE_OFFSET; |
996 | |
997 | opregion->asle->ardy = ASLE_ARDY_NOT_READY; |
998 | } |
999 | |
1000 | if (mboxes & MBOX_ASLE_EXT) { |
1001 | drm_dbg(&dev_priv->drm, "ASLE extension supported\n" ); |
1002 | opregion->asle_ext = base + OPREGION_ASLE_EXT_OFFSET; |
1003 | } |
1004 | |
1005 | if (mboxes & MBOX_BACKLIGHT) { |
1006 | drm_dbg(&dev_priv->drm, "Mailbox #2 for backlight present\n" ); |
1007 | } |
1008 | |
1009 | if (intel_load_vbt_firmware(dev_priv) == 0) |
1010 | goto out; |
1011 | |
1012 | if (dmi_check_system(list: intel_no_opregion_vbt)) |
1013 | goto out; |
1014 | |
1015 | if (opregion->header->over.major >= 2 && opregion->asle && |
1016 | opregion->asle->rvda && opregion->asle->rvds) { |
1017 | resource_size_t rvda = opregion->asle->rvda; |
1018 | |
1019 | /* |
1020 | * opregion 2.0: rvda is the physical VBT address. |
1021 | * |
1022 | * opregion 2.1+: rvda is unsigned, relative offset from |
1023 | * opregion base, and should never point within opregion. |
1024 | */ |
1025 | if (opregion->header->over.major > 2 || |
1026 | opregion->header->over.minor >= 1) { |
1027 | drm_WARN_ON(&dev_priv->drm, rvda < OPREGION_SIZE); |
1028 | |
1029 | rvda += asls; |
1030 | } |
1031 | |
1032 | opregion->rvda = memremap(offset: rvda, size: opregion->asle->rvds, |
1033 | flags: MEMREMAP_WB); |
1034 | |
1035 | vbt = opregion->rvda; |
1036 | vbt_size = opregion->asle->rvds; |
1037 | if (intel_bios_is_valid_vbt(i915: dev_priv, buf: vbt, size: vbt_size)) { |
1038 | drm_dbg_kms(&dev_priv->drm, |
1039 | "Found valid VBT in ACPI OpRegion (RVDA)\n" ); |
1040 | opregion->vbt = vbt; |
1041 | opregion->vbt_size = vbt_size; |
1042 | goto out; |
1043 | } else { |
1044 | drm_dbg_kms(&dev_priv->drm, |
1045 | "Invalid VBT in ACPI OpRegion (RVDA)\n" ); |
1046 | memunmap(addr: opregion->rvda); |
1047 | opregion->rvda = NULL; |
1048 | } |
1049 | } |
1050 | |
1051 | vbt = base + OPREGION_VBT_OFFSET; |
1052 | /* |
1053 | * The VBT specification says that if the ASLE ext mailbox is not used |
1054 | * its area is reserved, but on some CHT boards the VBT extends into the |
1055 | * ASLE ext area. Allow this even though it is against the spec, so we |
1056 | * do not end up rejecting the VBT on those boards (and end up not |
1057 | * finding the LCD panel because of this). |
1058 | */ |
1059 | vbt_size = (mboxes & MBOX_ASLE_EXT) ? |
1060 | OPREGION_ASLE_EXT_OFFSET : OPREGION_SIZE; |
1061 | vbt_size -= OPREGION_VBT_OFFSET; |
1062 | if (intel_bios_is_valid_vbt(i915: dev_priv, buf: vbt, size: vbt_size)) { |
1063 | drm_dbg_kms(&dev_priv->drm, |
1064 | "Found valid VBT in ACPI OpRegion (Mailbox #4)\n" ); |
1065 | opregion->vbt = vbt; |
1066 | opregion->vbt_size = vbt_size; |
1067 | } else { |
1068 | drm_dbg_kms(&dev_priv->drm, |
1069 | "Invalid VBT in ACPI OpRegion (Mailbox #4)\n" ); |
1070 | } |
1071 | |
1072 | out: |
1073 | return 0; |
1074 | |
1075 | err_out: |
1076 | memunmap(addr: base); |
1077 | err_memremap: |
1078 | kfree(objp: opregion); |
1079 | dev_priv->display.opregion = NULL; |
1080 | |
1081 | return err; |
1082 | } |
1083 | |
1084 | static int intel_use_opregion_panel_type_callback(const struct dmi_system_id *id) |
1085 | { |
1086 | DRM_INFO("Using panel type from OpRegion on %s\n" , id->ident); |
1087 | return 1; |
1088 | } |
1089 | |
1090 | static const struct dmi_system_id intel_use_opregion_panel_type[] = { |
1091 | { |
1092 | .callback = intel_use_opregion_panel_type_callback, |
1093 | .ident = "Conrac GmbH IX45GM2" , |
1094 | .matches = {DMI_MATCH(DMI_SYS_VENDOR, "Conrac GmbH" ), |
1095 | DMI_MATCH(DMI_PRODUCT_NAME, "IX45GM2" ), |
1096 | }, |
1097 | }, |
1098 | { } |
1099 | }; |
1100 | |
1101 | int |
1102 | intel_opregion_get_panel_type(struct drm_i915_private *dev_priv) |
1103 | { |
1104 | u32 panel_details; |
1105 | int ret; |
1106 | |
1107 | ret = swsci(dev_priv, SWSCI_GBDA_PANEL_DETAILS, parm: 0x0, parm_out: &panel_details); |
1108 | if (ret) |
1109 | return ret; |
1110 | |
1111 | ret = (panel_details >> 8) & 0xff; |
1112 | if (ret > 0x10) { |
1113 | drm_dbg_kms(&dev_priv->drm, |
1114 | "Invalid OpRegion panel type 0x%x\n" , ret); |
1115 | return -EINVAL; |
1116 | } |
1117 | |
1118 | /* fall back to VBT panel type? */ |
1119 | if (ret == 0x0) { |
1120 | drm_dbg_kms(&dev_priv->drm, "No panel type in OpRegion\n" ); |
1121 | return -ENODEV; |
1122 | } |
1123 | |
1124 | /* |
1125 | * So far we know that some machined must use it, others must not use it. |
1126 | * There doesn't seem to be any way to determine which way to go, except |
1127 | * via a quirk list :( |
1128 | */ |
1129 | if (!dmi_check_system(list: intel_use_opregion_panel_type)) { |
1130 | drm_dbg_kms(&dev_priv->drm, |
1131 | "Ignoring OpRegion panel type (%d)\n" , ret - 1); |
1132 | return -ENODEV; |
1133 | } |
1134 | |
1135 | return ret - 1; |
1136 | } |
1137 | |
1138 | /** |
1139 | * intel_opregion_get_edid - Fetch EDID from ACPI OpRegion mailbox #5 |
1140 | * @intel_connector: eDP connector |
1141 | * |
1142 | * This reads the ACPI Opregion mailbox #5 to extract the EDID that is passed |
1143 | * to it. |
1144 | * |
1145 | * Returns: |
1146 | * The EDID in the OpRegion, or NULL if there is none or it's invalid. |
1147 | * |
1148 | */ |
1149 | const struct drm_edid *intel_opregion_get_edid(struct intel_connector *intel_connector) |
1150 | { |
1151 | struct drm_connector *connector = &intel_connector->base; |
1152 | struct drm_i915_private *i915 = to_i915(dev: connector->dev); |
1153 | struct intel_opregion *opregion = i915->display.opregion; |
1154 | const struct drm_edid *drm_edid; |
1155 | const void *edid; |
1156 | int len; |
1157 | |
1158 | if (!opregion || !opregion->asle_ext) |
1159 | return NULL; |
1160 | |
1161 | edid = opregion->asle_ext->bddc; |
1162 | |
1163 | /* Validity corresponds to number of 128-byte blocks */ |
1164 | len = (opregion->asle_ext->phed & ASLE_PHED_EDID_VALID_MASK) * 128; |
1165 | if (!len || !memchr_inv(p: edid, c: 0, size: len)) |
1166 | return NULL; |
1167 | |
1168 | drm_edid = drm_edid_alloc(edid, size: len); |
1169 | |
1170 | if (!drm_edid_valid(drm_edid)) { |
1171 | drm_dbg_kms(&i915->drm, "Invalid EDID in ACPI OpRegion (Mailbox #5)\n" ); |
1172 | drm_edid_free(drm_edid); |
1173 | drm_edid = NULL; |
1174 | } |
1175 | |
1176 | return drm_edid; |
1177 | } |
1178 | |
1179 | const void *intel_opregion_get_vbt(struct drm_i915_private *i915, size_t *size) |
1180 | { |
1181 | struct intel_opregion *opregion = i915->display.opregion; |
1182 | |
1183 | if (!opregion || !opregion->vbt) |
1184 | return NULL; |
1185 | |
1186 | if (size) |
1187 | *size = opregion->vbt_size; |
1188 | |
1189 | return opregion->vbt; |
1190 | } |
1191 | |
1192 | bool intel_opregion_headless_sku(struct drm_i915_private *i915) |
1193 | { |
1194 | struct intel_opregion *opregion = i915->display.opregion; |
1195 | struct opregion_header *; |
1196 | |
1197 | if (!opregion) |
1198 | return false; |
1199 | |
1200 | header = opregion->header; |
1201 | |
1202 | if (!header || header->over.major < 2 || |
1203 | (header->over.major == 2 && header->over.minor < 3)) |
1204 | return false; |
1205 | |
1206 | return opregion->header->pcon & PCON_HEADLESS_SKU; |
1207 | } |
1208 | |
1209 | void intel_opregion_register(struct drm_i915_private *i915) |
1210 | { |
1211 | struct intel_opregion *opregion = i915->display.opregion; |
1212 | |
1213 | if (!opregion) |
1214 | return; |
1215 | |
1216 | if (opregion->acpi) { |
1217 | opregion->acpi_notifier.notifier_call = |
1218 | intel_opregion_video_event; |
1219 | register_acpi_notifier(&opregion->acpi_notifier); |
1220 | } |
1221 | |
1222 | intel_opregion_resume(dev_priv: i915); |
1223 | } |
1224 | |
1225 | static void intel_opregion_resume_display(struct drm_i915_private *i915) |
1226 | { |
1227 | struct intel_opregion *opregion = i915->display.opregion; |
1228 | |
1229 | if (opregion->acpi) { |
1230 | intel_didl_outputs(dev_priv: i915); |
1231 | intel_setup_cadls(dev_priv: i915); |
1232 | |
1233 | /* |
1234 | * Notify BIOS we are ready to handle ACPI video ext notifs. |
1235 | * Right now, all the events are handled by the ACPI video |
1236 | * module. We don't actually need to do anything with them. |
1237 | */ |
1238 | opregion->acpi->csts = 0; |
1239 | opregion->acpi->drdy = 1; |
1240 | } |
1241 | |
1242 | if (opregion->asle) { |
1243 | opregion->asle->tche = ASLE_TCHE_BLC_EN; |
1244 | opregion->asle->ardy = ASLE_ARDY_READY; |
1245 | } |
1246 | |
1247 | /* Some platforms abuse the _DSM to enable MUX */ |
1248 | intel_dsm_get_bios_data_funcs_supported(i915); |
1249 | } |
1250 | |
1251 | void intel_opregion_resume(struct drm_i915_private *i915) |
1252 | { |
1253 | struct intel_opregion *opregion = i915->display.opregion; |
1254 | |
1255 | if (!opregion) |
1256 | return; |
1257 | |
1258 | if (HAS_DISPLAY(i915)) |
1259 | intel_opregion_resume_display(i915); |
1260 | |
1261 | intel_opregion_notify_adapter(dev_priv: i915, PCI_D0); |
1262 | } |
1263 | |
1264 | static void intel_opregion_suspend_display(struct drm_i915_private *i915) |
1265 | { |
1266 | struct intel_opregion *opregion = i915->display.opregion; |
1267 | |
1268 | if (opregion->asle) |
1269 | opregion->asle->ardy = ASLE_ARDY_NOT_READY; |
1270 | |
1271 | cancel_work_sync(work: &opregion->asle_work); |
1272 | |
1273 | if (opregion->acpi) |
1274 | opregion->acpi->drdy = 0; |
1275 | } |
1276 | |
1277 | void intel_opregion_suspend(struct drm_i915_private *i915, pci_power_t state) |
1278 | { |
1279 | struct intel_opregion *opregion = i915->display.opregion; |
1280 | |
1281 | if (!opregion) |
1282 | return; |
1283 | |
1284 | intel_opregion_notify_adapter(dev_priv: i915, state); |
1285 | |
1286 | if (HAS_DISPLAY(i915)) |
1287 | intel_opregion_suspend_display(i915); |
1288 | } |
1289 | |
1290 | void intel_opregion_unregister(struct drm_i915_private *i915) |
1291 | { |
1292 | struct intel_opregion *opregion = i915->display.opregion; |
1293 | |
1294 | intel_opregion_suspend(i915, PCI_D1); |
1295 | |
1296 | if (!opregion) |
1297 | return; |
1298 | |
1299 | if (opregion->acpi_notifier.notifier_call) { |
1300 | unregister_acpi_notifier(&opregion->acpi_notifier); |
1301 | opregion->acpi_notifier.notifier_call = NULL; |
1302 | } |
1303 | } |
1304 | |
1305 | void intel_opregion_cleanup(struct drm_i915_private *i915) |
1306 | { |
1307 | struct intel_opregion *opregion = i915->display.opregion; |
1308 | |
1309 | if (!opregion) |
1310 | return; |
1311 | |
1312 | memunmap(addr: opregion->header); |
1313 | if (opregion->rvda) |
1314 | memunmap(addr: opregion->rvda); |
1315 | kfree(objp: opregion->vbt_firmware); |
1316 | kfree(objp: opregion); |
1317 | i915->display.opregion = NULL; |
1318 | } |
1319 | |
1320 | static int intel_opregion_show(struct seq_file *m, void *unused) |
1321 | { |
1322 | struct drm_i915_private *i915 = m->private; |
1323 | struct intel_opregion *opregion = i915->display.opregion; |
1324 | |
1325 | if (opregion) |
1326 | seq_write(seq: m, data: opregion->header, OPREGION_SIZE); |
1327 | |
1328 | return 0; |
1329 | } |
1330 | |
1331 | DEFINE_SHOW_ATTRIBUTE(intel_opregion); |
1332 | |
1333 | void intel_opregion_debugfs_register(struct drm_i915_private *i915) |
1334 | { |
1335 | struct drm_minor *minor = i915->drm.primary; |
1336 | |
1337 | debugfs_create_file(name: "i915_opregion" , mode: 0444, parent: minor->debugfs_root, |
1338 | data: i915, fops: &intel_opregion_fops); |
1339 | } |
1340 | |