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 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
29 | |
30 | #include <linux/printk.h> |
31 | #include <linux/acpi.h> |
32 | #include <acpi/video.h> |
33 | #include <asm/io.h> |
34 | |
35 | #include <drm/drmP.h> |
36 | #include <drm/i915_drm.h> |
37 | #include "i915_drv.h" |
38 | #include "intel_drv.h" |
39 | |
40 | #ifdef CONFIG_ACPI |
41 | |
42 | #define PCI_ASLE 0xe4 |
43 | #define PCI_ASLS 0xfc |
44 | #define PCI_SWSCI 0xe8 |
45 | #define PCI_SWSCI_SCISEL (1 << 15) |
46 | #define PCI_SWSCI_GSSCIE (1 << 0) |
47 | |
48 | #define 0 |
49 | #define OPREGION_ACPI_OFFSET 0x100 |
50 | #define ACPI_CLID 0x01ac /* current lid state indicator */ |
51 | #define ACPI_CDCK 0x01b0 /* current docking state indicator */ |
52 | #define OPREGION_SWSCI_OFFSET 0x200 |
53 | #define OPREGION_ASLE_OFFSET 0x300 |
54 | #define OPREGION_VBT_OFFSET 0x400 |
55 | |
56 | #define OPREGION_SIGNATURE "IntelGraphicsMem" |
57 | #define MBOX_ACPI (1<<0) |
58 | #define MBOX_SWSCI (1<<1) |
59 | #define MBOX_ASLE (1<<2) |
60 | |
61 | #ifdef __NetBSD__ /* XXX acpi iomem */ |
62 | # define __iomem __acpi_iomem |
63 | |
64 | static inline uint32_t |
65 | ioread32(const uint32_t __acpi_iomem *ptr) |
66 | { |
67 | const uint32_t value = *ptr; |
68 | |
69 | __insn_barrier(); |
70 | return value; |
71 | } |
72 | |
73 | static inline void |
74 | iowrite32(uint32_t value, uint32_t __acpi_iomem *ptr) |
75 | { |
76 | |
77 | __insn_barrier(); |
78 | *ptr = value; |
79 | } |
80 | #endif |
81 | |
82 | struct { |
83 | u8 [16]; |
84 | u32 ; |
85 | u32 ; |
86 | u8 [32]; |
87 | u8 [16]; |
88 | u8 [16]; |
89 | u32 ; |
90 | u8 [164]; |
91 | } __packed; |
92 | |
93 | /* OpRegion mailbox #1: public ACPI methods */ |
94 | struct opregion_acpi { |
95 | u32 drdy; /* driver readiness */ |
96 | u32 csts; /* notification status */ |
97 | u32 cevt; /* current event */ |
98 | u8 rsvd1[20]; |
99 | u32 didl[8]; /* supported display devices ID list */ |
100 | u32 cpdl[8]; /* currently presented display list */ |
101 | u32 cadl[8]; /* currently active display list */ |
102 | u32 nadl[8]; /* next active devices list */ |
103 | u32 aslp; /* ASL sleep time-out */ |
104 | u32 tidx; /* toggle table index */ |
105 | u32 chpd; /* current hotplug enable indicator */ |
106 | u32 clid; /* current lid state*/ |
107 | u32 cdck; /* current docking state */ |
108 | u32 sxsw; /* Sx state resume */ |
109 | u32 evts; /* ASL supported events */ |
110 | u32 cnot; /* current OS notification */ |
111 | u32 nrdy; /* driver status */ |
112 | u8 rsvd2[60]; |
113 | } __packed; |
114 | |
115 | /* OpRegion mailbox #2: SWSCI */ |
116 | struct opregion_swsci { |
117 | u32 scic; /* SWSCI command|status|data */ |
118 | u32 parm; /* command parameters */ |
119 | u32 dslp; /* driver sleep time-out */ |
120 | u8 rsvd[244]; |
121 | } __packed; |
122 | |
123 | /* OpRegion mailbox #3: ASLE */ |
124 | struct opregion_asle { |
125 | u32 ardy; /* driver readiness */ |
126 | u32 aslc; /* ASLE interrupt command */ |
127 | u32 tche; /* technology enabled indicator */ |
128 | u32 alsi; /* current ALS illuminance reading */ |
129 | u32 bclp; /* backlight brightness to set */ |
130 | u32 pfit; /* panel fitting state */ |
131 | u32 cblv; /* current brightness level */ |
132 | u16 bclm[20]; /* backlight level duty cycle mapping table */ |
133 | u32 cpfm; /* current panel fitting mode */ |
134 | u32 epfm; /* enabled panel fitting modes */ |
135 | u8 plut[74]; /* panel LUT and identifier */ |
136 | u32 pfmb; /* PWM freq and min brightness */ |
137 | u32 cddv; /* color correction default values */ |
138 | u32 pcft; /* power conservation features */ |
139 | u32 srot; /* supported rotation angles */ |
140 | u32 iuer; /* IUER events */ |
141 | u8 rsvd[86]; |
142 | } __packed; |
143 | |
144 | /* Driver readiness indicator */ |
145 | #define ASLE_ARDY_READY (1 << 0) |
146 | #define ASLE_ARDY_NOT_READY (0 << 0) |
147 | |
148 | /* ASLE Interrupt Command (ASLC) bits */ |
149 | #define ASLC_SET_ALS_ILLUM (1 << 0) |
150 | #define ASLC_SET_BACKLIGHT (1 << 1) |
151 | #define ASLC_SET_PFIT (1 << 2) |
152 | #define ASLC_SET_PWM_FREQ (1 << 3) |
153 | #define ASLC_SUPPORTED_ROTATION_ANGLES (1 << 4) |
154 | #define ASLC_BUTTON_ARRAY (1 << 5) |
155 | #define ASLC_CONVERTIBLE_INDICATOR (1 << 6) |
156 | #define ASLC_DOCKING_INDICATOR (1 << 7) |
157 | #define ASLC_ISCT_STATE_CHANGE (1 << 8) |
158 | #define ASLC_REQ_MSK 0x1ff |
159 | /* response bits */ |
160 | #define ASLC_ALS_ILLUM_FAILED (1 << 10) |
161 | #define ASLC_BACKLIGHT_FAILED (1 << 12) |
162 | #define ASLC_PFIT_FAILED (1 << 14) |
163 | #define ASLC_PWM_FREQ_FAILED (1 << 16) |
164 | #define ASLC_ROTATION_ANGLES_FAILED (1 << 18) |
165 | #define ASLC_BUTTON_ARRAY_FAILED (1 << 20) |
166 | #define ASLC_CONVERTIBLE_FAILED (1 << 22) |
167 | #define ASLC_DOCKING_FAILED (1 << 24) |
168 | #define ASLC_ISCT_STATE_FAILED (1 << 26) |
169 | |
170 | /* Technology enabled indicator */ |
171 | #define ASLE_TCHE_ALS_EN (1 << 0) |
172 | #define ASLE_TCHE_BLC_EN (1 << 1) |
173 | #define ASLE_TCHE_PFIT_EN (1 << 2) |
174 | #define ASLE_TCHE_PFMB_EN (1 << 3) |
175 | |
176 | /* ASLE backlight brightness to set */ |
177 | #define ASLE_BCLP_VALID (1<<31) |
178 | #define ASLE_BCLP_MSK (~(1<<31)) |
179 | |
180 | /* ASLE panel fitting request */ |
181 | #define ASLE_PFIT_VALID (1<<31) |
182 | #define ASLE_PFIT_CENTER (1<<0) |
183 | #define ASLE_PFIT_STRETCH_TEXT (1<<1) |
184 | #define ASLE_PFIT_STRETCH_GFX (1<<2) |
185 | |
186 | /* PWM frequency and minimum brightness */ |
187 | #define ASLE_PFMB_BRIGHTNESS_MASK (0xff) |
188 | #define ASLE_PFMB_BRIGHTNESS_VALID (1<<8) |
189 | #define ASLE_PFMB_PWM_MASK (0x7ffffe00) |
190 | #define ASLE_PFMB_PWM_VALID (1<<31) |
191 | |
192 | #define ASLE_CBLV_VALID (1<<31) |
193 | |
194 | /* IUER */ |
195 | #define ASLE_IUER_DOCKING (1 << 7) |
196 | #define ASLE_IUER_CONVERTIBLE (1 << 6) |
197 | #define ASLE_IUER_ROTATION_LOCK_BTN (1 << 4) |
198 | #define ASLE_IUER_VOLUME_DOWN_BTN (1 << 3) |
199 | #define ASLE_IUER_VOLUME_UP_BTN (1 << 2) |
200 | #define ASLE_IUER_WINDOWS_BTN (1 << 1) |
201 | #define ASLE_IUER_POWER_BTN (1 << 0) |
202 | |
203 | /* Software System Control Interrupt (SWSCI) */ |
204 | #define SWSCI_SCIC_INDICATOR (1 << 0) |
205 | #define SWSCI_SCIC_MAIN_FUNCTION_SHIFT 1 |
206 | #define SWSCI_SCIC_MAIN_FUNCTION_MASK (0xf << 1) |
207 | #define SWSCI_SCIC_SUB_FUNCTION_SHIFT 8 |
208 | #define SWSCI_SCIC_SUB_FUNCTION_MASK (0xff << 8) |
209 | #define SWSCI_SCIC_EXIT_PARAMETER_SHIFT 8 |
210 | #define SWSCI_SCIC_EXIT_PARAMETER_MASK (0xff << 8) |
211 | #define SWSCI_SCIC_EXIT_STATUS_SHIFT 5 |
212 | #define SWSCI_SCIC_EXIT_STATUS_MASK (7 << 5) |
213 | #define SWSCI_SCIC_EXIT_STATUS_SUCCESS 1 |
214 | |
215 | #define SWSCI_FUNCTION_CODE(main, sub) \ |
216 | ((main) << SWSCI_SCIC_MAIN_FUNCTION_SHIFT | \ |
217 | (sub) << SWSCI_SCIC_SUB_FUNCTION_SHIFT) |
218 | |
219 | /* SWSCI: Get BIOS Data (GBDA) */ |
220 | #define SWSCI_GBDA 4 |
221 | #define SWSCI_GBDA_SUPPORTED_CALLS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 0) |
222 | #define SWSCI_GBDA_REQUESTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 1) |
223 | #define SWSCI_GBDA_BOOT_DISPLAY_PREF SWSCI_FUNCTION_CODE(SWSCI_GBDA, 4) |
224 | #define SWSCI_GBDA_PANEL_DETAILS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 5) |
225 | #define SWSCI_GBDA_TV_STANDARD SWSCI_FUNCTION_CODE(SWSCI_GBDA, 6) |
226 | #define SWSCI_GBDA_INTERNAL_GRAPHICS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 7) |
227 | #define SWSCI_GBDA_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_GBDA, 10) |
228 | |
229 | /* SWSCI: System BIOS Callbacks (SBCB) */ |
230 | #define SWSCI_SBCB 6 |
231 | #define SWSCI_SBCB_SUPPORTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 0) |
232 | #define SWSCI_SBCB_INIT_COMPLETION SWSCI_FUNCTION_CODE(SWSCI_SBCB, 1) |
233 | #define SWSCI_SBCB_PRE_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 3) |
234 | #define SWSCI_SBCB_POST_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 4) |
235 | #define SWSCI_SBCB_DISPLAY_SWITCH SWSCI_FUNCTION_CODE(SWSCI_SBCB, 5) |
236 | #define SWSCI_SBCB_SET_TV_FORMAT SWSCI_FUNCTION_CODE(SWSCI_SBCB, 6) |
237 | #define SWSCI_SBCB_ADAPTER_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 7) |
238 | #define SWSCI_SBCB_DISPLAY_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 8) |
239 | #define SWSCI_SBCB_SET_BOOT_DISPLAY SWSCI_FUNCTION_CODE(SWSCI_SBCB, 9) |
240 | #define SWSCI_SBCB_SET_PANEL_DETAILS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 10) |
241 | #define SWSCI_SBCB_SET_INTERNAL_GFX SWSCI_FUNCTION_CODE(SWSCI_SBCB, 11) |
242 | #define SWSCI_SBCB_POST_HIRES_TO_DOS_FS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 16) |
243 | #define SWSCI_SBCB_SUSPEND_RESUME SWSCI_FUNCTION_CODE(SWSCI_SBCB, 17) |
244 | #define SWSCI_SBCB_SET_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 18) |
245 | #define SWSCI_SBCB_POST_VBE_PM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 19) |
246 | #define SWSCI_SBCB_ENABLE_DISABLE_AUDIO SWSCI_FUNCTION_CODE(SWSCI_SBCB, 21) |
247 | |
248 | #define ACPI_OTHER_OUTPUT (0<<8) |
249 | #define ACPI_VGA_OUTPUT (1<<8) |
250 | #define ACPI_TV_OUTPUT (2<<8) |
251 | #define ACPI_DIGITAL_OUTPUT (3<<8) |
252 | #define ACPI_LVDS_OUTPUT (4<<8) |
253 | |
254 | #define MAX_DSLP 1500 |
255 | |
256 | #ifdef CONFIG_ACPI |
257 | static int swsci(struct drm_device *dev, u32 function, u32 parm, u32 *parm_out) |
258 | { |
259 | struct drm_i915_private *dev_priv = dev->dev_private; |
260 | struct opregion_swsci __iomem *region_swsci = dev_priv->opregion.swsci; |
261 | u32 main_function, sub_function, scic; |
262 | u16 pci_swsci; |
263 | u32 dslp; |
264 | |
265 | if (!region_swsci) |
266 | return -ENODEV; |
267 | |
268 | main_function = (function & SWSCI_SCIC_MAIN_FUNCTION_MASK) >> |
269 | SWSCI_SCIC_MAIN_FUNCTION_SHIFT; |
270 | sub_function = (function & SWSCI_SCIC_SUB_FUNCTION_MASK) >> |
271 | SWSCI_SCIC_SUB_FUNCTION_SHIFT; |
272 | |
273 | /* Check if we can call the function. See swsci_setup for details. */ |
274 | if (main_function == SWSCI_SBCB) { |
275 | if ((dev_priv->opregion.swsci_sbcb_sub_functions & |
276 | (1 << sub_function)) == 0) |
277 | return -EINVAL; |
278 | } else if (main_function == SWSCI_GBDA) { |
279 | if ((dev_priv->opregion.swsci_gbda_sub_functions & |
280 | (1 << sub_function)) == 0) |
281 | return -EINVAL; |
282 | } |
283 | |
284 | /* Driver sleep timeout in ms. */ |
285 | dslp = ioread32(®ion_swsci->dslp); |
286 | if (!dslp) { |
287 | /* The spec says 2ms should be the default, but it's too small |
288 | * for some machines. */ |
289 | dslp = 50; |
290 | } else if (dslp > MAX_DSLP) { |
291 | /* Hey bios, trust must be earned. */ |
292 | DRM_INFO_ONCE("ACPI BIOS requests an excessive sleep of %u ms, " |
293 | "using %u ms instead\n" , dslp, MAX_DSLP); |
294 | dslp = MAX_DSLP; |
295 | } |
296 | |
297 | /* The spec tells us to do this, but we are the only user... */ |
298 | scic = ioread32(®ion_swsci->scic); |
299 | if (scic & SWSCI_SCIC_INDICATOR) { |
300 | DRM_DEBUG_DRIVER("SWSCI request already in progress\n" ); |
301 | return -EBUSY; |
302 | } |
303 | |
304 | scic = function | SWSCI_SCIC_INDICATOR; |
305 | |
306 | iowrite32(parm, ®ion_swsci->parm); |
307 | iowrite32(scic, ®ion_swsci->scic); |
308 | |
309 | /* Ensure SCI event is selected and event trigger is cleared. */ |
310 | pci_read_config_word(dev->pdev, PCI_SWSCI, &pci_swsci); |
311 | if (!(pci_swsci & PCI_SWSCI_SCISEL) || (pci_swsci & PCI_SWSCI_GSSCIE)) { |
312 | pci_swsci |= PCI_SWSCI_SCISEL; |
313 | pci_swsci &= ~PCI_SWSCI_GSSCIE; |
314 | pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci); |
315 | } |
316 | |
317 | /* Use event trigger to tell bios to check the mail. */ |
318 | pci_swsci |= PCI_SWSCI_GSSCIE; |
319 | pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci); |
320 | |
321 | /* Poll for the result. */ |
322 | #define C (((scic = ioread32(®ion_swsci->scic)) & SWSCI_SCIC_INDICATOR) == 0) |
323 | if (wait_for(C, dslp)) { |
324 | DRM_DEBUG_DRIVER("SWSCI request timed out\n" ); |
325 | return -ETIMEDOUT; |
326 | } |
327 | |
328 | scic = (scic & SWSCI_SCIC_EXIT_STATUS_MASK) >> |
329 | SWSCI_SCIC_EXIT_STATUS_SHIFT; |
330 | |
331 | /* Note: scic == 0 is an error! */ |
332 | if (scic != SWSCI_SCIC_EXIT_STATUS_SUCCESS) { |
333 | DRM_DEBUG_DRIVER("SWSCI request error %u\n" , scic); |
334 | return -EIO; |
335 | } |
336 | |
337 | if (parm_out) |
338 | *parm_out = ioread32(®ion_swsci->parm); |
339 | |
340 | return 0; |
341 | |
342 | #undef C |
343 | } |
344 | |
345 | #define DISPLAY_TYPE_CRT 0 |
346 | #define DISPLAY_TYPE_TV 1 |
347 | #define DISPLAY_TYPE_EXTERNAL_FLAT_PANEL 2 |
348 | #define DISPLAY_TYPE_INTERNAL_FLAT_PANEL 3 |
349 | |
350 | int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder, |
351 | bool enable) |
352 | { |
353 | struct drm_device *dev = intel_encoder->base.dev; |
354 | u32 parm = 0; |
355 | u32 type = 0; |
356 | u32 port; |
357 | |
358 | /* don't care about old stuff for now */ |
359 | if (!HAS_DDI(dev)) |
360 | return 0; |
361 | |
362 | port = intel_ddi_get_encoder_port(intel_encoder); |
363 | if (port == PORT_E) { |
364 | port = 0; |
365 | } else { |
366 | parm |= 1 << port; |
367 | port++; |
368 | } |
369 | |
370 | if (!enable) |
371 | parm |= 4 << 8; |
372 | |
373 | switch (intel_encoder->type) { |
374 | case INTEL_OUTPUT_ANALOG: |
375 | type = DISPLAY_TYPE_CRT; |
376 | break; |
377 | case INTEL_OUTPUT_UNKNOWN: |
378 | case INTEL_OUTPUT_DISPLAYPORT: |
379 | case INTEL_OUTPUT_HDMI: |
380 | type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL; |
381 | break; |
382 | case INTEL_OUTPUT_EDP: |
383 | type = DISPLAY_TYPE_INTERNAL_FLAT_PANEL; |
384 | break; |
385 | default: |
386 | WARN_ONCE(1, "unsupported intel_encoder type %d\n" , |
387 | intel_encoder->type); |
388 | return -EINVAL; |
389 | } |
390 | |
391 | parm |= type << (16 + port * 3); |
392 | |
393 | return swsci(dev, SWSCI_SBCB_DISPLAY_POWER_STATE, parm, NULL); |
394 | } |
395 | |
396 | static const struct { |
397 | pci_power_t pci_power_state; |
398 | u32 parm; |
399 | } power_state_map[] = { |
400 | { PCI_D0, 0x00 }, |
401 | { PCI_D1, 0x01 }, |
402 | { PCI_D2, 0x02 }, |
403 | { PCI_D3hot, 0x04 }, |
404 | { PCI_D3cold, 0x04 }, |
405 | }; |
406 | |
407 | int intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state) |
408 | { |
409 | int i; |
410 | |
411 | if (!HAS_DDI(dev)) |
412 | return 0; |
413 | |
414 | for (i = 0; i < ARRAY_SIZE(power_state_map); i++) { |
415 | if (state == power_state_map[i].pci_power_state) |
416 | return swsci(dev, SWSCI_SBCB_ADAPTER_POWER_STATE, |
417 | power_state_map[i].parm, NULL); |
418 | } |
419 | |
420 | return -EINVAL; |
421 | } |
422 | |
423 | static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) |
424 | { |
425 | struct drm_i915_private *dev_priv = dev->dev_private; |
426 | struct intel_connector *intel_connector; |
427 | struct opregion_asle __iomem *asle = dev_priv->opregion.asle; |
428 | |
429 | DRM_DEBUG_DRIVER("bclp = 0x%08x\n" , bclp); |
430 | |
431 | if (!(bclp & ASLE_BCLP_VALID)) |
432 | return ASLC_BACKLIGHT_FAILED; |
433 | |
434 | bclp &= ASLE_BCLP_MSK; |
435 | if (bclp > 255) |
436 | return ASLC_BACKLIGHT_FAILED; |
437 | |
438 | mutex_lock(&dev->mode_config.mutex); |
439 | |
440 | /* |
441 | * Update backlight on all connectors that support backlight (usually |
442 | * only one). |
443 | */ |
444 | DRM_DEBUG_KMS("updating opregion backlight %d/255\n" , bclp); |
445 | list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) |
446 | intel_panel_set_backlight(intel_connector, bclp, 255); |
447 | iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv); |
448 | |
449 | mutex_unlock(&dev->mode_config.mutex); |
450 | |
451 | |
452 | return 0; |
453 | } |
454 | |
455 | static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi) |
456 | { |
457 | /* alsi is the current ALS reading in lux. 0 indicates below sensor |
458 | range, 0xffff indicates above sensor range. 1-0xfffe are valid */ |
459 | DRM_DEBUG_DRIVER("Illum is not supported\n" ); |
460 | return ASLC_ALS_ILLUM_FAILED; |
461 | } |
462 | |
463 | static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb) |
464 | { |
465 | DRM_DEBUG_DRIVER("PWM freq is not supported\n" ); |
466 | return ASLC_PWM_FREQ_FAILED; |
467 | } |
468 | |
469 | static u32 asle_set_pfit(struct drm_device *dev, u32 pfit) |
470 | { |
471 | /* Panel fitting is currently controlled by the X code, so this is a |
472 | noop until modesetting support works fully */ |
473 | DRM_DEBUG_DRIVER("Pfit is not supported\n" ); |
474 | return ASLC_PFIT_FAILED; |
475 | } |
476 | |
477 | static u32 asle_set_supported_rotation_angles(struct drm_device *dev, u32 srot) |
478 | { |
479 | DRM_DEBUG_DRIVER("SROT is not supported\n" ); |
480 | return ASLC_ROTATION_ANGLES_FAILED; |
481 | } |
482 | |
483 | static u32 asle_set_button_array(struct drm_device *dev, u32 iuer) |
484 | { |
485 | if (!iuer) |
486 | DRM_DEBUG_DRIVER("Button array event is not supported (nothing)\n" ); |
487 | if (iuer & ASLE_IUER_ROTATION_LOCK_BTN) |
488 | DRM_DEBUG_DRIVER("Button array event is not supported (rotation lock)\n" ); |
489 | if (iuer & ASLE_IUER_VOLUME_DOWN_BTN) |
490 | DRM_DEBUG_DRIVER("Button array event is not supported (volume down)\n" ); |
491 | if (iuer & ASLE_IUER_VOLUME_UP_BTN) |
492 | DRM_DEBUG_DRIVER("Button array event is not supported (volume up)\n" ); |
493 | if (iuer & ASLE_IUER_WINDOWS_BTN) |
494 | DRM_DEBUG_DRIVER("Button array event is not supported (windows)\n" ); |
495 | if (iuer & ASLE_IUER_POWER_BTN) |
496 | DRM_DEBUG_DRIVER("Button array event is not supported (power)\n" ); |
497 | |
498 | return ASLC_BUTTON_ARRAY_FAILED; |
499 | } |
500 | |
501 | static u32 asle_set_convertible(struct drm_device *dev, u32 iuer) |
502 | { |
503 | if (iuer & ASLE_IUER_CONVERTIBLE) |
504 | DRM_DEBUG_DRIVER("Convertible is not supported (clamshell)\n" ); |
505 | else |
506 | DRM_DEBUG_DRIVER("Convertible is not supported (slate)\n" ); |
507 | |
508 | return ASLC_CONVERTIBLE_FAILED; |
509 | } |
510 | |
511 | static u32 asle_set_docking(struct drm_device *dev, u32 iuer) |
512 | { |
513 | if (iuer & ASLE_IUER_DOCKING) |
514 | DRM_DEBUG_DRIVER("Docking is not supported (docked)\n" ); |
515 | else |
516 | DRM_DEBUG_DRIVER("Docking is not supported (undocked)\n" ); |
517 | |
518 | return ASLC_DOCKING_FAILED; |
519 | } |
520 | |
521 | static u32 asle_isct_state(struct drm_device *dev) |
522 | { |
523 | DRM_DEBUG_DRIVER("ISCT is not supported\n" ); |
524 | return ASLC_ISCT_STATE_FAILED; |
525 | } |
526 | |
527 | static void asle_work(struct work_struct *work) |
528 | { |
529 | struct intel_opregion *opregion = |
530 | container_of(work, struct intel_opregion, asle_work); |
531 | struct drm_i915_private *dev_priv = |
532 | container_of(opregion, struct drm_i915_private, opregion); |
533 | struct drm_device *dev = dev_priv->dev; |
534 | struct opregion_asle __iomem *asle = dev_priv->opregion.asle; |
535 | u32 aslc_stat = 0; |
536 | u32 aslc_req; |
537 | |
538 | if (!asle) |
539 | return; |
540 | |
541 | aslc_req = ioread32(&asle->aslc); |
542 | |
543 | if (!(aslc_req & ASLC_REQ_MSK)) { |
544 | DRM_DEBUG_DRIVER("No request on ASLC interrupt 0x%08x\n" , |
545 | aslc_req); |
546 | return; |
547 | } |
548 | |
549 | if (aslc_req & ASLC_SET_ALS_ILLUM) |
550 | aslc_stat |= asle_set_als_illum(dev, ioread32(&asle->alsi)); |
551 | |
552 | if (aslc_req & ASLC_SET_BACKLIGHT) |
553 | aslc_stat |= asle_set_backlight(dev, ioread32(&asle->bclp)); |
554 | |
555 | if (aslc_req & ASLC_SET_PFIT) |
556 | aslc_stat |= asle_set_pfit(dev, ioread32(&asle->pfit)); |
557 | |
558 | if (aslc_req & ASLC_SET_PWM_FREQ) |
559 | aslc_stat |= asle_set_pwm_freq(dev, ioread32(&asle->pfmb)); |
560 | |
561 | if (aslc_req & ASLC_SUPPORTED_ROTATION_ANGLES) |
562 | aslc_stat |= asle_set_supported_rotation_angles(dev, |
563 | ioread32(&asle->srot)); |
564 | |
565 | if (aslc_req & ASLC_BUTTON_ARRAY) |
566 | aslc_stat |= asle_set_button_array(dev, ioread32(&asle->iuer)); |
567 | |
568 | if (aslc_req & ASLC_CONVERTIBLE_INDICATOR) |
569 | aslc_stat |= asle_set_convertible(dev, ioread32(&asle->iuer)); |
570 | |
571 | if (aslc_req & ASLC_DOCKING_INDICATOR) |
572 | aslc_stat |= asle_set_docking(dev, ioread32(&asle->iuer)); |
573 | |
574 | if (aslc_req & ASLC_ISCT_STATE_CHANGE) |
575 | aslc_stat |= asle_isct_state(dev); |
576 | |
577 | iowrite32(aslc_stat, &asle->aslc); |
578 | } |
579 | |
580 | void intel_opregion_asle_intr(struct drm_device *dev) |
581 | { |
582 | struct drm_i915_private *dev_priv = dev->dev_private; |
583 | |
584 | if (dev_priv->opregion.asle) |
585 | schedule_work(&dev_priv->opregion.asle_work); |
586 | } |
587 | |
588 | #define ACPI_EV_DISPLAY_SWITCH (1<<0) |
589 | #define ACPI_EV_LID (1<<1) |
590 | #define ACPI_EV_DOCK (1<<2) |
591 | |
592 | static struct intel_opregion *system_opregion; |
593 | |
594 | #ifdef __NetBSD__ |
595 | static void |
596 | intel_opregion_video_event(ACPI_HANDLE hdl, uint32_t notify, void *opaque) |
597 | { |
598 | device_t self = opaque; |
599 | struct opregion_acpi __iomem *acpi; |
600 | |
601 | DRM_DEBUG_DRIVER("notify=0x%08x\n" , notify); |
602 | |
603 | if (!system_opregion) |
604 | return; |
605 | |
606 | acpi = system_opregion->acpi; |
607 | |
608 | if (notify != 0x80) { |
609 | aprint_error_dev(self, "unknown notify 0x%02x\n" , notify); |
610 | } else if ((ioread32(&acpi->cevt) & 1) == 0) { |
611 | aprint_error_dev(self, "bad notify\n" ); |
612 | } |
613 | |
614 | iowrite32(0, &acpi->csts); |
615 | } |
616 | #else /* !__NetBSD__ */ |
617 | static int intel_opregion_video_event(struct notifier_block *nb, |
618 | unsigned long val, void *data) |
619 | { |
620 | /* The only video events relevant to opregion are 0x80. These indicate |
621 | either a docking event, lid switch or display switch request. In |
622 | Linux, these are handled by the dock, button and video drivers. |
623 | */ |
624 | |
625 | struct opregion_acpi __iomem *acpi; |
626 | struct acpi_bus_event *event = data; |
627 | int ret = NOTIFY_OK; |
628 | |
629 | if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0) |
630 | return NOTIFY_DONE; |
631 | |
632 | if (!system_opregion) |
633 | return NOTIFY_DONE; |
634 | |
635 | acpi = system_opregion->acpi; |
636 | |
637 | if (event->type == 0x80 && |
638 | (ioread32(&acpi->cevt) & 1) == 0) |
639 | ret = NOTIFY_BAD; |
640 | |
641 | iowrite32(0, &acpi->csts); |
642 | |
643 | return ret; |
644 | } |
645 | |
646 | static struct notifier_block intel_opregion_notifier = { |
647 | .notifier_call = intel_opregion_video_event, |
648 | }; |
649 | #endif /* __NetBSD__ */ |
650 | |
651 | /* |
652 | * Initialise the DIDL field in opregion. This passes a list of devices to |
653 | * the firmware. Values are defined by section B.4.2 of the ACPI specification |
654 | * (version 3) |
655 | */ |
656 | |
657 | static void intel_didl_outputs(struct drm_device *dev) |
658 | { |
659 | struct drm_i915_private *dev_priv = dev->dev_private; |
660 | struct intel_opregion *opregion = &dev_priv->opregion; |
661 | struct drm_connector *connector; |
662 | #ifdef __NetBSD__ |
663 | struct acpi_devnode *ad, *child; |
664 | ACPI_INTEGER device_id; |
665 | ACPI_STATUS status; |
666 | #else |
667 | acpi_handle handle; |
668 | struct acpi_device *acpi_dev, *acpi_cdev, *acpi_video_bus = NULL; |
669 | unsigned long long device_id; |
670 | acpi_status status; |
671 | #endif |
672 | u32 temp; |
673 | int i = 0; |
674 | |
675 | #ifdef __NetBSD__ |
676 | ad = dev->pdev->pd_ad; |
677 | if (ad == NULL || |
678 | ad->ad_device == NULL || |
679 | !device_is_a(ad->ad_device, "acpivga" )) |
680 | return; |
681 | #else |
682 | handle = ACPI_HANDLE(&dev->pdev->dev); |
683 | if (!handle || acpi_bus_get_device(handle, &acpi_dev)) |
684 | return; |
685 | |
686 | if (acpi_is_video_device(handle)) |
687 | acpi_video_bus = acpi_dev; |
688 | else { |
689 | list_for_each_entry(acpi_cdev, &acpi_dev->children, node) { |
690 | if (acpi_is_video_device(acpi_cdev->handle)) { |
691 | acpi_video_bus = acpi_cdev; |
692 | break; |
693 | } |
694 | } |
695 | } |
696 | |
697 | if (!acpi_video_bus) { |
698 | pr_warn("No ACPI video bus found\n" ); |
699 | return; |
700 | } |
701 | #endif |
702 | |
703 | #ifdef __NetBSD__ |
704 | SIMPLEQ_FOREACH(child, &ad->ad_child_head, ad_child_list) { |
705 | #else |
706 | list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) { |
707 | #endif |
708 | if (i >= 8) { |
709 | #ifdef __NetBSD__ |
710 | aprint_error_dev(dev->pdev->pd_dev, |
711 | #else |
712 | dev_dbg(&dev->pdev->dev, |
713 | #endif |
714 | "More than 8 outputs detected via ACPI\n" ); |
715 | return; |
716 | } |
717 | status = |
718 | #ifdef __NetBSD__ |
719 | acpi_eval_integer(child->ad_handle, "_ADR" , &device_id); |
720 | #else |
721 | acpi_evaluate_integer(acpi_cdev->handle, "_ADR" , |
722 | NULL, &device_id); |
723 | #endif |
724 | if (ACPI_SUCCESS(status)) { |
725 | if (!device_id) |
726 | goto blind_set; |
727 | iowrite32((u32)(device_id & 0x0f0f), |
728 | &opregion->acpi->didl[i]); |
729 | i++; |
730 | } |
731 | } |
732 | |
733 | end: |
734 | /* If fewer than 8 outputs, the list must be null terminated */ |
735 | if (i < 8) |
736 | iowrite32(0, &opregion->acpi->didl[i]); |
737 | return; |
738 | |
739 | blind_set: |
740 | i = 0; |
741 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
742 | int output_type = ACPI_OTHER_OUTPUT; |
743 | if (i >= 8) { |
744 | #ifdef __NetBSD__ |
745 | aprint_error_dev(dev->pdev->pd_dev, |
746 | #else |
747 | dev_dbg(&dev->pdev->dev, |
748 | #endif |
749 | "More than 8 outputs in connector list\n" ); |
750 | return; |
751 | } |
752 | switch (connector->connector_type) { |
753 | case DRM_MODE_CONNECTOR_VGA: |
754 | case DRM_MODE_CONNECTOR_DVIA: |
755 | output_type = ACPI_VGA_OUTPUT; |
756 | break; |
757 | case DRM_MODE_CONNECTOR_Composite: |
758 | case DRM_MODE_CONNECTOR_SVIDEO: |
759 | case DRM_MODE_CONNECTOR_Component: |
760 | case DRM_MODE_CONNECTOR_9PinDIN: |
761 | output_type = ACPI_TV_OUTPUT; |
762 | break; |
763 | case DRM_MODE_CONNECTOR_DVII: |
764 | case DRM_MODE_CONNECTOR_DVID: |
765 | case DRM_MODE_CONNECTOR_DisplayPort: |
766 | case DRM_MODE_CONNECTOR_HDMIA: |
767 | case DRM_MODE_CONNECTOR_HDMIB: |
768 | output_type = ACPI_DIGITAL_OUTPUT; |
769 | break; |
770 | case DRM_MODE_CONNECTOR_LVDS: |
771 | output_type = ACPI_LVDS_OUTPUT; |
772 | break; |
773 | } |
774 | temp = ioread32(&opregion->acpi->didl[i]); |
775 | iowrite32(temp | (1<<31) | output_type | i, |
776 | &opregion->acpi->didl[i]); |
777 | i++; |
778 | } |
779 | goto end; |
780 | } |
781 | |
782 | static void intel_setup_cadls(struct drm_device *dev) |
783 | { |
784 | struct drm_i915_private *dev_priv = dev->dev_private; |
785 | struct intel_opregion *opregion = &dev_priv->opregion; |
786 | int i = 0; |
787 | u32 disp_id; |
788 | |
789 | /* Initialize the CADL field by duplicating the DIDL values. |
790 | * Technically, this is not always correct as display outputs may exist, |
791 | * but not active. This initialization is necessary for some Clevo |
792 | * laptops that check this field before processing the brightness and |
793 | * display switching hotkeys. Just like DIDL, CADL is NULL-terminated if |
794 | * there are less than eight devices. */ |
795 | do { |
796 | disp_id = ioread32(&opregion->acpi->didl[i]); |
797 | iowrite32(disp_id, &opregion->acpi->cadl[i]); |
798 | } while (++i < 8 && disp_id != 0); |
799 | } |
800 | |
801 | void intel_opregion_init(struct drm_device *dev) |
802 | { |
803 | struct drm_i915_private *dev_priv = dev->dev_private; |
804 | struct intel_opregion *opregion = &dev_priv->opregion; |
805 | |
806 | if (!opregion->header) |
807 | return; |
808 | |
809 | if (opregion->acpi) { |
810 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { |
811 | intel_didl_outputs(dev); |
812 | intel_setup_cadls(dev); |
813 | } |
814 | |
815 | /* Notify BIOS we are ready to handle ACPI video ext notifs. |
816 | * Right now, all the events are handled by the ACPI video module. |
817 | * We don't actually need to do anything with them. */ |
818 | iowrite32(0, &opregion->acpi->csts); |
819 | iowrite32(1, &opregion->acpi->drdy); |
820 | |
821 | system_opregion = opregion; |
822 | #ifdef __NetBSD__ |
823 | if (dev->pdev->pd_ad != NULL) |
824 | acpi_register_notify(dev->pdev->pd_ad, |
825 | intel_opregion_video_event); |
826 | #else |
827 | register_acpi_notifier(&intel_opregion_notifier); |
828 | #endif |
829 | } |
830 | |
831 | if (opregion->asle) { |
832 | iowrite32(ASLE_TCHE_BLC_EN, &opregion->asle->tche); |
833 | iowrite32(ASLE_ARDY_READY, &opregion->asle->ardy); |
834 | } |
835 | } |
836 | |
837 | void intel_opregion_fini(struct drm_device *dev) |
838 | { |
839 | struct drm_i915_private *dev_priv = dev->dev_private; |
840 | struct intel_opregion *opregion = &dev_priv->opregion; |
841 | |
842 | if (!opregion->header) |
843 | return; |
844 | |
845 | if (opregion->asle) |
846 | iowrite32(ASLE_ARDY_NOT_READY, &opregion->asle->ardy); |
847 | |
848 | cancel_work_sync(&dev_priv->opregion.asle_work); |
849 | |
850 | if (opregion->acpi) { |
851 | iowrite32(0, &opregion->acpi->drdy); |
852 | |
853 | system_opregion = NULL; |
854 | #ifdef __NetBSD__ |
855 | if (dev->pdev->pd_ad != NULL) |
856 | acpi_deregister_notify(dev->pdev->pd_ad); |
857 | #else |
858 | unregister_acpi_notifier(&intel_opregion_notifier); |
859 | #endif |
860 | } |
861 | |
862 | /* just clear all opregion memory pointers now */ |
863 | #ifdef __NetBSD__ |
864 | acpi_os_iounmap(opregion->header, OPREGION_SIZE); |
865 | #else |
866 | iounmap(opregion->header); |
867 | #endif |
868 | opregion->header = NULL; |
869 | opregion->acpi = NULL; |
870 | opregion->swsci = NULL; |
871 | opregion->asle = NULL; |
872 | opregion->vbt = NULL; |
873 | opregion->lid_state = NULL; |
874 | } |
875 | |
876 | static void swsci_setup(struct drm_device *dev) |
877 | { |
878 | struct drm_i915_private *dev_priv = dev->dev_private; |
879 | struct intel_opregion *opregion = &dev_priv->opregion; |
880 | bool requested_callbacks = false; |
881 | u32 tmp; |
882 | |
883 | /* Sub-function code 0 is okay, let's allow them. */ |
884 | opregion->swsci_gbda_sub_functions = 1; |
885 | opregion->swsci_sbcb_sub_functions = 1; |
886 | |
887 | /* We use GBDA to ask for supported GBDA calls. */ |
888 | if (swsci(dev, SWSCI_GBDA_SUPPORTED_CALLS, 0, &tmp) == 0) { |
889 | /* make the bits match the sub-function codes */ |
890 | tmp <<= 1; |
891 | opregion->swsci_gbda_sub_functions |= tmp; |
892 | } |
893 | |
894 | /* |
895 | * We also use GBDA to ask for _requested_ SBCB callbacks. The driver |
896 | * must not call interfaces that are not specifically requested by the |
897 | * bios. |
898 | */ |
899 | if (swsci(dev, SWSCI_GBDA_REQUESTED_CALLBACKS, 0, &tmp) == 0) { |
900 | /* here, the bits already match sub-function codes */ |
901 | opregion->swsci_sbcb_sub_functions |= tmp; |
902 | requested_callbacks = true; |
903 | } |
904 | |
905 | /* |
906 | * But we use SBCB to ask for _supported_ SBCB calls. This does not mean |
907 | * the callback is _requested_. But we still can't call interfaces that |
908 | * are not requested. |
909 | */ |
910 | if (swsci(dev, SWSCI_SBCB_SUPPORTED_CALLBACKS, 0, &tmp) == 0) { |
911 | /* make the bits match the sub-function codes */ |
912 | u32 low = tmp & 0x7ff; |
913 | u32 high = tmp & ~0xfff; /* bit 11 is reserved */ |
914 | tmp = (high << 4) | (low << 1) | 1; |
915 | |
916 | /* best guess what to do with supported wrt requested */ |
917 | if (requested_callbacks) { |
918 | u32 req = opregion->swsci_sbcb_sub_functions; |
919 | if ((req & tmp) != req) |
920 | DRM_DEBUG_DRIVER("SWSCI BIOS requested (%08x) SBCB callbacks that are not supported (%08x)\n" , req, tmp); |
921 | /* XXX: for now, trust the requested callbacks */ |
922 | /* opregion->swsci_sbcb_sub_functions &= tmp; */ |
923 | } else { |
924 | opregion->swsci_sbcb_sub_functions |= tmp; |
925 | } |
926 | } |
927 | |
928 | DRM_DEBUG_DRIVER("SWSCI GBDA callbacks %08x, SBCB callbacks %08x\n" , |
929 | opregion->swsci_gbda_sub_functions, |
930 | opregion->swsci_sbcb_sub_functions); |
931 | } |
932 | #else /* CONFIG_ACPI */ |
933 | static inline void swsci_setup(struct drm_device *dev) {} |
934 | #endif /* CONFIG_ACPI */ |
935 | |
936 | int intel_opregion_setup(struct drm_device *dev) |
937 | { |
938 | struct drm_i915_private *dev_priv = dev->dev_private; |
939 | struct intel_opregion *opregion = &dev_priv->opregion; |
940 | void __iomem *base; |
941 | u32 asls, mboxes; |
942 | char buf[sizeof(OPREGION_SIGNATURE)]; |
943 | int err = 0; |
944 | |
945 | pci_read_config_dword(dev->pdev, PCI_ASLS, &asls); |
946 | DRM_DEBUG_DRIVER("graphic opregion physical addr: 0x%x\n" , asls); |
947 | if (asls == 0) { |
948 | DRM_DEBUG_DRIVER("ACPI OpRegion not supported!\n" ); |
949 | return -ENOTSUPP; |
950 | } |
951 | |
952 | #ifdef CONFIG_ACPI |
953 | INIT_WORK(&opregion->asle_work, asle_work); |
954 | #endif |
955 | |
956 | base = acpi_os_ioremap(asls, OPREGION_SIZE); |
957 | if (!base) |
958 | return -ENOMEM; |
959 | |
960 | memcpy_fromio(buf, base, sizeof(buf)); |
961 | |
962 | if (memcmp(buf, OPREGION_SIGNATURE, 16)) { |
963 | DRM_DEBUG_DRIVER("opregion signature mismatch\n" ); |
964 | err = -EINVAL; |
965 | goto err_out; |
966 | } |
967 | opregion->header = base; |
968 | opregion->vbt = (char *)base + OPREGION_VBT_OFFSET; |
969 | |
970 | opregion->lid_state = (void *)((char *)base + ACPI_CLID); |
971 | |
972 | mboxes = ioread32(&opregion->header->mboxes); |
973 | if (mboxes & MBOX_ACPI) { |
974 | DRM_DEBUG_DRIVER("Public ACPI methods supported\n" ); |
975 | opregion->acpi = (void *)((char *)base + OPREGION_ACPI_OFFSET); |
976 | } |
977 | |
978 | if (mboxes & MBOX_SWSCI) { |
979 | DRM_DEBUG_DRIVER("SWSCI supported\n" ); |
980 | opregion->swsci = (void *)((char *)base + OPREGION_SWSCI_OFFSET); |
981 | swsci_setup(dev); |
982 | } |
983 | if (mboxes & MBOX_ASLE) { |
984 | DRM_DEBUG_DRIVER("ASLE supported\n" ); |
985 | opregion->asle = (void *)((char *)base + OPREGION_ASLE_OFFSET); |
986 | |
987 | iowrite32(ASLE_ARDY_NOT_READY, &opregion->asle->ardy); |
988 | } |
989 | |
990 | return 0; |
991 | |
992 | err_out: |
993 | #ifdef __NetBSD__ |
994 | acpi_os_iounmap(base, OPREGION_SIZE); |
995 | #else |
996 | iounmap(base); |
997 | #endif |
998 | return err; |
999 | } |
1000 | |
1001 | #endif /* CONFIG_ACPI */ |
1002 | |