|
@@ -119,17 +119,34 @@ static int vc4_atomic_commit(struct drm_device *dev,
|
|
|
|
|
|
/* Make sure that any outstanding modesets have finished. */
|
|
|
if (nonblock) {
|
|
|
- ret = down_trylock(&vc4->async_modeset);
|
|
|
- if (ret) {
|
|
|
+ struct drm_crtc *crtc;
|
|
|
+ struct drm_crtc_state *crtc_state;
|
|
|
+ unsigned long flags;
|
|
|
+ bool busy = false;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If there's an undispatched event to send then we're
|
|
|
+ * obviously still busy. If there isn't, then we can
|
|
|
+ * unconditionally wait for the semaphore because it
|
|
|
+ * shouldn't be contended (for long).
|
|
|
+ *
|
|
|
+ * This is to prevent a race where queuing a new flip
|
|
|
+ * from userspace immediately on receipt of an event
|
|
|
+ * beats our clean-up and returns EBUSY.
|
|
|
+ */
|
|
|
+ spin_lock_irqsave(&dev->event_lock, flags);
|
|
|
+ for_each_crtc_in_state(state, crtc, crtc_state, i)
|
|
|
+ busy |= vc4_event_pending(crtc);
|
|
|
+ spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
|
+ if (busy) {
|
|
|
kfree(c);
|
|
|
return -EBUSY;
|
|
|
}
|
|
|
- } else {
|
|
|
- ret = down_interruptible(&vc4->async_modeset);
|
|
|
- if (ret) {
|
|
|
- kfree(c);
|
|
|
- return ret;
|
|
|
- }
|
|
|
+ }
|
|
|
+ ret = down_interruptible(&vc4->async_modeset);
|
|
|
+ if (ret) {
|
|
|
+ kfree(c);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
ret = drm_atomic_helper_prepare_planes(dev, state);
|