drm/i915: add initial Runtime PM functions
authorPaulo Zanoni <paulo.r.zanoni@intel.com>
Fri, 6 Dec 2013 22:32:13 +0000 (20:32 -0200)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Tue, 10 Dec 2013 21:43:14 +0000 (22:43 +0100)
This patch adds the initial infrastructure to allow a Runtime PM
implementation that sets the device to its D3 state. The patch just
adds the necessary callbacks and the initial infrastructure.

We still don't have any platform that actually uses this
infrastructure, we still don't call get/put in all the places we need
to, and we don't have any function to save/restore the state of the
registers. This is not a problem since no platform uses the code added
by this patch. We have a few people simultaneously working on runtime
PM, so this initial code could help everybody make their plans.

V2: - Move some functions to intel_pm.c
    - Remove useless pm_runtime_allow() call at init
    - Remove useless pm_runtime_mark_last_busy() call at get
    - Use pm_runtime_get_sync() instead of 2 calls
    - Add a WARN to check if we're really awake

V3: - Rebase.

V4: - Don't need to call pci_{save,restore}_state and
      pci_set_power_sate, since they're already called by the PCI
      layer
    - Remove wrong pm_runtime_enable() call at init_runtime_pm

Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Rodrigo Vivi <rodrigo.vivi@gmail.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_uncore.c

index a5d010c86368fe96269cb7e3d07d5b41dee8c99f..b49571df555f4fedf3e9ed17ded763527d8f938e 100644 (file)
@@ -42,6 +42,8 @@
 #include <linux/vga_switcheroo.h>
 #include <linux/slab.h>
 #include <acpi/video.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
 
 #define LP_RING(d) (&((struct drm_i915_private *)(d))->ring[RCS])
 
@@ -1663,6 +1665,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        if (IS_GEN5(dev))
                intel_gpu_ips_init(dev_priv);
 
+       intel_init_runtime_pm(dev_priv);
+
        return 0;
 
 out_power_well:
@@ -1708,6 +1712,8 @@ int i915_driver_unload(struct drm_device *dev)
                return ret;
        }
 
+       intel_fini_runtime_pm(dev_priv);
+
        intel_gpu_ips_teardown();
 
        /* The i915.ko module is still not prepared to be loaded when
index 13076db65eb962c5bfc5516c7869f8d66fa025d1..7d213617029394673425133d23d525b3965df494 100644 (file)
@@ -502,6 +502,8 @@ static int i915_drm_freeze(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_crtc *crtc;
 
+       intel_runtime_pm_get(dev_priv);
+
        /* ignore lid events during suspend */
        mutex_lock(&dev_priv->modeset_restore_lock);
        dev_priv->modeset_restore = MODESET_SUSPENDED;
@@ -686,6 +688,8 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
        mutex_lock(&dev_priv->modeset_restore_lock);
        dev_priv->modeset_restore = MODESET_DONE;
        mutex_unlock(&dev_priv->modeset_restore_lock);
+
+       intel_runtime_pm_put(dev_priv);
        return error;
 }
 
@@ -900,6 +904,36 @@ static int i915_pm_poweroff(struct device *dev)
        return i915_drm_freeze(drm_dev);
 }
 
