Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/i915: move power domain init earlier during system resume

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>

authored by

Imre Deak and committed by
Daniel Vetter
76c4b250 bc104d1f

+77 -15
+77 -15
drivers/gpu/drm/i915/i915_drv.c
··· 537 537 drm_helper_hpd_irq_event(dev); 538 538 } 539 539 540 + static int i915_drm_thaw_early(struct drm_device *dev) 541 + { 542 + struct drm_i915_private *dev_priv = dev->dev_private; 543 + 544 + intel_uncore_early_sanitize(dev); 545 + intel_uncore_sanitize(dev); 546 + intel_power_domains_init_hw(dev_priv); 547 + 548 + return 0; 549 + } 550 + 540 551 static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) 541 552 { 542 553 struct drm_i915_private *dev_priv = dev->dev_private; 543 554 int error = 0; 544 - 545 - intel_uncore_early_sanitize(dev); 546 - 547 - intel_uncore_sanitize(dev); 548 555 549 556 if (drm_core_check_feature(dev, DRIVER_MODESET) && 550 557 restore_gtt_mappings) { ··· 559 552 i915_gem_restore_gtt_mappings(dev); 560 553 mutex_unlock(&dev->struct_mutex); 561 554 } 562 - 563 - intel_power_domains_init_hw(dev_priv); 564 555 565 556 i915_restore_state(dev); 566 557 intel_opregion_setup(dev); ··· 624 619 return __i915_drm_thaw(dev, true); 625 620 } 626 621 627 - int i915_resume(struct drm_device *dev) 622 + static int i915_resume_early(struct drm_device *dev) 628 623 { 629 - struct drm_i915_private *dev_priv = dev->dev_private; 630 - int ret; 631 - 632 624 if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) 633 625 return 0; 634 626 627 + /* 628 + * We have a resume ordering issue with the snd-hda driver also 629 + * requiring our device to be power up. Due to the lack of a 630 + * parent/child relationship we currently solve this with an early 631 + * resume hook. 632 + * 633 + * FIXME: This should be solved with a special hdmi sink device or 634 + * similar so that power domains can be employed. 635 + */ 635 636 if (pci_enable_device(dev->pdev)) 636 637 return -EIO; 637 638 638 639 pci_set_master(dev->pdev); 640 + 641 + return i915_drm_thaw_early(dev); 642 + } 643 + 644 + int i915_resume(struct drm_device *dev) 645 + { 646 + struct drm_i915_private *dev_priv = dev->dev_private; 647 + int ret; 639 648 640 649 /* 641 650 * Platforms with opregion should have sane BIOS, older ones (gen3 and ··· 661 642 return ret; 662 643 663 644 drm_kms_helper_poll_enable(dev); 645 + return 0; 646 + } 647 + 648 + static int i915_resume_legacy(struct drm_device *dev) 649 + { 650 + i915_resume_early(dev); 651 + i915_resume(dev); 652 + 664 653 return 0; 665 654 } 666 655 ··· 803 776 { 804 777 struct pci_dev *pdev = to_pci_dev(dev); 805 778 struct drm_device *drm_dev = pci_get_drvdata(pdev); 806 - int error; 807 779 808 780 if (!drm_dev || !drm_dev->dev_private) { 809 781 dev_err(dev, "DRM not initialized, aborting suspend.\n"); ··· 812 786 if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) 813 787 return 0; 814 788 815 - error = i915_drm_freeze(drm_dev); 816 - if (error) 817 - return error; 789 + return i915_drm_freeze(drm_dev); 790 + } 791 + 792 + static int i915_pm_suspend_late(struct device *dev) 793 + { 794 + struct pci_dev *pdev = to_pci_dev(dev); 795 + struct drm_device *drm_dev = pci_get_drvdata(pdev); 796 + 797 + /* 798 + * We have a suspedn ordering issue with the snd-hda driver also 799 + * requiring our device to be power up. Due to the lack of a 800 + * parent/child relationship we currently solve this with an late 801 + * suspend hook. 802 + * 803 + * FIXME: This should be solved with a special hdmi sink device or 804 + * similar so that power domains can be employed. 805 + */ 806 + if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) 807 + return 0; 818 808 819 809 pci_disable_device(pdev); 820 810 pci_set_power_state(pdev, PCI_D3hot); 821 811 822 812 return 0; 813 + } 814 + 815 + static int i915_pm_resume_early(struct device *dev) 816 + { 817 + struct pci_dev *pdev = to_pci_dev(dev); 818 + struct drm_device *drm_dev = pci_get_drvdata(pdev); 819 + 820 + return i915_resume_early(drm_dev); 823 821 } 824 822 825 823 static int i915_pm_resume(struct device *dev) ··· 865 815 } 866 816 867 817 return i915_drm_freeze(drm_dev); 818 + } 819 + 820 + static int i915_pm_thaw_early(struct device *dev) 821 + { 822 + struct pci_dev *pdev = to_pci_dev(dev); 823 + struct drm_device *drm_dev = pci_get_drvdata(pdev); 824 + 825 + return i915_drm_thaw_early(drm_dev); 868 826 } 869 827 870 828 static int i915_pm_thaw(struct device *dev) ··· 945 887 946 888 static const struct dev_pm_ops i915_pm_ops = { 947 889 .suspend = i915_pm_suspend, 890 + .suspend_late = i915_pm_suspend_late, 891 + .resume_early = i915_pm_resume_early, 948 892 .resume = i915_pm_resume, 949 893 .freeze = i915_pm_freeze, 894 + .thaw_early = i915_pm_thaw_early, 950 895 .thaw = i915_pm_thaw, 951 896 .poweroff = i915_pm_poweroff, 897 + .restore_early = i915_pm_resume_early, 952 898 .restore = i915_pm_resume, 953 899 .runtime_suspend = i915_runtime_suspend, 954 900 .runtime_resume = i915_runtime_resume, ··· 995 933 996 934 /* Used in place of i915_pm_ops for non-DRIVER_MODESET */ 997 935 .suspend = i915_suspend, 998 - .resume = i915_resume, 936 + .resume = i915_resume_legacy, 999 937 1000 938 .device_is_agp = i915_driver_device_is_agp, 1001 939 .master_create = i915_master_create,