/*
- * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2015 The Linux Foundation. All rights reserved.
  * Copyright (C) 2013 Red Hat
  * Author: Rob Clark <robdclark@gmail.com>
  *
 
        struct mdp_irq vblank;
        struct mdp_irq err;
+       struct mdp_irq pp_done;
+
+       struct completion pp_completion;
+
+       bool cmd_mode;
 
        struct {
                /* protect REG_MDP5_LM_CURSOR* registers and cursor scanout_bo*/
        mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank);
 }
 
+static void request_pp_done_pending(struct drm_crtc *crtc)
+{
+       struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
+       reinit_completion(&mdp5_crtc->pp_completion);
+}
+
 static u32 crtc_flush(struct drm_crtc *crtc, u32 flush_mask)
 {
        struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
        if (WARN_ON(!mdp5_crtc->enabled))
                return;
 
+       if (mdp5_crtc->cmd_mode)
+               mdp_irq_unregister(&mdp5_kms->base, &mdp5_crtc->pp_done);
+
        mdp_irq_unregister(&mdp5_kms->base, &mdp5_crtc->err);
        mdp5_disable(mdp5_kms);
 
        mdp5_enable(mdp5_kms);
        mdp_irq_register(&mdp5_kms->base, &mdp5_crtc->err);
 
+       if (mdp5_crtc->cmd_mode)
+               mdp_irq_register(&mdp5_kms->base, &mdp5_crtc->pp_done);
+
        mdp5_crtc->enabled = true;
 }
 
 
        blend_setup(crtc);
 
+       /* PP_DONE irq is only used by command mode for now.
+        * It is better to request pending before FLUSH and START trigger
+        * to make sure no pp_done irq missed.
+        * This is safe because no pp_done will happen before SW trigger
+        * in command mode.
+        */
+       if (mdp5_crtc->cmd_mode)
+               request_pp_done_pending(crtc);
+
        mdp5_crtc->flushed_mask = crtc_flush_all(crtc);
 
        request_pending(crtc, PENDING_FLIP);
        DBG("%s: error: %08x", mdp5_crtc->name, irqstatus);
 }
 
+static void mdp5_crtc_pp_done_irq(struct mdp_irq *irq, uint32_t irqstatus)
+{
+       struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc,
+                                                               pp_done);
+
+       complete(&mdp5_crtc->pp_completion);
+}
+
+static void mdp5_crtc_wait_for_pp_done(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
+       int ret;
+
+       ret = wait_for_completion_timeout(&mdp5_crtc->pp_completion,
+                                               msecs_to_jiffies(50));
+       if (ret == 0)
+               dev_warn(dev->dev, "pp done time out, lm=%d\n", mdp5_crtc->lm);
+}
+
 static void mdp5_crtc_wait_for_flush_done(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
 
        /* now that we know what irq's we want: */
        mdp5_crtc->err.irqmask = intf2err(intf->num);
-
-       /* Register command mode Pingpong done as vblank for now,
-        * so that atomic commit should wait for it to finish.
-        * Ideally, in the future, we should take rd_ptr done as vblank,
-        * and let atomic commit wait for pingpong done for commond mode.
-        */
-       if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND)
-               mdp5_crtc->vblank.irqmask = lm2ppdone(lm);
-       else
-               mdp5_crtc->vblank.irqmask = intf2vblank(lm, intf);
+       mdp5_crtc->vblank.irqmask = intf2vblank(lm, intf);
+
+       if ((intf->type == INTF_DSI) &&
+               (intf->mode == MDP5_INTF_DSI_MODE_COMMAND)) {
+               mdp5_crtc->pp_done.irqmask = lm2ppdone(lm);
+               mdp5_crtc->pp_done.irq = mdp5_crtc_pp_done_irq;
+               mdp5_crtc->cmd_mode = true;
+       } else {
+               mdp5_crtc->pp_done.irqmask = 0;
+               mdp5_crtc->pp_done.irq = NULL;
+               mdp5_crtc->cmd_mode = false;
+       }
 
        mdp_irq_update(&mdp5_kms->base);
 
 
 void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc)
 {
-       /* wait_for_flush_done is the only case for now.
-        * Later we will have command mode CRTC to wait for
-        * other event.
-        */
-       mdp5_crtc_wait_for_flush_done(crtc);
+       struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
+
+       if (mdp5_crtc->cmd_mode)
+               mdp5_crtc_wait_for_pp_done(crtc);
+       else
+               mdp5_crtc_wait_for_flush_done(crtc);
 }
 
 /* initialize crtc */
 
        spin_lock_init(&mdp5_crtc->lm_lock);
        spin_lock_init(&mdp5_crtc->cursor.lock);
+       init_completion(&mdp5_crtc->pp_completion);
 
        mdp5_crtc->vblank.irq = mdp5_crtc_vblank_irq;
        mdp5_crtc->err.irq = mdp5_crtc_err_irq;