diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 91 |
1 files changed, 49 insertions, 42 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6d11e253218a..17d8fcb1b6f7 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -621,36 +621,15 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) #define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__)) #define __raw_i915_read16(dev_priv__, reg__) readw((dev_priv__)->regs + (reg__)) -static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) +static bool ilk_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t status; - int reg; - if (IS_VALLEYVIEW(dev)) { - status = pipe == PIPE_A ? - I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT : - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; - - reg = VLV_ISR; - } else if (IS_GEN2(dev)) { - status = pipe == PIPE_A ? - I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT : - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; - - reg = ISR; - } else if (INTEL_INFO(dev)->gen < 5) { - status = pipe == PIPE_A ? - I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT : - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; - - reg = ISR; - } else if (INTEL_INFO(dev)->gen < 7) { + if (INTEL_INFO(dev)->gen < 7) { status = pipe == PIPE_A ? DE_PIPEA_VBLANK : DE_PIPEB_VBLANK; - - reg = DEISR; } else { switch (pipe) { default: @@ -664,18 +643,14 @@ static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) status = DE_PIPEC_VBLANK_IVB; break; } - - reg = DEISR; } - if (IS_GEN2(dev)) - return __raw_i915_read16(dev_priv, reg) & status; - else - return __raw_i915_read32(dev_priv, reg) & status; + return __raw_i915_read32(dev_priv, DEISR) & status; } static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, - int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) + unsigned int flags, int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; @@ -698,6 +673,12 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, vbl_start = mode->crtc_vblank_start; vbl_end = mode->crtc_vblank_end; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vbl_start = DIV_ROUND_UP(vbl_start, 2); + vbl_end /= 2; + vtotal /= 2; + } + ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE; /* @@ -722,17 +703,42 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, else position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3; - /* - * The scanline counter increments at the leading edge - * of hsync, ie. it completely misses the active portion - * of the line. Fix up the counter at both edges of vblank - * to get a more accurate picture whether we're in vblank - * or not. - */ - in_vbl = intel_pipe_in_vblank_locked(dev, pipe); - if ((in_vbl && position == vbl_start - 1) || - (!in_vbl && position == vbl_end - 1)) - position = (position + 1) % vtotal; + if (HAS_PCH_SPLIT(dev)) { + /* + * The scanline counter increments at the leading edge + * of hsync, ie. it completely misses the active portion + * of the line. Fix up the counter at both edges of vblank + * to get a more accurate picture whether we're in vblank + * or not. + */ + in_vbl = ilk_pipe_in_vblank_locked(dev, pipe); + if ((in_vbl && position == vbl_start - 1) || + (!in_vbl && position == vbl_end - 1)) + position = (position + 1) % vtotal; + } else { + /* + * ISR vblank status bits don't work the way we'd want + * them to work on non-PCH platforms (for + * ilk_pipe_in_vblank_locked()), and there doesn't + * appear any other way to determine if we're currently + * in vblank. + * + * Instead let's assume that we're already in vblank if + * we got called from the vblank interrupt and the + * scanline counter value indicates that we're on the + * line just prior to vblank start. This should result + * in the correct answer, unless the vblank interrupt + * delivery really got delayed for almost exactly one + * full frame/field. + */ + if (flags & DRM_CALLED_FROM_VBLIRQ && + position == vbl_start - 1) { + position = (position + 1) % vtotal; + + /* Signal this correction as "applied". */ + ret |= 0x8; + } + } } else { /* Have access to pixelcount since start of frame. * We can split this into vertical and horizontal @@ -809,7 +815,8 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe, /* Helper routine in DRM core does all the work: */ return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error, vblank_time, flags, - crtc); + crtc, + &to_intel_crtc(crtc)->config.adjusted_mode); } static bool intel_hpd_irq_event(struct drm_device *dev, |