drm/i915: Improve irq handling after gpu resets
[deliverable/linux.git] / drivers / gpu / drm / i915 / i915_drv.c
index b948c7110be89c2319592a968650120b90a198e0..481d2a14cdc697de5ac79e3f850994a920d78025 100644 (file)
@@ -36,6 +36,7 @@
 
 #include <linux/console.h>
 #include <linux/module.h>
+#include <linux/pm_runtime.h>
 #include <drm/drm_crtc_helper.h>
 
 static struct drm_driver driver;
@@ -49,12 +50,30 @@ static struct drm_driver driver;
        .dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET }, \
        .palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET }
 
+#define GEN_CHV_PIPEOFFSETS \
+       .pipe_offsets = { PIPE_A_OFFSET, PIPE_B_OFFSET, \
+                         CHV_PIPE_C_OFFSET }, \
+       .trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \
+                          CHV_TRANSCODER_C_OFFSET, }, \
+       .dpll_offsets = { DPLL_A_OFFSET, DPLL_B_OFFSET, \
+                         CHV_DPLL_C_OFFSET }, \
+       .dpll_md_offsets = { DPLL_A_MD_OFFSET, DPLL_B_MD_OFFSET, \
+                            CHV_DPLL_C_MD_OFFSET }, \
+       .palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET, \
+                            CHV_PALETTE_C_OFFSET }
+
+#define CURSOR_OFFSETS \
+       .cursor_offsets = { CURSOR_A_OFFSET, CURSOR_B_OFFSET, CHV_CURSOR_C_OFFSET }
+
+#define IVB_CURSOR_OFFSETS \
+       .cursor_offsets = { CURSOR_A_OFFSET, IVB_CURSOR_B_OFFSET, IVB_CURSOR_C_OFFSET }
 
 static const struct intel_device_info intel_i830_info = {
        .gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .num_pipes = 2,
        .has_overlay = 1, .overlay_needs_physical = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_845g_info = {
@@ -62,6 +81,7 @@ static const struct intel_device_info intel_845g_info = {
        .has_overlay = 1, .overlay_needs_physical = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_i85x_info = {
@@ -71,6 +91,7 @@ static const struct intel_device_info intel_i85x_info = {
        .has_fbc = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_i865g_info = {
@@ -78,6 +99,7 @@ static const struct intel_device_info intel_i865g_info = {
        .has_overlay = 1, .overlay_needs_physical = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_i915g_info = {
@@ -85,6 +107,7 @@ static const struct intel_device_info intel_i915g_info = {
        .has_overlay = 1, .overlay_needs_physical = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 static const struct intel_device_info intel_i915gm_info = {
        .gen = 3, .is_mobile = 1, .num_pipes = 2,
@@ -94,12 +117,14 @@ static const struct intel_device_info intel_i915gm_info = {
        .has_fbc = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 static const struct intel_device_info intel_i945g_info = {
        .gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .num_pipes = 2,
        .has_overlay = 1, .overlay_needs_physical = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 static const struct intel_device_info intel_i945gm_info = {
        .gen = 3, .is_i945gm = 1, .is_mobile = 1, .num_pipes = 2,
@@ -109,6 +134,7 @@ static const struct intel_device_info intel_i945gm_info = {
        .has_fbc = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_i965g_info = {
@@ -117,6 +143,7 @@ static const struct intel_device_info intel_i965g_info = {
        .has_overlay = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_i965gm_info = {
@@ -126,6 +153,7 @@ static const struct intel_device_info intel_i965gm_info = {
        .supports_tv = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_g33_info = {
@@ -134,6 +162,7 @@ static const struct intel_device_info intel_g33_info = {
        .has_overlay = 1,
        .ring_mask = RENDER_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_g45_info = {
@@ -141,6 +170,7 @@ static const struct intel_device_info intel_g45_info = {
        .has_pipe_cxsr = 1, .has_hotplug = 1,
        .ring_mask = RENDER_RING | BSD_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_gm45_info = {
@@ -150,6 +180,7 @@ static const struct intel_device_info intel_gm45_info = {
        .supports_tv = 1,
        .ring_mask = RENDER_RING | BSD_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_pineview_info = {
@@ -157,6 +188,7 @@ static const struct intel_device_info intel_pineview_info = {
        .need_gfx_hws = 1, .has_hotplug = 1,
        .has_overlay = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_ironlake_d_info = {
@@ -164,6 +196,7 @@ static const struct intel_device_info intel_ironlake_d_info = {
        .need_gfx_hws = 1, .has_hotplug = 1,
        .ring_mask = RENDER_RING | BSD_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_ironlake_m_info = {
@@ -172,6 +205,7 @@ static const struct intel_device_info intel_ironlake_m_info = {
        .has_fbc = 1,
        .ring_mask = RENDER_RING | BSD_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_sandybridge_d_info = {
@@ -181,6 +215,7 @@ static const struct intel_device_info intel_sandybridge_d_info = {
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING,
        .has_llc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_sandybridge_m_info = {
@@ -190,6 +225,7 @@ static const struct intel_device_info intel_sandybridge_m_info = {
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING,
        .has_llc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 #define GEN7_FEATURES  \
@@ -203,6 +239,7 @@ static const struct intel_device_info intel_ivybridge_d_info = {
        GEN7_FEATURES,
        .is_ivybridge = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_ivybridge_m_info = {
@@ -210,6 +247,7 @@ static const struct intel_device_info intel_ivybridge_m_info = {
        .is_ivybridge = 1,
        .is_mobile = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_ivybridge_q_info = {
@@ -217,6 +255,7 @@ static const struct intel_device_info intel_ivybridge_q_info = {
        .is_ivybridge = 1,
        .num_pipes = 0, /* legal, last one wins */
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_valleyview_m_info = {
@@ -228,6 +267,7 @@ static const struct intel_device_info intel_valleyview_m_info = {
        .has_fbc = 0, /* legal, last one wins */
        .has_llc = 0, /* legal, last one wins */
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_valleyview_d_info = {
@@ -238,6 +278,7 @@ static const struct intel_device_info intel_valleyview_d_info = {
        .has_fbc = 0, /* legal, last one wins */
        .has_llc = 0, /* legal, last one wins */
        GEN_DEFAULT_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_haswell_d_info = {
@@ -247,6 +288,7 @@ static const struct intel_device_info intel_haswell_d_info = {
        .has_fpga_dbg = 1,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_haswell_m_info = {
@@ -257,6 +299,7 @@ static const struct intel_device_info intel_haswell_m_info = {
        .has_fpga_dbg = 1,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_broadwell_d_info = {
@@ -267,6 +310,7 @@ static const struct intel_device_info intel_broadwell_d_info = {
        .has_ddi = 1,
        .has_fbc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_broadwell_m_info = {
@@ -297,15 +341,18 @@ static const struct intel_device_info intel_broadwell_gt3m_info = {
        .has_ddi = 1,
        .has_fbc = 1,
        GEN_DEFAULT_PIPEOFFSETS,
+       IVB_CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_cherryview_info = {
        .is_preliminary = 1,
-       .gen = 8, .num_pipes = 2,
+       .gen = 8, .num_pipes = 3,
        .need_gfx_hws = 1, .has_hotplug = 1,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
        .is_valleyview = 1,
        .display_mmio_offset = VLV_DISPLAY_BASE,
+       GEN_CHV_PIPEOFFSETS,
+       CURSOR_OFFSETS,
 };
 
 /*
@@ -477,18 +524,20 @@ static int i915_drm_freeze(struct drm_device *dev)
                        return error;
                }
 
-               cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work);
-
                drm_irq_uninstall(dev);
                dev_priv->enable_hotplug_processing = false;
+
+               intel_disable_gt_powersave(dev);
+
                /*
                 * Disable CRTCs directly since we want to preserve sw state
                 * for _thaw.
                 */
-               mutex_lock(&dev->mode_config.mutex);
-               for_each_crtc(dev, crtc)
+               drm_modeset_lock_all(dev);
+               for_each_crtc(dev, crtc) {
                        dev_priv->display.crtc_disable(crtc);
-               mutex_unlock(&dev->mode_config.mutex);
+               }
+               drm_modeset_unlock_all(dev);
 
                intel_modeset_suspend_hw(dev);
        }
@@ -551,24 +600,6 @@ void intel_console_resume(struct work_struct *work)
        console_unlock();
 }
 
-static void intel_resume_hotplug(struct drm_device *dev)
-{
-       struct drm_mode_config *mode_config = &dev->mode_config;
-       struct intel_encoder *encoder;
-
-       mutex_lock(&mode_config->mutex);
-       DRM_DEBUG_KMS("running encoder hotplug functions\n");
-
-       list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
-               if (encoder->hot_plug)
-                       encoder->hot_plug(encoder);
-
-       mutex_unlock(&mode_config->mutex);
-
-       /* Just fire off a uevent and let userspace tell us what to do */
-       drm_helper_hpd_irq_event(dev);
-}
-
 static int i915_drm_thaw_early(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -624,7 +655,7 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
                intel_hpd_init(dev);
                dev_priv->enable_hotplug_processing = true;
                /* Config may have changed between suspend and resume */
-               intel_resume_hotplug(dev);
+               drm_helper_hpd_irq_event(dev);
        }
 
        intel_opregion_init(dev);
@@ -780,17 +811,17 @@ int i915_reset(struct drm_device *dev)
                }
 
                /*
-                * FIXME: This is horribly race against concurrent pageflip and
-                * vblank wait ioctls since they can observe dev->irqs_disabled
-                * being false when they shouldn't be able to.
+                * FIXME: This races pretty badly against concurrent holders of
+                * ring interrupts. This is possible since we've started to drop
+                * dev->struct_mutex in select places when waiting for the gpu.
                 */
-               drm_irq_uninstall(dev);
-               drm_irq_install(dev, dev->pdev->irq);
 
-               /* rps/rc6 re-init is necessary to restore state lost after the
-                * reset and the re-install of drm irq. Skip for ironlake per
+               /*
+                * rps/rc6 re-init is necessary to restore state lost after the
+                * reset and the re-install of gt irqs. Skip for ironlake per
                 * previous concerns that it doesn't respond well to some forms
-                * of re-init after reset. */
+                * of re-init after reset.
+                */
                if (INTEL_INFO(dev)->gen > 5)
                        intel_reset_gt_powersave(dev);
 
@@ -1324,6 +1355,30 @@ static int intel_runtime_suspend(struct device *device)
 
        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;
+       }
+       /*
+        * 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);
+
        /*
         * rps.work can't be rearmed here, since we get here only after making
         * sure the GPU is idle and the RPS freq is set to the minimum. See
@@ -1350,8 +1405,6 @@ static int intel_runtime_suspend(struct device *device)
                return ret;
        }
 
-       i915_gem_release_all_mmaps(dev_priv);
-
        del_timer_sync(&dev_priv->gpu_error.hangcheck_timer);
        dev_priv->pm.suspended = true;
 
This page took 0.029983 seconds and 5 git commands to generate.