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_IRONLAKE(dev) || IS_GEN6(dev)) gpu's has not been tested so far due to lack of hardware. Signed-off-by: Mario Kleiner <mario.kleiner@xxxxxxxxxxxxxxxx> --- drivers/gpu/drm/i915/i915_drv.c | 2 + drivers/gpu/drm/i915/i915_drv.h | 7 +++ drivers/gpu/drm/i915/i915_irq.c | 92 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 31 +++++++++--- drivers/gpu/drm/i915/intel_drv.h | 1 + 5 files changed, 126 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index c3decb2..4d8184a 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -603,6 +603,8 @@ static struct drm_driver driver = { .device_is_agp = i915_driver_device_is_agp, .enable_vblank = i915_enable_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_postinstall = i915_driver_irq_postinstall, .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 73ad8bf..276fbdb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -948,6 +948,13 @@ void i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); 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 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 64c07c2..c9c8d4a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -243,6 +243,98 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) 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; + int reg; + 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. */ + reg = pipe ? VTOTAL_B : VTOTAL_A; + vtotal = 1 + ((I915_READ(reg) >> 16) & 0x1fff); + + if (IS_G4X(dev) || IS_IRONLAKE(dev) || IS_GEN6(dev)) { + /* No obvious pixelcount register. Only query vertical + * scanout position from Display scan line register. + */ + reg = pipe ? PIPEBDSL : PIPEADSL; + position = I915_READ(reg); + + /* 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. + */ + reg = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; + position = (I915_READ(reg) & PIPE_PIXEL_MASK) >> PIPE_PIXEL_SHIFT; + + reg = pipe ? HTOTAL_B : HTOTAL_A; + htotal = 1 + ((I915_READ(reg) >> 16) & 0x1fff); + *vpos = position / htotal; + *hpos = position - (*vpos * htotal); + } + + /* Query vblank area. */ + reg = pipe ? VBLANK_B : VBLANK_A; + vbl = I915_READ(reg); + + /* 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. */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 96d08a9..47853e5 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4591,7 +4591,7 @@ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, } /* Returns the clock of the currently programmed mode of the given pipe. */ -static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) +int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -4955,7 +4955,8 @@ static void intel_unpin_work_fn(struct work_struct *__work) } 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; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -4977,19 +4978,33 @@ static void do_intel_finish_page_flip(struct drm_device *dev, } intel_crtc->unpin_work = NULL; - drm_vblank_put(dev, intel_crtc->pipe); if (work->event) { e = work->event; - do_gettimeofday(&now); - e->event.sequence = drm_vblank_count(dev, intel_crtc->pipe); + e->event.sequence = drm_vblank_count_and_time(dev, intel_crtc->pipe, &now); + + /* 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_usec = now.tv_usec; + list_add_tail(&e->base.link, &e->base.file_priv->event_list); wake_up_interruptible(&e->base.file_priv->event_wait); } + drm_vblank_put(dev, intel_crtc->pipe); + spin_unlock_irqrestore(&dev->event_lock, flags); obj_priv = to_intel_bo(work->pending_flip_obj); @@ -5008,7 +5023,8 @@ void intel_finish_page_flip(struct drm_device *dev, int pipe) drm_i915_private_t *dev_priv = dev->dev_private; 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) @@ -5016,7 +5032,8 @@ void intel_finish_page_flip_plane(struct drm_device *dev, int plane) drm_i915_private_t *dev_priv = dev->dev_private; 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) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 40e99bf..1e19e3e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -240,6 +240,7 @@ extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector); extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, struct drm_crtc *crtc); +extern int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc); int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, struct drm_file *file_priv); extern void intel_wait_for_vblank(struct drm_device *dev, int pipe); -- 1.7.1 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel