[Intel-gfx] [PATCH] drm/i915: add plumbing for SWSCI
Paulo Zanoni
przanoni at gmail.com
Thu Aug 29 16:57:12 CEST 2013
2013/8/29 Paulo Zanoni <przanoni at gmail.com>:
> Hi
>
> 2013/8/28 Jani Nikula <jani.nikula at intel.com>:
>> 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
>>
>> Signed-off-by: Jani Nikula <jani.nikula at intel.com>
>> ---
>> drivers/gpu/drm/i915/i915_drv.h | 1 +
>> drivers/gpu/drm/i915/intel_opregion.c | 133 ++++++++++++++++++++++++++++++++-
>> 2 files changed, 131 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
>> index 84b95b1..adc2f46 100644
>> --- a/drivers/gpu/drm/i915/i915_drv.h
>> +++ b/drivers/gpu/drm/i915/i915_drv.h
>> @@ -225,6 +225,7 @@ struct intel_opregion {
>> struct opregion_header __iomem *header;
>> struct opregion_acpi __iomem *acpi;
>> struct opregion_swsci __iomem *swsci;
>> + u32 swsci_requested_callbacks;
>> struct opregion_asle __iomem *asle;
>> void __iomem *vbt;
>> u32 __iomem *lid_state;
>> diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
>> index cfb8fb6..233cc7f 100644
>> --- a/drivers/gpu/drm/i915/intel_opregion.c
>> +++ b/drivers/gpu/drm/i915/intel_opregion.c
>> @@ -36,8 +36,11 @@
>> #include "i915_drv.h"
>> #include "intel_drv.h"
>>
>> -#define PCI_ASLE 0xe4
>> -#define PCI_ASLS 0xfc
>> +#define PCI_ASLE 0xe4
>> +#define PCI_ASLS 0xfc
>> +#define PCI_SWSCI 0xe8
>> +#define PCI_SWSCI_SCISEL (1 << 15)
>> +#define PCI_SWSCI_GSSCIE (1 << 0)
>>
>> #define OPREGION_HEADER_OFFSET 0
>> #define OPREGION_ACPI_OFFSET 0x100
>> @@ -151,6 +154,48 @@ struct opregion_asle {
>>
>> #define ASLE_CBLV_VALID (1<<31)
>>
>> +/* Software System Control Interrupt (SWSCI) */
>> +#define SWSCI_SCIC_INDICATOR (1 << 0)
>> +#define SWSCI_SCIC_MAIN_FUNCTION_SHIFT 1
>> +#define SWSCI_SCIC_MAIN_FUNCTION_MASK (0xf << 1)
>> +#define SWSCI_SCIC_SUB_FUNCTION_SHIFT 8
>> +#define SWSCI_SCIC_SUB_FUNCTION_MASK (0x7f << 8)
>
> Shouldn't this be (0xff << 8) since it's bits 15:8 ?
>
>
>> +#define SWSCI_SCIC_EXIT_STATUS_SHIFT 5
>> +#define SWSCI_SCIC_EXIT_STATUS_MASK (7 << 5)
>> +#define SWSCI_SCIC_EXIT_STATUS_SUCCESS 1
>> +
>> +#define SWSCI_FUNCTION_CODE(main, sub) \
>> + ((main) << SWSCI_SCIC_MAIN_FUNCTION_SHIFT | \
>> + (sub) << SWSCI_SCIC_SUB_FUNCTION_SHIFT)
>> +
>> +/* SWSCI: Get BIOS Data (GBDA) */
>> +#define SWSCI_GBDA 4
>> +#define SWSCI_GBDA_SUPPORTED_CALLS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 0)
>> +#define SWSCI_GBDA_REQUESTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 1)
>> +#define SWSCI_GBDA_BOOT_DISPLAY_PREF SWSCI_FUNCTION_CODE(SWSCI_GBDA, 4)
>> +#define SWSCI_GBDA_PANEL_DETAILS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 5)
>> +#define SWSCI_GBDA_TV_STANDARD SWSCI_FUNCTION_CODE(SWSCI_GBDA, 6)
>
> The newer spec says "reserved" here. But let's leave your definition
> until they assign it again to something else :) (same thing for the
> other TV bit below)
>
>
>> +#define SWSCI_GBDA_INTERNAL_GRAPHICS SWSCI_FUNCTION_CODE(SWSCI_GBDA, 7)
>> +#define SWSCI_GBDA_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_GBDA, 10)
>> +
>> +/* SWSCI: System BIOS Callbacks (SBCB) */
>> +#define SWSCI_SBCB 6
>> +#define SWSCI_SBCB_SUPPORTED_CALLBACKS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 0)
>> +#define SWSCI_SBCB_INIT_COMPLETION SWSCI_FUNCTION_CODE(SWSCI_SBCB, 1)
>> +#define SWSCI_SBCB_PRE_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 3)
>> +#define SWSCI_SBCB_POST_HIRES_SET_MODE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 4)
>> +#define SWSCI_SBCB_DISPLAY_SWITCH SWSCI_FUNCTION_CODE(SWSCI_SBCB, 5)
>> +#define SWSCI_SBCB_SET_TV_FORMAT SWSCI_FUNCTION_CODE(SWSCI_SBCB, 6)
>> +#define SWSCI_SBCB_ADAPTER_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 7)
>> +#define SWSCI_SBCB_DISPLAY_POWER_STATE SWSCI_FUNCTION_CODE(SWSCI_SBCB, 8)
>> +#define SWSCI_SBCB_SET_BOOT_DISPLAY SWSCI_FUNCTION_CODE(SWSCI_SBCB, 9)
>> +#define SWSCI_SBCB_SET_INTERNAL_GFX SWSCI_FUNCTION_CODE(SWSCI_SBCB, 11)
>> +#define SWSCI_SBCB_POST_HIRES_TO_DOS_FS SWSCI_FUNCTION_CODE(SWSCI_SBCB, 16)
>> +#define SWSCI_SBCB_SUSPEND_RESUME SWSCI_FUNCTION_CODE(SWSCI_SBCB, 17)
>> +#define SWSCI_SBCB_SET_SPREAD_SPECTRUM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 18)
>> +#define SWSCI_SBCB_POST_VBE_PM SWSCI_FUNCTION_CODE(SWSCI_SBCB, 19)
>> +#define SWSCI_SBCB_ENABLE_DISABLE_AUDIO SWSCI_FUNCTION_CODE(SWSCI_SBCB, 21)
>> +
>> #define ACPI_OTHER_OUTPUT (0<<8)
>> #define ACPI_VGA_OUTPUT (1<<8)
>> #define ACPI_TV_OUTPUT (2<<8)
>> @@ -158,6 +203,84 @@ struct opregion_asle {
>> #define ACPI_LVDS_OUTPUT (4<<8)
>>
>> #ifdef CONFIG_ACPI
>> +static int swsci(struct drm_device *dev, u32 function, u32 parm, u32 *parm_out)
>> +{
>> + struct drm_i915_private *dev_priv = dev->dev_private;
>> + struct opregion_swsci __iomem *swsci = dev_priv->opregion.swsci;
>> + u32 main_function, sub_function, scic;
>> + u16 pci_swsci;
>> +
>> + if (!swsci)
>> + return 0;
>
> You also use "return 0" for success below. I'd return -ESOMETHING
> here. Perhaps also print an error message.
>
>
>> +
>> + main_function = (function & SWSCI_SCIC_MAIN_FUNCTION_MASK) >>
>> + SWSCI_SCIC_MAIN_FUNCTION_SHIFT;
>> + sub_function = (function & SWSCI_SCIC_SUB_FUNCTION_MASK) >>
>> + SWSCI_SCIC_SUB_FUNCTION_SHIFT;
>> +
>> + if (main_function == SWSCI_SBCB && sub_function != 0) {
>> + /*
>> + * SBCB sub-function codes correspond to the bits in requested
>> + * callbacks, except for bit 0 which is reserved. The driver
>> + * must not call interfaces that are not specifically requested
>> + * by the bios.
>> + */
>> + if ((dev_priv->opregion.swsci_requested_callbacks &
>> + (1 << sub_function)) == 0)
>
> I think you have to do (1 << sub_funcition -1) to make it correct.
>
>
>> + return 0;
>
> I'd return -ESOMETHINGELSE here too, possibly printing an error message.
>
>
>> + }
>> +
>> + /* XXX: The spec tells us to do this, but we are the only user... */
>
> I'd remove the "XXX: " substring from the comment. The spec suggests
> only graphics uses this specific interface, but it doesn't say that
> other events happening on the system won't cause this bit to be
> flipped. I don't know... the code seems correct, so the "XXX" code may
> be misleading.
>
> The sad thing is that there's also no guarantees that someone won't
> start using the interface just after you read this bit and before
> writing it to 1... We may need dev_priv->swsci_lock depending on how
> we use this function (I haven't checked the next patches yet).
>
>
>> + scic = ioread32(&swsci->scic);
>> + if (scic & SWSCI_SCIC_INDICATOR) {
>> + DRM_DEBUG_DRIVER("SWSCI request already in progress\n");
>> + return -EBUSY;
>> + }
>> +
>> + scic = function | SWSCI_SCIC_INDICATOR;
>> +
>> + iowrite32(parm, &swsci->parm);
>> + iowrite32(scic, &swsci->scic);
>> +
>
>
> From what I understood of the spec, from the line below...
>
>> + /* Ensure SCI event is selected and event trigger is cleared. */
>> + pci_read_config_word(dev->pdev, PCI_SWSCI, &pci_swsci);
>> + if (!(pci_swsci & PCI_SWSCI_SCISEL) || (pci_swsci & PCI_SWSCI_GSSCIE)) {
>> + pci_swsci |= PCI_SWSCI_SCISEL;
>> + pci_swsci &= ~PCI_SWSCI_GSSCIE;
>> + pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci);
>> + }
>> +
>> + /* Use event trigger to tell bios to check the mail. */
>> + pci_swsci |= PCI_SWSCI_GSSCIE;
>> + pci_write_config_word(dev->pdev, PCI_SWSCI, pci_swsci);
>
> ... to the line above, we don't need this code. My understanding is
> that the BIOS will do all these things automagically for us, so if we
> write these regs we may even be racing with it, especially since we
> already wrote swsci->scic. Do we really need this code?
>
>
>> +
>> + /*
>> + * Poll for the result. The spec says nothing about timing out; add an
>> + * arbitrary timeout to not hang if the bios fails.
>> + */
>
> The spec does mention a timeout, check the description of DSLP: Driver
> Sleep Timeout.
>
>
>> +#define C (((scic = ioread32(&swsci->scic)) & SWSCI_SCIC_INDICATOR) == 0)
>> + if (wait_for(C, 500)) {
>> + DRM_DEBUG_DRIVER("SWSCI request timed out\n");
>> + return -ETIMEDOUT;
>> + }
>> +
>> + scic = (scic & SWSCI_SCIC_EXIT_STATUS_MASK) >>
>> + SWSCI_SCIC_EXIT_STATUS_SHIFT;
>> +
>> + /* Note: scic == 0 is an error! */
>> + if (scic != SWSCI_SCIC_EXIT_STATUS_SUCCESS) {
>> + DRM_DEBUG_DRIVER("SWSCI request error %u\n", scic);
>> + return -EINVAL; /* XXX */
>
> Why XXX above? To me, a XXX means there's something on the code that
> must be fixed.
>
>
>> + }
>> +
>> + if (parm_out)
>> + *parm_out = ioread32(&swsci->parm);
>> +
>> + return 0;
>> +
>> +#undef C
>> +}
>> +
>> static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
>> {
>> struct drm_i915_private *dev_priv = dev->dev_private;
>> @@ -488,8 +611,12 @@ int intel_opregion_setup(struct drm_device *dev)
>> }
>>
>> if (mboxes & MBOX_SWSCI) {
>> - DRM_DEBUG_DRIVER("SWSCI supported\n");
>> opregion->swsci = base + OPREGION_SWSCI_OFFSET;
>> +
>> + swsci(dev, SWSCI_GBDA_REQUESTED_CALLBACKS, 0,
>> + &opregion->swsci_requested_callbacks);
>
> No error checking?
Also, the "swsci" declaration is under "#ifdef CONFIG_ACPI", but not this call.
>
>
>> + DRM_DEBUG_DRIVER("SWSCI supported, requested callbacks %08x\n",
>> + opregion->swsci_requested_callbacks);
>> }
>> if (mboxes & MBOX_ASLE) {
>> DRM_DEBUG_DRIVER("ASLE supported\n");
>> --
>> 1.7.10.4
>>
>
>
>
> --
> Paulo Zanoni
--
Paulo Zanoni
More information about the Intel-gfx
mailing list