summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLucas Stach <l.stach@pengutronix.de>2018-09-11 16:03:16 +0200
committerPhilipp Zabel <p.zabel@pengutronix.de>2019-02-22 12:17:58 +0100
commita0ea4ffff2662c10ca69d5e246adf5543fd945b9 (patch)
tree7540f9d167c79ef14ac61f461c13698e2b828237
parentf6019702d9646da03c11bb5c9bd89e23ccc4e14d (diff)
downloadlinux-a0ea4ffff2662c10ca69d5e246adf5543fd945b9.tar.gz
linux-a0ea4ffff2662c10ca69d5e246adf5543fd945b9.tar.bz2
linux-a0ea4ffff2662c10ca69d5e246adf5543fd945b9.zip
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: Lucas 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: Philipp Zabel <p.zabel@pengutronix.de>
-rw-r--r--drivers/gpu/drm/imx/ipuv3-crtc.c30
1 files changed, 28 insertions, 2 deletions
diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c
index 058b53c0aa7e..b1be495b1d06 100644
--- a/drivers/gpu/drm/imx/ipuv3-crtc.c
+++ b/drivers/gpu/drm/imx/ipuv3-crtc.c
@@ -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];
- drm_crtc_handle_vblank(&ipu_crtc->base);
+ if (!plane)
+ continue;
+
+ if (ipu_plane_atomic_update_pending(&plane->base))
+ break;
+ }
+
+ 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);