diff options
author | Thomas Zimmermann <tzimmermann@suse.de> | 2023-01-31 14:18:33 +0100 |
---|---|---|
committer | Thomas Zimmermann <tzimmermann@suse.de> | 2023-01-31 14:18:33 +0100 |
commit | df5bf3b942a8d344bd9cbbe6ac31c9a2ea1557a4 (patch) | |
tree | bfbcbe56b9f4f8b1e44242b80800a68b2ae5b2d6 /drivers/gpu/drm/i915/i915_irq.c | |
parent | 532a38292c7213aa6d950e6a1b86659d08b5aa67 (diff) | |
parent | aebd8f0c6f8280ba35bc989f4a9ea47469d3589a (diff) |
Merge drm/drm-next into drm-misc-next
Backmerging to get v6.2-rc6.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 408 |
1 files changed, 0 insertions, 408 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 54ea28cf8a1a..240d5e198904 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -614,414 +614,6 @@ static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv) spin_unlock_irq(&dev_priv->irq_lock); } -/* - * This timing diagram depicts the video signal in and - * around the vertical blanking period. - * - * Assumptions about the fictitious mode used in this example: - * vblank_start >= 3 - * vsync_start = vblank_start + 1 - * vsync_end = vblank_start + 2 - * vtotal = vblank_start + 3 - * - * start of vblank: - * latch double buffered registers - * increment frame counter (ctg+) - * generate start of vblank interrupt (gen4+) - * | - * | frame start: - * | generate frame start interrupt (aka. vblank interrupt) (gmch) - * | may be shifted forward 1-3 extra lines via PIPECONF - * | | - * | | start of vsync: - * | | generate vsync interrupt - * | | | - * ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx - * . \hs/ . \hs/ \hs/ \hs/ . \hs/ - * ----va---> <-----------------vb--------------------> <--------va------------- - * | | <----vs-----> | - * -vbs-----> <---vbs+1---> <---vbs+2---> <-----0-----> <-----1-----> <-----2--- (scanline counter gen2) - * -vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2---> <-----0--- (scanline counter gen3+) - * -vbs-2---> <---vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2- (scanline counter hsw+ hdmi) - * | | | - * last visible pixel first visible pixel - * | increment frame counter (gen3/4) - * pixel counter = vblank_start * htotal pixel counter = 0 (gen3/4) - * - * x = horizontal active - * _ = horizontal blanking - * hs = horizontal sync - * va = vertical active - * vb = vertical blanking - * vs = vertical sync - * vbs = vblank_start (number) - * - * Summary: - * - most events happen at the start of horizontal sync - * - frame start happens at the start of horizontal blank, 1-4 lines - * (depending on PIPECONF settings) after the start of vblank - * - gen3/4 pixel and frame counter are synchronized with the start - * of horizontal active on the first line of vertical active - */ - -/* Called from drm generic code, passed a 'crtc', which - * we use as a pipe index - */ -u32 i915_get_vblank_counter(struct drm_crtc *crtc) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->dev); - struct drm_vblank_crtc *vblank = &dev_priv->drm.vblank[drm_crtc_index(crtc)]; - const struct drm_display_mode *mode = &vblank->hwmode; - enum pipe pipe = to_intel_crtc(crtc)->pipe; - i915_reg_t high_frame, low_frame; - u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal; - unsigned long irqflags; - - /* - * On i965gm TV output the frame counter only works up to - * the point when we enable the TV encoder. After that the - * frame counter ceases to work and reads zero. We need a - * vblank wait before enabling the TV encoder and so we - * have to enable vblank interrupts while the frame counter - * is still in a working state. However the core vblank code - * does not like us returning non-zero frame counter values - * when we've told it that we don't have a working frame - * counter. Thus we must stop non-zero values leaking out. - */ - if (!vblank->max_vblank_count) - return 0; - - htotal = mode->crtc_htotal; - hsync_start = mode->crtc_hsync_start; - vbl_start = mode->crtc_vblank_start; - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - vbl_start = DIV_ROUND_UP(vbl_start, 2); - - /* Convert to pixel count */ - vbl_start *= htotal; - - /* Start of vblank event occurs at start of hsync */ - vbl_start -= htotal - hsync_start; - - high_frame = PIPEFRAME(pipe); - low_frame = PIPEFRAMEPIXEL(pipe); - - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - - /* - * High & low register fields aren't synchronized, so make sure - * we get a low value that's stable across two reads of the high - * register. - */ - do { - high1 = intel_de_read_fw(dev_priv, high_frame) & PIPE_FRAME_HIGH_MASK; - low = intel_de_read_fw(dev_priv, low_frame); - high2 = intel_de_read_fw(dev_priv, high_frame) & PIPE_FRAME_HIGH_MASK; - } while (high1 != high2); - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); - - high1 >>= PIPE_FRAME_HIGH_SHIFT; - pixel = low & PIPE_PIXEL_MASK; - low >>= PIPE_FRAME_LOW_SHIFT; - - /* - * The frame counter increments at beginning of active. - * Cook up a vblank counter by also checking the pixel - * counter against vblank start. - */ - return (((high1 << 8) | low) + (pixel >= vbl_start)) & 0xffffff; -} - -u32 g4x_get_vblank_counter(struct drm_crtc *crtc) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->dev); - struct drm_vblank_crtc *vblank = &dev_priv->drm.vblank[drm_crtc_index(crtc)]; - enum pipe pipe = to_intel_crtc(crtc)->pipe; - - if (!vblank->max_vblank_count) - return 0; - - return intel_uncore_read(&dev_priv->uncore, PIPE_FRMCOUNT_G4X(pipe)); -} - -static u32 intel_crtc_scanlines_since_frame_timestamp(struct intel_crtc *crtc) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - struct drm_vblank_crtc *vblank = - &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)]; - const struct drm_display_mode *mode = &vblank->hwmode; - u32 htotal = mode->crtc_htotal; - u32 clock = mode->crtc_clock; - u32 scan_prev_time, scan_curr_time, scan_post_time; - - /* - * To avoid the race condition where we might cross into the - * next vblank just between the PIPE_FRMTMSTMP and TIMESTAMP_CTR - * reads. We make sure we read PIPE_FRMTMSTMP and TIMESTAMP_CTR - * during the same frame. - */ - do { - /* - * This field provides read back of the display - * pipe frame time stamp. The time stamp value - * is sampled at every start of vertical blank. - */ - scan_prev_time = intel_de_read_fw(dev_priv, - PIPE_FRMTMSTMP(crtc->pipe)); - - /* - * The TIMESTAMP_CTR register has the current - * time stamp value. - */ - scan_curr_time = intel_de_read_fw(dev_priv, IVB_TIMESTAMP_CTR); - - scan_post_time = intel_de_read_fw(dev_priv, - PIPE_FRMTMSTMP(crtc->pipe)); - } while (scan_post_time != scan_prev_time); - - return div_u64(mul_u32_u32(scan_curr_time - scan_prev_time, - clock), 1000 * htotal); -} - -/* - * On certain encoders on certain platforms, pipe - * scanline register will not work to get the scanline, - * since the timings are driven from the PORT or issues - * with scanline register updates. - * This function will use Framestamp and current - * timestamp registers to calculate the scanline. - */ -static u32 __intel_get_crtc_scanline_from_timestamp(struct intel_crtc *crtc) -{ - struct drm_vblank_crtc *vblank = - &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)]; - const struct drm_display_mode *mode = &vblank->hwmode; - u32 vblank_start = mode->crtc_vblank_start; - u32 vtotal = mode->crtc_vtotal; - u32 scanline; - - scanline = intel_crtc_scanlines_since_frame_timestamp(crtc); - scanline = min(scanline, vtotal - 1); - scanline = (scanline + vblank_start) % vtotal; - - return scanline; -} - -/* - * intel_de_read_fw(), only for fast reads of display block, no need for - * forcewake etc. - */ -static int __intel_get_crtc_scanline(struct intel_crtc *crtc) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - const struct drm_display_mode *mode; - struct drm_vblank_crtc *vblank; - enum pipe pipe = crtc->pipe; - int position, vtotal; - - if (!crtc->active) - return 0; - - vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)]; - mode = &vblank->hwmode; - - if (crtc->mode_flags & I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP) - return __intel_get_crtc_scanline_from_timestamp(crtc); - - vtotal = mode->crtc_vtotal; - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - vtotal /= 2; - - position = intel_de_read_fw(dev_priv, PIPEDSL(pipe)) & PIPEDSL_LINE_MASK; - - /* - * On HSW, the DSL reg (0x70000) appears to return 0 if we - * read it just before the start of vblank. So try it again - * so we don't accidentally end up spanning a vblank frame - * increment, causing the pipe_update_end() code to squak at us. - * - * The nature of this problem means we can't simply check the ISR - * bit and return the vblank start value; nor can we use the scanline - * debug register in the transcoder as it appears to have the same - * problem. We may need to extend this to include other platforms, - * but so far testing only shows the problem on HSW. - */ - if (HAS_DDI(dev_priv) && !position) { - int i, temp; - - for (i = 0; i < 100; i++) { - udelay(1); - temp = intel_de_read_fw(dev_priv, PIPEDSL(pipe)) & PIPEDSL_LINE_MASK; - if (temp != position) { - position = temp; - break; - } - } - } - - /* - * See update_scanline_offset() for the details on the - * scanline_offset adjustment. - */ - return (position + crtc->scanline_offset) % vtotal; -} - -static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, - bool in_vblank_irq, - int *vpos, int *hpos, - ktime_t *stime, ktime_t *etime, - const struct drm_display_mode *mode) -{ - struct drm_device *dev = _crtc->dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *crtc = to_intel_crtc(_crtc); - enum pipe pipe = crtc->pipe; - int position; - int vbl_start, vbl_end, hsync_start, htotal, vtotal; - unsigned long irqflags; - bool use_scanline_counter = DISPLAY_VER(dev_priv) >= 5 || - IS_G4X(dev_priv) || DISPLAY_VER(dev_priv) == 2 || - crtc->mode_flags & I915_MODE_FLAG_USE_SCANLINE_COUNTER; - - if (drm_WARN_ON(&dev_priv->drm, !mode->crtc_clock)) { - drm_dbg(&dev_priv->drm, - "trying to get scanoutpos for disabled " - "pipe %c\n", pipe_name(pipe)); - return false; - } - - htotal = mode->crtc_htotal; - hsync_start = mode->crtc_hsync_start; - vtotal = mode->crtc_vtotal; - 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; - } - - /* - * Lock uncore.lock, as we will do multiple timing critical raw - * register reads, potentially with preemption disabled, so the - * following code must not block on uncore.lock. - */ - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - - /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ - - /* Get optional system timestamp before query. */ - if (stime) - *stime = ktime_get(); - - if (crtc->mode_flags & I915_MODE_FLAG_VRR) { - int scanlines = intel_crtc_scanlines_since_frame_timestamp(crtc); - - position = __intel_get_crtc_scanline(crtc); - - /* - * Already exiting vblank? If so, shift our position - * so it looks like we're already apporaching the full - * vblank end. This should make the generated timestamp - * more or less match when the active portion will start. - */ - if (position >= vbl_start && scanlines < position) - position = min(crtc->vmax_vblank_start + scanlines, vtotal - 1); - } else if (use_scanline_counter) { - /* No obvious pixelcount register. Only query vertical - * scanout position from Display scan line register. - */ - position = __intel_get_crtc_scanline(crtc); - } else { - /* Have access to pixelcount since start of frame. - * We can split this into vertical and horizontal - * scanout position. - */ - position = (intel_de_read_fw(dev_priv, PIPEFRAMEPIXEL(pipe)) & PIPE_PIXEL_MASK) >> PIPE_PIXEL_SHIFT; - - /* convert to pixel counts */ - vbl_start *= htotal; - vbl_end *= htotal; - vtotal *= htotal; - - /* - * In interlaced modes, the pixel counter counts all pixels, - * so one field will have htotal more pixels. In order to avoid - * the reported position from jumping backwards when the pixel - * counter is beyond the length of the shorter field, just - * clamp the position the length of the shorter field. This - * matches how the scanline counter based position works since - * the scanline counter doesn't count the two half lines. - */ - if (position >= vtotal) - position = vtotal - 1; - - /* - * Start of vblank interrupt is triggered at start of hsync, - * just prior to the first active line of vblank. However we - * consider lines to start at the leading edge of horizontal - * active. So, should we get here before we've crossed into - * the horizontal active of the first line in vblank, we would - * not set the DRM_SCANOUTPOS_INVBL flag. In order to fix that, - * always add htotal-hsync_start to the current pixel position. - */ - position = (position + htotal - hsync_start) % vtotal; - } - - /* Get optional system timestamp after query. */ - if (etime) - *etime = ktime_get(); - - /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ - - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); - - /* - * While in vblank, position will be negative - * counting up towards 0 at vbl_end. And outside - * vblank, position will be positive counting - * up since vbl_end. - */ - if (position >= vbl_start) - position -= vbl_end; - else - position += vtotal - vbl_end; - - if (use_scanline_counter) { - *vpos = position; - *hpos = 0; - } else { - *vpos = position / htotal; - *hpos = position - (*vpos * htotal); - } - - return true; -} - -bool intel_crtc_get_vblank_timestamp(struct drm_crtc *crtc, int *max_error, - ktime_t *vblank_time, bool in_vblank_irq) -{ - return drm_crtc_vblank_helper_get_vblank_timestamp_internal( - crtc, max_error, vblank_time, in_vblank_irq, - i915_get_crtc_scanoutpos); -} - -int intel_get_crtc_scanline(struct intel_crtc *crtc) -{ - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); - unsigned long irqflags; - int position; - - spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); - position = __intel_get_crtc_scanline(crtc); - spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); - - return position; -} - /** * ivb_parity_work - Workqueue called when a parity error interrupt * occurred. |