[RFC PATCH] drm/vblanks: Deal with HW vblank counter resets.

Dhinakaran Pandiyan dhinakaran.pandiyan at intel.com
Tue Nov 7 06:26:17 UTC 2017


Some HW vblank counters reset due to power management events, which messes
up the vblank counting logic. This leads to screen freezes with user space
waiting on vblank events that may not occur if the counter keeps resetting.

For e.g., After the HW vblank counter resets
[    9.007359] [drm:drm_update_vblank_count [drm]] updating vblank count
on crtc 0: current=297, diff=4294965389, hw=5 hw_last=1912

So, fall back to the SW counter, computed using  vblank timestamps
and frame duration, when the HW counter value deviates by 50% of the SW
computed value.

I have tested this patch on my SKL laptop with i915.enable_psr=1 and it
*seems* to solve the screen freeze issue seen with PSR when DMC is loaded.

Known issues:
1) The 50% deviation margin is arbitrary.
2) "Redundant vblirq ignored" messages are more frequent.

I am sending this as an RFC to get feedback on whether the fall back
approach is sane and if it should be implemented in the core.

Cc: Daniel Vetter <daniel.vetter at ffwll.ch>
Cc: Rodrigo Vivi <rodrigo.vivi at intel.com>
Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
Signed-off-by: Dhinakaran Pandiyan <dhinakaran.pandiyan at intel.com>
---
 drivers/gpu/drm/drm_vblank.c | 26 ++++++++++++++++----------
 1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 57cc6e37c810..8000aae5f1f7 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -190,11 +190,12 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
 				    bool in_vblank_irq)
 {
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
-	u32 cur_vblank, diff;
+	u32 cur_vblank;
 	bool rc;
 	ktime_t t_vblank;
 	int count = DRM_TIMESTAMP_MAXRETRIES;
 	int framedur_ns = vblank->framedur_ns;
+	u32 diff = in_vblank_irq ? 1 : 0;
 
 	/*
 	 * Interrupts were disabled prior to this call, so deal with counter
@@ -213,26 +214,31 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
 		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
 	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
 
-	if (dev->max_vblank_count != 0) {
-		/* trust the hw counter when it's around */
+	if (dev->max_vblank_count)
 		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
-	} else if (rc && framedur_ns) {
+
+	if (rc && framedur_ns) {
 		u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));
+		u32 sw_diff;
 
 		/*
 		 * Figure out how many vblanks we've missed based
 		 * on the difference in the timestamps and the
 		 * frame/field duration.
 		 */
-		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
-
-		if (diff == 0 && in_vblank_irq)
+		sw_diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
+		if (sw_diff == 0 && in_vblank_irq)
 			DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
 				      " diff_ns = %lld, framedur_ns = %d)\n",
 				      pipe, (long long) diff_ns, framedur_ns);
-	} else {
-		/* some kind of default for drivers w/o accurate vbl timestamping */
-		diff = in_vblank_irq ? 1 : 0;
+
+		if (!dev->max_vblank_count)
+			diff = sw_diff;
+		else if (sw_diff && abs(diff - sw_diff) > DIV_ROUND_CLOSEST(sw_diff, 2)) {
+			DRM_DEBUG_VBL("hw vblank counter(%u) deviates from sw (%u)\n",
+				      diff, sw_diff);
+			diff = sw_diff;
+		}
 	}
 
 	/*
-- 
2.11.0



More information about the dri-devel mailing list