drm/i915: move power domain init earlier during system resume
authorImre Deak <imre.deak@intel.com>
Tue, 1 Apr 2014 16:55:22 +0000 (19:55 +0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Fri, 4 Apr 2014 07:30:10 +0000 (09:30 +0200)
During resume the intel hda audio driver depends on the i915 driver
reinitializing the audio power domain. Since the order of calling the
i915 resume handler wrt. that of the audio driver is not guaranteed,
move the power domain reinitialization step to the resume_early
handler. This is guaranteed to run before the resume handler of any
other driver.

The power domain initialization in turn requires us to enable the i915
pci device first, so move that part earlier too.

Accordingly disabling of the i915 pci device should happen after the
audio suspend handler ran. So move the disabling later from the i915
resume handler to the resume_late handler.

v2:
- move intel_uncore_sanitize/early_sanitize earlier too, so they don't
  get reordered wrt. intel_power_domains_init_hw()

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=76152
Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Cc: stable@vger.kernel.org
[danvet: Add cc: stable and loud comments that this is just a hack.]
[danvet: Fix "Should it be static?" sparse warning reported by Wu
Fengguang's kbuilder.]
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_drv.c

index 11f77a8e4c4d0b39b952a3fa1004b79f7b552bcb..82f4d1f47d3b734c582b1d2b02b770039fb042fa 100644 (file)
@@ -537,14 +537,21 @@ static void intel_resume_hotplug(struct drm_device *dev)
        drm_helper_hpd_irq_event(dev);
 }
 
-static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
+static int i915_drm_thaw_early(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int error = 0;
 
        intel_uncore_early_sanitize(dev);
-
        intel_uncore_sanitize(dev);
+       intel_power_domains_init_hw(dev_priv);
+
+       return 0;
+}
+
+static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int error = 0;
 
        if (drm_core_check_feature(dev, DRIVER_MODESET) &&
            restore_gtt_mappings) {
@@ -553,8 +560,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
                mutex_unlock(&dev->struct_mutex);
        }
 
-       intel_power_domains_init_hw(dev_priv);
-
        i915_restore_state(dev);
        intel_opregion_setup(dev);
 
@@ -619,19 +624,33 @@ static int i915_drm_thaw(struct drm_device *dev)
        return __i915_drm_thaw(dev, true);
 }
 
-int i915_resume(struct drm_device *dev)
+static int i915_resume_early(struct drm_device *dev)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       int ret;
-
        if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                return 0;
 
+       /*
+        * We have a resume ordering issue with the snd-hda driver also
+        * requiring our device to be power up. Due to the lack of a
+        * parent/child relationship we currently solve this with an early
+        * resume hook.
+        *
+        * FIXME: This should be solved with a special hdmi sink device or
+        * similar so that power domains can be employed.
+        */
        if (pci_enable_device(dev->pdev))
                return -EIO;
 
        pci_set_master(dev->pdev);
 
+       return i915_drm_thaw_early(dev);
+}
+
+int i915_resume(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
        /*
         * Platforms with opregion should have sane BIOS, older ones (gen3 and
         * earlier) need to restore the GTT mappings since the BIOS might clear
@@ -645,6 +664,14 @@ int i915_resume(struct drm_device *dev)
        return 0;
 }
 
+static int i915_resume_legacy(struct drm_device *dev)
+{
+       i915_resume_early(dev);
+       i915_resume(dev);
+
+       return 0;
+}
+
 /**
  * i915_reset - reset chip after a hang
  * @dev: drm device to reset
@@ -776,7 +803,6 @@ static int i915_pm_suspend(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        struct drm_device *drm_dev = pci_get_drvdata(pdev);
-       int error;
 
        if (!drm_dev || !drm_dev->dev_private) {
                dev_err(dev, "DRM not initialized, aborting suspend.\n");
@@ -786,9 +812,25 @@ static int i915_pm_suspend(struct device *dev)
        if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                return 0;
 
-       error = i915_drm_freeze(drm_dev);
-       if (error)
-               return error;
+       return i915_drm_freeze(drm_dev);
+}
+
+static int i915_pm_suspend_late(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+       /*
+        * We have a suspedn ordering issue with the snd-hda driver also
+        * requiring our device to be power up. Due to the lack of a
+        * parent/child relationship we currently solve this with an late
+        * suspend hook.
+        *
+        * FIXME: This should be solved with a special hdmi sink device or
+        * similar so that power domains can be employed.
+        */
+       if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+               return 0;
 
        pci_disable_device(pdev);
        pci_set_power_state(pdev, PCI_D3hot);
@@ -796,6 +838,14 @@ static int i915_pm_suspend(struct device *dev)
        return 0;
 }
 
+static int i915_pm_resume_early(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+       return i915_resume_early(drm_dev);
+}
+
 static int i915_pm_resume(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
@@ -817,6 +867,14 @@ static int i915_pm_freeze(struct device *dev)
        return i915_drm_freeze(drm_dev);
 }
 
+static int i915_pm_thaw_early(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+       return i915_drm_thaw_early(drm_dev);
+}
+
 static int i915_pm_thaw(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
@@ -887,10 +945,14 @@ static int i915_runtime_resume(struct device *device)
 
 static const struct dev_pm_ops i915_pm_ops = {
        .suspend = i915_pm_suspend,
+       .suspend_late = i915_pm_suspend_late,
+       .resume_early = i915_pm_resume_early,
        .resume = i915_pm_resume,
        .freeze = i915_pm_freeze,
+       .thaw_early = i915_pm_thaw_early,
        .thaw = i915_pm_thaw,
        .poweroff = i915_pm_poweroff,
+       .restore_early = i915_pm_resume_early,
        .restore = i915_pm_resume,
        .runtime_suspend = i915_runtime_suspend,
        .runtime_resume = i915_runtime_resume,
@@ -933,7 +995,7 @@ static struct drm_driver driver = {
 
        /* Used in place of i915_pm_ops for non-DRIVER_MODESET */
        .suspend = i915_suspend,
-       .resume = i915_resume,
+       .resume = i915_resume_legacy,
 
        .device_is_agp = i915_driver_device_is_agp,
        .master_create = i915_master_create,