drm/msm: Use customized function to wait for atomic commit done
authorHai Li <hali@codeaurora.org>
Tue, 28 Apr 2015 23:35:37 +0000 (19:35 -0400)
committerRob Clark <robdclark@gmail.com>
Thu, 11 Jun 2015 17:11:04 +0000 (13:11 -0400)
MDP FLUSH registers could indicate if the previous flush updates
has taken effect at vsync boundary. Making use of this H/W feature
can catch the vsync that happened between CRTC atomic_flush and
*_wait_for_vblanks, to avoid unnecessary wait.

This change allows kms CRTCs to use their own *_wait_for_commit_done
functions to wait for FLUSH register cleared at vsync, before commit
completion.

Signed-off-by: Hai Li <hali@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
drivers/gpu/drm/msm/msm_atomic.c
drivers/gpu/drm/msm/msm_kms.h

index 73afa21822b4efdb6294aa107ff901b882deb4c2..c4bb9d9c7667e8453242fced07eae6d06c63bf74 100644 (file)
@@ -51,6 +51,11 @@ struct mdp4_crtc {
        /* if there is a pending flip, these will be non-null: */
        struct drm_pending_vblank_event *event;
 
+       /* Bits have been flushed at the last commit,
+        * used to decide if a vsync has happened since last commit.
+        */
+       u32 flushed_mask;
+
 #define PENDING_CURSOR 0x1
 #define PENDING_FLIP   0x2
        atomic_t pending;
@@ -93,6 +98,8 @@ static void crtc_flush(struct drm_crtc *crtc)
 
        DBG("%s: flush=%08x", mdp4_crtc->name, flush);
 
+       mdp4_crtc->flushed_mask = flush;
+
        mdp4_write(mdp4_kms, REG_MDP4_OVERLAY_FLUSH, flush);
 }
 
@@ -537,6 +544,29 @@ static void mdp4_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
        crtc_flush(crtc);
 }
 
+static void mdp4_crtc_wait_for_flush_done(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
+       struct mdp4_kms *mdp4_kms = get_kms(crtc);
+       int ret;
+
+       ret = drm_crtc_vblank_get(crtc);
+       if (ret)
+               return;
+
+       ret = wait_event_timeout(dev->vblank[drm_crtc_index(crtc)].queue,
+               !(mdp4_read(mdp4_kms, REG_MDP4_OVERLAY_FLUSH) &
+                       mdp4_crtc->flushed_mask),
+               msecs_to_jiffies(50));
+       if (ret <= 0)
+               dev_warn(dev->dev, "vblank time out, crtc=%d\n", mdp4_crtc->id);
+
+       mdp4_crtc->flushed_mask = 0;
+
+       drm_crtc_vblank_put(crtc);
+}
+
 uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc)
 {
        struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
@@ -600,6 +630,15 @@ void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf, int mixer)
        mdp4_write(mdp4_kms, REG_MDP4_DISP_INTF_SEL, intf_sel);
 }
 
+void mdp4_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.
+        */
+       mdp4_crtc_wait_for_flush_done(crtc);
+}
+
 static const char *dma_names[] = {
                "DMA_P", "DMA_S", "DMA_E",
 };
index 88a75cbc8f7173c42a9154866168d7323a719153..531e4acc2a872f8263b1e124e3e801ff30521e7b 100644 (file)
@@ -159,6 +159,12 @@ static void mdp4_complete_commit(struct msm_kms *kms, struct drm_atomic_state *s
        mdp4_disable(mdp4_kms);
 }
 
