diff mbox

[13/16] drm: Move ->get_scanout_position() to struct drm_crtc_funcs

Message ID 1443112538-9616-13-git-send-email-thierry.reding@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Thierry Reding Sept. 24, 2015, 4:35 p.m. UTC
From: Thierry Reding <treding@nvidia.com>

None of the drivers use this in legacy mode, so it can be converted to
use struct drm_crtc * directly. While at it, also make the sole user of
the callback, drm_calc_vbltimestamp_from_scanoutpos(), pass through the
CRTC directly.

v2: use standard [CRTC:%u] format

Cc: Daniel Vetter <daniel.vetter@intel.com>
Cc: Jani Nikula <jani.nikula@linux.intel.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_display.c | 13 ++++----
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c     |  1 -
 drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c     |  2 +-
 drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h    |  6 ++--
 drivers/gpu/drm/amd/amdgpu/dce_v10_0.c      |  1 +
 drivers/gpu/drm/amd/amdgpu/dce_v11_0.c      |  1 +
 drivers/gpu/drm/amd/amdgpu/dce_v8_0.c       |  1 +
 drivers/gpu/drm/drm_irq.c                   | 40 ++++++++++---------------
 drivers/gpu/drm/i915/i915_irq.c             | 14 ++++-----
 drivers/gpu/drm/i915/intel_display.c        |  1 +
 drivers/gpu/drm/i915/intel_drv.h            |  4 +++
 drivers/gpu/drm/nouveau/dispnv04/crtc.c     |  1 +
 drivers/gpu/drm/nouveau/nouveau_display.c   | 28 ++++--------------
 drivers/gpu/drm/nouveau/nouveau_display.h   | 12 ++++----
 drivers/gpu/drm/nouveau/nouveau_drm.c       |  1 -
 drivers/gpu/drm/nouveau/nv50_display.c      |  1 +
 drivers/gpu/drm/radeon/radeon_display.c     | 37 ++++++++++++-----------
 drivers/gpu/drm/radeon/radeon_drv.c         |  1 -
 drivers/gpu/drm/radeon/radeon_kms.c         |  2 +-
 drivers/gpu/drm/radeon/radeon_mode.h        |  6 ++--
 drivers/gpu/drm/radeon/radeon_pm.c          |  6 ++--
 include/drm/drmP.h                          | 46 -----------------------------
 include/drm/drm_crtc.h                      | 46 +++++++++++++++++++++++++++++
 23 files changed, 127 insertions(+), 144 deletions(-)

Comments

Daniel Vetter Sept. 24, 2015, 6:22 p.m. UTC | #1
On Thu, Sep 24, 2015 at 06:35:35PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> None of the drivers use this in legacy mode, so it can be converted to
> use struct drm_crtc * directly. While at it, also make the sole user of
> the callback, drm_calc_vbltimestamp_from_scanoutpos(), pass through the
> CRTC directly.
> 
> v2: use standard [CRTC:%u] format

I very much like this as a first small step towards untangling drm_irq.c.
Two comments below.

> 
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> Cc: Jani Nikula <jani.nikula@linux.intel.com>
> Cc: Ben Skeggs <bskeggs@redhat.com>
> Cc: Alex Deucher <alexander.deucher@amd.com>
> Cc: Christian König <christian.koenig@amd.com>
> Signed-off-by: Thierry Reding <treding@nvidia.com>


> diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
> index 4a5dee5cd327..525bd82ab514 100644
> --- a/drivers/gpu/drm/drm_irq.c
> +++ b/drivers/gpu/drm/drm_irq.c
> @@ -653,8 +653,7 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
>  
>  /**
>   * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
> - * @dev: DRM device
> - * @pipe: index of CRTC whose vblank timestamp to retrieve
> + * @crtc: CRTC whose vblank timestamp to retrieve
>   * @max_error: Desired maximum allowable error in timestamps (nanosecs)
>   *             On return contains true maximum error of timestamp
>   * @vblank_time: Pointer to struct timeval which should receive the timestamp
> @@ -696,13 +695,13 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
>   * DRM_VBLANKTIME_INVBL - Timestamp taken while scanout was in vblank interval.
>   *
>   */
> -int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
> -					  unsigned int pipe,
> +int drm_calc_vbltimestamp_from_scanoutpos(struct drm_crtc *crtc,
>  					  int *max_error,
>  					  struct timeval *vblank_time,
>  					  unsigned flags,
>  					  const struct drm_display_mode *mode)

This function is actually more a helper, since if you use some hardware
vblank timestamp register you don't really need it at all. Hence I think
we should fix this properly and instead move this function into a new
drm_irq_helper.c (part of drm_kms_helper.ko) - there will be more once
drm_irq.c is untangled. And also push the callback into the corresponding
helper funcs sturctures.

