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

Commit 0af7e4df authored by Mario Kleiner's avatar Mario Kleiner Committed by Chris Wilson
Browse files

drm/i915: Add support for precise vblank timestamping (v2)



v2: Change IS_IRONLAKE to IS_GEN5 to adapt to 2.6.37

This patch adds new functions for use by the drm core:

.get_vblank_timestamp() provides a precise timestamp
for the end of the most recent (or current) vblank
interval of a given crtc, as needed for the DRI2
implementation of the OML_sync_control extension.
It is a thin wrapper around the drm function
drm_calc_vbltimestamp_from_scanoutpos() which does
almost all the work.

.get_scanout_position() provides the current horizontal
and vertical video scanout position and "in vblank"
status of a given crtc, as needed by the drm for use by
drm_calc_vbltimestamp_from_scanoutpos().

The patch modifies the pageflip completion routine
to use these precise vblank timestamps as the timestamps
for pageflip completion events.

This code has been only tested on a HP-Mini Netbook with
Atom processor and Intel 945GME gpu. The codepath for
(IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev)) gpu's
has not been tested so far due to lack of hardware.

Signed-off-by: default avatarMario Kleiner <mario.kleiner@tuebingen.mpg.de>
Acked-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
parent d8c58fab
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -652,6 +652,8 @@ static struct drm_driver driver = {
	.device_is_agp = i915_driver_device_is_agp,
	.device_is_agp = i915_driver_device_is_agp,
	.enable_vblank = i915_enable_vblank,
	.enable_vblank = i915_enable_vblank,
	.disable_vblank = i915_disable_vblank,
	.disable_vblank = i915_disable_vblank,
	.get_vblank_timestamp = i915_get_vblank_timestamp,
	.get_scanout_position = i915_get_crtc_scanoutpos,
	.irq_preinstall = i915_driver_irq_preinstall,
	.irq_preinstall = i915_driver_irq_preinstall,
	.irq_postinstall = i915_driver_irq_postinstall,
	.irq_postinstall = i915_driver_irq_postinstall,
	.irq_uninstall = i915_driver_irq_uninstall,
	.irq_uninstall = i915_driver_irq_uninstall,
+7 −0
Original line number Original line Diff line number Diff line
@@ -1019,6 +1019,13 @@ void
i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);


void intel_enable_asle (struct drm_device *dev);
void intel_enable_asle (struct drm_device *dev);
int i915_get_vblank_timestamp(struct drm_device *dev, int crtc,
			      int *max_error,
			      struct timeval *vblank_time,
			      unsigned flags);

int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
			     int *vpos, int *hpos);


#ifdef CONFIG_DEBUG_FS
#ifdef CONFIG_DEBUG_FS
extern void i915_destroy_error_state(struct drm_device *dev);
extern void i915_destroy_error_state(struct drm_device *dev);
+86 −0
Original line number Original line Diff line number Diff line
@@ -248,6 +248,92 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
	return I915_READ(reg);
	return I915_READ(reg);
}
}


int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
			     int *vpos, int *hpos)
{
	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
	u32 vbl = 0, position = 0;
	int vbl_start, vbl_end, htotal, vtotal;
	bool in_vbl = true;
	int ret = 0;

	if (!i915_pipe_enabled(dev, pipe)) {
		DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
					"pipe %d\n", pipe);
		return 0;
	}

	/* Get vtotal. */
	vtotal = 1 + ((I915_READ(VTOTAL(pipe)) >> 16) & 0x1fff);

	if (INTEL_INFO(dev)->gen >= 4) {
		/* No obvious pixelcount register. Only query vertical
		 * scanout position from Display scan line register.
		 */
		position = I915_READ(PIPEDSL(pipe));

		/* Decode into vertical scanout position. Don't have
		 * horizontal scanout position.
		 */
		*vpos = position & 0x1fff;
		*hpos = 0;
	} else {
		/* Have access to pixelcount since start of frame.
		 * We can split this into vertical and horizontal
		 * scanout position.
		 */
		position = (I915_READ(PIPEFRAMEPIXEL(pipe)) & PIPE_PIXEL_MASK) >> PIPE_PIXEL_SHIFT;

		htotal = 1 + ((I915_READ(HTOTAL(pipe)) >> 16) & 0x1fff);
		*vpos = position / htotal;
		*hpos = position - (*vpos * htotal);
	}

	/* Query vblank area. */
	vbl = I915_READ(VBLANK(pipe));

	/* Test position against vblank region. */
	vbl_start = vbl & 0x1fff;
	vbl_end = (vbl >> 16) & 0x1fff;

	if ((*vpos < vbl_start) || (*vpos > vbl_end))
		in_vbl = false;

	/* Inside "upper part" of vblank area? Apply corrective offset: */
	if (in_vbl && (*vpos >= vbl_start))
		*vpos = *vpos - vtotal;

	/* Readouts valid? */
	if (vbl > 0)
		ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;

	/* In vblank? */
	if (in_vbl)
		ret |= DRM_SCANOUTPOS_INVBL;

	return ret;
}