+static void mdp4_wait_for_crtc_commit_done(struct msm_kms *kms,
+                                               struct drm_crtc *crtc)
+{
+       mdp4_crtc_wait_for_commit_done(crtc);
+}
+
 static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,
                struct drm_encoder *encoder)
 {
@@ -197,6 +203,7 @@ static const struct mdp_kms_funcs kms_funcs = {
                .disable_vblank  = mdp4_disable_vblank,
                .prepare_commit  = mdp4_prepare_commit,
                .complete_commit = mdp4_complete_commit,
+               .wait_for_crtc_commit_done = mdp4_wait_for_crtc_commit_done,
                .get_format      = mdp_get_format,
                .round_pixclk    = mdp4_round_pixclk,
                .preclose        = mdp4_preclose,
index a537c078593ba8548ad54d416f3aa5670af7110f..c1ecb9d6bdefecda24ba9ea308eb8a3430f6e1fb 100644 (file)
@@ -206,6 +206,7 @@ uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc);
 void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
 void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config);
 void mdp4_crtc_set_intf(struct drm_crtc *crtc, enum mdp4_intf intf, int mixer);
+void mdp4_crtc_wait_for_commit_done(struct drm_crtc *crtc);
 struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
                struct drm_plane *plane, int id, int ovlp_id,
                enum mdp4_dma dma_id);
index 13dd7dd853a984354dad9d6d5dfb709e98c189bf..672ffd4dde9469b7eaee853060c9c26f3fa73da1 100644 (file)
@@ -46,6 +46,11 @@ struct mdp5_crtc {
        /* if there is a pending flip, these will be non-null: */
        struct drm_pending_vblank_event *event;
 
+       /* Bits have been flushed at the last commit,
+        * used to decide if a vsync has happened since last commit.
+        */
+       u32 flushed_mask;
+
 #define PENDING_CURSOR 0x1
 #define PENDING_FLIP   0x2
        atomic_t pending;
@@ -82,12 +87,12 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending)
        mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank);
 }
 
-static void crtc_flush(struct drm_crtc *crtc, u32 flush_mask)
+static u32 crtc_flush(struct drm_crtc *crtc, u32 flush_mask)
 {
        struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
 
        DBG("%s: flush=%08x", mdp5_crtc->name, flush_mask);
-       mdp5_ctl_commit(mdp5_crtc->ctl, flush_mask);
+       return mdp5_ctl_commit(mdp5_crtc->ctl, flush_mask);
 }
 
 /*
@@ -95,7 +100,7 @@ static void crtc_flush(struct drm_crtc *crtc, u32 flush_mask)
  * so that we can safely queue unref to current fb (ie. next
  * vblank we know hw is done w/ previous scanout_fb).
  */
-static void crtc_flush_all(struct drm_crtc *crtc)
+static u32 crtc_flush_all(struct drm_crtc *crtc)
 {
        struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
        struct drm_plane *plane;
@@ -103,7 +108,7 @@ static void crtc_flush_all(struct drm_crtc *crtc)
 
        /* this should not happen: */
        if (WARN_ON(!mdp5_crtc->ctl))
-               return;
+               return 0;
 
        drm_atomic_crtc_for_each_plane(plane, crtc) {
                flush_mask |= mdp5_plane_get_flush(plane);
@@ -111,7 +116,7 @@ static void crtc_flush_all(struct drm_crtc *crtc)
 
        flush_mask |= mdp_ctl_flush_mask_lm(mdp5_crtc->lm);
 
-       crtc_flush(crtc, flush_mask);
+       return crtc_flush(crtc, flush_mask);
 }
 
 /* if file!=NULL, this is preclose potential cancel-flip path */
@@ -395,7 +400,9 @@ static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc)
                return;
 
        blend_setup(crtc);
-       crtc_flush_all(crtc);
+
+       mdp5_crtc->flushed_mask = crtc_flush_all(crtc);
+
        request_pending(crtc, PENDING_FLIP);
 }
 
@@ -600,6 +607,32 @@ static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
        DBG("%s: error: %08x", mdp5_crtc->name, irqstatus);
 }
 