+static int i915_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;
+
+       WARN_ON(!HAS_RUNTIME_PM(dev));
+
+       DRM_DEBUG_KMS("Suspending device\n");
+
+       dev_priv->pm.suspended = true;
+
+       return 0;
+}
+
+static int i915_runtime_resume(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;
+
+       WARN_ON(!HAS_RUNTIME_PM(dev));
+
+       DRM_DEBUG_KMS("Resuming device\n");
+
+       dev_priv->pm.suspended = false;
+
+       return 0;
+}
+
 static const struct dev_pm_ops i915_pm_ops = {
        .suspend = i915_pm_suspend,
        .resume = i915_pm_resume,
@@ -907,6 +941,8 @@ static const struct dev_pm_ops i915_pm_ops = {
        .thaw = i915_pm_thaw,
        .poweroff = i915_pm_poweroff,
        .restore = i915_pm_resume,
+       .runtime_suspend = i915_runtime_suspend,
+       .runtime_resume = i915_runtime_resume,
 };
 
 static const struct vm_operations_struct i915_gem_vm_ops = {
index 780f815b6c9f228b17d8cbd11bb93f2ac91fefaf..98fd1c043777dd34df6dce5857e4bac546933d25 100644 (file)
@@ -1289,6 +1289,10 @@ struct i915_package_c8 {
        } regsave;
 };
 
+struct i915_runtime_pm {
+       bool suspended;
+};
+
 enum intel_pipe_crc_source {
        INTEL_PIPE_CRC_SOURCE_NONE,
        INTEL_PIPE_CRC_SOURCE_PLANE1,
@@ -1519,6 +1523,8 @@ typedef struct drm_i915_private {
 
        struct i915_package_c8 pc8;
 
+       struct i915_runtime_pm pm;
+
        /* Old dri1 support infrastructure, beware the dragons ya fools entering
         * here! */
        struct i915_dri1_state dri1;
@@ -1843,6 +1849,7 @@ struct drm_i915_file_private {
 #define HAS_FPGA_DBG_UNCLAIMED(dev)    (INTEL_INFO(dev)->has_fpga_dbg)
 #define HAS_PSR(dev)           (IS_HASWELL(dev) || IS_BROADWELL(dev))
 #define HAS_PC8(dev)           (IS_HASWELL(dev)) /* XXX HSW:ULX */
+#define HAS_RUNTIME_PM(dev)    false
 
 #define INTEL_PCH_DEVICE_ID_MASK               0xff00
 #define INTEL_PCH_IBX_DEVICE_ID_TYPE           0x3b00
index 79585cddc2cd8374197748d86964c6c86e96e417..4a4effba134bf6497f7dc9fffd73b5a9e5451dcc 100644 (file)
@@ -862,6 +862,10 @@ void gen6_rps_idle(struct drm_i915_private *dev_priv);
 void gen6_rps_boost(struct drm_i915_private *dev_priv);
 void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv);
 void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv);
+void intel_runtime_pm_get(struct drm_i915_private *dev_priv);
+void intel_runtime_pm_put(struct drm_i915_private *dev_priv);
+void intel_init_runtime_pm(struct drm_i915_private *dev_priv);
+void intel_fini_runtime_pm(struct drm_i915_private *dev_priv);
 void ilk_wm_get_hw_state(struct drm_device *dev);
 
 
index cd3f511847ec9a82ef51c22e9bba43f9d1ca02bb..2590a5c90725253edbeb012ff1d83bc2f9f2ef12 100644 (file)
@@ -31,6 +31,7 @@
 #include "../../../platform/x86/intel_ips.h"
 #include <linux/module.h>
 #include <drm/i915_powerwell.h>
+#include <linux/pm_runtime.h>
 
 /**
  * RC6 is a special power stage which allows the GPU to enter an very
@@ -5961,6 +5962,60 @@ void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv)
        hsw_enable_package_c8(dev_priv);
 }
 
+void intel_runtime_pm_get(struct drm_i915_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+       struct device *device = &dev->pdev->dev;
+
+       if (!HAS_RUNTIME_PM(dev))
+               return;
+
+       pm_runtime_get_sync(device);
+       WARN(dev_priv->pm.suspended, "Device still suspended.\n");
+}
+
+void intel_runtime_pm_put(struct drm_i915_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+       struct device *device = &dev->pdev->dev;
+
+       if (!HAS_RUNTIME_PM(dev))
+               return;
+
+       pm_runtime_mark_last_busy(device);
+       pm_runtime_put_autosuspend(device);
+}
+
+void intel_init_runtime_pm(struct drm_i915_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+       struct device *device = &dev->pdev->dev;
+
+       dev_priv->pm.suspended = false;
+
+       if (!HAS_RUNTIME_PM(dev))
+               return;
+
+       pm_runtime_set_active(device);
+
+       pm_runtime_set_autosuspend_delay(device, 10000); /* 10s */
+       pm_runtime_mark_last_busy(device);
+       pm_runtime_use_autosuspend(device);
+}
+
+void intel_fini_runtime_pm(struct drm_i915_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+       struct device *device = &dev->pdev->dev;
+
+       if (!HAS_RUNTIME_PM(dev))
+               return;
+
+       /* Make sure we're not suspended first. */
+       pm_runtime_get_sync(device);
+       pm_runtime_disable(device);
+}
+
 /* Set up chip specific power management-related functions */
 void intel_init_pm(struct drm_device *dev)
 {
index e63658e0cfd30be83fd37938670c740fd769870f..0c4c302aa38cf718b4770ea23ccc6e290cdc0839 100644 (file)
@@ -437,6 +437,13 @@ hsw_unclaimed_reg_check(struct drm_i915_private *dev_priv, u32 reg)
        }
 }
 
+static void
+assert_device_not_suspended(struct drm_i915_private *dev_priv)
+{
+       WARN(HAS_RUNTIME_PM(dev_priv->dev) && dev_priv->pm.suspended,
+            "Device suspended\n");
+}
+
 #define REG_READ_HEADER(x) \
        unsigned long irqflags; \
        u##x val = 0; \
@@ -568,6 +575,7 @@ gen6_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace
        if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
                __fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
        } \
+       assert_device_not_suspended(dev_priv); \
        __raw_i915_write##x(dev_priv, reg, val); \
        if (unlikely(__fifo_ret)) { \
                gen6_gt_check_fifodbg(dev_priv); \
@@ -583,6 +591,7 @@ hsw_write##x(struct drm_i915_private *dev_priv, off_t reg, u##x val, bool trace)
        if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
                __fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
        } \
+       assert_device_not_suspended(dev_priv); \
        hsw_unclaimed_reg_clear(dev_priv, reg); \
        __raw_i915_write##x(dev_priv, reg, val); \
        if (unlikely(__fifo_ret)) { \