static int intel_runtime_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct drm_device *dev = pci_get_drvdata(pdev); struct drm_i915_private *dev_priv = dev->dev_private; int ret; if (WARN_ON_ONCE(!(dev_priv->rps.enabled && intel_enable_rc6(dev)))) return -ENODEV; if (WARN_ON_ONCE(!HAS_RUNTIME_PM(dev))) return -ENODEV; DRM_DEBUG_KMS("Suspending device\n"); /* * We could deadlock here in case another thread holding struct_mutex * calls RPM suspend concurrently, since the RPM suspend will wait * first for this RPM suspend to finish. In this case the concurrent * RPM resume will be followed by its RPM suspend counterpart. Still * for consistency return -EAGAIN, which will reschedule this suspend. */ if (!mutex_trylock(&dev->struct_mutex)) { DRM_DEBUG_KMS("device lock contention, deffering suspend\n"); /* * Bump the expiration timestamp, otherwise the suspend won't * be rescheduled. */ pm_runtime_mark_last_busy(device); return -EAGAIN; } disable_rpm_wakeref_asserts(dev_priv); /* * We are safe here against re-faults, since the fault handler takes * an RPM reference. */ i915_gem_release_all_mmaps(dev_priv); mutex_unlock(&dev->struct_mutex); cancel_delayed_work_sync(&dev_priv->gpu_error.hangcheck_work); intel_guc_suspend(dev); intel_suspend_gt_powersave(dev); intel_runtime_pm_disable_interrupts(dev_priv); ret = intel_suspend_complete(dev_priv); if (ret) { DRM_ERROR("Runtime suspend failed, disabling it (%d)\n", ret); intel_runtime_pm_enable_interrupts(dev_priv); enable_rpm_wakeref_asserts(dev_priv); return ret; } intel_uncore_forcewake_reset(dev, false); enable_rpm_wakeref_asserts(dev_priv); WARN_ON_ONCE(atomic_read(&dev_priv->pm.wakeref_count)); if (intel_uncore_arm_unclaimed_mmio_detection(dev_priv)) DRM_ERROR("Unclaimed access detected prior to suspending\n"); dev_priv->pm.suspended = true; /* * FIXME: We really should find a document that references the arguments * used below! */ if (IS_BROADWELL(dev)) { /* * On Broadwell, if we use PCI_D1 the PCH DDI ports will stop * being detected, and the call we do at intel_runtime_resume() * won't be able to restore them. Since PCI_D3hot matches the * actual specification and appears to be working, use it. */ intel_opregion_notify_adapter(dev, PCI_D3hot); } else { /* * current versions of firmware which depend on this opregion * notification have repurposed the D1 definition to mean * "runtime suspended" vs. what you would normally expect (D3) * to distinguish it from notifications that might be sent via * the suspend path. */ intel_opregion_notify_adapter(dev, PCI_D1); } assert_forcewakes_inactive(dev_priv); DRM_DEBUG_KMS("Device suspended\n"); return 0; }
static int i915_drm_suspend(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; pci_power_t opregion_target_state; int error; /* ignore lid events during suspend */ mutex_lock(&dev_priv->modeset_restore_lock); dev_priv->modeset_restore = MODESET_SUSPENDED; mutex_unlock(&dev_priv->modeset_restore_lock); disable_rpm_wakeref_asserts(dev_priv); /* We do a lot of poking in a lot of registers, make sure they work * properly. */ intel_display_set_init_power(dev_priv, true); drm_kms_helper_poll_disable(dev); pci_save_state(dev->pdev); error = i915_gem_suspend(dev); if (error) { dev_err(&dev->pdev->dev, "GEM idle failed, resume might fail\n"); goto out; } intel_guc_suspend(dev); intel_suspend_gt_powersave(dev); intel_display_suspend(dev); intel_dp_mst_suspend(dev); intel_runtime_pm_disable_interrupts(dev_priv); intel_hpd_cancel_work(dev_priv); intel_suspend_encoders(dev_priv); intel_suspend_hw(dev); i915_gem_suspend_gtt_mappings(dev); i915_save_state(dev); opregion_target_state = suspend_to_idle(dev_priv) ? PCI_D1 : PCI_D3cold; intel_opregion_notify_adapter(dev, opregion_target_state); intel_uncore_forcewake_reset(dev, false); intel_opregion_fini(dev); intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED, true); dev_priv->suspend_count++; intel_display_set_init_power(dev_priv, false); if (HAS_CSR(dev_priv)) flush_work(&dev_priv->csr.work); out: enable_rpm_wakeref_asserts(dev_priv); return error; }
static int i915_drm_freeze(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc; pci_power_t opregion_target_state; /* ignore lid events during suspend */ mutex_lock(&dev_priv->modeset_restore_lock); dev_priv->modeset_restore = MODESET_SUSPENDED; mutex_unlock(&dev_priv->modeset_restore_lock); /* We do a lot of poking in a lot of registers, make sure they work * properly. */ intel_display_set_init_power(dev_priv, true); drm_kms_helper_poll_disable(dev); pci_save_state(dev->pdev); /* If KMS is active, we do the leavevt stuff here */ if (drm_core_check_feature(dev, DRIVER_MODESET)) { int error; error = i915_gem_suspend(dev); if (error) { dev_err(&dev->pdev->dev, "GEM idle failed, resume might fail\n"); return error; } /* * Disable CRTCs directly since we want to preserve sw state * for _thaw. Also, power gate the CRTC power wells. */ drm_modeset_lock_all(dev); for_each_crtc(dev, crtc) intel_crtc_control(crtc, false); drm_modeset_unlock_all(dev); intel_dp_mst_suspend(dev); flush_delayed_work(&dev_priv->rps.delayed_resume_work); intel_runtime_pm_disable_interrupts(dev); intel_suspend_gt_powersave(dev); intel_modeset_suspend_hw(dev); } i915_gem_suspend_gtt_mappings(dev); i915_save_state(dev); opregion_target_state = PCI_D3cold; #if IS_ENABLED(CONFIG_ACPI_SLEEP) if (acpi_target_system_state() < ACPI_STATE_S3) opregion_target_state = PCI_D1; #endif intel_opregion_notify_adapter(dev, opregion_target_state); intel_uncore_forcewake_reset(dev, false); intel_opregion_fini(dev); console_lock(); intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED); console_unlock(); dev_priv->suspend_count++; intel_display_set_init_power(dev_priv, false); return 0; }