aboutsummaryrefslogtreecommitdiffstats
path: root/meta-amd-bsp/recipes-kernel/linux/linux-yocto-4.14.71/0333-drm-amd-display-Fix-race-between-vblank-irq-and-page.patch
blob: 5e7b4e779e513d4cce5dddc3cb995a5760f5d9fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
From 87e40d9c82472f1aba4f9d52e51acc413b7efeba Mon Sep 17 00:00:00 2001
From: Mario Kleiner <mario.kleiner.de@gmail.com>
Date: Mon, 24 Apr 2017 11:46:44 +0200
Subject: [PATCH 0333/4131] drm/amd/display: Fix race between vblank irq and
 pageflip irq. (v2)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Since DC now uses CRTC_VERTICAL_INTERRUPT0 as VBLANK irq trigger
and vblank interrupts actually happen earliest at start of vblank,
instead of a bit before vblank, we no longer need some of the
fudging logic to deal with too early vblank irq handling (grep for
lb_vblank_lead_lines). This itself fixes a pageflip scheduling
bug in DC, caused by uninitialized  use of lb_vblank_lead_lines,
with a wrong startup value of 0. Thanks to the new vblank irq
trigger this value of zero is now actually correct for DC :).

A new problem is that vblank irq's race against pflip irq's,
and as both can fire at first line of vblank, it is no longer
guaranteed that vblank irq handling (therefore -> drm_handle_vblank()
-> drm_update_vblank_count()) executes before pflip irq handling
for a given vblank interval when a pageflip completes. Therefore
the vblank count and timestamps emitted to user-space as part of
the pageflip completion event will be often stale and cause new
timestamping and swap scheduling errors in user-space.

This was observed with large frequency on R9 380 Tonga Pro.

Fix this by enforcing a vblank count+timestamp update right
before emitting the pageflip completion event from the pflip
irq handler. The logic in core drm_update_vblank_count() makes
sure that no redundant or conflicting updates happen, iow. the
call turns into a no-op if it wasn't needed for that vblank,
burning a few microseconds of cpu time though.

Successfully tested on AMD R9 380 "Tonga Pro" (VI/DCE 10)
with DC enabled on the current DC staging branch. Independent
measurement of pageflip completion timing with special hardware
measurement equipment now confirms correct pageflip timestamps
and counts in the pageflip completion events.

v2: Review comments by Michel, drop outdated paragraph
    about problem already fixed in 2nd patch of the series.
    Add acked/r-b by Harry and Michel.

Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com>
Acked-by: Harry Wentland <harry.wentland@amd.com> (v1)
Reviewed-by: Michel Dänzer <michel.daenzer@amd.com>

Cc: Andrey Grodzovsky <Andrey.Grodzovsky@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Michel Dänzer <michel.daenzer@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Kalyan Alle <kalyan.alle@amd.com>
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |  3 +++
 drivers/gpu/drm/drm_vblank.c                      | 31 +++++++++++++++++++++++
 include/drm/drm_vblank.h                          |  1 +
 3 files changed, 35 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index c51e181..7ede233 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -202,6 +202,9 @@ static void dm_pflip_high_irq(void *interrupt_params)
 	if (amdgpu_crtc->event
 			&& amdgpu_crtc->event->event.base.type
 			== DRM_EVENT_FLIP_COMPLETE) {
+		/* Update to correct count/ts if racing with vblank irq */
+		drm_accurate_vblank_count(&amdgpu_crtc->base);
+
 		drm_crtc_send_vblank_event(&amdgpu_crtc->base, amdgpu_crtc->event);
 		/* page flip completed. clean up */
 		amdgpu_crtc->event = NULL;
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 17e8ef9..1b82d73 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -293,6 +293,37 @@ static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
 	return vblank->count;
 }
 
+ /**
+ * drm_accurate_vblank_count - retrieve the master vblank counter
+ * @crtc: which counter to retrieve
+ *
+ * This function is similar to @drm_crtc_vblank_count but this
+ * function interpolates to handle a race with vblank irq's.
+ *
+ * This is mostly useful for hardware that can obtain the scanout
+ * position, but doesn't have a frame counter.
+ */
+u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned int pipe = drm_crtc_index(crtc);
+	u32 vblank;
+	unsigned long flags;
+
+	WARN(!dev->driver->get_vblank_timestamp,
+		"This function requires support for accurate vblank timestamps.");
+
+	spin_lock_irqsave(&dev->vblank_time_lock, flags);
+
+	drm_update_vblank_count(dev, pipe, false);
+	vblank = drm_vblank_count(dev, pipe);
+
+	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
+
+	return vblank;
+}
+EXPORT_SYMBOL(drm_accurate_vblank_count);
+
 /**
  * drm_crtc_accurate_vblank_count - retrieve the master vblank counter
  * @crtc: which counter to retrieve
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
index 7fba9ef..d0d1f2a 100644
--- a/include/drm/drm_vblank.h
+++ b/include/drm/drm_vblank.h
@@ -169,6 +169,7 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc);
 void drm_crtc_vblank_reset(struct drm_crtc *crtc);
 void drm_crtc_vblank_on(struct drm_crtc *crtc);
 u32 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc);
+u32 drm_accurate_vblank_count(struct drm_crtc *crtc);
 
 bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
 					   unsigned int pipe, int *max_error,
-- 
2.7.4