>  {
> +	const struct drm_crtc_funcs *funcs = crtc->funcs;
>  	struct timeval tv_etime;
>  	ktime_t stime, etime;
>  	unsigned int vbl_status;
> @@ -710,22 +709,16 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
>  	int vpos, hpos, i;
>  	int delta_ns, duration_ns;
>  
> -	if (pipe >= dev->num_crtcs) {
> -		DRM_ERROR("Invalid crtc %u\n", pipe);
> -		return -EINVAL;
> -	}
> -
>  	/* Scanout position query not supported? Should not happen. */
> -	if (!dev->driver->get_scanout_position) {
> -		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
> -		return -EIO;
> -	}
> +	if (WARN_ON(funcs->get_scanout_position == NULL))
> +		return -ENOSYS;
>  
>  	/* If mode timing undefined, just return as no-op:
>  	 * Happens during initial modesetting of a crtc.
>  	 */
>  	if (mode->crtc_clock == 0) {
> -		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
> +		DRM_DEBUG("[CRTC:%u] Noop due to uninitialized mode.\n",
> +			  crtc->base.id);
>  		return -EAGAIN;
>  	}
>  
> @@ -741,15 +734,14 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
>  		 * Get vertical and horizontal scanout position vpos, hpos,
>  		 * and bounding timestamps stime, etime, pre/post query.
>  		 */
> -		vbl_status = dev->driver->get_scanout_position(dev, pipe, flags,
> -							       &vpos, &hpos,
> -							       &stime, &etime,
> -							       mode);
> +		vbl_status = funcs->get_scanout_position(crtc, flags, &vpos,
> +							 &hpos, &stime, &etime,
> +							 mode);
>  
>  		/* Return as no-op if scanout query unsupported or failed. */
>  		if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
> -			DRM_DEBUG("crtc %u : scanoutpos query failed [0x%x].\n",
> -				  pipe, vbl_status);
> +			DRM_DEBUG("[CRTC:%u] scanoutpos query failed [%d].\n",
> +				  crtc->base.id, vbl_status);
>  			return -EIO;
>  		}
>  
> @@ -763,8 +755,8 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
>  
>  	/* Noisy system timing? */
>  	if (i == DRM_TIMESTAMP_MAXRETRIES) {
> -		DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n",
> -			  pipe, duration_ns/1000, *max_error/1000, i);
> +		DRM_DEBUG("[CRTC:%u] Noisy timestamp %d us > %d us [%d reps].\n",
> +			  crtc->base.id, duration_ns/1000, *max_error/1000, i);
>  	}
>  
>  	/* Return upper bound of timestamp precision error. */
> @@ -799,8 +791,8 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
>  		etime = ktime_sub_ns(etime, delta_ns);
>  	*vblank_time = ktime_to_timeval(etime);
>  
> -	DRM_DEBUG("crtc %u : v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
> -		  pipe, vbl_status, hpos, vpos,
> +	DRM_DEBUG("[CRTC:%u] v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
> +		  crtc->base.id, vbl_status, hpos, vpos,
>  		  (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
>  		  (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
>  		  duration_ns/1000, i);
> diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
> index 7b3aeb0f8056..6eec529b3a5b 100644
> --- a/drivers/gpu/drm/i915/i915_irq.c
> +++ b/drivers/gpu/drm/i915/i915_irq.c
> @@ -767,14 +767,15 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
>  	return (position + crtc->scanline_offset) % vtotal;
>  }
>  
> -static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
> -				    unsigned int flags, int *vpos, int *hpos,
> -				    ktime_t *stime, ktime_t *etime,
> -				    const struct drm_display_mode *mode)
> +int i915_get_crtc_scanoutpos(struct drm_crtc *crtc, unsigned int flags,
> +			     int *vpos, int *hpos, ktime_t *stime,
> +			     ktime_t *etime,
> +			     const struct drm_display_mode *mode)
>  {
> +	struct drm_device *dev = crtc->dev;
>  	struct drm_i915_private *dev_priv = dev->dev_private;
> -	struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
>  	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
> +	enum pipe pipe = intel_crtc->pipe;
>  	int position;
>  	int vbl_start, vbl_end, hsync_start, htotal, vtotal;
>  	bool in_vbl = true;
> @@ -929,7 +930,7 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
>  	}
>  
>  	/* Helper routine in DRM core does all the work: */
> -	return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
> +	return drm_calc_vbltimestamp_from_scanoutpos(crtc, max_error,
>  						     vblank_time, flags,
>  						     &crtc->hwmode);
>  }
> @@ -4387,7 +4388,6 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
>  		dev->vblank_disable_immediate = true;
>  
>  	dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
> -	dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
>  
>  	if (IS_CHERRYVIEW(dev_priv)) {
>  		dev->driver->irq_handler = cherryview_irq_handler;

> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 683f1421a825..c5c9e316251a 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -307,6 +307,11 @@ struct drm_crtc_state {
>  	struct drm_atomic_state *state;
>  };
>  
> +/* get_scanout_position() return flags */
> +#define DRM_SCANOUTPOS_VALID        (1 << 0)
> +#define DRM_SCANOUTPOS_IN_VBLANK    (1 << 1)
> +#define DRM_SCANOUTPOS_ACCURATE     (1 << 2)
> +
>  /**
>   * struct drm_crtc_funcs - control CRTCs for a given device
>   * @save: save CRTC state
> @@ -326,6 +331,7 @@ struct drm_crtc_state {
>   *    (do not call directly, use drm_atomic_crtc_set_property())
>   * @atomic_get_property: get a property on an atomic state for this CRTC
>   *    (do not call directly, use drm_atomic_crtc_get_property())
> + * @get_scanout_position: return the current scanout position
>   *
>   * The drm_crtc_funcs structure is the central CRTC management structure
>   * in the DRM.  Each CRTC controls one or more connectors (note that the name
> @@ -389,6 +395,40 @@ struct drm_crtc_funcs {
>  				   const struct drm_crtc_state *state,
>  				   struct drm_property *property,
>  				   uint64_t *val);
> +
> +	/**

Please use the new inline structure documentation layout for this (merged
into 4.3): Just drop the @get_scanout_position: from the top-level comment
and add that here.
-Daniel

> +	 * Called by vblank timestamping code.
> +	 *
> +	 * Return the current display scanout position from a crtc, and an
> +	 * optional accurate ktime_get timestamp of when position was measured.
> +	 *
> +	 * \param crtc CRTC to query.
> +	 * \param flags Flags from the caller (DRM_CALLED_FROM_VBLIRQ or 0).
> +	 * \param *vpos Target location for current vertical scanout position.
> +	 * \param *hpos Target location for current horizontal scanout position.
> +	 * \param *stime Target location for timestamp taken immediately before
> +	 *               scanout position query. Can be NULL to skip timestamp.
> +	 * \param *etime Target location for timestamp taken immediately after
> +	 *               scanout position query. Can be NULL to skip timestamp.
> +	 * \param mode Current display timings.
> +	 *
> +	 * Returns vpos as a positive number while in active scanout area.
> +	 * Returns vpos as a negative number inside vblank, counting the number
> +	 * of scanlines to go until end of vblank, e.g., -1 means "one scanline
> +	 * until start of active scanout / end of vblank."
> +	 *
> +	 * \return Flags, or'ed together as follows:
> +	 *
> +	 * DRM_SCANOUTPOS_VALID = Query successful.
> +	 * DRM_SCANOUTPOS_INVBL = Inside vblank.
> +	 * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of
> +	 * this flag means that returned position may be offset by a constant
> +	 * but unknown small number of scanlines wrt. real scanout position.
> +	 */
> +	int (*get_scanout_position)(struct drm_crtc *crtc, unsigned int flags,
> +				    int *vpos, int *hpos, ktime_t *stime,
> +				    ktime_t *etime,
> +				    const struct drm_display_mode *mode);
>  };
>  
>  /**
> @@ -1194,6 +1234,12 @@ extern int drm_crtc_init_with_planes(struct drm_device *dev,
>  extern void drm_crtc_cleanup(struct drm_crtc *crtc);
>  extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
>  
> +extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_crtc *crtc,
> +						 int *max_error,
> +						 struct timeval *vblank_time,
> +						 unsigned flags,
> +						 const struct drm_display_mode *mode);
> +
>  /**
>   * drm_crtc_mask - find the mask of a registered CRTC
>   * @crtc: CRTC to find mask for
> -- 
> 2.5.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
diff mbox

Patch

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
index de116398fa49..ef2a32854fac 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
@@ -720,8 +720,7 @@  bool amdgpu_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
  * Retrieve current video scanout position of crtc on a given gpu, and
  * an optional accurate timestamp of when query happened.
  *
- * \param dev Device to query.
- * \param pipe Crtc to query.
+ * \param crtc CRTC to query.
  * \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0).
  * \param *vpos Location where vertical scanout position should be stored.
  * \param *hpos Location where horizontal scanout position should go.
@@ -744,17 +743,17 @@  bool amdgpu_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
  * unknown small number of scanlines wrt. real scanout position.
  *
  */
