diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 120 |
1 files changed, 98 insertions, 22 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index fc2b2a7e2c55..caaf1e2a7bc1 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -136,12 +136,6 @@ static const u32 hpd_bxt[HPD_NUM_PINS] = { POSTING_READ(type##IIR); \ } while (0) -static void -intel_finish_page_flip_cs(struct drm_i915_private *dev_priv, unsigned pipe) -{ - DRM_DEBUG_KMS("Finished page flip\n"); -} - /* * We should clear IMR at preinstall/uninstall, and just check at postinstall. */ @@ -1637,11 +1631,16 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir) } } -static void intel_pipe_handle_vblank(struct drm_i915_private *dev_priv, +static bool intel_pipe_handle_vblank(struct drm_i915_private *dev_priv, enum pipe pipe) { - if (drm_handle_vblank(dev_priv->dev, pipe)) + bool ret; + + ret = drm_handle_vblank(dev_priv->dev, pipe); + if (ret) intel_finish_page_flip_mmio(dev_priv, pipe); + + return ret; } static void valleyview_pipestat_irq_ack(struct drm_i915_private *dev_priv, @@ -1708,8 +1707,9 @@ static void valleyview_pipestat_irq_handler(struct drm_i915_private *dev_priv, enum pipe pipe; for_each_pipe(dev_priv, pipe) { - if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS) - intel_pipe_handle_vblank(dev_priv, pipe); + if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS && + intel_pipe_handle_vblank(dev_priv, pipe)) + intel_check_page_flip(dev_priv, pipe); if (pipe_stats[pipe] & PLANE_FLIP_DONE_INT_STATUS_VLV) intel_finish_page_flip_cs(dev_priv, pipe); @@ -2155,8 +2155,9 @@ static void ilk_display_irq_handler(struct drm_i915_private *dev_priv, DRM_ERROR("Poison interrupt\n"); for_each_pipe(dev_priv, pipe) { - if (de_iir & DE_PIPE_VBLANK(pipe)) - intel_pipe_handle_vblank(dev_priv, pipe); + if (de_iir & DE_PIPE_VBLANK(pipe) && + intel_pipe_handle_vblank(dev_priv, pipe)) + intel_check_page_flip(dev_priv, pipe); if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe)) intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); @@ -2205,8 +2206,9 @@ static void ivb_display_irq_handler(struct drm_i915_private *dev_priv, intel_opregion_asle_intr(dev_priv); for_each_pipe(dev_priv, pipe) { - if (de_iir & (DE_PIPE_VBLANK_IVB(pipe))) - intel_pipe_handle_vblank(dev_priv, pipe); + if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)) && + intel_pipe_handle_vblank(dev_priv, pipe)) + intel_check_page_flip(dev_priv, pipe); /* plane/pipes map 1:1 on ilk+ */ if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe)) @@ -2405,8 +2407,9 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) ret = IRQ_HANDLED; I915_WRITE(GEN8_DE_PIPE_IIR(pipe), iir); - if (iir & GEN8_PIPE_VBLANK) - intel_pipe_handle_vblank(dev_priv, pipe); + if (iir & GEN8_PIPE_VBLANK && + intel_pipe_handle_vblank(dev_priv, pipe)) + intel_check_page_flip(dev_priv, pipe); flip_done = iir; if (INTEL_INFO(dev_priv)->gen >= 9) @@ -3972,6 +3975,37 @@ static int i8xx_irq_postinstall(struct drm_device *dev) return 0; } +/* + * Returns true when a page flip has completed. + */ +static bool i8xx_handle_vblank(struct drm_i915_private *dev_priv, + int plane, int pipe, u32 iir) +{ + u16 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane); + + if (!intel_pipe_handle_vblank(dev_priv, pipe)) + return false; + + if ((iir & flip_pending) == 0) + goto check_page_flip; + + /* We detect FlipDone by looking for the change in PendingFlip from '1' + * to '0' on the following vblank, i.e. IIR has the Pendingflip + * asserted following the MI_DISPLAY_FLIP, but ISR is deasserted, hence + * the flip is completed (no longer pending). Since this doesn't raise + * an interrupt per se, we watch for the change at vblank. + */ + if (I915_READ16(ISR) & flip_pending) + goto check_page_flip; + + intel_finish_page_flip_cs(dev_priv, pipe); + return true; + +check_page_flip: + intel_check_page_flip(dev_priv, pipe); + return false; +} + static irqreturn_t i8xx_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; @@ -4024,8 +4058,13 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg) notify_ring(&dev_priv->engine[RCS]); for_each_pipe(dev_priv, pipe) { - if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) - intel_pipe_handle_vblank(dev_priv, pipe); + int plane = pipe; + if (HAS_FBC(dev_priv)) + plane = !plane; + + if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS && + i8xx_handle_vblank(dev_priv, plane, pipe, iir)) + flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(plane); if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) i9xx_pipe_crc_irq_handler(dev_priv, pipe); @@ -4125,6 +4164,37 @@ static int i915_irq_postinstall(struct drm_device *dev) return 0; } +/* + * Returns true when a page flip has completed. + */ +static bool i915_handle_vblank(struct drm_i915_private *dev_priv, + int plane, int pipe, u32 iir) +{ + u32 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane); + + if (!intel_pipe_handle_vblank(dev_priv, pipe)) + return false; + + if ((iir & flip_pending) == 0) + goto check_page_flip; + + /* We detect FlipDone by looking for the change in PendingFlip from '1' + * to '0' on the following vblank, i.e. IIR has the Pendingflip + * asserted following the MI_DISPLAY_FLIP, but ISR is deasserted, hence + * the flip is completed (no longer pending). Since this doesn't raise + * an interrupt per se, we watch for the change at vblank. + */ + if (I915_READ(ISR) & flip_pending) + goto check_page_flip; + + intel_finish_page_flip_cs(dev_priv, pipe); + return true; + +check_page_flip: + intel_check_page_flip(dev_priv, pipe); + return false; +} + static irqreturn_t i915_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; @@ -4185,8 +4255,13 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) notify_ring(&dev_priv->engine[RCS]); for_each_pipe(dev_priv, pipe) { - if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) - intel_pipe_handle_vblank(dev_priv, pipe); + int plane = pipe; + if (HAS_FBC(dev_priv)) + plane = !plane; + + if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS && + i915_handle_vblank(dev_priv, plane, pipe, iir)) + flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(plane); if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) blc_event = true; @@ -4414,8 +4489,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) notify_ring(&dev_priv->engine[VCS]); for_each_pipe(dev_priv, pipe) { - if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS) - intel_pipe_handle_vblank(dev_priv, pipe); + if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS && + i915_handle_vblank(dev_priv, pipe, pipe, iir)) + flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(pipe); if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) blc_event = true; |