+static void mdp5_crtc_wait_for_flush_done(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
+       int ret;
+
+       /* Should not call this function if crtc is disabled. */
+       if (!mdp5_crtc->ctl)
+               return;
+
+       ret = drm_crtc_vblank_get(crtc);
+       if (ret)
+               return;
+
+       ret = wait_event_timeout(dev->vblank[drm_crtc_index(crtc)].queue,
+               ((mdp5_ctl_get_commit_status(mdp5_crtc->ctl) &
+               mdp5_crtc->flushed_mask) == 0),
+               msecs_to_jiffies(50));
+       if (ret <= 0)
+               dev_warn(dev->dev, "vblank time out, crtc=%d\n", mdp5_crtc->id);
+
+       mdp5_crtc->flushed_mask = 0;
+
+       drm_crtc_vblank_put(crtc);
+}
+
 uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc)
 {
        struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
@@ -631,6 +664,7 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, struct mdp5_interface *intf)
                mdp5_crtc->vblank.irqmask = lm2ppdone(lm);
        else
                mdp5_crtc->vblank.irqmask = intf2vblank(lm, intf);
+
        mdp_irq_update(&mdp5_kms->base);
 
        mdp5_ctl_set_intf(mdp5_crtc->ctl, intf);
@@ -648,6 +682,15 @@ struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc)
        return WARN_ON(!crtc) ? NULL : mdp5_crtc->ctl;
 }
 
+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);
+}
+
 /* initialize crtc */
 struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
                struct drm_plane *plane, int id)
index 5488b687c8d11e8c2c6db2217654c82d4e8de323..f2530f224a762be72e92b32ed2c57521311f9603 100644 (file)
@@ -392,8 +392,10 @@ static u32 fix_sw_flush(struct mdp5_ctl *ctl, u32 flush_mask)
  * CTL registers need to be flushed in some circumstances; if that is the
  * case, some trigger bits will be present in both flush mask and
  * ctl->pending_ctl_trigger.
+ *
+ * Return H/W flushed bit mask.
  */
-int mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask)
+u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask)
 {
        struct mdp5_ctl_manager *ctl_mgr = ctl->ctlm;
        struct op_mode *pipeline = &ctl->pipeline;
@@ -424,7 +426,12 @@ int mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask)
                refill_start_mask(ctl);
        }
 
-       return 0;
+       return flush_mask;
+}
+
+u32 mdp5_ctl_get_commit_status(struct mdp5_ctl *ctl)
+{
+       return ctl_read(ctl, REG_MDP5_CTL_FLUSH(ctl->id));
 }
 
 void mdp5_ctl_release(struct mdp5_ctl *ctl)
index 7a62000994a16fc906ee919d4e2ad65860f6b944..4678228c4f14efb41f8d123230ffda3b0eb2917e 100644 (file)
@@ -88,7 +88,8 @@ u32 mdp_ctl_flush_mask_cursor(int cursor_id);
 u32 mdp_ctl_flush_mask_encoder(struct mdp5_interface *intf);
 
 /* @flush_mask: see CTL flush masks definitions below */
-int mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask);
+u32 mdp5_ctl_commit(struct mdp5_ctl *ctl, u32 flush_mask);
+u32 mdp5_ctl_get_commit_status(struct mdp5_ctl *ctl);
 
 void mdp5_ctl_release(struct mdp5_ctl *ctl);
 
index bbacf9d2b7383cc12d21bfeb48327c4b04604f56..206f758f7d64849af986e6c70589bf96d7b0cb7b 100644 (file)
@@ -80,6 +80,12 @@ static void mdp5_complete_commit(struct msm_kms *kms, struct drm_atomic_state *s
        mdp5_disable(mdp5_kms);
 }
 
