UPSTREAM: drm/rockchip: Avoid race with vblank count increment
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / rockchip / rockchip_drm_vop.c
index a0333b8e594c45877aa516ac64cf642b58deb1f4..7253175a133a780adfc1ec55b8086829689b262d 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/iopoll.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/pm_runtime.h>
@@ -2069,6 +2070,32 @@ static void vop_cfg_update(struct drm_crtc *crtc,
        spin_unlock(&vop->reg_lock);
 }
 
+static bool vop_fs_irq_is_pending(struct vop *vop)
+{
+       return VOP_INTR_GET_TYPE(vop, status, FS_INTR);
+}
+
+static void vop_wait_for_irq_handler(struct vop *vop)
+{
+       bool pending;
+       int ret;
+
+       /*
+        * Spin until frame start interrupt status bit goes low, which means
+        * that interrupt handler was invoked and cleared it. The timeout of
+        * 10 msecs is really too long, but it is just a safety measure if
+        * something goes really wrong. The wait will only happen in the very
+        * unlikely case of a vblank happening exactly at the same time and
+        * shouldn't exceed microseconds range.
+        */
+       ret = readx_poll_timeout_atomic(vop_fs_irq_is_pending, vop, pending,
+                                       !pending, 0, 10 * 1000);
+       if (ret)
+               DRM_DEV_ERROR(vop->dev, "VOP vblank IRQ stuck for 10 ms\n");
+
+       synchronize_irq(vop->irq);
+}
+
 static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
                                  struct drm_crtc_state *old_crtc_state)
 {
@@ -2118,6 +2145,13 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
        }
 
        vop_cfg_done(vop);
+
+       /*
+        * There is a (rather unlikely) possiblity that a vblank interrupt
+        * fired before we set the cfg_done bit. To avoid spuriously
+        * signalling flip completion we need to wait for it to finish.
+        */
+       vop_wait_for_irq_handler(vop);
 }
 
 static void vop_crtc_atomic_begin(struct drm_crtc *crtc,