Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5395ba3 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge branch 'drm-vbl-timestamp' of git://gitorious.org/vsyrjala/linux into drm-next

Here's the vblank timestamp pull request you wanted.

I addressed the few bugs that Mario pointed out and added
the r-bs.

As it has been a while since I made the changes, I gave it a
quick spin on a few different i915 machines. Fortunately
everything still seems to be fine.

* 'drm-vbl-timestamp' of git://gitorious.org/vsyrjala/linux:
  drm/i915: Add a kludge for DSL incrementing too late and ISR not working
  drm/radeon: Move the early vblank IRQ fixup to radeon_get_crtc_scanoutpos()
  drm: Pass 'flags' from the caller to .get_scanout_position()
  drm: Fix vblank timestamping constants for interlaced modes
  drm/i915: Fix scanoutpos calculations for interlaced modes
  drm: Change {pixel,line,frame}dur_ns from s64 to int
  drm: Use crtc_clock in drm_calc_timestamping_constants()
  drm/radeon: Populate crtc_clock in radeon_atom_get_tv_timings()
  drm: Simplify the math in drm_calc_timestamping_constants()
  drm: Improve drm_calc_timestamping_constants() documentation
  drm/i915: Call drm_calc_timestamping_constants() earlier
  drm/i915: Kill hwmode save/restore
  drm: Pass the display mode to drm_calc_vbltimestamp_from_scanoutpos()
  drm: Pass the display mode to drm_calc_timestamping_constants()
parents 2b76a676 095163ba
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -536,7 +536,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
	 * are later needed by vblank and swap-completion
	 * timestamping. They are derived from true hwmode.
	 */
	drm_calc_timestamping_constants(crtc);
	drm_calc_timestamping_constants(crtc, &crtc->hwmode);

	/* FIXME: add subpixel order */
done:
+42 −67
Original line number Diff line number Diff line
@@ -436,45 +436,41 @@ int drm_control(struct drm_device *dev, void *data,
}

/**
 * drm_calc_timestamping_constants - Calculate and
 * store various constants which are later needed by
 * vblank and swap-completion timestamping, e.g, by
 * drm_calc_vbltimestamp_from_scanoutpos().
 * They are derived from crtc's true scanout timing,
 * so they take things like panel scaling or other
 * adjustments into account.
 * drm_calc_timestamping_constants - Calculate vblank timestamp constants
 *
 * @crtc drm_crtc whose timestamp constants should be updated.
 * @mode display mode containing the scanout timings
 *
 * Calculate and store various constants which are later
 * needed by vblank and swap-completion timestamping, e.g,
 * by drm_calc_vbltimestamp_from_scanoutpos(). They are
 * derived from crtc's true scanout timing, so they take
 * things like panel scaling or other adjustments into account.
 */
void drm_calc_timestamping_constants(struct drm_crtc *crtc)
void drm_calc_timestamping_constants(struct drm_crtc *crtc,
				     const struct drm_display_mode *mode)
{
	s64 linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0;
	u64 dotclock;
	int linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0;
	int dotclock = mode->crtc_clock;

	/* Dot clock in Hz: */
	dotclock = (u64) crtc->hwmode.clock * 1000;
	/* Valid dotclock? */
	if (dotclock > 0) {
		int frame_size = mode->crtc_htotal * mode->crtc_vtotal;

	/* Fields of interlaced scanout modes are only half a frame duration.
	 * Double the dotclock to get half the frame-/line-/pixelduration.
		/*
		 * Convert scanline length in pixels and video
		 * dot clock to line duration, frame duration
		 * and pixel duration in nanoseconds:
		 */
	if (crtc->hwmode.flags & DRM_MODE_FLAG_INTERLACE)
		dotclock *= 2;
		pixeldur_ns = 1000000 / dotclock;
		linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
		framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);

	/* Valid dotclock? */
	if (dotclock > 0) {
		int frame_size;
		/* Convert scanline length in pixels and video dot clock to
		 * line duration, frame duration and pixel duration in
		 * nanoseconds:
		/*
		 * Fields of interlaced scanout modes are only half a frame duration.
		 */
		pixeldur_ns = (s64) div64_u64(1000000000, dotclock);
		linedur_ns  = (s64) div64_u64(((u64) crtc->hwmode.crtc_htotal *
					      1000000000), dotclock);
		frame_size = crtc->hwmode.crtc_htotal *
				crtc->hwmode.crtc_vtotal;
		framedur_ns = (s64) div64_u64((u64) frame_size * 1000000000,
					      dotclock);
		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
			framedur_ns /= 2;
	} else
		DRM_ERROR("crtc %d: Can't calculate constants, dotclock = 0!\n",
			  crtc->base.id);
@@ -484,11 +480,11 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc)
	crtc->framedur_ns = framedur_ns;

	DRM_DEBUG("crtc %d: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
		  crtc->base.id, crtc->hwmode.crtc_htotal,
		  crtc->hwmode.crtc_vtotal, crtc->hwmode.crtc_vdisplay);
		  crtc->base.id, mode->crtc_htotal,
		  mode->crtc_vtotal, mode->crtc_vdisplay);
	DRM_DEBUG("crtc %d: clock %d kHz framedur %d linedur %d, pixeldur %d\n",
		  crtc->base.id, (int) dotclock/1000, (int) framedur_ns,
		  (int) linedur_ns, (int) pixeldur_ns);
		  crtc->base.id, dotclock, framedur_ns,
		  linedur_ns, pixeldur_ns);
}
EXPORT_SYMBOL(drm_calc_timestamping_constants);