+static void mdp5_wait_for_crtc_commit_done(struct msm_kms *kms,
+                                               struct drm_crtc *crtc)
+{
+       mdp5_crtc_wait_for_commit_done(crtc);
+}
+
 static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate,
                struct drm_encoder *encoder)
 {
@@ -141,6 +147,7 @@ static const struct mdp_kms_funcs kms_funcs = {
                .disable_vblank  = mdp5_disable_vblank,
                .prepare_commit  = mdp5_prepare_commit,
                .complete_commit = mdp5_complete_commit,
+               .wait_for_crtc_commit_done = mdp5_wait_for_crtc_commit_done,
                .get_format      = mdp_get_format,
                .round_pixclk    = mdp5_round_pixclk,
                .set_split_display = mdp5_set_split_display,
index 2c0de174cc09b9e198d05e3eed1745e193d997ba..e0eb24587c84d7887a69c47c0428ad3f0d4f1396 100644 (file)
@@ -237,6 +237,7 @@ int mdp5_crtc_get_lm(struct drm_crtc *crtc);
 struct mdp5_ctl *mdp5_crtc_get_ctl(struct drm_crtc *crtc);
 void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
 void mdp5_crtc_set_intf(struct drm_crtc *crtc, struct mdp5_interface *intf);
+void mdp5_crtc_wait_for_commit_done(struct drm_crtc *crtc);
 struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
                struct drm_plane *plane, int id);
 
index 5b192128cda2b9afb978f8c079d08d20e86f10e4..84c0a72b446b884380fec12f4613562c247a1ab6 100644 (file)
@@ -84,6 +84,33 @@ static void commit_destroy(struct msm_commit *c)
        kfree(c);
 }
 
+static void msm_atomic_wait_for_commit_done(struct drm_device *dev,
+               struct drm_atomic_state *old_state)
+{
+       struct drm_crtc *crtc;
+       struct msm_drm_private *priv = old_state->dev->dev_private;
+       struct msm_kms *kms = priv->kms;
+       int ncrtcs = old_state->dev->mode_config.num_crtc;
+       int i;
+
+       for (i = 0; i < ncrtcs; i++) {
+               crtc = old_state->crtcs[i];
+
+               if (!crtc)
+                       continue;
+
+               if (!crtc->state->enable)
+                       continue;
+
+               /* Legacy cursor ioctls are completely unsynced, and userspace
+                * relies on that (by doing tons of cursor updates). */
+               if (old_state->legacy_cursor_update)
+                       continue;
+
+               kms->funcs->wait_for_crtc_commit_done(kms, crtc);
+       }
+}
+
 /* The (potentially) asynchronous part of the commit.  At this point
  * nothing can fail short of armageddon.
  */
@@ -115,7 +142,7 @@ static void complete_commit(struct msm_commit *c)
         * not be critical path)
         */
 
-       drm_atomic_helper_wait_for_vblanks(dev, state);
+       msm_atomic_wait_for_commit_done(dev, state);
 
        drm_atomic_helper_cleanup_planes(dev, state);
 
@@ -139,7 +166,6 @@ static void add_fb(struct msm_commit *c, struct drm_framebuffer *fb)
        c->fence = max(c->fence, msm_gem_fence(to_msm_bo(obj), MSM_PREP_READ));
 }
 
-
 int msm_atomic_check(struct drm_device *dev,
                     struct drm_atomic_state *state)
 {
index a9f17bdb4530e856e9f5e04e7cc7dd9029cedf81..9bcabaada17930d73a742dc603a65688335cf91e 100644 (file)
@@ -43,6 +43,9 @@ struct msm_kms_funcs {
        /* modeset, bracketing atomic_commit(): */
        void (*prepare_commit)(struct msm_kms *kms, struct drm_atomic_state *state);
        void (*complete_commit)(struct msm_kms *kms, struct drm_atomic_state *state);
+       /* functions to wait for atomic commit completed on each CRTC */
+       void (*wait_for_crtc_commit_done)(struct msm_kms *kms,
+                                       struct drm_crtc *crtc);
        /* misc: */
        const struct msm_format *(*get_format)(struct msm_kms *kms, uint32_t format);
        long (*round_pixclk)(struct msm_kms *kms, unsigned long rate,