@@ -76,13 +76,15 @@ module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
76
76
77
77
static void store_vblank (struct drm_device * dev , unsigned int pipe ,
78
78
u32 vblank_count_inc ,
79
- struct timeval * t_vblank )
79
+ struct timeval * t_vblank , u32 last )
80
80
{
81
81
struct drm_vblank_crtc * vblank = & dev -> vblank [pipe ];
82
82
u32 tslot ;
83
83
84
84
assert_spin_locked (& dev -> vblank_time_lock );
85
85
86
+ vblank -> last = last ;
87
+
86
88
/* All writers hold the spinlock, but readers are serialized by
87
89
* the latching of vblank->count below.
88
90
*/
@@ -102,6 +104,54 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe,
102
104
smp_wmb ();
103
105
}
104
106
107
+ /**
108
+ * drm_reset_vblank_timestamp - reset the last timestamp to the last vblank
109
+ * @dev: DRM device
110
+ * @pipe: index of CRTC for which to reset the timestamp
111
+ *
112
+ * Reset the stored timestamp for the current vblank count to correspond
113
+ * to the last vblank occurred.
114
+ *
115
+ * Only to be called from drm_vblank_on().
116
+ *
117
+ * Note: caller must hold dev->vbl_lock since this reads & writes
118
+ * device vblank fields.
119
+ */
120
+ static void drm_reset_vblank_timestamp (struct drm_device * dev , unsigned int pipe )
121
+ {
122
+ u32 cur_vblank ;
123
+ bool rc ;
124
+ struct timeval t_vblank ;
125
+ int count = DRM_TIMESTAMP_MAXRETRIES ;
126
+
127
+ spin_lock (& dev -> vblank_time_lock );
128
+
129
+ /*
130
+ * sample the current counter to avoid random jumps
131
+ * when drm_vblank_enable() applies the diff
132
+ */
133
+ do {
134
+ cur_vblank = dev -> driver -> get_vblank_counter (dev , pipe );
135
+ rc = drm_get_last_vbltimestamp (dev , pipe , & t_vblank , 0 );
136
+ } while (cur_vblank != dev -> driver -> get_vblank_counter (dev , pipe ) && -- count > 0 );
137
+
138
+ /*
139
+ * Only reinitialize corresponding vblank timestamp if high-precision query
140
+ * available and didn't fail. Otherwise reinitialize delayed at next vblank
141
+ * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
142
+ */
143
+ if (!rc )
144
+ t_vblank = (struct timeval ) {0 , 0 };
145
+
146
+ /*
147
+ * +1 to make sure user will never see the same
148
+ * vblank counter value before and after a modeset
149
+ */
150
+ store_vblank (dev , pipe , 1 , & t_vblank , cur_vblank );
151
+
152
+ spin_unlock (& dev -> vblank_time_lock );
153
+ }
154
+
105
155
/**
106
156
* drm_update_vblank_count - update the master vblank counter
107
157
* @dev: DRM device
@@ -126,6 +176,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
126
176
bool rc ;
127
177
struct timeval t_vblank ;
128
178
int count = DRM_TIMESTAMP_MAXRETRIES ;
179
+ int framedur_ns = vblank -> framedur_ns ;
129
180
130
181
/*
131
182
* Interrupts were disabled prior to this call, so deal with counter
@@ -144,20 +195,40 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
144
195
rc = drm_get_last_vbltimestamp (dev , pipe , & t_vblank , flags );
145
196
} while (cur_vblank != dev -> driver -> get_vblank_counter (dev , pipe ) && -- count > 0 );
146
197
147
- /* Deal with counter wrap */
148
- diff = cur_vblank - vblank -> last ;
149
- if (cur_vblank < vblank -> last ) {
150
- diff += dev -> max_vblank_count + 1 ;
198
+ if (dev -> max_vblank_count != 0 ) {
199
+ /* trust the hw counter when it's around */
200
+ diff = (cur_vblank - vblank -> last ) & dev -> max_vblank_count ;
201
+ } else if (rc && framedur_ns ) {
202
+ const struct timeval * t_old ;
203
+ u64 diff_ns ;
204
+
205
+ t_old = & vblanktimestamp (dev , pipe , vblank -> count );
206
+ diff_ns = timeval_to_ns (& t_vblank ) - timeval_to_ns (t_old );
151
207
152
- DRM_DEBUG ("last_vblank[%u]=0x%x, cur_vblank=0x%x => diff=0x%x\n" ,
153
- pipe , vblank -> last , cur_vblank , diff );
208
+ /*
209
+ * Figure out how many vblanks we've missed based
210
+ * on the difference in the timestamps and the
211
+ * frame/field duration.
212
+ */
213
+ diff = DIV_ROUND_CLOSEST_ULL (diff_ns , framedur_ns );
214
+
215
+ if (diff == 0 && flags & DRM_CALLED_FROM_VBLIRQ )
216
+ DRM_DEBUG ("crtc %u: Redundant vblirq ignored."
217
+ " diff_ns = %lld, framedur_ns = %d)\n" ,
218
+ pipe , (long long ) diff_ns , framedur_ns );
219
+ } else {
220
+ /* some kind of default for drivers w/o accurate vbl timestamping */
221
+ diff = (flags & DRM_CALLED_FROM_VBLIRQ ) != 0 ;
154
222
}
155
223
156
- DRM_DEBUG ("updating vblank count on crtc %u, missed %d\n" ,
157
- pipe , diff );
224
+ DRM_DEBUG ("updating vblank count on crtc %u:"
225
+ " current=%u, diff=%u, hw=%u hw_last=%u\n" ,
226
+ pipe , vblank -> count , diff , cur_vblank , vblank -> last );
158
227
159
- if (diff == 0 )
228
+ if (diff == 0 ) {
229
+ WARN_ON_ONCE (cur_vblank != vblank -> last );
160
230
return ;
231
+ }
161
232
162
233
/*
163
234
* Only reinitialize corresponding vblank timestamp if high-precision query
@@ -167,7 +238,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
167
238
if (!rc )
168
239
t_vblank = (struct timeval ) {0 , 0 };
169
240
170
- store_vblank (dev , pipe , diff , & t_vblank );
241
+ store_vblank (dev , pipe , diff , & t_vblank , cur_vblank );
171
242
}
172
243
173
244
/*
@@ -180,38 +251,13 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
180
251
{
181
252
struct drm_vblank_crtc * vblank = & dev -> vblank [pipe ];
182
253
unsigned long irqflags ;
183
- u32 vblcount ;
184
- s64 diff_ns ;
185
- bool vblrc ;
186
- struct timeval tvblank ;
187
- int count = DRM_TIMESTAMP_MAXRETRIES ;
188
254
189
255
/* Prevent vblank irq processing while disabling vblank irqs,
190
256
* so no updates of timestamps or count can happen after we've
191
257
* disabled. Needed to prevent races in case of delayed irq's.
192
258
*/
193
259
spin_lock_irqsave (& dev -> vblank_time_lock , irqflags );
194
260
195
- /*
196
- * If the vblank interrupt was already disabled update the count
197
- * and timestamp to maintain the appearance that the counter
198
- * has been ticking all along until this time. This makes the
199
- * count account for the entire time between drm_vblank_on() and
200
- * drm_vblank_off().
201
- *
202
- * But only do this if precise vblank timestamps are available.
203
- * Otherwise we might read a totally bogus timestamp since drivers
204
- * lacking precise timestamp support rely upon sampling the system clock
205
- * at vblank interrupt time. Which obviously won't work out well if the
206
- * vblank interrupt is disabled.
207
- */
208
- if (!vblank -> enabled &&
209
- drm_get_last_vbltimestamp (dev , pipe , & tvblank , 0 )) {
210
- drm_update_vblank_count (dev , pipe , 0 );
211
- spin_unlock_irqrestore (& dev -> vblank_time_lock , irqflags );
212
- return ;
213
- }
214
-
215
261
/*
216
262
* Only disable vblank interrupts if they're enabled. This avoids
217
263
* calling the ->disable_vblank() operation in atomic context with the
@@ -222,47 +268,13 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
222
268
vblank -> enabled = false;
223
269
}
224
270
225
- /* No further vblank irq's will be processed after
226
- * this point. Get current hardware vblank count and
227
- * vblank timestamp, repeat until they are consistent.
228
- *
229
- * FIXME: There is still a race condition here and in
230
- * drm_update_vblank_count() which can cause off-by-one
231
- * reinitialization of software vblank counter. If gpu
232
- * vblank counter doesn't increment exactly at the leading
233
- * edge of a vblank interval, then we can lose 1 count if
234
- * we happen to execute between start of vblank and the
235
- * delayed gpu counter increment.
236
- */
237
- do {
238
- vblank -> last = dev -> driver -> get_vblank_counter (dev , pipe );
239
- vblrc = drm_get_last_vbltimestamp (dev , pipe , & tvblank , 0 );
240
- } while (vblank -> last != dev -> driver -> get_vblank_counter (dev , pipe ) && (-- count ) && vblrc );
241
-
242
- if (!count )
243
- vblrc = 0 ;
244
-
245
- /* Compute time difference to stored timestamp of last vblank
246
- * as updated by last invocation of drm_handle_vblank() in vblank irq.
247
- */
248
- vblcount = vblank -> count ;
249
- diff_ns = timeval_to_ns (& tvblank ) -
250
- timeval_to_ns (& vblanktimestamp (dev , pipe , vblcount ));
251
-
252
- /* If there is at least 1 msec difference between the last stored
253
- * timestamp and tvblank, then we are currently executing our
254
- * disable inside a new vblank interval, the tvblank timestamp
255
- * corresponds to this new vblank interval and the irq handler
256
- * for this vblank didn't run yet and won't run due to our disable.
257
- * Therefore we need to do the job of drm_handle_vblank() and
258
- * increment the vblank counter by one to account for this vblank.
259
- *
260
- * Skip this step if there isn't any high precision timestamp
261
- * available. In that case we can't account for this and just
262
- * hope for the best.
271
+ /*
272
+ * Always update the count and timestamp to maintain the
273
+ * appearance that the counter has been ticking all along until
274
+ * this time. This makes the count account for the entire time
275
+ * between drm_vblank_on() and drm_vblank_off().
263
276
*/
264
- if (vblrc && (abs64 (diff_ns ) > 1000000 ))
265
- store_vblank (dev , pipe , 1 , & tvblank );
277
+ drm_update_vblank_count (dev , pipe , 0 );
266
278
267
279
spin_unlock_irqrestore (& dev -> vblank_time_lock , irqflags );
268
280
}
@@ -1325,16 +1337,8 @@ void drm_vblank_on(struct drm_device *dev, unsigned int pipe)
1325
1337
vblank -> inmodeset = 0 ;
1326
1338
}
1327
1339
1328
- /*
1329
- * sample the current counter to avoid random jumps
1330
- * when drm_vblank_enable() applies the diff
1331
- *
1332
- * -1 to make sure user will never see the same
1333
- * vblank counter value before and after a modeset
1334
- */
1335
- vblank -> last =
1336
- (dev -> driver -> get_vblank_counter (dev , pipe ) - 1 ) &
1337
- dev -> max_vblank_count ;
1340
+ drm_reset_vblank_timestamp (dev , pipe );
1341
+
1338
1342
/*
1339
1343
* re-enable interrupts if there are users left, or the
1340
1344
* user wishes vblank interrupts to be enabled all the time.
@@ -1717,9 +1721,6 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
1717
1721
bool drm_handle_vblank (struct drm_device * dev , unsigned int pipe )
1718
1722
{
1719
1723
struct drm_vblank_crtc * vblank = & dev -> vblank [pipe ];
1720
- u32 vblcount ;
1721
- s64 diff_ns ;
1722
- struct timeval tvblank ;
1723
1724
unsigned long irqflags ;
1724
1725
1725
1726
if (WARN_ON_ONCE (!dev -> num_crtcs ))
@@ -1743,32 +1744,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
1743
1744
return false;
1744
1745
}
1745
1746
1746
- /* Fetch corresponding timestamp for this vblank interval from
1747
- * driver and store it in proper slot of timestamp ringbuffer.
1748
- */
1749
-
1750
- /* Get current timestamp and count. */
1751
- vblcount = vblank -> count ;
1752
- drm_get_last_vbltimestamp (dev , pipe , & tvblank , DRM_CALLED_FROM_VBLIRQ );
1753
-
1754
- /* Compute time difference to timestamp of last vblank */
1755
- diff_ns = timeval_to_ns (& tvblank ) -
1756
- timeval_to_ns (& vblanktimestamp (dev , pipe , vblcount ));
1757
-
1758
- /* Update vblank timestamp and count if at least
1759
- * DRM_REDUNDANT_VBLIRQ_THRESH_NS nanoseconds
1760
- * difference between last stored timestamp and current
1761
- * timestamp. A smaller difference means basically
1762
- * identical timestamps. Happens if this vblank has
1763
- * been already processed and this is a redundant call,
1764
- * e.g., due to spurious vblank interrupts. We need to
1765
- * ignore those for accounting.
1766
- */
1767
- if (abs64 (diff_ns ) > DRM_REDUNDANT_VBLIRQ_THRESH_NS )
1768
- store_vblank (dev , pipe , 1 , & tvblank );
1769
- else
1770
- DRM_DEBUG ("crtc %u: Redundant vblirq ignored. diff_ns = %d\n" ,
1771
- pipe , (int ) diff_ns );
1747
+ drm_update_vblank_count (dev , pipe , DRM_CALLED_FROM_VBLIRQ );
1772
1748
1773
1749
spin_unlock (& dev -> vblank_time_lock );
1774
1750
0 commit comments