int i915_get_vblank_timestamp(struct drm_device *dev, int crtc,
			      int *max_error,
			      struct timeval *vblank_time,
			      unsigned flags)
{
	struct drm_crtc *drmcrtc;

	if (crtc < 0 || crtc >= dev->num_crtcs) {
		DRM_ERROR("Invalid crtc %d\n", crtc);
		return -EINVAL;
	}

	/* Get drm_crtc to timestamp: */
	drmcrtc = intel_get_crtc_for_pipe(dev, crtc);

	/* Helper routine in DRM core does all the work: */
	return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error,
						     vblank_time, flags, drmcrtc);
}

/*
/*
 * Handle hotplug events outside the interrupt handler proper.
 * Handle hotplug events outside the interrupt handler proper.
 */
 */
+1 −0
Original line number Original line Diff line number Diff line
@@ -2253,6 +2253,7 @@
#define PIPESRC(pipe) _PIPE(pipe, PIPEASRC, PIPEBSRC)
#define PIPESRC(pipe) _PIPE(pipe, PIPEASRC, PIPEBSRC)
#define PIPECONF(pipe) _PIPE(pipe, PIPEACONF, PIPEBCONF)
#define PIPECONF(pipe) _PIPE(pipe, PIPEACONF, PIPEBCONF)
#define PIPEDSL(pipe)  _PIPE(pipe, PIPEADSL, PIPEBDSL)
#define PIPEDSL(pipe)  _PIPE(pipe, PIPEADSL, PIPEBDSL)
#define PIPEFRAMEPIXEL(pipe)  _PIPE(pipe, PIPEAFRAMEPIXEL, PIPEBFRAMEPIXEL)


#define DSPARB			0x70030
#define DSPARB			0x70030
#define   DSPARB_CSTART_MASK	(0x7f << 7)
#define   DSPARB_CSTART_MASK	(0x7f << 7)
+23 −6
Original line number Original line Diff line number Diff line
@@ -5252,7 +5252,8 @@ static void intel_unpin_work_fn(struct work_struct *__work)
}
}


static void do_intel_finish_page_flip(struct drm_device *dev,
static void do_intel_finish_page_flip(struct drm_device *dev,
				      struct drm_crtc *crtc)
				      struct drm_crtc *crtc,
				      int called_before_vblirq)
{
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	drm_i915_private_t *dev_priv = dev->dev_private;
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -5274,19 +5275,33 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
	}
	}


	intel_crtc->unpin_work = NULL;
	intel_crtc->unpin_work = NULL;
	drm_vblank_put(dev, intel_crtc->pipe);


	if (work->event) {
	if (work->event) {
		e = work->event;
		e = work->event;
		do_gettimeofday(&now);
		e->event.sequence = drm_vblank_count_and_time(dev, intel_crtc->pipe, &now);
		e->event.sequence = drm_vblank_count(dev, intel_crtc->pipe);

		/* Called before vblank count and timestamps have
		 * been updated for the vblank interval of flip
		 * completion? Need to increment vblank count and
		 * add one videorefresh duration to returned timestamp
		 * to account for this.
		 */
		if (called_before_vblirq) {
			e->event.sequence++;
			now = ns_to_timeval(timeval_to_ns(&now) +
					    crtc->framedur_ns);
		}

		e->event.tv_sec = now.tv_sec;
		e->event.tv_sec = now.tv_sec;
		e->event.tv_usec = now.tv_usec;
		e->event.tv_usec = now.tv_usec;

		list_add_tail(&e->base.link,
		list_add_tail(&e->base.link,
			      &e->base.file_priv->event_list);
			      &e->base.file_priv->event_list);
		wake_up_interruptible(&e->base.file_priv->event_wait);
		wake_up_interruptible(&e->base.file_priv->event_wait);
	}
	}


	drm_vblank_put(dev, intel_crtc->pipe);

	spin_unlock_irqrestore(&dev->event_lock, flags);
	spin_unlock_irqrestore(&dev->event_lock, flags);


	obj = work->old_fb_obj;
	obj = work->old_fb_obj;
@@ -5306,7 +5321,8 @@ void intel_finish_page_flip(struct drm_device *dev, int pipe)
	drm_i915_private_t *dev_priv = dev->dev_private;
	drm_i915_private_t *dev_priv = dev->dev_private;
	struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
	struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];


	do_intel_finish_page_flip(dev, crtc);
	/* Called after drm_handle_vblank has run for finish vblank. */
	do_intel_finish_page_flip(dev, crtc, 0);
}
}


void intel_finish_page_flip_plane(struct drm_device *dev, int plane)
void intel_finish_page_flip_plane(struct drm_device *dev, int plane)
@@ -5314,7 +5330,8 @@ void intel_finish_page_flip_plane(struct drm_device *dev, int plane)
	drm_i915_private_t *dev_priv = dev->dev_private;
	drm_i915_private_t *dev_priv = dev->dev_private;
	struct drm_crtc *crtc = dev_priv->plane_to_crtc_mapping[plane];
	struct drm_crtc *crtc = dev_priv->plane_to_crtc_mapping[plane];


	do_intel_finish_page_flip(dev, crtc);
	/* Called before drm_handle_vblank has run for finish vblank. */
	do_intel_finish_page_flip(dev, crtc, 1);
}
}


void intel_prepare_page_flip(struct drm_device *dev, int plane)
void intel_prepare_page_flip(struct drm_device *dev, int plane)