Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a0ea4fff authored by Lucas Stach's avatar Lucas Stach Committed by Philipp Zabel
Browse files

drm/imx: only send commit done event when all state has been applied



Currently there is a small race window where we could manage to arm the
vblank event from atomic flush, but programming the hardware was too close
to the frame end, so the hardware will only apply the current state on the
next vblank. In this case we will send out the commit done event too early
causing userspace to reuse framebuffes that are still in use.

Instead of using the event arming mechnism, just remember the pending event
and send it from the vblank IRQ handler, once we are sure that all state
has been applied successfully.

Signed-off-by: default avatarLucas Stach <l.stach@pengutronix.de>
[p.zabel@pengutronix.de: inverted logic: done -> pending, added back
 spinlock in atomic_flush, commit message typo fix]
Signed-off-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
parent f6019702
Loading
Loading
Loading
Loading
+28 −2
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ struct ipu_crtc {
	struct ipu_dc		*dc;
	struct ipu_di		*di;
	int			irq;
	struct drm_pending_vblank_event *event;
};

static inline struct ipu_crtc *to_ipu_crtc(struct drm_crtc *crtc)
@@ -173,8 +174,31 @@ static const struct drm_crtc_funcs ipu_crtc_funcs = {
static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
{
	struct ipu_crtc *ipu_crtc = dev_id;
	struct drm_crtc *crtc = &ipu_crtc->base;
	unsigned long flags;
	int i;

	drm_crtc_handle_vblank(crtc);

	if (ipu_crtc->event) {
		for (i = 0; i < ARRAY_SIZE(ipu_crtc->plane); i++) {
			struct ipu_plane *plane = ipu_crtc->plane[i];

			if (!plane)
				continue;

			if (ipu_plane_atomic_update_pending(&plane->base))
				break;
		}

	drm_crtc_handle_vblank(&ipu_crtc->base);
		if (i == ARRAY_SIZE(ipu_crtc->plane)) {
			spin_lock_irqsave(&crtc->dev->event_lock, flags);
			drm_crtc_send_vblank_event(crtc, ipu_crtc->event);
			ipu_crtc->event = NULL;
			drm_crtc_vblank_put(crtc);
			spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
		}
	}

	return IRQ_HANDLED;
}
@@ -223,8 +247,10 @@ static void ipu_crtc_atomic_flush(struct drm_crtc *crtc,
{
	spin_lock_irq(&crtc->dev->event_lock);
	if (crtc->state->event) {
		struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);

		WARN_ON(drm_crtc_vblank_get(crtc));
		drm_crtc_arm_vblank_event(crtc, crtc->state->event);
		ipu_crtc->event = crtc->state->event;
		crtc->state->event = NULL;
	}
	spin_unlock_irq(&crtc->dev->event_lock);