aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_opregion.c
diff options
context:
space:
mode:
authorJani Nikula <jani.nikula@intel.com>2013-09-02 03:38:59 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2013-09-04 11:34:55 -0400
commitebde53c7bc0f1a56b67799c60c47dc89e0875c6a (patch)
treedc6a3de75985fec0bd28478cef28465c61f04e1f /drivers/gpu/drm/i915/intel_opregion.c
parent508842a0366253cf99803277dfba837d3decfeeb (diff)
drm/i915: add plumbing for SWSCI
SWSCI is a driver to bios call interface. This checks for SWSCI availability and bios requested callbacks, and filters out any calls that shouldn't happen. This way the callers don't need to do the checks all over the place. v2: silence some checkpatch nagging v3: set PCI_SWSCI bit 0 to trigger interrupt (Mengdong Lin) v4: remove an extra #define (Jesse) v5: spec says s/w is responsible for clearing PCI_SWSCI bit 0 too v6: per Paulo's review and more: - fix sub-function mask - add exit parameter - add define for set panel details call - return more errors from swsci - clean up the supported/requested callbacks bit masks mess - use DSLP for timeout - fix build for CONFIG_ACPI=n v7: tiny adjustment of requested vs. supported SBCB callbacks handling (Paulo) Signed-off-by: Jani Nikula <jani.nikula@intel.com> Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_opregion.c')
-rw-r--r--drivers/gpu/drm/i915/intel_opregion.c199
1 files changed, 196 insertions, 3 deletions
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index cfb8fb68f09c..c45fec5b5521 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -36,8 +36,11 @@
36#include "i915_drv.h" 36#include "i915_drv.h"
37#include "intel_drv.h" 37#include "intel_drv.h"
38 38
39#define PCI_ASLE 0xe4 39#define PCI_ASLE 0xe4
40#define PCI_ASLS 0xfc 40#define PCI_ASLS 0xfc
41#define PCI_SWSCI 0xe8
42#define PCI_SWSCI_SCISEL (1 << 15)
43#define PCI_SWSCI_GSSCIE (1 << 0)
41 44
42#define OPREGION_HEADER_OFFSET 0 45#define OPREGION_HEADER_OFFSET 0
43#define OPREGION_ACPI_OFFSET 0x100 46#define OPREGION_ACPI_OFFSET 0x100
@@ -151,6 +154,51 @@ struct opregion_asle {
151 154
152#define ASLE_CBLV_VALID (1<<31) 155#define ASLE_CBLV_VALID (1<<31)
153 156
157/* Software System Control Interrupt (SWSCI) */
158#define SWSCI_SCIC_INDICATOR (1 << 0)
159#define SWSCI_SCIC_MAIN_FUNCTION_SHIFT 1
160#define SWSCI_SCIC_MAIN_FUNCTION_MASK (0xf << 1)
161#define SWSCI_SCIC_SUB_FUNCTION_SHIFT 8
162#define SWSCI_SCIC_SUB_FUNCTION_MASK (0xff << 8)
163#define SWSCI_SCIC_EXIT_PARAMETER_SHIFT 8
164#define SWSCI_SCIC_EXIT_PARAMETER_MASK (0xff << 8)
165#define SWSCI_SCIC_EXIT_STATUS_SHIFT 5
166#define SWSCI_SCIC_EXIT_STATUS_MASK (7 << 5)
167#define SWSCI_SCIC_EXIT_STATUS_SUCCESS 1
168
169#define SWSCI_FUNCTION_CODE(main, sub) \
170 ((main) << SWSCI_SCIC_MAIN_FUNCTION_SHIFT | \
171 (sub) << SWSCI_SCIC_SUB_FUNCTION_SHIFT)
172
173/* SWSCI: Get BIOS Data (GBDA) */
174#define SWSCI_GBDA 4
175#define SWSCI_GBDA_SUPPORTED_CALLS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 0)
176#define SWSCI_GBDA_REQUESTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 1)
177#define SWSCI_GBDA_BOOT_DISPLAY_PREF SWSCI_FUNCTION_CODE(SWSCI_GBDA, 4)
178#define SWSCI_GBDA_PANEL_DETAILS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 5)
179#define SWSCI_GBDA_TV_STANDARD SWSCI_FUNCTION_CODE(SWSCI_GBDA, 6)
180#define SWSCI_GBDA_INTERNAL_GRAPHICS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 7)
181#define SWSCI_GBDA_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_GBDA, 10)
182
183/* SWSCI: System BIOS Callbacks (SBCB) */
184#define SWSCI_SBCB 6
185#define SWSCI_SBCB_SUPPORTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 0)
186#define SWSCI_SBCB_INIT_COMPLETION SWSCI_FUNCTION_CODE(SWSCI_SBCB, 1)
187#define SWSCI_SBCB_PRE_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 3)
188#define SWSCI_SBCB_POST_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 4)
189#define SWSCI_SBCB_DISPLAY_SWITCH SWSCI_FUNCTION_CODE(SWSCI_SBCB, 5)
190#define SWSCI_SBCB_SET_TV_FORMAT SWSCI_FUNCTION_CODE(SWSCI_SBCB, 6)
191#define SWSCI_SBCB_ADAPTER_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 7)
192#define SWSCI_SBCB_DISPLAY_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 8)
193#define SWSCI_SBCB_SET_BOOT_DISPLAY SWSCI_FUNCTION_CODE(SWSCI_SBCB, 9)
194#define SWSCI_SBCB_SET_PANEL_DETAILS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 10)
195#define SWSCI_SBCB_SET_INTERNAL_GFX SWSCI_FUNCTION_CODE(SWSCI_SBCB, 11)
196#define SWSCI_SBCB_POST_HIRES_TO_DOS_FS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 16)
197#define SWSCI_SBCB_SUSPEND_RESUME SWSCI_FUNCTION_CODE(SWSCI_SBCB, 17)
198#define SWSCI_SBCB_SET_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 18)
199#define SWSCI_SBCB_POST_VBE_PM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 19)
200#define SWSCI_SBCB_ENABLE_DISABLE_AUDIO SWSCI_FUNCTION_CODE(SWSCI_SBCB, 21)
201
154#define ACPI_OTHER_OUTPUT (0<<8) 202#define ACPI_OTHER_OUTPUT (0<<8)
155#define ACPI_VGA_OUTPUT (1<<8) 203#define ACPI_VGA_OUTPUT (1<<8)
156#define ACPI_TV_OUTPUT (2<<8) 204#define ACPI_TV_OUTPUT (2<<8)
@@ -158,6 +206,91 @@ struct opregion_asle {
158#define ACPI_LVDS_OUTPUT (4<<8) 206#define ACPI_LVDS_OUTPUT (4<<8)
159 207
160#ifdef CONFIG_ACPI 208#ifdef CONFIG_ACPI
209static int swsci(struct drm_device *dev, u32 function, u32 parm, u32 *parm_out)
210{
211 struct drm_i915_private *dev_priv = dev->dev_private;
212 struct opregion_swsci __iomem *swsci = dev_priv->opregion.swsci;
213 u32 main_function, sub_function, scic;
214 u16 pci_swsci;
215 u32 dslp;
216
217 if (!swsci)
218 return -ENODEV;
219
220 main_function = (function & SWSCI_SCIC_MAIN_FUNCTION_MASK) >>
221 SWSCI_SCIC_MAIN_FUNCTION_SHIFT;
222 sub_function = (function & SWSCI_SCIC_SUB_FUNCTION_MASK) >>
223 SWSCI_SCIC_SUB_FUNCTION_SHIFT;
224
225 /* Check if we can call the function. See swsci_setup for details. */
226 if (main_function == SWSCI_SBCB) {
227 if ((dev_priv->opregion.swsci_sbcb_sub_functions &
228 (1 << sub_function)) == 0)
229 return -EINVAL;
230 } else if (main_function == SWSCI_GBDA) {
231 if ((dev_priv->opregion.swsci_gbda_sub_functions &
232 (1 << sub_function)) == 0)
233 return -EINVAL;
234 }
235
236 /* Driver sleep timeout in ms. */
237 dslp = ioread32(&swsci->dslp);
238 if (!dslp) {
239 dslp = 2;
240 } else if (dslp > 500) {
241 /* Hey bios, trust must be earned. */
242 WARN_ONCE(1, "excessive driver sleep timeout (DSPL) %u\n", dslp);
243 dslp = 500;
244 }
245
246 /* The spec tells us to do this, but we are the only user... */
247 scic = ioread32(&swsci->scic);
248 if (scic & SWSCI_SCIC_INDICATOR) {
249 DRM_DEBUG_DRIVER("SWSCI request already in progress\n");
250 return -EBUSY;
251 }
252
253 scic = function | SWSCI_SCIC_INDICATOR;
254
255 iowrite32(parm, &swsci->parm);
256 iowrite32(scic, &swsci->scic);
257
258 /* Ensure SCI event is selected and event trigger is cleared. */
259 pci_read_config_word(dev->pdev, PCI_SWSCI, &pci_swsci);
260 if (!(pci_swsci & PCI_SWSCI_SCISEL) || (pci_swsci & PCI_SWSCI_GSSCIE)) {
261 pci_swsci |= PCI_SWSCI_SCISEL;
262 pci_swsci &= ~PCI_SWSCI_GSSCIE;
263 pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci);
264 }
265
266 /* Use event trigger to tell bios to check the mail. */
267 pci_swsci |= PCI_SWSCI_GSSCIE;
268 pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci);
269
270 /* Poll for the result. */
271#define C (((scic = ioread32(&swsci->scic)) & SWSCI_SCIC_INDICATOR) == 0)
272 if (wait_for(C, dslp)) {
273 DRM_DEBUG_DRIVER("SWSCI request timed out\n");
274 return -ETIMEDOUT;
275 }
276
277 scic = (scic & SWSCI_SCIC_EXIT_STATUS_MASK) >>
278 SWSCI_SCIC_EXIT_STATUS_SHIFT;
279
280 /* Note: scic == 0 is an error! */
281 if (scic != SWSCI_SCIC_EXIT_STATUS_SUCCESS) {
282 DRM_DEBUG_DRIVER("SWSCI request error %u\n", scic);
283 return -EIO;
284 }
285
286 if (parm_out)
287 *parm_out = ioread32(&swsci->parm);
288
289 return 0;
290
291#undef C
292}
293
161static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) 294static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
162{ 295{
163 struct drm_i915_private *dev_priv = dev->dev_private; 296 struct drm_i915_private *dev_priv = dev->dev_private;
@@ -447,7 +580,66 @@ void intel_opregion_fini(struct drm_device *dev)
447 opregion->asle = NULL; 580 opregion->asle = NULL;
448 opregion->vbt = NULL; 581 opregion->vbt = NULL;
449} 582}
450#endif 583
584static void swsci_setup(struct drm_device *dev)
585{
586 struct drm_i915_private *dev_priv = dev->dev_private;
587 struct intel_opregion *opregion = &dev_priv->opregion;
588 bool requested_callbacks = false;
589 u32 tmp;
590
591 /* Sub-function code 0 is okay, let's allow them. */
592 opregion->swsci_gbda_sub_functions = 1;
593 opregion->swsci_sbcb_sub_functions = 1;
594
595 /* We use GBDA to ask for supported GBDA calls. */
596 if (swsci(dev, SWSCI_GBDA_SUPPORTED_CALLS, 0, &tmp) == 0) {
597 /* make the bits match the sub-function codes */
598 tmp <<= 1;
599 opregion->swsci_gbda_sub_functions |= tmp;
600 }
601
602 /*
603 * We also use GBDA to ask for _requested_ SBCB callbacks. The driver
604 * must not call interfaces that are not specifically requested by the
605 * bios.
606 */
607 if (swsci(dev, SWSCI_GBDA_REQUESTED_CALLBACKS, 0, &tmp) == 0) {
608 /* here, the bits already match sub-function codes */
609 opregion->swsci_sbcb_sub_functions |= tmp;
610 requested_callbacks = true;
611 }
612
613 /*
614 * But we use SBCB to ask for _supported_ SBCB calls. This does not mean
615 * the callback is _requested_. But we still can't call interfaces that
616 * are not requested.
617 */
618 if (swsci(dev, SWSCI_SBCB_SUPPORTED_CALLBACKS, 0, &tmp) == 0) {
619 /* make the bits match the sub-function codes */
620 u32 low = tmp & 0x7ff;
621 u32 high = tmp & ~0xfff; /* bit 11 is reserved */
622 tmp = (high << 4) | (low << 1) | 1;
623
624 /* best guess what to do with supported wrt requested */
625 if (requested_callbacks) {
626 u32 req = opregion->swsci_sbcb_sub_functions;
627 if ((req & tmp) != req)
628 DRM_DEBUG_DRIVER("SWSCI BIOS requested (%08x) SBCB callbacks that are not supported (%08x)\n", req, tmp);
629 /* XXX: for now, trust the requested callbacks */
630 /* opregion->swsci_sbcb_sub_functions &= tmp; */
631 } else {
632 opregion->swsci_sbcb_sub_functions |= tmp;
633 }
634 }
635
636 DRM_DEBUG_DRIVER("SWSCI GBDA callbacks %08x, SBCB callbacks %08x\n",
637 opregion->swsci_gbda_sub_functions,
638 opregion->swsci_sbcb_sub_functions);
639}
640#else /* CONFIG_ACPI */
641static inline void swsci_setup(struct drm_device *dev) {}
642#endif /* CONFIG_ACPI */
451 643
452int intel_opregion_setup(struct drm_device *dev) 644int intel_opregion_setup(struct drm_device *dev)
453{ 645{
@@ -490,6 +682,7 @@ int intel_opregion_setup(struct drm_device *dev)
490 if (mboxes & MBOX_SWSCI) { 682 if (mboxes & MBOX_SWSCI) {
491 DRM_DEBUG_DRIVER("SWSCI supported\n"); 683 DRM_DEBUG_DRIVER("SWSCI supported\n");
492 opregion->swsci = base + OPREGION_SWSCI_OFFSET; 684 opregion->swsci = base + OPREGION_SWSCI_OFFSET;
685 swsci_setup(dev);
493 } 686 }
494 if (mboxes & MBOX_ASLE) { 687 if (mboxes & MBOX_ASLE) {
495 DRM_DEBUG_DRIVER("ASLE supported\n"); 688 DRM_DEBUG_DRIVER("ASLE supported\n");