diff options
author | Mario Kleiner <mario.kleiner@tuebingen.mpg.de> | 2010-12-07 22:07:19 -0500 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2010-12-16 16:02:24 -0500 |
commit | 0af7e4dff50454905092d468e91c1ef92e10e6b4 (patch) | |
tree | 6c1c9542720c6cf3b6de7a9ac6e8216ba55aebbc | |
parent | d8c58fabd75021cdd99abcd96513cb088d41092b (diff) |
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: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 86 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_reg.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 29 |
5 files changed, 119 insertions, 6 deletions
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index bdb29b2a01ed..9eee6cf7901e 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c | |||
@@ -652,6 +652,8 @@ static struct drm_driver driver = { | |||
652 | .device_is_agp = i915_driver_device_is_agp, | 652 | .device_is_agp = i915_driver_device_is_agp, |
653 | .enable_vblank = i915_enable_vblank, | 653 | .enable_vblank = i915_enable_vblank, |
654 | .disable_vblank = i915_disable_vblank, | 654 | .disable_vblank = i915_disable_vblank, |
655 | .get_vblank_timestamp = i915_get_vblank_timestamp, | ||
656 | .get_scanout_position = i915_get_crtc_scanoutpos, | ||
655 | .irq_preinstall = i915_driver_irq_preinstall, | 657 | .irq_preinstall = i915_driver_irq_preinstall, |
656 | .irq_postinstall = i915_driver_irq_postinstall, | 658 | .irq_postinstall = i915_driver_irq_postinstall, |
657 | .irq_uninstall = i915_driver_irq_uninstall, | 659 | .irq_uninstall = i915_driver_irq_uninstall, |
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 30780f2cab6f..53dfc8398a96 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h | |||
@@ -1019,6 +1019,13 @@ void | |||
1019 | i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); | 1019 | i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); |
1020 | 1020 | ||
1021 | void intel_enable_asle (struct drm_device *dev); | 1021 | void intel_enable_asle (struct drm_device *dev); |
1022 | int i915_get_vblank_timestamp(struct drm_device *dev, int crtc, | ||
1023 | int *max_error, | ||
1024 | struct timeval *vblank_time, | ||
1025 | unsigned flags); | ||
1026 | |||
1027 | int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, | ||
1028 | int *vpos, int *hpos); | ||
1022 | 1029 | ||
1023 | #ifdef CONFIG_DEBUG_FS | 1030 | #ifdef CONFIG_DEBUG_FS |
1024 | extern void i915_destroy_error_state(struct drm_device *dev); | 1031 | extern void i915_destroy_error_state(struct drm_device *dev); |
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index e4a2e2c3dbe3..adf983f01dda 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c | |||
@@ -248,6 +248,92 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) | |||
248 | return I915_READ(reg); | 248 | return I915_READ(reg); |
249 | } | 249 | } |
250 | 250 | ||
251 | int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, | ||
252 | int *vpos, int *hpos) | ||
253 | { | ||
254 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
255 | u32 vbl = 0, position = 0; | ||
256 | int vbl_start, vbl_end, htotal, vtotal; | ||
257 | bool in_vbl = true; | ||
258 | int ret = 0; | ||
259 | |||
260 | if (!i915_pipe_enabled(dev, pipe)) { | ||
261 | DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled " | ||
262 | "pipe %d\n", pipe); | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | /* Get vtotal. */ | ||
267 | vtotal = 1 + ((I915_READ(VTOTAL(pipe)) >> 16) & 0x1fff); | ||
268 | |||
269 | if (INTEL_INFO(dev)->gen >= 4) { | ||
270 | /* No obvious pixelcount register. Only query vertical | ||
271 | * scanout position from Display scan line register. | ||
272 | */ | ||
273 | position = I915_READ(PIPEDSL(pipe)); | ||
274 | |||
275 | /* Decode into vertical scanout position. Don't have | ||
276 | * horizontal scanout position. | ||
277 | */ | ||
278 | *vpos = position & 0x1fff; | ||
279 | *hpos = 0; | ||
280 | } else { | ||
281 | /* Have access to pixelcount since start of frame. | ||
282 | * We can split this into vertical and horizontal | ||
283 | * scanout position. | ||
284 | */ | ||
285 | position = (I915_READ(PIPEFRAMEPIXEL(pipe)) & PIPE_PIXEL_MASK) >> PIPE_PIXEL_SHIFT; | ||
286 | |||
287 | htotal = 1 + ((I915_READ(HTOTAL(pipe)) >> 16) & 0x1fff); | ||
288 | *vpos = position / htotal; | ||
289 | *hpos = position - (*vpos * htotal); | ||
290 | } | ||
291 | |||
292 | /* Query vblank area. */ | ||
293 | vbl = I915_READ(VBLANK(pipe)); | ||
294 | |||
295 | /* Test position against vblank region. */ | ||
296 | vbl_start = vbl & 0x1fff; | ||
297 | vbl_end = (vbl >> 16) & 0x1fff; | ||
298 | |||
299 | if ((*vpos < vbl_start) || (*vpos > vbl_end)) | ||
300 | in_vbl = false; | ||
301 | |||
302 | /* Inside "upper part" of vblank area? Apply corrective offset: */ | ||
303 | if (in_vbl && (*vpos >= vbl_start)) | ||
304 | *vpos = *vpos - vtotal; | ||
305 | |||
306 | /* Readouts valid? */ | ||
307 | if (vbl > 0) | ||
308 | ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE; | ||
309 | |||
310 | /* In vblank? */ | ||
311 | if (in_vbl) | ||
312 | ret |= DRM_SCANOUTPOS_INVBL; | ||
313 | |||
314 | return ret; | ||
315 | } | ||
316 | |||
317 | int i915_get_vblank_timestamp(struct drm_device *dev, int crtc, | ||
318 | int *max_error, | ||
319 | struct timeval *vblank_time, | ||
320 | unsigned flags) | ||
321 | { | ||
322 | struct drm_crtc *drmcrtc; | ||
323 | |||
324 | if (crtc < 0 || crtc >= dev->num_crtcs) { | ||
325 | DRM_ERROR("Invalid crtc %d\n", crtc); | ||
326 | return -EINVAL; | ||
327 | } | ||
328 | |||
329 | /* Get drm_crtc to timestamp: */ | ||
330 | drmcrtc = intel_get_crtc_for_pipe(dev, crtc); | ||
331 | |||
332 | /* Helper routine in DRM core does all the work: */ | ||
333 | return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, | ||
334 | vblank_time, flags, drmcrtc); | ||
335 | } | ||
336 | |||
251 | /* | 337 | /* |
252 | * Handle hotplug events outside the interrupt handler proper. | 338 | * Handle hotplug events outside the interrupt handler proper. |
253 | */ | 339 | */ |
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 820e9dfaadc7..c2231f7d2d97 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h | |||
@@ -2253,6 +2253,7 @@ | |||
2253 | #define PIPESRC(pipe) _PIPE(pipe, PIPEASRC, PIPEBSRC) | 2253 | #define PIPESRC(pipe) _PIPE(pipe, PIPEASRC, PIPEBSRC) |
2254 | #define PIPECONF(pipe) _PIPE(pipe, PIPEACONF, PIPEBCONF) | 2254 | #define PIPECONF(pipe) _PIPE(pipe, PIPEACONF, PIPEBCONF) |
2255 | #define PIPEDSL(pipe) _PIPE(pipe, PIPEADSL, PIPEBDSL) | 2255 | #define PIPEDSL(pipe) _PIPE(pipe, PIPEADSL, PIPEBDSL) |
2256 | #define PIPEFRAMEPIXEL(pipe) _PIPE(pipe, PIPEAFRAMEPIXEL, PIPEBFRAMEPIXEL) | ||
2256 | 2257 | ||
2257 | #define DSPARB 0x70030 | 2258 | #define DSPARB 0x70030 |
2258 | #define DSPARB_CSTART_MASK (0x7f << 7) | 2259 | #define DSPARB_CSTART_MASK (0x7f << 7) |
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8645a974a499..0c201d684584 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c | |||
@@ -5252,7 +5252,8 @@ static void intel_unpin_work_fn(struct work_struct *__work) | |||
5252 | } | 5252 | } |
5253 | 5253 | ||
5254 | static void do_intel_finish_page_flip(struct drm_device *dev, | 5254 | static void do_intel_finish_page_flip(struct drm_device *dev, |
5255 | struct drm_crtc *crtc) | 5255 | struct drm_crtc *crtc, |
5256 | int called_before_vblirq) | ||
5256 | { | 5257 | { |
5257 | drm_i915_private_t *dev_priv = dev->dev_private; | 5258 | drm_i915_private_t *dev_priv = dev->dev_private; |
5258 | struct intel_crtc *intel_crtc = to_intel_crtc(crtc); | 5259 | struct intel_crtc *intel_crtc = to_intel_crtc(crtc); |
@@ -5274,19 +5275,33 @@ static void do_intel_finish_page_flip(struct drm_device *dev, | |||
5274 | } | 5275 | } |
5275 | 5276 | ||
5276 | intel_crtc->unpin_work = NULL; | 5277 | intel_crtc->unpin_work = NULL; |
5277 | drm_vblank_put(dev, intel_crtc->pipe); | ||
5278 | 5278 | ||
5279 | if (work->event) { | 5279 | if (work->event) { |
5280 | e = work->event; | 5280 | e = work->event; |
5281 | do_gettimeofday(&now); | 5281 | e->event.sequence = drm_vblank_count_and_time(dev, intel_crtc->pipe, &now); |
5282 | e->event.sequence = drm_vblank_count(dev, intel_crtc->pipe); | 5282 | |
5283 | /* Called before vblank count and timestamps have | ||
5284 | * been updated for the vblank interval of flip | ||
5285 | * completion? Need to increment vblank count and | ||
5286 | * add one videorefresh duration to returned timestamp | ||
5287 | * to account for this. | ||
5288 | */ | ||
5289 | if (called_before_vblirq) { | ||
5290 | e->event.sequence++; | ||
5291 | now = ns_to_timeval(timeval_to_ns(&now) + | ||
5292 | crtc->framedur_ns); | ||
5293 | } | ||
5294 | |||
5283 | e->event.tv_sec = now.tv_sec; | 5295 | e->event.tv_sec = now.tv_sec; |
5284 | e->event.tv_usec = now.tv_usec; | 5296 | e->event.tv_usec = now.tv_usec; |
5297 | |||
5285 | list_add_tail(&e->base.link, | 5298 | list_add_tail(&e->base.link, |
5286 | &e->base.file_priv->event_list); | 5299 | &e->base.file_priv->event_list); |
5287 | wake_up_interruptible(&e->base.file_priv->event_wait); | 5300 | wake_up_interruptible(&e->base.file_priv->event_wait); |
5288 | } | 5301 | } |
5289 | 5302 | ||
5303 | drm_vblank_put(dev, intel_crtc->pipe); | ||
5304 | |||
5290 | spin_unlock_irqrestore(&dev->event_lock, flags); | 5305 | spin_unlock_irqrestore(&dev->event_lock, flags); |
5291 | 5306 | ||
5292 | obj = work->old_fb_obj; | 5307 | obj = work->old_fb_obj; |
@@ -5306,7 +5321,8 @@ void intel_finish_page_flip(struct drm_device *dev, int pipe) | |||
5306 | drm_i915_private_t *dev_priv = dev->dev_private; | 5321 | drm_i915_private_t *dev_priv = dev->dev_private; |
5307 | struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; | 5322 | struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; |
5308 | 5323 | ||
5309 | do_intel_finish_page_flip(dev, crtc); | 5324 | /* Called after drm_handle_vblank has run for finish vblank. */ |
5325 | do_intel_finish_page_flip(dev, crtc, 0); | ||
5310 | } | 5326 | } |
5311 | 5327 | ||
5312 | void intel_finish_page_flip_plane(struct drm_device *dev, int plane) | 5328 | 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) | |||
5314 | drm_i915_private_t *dev_priv = dev->dev_private; | 5330 | drm_i915_private_t *dev_priv = dev->dev_private; |
5315 | struct drm_crtc *crtc = dev_priv->plane_to_crtc_mapping[plane]; | 5331 | struct drm_crtc *crtc = dev_priv->plane_to_crtc_mapping[plane]; |
5316 | 5332 | ||
5317 | do_intel_finish_page_flip(dev, crtc); | 5333 | /* Called before drm_handle_vblank has run for finish vblank. */ |
5334 | do_intel_finish_page_flip(dev, crtc, 1); | ||
5318 | } | 5335 | } |
5319 | 5336 | ||
5320 | void intel_prepare_page_flip(struct drm_device *dev, int plane) | 5337 | void intel_prepare_page_flip(struct drm_device *dev, int plane) |