-int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
-			       unsigned int flags, int *vpos, int *hpos,
-			       ktime_t *stime, ktime_t *etime,
+int amdgpu_get_crtc_scanoutpos(struct drm_crtc *crtc, unsigned int flags,
+			       int *vpos, int *hpos, ktime_t *stime,
+			       ktime_t *etime,
 			       const struct drm_display_mode *mode)
 {
+	struct amdgpu_device *adev = crtc->dev->dev_private;
+	unsigned int pipe = drm_crtc_index(crtc);
 	u32 vbl = 0, position = 0;
 	int vbl_start, vbl_end, vtotal, ret = 0;
 	bool in_vbl = true;
 
-	struct amdgpu_device *adev = dev->dev_private;
-
 	/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
 
 	/* Get optional system timestamp before query. */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 0fcc0bd1622c..b44f0bd9edfc 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -480,7 +480,6 @@  static struct drm_driver kms_driver = {
 	.enable_vblank = amdgpu_enable_vblank_kms,
 	.disable_vblank = amdgpu_disable_vblank_kms,
 	.get_vblank_timestamp = amdgpu_get_vblank_timestamp_kms,
-	.get_scanout_position = amdgpu_get_crtc_scanoutpos,
 #if defined(CONFIG_DEBUG_FS)
 	.debugfs_init = amdgpu_debugfs_init,
 	.debugfs_cleanup = amdgpu_debugfs_cleanup,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index 0118fd3cda13..231b7a4c1391 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -679,7 +679,7 @@  int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
 	crtc = &adev->mode_info.crtcs[pipe]->base;
 
 	/* Helper routine in DRM core does all the work: */
-	return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
+	return drm_calc_vbltimestamp_from_scanoutpos(crtc, max_error,
 						     vblank_time, flags,
 						     &crtc->hwmode);
 }
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
index f6b02994442b..4e0c66adfaf8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
@@ -540,9 +540,9 @@  bool amdgpu_ddc_probe(struct amdgpu_connector *amdgpu_connector, bool use_aux);
 
 void amdgpu_encoder_set_active_device(struct drm_encoder *encoder);
 
-int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
-			       unsigned int flags, int *vpos, int *hpos,
-			       ktime_t *stime, ktime_t *etime,
+int amdgpu_get_crtc_scanoutpos(struct drm_crtc *crtc, unsigned int flags,
+			       int *vpos, int *hpos, ktime_t *stime,
+			       ktime_t *etime,
 			       const struct drm_display_mode *mode);
 
 int amdgpu_framebuffer_init(struct drm_device *dev,
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
index e4d101b1252a..1932f41b610d 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
@@ -2647,6 +2647,7 @@  static const struct drm_crtc_funcs dce_v10_0_crtc_funcs = {
 	.set_config = amdgpu_crtc_set_config,
 	.destroy = dce_v10_0_crtc_destroy,
 	.page_flip = amdgpu_crtc_page_flip,
+	.get_scanout_position = amdgpu_get_crtc_scanoutpos,
 };
 
 static void dce_v10_0_crtc_dpms(struct drm_crtc *crtc, int mode)
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
index 6411e8244671..e89bce0aabca 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
@@ -2624,6 +2624,7 @@  static const struct drm_crtc_funcs dce_v11_0_crtc_funcs = {
 	.set_config = amdgpu_crtc_set_config,
 	.destroy = dce_v11_0_crtc_destroy,
 	.page_flip = amdgpu_crtc_page_flip,
+	.get_scanout_position = amdgpu_get_crtc_scanoutpos,
 };
 
 static void dce_v11_0_crtc_dpms(struct drm_crtc *crtc, int mode)
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
index c86911c2ea2a..f6ab329b2e2f 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
@@ -2559,6 +2559,7 @@  static const struct drm_crtc_funcs dce_v8_0_crtc_funcs = {
 	.set_config = amdgpu_crtc_set_config,
 	.destroy = dce_v8_0_crtc_destroy,
 	.page_flip = amdgpu_crtc_page_flip,
+	.get_scanout_position = amdgpu_get_crtc_scanoutpos,
 };
 
 static void dce_v8_0_crtc_dpms(struct drm_crtc *crtc, int mode)
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 4a5dee5cd327..525bd82ab514 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -653,8 +653,7 @@  EXPORT_SYMBOL(drm_calc_timestamping_constants);
 
 /**
  * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper
- * @dev: DRM device
- * @pipe: index of CRTC whose vblank timestamp to retrieve
+ * @crtc: CRTC whose vblank timestamp to retrieve
  * @max_error: Desired maximum allowable error in timestamps (nanosecs)
  *             On return contains true maximum error of timestamp
  * @vblank_time: Pointer to struct timeval which should receive the timestamp
@@ -696,13 +695,13 @@  EXPORT_SYMBOL(drm_calc_timestamping_constants);
  * DRM_VBLANKTIME_INVBL - Timestamp taken while scanout was in vblank interval.
  *
  */
-int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
-					  unsigned int pipe,
+int drm_calc_vbltimestamp_from_scanoutpos(struct drm_crtc *crtc,
 					  int *max_error,
 					  struct timeval *vblank_time,
 					  unsigned flags,
 					  const struct drm_display_mode *mode)
 {
+	const struct drm_crtc_funcs *funcs = crtc->funcs;
 	struct timeval tv_etime;
 	ktime_t stime, etime;
 	unsigned int vbl_status;
@@ -710,22 +709,16 @@  int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
 	int vpos, hpos, i;
 	int delta_ns, duration_ns;
 
-	if (pipe >= dev->num_crtcs) {
-		DRM_ERROR("Invalid crtc %u\n", pipe);
-		return -EINVAL;
-	}
-
 	/* Scanout position query not supported? Should not happen. */
-	if (!dev->driver->get_scanout_position) {
-		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
-		return -EIO;
-	}
+	if (WARN_ON(funcs->get_scanout_position == NULL))
+		return -ENOSYS;
 
 	/* If mode timing undefined, just return as no-op:
 	 * Happens during initial modesetting of a crtc.
 	 */
 	if (mode->crtc_clock == 0) {
-		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
+		DRM_DEBUG("[CRTC:%u] Noop due to uninitialized mode.\n",
+			  crtc->base.id);
 		return -EAGAIN;
 	}
 
@@ -741,15 +734,14 @@  int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
 		 * Get vertical and horizontal scanout position vpos, hpos,
 		 * and bounding timestamps stime, etime, pre/post query.
 		 */
-		vbl_status = dev->driver->get_scanout_position(dev, pipe, flags,
-							       &vpos, &hpos,
-							       &stime, &etime,
-							       mode);
+		vbl_status = funcs->get_scanout_position(crtc, flags, &vpos,
+							 &hpos, &stime, &etime,
+							 mode);
 
 		/* Return as no-op if scanout query unsupported or failed. */
 		if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
-			DRM_DEBUG("crtc %u : scanoutpos query failed [0x%x].\n",
-				  pipe, vbl_status);
+			DRM_DEBUG("[CRTC:%u] scanoutpos query failed [%d].\n",
+				  crtc->base.id, vbl_status);
 			return -EIO;
 		}
 
@@ -763,8 +755,8 @@  int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
 
 	/* Noisy system timing? */
 	if (i == DRM_TIMESTAMP_MAXRETRIES) {
-		DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n",
-			  pipe, duration_ns/1000, *max_error/1000, i);
+		DRM_DEBUG("[CRTC:%u] Noisy timestamp %d us > %d us [%d reps].\n",
+			  crtc->base.id, duration_ns/1000, *max_error/1000, i);
 	}
 
 	/* Return upper bound of timestamp precision error. */
@@ -799,8 +791,8 @@  int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
 		etime = ktime_sub_ns(etime, delta_ns);
 	*vblank_time = ktime_to_timeval(etime);
 
-	DRM_DEBUG("crtc %u : v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
-		  pipe, vbl_status, hpos, vpos,
+	DRM_DEBUG("[CRTC:%u] v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
+		  crtc->base.id, vbl_status, hpos, vpos,
 		  (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
 		  (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
 		  duration_ns/1000, i);
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 7b3aeb0f8056..6eec529b3a5b 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -767,14 +767,15 @@  static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
 	return (position + crtc->scanline_offset) % vtotal;
 }
 
-static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
-				    unsigned int flags, int *vpos, int *hpos,
-				    ktime_t *stime, ktime_t *etime,
-				    const struct drm_display_mode *mode)
+int i915_get_crtc_scanoutpos(struct drm_crtc *crtc, unsigned int flags,
+			     int *vpos, int *hpos, ktime_t *stime,
+			     ktime_t *etime,
+			     const struct drm_display_mode *mode)
 {
+	struct drm_device *dev = crtc->dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
-	struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	enum pipe pipe = intel_crtc->pipe;
 	int position;
 	int vbl_start, vbl_end, hsync_start, htotal, vtotal;
 	bool in_vbl = true;
@@ -929,7 +930,7 @@  static int i915_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
 	}
 
 	/* Helper routine in DRM core does all the work: */
-	return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
+	return drm_calc_vbltimestamp_from_scanoutpos(crtc, max_error,
 						     vblank_time, flags,
 						     &crtc->hwmode);
 }
@@ -4387,7 +4388,6 @@  void intel_irq_init(struct drm_i915_private *dev_priv)
 		dev->vblank_disable_immediate = true;
 
 	dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
-	dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
 
 	if (IS_CHERRYVIEW(dev_priv)) {
 		dev->driver->irq_handler = cherryview_irq_handler;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 2a1fab3eb285..48965f206fda 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -13203,6 +13203,7 @@  static const struct drm_crtc_funcs intel_crtc_funcs = {
 	.page_flip = intel_crtc_page_flip,
 	.atomic_duplicate_state = intel_crtc_duplicate_state,
 	.atomic_destroy_state = intel_crtc_destroy_state,
+	.get_scanout_position = i915_get_crtc_scanoutpos,
 };
 
 static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv,
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index f4242d23f63f..bc86ae28d455 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -955,6 +955,10 @@  static inline bool intel_irqs_enabled(struct drm_i915_private *dev_priv)
 	return dev_priv->pm.irqs_enabled;
 }
 
+int i915_get_crtc_scanoutpos(struct drm_crtc *crtc, unsigned int flags,
+			     int *vpos, int *hpos, ktime_t *stime,
+			     ktime_t *etime,
+			     const struct drm_display_mode *mode);
 int intel_get_crtc_scanline(struct intel_crtc *crtc);
 void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv,
 				     unsigned int pipe_mask);
diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
index 3d96b49fe662..1a1f9dec1572 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
@@ -1089,6 +1089,7 @@  static const struct drm_crtc_funcs nv04_crtc_funcs = {
 	.set_config = nouveau_crtc_set_config,
 	.page_flip = nouveau_crtc_page_flip,
 	.destroy = nv_crtc_destroy,
+	.get_scanout_position = nouveau_display_scanoutpos,
 };
 
 static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = {
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 886079dd9baa..8bd8421ce63c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -92,8 +92,9 @@  calc(int blanks, int blanke, int total, int line)
 }
 
 int
-nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
-				ktime_t *stime, ktime_t *etime)
+nouveau_display_scanoutpos(struct drm_crtc *crtc, unsigned int flags,
+			   int *vpos, int *hpos, ktime_t *stime,
+			   ktime_t *etime, const struct drm_display_mode *mode)
 {
 	struct {
 		struct nv04_disp_mthd_v0 base;
@@ -132,24 +133,6 @@  nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
 }
 
 int
-nouveau_display_scanoutpos(struct drm_device *dev, unsigned int pipe,
-			   unsigned int flags, int *vpos, int *hpos,
-			   ktime_t *stime, ktime_t *etime,
-			   const struct drm_display_mode *mode)
-{
-	struct drm_crtc *crtc;
-
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-		if (nouveau_crtc(crtc)->index == pipe) {
-			return nouveau_display_scanoutpos_head(crtc, vpos, hpos,
-							       stime, etime);
-		}
-	}
-
-	return 0;
-}
-
-int
 nouveau_display_vblstamp(struct drm_device *dev, unsigned int pipe,
 			 int *max_error, struct timeval *time, unsigned flags)
 {
@@ -157,9 +140,8 @@  nouveau_display_vblstamp(struct drm_device *dev, unsigned int pipe,
 
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		if (nouveau_crtc(crtc)->index == pipe) {
-			return drm_calc_vbltimestamp_from_scanoutpos(dev,
-					pipe, max_error, time, flags,
-					&crtc->hwmode);
+			return drm_calc_vbltimestamp_from_scanoutpos(crtc,
+					max_error, time, flags, &crtc->hwmode);
 		}
 	}
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h
index 856abe0f070d..e8b40117cb04 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.h
+++ b/drivers/gpu/drm/nouveau/nouveau_display.h
@@ -65,12 +65,12 @@  int  nouveau_display_init(struct drm_device *dev);
 void nouveau_display_fini(struct drm_device *dev);
 int  nouveau_display_suspend(struct drm_device *dev, bool runtime);
 void nouveau_display_resume(struct drm_device *dev, bool runtime);
-int  nouveau_display_vblank_enable(struct drm_device *, unsigned int);
-void nouveau_display_vblank_disable(struct drm_device *, unsigned int);
-int  nouveau_display_scanoutpos(struct drm_device *, unsigned int,
-				unsigned int, int *, int *, ktime_t *,
-				ktime_t *, const struct drm_display_mode *);
-int  nouveau_display_vblstamp(struct drm_device *, unsigned int, int *,
+int  nouveau_display_vblank_enable(struct drm_device *, unsigned int pipe);
+void nouveau_display_vblank_disable(struct drm_device *, unsigned int pipe);
+int  nouveau_display_scanoutpos(struct drm_crtc *, unsigned int, int *, int *,
+				ktime_t *, ktime_t *,
+				const struct drm_display_mode *);
+int  nouveau_display_vblstamp(struct drm_device *, unsigned int pipe, int *,
 			      struct timeval *, unsigned);
 
 int  nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index ccefb645fd55..55eac5310449 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -937,7 +937,6 @@  driver_stub = {
 	.get_vblank_counter = drm_vblank_count,
 	.enable_vblank = nouveau_display_vblank_enable,
 	.disable_vblank = nouveau_display_vblank_disable,
-	.get_scanout_position = nouveau_display_scanoutpos,
 	.get_vblank_timestamp = nouveau_display_vblstamp,
 
 	.ioctls = nouveau_ioctls,
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 4ae87aed4505..0cd1826c20e4 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -1423,6 +1423,7 @@  static const struct drm_crtc_funcs nv50_crtc_func = {
 	.set_config = nouveau_crtc_set_config,
 	.destroy = nv50_crtc_destroy,
 	.page_flip = nouveau_crtc_page_flip,
+	.get_scanout_position = nouveau_display_scanoutpos,
 };
 
 static int
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index a58635c5db3d..8dd7ffebef7f 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -322,10 +322,10 @@  void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id)
 	 * to complete in this vblank?
 	 */
 	if (update_pending &&
-	    (DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id, 0,
+	    (DRM_SCANOUTPOS_VALID & radeon_crtc_get_scanoutpos(&radeon_crtc->base, 0,
 							       &vpos, &hpos, NULL, NULL,
-							       &rdev->mode_info.crtcs[crtc_id]->base.hwmode)) &&
-	    ((vpos >= (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100) ||
+							       &radeon_crtc->base.hwmode)) &&
+	    ((vpos >= (99 * radeon_crtc->base.hwmode.crtc_vdisplay)/100) ||
 	     (vpos < 0 && !ASIC_IS_AVIVO(rdev)))) {
 		/* crtc didn't flip in this target vblank interval,
 		 * but flip is pending in crtc. Based on the current
@@ -642,6 +642,7 @@  static const struct drm_crtc_funcs radeon_crtc_funcs = {
 	.set_config = radeon_crtc_set_config,
 	.destroy = radeon_crtc_destroy,
 	.page_flip = radeon_crtc_page_flip,
+	.get_scanout_position = radeon_crtc_get_scanoutpos,
 };
 
 static void radeon_crtc_init(struct drm_device *dev, int index)
@@ -1775,7 +1776,6 @@  bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
  * Retrieve current video scanout position of crtc on a given gpu, and
  * an optional accurate timestamp of when query happened.
  *
- * \param dev Device to query.
  * \param crtc Crtc to query.
  * \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0).
  * \param *vpos Location where vertical scanout position should be stored.
@@ -1799,16 +1799,17 @@  bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
  * unknown small number of scanlines wrt. real scanout position.
  *
  */
-int radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
-			       unsigned int flags, int *vpos, int *hpos,
-			       ktime_t *stime, ktime_t *etime,
+int radeon_crtc_get_scanoutpos(struct drm_crtc *crtc, unsigned int flags,
+			       int *vpos, int *hpos, ktime_t *stime,
+			       ktime_t *etime,
 			       const struct drm_display_mode *mode)
 {
+	struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
 	u32 stat_crtc = 0, vbl = 0, position = 0;
 	int vbl_start, vbl_end, vtotal, ret = 0;
 	bool in_vbl = true;
 
-	struct radeon_device *rdev = dev->dev_private;
+	struct radeon_device *rdev = crtc->dev->dev_private;
 
 	/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
 
@@ -1817,42 +1818,42 @@  int radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
 		*stime = ktime_get();
 
 	if (ASIC_IS_DCE4(rdev)) {
-		if (pipe == 0) {
+		if (radeon_crtc->crtc_id == 0) {
 			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
 				     EVERGREEN_CRTC0_REGISTER_OFFSET);
 			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
 					  EVERGREEN_CRTC0_REGISTER_OFFSET);
 			ret |= DRM_SCANOUTPOS_VALID;
 		}
-		if (pipe == 1) {
+		if (radeon_crtc->crtc_id == 1) {
 			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
 				     EVERGREEN_CRTC1_REGISTER_OFFSET);
 			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
 					  EVERGREEN_CRTC1_REGISTER_OFFSET);
 			ret |= DRM_SCANOUTPOS_VALID;
 		}
-		if (pipe == 2) {
+		if (radeon_crtc->crtc_id == 2) {
 			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
 				     EVERGREEN_CRTC2_REGISTER_OFFSET);
 			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
 					  EVERGREEN_CRTC2_REGISTER_OFFSET);
 			ret |= DRM_SCANOUTPOS_VALID;
 		}
-		if (pipe == 3) {
+		if (radeon_crtc->crtc_id == 3) {
 			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
 				     EVERGREEN_CRTC3_REGISTER_OFFSET);
 			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
 					  EVERGREEN_CRTC3_REGISTER_OFFSET);
 			ret |= DRM_SCANOUTPOS_VALID;
 		}
-		if (pipe == 4) {
+		if (radeon_crtc->crtc_id == 4) {
 			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
 				     EVERGREEN_CRTC4_REGISTER_OFFSET);
 			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
 					  EVERGREEN_CRTC4_REGISTER_OFFSET);
 			ret |= DRM_SCANOUTPOS_VALID;
 		}
-		if (pipe == 5) {
+		if (radeon_crtc->crtc_id == 5) {
 			vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END +
 				     EVERGREEN_CRTC5_REGISTER_OFFSET);
 			position = RREG32(EVERGREEN_CRTC_STATUS_POSITION +
@@ -1860,19 +1861,19 @@  int radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
 			ret |= DRM_SCANOUTPOS_VALID;
 		}
 	} else if (ASIC_IS_AVIVO(rdev)) {
-		if (pipe == 0) {
+		if (radeon_crtc->crtc_id == 0) {
 			vbl = RREG32(AVIVO_D1CRTC_V_BLANK_START_END);
 			position = RREG32(AVIVO_D1CRTC_STATUS_POSITION);
 			ret |= DRM_SCANOUTPOS_VALID;
 		}
-		if (pipe == 1) {
+		if (radeon_crtc->crtc_id == 1) {
 			vbl = RREG32(AVIVO_D2CRTC_V_BLANK_START_END);
 			position = RREG32(AVIVO_D2CRTC_STATUS_POSITION);
 			ret |= DRM_SCANOUTPOS_VALID;
 		}
 	} else {
 		/* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */
-		if (pipe == 0) {
+		if (radeon_crtc->crtc_id == 0) {
 			/* Assume vbl_end == 0, get vbl_start from
 			 * upper 16 bits.
 			 */
@@ -1886,7 +1887,7 @@  int radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
 
 			ret |= DRM_SCANOUTPOS_VALID;
 		}
-		if (pipe == 1) {
+		if (radeon_crtc->crtc_id == 1) {
 			vbl = (RREG32(RADEON_CRTC2_V_TOTAL_DISP) &
 				RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT;
 			position = (RREG32(RADEON_CRTC2_VLINE_CRNT_VLINE) >> 16) & RADEON_CRTC_V_TOTAL;
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 5b6a6f5b3619..30ef157f02b5 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -578,7 +578,6 @@  static struct drm_driver kms_driver = {
 	.enable_vblank = radeon_enable_vblank_kms,
 	.disable_vblank = radeon_disable_vblank_kms,
 	.get_vblank_timestamp = radeon_get_vblank_timestamp_kms,
-	.get_scanout_position = radeon_get_crtc_scanoutpos,
 #if defined(CONFIG_DEBUG_FS)
 	.debugfs_init = radeon_debugfs_init,
 	.debugfs_cleanup = radeon_debugfs_cleanup,
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index fd9da282b29c..f3bcf925c3ba 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -839,7 +839,7 @@  int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc,
 		return -EINVAL;
 
 	/* Helper routine in DRM core does all the work: */
-	return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error,
+	return drm_calc_vbltimestamp_from_scanoutpos(drmcrtc, max_error,
 						     vblank_time, flags,
 						     &drmcrtc->hwmode);
 }
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index de18f0668bea..4e11f1e40cef 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -874,9 +874,9 @@  extern int radeon_crtc_cursor_move(struct drm_crtc *crtc,
 				   int x, int y);
 extern void radeon_cursor_reset(struct drm_crtc *crtc);
 
-extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
-				      unsigned int flags, int *vpos, int *hpos,
-				      ktime_t *stime, ktime_t *etime,
+extern int radeon_crtc_get_scanoutpos(struct drm_crtc *crtc, unsigned int flags,
+				      int *vpos, int *hpos, ktime_t *stime,
+				      ktime_t *etime,
 				      const struct drm_display_mode *mode);
 
 extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 10f4c12e439e..dc4c29938cd1 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -1732,10 +1732,12 @@  static bool radeon_pm_in_vbl(struct radeon_device *rdev)
 	 * otherwise return in_vbl == false.
 	 */
 	for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) {
+		struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc];
+
 		if (rdev->pm.active_crtcs & (1 << crtc)) {
-			vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, 0,
+			vbl_status = radeon_crtc_get_scanoutpos(&radeon_crtc->base, 0,
 								&vpos, &hpos, NULL, NULL,
-								&rdev->mode_info.crtcs[crtc]->base.hwmode);
+								&radeon_crtc->base.hwmode);
 			if ((vbl_status & DRM_SCANOUTPOS_VALID) &&
 			    !(vbl_status & DRM_SCANOUTPOS_IN_VBLANK))
 				in_vbl = false;
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index a013a0ed8e6a..ce693a1366c1 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -384,11 +384,6 @@  struct drm_master {
 #define DRM_VBLANKTIME_SCANOUTPOS_METHOD (1 << 0)
 #define DRM_VBLANKTIME_IN_VBLANK         (1 << 1)
 
-/* get_scanout_position() return flags */
-#define DRM_SCANOUTPOS_VALID        (1 << 0)
-#define DRM_SCANOUTPOS_IN_VBLANK    (1 << 1)
-#define DRM_SCANOUTPOS_ACCURATE     (1 << 2)
-
 /**
  * DRM driver structure. This structure represent the common code for
  * a family of cards. There will one drm_device for each card present
@@ -468,42 +463,6 @@  struct drm_driver {
 	int (*device_is_agp) (struct drm_device *dev);
 
 	/**
-	 * Called by vblank timestamping code.
-	 *
-	 * Return the current display scanout position from a crtc, and an
-	 * optional accurate ktime_get timestamp of when position was measured.
-	 *
-	 * \param dev  DRM device.
-	 * \param pipe Id of the crtc to query.
-	 * \param flags Flags from the caller (DRM_CALLED_FROM_VBLIRQ or 0).
-	 * \param *vpos Target location for current vertical scanout position.
-	 * \param *hpos Target location for current horizontal scanout position.
-	 * \param *stime Target location for timestamp taken immediately before
-	 *               scanout position query. Can be NULL to skip timestamp.
-	 * \param *etime Target location for timestamp taken immediately after
-	 *               scanout position query. Can be NULL to skip timestamp.
-	 * \param mode Current display timings.
-	 *
-	 * Returns vpos as a positive number while in active scanout area.
-	 * Returns vpos as a negative number inside vblank, counting the number
-	 * of scanlines to go until end of vblank, e.g., -1 means "one scanline
-	 * until start of active scanout / end of vblank."
-	 *
-	 * \return Flags, or'ed together as follows:
-	 *
-	 * DRM_SCANOUTPOS_VALID = Query successful.
-	 * DRM_SCANOUTPOS_INVBL = Inside vblank.
-	 * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of
-	 * this flag means that returned position may be offset by a constant
-	 * but unknown small number of scanlines wrt. real scanout position.
-	 *
-	 */
-	int (*get_scanout_position) (struct drm_device *dev, unsigned int pipe,
-				     unsigned int flags, int *vpos, int *hpos,
-				     ktime_t *stime, ktime_t *etime,
-				     const struct drm_display_mode *mode);
-
-	/**
 	 * Called by \c drm_get_last_vbltimestamp. Should return a precise
 	 * timestamp when the most recent VBLANK interval ended or will end.
 	 *
@@ -949,11 +908,6 @@  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 int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
-						 unsigned int pipe, int *max_error,
-						 struct timeval *vblank_time,
-						 unsigned flags,
-						 const struct drm_display_mode *mode);
 extern void drm_calc_timestamping_constants(struct drm_crtc *crtc,
 					    const struct drm_display_mode *mode);
 
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 683f1421a825..c5c9e316251a 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -307,6 +307,11 @@  struct drm_crtc_state {
 	struct drm_atomic_state *state;
 };
 
+/* get_scanout_position() return flags */
+#define DRM_SCANOUTPOS_VALID        (1 << 0)
+#define DRM_SCANOUTPOS_IN_VBLANK    (1 << 1)
+#define DRM_SCANOUTPOS_ACCURATE     (1 << 2)
+
 /**
  * struct drm_crtc_funcs - control CRTCs for a given device
  * @save: save CRTC state
@@ -326,6 +331,7 @@  struct drm_crtc_state {
  *    (do not call directly, use drm_atomic_crtc_set_property())
  * @atomic_get_property: get a property on an atomic state for this CRTC
  *    (do not call directly, use drm_atomic_crtc_get_property())
+ * @get_scanout_position: return the current scanout position
  *
  * The drm_crtc_funcs structure is the central CRTC management structure
  * in the DRM.  Each CRTC controls one or more connectors (note that the name
@@ -389,6 +395,40 @@  struct drm_crtc_funcs {
 				   const struct drm_crtc_state *state,
 				   struct drm_property *property,
 				   uint64_t *val);
+
+	/**
+	 * Called by vblank timestamping code.
+	 *
+	 * Return the current display scanout position from a crtc, and an
+	 * optional accurate ktime_get timestamp of when position was measured.
+	 *
+	 * \param crtc CRTC to query.
+	 * \param flags Flags from the caller (DRM_CALLED_FROM_VBLIRQ or 0).
+	 * \param *vpos Target location for current vertical scanout position.
+	 * \param *hpos Target location for current horizontal scanout position.
+	 * \param *stime Target location for timestamp taken immediately before
+	 *               scanout position query. Can be NULL to skip timestamp.
+	 * \param *etime Target location for timestamp taken immediately after
+	 *               scanout position query. Can be NULL to skip timestamp.
+	 * \param mode Current display timings.
+	 *
+	 * Returns vpos as a positive number while in active scanout area.
+	 * Returns vpos as a negative number inside vblank, counting the number
+	 * of scanlines to go until end of vblank, e.g., -1 means "one scanline
+	 * until start of active scanout / end of vblank."
+	 *
+	 * \return Flags, or'ed together as follows:
+	 *
+	 * DRM_SCANOUTPOS_VALID = Query successful.
+	 * DRM_SCANOUTPOS_INVBL = Inside vblank.
+	 * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of
+	 * this flag means that returned position may be offset by a constant
+	 * but unknown small number of scanlines wrt. real scanout position.
+	 */
+	int (*get_scanout_position)(struct drm_crtc *crtc, unsigned int flags,
+				    int *vpos, int *hpos, ktime_t *stime,
+				    ktime_t *etime,
+				    const struct drm_display_mode *mode);
 };
 
 /**
@@ -1194,6 +1234,12 @@  extern int drm_crtc_init_with_planes(struct drm_device *dev,
 extern void drm_crtc_cleanup(struct drm_crtc *crtc);
 extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
 
+extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_crtc *crtc,
+						 int *max_error,
+						 struct timeval *vblank_time,
+						 unsigned flags,
+						 const struct drm_display_mode *mode);
+
 /**
  * drm_crtc_mask - find the mask of a registered CRTC
  * @crtc: CRTC to find mask for