[PATCH] drm/core: Add drm_accurate_vblank_count_and_time.

Ville Syrjälä ville.syrjala at linux.intel.com
Wed Mar 30 13:13:16 UTC 2016


On Tue, Mar 29, 2016 at 03:38:55PM +0200, Maarten Lankhorst wrote:
> This function is useful for gen2 intel devices which have no frame
> counter, but need a way to determine the current vblank count without
> racing with the vblank interrupt handler.
> 
> intel_pipe_update_start checks if no vblank interrupt will occur
> during vblank evasion, but cannot check whether the vblank handler has
> run to completion. This function uses the timestamps to determine
> when the last vblank has happened, and interpolates from there.

Didn't really read it in detail, but on a glance it seems too
complicated to me. All we should really need is something like this:

drm_vblank_get();
drm_update_vblank_count();
accurate = drm_vblank_count();

> 
> Cc: Mario Kleiner <mario.kleiner.de at gmail.com>
> Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst at linux.intel.com>
> ---
> Unfortunately only compile time tested, I don't have a gen2 to test with.
> 
>  drivers/gpu/drm/drm_irq.c | 90 +++++++++++++++++++++++++++++++++++------------
>  include/drm/drmP.h        |  2 ++
>  2 files changed, 69 insertions(+), 23 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
> index 881c5a6c180c..44d8f9607ccd 100644
> --- a/drivers/gpu/drm/drm_irq.c
> +++ b/drivers/gpu/drm/drm_irq.c
> @@ -155,24 +155,10 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
>  	spin_unlock(&dev->vblank_time_lock);
>  }
>  
> -/**
> - * drm_update_vblank_count - update the master vblank counter
> - * @dev: DRM device
> - * @pipe: counter to update
> - *
> - * Call back into the driver to update the appropriate vblank counter
> - * (specified by @pipe).  Deal with wraparound, if it occurred, and
> - * update the last read value so we can deal with wraparound on the next
> - * call if necessary.
> - *
> - * Only necessary when going from off->on, to account for frames we
> - * didn't get an interrupt for.
> - *
> - * Note: caller must hold dev->vbl_lock since this reads & writes
> - * device vblank fields.
> - */
> -static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
> -				    unsigned long flags)
> +static u32 __drm_accurate_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
> +						const struct timeval *t_old,
> +						unsigned long flags, u32 *pdiff,
> +					        struct timeval *tv_ret)
>  {
>  	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
>  	u32 cur_vblank, diff;
> @@ -202,10 +188,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
>  		/* trust the hw counter when it's around */
>  		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
>  	} else if (rc && framedur_ns) {
> -		const struct timeval *t_old;
>  		u64 diff_ns;
>  
> -		t_old = &vblanktimestamp(dev, pipe, vblank->count);
>  		diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
>  
>  		/*
> @@ -286,9 +270,13 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
>  		      " current=%u, diff=%u, hw=%u hw_last=%u\n",
>  		      pipe, vblank->count, diff, cur_vblank, vblank->last);
>  
> +	*pdiff = diff;
> +	*tv_ret = t_vblank;
> +
>  	if (diff == 0) {
>  		WARN_ON_ONCE(cur_vblank != vblank->last);
> -		return;
> +
> +		return cur_vblank;
>  	}
>  
>  	/*
> @@ -298,9 +286,65 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
>  	 * for now, to mark the vblanktimestamp as invalid.
>  	 */
>  	if (!rc && (flags & DRM_CALLED_FROM_VBLIRQ) == 0)
> -		t_vblank = (struct timeval) {0, 0};
> +		*tv_ret = (struct timeval) {0, 0};
> +
> +	return cur_vblank;
> +}
> +
> +/**
> + * drm_accurate_vblank_count_and_time - retrieve the master vblank counter
> + * @crtc: which counter to retrieve
> + * @tv_ret: last time counter was updated
> + *
> + * This function is similar to @drm_update_vblank_count_and_time but
> + * this function interpolates to handle a race with vblank irq's, and
> + * is only useful for crtc's that have no hw vblank counter.
> + */
> +
> +u32 drm_accurate_vblank_count_and_time(struct drm_crtc *crtc,
> +				       struct timeval *tv_ret)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	u32 pdiff, old_vblank;
> +	struct timeval tv_old = {};
> +
> +	WARN(dev->max_vblank_count, "This function is only useful when a hw counter is unavailable.");
> +
> +	old_vblank = drm_crtc_vblank_count_and_time(crtc, &tv_old);
> +
> +	__drm_accurate_vblank_count_and_time(dev, drm_crtc_index(crtc),
> +					     &tv_old, 0, &pdiff, tv_ret);
> +
> +	return old_vblank + pdiff;
> +}
> +EXPORT_SYMBOL(drm_accurate_vblank_count_and_time);
> +
> +/**
> + * drm_update_vblank_count - update the master vblank counter
> + * @dev: DRM device
> + * @pipe: counter to update
> + *
> + * Call back into the driver to update the appropriate vblank counter
> + * (specified by @pipe).  Deal with wraparound, if it occurred, and
> + * update the last read value so we can deal with wraparound on the next
> + * call if necessary.
> + *
> + * Only necessary when going from off->on, to account for frames we
> + * didn't get an interrupt for.
> + *
> + * Note: caller must hold dev->vbl_lock since this reads & writes
> + * device vblank fields.
> + */
> +static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
> +				    unsigned long flags)
> +{
> +	u32 diff, cur_vblank, old_vblank = dev->vblank[pipe].count;
> +	struct timeval t_vblank;
> +	const struct timeval *tv_old = &vblanktimestamp(dev, pipe, old_vblank);
>  
> -	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
> +	cur_vblank = __drm_accurate_vblank_count_and_time(dev, pipe, tv_old, flags, &diff, &t_vblank);
> +	if (diff)
> +		store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
>  }
>  
>  /*
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index 31483c2fef51..1df65922c7c6 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -995,6 +995,8 @@ extern void drm_crtc_vblank_off(struct drm_crtc *crtc);
>  extern void drm_crtc_vblank_reset(struct drm_crtc *crtc);
>  extern void drm_crtc_vblank_on(struct drm_crtc *crtc);
>  extern void drm_vblank_cleanup(struct drm_device *dev);
> +extern u32 drm_accurate_vblank_count_and_time(struct drm_crtc *crtc,
> +					      struct timeval *tv_ret);
>  extern u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe);
>  
>  extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
> -- 
> 2.1.0

-- 
Ville Syrjälä
Intel OTC


More information about the dri-devel mailing list