@@ -521,6 +517,7 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
 *         0 = Default.
 *         DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler.
 * @refcrtc: drm_crtc* of crtc which defines scanout timing.
 * @mode: mode which defines the scanout timings
 *
 * Returns negative value on error, failure or if not supported in current
 * video mode:
@@ -540,14 +537,14 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
					  int *max_error,
					  struct timeval *vblank_time,
					  unsigned flags,
					  struct drm_crtc *refcrtc)
					  const struct drm_crtc *refcrtc,
					  const struct drm_display_mode *mode)
{
	ktime_t stime, etime, mono_time_offset;
	struct timeval tv_etime;
	struct drm_display_mode *mode;
	int vbl_status, vtotal, vdisplay;
	int vbl_status;
	int vpos, hpos, i;
	s64 framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns;
	int framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns;
	bool invbl;

	if (crtc < 0 || crtc >= dev->num_crtcs) {
@@ -561,10 +558,6 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
		return -EIO;
	}

	mode = &refcrtc->hwmode;
	vtotal = mode->crtc_vtotal;
	vdisplay = mode->crtc_vdisplay;

	/* Durations of frames, lines, pixels in nanoseconds. */
	framedur_ns = refcrtc->framedur_ns;
	linedur_ns  = refcrtc->linedur_ns;
@@ -573,7 +566,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
	/* If mode timing undefined, just return as no-op:
	 * Happens during initial modesetting of a crtc.
	 */
	if (vtotal <= 0 || vdisplay <= 0 || framedur_ns == 0) {
	if (framedur_ns == 0) {
		DRM_DEBUG("crtc %d: Noop due to uninitialized mode.\n", crtc);
		return -EAGAIN;
	}
@@ -590,7 +583,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
		 * Get vertical and horizontal scanout position vpos, hpos,
		 * and bounding timestamps stime, etime, pre/post query.
		 */
		vbl_status = dev->driver->get_scanout_position(dev, crtc, &vpos,
		vbl_status = dev->driver->get_scanout_position(dev, crtc, flags, &vpos,
							       &hpos, &stime, &etime);

		/*
@@ -611,18 +604,18 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
		duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);

		/* Accept result with <  max_error nsecs timing uncertainty. */
		if (duration_ns <= (s64) *max_error)
		if (duration_ns <= *max_error)
			break;
	}

	/* Noisy system timing? */
	if (i == DRM_TIMESTAMP_MAXRETRIES) {
		DRM_DEBUG("crtc %d: Noisy timestamp %d us > %d us [%d reps].\n",
			  crtc, (int) duration_ns/1000, *max_error/1000, i);
			  crtc, duration_ns/1000, *max_error/1000, i);
	}

	/* Return upper bound of timestamp precision error. */
	*max_error = (int) duration_ns;
	*max_error = duration_ns;

	/* Check if in vblank area:
	 * vpos is >=0 in video scanout area, but negative
@@ -635,25 +628,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
	 * since start of scanout at first display scanline. delta_ns
	 * can be negative if start of scanout hasn't happened yet.
	 */
	delta_ns = (s64) vpos * linedur_ns + (s64) hpos * pixeldur_ns;

	/* Is vpos outside nominal vblank area, but less than
	 * 1/100 of a frame height away from start of vblank?
	 * If so, assume this isn't a massively delayed vblank
	 * interrupt, but a vblank interrupt that fired a few
	 * microseconds before true start of vblank. Compensate
	 * by adding a full frame duration to the final timestamp.
	 * Happens, e.g., on ATI R500, R600.
	 *
	 * We only do this if DRM_CALLED_FROM_VBLIRQ.
	 */
	if ((flags & DRM_CALLED_FROM_VBLIRQ) && !invbl &&
	    ((vdisplay - vpos) < vtotal / 100)) {
		delta_ns = delta_ns - framedur_ns;

		/* Signal this correction as "applied". */
		vbl_status |= 0x8;
	}
	delta_ns = vpos * linedur_ns + hpos * pixeldur_ns;

	if (!drm_timestamp_monotonic)
		etime = ktime_sub(etime, mono_time_offset);
@@ -673,7 +648,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
		  crtc, (int)vbl_status, hpos, vpos,
		  (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
		  (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
		  (int)duration_ns/1000, i);
		  duration_ns/1000, i);

	vbl_status = DRM_VBLANKTIME_SCANOUTPOS_METHOD;
	if (invbl)
+49 −42
Original line number Diff line number Diff line
@@ -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,6 +703,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
		else
			position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;

		if (HAS_PCH_SPLIT(dev)) {
			/*
			 * The scanline counter increments at the leading edge
			 * of hsync, ie. it completely misses the active portion
@@ -729,10 +711,34 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
			 * to get a more accurate picture whether we're in vblank
			 * or not.
			 */
		in_vbl = intel_pipe_in_vblank_locked(dev, pipe);
			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,
+11 −18
Original line number Diff line number Diff line
@@ -9597,21 +9597,19 @@ static int __intel_set_mode(struct drm_crtc *crtc,
{
	struct drm_device *dev = crtc->dev;
	drm_i915_private_t *dev_priv = dev->dev_private;
	struct drm_display_mode *saved_mode, *saved_hwmode;
	struct drm_display_mode *saved_mode;
	struct intel_crtc_config *pipe_config = NULL;
	struct intel_crtc *intel_crtc;
	unsigned disable_pipes, prepare_pipes, modeset_pipes;
	int ret = 0;

	saved_mode = kcalloc(2, sizeof(*saved_mode), GFP_KERNEL);
	saved_mode = kmalloc(sizeof(*saved_mode), GFP_KERNEL);
	if (!saved_mode)
		return -ENOMEM;
	saved_hwmode = saved_mode + 1;

	intel_modeset_affected_pipes(crtc, &modeset_pipes,
				     &prepare_pipes, &disable_pipes);

	*saved_hwmode = crtc->hwmode;
	*saved_mode = crtc->mode;

	/* Hack: Because we don't (yet) support global modeset on multiple
@@ -9662,6 +9660,14 @@ static int __intel_set_mode(struct drm_crtc *crtc,
		/* mode_set/enable/disable functions rely on a correct pipe
		 * config. */
		to_intel_crtc(crtc)->config = *pipe_config;

		/*
		 * Calculate and store various constants which
		 * are later needed by vblank and swap-completion
		 * timestamping. They are derived from true hwmode.
		 */
		drm_calc_timestamping_constants(crtc,
						&pipe_config->adjusted_mode);
	}

	/* Only after disabling all output pipelines that will be changed can we
@@ -9685,23 +9691,10 @@ static int __intel_set_mode(struct drm_crtc *crtc,
	for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc)
		dev_priv->display.crtc_enable(&intel_crtc->base);

	if (modeset_pipes) {
		/* Store real post-adjustment hardware mode. */
		crtc->hwmode = pipe_config->adjusted_mode;

		/* Calculate and store various constants which
		 * are later needed by vblank and swap-completion
		 * timestamping. They are derived from true hwmode.
		 */
		drm_calc_timestamping_constants(crtc);
	}

	/* FIXME: add subpixel order */
done:
	if (ret && crtc->enabled) {
		crtc->hwmode = *saved_hwmode;
	if (ret && crtc->enabled)
		crtc->mode = *saved_mode;
	}

out:
	kfree(pipe_config);
+4 −2
Original line number Diff line number Diff line
@@ -1799,7 +1799,8 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
		if (misc & ATOM_DOUBLE_CLOCK_MODE)
			mode->flags |= DRM_MODE_FLAG_DBLSCAN;

		mode->clock = le16_to_cpu(tv_info->aModeTimings[index].usPixelClock) * 10;
		mode->crtc_clock = mode->clock =
			le16_to_cpu(tv_info->aModeTimings[index].usPixelClock) * 10;

		if (index == 1) {
			/* PAL timings appear to have wrong values for totals */
@@ -1842,7 +1843,8 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
		if (misc & ATOM_DOUBLE_CLOCK_MODE)
			mode->flags |= DRM_MODE_FLAG_DBLSCAN;

		mode->clock = le16_to_cpu(dtd_timings->usPixClk) * 10;
		mode->crtc_clock = mode->clock =
			le16_to_cpu(dtd_timings->usPixClk) * 10;
		break;
	}
	return true;
Loading