@@ -76,13 +76,15 @@ module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
7676
7777static void store_vblank (struct drm_device * dev , unsigned int pipe ,
7878 u32 vblank_count_inc ,
79- struct timeval * t_vblank )
79+ struct timeval * t_vblank , u32 last )
8080{
8181 struct drm_vblank_crtc * vblank = & dev -> vblank [pipe ];
8282 u32 tslot ;
8383
8484 assert_spin_locked (& dev -> vblank_time_lock );
8585
86+ vblank -> last = last ;
87+
8688 /* All writers hold the spinlock, but readers are serialized by
8789 * the latching of vblank->count below.
8890 */
@@ -102,6 +104,54 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe,
102104 smp_wmb ();
103105}
104106
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+
105155/**
106156 * drm_update_vblank_count - update the master vblank counter
107157 * @dev: DRM device
@@ -126,6 +176,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
126176 bool rc ;
127177 struct timeval t_vblank ;
128178 int count = DRM_TIMESTAMP_MAXRETRIES ;
179+ int framedur_ns = vblank -> framedur_ns ;
129180
130181 /*
131182 * 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,
144195 rc = drm_get_last_vbltimestamp (dev , pipe , & t_vblank , flags );
145196 } while (cur_vblank != dev -> driver -> get_vblank_counter (dev , pipe ) && -- count > 0 );
146197
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 );
151207
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 ;
154222 }
155223
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 );
158227
159- if (diff == 0 )
228+ if (diff == 0 ) {
229+ WARN_ON_ONCE (cur_vblank != vblank -> last );
160230 return ;
231+ }
161232
162233 /*
163234 * 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,
167238 if (!rc )
168239 t_vblank = (struct timeval ) {0 , 0 };
169240
170- store_vblank (dev , pipe , diff , & t_vblank );
241+ store_vblank (dev , pipe , diff , & t_vblank , cur_vblank );
171242}
172243
173244/*
@@ -180,38 +251,13 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
180251{
181252 struct drm_vblank_crtc * vblank = & dev -> vblank [pipe ];
182253 unsigned long irqflags ;
183- u32 vblcount ;
184- s64 diff_ns ;
185- bool vblrc ;
186- struct timeval tvblank ;
187- int count = DRM_TIMESTAMP_MAXRETRIES ;
188254
189255 /* Prevent vblank irq processing while disabling vblank irqs,
190256 * so no updates of timestamps or count can happen after we've
191257 * disabled. Needed to prevent races in case of delayed irq's.
192258 */
193259 spin_lock_irqsave (& dev -> vblank_time_lock , irqflags );
194260
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-
215261 /*
216262 * Only disable vblank interrupts if they're enabled. This avoids
217263 * 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)
222268 vblank -> enabled = false;
223269 }
224270
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().
263276 */
264- if (vblrc && (abs64 (diff_ns ) > 1000000 ))
265- store_vblank (dev , pipe , 1 , & tvblank );
277+ drm_update_vblank_count (dev , pipe , 0 );
266278
267279 spin_unlock_irqrestore (& dev -> vblank_time_lock , irqflags );
268280}
@@ -1325,16 +1337,8 @@ void drm_vblank_on(struct drm_device *dev, unsigned int pipe)
13251337 vblank -> inmodeset = 0 ;
13261338 }
13271339
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+
13381342 /*
13391343 * re-enable interrupts if there are users left, or the
13401344 * 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)
17171721bool drm_handle_vblank (struct drm_device * dev , unsigned int pipe )
17181722{
17191723 struct drm_vblank_crtc * vblank = & dev -> vblank [pipe ];
1720- u32 vblcount ;
1721- s64 diff_ns ;
1722- struct timeval tvblank ;
17231724 unsigned long irqflags ;
17241725
17251726 if (WARN_ON_ONCE (!dev -> num_crtcs ))
@@ -1743,32 +1744,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
17431744 return false;
17441745 }
17451746
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 );
17721748
17731749 spin_unlock (& dev -> vblank_time_lock );
17741750
0 commit comments