File: | dev/pci/drm/i915/display/intel_opregion.c |
Warning: | line 828, column 30 Access to field 'data' results in a dereference of a null pointer (loaded from variable 'fw') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 "display/intel_panel.h" | |||
34 | ||||
35 | #include "i915_drv.h" | |||
36 | #include "intel_acpi.h" | |||
37 | #include "intel_display_types.h" | |||
38 | #include "intel_opregion.h" | |||
39 | ||||
40 | #define OPREGION_HEADER_OFFSET0 0 | |||
41 | #define OPREGION_ACPI_OFFSET0x100 0x100 | |||
42 | #define ACPI_CLID0x01ac 0x01ac /* current lid state indicator */ | |||
43 | #define ACPI_CDCK0x01b0 0x01b0 /* current docking state indicator */ | |||
44 | #define OPREGION_SWSCI_OFFSET0x200 0x200 | |||
45 | #define OPREGION_ASLE_OFFSET0x300 0x300 | |||
46 | #define OPREGION_VBT_OFFSET0x400 0x400 | |||
47 | #define OPREGION_ASLE_EXT_OFFSET0x1C00 0x1C00 | |||
48 | ||||
49 | #define OPREGION_SIGNATURE"IntelGraphicsMem" "IntelGraphicsMem" | |||
50 | #define MBOX_ACPI(1<<0) (1<<0) | |||
51 | #define MBOX_SWSCI(1<<1) (1<<1) | |||
52 | #define MBOX_ASLE(1<<2) (1<<2) | |||
53 | #define MBOX_ASLE_EXT(1<<4) (1<<4) | |||
54 | ||||
55 | struct opregion_header { | |||
56 | u8 signature[16]; | |||
57 | u32 size; | |||
58 | struct { | |||
59 | u8 rsvd; | |||
60 | u8 revision; | |||
61 | u8 minor; | |||
62 | u8 major; | |||
63 | } __packed__attribute__((__packed__)) over; | |||
64 | u8 bios_ver[32]; | |||
65 | u8 vbios_ver[16]; | |||
66 | u8 driver_ver[16]; | |||
67 | u32 mboxes; | |||
68 | u32 driver_model; | |||
69 | u32 pcon; | |||
70 | u8 dver[32]; | |||
71 | u8 rsvd[124]; | |||
72 | } __packed__attribute__((__packed__)); | |||
73 | ||||
74 | /* OpRegion mailbox #1: public ACPI methods */ | |||
75 | struct opregion_acpi { | |||
76 | u32 drdy; /* driver readiness */ | |||
77 | u32 csts; /* notification status */ | |||
78 | u32 cevt; /* current event */ | |||
79 | u8 rsvd1[20]; | |||
80 | u32 didl[8]; /* supported display devices ID list */ | |||
81 | u32 cpdl[8]; /* currently presented display list */ | |||
82 | u32 cadl[8]; /* currently active display list */ | |||
83 | u32 nadl[8]; /* next active devices list */ | |||
84 | u32 aslp; /* ASL sleep time-out */ | |||
85 | u32 tidx; /* toggle table index */ | |||
86 | u32 chpd; /* current hotplug enable indicator */ | |||
87 | u32 clid; /* current lid state*/ | |||
88 | u32 cdck; /* current docking state */ | |||
89 | u32 sxsw; /* Sx state resume */ | |||
90 | u32 evts; /* ASL supported events */ | |||
91 | u32 cnot; /* current OS notification */ | |||
92 | u32 nrdy; /* driver status */ | |||
93 | u32 did2[7]; /* extended supported display devices ID list */ | |||
94 | u32 cpd2[7]; /* extended attached display devices list */ | |||
95 | u8 rsvd2[4]; | |||
96 | } __packed__attribute__((__packed__)); | |||
97 | ||||
98 | /* OpRegion mailbox #2: SWSCI */ | |||
99 | struct opregion_swsci { | |||
100 | u32 scic; /* SWSCI command|status|data */ | |||
101 | u32 parm; /* command parameters */ | |||
102 | u32 dslp; /* driver sleep time-out */ | |||
103 | u8 rsvd[244]; | |||
104 | } __packed__attribute__((__packed__)); | |||
105 | ||||
106 | /* OpRegion mailbox #3: ASLE */ | |||
107 | struct opregion_asle { | |||
108 | u32 ardy; /* driver readiness */ | |||
109 | u32 aslc; /* ASLE interrupt command */ | |||
110 | u32 tche; /* technology enabled indicator */ | |||
111 | u32 alsi; /* current ALS illuminance reading */ | |||
112 | u32 bclp; /* backlight brightness to set */ | |||
113 | u32 pfit; /* panel fitting state */ | |||
114 | u32 cblv; /* current brightness level */ | |||
115 | u16 bclm[20]; /* backlight level duty cycle mapping table */ | |||
116 | u32 cpfm; /* current panel fitting mode */ | |||
117 | u32 epfm; /* enabled panel fitting modes */ | |||
118 | u8 plut[74]; /* panel LUT and identifier */ | |||
119 | u32 pfmb; /* PWM freq and min brightness */ | |||
120 | u32 cddv; /* color correction default values */ | |||
121 | u32 pcft; /* power conservation features */ | |||
122 | u32 srot; /* supported rotation angles */ | |||
123 | u32 iuer; /* IUER events */ | |||
124 | u64 fdss; | |||
125 | u32 fdsp; | |||
126 | u32 stat; | |||
127 | u64 rvda; /* Physical (2.0) or relative from opregion (2.1+) | |||
128 | * address of raw VBT data. */ | |||
129 | u32 rvds; /* Size of raw vbt data */ | |||
130 | u8 rsvd[58]; | |||
131 | } __packed__attribute__((__packed__)); | |||
132 | ||||
133 | /* OpRegion mailbox #5: ASLE ext */ | |||
134 | struct opregion_asle_ext { | |||
135 | u32 phed; /* Panel Header */ | |||
136 | u8 bddc[256]; /* Panel EDID */ | |||
137 | u8 rsvd[764]; | |||
138 | } __packed__attribute__((__packed__)); | |||
139 | ||||
140 | /* Driver readiness indicator */ | |||
141 | #define ASLE_ARDY_READY(1 << 0) (1 << 0) | |||
142 | #define ASLE_ARDY_NOT_READY(0 << 0) (0 << 0) | |||
143 | ||||
144 | /* ASLE Interrupt Command (ASLC) bits */ | |||
145 | #define ASLC_SET_ALS_ILLUM(1 << 0) (1 << 0) | |||
146 | #define ASLC_SET_BACKLIGHT(1 << 1) (1 << 1) | |||
147 | #define ASLC_SET_PFIT(1 << 2) (1 << 2) | |||
148 | #define ASLC_SET_PWM_FREQ(1 << 3) (1 << 3) | |||
149 | #define ASLC_SUPPORTED_ROTATION_ANGLES(1 << 4) (1 << 4) | |||
150 | #define ASLC_BUTTON_ARRAY(1 << 5) (1 << 5) | |||
151 | #define ASLC_CONVERTIBLE_INDICATOR(1 << 6) (1 << 6) | |||
152 | #define ASLC_DOCKING_INDICATOR(1 << 7) (1 << 7) | |||
153 | #define ASLC_ISCT_STATE_CHANGE(1 << 8) (1 << 8) | |||
154 | #define ASLC_REQ_MSK0x1ff 0x1ff | |||
155 | /* response bits */ | |||
156 | #define ASLC_ALS_ILLUM_FAILED(1 << 10) (1 << 10) | |||
157 | #define ASLC_BACKLIGHT_FAILED(1 << 12) (1 << 12) | |||
158 | #define ASLC_PFIT_FAILED(1 << 14) (1 << 14) | |||
159 | #define ASLC_PWM_FREQ_FAILED(1 << 16) (1 << 16) | |||
160 | #define ASLC_ROTATION_ANGLES_FAILED(1 << 18) (1 << 18) | |||
161 | #define ASLC_BUTTON_ARRAY_FAILED(1 << 20) (1 << 20) | |||
162 | #define ASLC_CONVERTIBLE_FAILED(1 << 22) (1 << 22) | |||
163 | #define ASLC_DOCKING_FAILED(1 << 24) (1 << 24) | |||
164 | #define ASLC_ISCT_STATE_FAILED(1 << 26) (1 << 26) | |||
165 | ||||
166 | /* Technology enabled indicator */ | |||
167 | #define ASLE_TCHE_ALS_EN(1 << 0) (1 << 0) | |||
168 | #define ASLE_TCHE_BLC_EN(1 << 1) (1 << 1) | |||
169 | #define ASLE_TCHE_PFIT_EN(1 << 2) (1 << 2) | |||
170 | #define ASLE_TCHE_PFMB_EN(1 << 3) (1 << 3) | |||
171 | ||||
172 | /* ASLE backlight brightness to set */ | |||
173 | #define ASLE_BCLP_VALID(1<<31) (1<<31) | |||
174 | #define ASLE_BCLP_MSK(~(1<<31)) (~(1<<31)) | |||
175 | ||||
176 | /* ASLE panel fitting request */ | |||
177 | #define ASLE_PFIT_VALID(1<<31) (1<<31) | |||
178 | #define ASLE_PFIT_CENTER(1<<0) (1<<0) | |||
179 | #define ASLE_PFIT_STRETCH_TEXT(1<<1) (1<<1) | |||
180 | #define ASLE_PFIT_STRETCH_GFX(1<<2) (1<<2) | |||
181 | ||||
182 | /* PWM frequency and minimum brightness */ | |||
183 | #define ASLE_PFMB_BRIGHTNESS_MASK(0xff) (0xff) | |||
184 | #define ASLE_PFMB_BRIGHTNESS_VALID(1<<8) (1<<8) | |||
185 | #define ASLE_PFMB_PWM_MASK(0x7ffffe00) (0x7ffffe00) | |||
186 | #define ASLE_PFMB_PWM_VALID(1<<31) (1<<31) | |||
187 | ||||
188 | #define ASLE_CBLV_VALID(1<<31) (1<<31) | |||
189 | ||||
190 | /* IUER */ | |||
191 | #define ASLE_IUER_DOCKING(1 << 7) (1 << 7) | |||
192 | #define ASLE_IUER_CONVERTIBLE(1 << 6) (1 << 6) | |||
193 | #define ASLE_IUER_ROTATION_LOCK_BTN(1 << 4) (1 << 4) | |||
194 | #define ASLE_IUER_VOLUME_DOWN_BTN(1 << 3) (1 << 3) | |||
195 | #define ASLE_IUER_VOLUME_UP_BTN(1 << 2) (1 << 2) | |||
196 | #define ASLE_IUER_WINDOWS_BTN(1 << 1) (1 << 1) | |||
197 | #define ASLE_IUER_POWER_BTN(1 << 0) (1 << 0) | |||
198 | ||||
199 | /* Software System Control Interrupt (SWSCI) */ | |||
200 | #define SWSCI_SCIC_INDICATOR(1 << 0) (1 << 0) | |||
201 | #define SWSCI_SCIC_MAIN_FUNCTION_SHIFT1 1 | |||
202 | #define SWSCI_SCIC_MAIN_FUNCTION_MASK(0xf << 1) (0xf << 1) | |||
203 | #define SWSCI_SCIC_SUB_FUNCTION_SHIFT8 8 | |||
204 | #define SWSCI_SCIC_SUB_FUNCTION_MASK(0xff << 8) (0xff << 8) | |||
205 | #define SWSCI_SCIC_EXIT_PARAMETER_SHIFT8 8 | |||
206 | #define SWSCI_SCIC_EXIT_PARAMETER_MASK(0xff << 8) (0xff << 8) | |||
207 | #define SWSCI_SCIC_EXIT_STATUS_SHIFT5 5 | |||
208 | #define SWSCI_SCIC_EXIT_STATUS_MASK(7 << 5) (7 << 5) | |||
209 | #define SWSCI_SCIC_EXIT_STATUS_SUCCESS1 1 | |||
210 | ||||
211 | #define SWSCI_FUNCTION_CODE(main, sub)((main) << 1 | (sub) << 8) \ | |||
212 | ((main) << SWSCI_SCIC_MAIN_FUNCTION_SHIFT1 | \ | |||
213 | (sub) << SWSCI_SCIC_SUB_FUNCTION_SHIFT8) | |||
214 | ||||
215 | /* SWSCI: Get BIOS Data (GBDA) */ | |||
216 | #define SWSCI_GBDA4 4 | |||
217 | #define SWSCI_GBDA_SUPPORTED_CALLS((4) << 1 | (0) << 8) SWSCI_FUNCTION_CODE(SWSCI_GBDA, 0)((4) << 1 | (0) << 8) | |||
218 | #define SWSCI_GBDA_REQUESTED_CALLBACKS((4) << 1 | (1) << 8) SWSCI_FUNCTION_CODE(SWSCI_GBDA, 1)((4) << 1 | (1) << 8) | |||
219 | #define SWSCI_GBDA_BOOT_DISPLAY_PREF((4) << 1 | (4) << 8) SWSCI_FUNCTION_CODE(SWSCI_GBDA, 4)((4) << 1 | (4) << 8) | |||
220 | #define SWSCI_GBDA_PANEL_DETAILS((4) << 1 | (5) << 8) SWSCI_FUNCTION_CODE(SWSCI_GBDA, 5)((4) << 1 | (5) << 8) | |||
221 | #define SWSCI_GBDA_TV_STANDARD((4) << 1 | (6) << 8) SWSCI_FUNCTION_CODE(SWSCI_GBDA, 6)((4) << 1 | (6) << 8) | |||
222 | #define SWSCI_GBDA_INTERNAL_GRAPHICS((4) << 1 | (7) << 8) SWSCI_FUNCTION_CODE(SWSCI_GBDA, 7)((4) << 1 | (7) << 8) | |||
223 | #define SWSCI_GBDA_SPREAD_SPECTRUM((4) << 1 | (10) << 8) SWSCI_FUNCTION_CODE(SWSCI_GBDA, 10)((4) << 1 | (10) << 8) | |||
224 | ||||
225 | /* SWSCI: System BIOS Callbacks (SBCB) */ | |||
226 | #define SWSCI_SBCB6 6 | |||
227 | #define SWSCI_SBCB_SUPPORTED_CALLBACKS((6) << 1 | (0) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 0)((6) << 1 | (0) << 8) | |||
228 | #define SWSCI_SBCB_INIT_COMPLETION((6) << 1 | (1) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 1)((6) << 1 | (1) << 8) | |||
229 | #define SWSCI_SBCB_PRE_HIRES_SET_MODE((6) << 1 | (3) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 3)((6) << 1 | (3) << 8) | |||
230 | #define SWSCI_SBCB_POST_HIRES_SET_MODE((6) << 1 | (4) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 4)((6) << 1 | (4) << 8) | |||
231 | #define SWSCI_SBCB_DISPLAY_SWITCH((6) << 1 | (5) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 5)((6) << 1 | (5) << 8) | |||
232 | #define SWSCI_SBCB_SET_TV_FORMAT((6) << 1 | (6) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 6)((6) << 1 | (6) << 8) | |||
233 | #define SWSCI_SBCB_ADAPTER_POWER_STATE((6) << 1 | (7) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 7)((6) << 1 | (7) << 8) | |||
234 | #define SWSCI_SBCB_DISPLAY_POWER_STATE((6) << 1 | (8) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 8)((6) << 1 | (8) << 8) | |||
235 | #define SWSCI_SBCB_SET_BOOT_DISPLAY((6) << 1 | (9) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 9)((6) << 1 | (9) << 8) | |||
236 | #define SWSCI_SBCB_SET_PANEL_DETAILS((6) << 1 | (10) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 10)((6) << 1 | (10) << 8) | |||
237 | #define SWSCI_SBCB_SET_INTERNAL_GFX((6) << 1 | (11) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 11)((6) << 1 | (11) << 8) | |||
238 | #define SWSCI_SBCB_POST_HIRES_TO_DOS_FS((6) << 1 | (16) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 16)((6) << 1 | (16) << 8) | |||
239 | #define SWSCI_SBCB_SUSPEND_RESUME((6) << 1 | (17) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 17)((6) << 1 | (17) << 8) | |||
240 | #define SWSCI_SBCB_SET_SPREAD_SPECTRUM((6) << 1 | (18) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 18)((6) << 1 | (18) << 8) | |||
241 | #define SWSCI_SBCB_POST_VBE_PM((6) << 1 | (19) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 19)((6) << 1 | (19) << 8) | |||
242 | #define SWSCI_SBCB_ENABLE_DISABLE_AUDIO((6) << 1 | (21) << 8) SWSCI_FUNCTION_CODE(SWSCI_SBCB, 21)((6) << 1 | (21) << 8) | |||
243 | ||||
244 | #define MAX_DSLP1500 1500 | |||
245 | ||||
246 | static int swsci(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
247 | u32 function, u32 parm, u32 *parm_out) | |||
248 | { | |||
249 | struct opregion_swsci *swsci = dev_priv->opregion.swsci; | |||
250 | struct pci_dev *pdev = dev_priv->drm.pdev; | |||
251 | u32 main_function, sub_function, scic; | |||
252 | u16 swsci_val; | |||
253 | u32 dslp; | |||
254 | ||||
255 | if (!swsci) | |||
256 | return -ENODEV19; | |||
257 | ||||
258 | main_function = (function & SWSCI_SCIC_MAIN_FUNCTION_MASK(0xf << 1)) >> | |||
259 | SWSCI_SCIC_MAIN_FUNCTION_SHIFT1; | |||
260 | sub_function = (function & SWSCI_SCIC_SUB_FUNCTION_MASK(0xff << 8)) >> | |||
261 | SWSCI_SCIC_SUB_FUNCTION_SHIFT8; | |||
262 | ||||
263 | /* Check if we can call the function. See swsci_setup for details. */ | |||
264 | if (main_function == SWSCI_SBCB6) { | |||
265 | if ((dev_priv->opregion.swsci_sbcb_sub_functions & | |||
266 | (1 << sub_function)) == 0) | |||
267 | return -EINVAL22; | |||
268 | } else if (main_function == SWSCI_GBDA4) { | |||
269 | if ((dev_priv->opregion.swsci_gbda_sub_functions & | |||
270 | (1 << sub_function)) == 0) | |||
271 | return -EINVAL22; | |||
272 | } | |||
273 | ||||
274 | /* Driver sleep timeout in ms. */ | |||
275 | dslp = swsci->dslp; | |||
276 | if (!dslp) { | |||
277 | /* The spec says 2ms should be the default, but it's too small | |||
278 | * for some machines. */ | |||
279 | dslp = 50; | |||
280 | } else if (dslp > MAX_DSLP1500) { | |||
281 | /* Hey bios, trust must be earned. */ | |||
282 | DRM_INFO_ONCE("ACPI BIOS requests an excessive sleep of %u ms, "({ static int __warned; if (!__warned) { printk("\0016" "[" "drm" "] " "ACPI BIOS requests an excessive sleep of %u ms, " "using %u ms instead\n" , dslp, 1500); __warned = 1; } }) | |||
283 | "using %u ms instead\n", dslp, MAX_DSLP)({ static int __warned; if (!__warned) { printk("\0016" "[" "drm" "] " "ACPI BIOS requests an excessive sleep of %u ms, " "using %u ms instead\n" , dslp, 1500); __warned = 1; } }); | |||
284 | dslp = MAX_DSLP1500; | |||
285 | } | |||
286 | ||||
287 | /* The spec tells us to do this, but we are the only user... */ | |||
288 | scic = swsci->scic; | |||
289 | if (scic & SWSCI_SCIC_INDICATOR(1 << 0)) { | |||
290 | drm_dbg(&dev_priv->drm, "SWSCI request already in progress\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SWSCI request already in progress\n" ); | |||
291 | return -EBUSY16; | |||
292 | } | |||
293 | ||||
294 | scic = function | SWSCI_SCIC_INDICATOR(1 << 0); | |||
295 | ||||
296 | swsci->parm = parm; | |||
297 | swsci->scic = scic; | |||
298 | ||||
299 | /* Ensure SCI event is selected and event trigger is cleared. */ | |||
300 | pci_read_config_word(pdev, SWSCI0xe8, &swsci_val); | |||
301 | if (!(swsci_val & SWSCI_SCISEL(1 << 15)) || (swsci_val & SWSCI_GSSCIE(1 << 0))) { | |||
302 | swsci_val |= SWSCI_SCISEL(1 << 15); | |||
303 | swsci_val &= ~SWSCI_GSSCIE(1 << 0); | |||
304 | pci_write_config_word(pdev, SWSCI0xe8, swsci_val); | |||
305 | } | |||
306 | ||||
307 | /* Use event trigger to tell bios to check the mail. */ | |||
308 | swsci_val |= SWSCI_GSSCIE(1 << 0); | |||
309 | pci_write_config_word(pdev, SWSCI0xe8, swsci_val); | |||
310 | ||||
311 | /* Poll for the result. */ | |||
312 | #define C (((scic = swsci->scic) & SWSCI_SCIC_INDICATOR(1 << 0)) == 0) | |||
313 | if (wait_for(C, dslp)({ const ktime_t end__ = ktime_add_ns(ktime_get_raw(), 1000ll * (((dslp) * 1000))); long wait__ = ((10)); int ret__; assertwaitok (); for (;;) { const _Bool expired__ = ktime_after(ktime_get_raw (), end__); ; __asm volatile("" : : : "memory"); if (((C))) { ret__ = 0; break; } if (expired__) { ret__ = -60; break; } usleep_range (wait__, wait__ * 2); if (wait__ < ((1000))) wait__ <<= 1; } ret__; })) { | |||
314 | drm_dbg(&dev_priv->drm, "SWSCI request timed out\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SWSCI request timed out\n" ); | |||
315 | return -ETIMEDOUT60; | |||
316 | } | |||
317 | ||||
318 | scic = (scic & SWSCI_SCIC_EXIT_STATUS_MASK(7 << 5)) >> | |||
319 | SWSCI_SCIC_EXIT_STATUS_SHIFT5; | |||
320 | ||||
321 | /* Note: scic == 0 is an error! */ | |||
322 | if (scic != SWSCI_SCIC_EXIT_STATUS_SUCCESS1) { | |||
323 | drm_dbg(&dev_priv->drm, "SWSCI request error %u\n", scic)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SWSCI request error %u\n" , scic); | |||
324 | return -EIO5; | |||
325 | } | |||
326 | ||||
327 | if (parm_out) | |||
328 | *parm_out = swsci->parm; | |||
329 | ||||
330 | return 0; | |||
331 | ||||
332 | #undef C | |||
333 | } | |||
334 | ||||
335 | #define DISPLAY_TYPE_CRT0 0 | |||
336 | #define DISPLAY_TYPE_TV1 1 | |||
337 | #define DISPLAY_TYPE_EXTERNAL_FLAT_PANEL2 2 | |||
338 | #define DISPLAY_TYPE_INTERNAL_FLAT_PANEL3 3 | |||
339 | ||||
340 | int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder, | |||
341 | bool_Bool enable) | |||
342 | { | |||
343 | struct drm_i915_privateinteldrm_softc *dev_priv = to_i915(intel_encoder->base.dev); | |||
344 | u32 parm = 0; | |||
345 | u32 type = 0; | |||
346 | u32 port; | |||
347 | ||||
348 | /* don't care about old stuff for now */ | |||
349 | if (!HAS_DDI(dev_priv)((&(dev_priv)->__info)->display.has_ddi)) | |||
350 | return 0; | |||
351 | ||||
352 | if (intel_encoder->type == INTEL_OUTPUT_DSI) | |||
353 | port = 0; | |||
354 | else | |||
355 | port = intel_encoder->port; | |||
356 | ||||
357 | if (port == PORT_E) { | |||
358 | port = 0; | |||
359 | } else { | |||
360 | parm |= 1 << port; | |||
361 | port++; | |||
362 | } | |||
363 | ||||
364 | if (!enable) | |||
365 | parm |= 4 << 8; | |||
366 | ||||
367 | switch (intel_encoder->type) { | |||
368 | case INTEL_OUTPUT_ANALOG: | |||
369 | type = DISPLAY_TYPE_CRT0; | |||
370 | break; | |||
371 | case INTEL_OUTPUT_DDI: | |||
372 | case INTEL_OUTPUT_DP: | |||
373 | case INTEL_OUTPUT_HDMI: | |||
374 | case INTEL_OUTPUT_DP_MST: | |||
375 | type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL2; | |||
376 | break; | |||
377 | case INTEL_OUTPUT_EDP: | |||
378 | case INTEL_OUTPUT_DSI: | |||
379 | type = DISPLAY_TYPE_INTERNAL_FLAT_PANEL3; | |||
380 | break; | |||
381 | default: | |||
382 | drm_WARN_ONCE(&dev_priv->drm, 1,({ static int __warned; int __ret = !!(1); if (__ret && !__warned) { printf("%s %s: " "unsupported intel_encoder type %d\n" , dev_driver_string((&dev_priv->drm)->dev), "", intel_encoder ->type); __warned = 1; } __builtin_expect(!!(__ret), 0); } ) | |||
383 | "unsupported intel_encoder type %d\n",({ static int __warned; int __ret = !!(1); if (__ret && !__warned) { printf("%s %s: " "unsupported intel_encoder type %d\n" , dev_driver_string((&dev_priv->drm)->dev), "", intel_encoder ->type); __warned = 1; } __builtin_expect(!!(__ret), 0); } ) | |||
384 | intel_encoder->type)({ static int __warned; int __ret = !!(1); if (__ret && !__warned) { printf("%s %s: " "unsupported intel_encoder type %d\n" , dev_driver_string((&dev_priv->drm)->dev), "", intel_encoder ->type); __warned = 1; } __builtin_expect(!!(__ret), 0); } ); | |||
385 | return -EINVAL22; | |||
386 | } | |||
387 | ||||
388 | parm |= type << (16 + port * 3); | |||
389 | ||||
390 | return swsci(dev_priv, SWSCI_SBCB_DISPLAY_POWER_STATE((6) << 1 | (8) << 8), parm, NULL((void *)0)); | |||
391 | } | |||
392 | ||||
393 | static const struct { | |||
394 | pci_power_t pci_power_state; | |||
395 | u32 parm; | |||
396 | } power_state_map[] = { | |||
397 | { PCI_D0, 0x00 }, | |||
398 | { PCI_D1, 0x01 }, | |||
399 | { PCI_D2, 0x02 }, | |||
400 | { PCI_D3hot, 0x04 }, | |||
401 | { PCI_D3cold, 0x04 }, | |||
402 | }; | |||
403 | ||||
404 | int intel_opregion_notify_adapter(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
405 | pci_power_t state) | |||
406 | { | |||
407 | int i; | |||
408 | ||||
409 | if (!HAS_DDI(dev_priv)((&(dev_priv)->__info)->display.has_ddi)) | |||
410 | return 0; | |||
411 | ||||
412 | for (i = 0; i < ARRAY_SIZE(power_state_map)(sizeof((power_state_map)) / sizeof((power_state_map)[0])); i++) { | |||
413 | if (state == power_state_map[i].pci_power_state) | |||
414 | return swsci(dev_priv, SWSCI_SBCB_ADAPTER_POWER_STATE((6) << 1 | (7) << 8), | |||
415 | power_state_map[i].parm, NULL((void *)0)); | |||
416 | } | |||
417 | ||||
418 | return -EINVAL22; | |||
419 | } | |||
420 | ||||
421 | static u32 asle_set_backlight(struct drm_i915_privateinteldrm_softc *dev_priv, u32 bclp) | |||
422 | { | |||
423 | struct intel_connector *connector; | |||
424 | struct drm_connector_list_iter conn_iter; | |||
425 | struct opregion_asle *asle = dev_priv->opregion.asle; | |||
426 | struct drm_device *dev = &dev_priv->drm; | |||
427 | ||||
428 | drm_dbg(&dev_priv->drm, "bclp = 0x%08x\n", bclp)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "bclp = 0x%08x\n" , bclp); | |||
429 | ||||
430 | #ifdef __linux__ | |||
431 | if (acpi_video_get_backlight_type() == acpi_backlight_native) { | |||
432 | drm_dbg_kms(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "opregion backlight request ignored\n" ) | |||
433 | "opregion backlight request ignored\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "opregion backlight request ignored\n" ); | |||
434 | return 0; | |||
435 | } | |||
436 | #endif | |||
437 | ||||
438 | if (!(bclp & ASLE_BCLP_VALID(1<<31))) | |||
439 | return ASLC_BACKLIGHT_FAILED(1 << 12); | |||
440 | ||||
441 | bclp &= ASLE_BCLP_MSK(~(1<<31)); | |||
442 | if (bclp > 255) | |||
443 | return ASLC_BACKLIGHT_FAILED(1 << 12); | |||
444 | ||||
445 | drm_modeset_lock(&dev->mode_config.connection_mutex, NULL((void *)0)); | |||
446 | ||||
447 | /* | |||
448 | * Update backlight on all connectors that support backlight (usually | |||
449 | * only one). | |||
450 | */ | |||
451 | drm_dbg_kms(&dev_priv->drm, "updating opregion backlight %d/255\n",drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "updating opregion backlight %d/255\n" , bclp) | |||
452 | bclp)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "updating opregion backlight %d/255\n" , bclp); | |||
453 | drm_connector_list_iter_begin(dev, &conn_iter); | |||
454 | for_each_intel_connector_iter(connector, &conn_iter)while ((connector = ({ const __typeof( ((struct intel_connector *)0)->base ) *__mptr = (drm_connector_list_iter_next(& conn_iter)); (struct intel_connector *)( (char *)__mptr - __builtin_offsetof (struct intel_connector, base) );}))) | |||
455 | intel_panel_set_backlight_acpi(connector->base.state, bclp, 255); | |||
456 | drm_connector_list_iter_end(&conn_iter); | |||
457 | asle->cblv = DIV_ROUND_UP(bclp * 100, 255)(((bclp * 100) + ((255) - 1)) / (255)) | ASLE_CBLV_VALID(1<<31); | |||
458 | ||||
459 | drm_modeset_unlock(&dev->mode_config.connection_mutex); | |||
460 | ||||
461 | ||||
462 | return 0; | |||
463 | } | |||
464 | ||||
465 | static u32 asle_set_als_illum(struct drm_i915_privateinteldrm_softc *dev_priv, u32 alsi) | |||
466 | { | |||
467 | /* alsi is the current ALS reading in lux. 0 indicates below sensor | |||
468 | range, 0xffff indicates above sensor range. 1-0xfffe are valid */ | |||
469 | drm_dbg(&dev_priv->drm, "Illum is not supported\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Illum is not supported\n" ); | |||
470 | return ASLC_ALS_ILLUM_FAILED(1 << 10); | |||
471 | } | |||
472 | ||||
473 | static u32 asle_set_pwm_freq(struct drm_i915_privateinteldrm_softc *dev_priv, u32 pfmb) | |||
474 | { | |||
475 | drm_dbg(&dev_priv->drm, "PWM freq is not supported\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "PWM freq is not supported\n" ); | |||
476 | return ASLC_PWM_FREQ_FAILED(1 << 16); | |||
477 | } | |||
478 | ||||
479 | static u32 asle_set_pfit(struct drm_i915_privateinteldrm_softc *dev_priv, u32 pfit) | |||
480 | { | |||
481 | /* Panel fitting is currently controlled by the X code, so this is a | |||
482 | noop until modesetting support works fully */ | |||
483 | drm_dbg(&dev_priv->drm, "Pfit is not supported\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Pfit is not supported\n" ); | |||
484 | return ASLC_PFIT_FAILED(1 << 14); | |||
485 | } | |||
486 | ||||
487 | static u32 asle_set_supported_rotation_angles(struct drm_i915_privateinteldrm_softc *dev_priv, u32 srot) | |||
488 | { | |||
489 | drm_dbg(&dev_priv->drm, "SROT is not supported\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SROT is not supported\n" ); | |||
490 | return ASLC_ROTATION_ANGLES_FAILED(1 << 18); | |||
491 | } | |||
492 | ||||
493 | static u32 asle_set_button_array(struct drm_i915_privateinteldrm_softc *dev_priv, u32 iuer) | |||
494 | { | |||
495 | if (!iuer) | |||
496 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (nothing)\n" ) | |||
497 | "Button array event is not supported (nothing)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (nothing)\n" ); | |||
498 | if (iuer & ASLE_IUER_ROTATION_LOCK_BTN(1 << 4)) | |||
499 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (rotation lock)\n" ) | |||
500 | "Button array event is not supported (rotation lock)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (rotation lock)\n" ); | |||
501 | if (iuer & ASLE_IUER_VOLUME_DOWN_BTN(1 << 3)) | |||
502 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (volume down)\n" ) | |||
503 | "Button array event is not supported (volume down)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (volume down)\n" ); | |||
504 | if (iuer & ASLE_IUER_VOLUME_UP_BTN(1 << 2)) | |||
505 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (volume up)\n" ) | |||
506 | "Button array event is not supported (volume up)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (volume up)\n" ); | |||
507 | if (iuer & ASLE_IUER_WINDOWS_BTN(1 << 1)) | |||
508 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (windows)\n" ) | |||
509 | "Button array event is not supported (windows)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (windows)\n" ); | |||
510 | if (iuer & ASLE_IUER_POWER_BTN(1 << 0)) | |||
511 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (power)\n" ) | |||
512 | "Button array event is not supported (power)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Button array event is not supported (power)\n" ); | |||
513 | ||||
514 | return ASLC_BUTTON_ARRAY_FAILED(1 << 20); | |||
515 | } | |||
516 | ||||
517 | static u32 asle_set_convertible(struct drm_i915_privateinteldrm_softc *dev_priv, u32 iuer) | |||
518 | { | |||
519 | if (iuer & ASLE_IUER_CONVERTIBLE(1 << 6)) | |||
520 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Convertible is not supported (clamshell)\n" ) | |||
521 | "Convertible is not supported (clamshell)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Convertible is not supported (clamshell)\n" ); | |||
522 | else | |||
523 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Convertible is not supported (slate)\n" ) | |||
524 | "Convertible is not supported (slate)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Convertible is not supported (slate)\n" ); | |||
525 | ||||
526 | return ASLC_CONVERTIBLE_FAILED(1 << 22); | |||
527 | } | |||
528 | ||||
529 | static u32 asle_set_docking(struct drm_i915_privateinteldrm_softc *dev_priv, u32 iuer) | |||
530 | { | |||
531 | if (iuer & ASLE_IUER_DOCKING(1 << 7)) | |||
532 | drm_dbg(&dev_priv->drm, "Docking is not supported (docked)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Docking is not supported (docked)\n" ); | |||
533 | else | |||
534 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Docking is not supported (undocked)\n" ) | |||
535 | "Docking is not supported (undocked)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Docking is not supported (undocked)\n" ); | |||
536 | ||||
537 | return ASLC_DOCKING_FAILED(1 << 24); | |||
538 | } | |||
539 | ||||
540 | static u32 asle_isct_state(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
541 | { | |||
542 | drm_dbg(&dev_priv->drm, "ISCT is not supported\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "ISCT is not supported\n" ); | |||
543 | return ASLC_ISCT_STATE_FAILED(1 << 26); | |||
544 | } | |||
545 | ||||
546 | static void asle_work(struct work_struct *work) | |||
547 | { | |||
548 | struct intel_opregion *opregion = | |||
549 | container_of(work, struct intel_opregion, asle_work)({ const __typeof( ((struct intel_opregion *)0)->asle_work ) *__mptr = (work); (struct intel_opregion *)( (char *)__mptr - __builtin_offsetof(struct intel_opregion, asle_work) );}); | |||
550 | struct drm_i915_privateinteldrm_softc *dev_priv = | |||
551 | container_of(opregion, struct drm_i915_private, opregion)({ const __typeof( ((struct inteldrm_softc *)0)->opregion ) *__mptr = (opregion); (struct inteldrm_softc *)( (char *)__mptr - __builtin_offsetof(struct inteldrm_softc, opregion) );}); | |||
552 | struct opregion_asle *asle = dev_priv->opregion.asle; | |||
553 | u32 aslc_stat = 0; | |||
554 | u32 aslc_req; | |||
555 | ||||
556 | if (!asle) | |||
557 | return; | |||
558 | ||||
559 | aslc_req = asle->aslc; | |||
560 | ||||
561 | if (!(aslc_req & ASLC_REQ_MSK0x1ff)) { | |||
562 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "No request on ASLC interrupt 0x%08x\n" , aslc_req) | |||
563 | "No request on ASLC interrupt 0x%08x\n", aslc_req)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "No request on ASLC interrupt 0x%08x\n" , aslc_req); | |||
564 | return; | |||
565 | } | |||
566 | ||||
567 | if (aslc_req & ASLC_SET_ALS_ILLUM(1 << 0)) | |||
568 | aslc_stat |= asle_set_als_illum(dev_priv, asle->alsi); | |||
569 | ||||
570 | if (aslc_req & ASLC_SET_BACKLIGHT(1 << 1)) | |||
571 | aslc_stat |= asle_set_backlight(dev_priv, asle->bclp); | |||
572 | ||||
573 | if (aslc_req & ASLC_SET_PFIT(1 << 2)) | |||
574 | aslc_stat |= asle_set_pfit(dev_priv, asle->pfit); | |||
575 | ||||
576 | if (aslc_req & ASLC_SET_PWM_FREQ(1 << 3)) | |||
577 | aslc_stat |= asle_set_pwm_freq(dev_priv, asle->pfmb); | |||
578 | ||||
579 | if (aslc_req & ASLC_SUPPORTED_ROTATION_ANGLES(1 << 4)) | |||
580 | aslc_stat |= asle_set_supported_rotation_angles(dev_priv, | |||
581 | asle->srot); | |||
582 | ||||
583 | if (aslc_req & ASLC_BUTTON_ARRAY(1 << 5)) | |||
584 | aslc_stat |= asle_set_button_array(dev_priv, asle->iuer); | |||
585 | ||||
586 | if (aslc_req & ASLC_CONVERTIBLE_INDICATOR(1 << 6)) | |||
587 | aslc_stat |= asle_set_convertible(dev_priv, asle->iuer); | |||
588 | ||||
589 | if (aslc_req & ASLC_DOCKING_INDICATOR(1 << 7)) | |||
590 | aslc_stat |= asle_set_docking(dev_priv, asle->iuer); | |||
591 | ||||
592 | if (aslc_req & ASLC_ISCT_STATE_CHANGE(1 << 8)) | |||
593 | aslc_stat |= asle_isct_state(dev_priv); | |||
594 | ||||
595 | asle->aslc = aslc_stat; | |||
596 | } | |||
597 | ||||
598 | void intel_opregion_asle_intr(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
599 | { | |||
600 | if (dev_priv->opregion.asle) | |||
601 | schedule_work(&dev_priv->opregion.asle_work); | |||
602 | } | |||
603 | ||||
604 | #define ACPI_EV_DISPLAY_SWITCH(1<<0) (1<<0) | |||
605 | #define ACPI_EV_LID(1<<1) (1<<1) | |||
606 | #define ACPI_EV_DOCK(1<<2) (1<<2) | |||
607 | ||||
608 | #ifdef notyet | |||
609 | ||||
610 | /* | |||
611 | * The only video events relevant to opregion are 0x80. These indicate either a | |||
612 | * docking event, lid switch or display switch request. In Linux, these are | |||
613 | * handled by the dock, button and video drivers. | |||
614 | */ | |||
615 | static int intel_opregion_video_event(struct notifier_block *nb, | |||
616 | unsigned long val, void *data) | |||
617 | { | |||
618 | struct intel_opregion *opregion = container_of(nb, struct intel_opregion,({ const __typeof( ((struct intel_opregion *)0)->acpi_notifier ) *__mptr = (nb); (struct intel_opregion *)( (char *)__mptr - __builtin_offsetof(struct intel_opregion, acpi_notifier) );} ) | |||
619 | acpi_notifier)({ const __typeof( ((struct intel_opregion *)0)->acpi_notifier ) *__mptr = (nb); (struct intel_opregion *)( (char *)__mptr - __builtin_offsetof(struct intel_opregion, acpi_notifier) );} ); | |||
620 | struct acpi_bus_event *event = data; | |||
621 | struct opregion_acpi *acpi; | |||
622 | int ret = NOTIFY_OK1; | |||
623 | ||||
624 | if (strcmp(event->device_class, ACPI_VIDEO_CLASS"video") != 0) | |||
625 | return NOTIFY_DONE0; | |||
626 | ||||
627 | acpi = opregion->acpi; | |||
628 | ||||
629 | if (event->type == 0x80 && ((acpi->cevt & 1) == 0)) | |||
630 | ret = NOTIFY_BAD2; | |||
631 | ||||
632 | acpi->csts = 0; | |||
633 | ||||
634 | return ret; | |||
635 | } | |||
636 | ||||
637 | /* | |||
638 | * Initialise the DIDL field in opregion. This passes a list of devices to | |||
639 | * the firmware. Values are defined by section B.4.2 of the ACPI specification | |||
640 | * (version 3) | |||
641 | */ | |||
642 | ||||
643 | static void set_did(struct intel_opregion *opregion, int i, u32 val) | |||
644 | { | |||
645 | if (i < ARRAY_SIZE(opregion->acpi->didl)(sizeof((opregion->acpi->didl)) / sizeof((opregion-> acpi->didl)[0]))) { | |||
646 | opregion->acpi->didl[i] = val; | |||
647 | } else { | |||
648 | i -= ARRAY_SIZE(opregion->acpi->didl)(sizeof((opregion->acpi->didl)) / sizeof((opregion-> acpi->didl)[0])); | |||
649 | ||||
650 | if (WARN_ON(i >= ARRAY_SIZE(opregion->acpi->did2))({ int __ret = !!((i >= (sizeof((opregion->acpi->did2 )) / sizeof((opregion->acpi->did2)[0])))); if (__ret) printf ("%s", "WARN_ON(" "i >= (sizeof((opregion->acpi->did2)) / sizeof((opregion->acpi->did2)[0]))" ")"); __builtin_expect(!!(__ret), 0); })) | |||
651 | return; | |||
652 | ||||
653 | opregion->acpi->did2[i] = val; | |||
654 | } | |||
655 | } | |||
656 | ||||
657 | static void intel_didl_outputs(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
658 | { | |||
659 | struct intel_opregion *opregion = &dev_priv->opregion; | |||
660 | struct intel_connector *connector; | |||
661 | struct drm_connector_list_iter conn_iter; | |||
662 | int i = 0, max_outputs; | |||
663 | ||||
664 | /* | |||
665 | * In theory, did2, the extended didl, gets added at opregion version | |||
666 | * 3.0. In practice, however, we're supposed to set it for earlier | |||
667 | * versions as well, since a BIOS that doesn't understand did2 should | |||
668 | * not look at it anyway. Use a variable so we can tweak this if a need | |||
669 | * arises later. | |||
670 | */ | |||
671 | max_outputs = ARRAY_SIZE(opregion->acpi->didl)(sizeof((opregion->acpi->didl)) / sizeof((opregion-> acpi->didl)[0])) + | |||
672 | ARRAY_SIZE(opregion->acpi->did2)(sizeof((opregion->acpi->did2)) / sizeof((opregion-> acpi->did2)[0])); | |||
673 | ||||
674 | intel_acpi_device_id_update(dev_priv); | |||
675 | ||||
676 | drm_connector_list_iter_begin(&dev_priv->drm, &conn_iter); | |||
677 | for_each_intel_connector_iter(connector, &conn_iter)while ((connector = ({ const __typeof( ((struct intel_connector *)0)->base ) *__mptr = (drm_connector_list_iter_next(& conn_iter)); (struct intel_connector *)( (char *)__mptr - __builtin_offsetof (struct intel_connector, base) );}))) { | |||
678 | if (i < max_outputs) | |||
679 | set_did(opregion, i, connector->acpi_device_id); | |||
680 | i++; | |||
681 | } | |||
682 | drm_connector_list_iter_end(&conn_iter); | |||
683 | ||||
684 | drm_dbg_kms(&dev_priv->drm, "%d outputs detected\n", i)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "%d outputs detected\n" , i); | |||
685 | ||||
686 | if (i > max_outputs) | |||
687 | drm_err(&dev_priv->drm,printf("drm:pid%d:%s *ERROR* " "[drm] " "*ERROR* " "More than %d outputs in connector list\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , max_outputs ) | |||
688 | "More than %d outputs in connector list\n",printf("drm:pid%d:%s *ERROR* " "[drm] " "*ERROR* " "More than %d outputs in connector list\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , max_outputs ) | |||
689 | max_outputs)printf("drm:pid%d:%s *ERROR* " "[drm] " "*ERROR* " "More than %d outputs in connector list\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , max_outputs ); | |||
690 | ||||
691 | /* If fewer than max outputs, the list must be null terminated */ | |||
692 | if (i < max_outputs) | |||
693 | set_did(opregion, i, 0); | |||
694 | } | |||
695 | ||||
696 | static void intel_setup_cadls(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
697 | { | |||
698 | struct intel_opregion *opregion = &dev_priv->opregion; | |||
699 | struct intel_connector *connector; | |||
700 | struct drm_connector_list_iter conn_iter; | |||
701 | int i = 0; | |||
702 | ||||
703 | /* | |||
704 | * Initialize the CADL field from the connector device ids. This is | |||
705 | * essentially the same as copying from the DIDL. Technically, this is | |||
706 | * not always correct as display outputs may exist, but not active. This | |||
707 | * initialization is necessary for some Clevo laptops that check this | |||
708 | * field before processing the brightness and display switching hotkeys. | |||
709 | * | |||
710 | * Note that internal panels should be at the front of the connector | |||
711 | * list already, ensuring they're not left out. | |||
712 | */ | |||
713 | drm_connector_list_iter_begin(&dev_priv->drm, &conn_iter); | |||
714 | for_each_intel_connector_iter(connector, &conn_iter)while ((connector = ({ const __typeof( ((struct intel_connector *)0)->base ) *__mptr = (drm_connector_list_iter_next(& conn_iter)); (struct intel_connector *)( (char *)__mptr - __builtin_offsetof (struct intel_connector, base) );}))) { | |||
715 | if (i >= ARRAY_SIZE(opregion->acpi->cadl)(sizeof((opregion->acpi->cadl)) / sizeof((opregion-> acpi->cadl)[0]))) | |||
716 | break; | |||
717 | opregion->acpi->cadl[i++] = connector->acpi_device_id; | |||
718 | } | |||
719 | drm_connector_list_iter_end(&conn_iter); | |||
720 | ||||
721 | /* If fewer than 8 active devices, the list must be null terminated */ | |||
722 | if (i < ARRAY_SIZE(opregion->acpi->cadl)(sizeof((opregion->acpi->cadl)) / sizeof((opregion-> acpi->cadl)[0]))) | |||
723 | opregion->acpi->cadl[i] = 0; | |||
724 | } | |||
725 | ||||
726 | #endif | |||
727 | ||||
728 | static void swsci_setup(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
729 | { | |||
730 | struct intel_opregion *opregion = &dev_priv->opregion; | |||
731 | bool_Bool requested_callbacks = false0; | |||
732 | u32 tmp; | |||
733 | ||||
734 | /* Sub-function code 0 is okay, let's allow them. */ | |||
735 | opregion->swsci_gbda_sub_functions = 1; | |||
736 | opregion->swsci_sbcb_sub_functions = 1; | |||
737 | ||||
738 | /* We use GBDA to ask for supported GBDA calls. */ | |||
739 | if (swsci(dev_priv, SWSCI_GBDA_SUPPORTED_CALLS((4) << 1 | (0) << 8), 0, &tmp) == 0) { | |||
740 | /* make the bits match the sub-function codes */ | |||
741 | tmp <<= 1; | |||
742 | opregion->swsci_gbda_sub_functions |= tmp; | |||
743 | } | |||
744 | ||||
745 | /* | |||
746 | * We also use GBDA to ask for _requested_ SBCB callbacks. The driver | |||
747 | * must not call interfaces that are not specifically requested by the | |||
748 | * bios. | |||
749 | */ | |||
750 | if (swsci(dev_priv, SWSCI_GBDA_REQUESTED_CALLBACKS((4) << 1 | (1) << 8), 0, &tmp) == 0) { | |||
751 | /* here, the bits already match sub-function codes */ | |||
752 | opregion->swsci_sbcb_sub_functions |= tmp; | |||
753 | requested_callbacks = true1; | |||
754 | } | |||
755 | ||||
756 | /* | |||
757 | * But we use SBCB to ask for _supported_ SBCB calls. This does not mean | |||
758 | * the callback is _requested_. But we still can't call interfaces that | |||
759 | * are not requested. | |||
760 | */ | |||
761 | if (swsci(dev_priv, SWSCI_SBCB_SUPPORTED_CALLBACKS((6) << 1 | (0) << 8), 0, &tmp) == 0) { | |||
762 | /* make the bits match the sub-function codes */ | |||
763 | u32 low = tmp & 0x7ff; | |||
764 | u32 high = tmp & ~0xfff; /* bit 11 is reserved */ | |||
765 | tmp = (high << 4) | (low << 1) | 1; | |||
766 | ||||
767 | /* best guess what to do with supported wrt requested */ | |||
768 | if (requested_callbacks) { | |||
769 | u32 req = opregion->swsci_sbcb_sub_functions; | |||
770 | if ((req & tmp) != req) | |||
771 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SWSCI BIOS requested (%08x) SBCB callbacks that are not supported (%08x)\n" , req, tmp) | |||
772 | "SWSCI BIOS requested (%08x) SBCB callbacks that are not supported (%08x)\n",drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SWSCI BIOS requested (%08x) SBCB callbacks that are not supported (%08x)\n" , req, tmp) | |||
773 | req, tmp)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SWSCI BIOS requested (%08x) SBCB callbacks that are not supported (%08x)\n" , req, tmp); | |||
774 | /* XXX: for now, trust the requested callbacks */ | |||
775 | /* opregion->swsci_sbcb_sub_functions &= tmp; */ | |||
776 | } else { | |||
777 | opregion->swsci_sbcb_sub_functions |= tmp; | |||
778 | } | |||
779 | } | |||
780 | ||||
781 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SWSCI GBDA callbacks %08x, SBCB callbacks %08x\n" , opregion->swsci_gbda_sub_functions, opregion->swsci_sbcb_sub_functions ) | |||
782 | "SWSCI GBDA callbacks %08x, SBCB callbacks %08x\n",drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SWSCI GBDA callbacks %08x, SBCB callbacks %08x\n" , opregion->swsci_gbda_sub_functions, opregion->swsci_sbcb_sub_functions ) | |||
783 | opregion->swsci_gbda_sub_functions,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SWSCI GBDA callbacks %08x, SBCB callbacks %08x\n" , opregion->swsci_gbda_sub_functions, opregion->swsci_sbcb_sub_functions ) | |||
784 | opregion->swsci_sbcb_sub_functions)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SWSCI GBDA callbacks %08x, SBCB callbacks %08x\n" , opregion->swsci_gbda_sub_functions, opregion->swsci_sbcb_sub_functions ); | |||
785 | } | |||
786 | ||||
787 | static int intel_no_opregion_vbt_callback(const struct dmi_system_id *id) | |||
788 | { | |||
789 | DRM_DEBUG_KMS("Falling back to manually reading VBT from "__drm_dbg(DRM_UT_KMS, "Falling back to manually reading VBT from " "VBIOS ROM for %s\n", id->ident) | |||
790 | "VBIOS ROM for %s\n", id->ident)__drm_dbg(DRM_UT_KMS, "Falling back to manually reading VBT from " "VBIOS ROM for %s\n", id->ident); | |||
791 | return 1; | |||
792 | } | |||
793 | ||||
794 | static const struct dmi_system_id intel_no_opregion_vbt[] = { | |||
795 | { | |||
796 | .callback = intel_no_opregion_vbt_callback, | |||
797 | .ident = "ThinkCentre A57", | |||
798 | .matches = { | |||
799 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"){(DMI_SYS_VENDOR), ("LENOVO")}, | |||
800 | DMI_MATCH(DMI_PRODUCT_NAME, "97027RG"){(DMI_PRODUCT_NAME), ("97027RG")}, | |||
801 | }, | |||
802 | }, | |||
803 | { } | |||
804 | }; | |||
805 | ||||
806 | static int intel_load_vbt_firmware(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
807 | { | |||
808 | struct intel_opregion *opregion = &dev_priv->opregion; | |||
809 | const struct firmware *fw = NULL((void *)0); | |||
810 | const char *name = dev_priv->params.vbt_firmware; | |||
811 | int ret; | |||
812 | ||||
813 | if (!name || !*name) | |||
814 | return -ENOENT2; | |||
815 | ||||
816 | #ifdef __linux__ | |||
817 | ret = request_firmware(&fw, name, &dev_priv->drm.pdev->dev); | |||
818 | #else | |||
819 | ret = request_firmware(&fw, name, NULL((void *)0)); | |||
820 | #endif | |||
821 | if (ret) { | |||
822 | drm_err(&dev_priv->drm,printf("drm:pid%d:%s *ERROR* " "[drm] " "*ERROR* " "Requesting VBT firmware \"%s\" failed (%d)\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , name, ret ) | |||
823 | "Requesting VBT firmware \"%s\" failed (%d)\n",printf("drm:pid%d:%s *ERROR* " "[drm] " "*ERROR* " "Requesting VBT firmware \"%s\" failed (%d)\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , name, ret ) | |||
824 | name, ret)printf("drm:pid%d:%s *ERROR* " "[drm] " "*ERROR* " "Requesting VBT firmware \"%s\" failed (%d)\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , name, ret ); | |||
825 | return ret; | |||
826 | } | |||
827 | ||||
828 | if (intel_bios_is_valid_vbt(fw->data, fw->size)) { | |||
| ||||
829 | opregion->vbt_firmware = kmemdup(fw->data, fw->size, GFP_KERNEL(0x0001 | 0x0004)); | |||
830 | if (opregion->vbt_firmware) { | |||
831 | drm_dbg_kms(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Found valid VBT firmware \"%s\"\n" , name) | |||
832 | "Found valid VBT firmware \"%s\"\n", name)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Found valid VBT firmware \"%s\"\n" , name); | |||
833 | opregion->vbt = opregion->vbt_firmware; | |||
834 | opregion->vbt_size = fw->size; | |||
835 | ret = 0; | |||
836 | } else { | |||
837 | ret = -ENOMEM12; | |||
838 | } | |||
839 | } else { | |||
840 | drm_dbg_kms(&dev_priv->drm, "Invalid VBT firmware \"%s\"\n",drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Invalid VBT firmware \"%s\"\n" , name) | |||
841 | name)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Invalid VBT firmware \"%s\"\n" , name); | |||
842 | ret = -EINVAL22; | |||
843 | } | |||
844 | ||||
845 | release_firmware(fw); | |||
846 | ||||
847 | return ret; | |||
848 | } | |||
849 | ||||
850 | int intel_opregion_setup(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
851 | { | |||
852 | struct intel_opregion *opregion = &dev_priv->opregion; | |||
853 | struct pci_dev *pdev = dev_priv->drm.pdev; | |||
854 | u32 asls, mboxes; | |||
855 | char buf[sizeof(OPREGION_SIGNATURE"IntelGraphicsMem")]; | |||
856 | int err = 0; | |||
857 | void *base; | |||
858 | const void *vbt; | |||
859 | u32 vbt_size; | |||
860 | ||||
861 | BUILD_BUG_ON(sizeof(struct opregion_header) != 0x100)extern char _ctassert[(!(sizeof(struct opregion_header) != 0x100 )) ? 1 : -1 ] __attribute__((__unused__)); | |||
862 | BUILD_BUG_ON(sizeof(struct opregion_acpi) != 0x100)extern char _ctassert[(!(sizeof(struct opregion_acpi) != 0x100 )) ? 1 : -1 ] __attribute__((__unused__)); | |||
863 | BUILD_BUG_ON(sizeof(struct opregion_swsci) != 0x100)extern char _ctassert[(!(sizeof(struct opregion_swsci) != 0x100 )) ? 1 : -1 ] __attribute__((__unused__)); | |||
864 | BUILD_BUG_ON(sizeof(struct opregion_asle) != 0x100)extern char _ctassert[(!(sizeof(struct opregion_asle) != 0x100 )) ? 1 : -1 ] __attribute__((__unused__)); | |||
865 | BUILD_BUG_ON(sizeof(struct opregion_asle_ext) != 0x400)extern char _ctassert[(!(sizeof(struct opregion_asle_ext) != 0x400 )) ? 1 : -1 ] __attribute__((__unused__)); | |||
866 | ||||
867 | pci_read_config_dword(pdev, ASLS0xfc, &asls); | |||
868 | drm_dbg(&dev_priv->drm, "graphic opregion physical addr: 0x%x\n",drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "graphic opregion physical addr: 0x%x\n" , asls) | |||
869 | asls)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "graphic opregion physical addr: 0x%x\n" , asls); | |||
870 | if (asls == 0) { | |||
| ||||
871 | drm_dbg(&dev_priv->drm, "ACPI OpRegion not supported!\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "ACPI OpRegion not supported!\n" ); | |||
872 | return -ENOTSUPP91; | |||
873 | } | |||
874 | ||||
875 | INIT_WORK(&opregion->asle_work, asle_work); | |||
876 | ||||
877 | #ifdef __linux__ | |||
878 | base = memremap(asls, OPREGION_SIZE(8 * 1024), MEMREMAP_WB); | |||
879 | if (!base) | |||
880 | return -ENOMEM12; | |||
881 | #else | |||
882 | if (bus_space_map(dev_priv->bst, asls, OPREGION_SIZE(8 * 1024), | |||
883 | BUS_SPACE_MAP_LINEAR0x0002, &dev_priv->opregion_ioh)) | |||
884 | return -ENOMEM12; | |||
885 | base = bus_space_vaddr(dev_priv->bst, dev_priv->opregion_ioh)((dev_priv->bst)->vaddr((dev_priv->opregion_ioh))); | |||
886 | #endif | |||
887 | ||||
888 | memcpy(buf, base, sizeof(buf))__builtin_memcpy((buf), (base), (sizeof(buf))); | |||
889 | ||||
890 | if (memcmp(buf, OPREGION_SIGNATURE, 16)__builtin_memcmp((buf), ("IntelGraphicsMem"), (16))) { | |||
891 | drm_dbg(&dev_priv->drm, "opregion signature mismatch\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "opregion signature mismatch\n" ); | |||
892 | err = -EINVAL22; | |||
893 | goto err_out; | |||
894 | } | |||
895 | opregion->header = base; | |||
896 | opregion->lid_state = base + ACPI_CLID0x01ac; | |||
897 | ||||
898 | drm_dbg(&dev_priv->drm, "ACPI OpRegion version %u.%u.%u\n",drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "ACPI OpRegion version %u.%u.%u\n" , opregion->header->over.major, opregion->header-> over.minor, opregion->header->over.revision) | |||
899 | opregion->header->over.major,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "ACPI OpRegion version %u.%u.%u\n" , opregion->header->over.major, opregion->header-> over.minor, opregion->header->over.revision) | |||
900 | opregion->header->over.minor,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "ACPI OpRegion version %u.%u.%u\n" , opregion->header->over.major, opregion->header-> over.minor, opregion->header->over.revision) | |||
901 | opregion->header->over.revision)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "ACPI OpRegion version %u.%u.%u\n" , opregion->header->over.major, opregion->header-> over.minor, opregion->header->over.revision); | |||
902 | ||||
903 | mboxes = opregion->header->mboxes; | |||
904 | if (mboxes & MBOX_ACPI(1<<0)) { | |||
905 | drm_dbg(&dev_priv->drm, "Public ACPI methods supported\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Public ACPI methods supported\n" ); | |||
906 | opregion->acpi = base + OPREGION_ACPI_OFFSET0x100; | |||
907 | /* | |||
908 | * Indicate we handle monitor hotplug events ourselves so we do | |||
909 | * not need ACPI notifications for them. Disabling these avoids | |||
910 | * triggering the AML code doing the notifation, which may be | |||
911 | * broken as Windows also seems to disable these. | |||
912 | */ | |||
913 | opregion->acpi->chpd = 1; | |||
914 | } | |||
915 | ||||
916 | if (mboxes & MBOX_SWSCI(1<<1)) { | |||
917 | drm_dbg(&dev_priv->drm, "SWSCI supported\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "SWSCI supported\n" ); | |||
918 | opregion->swsci = base + OPREGION_SWSCI_OFFSET0x200; | |||
919 | swsci_setup(dev_priv); | |||
920 | } | |||
921 | ||||
922 | if (mboxes & MBOX_ASLE(1<<2)) { | |||
923 | drm_dbg(&dev_priv->drm, "ASLE supported\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "ASLE supported\n" ); | |||
924 | opregion->asle = base + OPREGION_ASLE_OFFSET0x300; | |||
925 | ||||
926 | opregion->asle->ardy = ASLE_ARDY_NOT_READY(0 << 0); | |||
927 | } | |||
928 | ||||
929 | if (mboxes & MBOX_ASLE_EXT(1<<4)) | |||
930 | drm_dbg(&dev_priv->drm, "ASLE extension supported\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "ASLE extension supported\n" ); | |||
931 | ||||
932 | if (intel_load_vbt_firmware(dev_priv) == 0) | |||
933 | goto out; | |||
934 | ||||
935 | if (dmi_check_system(intel_no_opregion_vbt)) | |||
936 | goto out; | |||
937 | ||||
938 | if (opregion->header->over.major >= 2 && opregion->asle && | |||
939 | opregion->asle->rvda && opregion->asle->rvds) { | |||
940 | resource_size_t rvda = opregion->asle->rvda; | |||
941 | ||||
942 | /* | |||
943 | * opregion 2.0: rvda is the physical VBT address. | |||
944 | * | |||
945 | * opregion 2.1+: rvda is unsigned, relative offset from | |||
946 | * opregion base, and should never point within opregion. | |||
947 | */ | |||
948 | if (opregion->header->over.major > 2 || | |||
949 | opregion->header->over.minor >= 1) { | |||
950 | drm_WARN_ON(&dev_priv->drm, rvda < OPREGION_SIZE)({ int __ret = !!((rvda < (8 * 1024))); if (__ret) printf( "%s %s: " "%s", dev_driver_string(((&dev_priv->drm))-> dev), "", "drm_WARN_ON(" "rvda < (8 * 1024)" ")"); __builtin_expect (!!(__ret), 0); }); | |||
951 | ||||
952 | rvda += asls; | |||
953 | } | |||
954 | ||||
955 | #ifdef __linux__ | |||
956 | opregion->rvda = memremap(rvda, opregion->asle->rvds, | |||
957 | MEMREMAP_WB); | |||
958 | #else | |||
959 | if (bus_space_map(dev_priv->bst, rvda, opregion->asle->rvds, | |||
960 | BUS_SPACE_MAP_LINEAR0x0002, &dev_priv->opregion_rvda_ioh)) | |||
961 | return -ENOMEM12; | |||
962 | opregion->rvda = bus_space_vaddr(dev_priv->bst,((dev_priv->bst)->vaddr((dev_priv->opregion_rvda_ioh ))) | |||
963 | dev_priv->opregion_rvda_ioh)((dev_priv->bst)->vaddr((dev_priv->opregion_rvda_ioh ))); | |||
964 | dev_priv->opregion_rvda_size = opregion->asle->rvds; | |||
965 | #endif | |||
966 | ||||
967 | vbt = opregion->rvda; | |||
968 | vbt_size = opregion->asle->rvds; | |||
969 | if (intel_bios_is_valid_vbt(vbt, vbt_size)) { | |||
970 | drm_dbg_kms(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Found valid VBT in ACPI OpRegion (RVDA)\n" ) | |||
971 | "Found valid VBT in ACPI OpRegion (RVDA)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Found valid VBT in ACPI OpRegion (RVDA)\n" ); | |||
972 | opregion->vbt = vbt; | |||
973 | opregion->vbt_size = vbt_size; | |||
974 | goto out; | |||
975 | } else { | |||
976 | drm_dbg_kms(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Invalid VBT in ACPI OpRegion (RVDA)\n" ) | |||
977 | "Invalid VBT in ACPI OpRegion (RVDA)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Invalid VBT in ACPI OpRegion (RVDA)\n" ); | |||
978 | #ifdef __linux__ | |||
979 | memunmap(opregion->rvda); | |||
980 | #else | |||
981 | bus_space_unmap(dev_priv->bst, dev_priv->opregion_rvda_ioh, | |||
982 | dev_priv->opregion_rvda_size); | |||
983 | #endif | |||
984 | opregion->rvda = NULL((void *)0); | |||
985 | } | |||
986 | } | |||
987 | ||||
988 | vbt = base + OPREGION_VBT_OFFSET0x400; | |||
989 | /* | |||
990 | * The VBT specification says that if the ASLE ext mailbox is not used | |||
991 | * its area is reserved, but on some CHT boards the VBT extends into the | |||
992 | * ASLE ext area. Allow this even though it is against the spec, so we | |||
993 | * do not end up rejecting the VBT on those boards (and end up not | |||
994 | * finding the LCD panel because of this). | |||
995 | */ | |||
996 | vbt_size = (mboxes & MBOX_ASLE_EXT(1<<4)) ? | |||
997 | OPREGION_ASLE_EXT_OFFSET0x1C00 : OPREGION_SIZE(8 * 1024); | |||
998 | vbt_size -= OPREGION_VBT_OFFSET0x400; | |||
999 | if (intel_bios_is_valid_vbt(vbt, vbt_size)) { | |||
1000 | drm_dbg_kms(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Found valid VBT in ACPI OpRegion (Mailbox #4)\n" ) | |||
1001 | "Found valid VBT in ACPI OpRegion (Mailbox #4)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Found valid VBT in ACPI OpRegion (Mailbox #4)\n" ); | |||
1002 | opregion->vbt = vbt; | |||
1003 | opregion->vbt_size = vbt_size; | |||
1004 | } else { | |||
1005 | drm_dbg_kms(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Invalid VBT in ACPI OpRegion (Mailbox #4)\n" ) | |||
1006 | "Invalid VBT in ACPI OpRegion (Mailbox #4)\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Invalid VBT in ACPI OpRegion (Mailbox #4)\n" ); | |||
1007 | } | |||
1008 | ||||
1009 | out: | |||
1010 | return 0; | |||
1011 | ||||
1012 | err_out: | |||
1013 | #ifdef __linux__ | |||
1014 | memunmap(base); | |||
1015 | #else | |||
1016 | bus_space_unmap(dev_priv->bst, dev_priv->opregion_ioh, OPREGION_SIZE(8 * 1024)); | |||
1017 | #endif | |||
1018 | return err; | |||
1019 | } | |||
1020 | ||||
1021 | static int intel_use_opregion_panel_type_callback(const struct dmi_system_id *id) | |||
1022 | { | |||
1023 | DRM_INFO("Using panel type from OpRegion on %s\n", id->ident)printk("\0016" "[" "drm" "] " "Using panel type from OpRegion on %s\n" , id->ident); | |||
1024 | return 1; | |||
1025 | } | |||
1026 | ||||
1027 | static const struct dmi_system_id intel_use_opregion_panel_type[] = { | |||
1028 | { | |||
1029 | .callback = intel_use_opregion_panel_type_callback, | |||
1030 | .ident = "Conrac GmbH IX45GM2", | |||
1031 | .matches = {DMI_MATCH(DMI_SYS_VENDOR, "Conrac GmbH"){(DMI_SYS_VENDOR), ("Conrac GmbH")}, | |||
1032 | DMI_MATCH(DMI_PRODUCT_NAME, "IX45GM2"){(DMI_PRODUCT_NAME), ("IX45GM2")}, | |||
1033 | }, | |||
1034 | }, | |||
1035 | { } | |||
1036 | }; | |||
1037 | ||||
1038 | int | |||
1039 | intel_opregion_get_panel_type(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
1040 | { | |||
1041 | u32 panel_details; | |||
1042 | int ret; | |||
1043 | ||||
1044 | ret = swsci(dev_priv, SWSCI_GBDA_PANEL_DETAILS((4) << 1 | (5) << 8), 0x0, &panel_details); | |||
1045 | if (ret) { | |||
1046 | drm_dbg_kms(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Failed to get panel details from OpRegion (%d)\n" , ret) | |||
1047 | "Failed to get panel details from OpRegion (%d)\n",drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Failed to get panel details from OpRegion (%d)\n" , ret) | |||
1048 | ret)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Failed to get panel details from OpRegion (%d)\n" , ret); | |||
1049 | return ret; | |||
1050 | } | |||
1051 | ||||
1052 | ret = (panel_details >> 8) & 0xff; | |||
1053 | if (ret > 0x10) { | |||
1054 | drm_dbg_kms(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Invalid OpRegion panel type 0x%x\n" , ret) | |||
1055 | "Invalid OpRegion panel type 0x%x\n", ret)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Invalid OpRegion panel type 0x%x\n" , ret); | |||
1056 | return -EINVAL22; | |||
1057 | } | |||
1058 | ||||
1059 | /* fall back to VBT panel type? */ | |||
1060 | if (ret == 0x0) { | |||
1061 | drm_dbg_kms(&dev_priv->drm, "No panel type in OpRegion\n")drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "No panel type in OpRegion\n" ); | |||
1062 | return -ENODEV19; | |||
1063 | } | |||
1064 | ||||
1065 | /* | |||
1066 | * So far we know that some machined must use it, others must not use it. | |||
1067 | * There doesn't seem to be any way to determine which way to go, except | |||
1068 | * via a quirk list :( | |||
1069 | */ | |||
1070 | if (!dmi_check_system(intel_use_opregion_panel_type)) { | |||
1071 | drm_dbg_kms(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Ignoring OpRegion panel type (%d)\n" , ret - 1) | |||
1072 | "Ignoring OpRegion panel type (%d)\n", ret - 1)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_KMS, "Ignoring OpRegion panel type (%d)\n" , ret - 1); | |||
1073 | return -ENODEV19; | |||
1074 | } | |||
1075 | ||||
1076 | return ret - 1; | |||
1077 | } | |||
1078 | ||||
1079 | void intel_opregion_register(struct drm_i915_privateinteldrm_softc *i915) | |||
1080 | { | |||
1081 | struct intel_opregion *opregion = &i915->opregion; | |||
1082 | ||||
1083 | if (!opregion->header) | |||
1084 | return; | |||
1085 | ||||
1086 | if (opregion->acpi) { | |||
1087 | #ifdef notyet | |||
1088 | opregion->acpi_notifier.notifier_call = | |||
1089 | intel_opregion_video_event; | |||
1090 | register_acpi_notifier(&opregion->acpi_notifier); | |||
1091 | #endif | |||
1092 | } | |||
1093 | ||||
1094 | intel_opregion_resume(i915); | |||
1095 | } | |||
1096 | ||||
1097 | void intel_opregion_resume(struct drm_i915_privateinteldrm_softc *i915) | |||
1098 | { | |||
1099 | struct intel_opregion *opregion = &i915->opregion; | |||
1100 | ||||
1101 | if (!opregion->header) | |||
1102 | return; | |||
1103 | ||||
1104 | if (opregion->acpi) { | |||
1105 | #ifdef notyet | |||
1106 | intel_didl_outputs(i915); | |||
1107 | intel_setup_cadls(i915); | |||
1108 | #endif | |||
1109 | ||||
1110 | /* | |||
1111 | * Notify BIOS we are ready to handle ACPI video ext notifs. | |||
1112 | * Right now, all the events are handled by the ACPI video | |||
1113 | * module. We don't actually need to do anything with them. | |||
1114 | */ | |||
1115 | opregion->acpi->csts = 0; | |||
1116 | opregion->acpi->drdy = 1; | |||
1117 | } | |||
1118 | ||||
1119 | if (opregion->asle) { | |||
1120 | opregion->asle->tche = ASLE_TCHE_BLC_EN(1 << 1); | |||
1121 | opregion->asle->ardy = ASLE_ARDY_READY(1 << 0); | |||
1122 | } | |||
1123 | ||||
1124 | intel_opregion_notify_adapter(i915, PCI_D0); | |||
1125 | } | |||
1126 | ||||
1127 | void intel_opregion_suspend(struct drm_i915_privateinteldrm_softc *i915, pci_power_t state) | |||
1128 | { | |||
1129 | struct intel_opregion *opregion = &i915->opregion; | |||
1130 | ||||
1131 | if (!opregion->header) | |||
1132 | return; | |||
1133 | ||||
1134 | intel_opregion_notify_adapter(i915, state); | |||
1135 | ||||
1136 | if (opregion->asle) | |||
1137 | opregion->asle->ardy = ASLE_ARDY_NOT_READY(0 << 0); | |||
1138 | ||||
1139 | cancel_work_sync(&i915->opregion.asle_work); | |||
1140 | ||||
1141 | if (opregion->acpi) | |||
1142 | opregion->acpi->drdy = 0; | |||
1143 | } | |||
1144 | ||||
1145 | void intel_opregion_unregister(struct drm_i915_privateinteldrm_softc *i915) | |||
1146 | { | |||
1147 | struct intel_opregion *opregion = &i915->opregion; | |||
1148 | ||||
1149 | intel_opregion_suspend(i915, PCI_D1); | |||
1150 | ||||
1151 | if (!opregion->header) | |||
1152 | return; | |||
1153 | ||||
1154 | if (opregion->acpi_notifier.notifier_call) { | |||
1155 | unregister_acpi_notifier(&opregion->acpi_notifier); | |||
1156 | opregion->acpi_notifier.notifier_call = NULL((void *)0); | |||
1157 | } | |||
1158 | ||||
1159 | /* just clear all opregion memory pointers now */ | |||
1160 | #ifdef __linux__ | |||
1161 | memunmap(opregion->header); | |||
1162 | if (opregion->rvda) { | |||
1163 | memunmap(opregion->rvda); | |||
1164 | opregion->rvda = NULL((void *)0); | |||
1165 | } | |||
1166 | #else | |||
1167 | bus_space_unmap(i915->bst, i915->opregion_ioh, OPREGION_SIZE(8 * 1024)); | |||
1168 | if (opregion->rvda) { | |||
1169 | bus_space_unmap(i915->bst, i915->opregion_rvda_ioh, | |||
1170 | i915->opregion_rvda_size); | |||
1171 | opregion->rvda = NULL((void *)0); | |||
1172 | } | |||
1173 | #endif | |||
1174 | if (opregion->vbt_firmware) { | |||
1175 | kfree(opregion->vbt_firmware); | |||
1176 | opregion->vbt_firmware = NULL((void *)0); | |||
1177 | } | |||
1178 | opregion->header = NULL((void *)0); | |||
1179 | opregion->acpi = NULL((void *)0); | |||
1180 | opregion->swsci = NULL((void *)0); | |||
1181 | opregion->asle = NULL((void *)0); | |||
1182 | opregion->vbt = NULL((void *)0); | |||
1183 | opregion->lid_state = NULL((void *)0); | |||
1184 | } |
1 | /* Public domain. */ |
2 | |
3 | #ifndef _LINUX_FIRMWARE_H |
4 | #define _LINUX_FIRMWARE_H |
5 | |
6 | #include <sys/types.h> |
7 | #include <sys/malloc.h> |
8 | #include <sys/device.h> |
9 | #include <linux/types.h> |
10 | #include <linux/gfp.h> |
11 | |
12 | #ifndef __DECONST |
13 | #define __DECONST(type, var)((type)(__uintptr_t)(const void *)(var)) ((type)(__uintptr_t)(const void *)(var)) |
14 | #endif |
15 | |
16 | struct firmware { |
17 | size_t size; |
18 | const u8 *data; |
19 | }; |
20 | |
21 | static inline int |
22 | request_firmware(const struct firmware **fw, const char *name, |
23 | struct device *device) |
24 | { |
25 | int r; |
26 | struct firmware *f = malloc(sizeof(struct firmware), M_DRM145, |
27 | M_WAITOK0x0001 | M_ZERO0x0008); |
28 | r = loadfirmware(name, __DECONST(u_char **, &f->data)((u_char **)(__uintptr_t)(const void *)(&f->data)), &f->size); |
29 | if (r != 0) { |
30 | free(f, M_DRM145, sizeof(struct firmware)); |
31 | *fw = NULL((void *)0); |
32 | return -r; |
33 | } else { |
34 | *fw = f; |
35 | return 0; |
36 | } |
37 | } |
38 | |
39 | static inline int |
40 | request_firmware_direct(const struct firmware **fw, const char *name, |
41 | struct device *device) |
42 | { |
43 | return request_firmware(fw, name, device); |
44 | } |
45 | |
46 | #define request_firmware_nowait(a, b, c, d, e, f, g)-22 -EINVAL22 |
47 | |
48 | static inline void |
49 | release_firmware(const struct firmware *fw) |
50 | { |
51 | if (fw) |
52 | free(__DECONST(u_char *, fw->data)((u_char *)(__uintptr_t)(const void *)(fw->data)), M_DEVBUF2, fw->size); |
53 | free(__DECONST(struct firmware *, fw)((struct firmware *)(__uintptr_t)(const void *)(fw)), M_DRM145, sizeof(*fw)); |
54 | } |
55 | |
56 | #endif |