Commit b0b544cd authored by Chris Wilson's avatar Chris Wilson

drm/i915: Use PM QoS to prevent C-State starvation of gen3 GPU

945 class hardware has an interesting quirk in which the vblank
interrupt is not raised if the CPU is in a low power state. (We also
suspect that the memory bus is clocked to the CPU/c-state and not the
GPU so there are secondary starvation issues.) In order to prevent the
most obvious issue of the low of the vblank interrupt (stuttering
compositing that only updates when the mouse is moving) is to install a
PM QoS request to prevent low c-states whilst the GPU is active.
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
parent f67a559d
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "intel_ringbuffer.h" #include "intel_ringbuffer.h"
#include <linux/io-mapping.h> #include <linux/io-mapping.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/pm_qos_params.h>
#include <drm/intel-gtt.h> #include <drm/intel-gtt.h>
/* General customization: /* General customization:
...@@ -310,6 +311,10 @@ typedef struct drm_i915_private { ...@@ -310,6 +311,10 @@ typedef struct drm_i915_private {
int vblank_pipe; int vblank_pipe;
int num_pipe; int num_pipe;
atomic_t vblank_enabled;
struct pm_qos_request_list vblank_pm_qos;
struct work_struct vblank_work;
/* For hangcheck timer */ /* For hangcheck timer */
#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ #define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */
struct timer_list hangcheck_timer; struct timer_list hangcheck_timer;
......
...@@ -1337,6 +1337,22 @@ int i915_irq_wait(struct drm_device *dev, void *data, ...@@ -1337,6 +1337,22 @@ int i915_irq_wait(struct drm_device *dev, void *data,
return i915_wait_irq(dev, irqwait->irq_seq); return i915_wait_irq(dev, irqwait->irq_seq);
} }
static void i915_vblank_work_func(struct work_struct *work)
{
drm_i915_private_t *dev_priv =
container_of(work, drm_i915_private_t, vblank_work);
if (atomic_read(&dev_priv->vblank_enabled)) {
if (!dev_priv->vblank_pm_qos.pm_qos_class)
pm_qos_add_request(&dev_priv->vblank_pm_qos,
PM_QOS_CPU_DMA_LATENCY,
15); //>=20 won't work
} else {
if (dev_priv->vblank_pm_qos.pm_qos_class)
pm_qos_remove_request(&dev_priv->vblank_pm_qos);
}
}
/* Called from drm generic code, passed 'crtc' which /* Called from drm generic code, passed 'crtc' which
* we use as a pipe index * we use as a pipe index
*/ */
...@@ -1359,6 +1375,16 @@ int i915_enable_vblank(struct drm_device *dev, int pipe) ...@@ -1359,6 +1375,16 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
i915_enable_pipestat(dev_priv, pipe, i915_enable_pipestat(dev_priv, pipe,
PIPE_VBLANK_INTERRUPT_ENABLE); PIPE_VBLANK_INTERRUPT_ENABLE);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
/* gen3 platforms have an issue with vsync interrupts not reaching
* cpu during deep c-state sleep (>C1), so we need to install a
* PM QoS handle to prevent C-state starvation of the GPU.
*/
if (dev_priv->info->gen == 3 && !dev_priv->info->is_g33) {
atomic_inc(&dev_priv->vblank_enabled);
queue_work(dev_priv->wq, &dev_priv->vblank_work);
}
return 0; return 0;
} }
...@@ -1370,6 +1396,11 @@ void i915_disable_vblank(struct drm_device *dev, int pipe) ...@@ -1370,6 +1396,11 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
unsigned long irqflags; unsigned long irqflags;
if (dev_priv->info->gen == 3 && !dev_priv->info->is_g33) {
atomic_dec(&dev_priv->vblank_enabled);
queue_work(dev_priv->wq, &dev_priv->vblank_work);
}
spin_lock_irqsave(&dev_priv->irq_lock, irqflags); spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
if (HAS_PCH_SPLIT(dev)) if (HAS_PCH_SPLIT(dev))
ironlake_disable_display_irq(dev_priv, (pipe == 0) ? ironlake_disable_display_irq(dev_priv, (pipe == 0) ?
...@@ -1659,9 +1690,11 @@ void i915_driver_irq_preinstall(struct drm_device * dev) ...@@ -1659,9 +1690,11 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
atomic_set(&dev_priv->irq_received, 0); atomic_set(&dev_priv->irq_received, 0);
atomic_set(&dev_priv->vblank_enabled, 0);
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
INIT_WORK(&dev_priv->error_work, i915_error_work_func); INIT_WORK(&dev_priv->error_work, i915_error_work_func);
INIT_WORK(&dev_priv->vblank_work, i915_vblank_work_func);
if (HAS_PCH_SPLIT(dev)) { if (HAS_PCH_SPLIT(dev)) {
ironlake_irq_preinstall(dev); ironlake_irq_preinstall(dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment