MLK-21656-2 drm/imx: dpu: crtc: Tune enablement sequence to correctly switch Tcon...
authorLiu Ying <victor.liu@nxp.com>
Tue, 7 May 2019 03:02:18 +0000 (11:02 +0800)
committerLiu Ying <victor.liu@nxp.com>
Fri, 10 May 2019 05:15:17 +0000 (13:15 +0800)
As suggested by the design team, there is rigorous timing requirement
to address TKT320590, that is, we need to turn Tcon(s) from bypass mode
into operation mode as soon as the first dumb frame is generated by DPU.
When dual stream is used, we should look at the first dumb frame generated
by the master FrameGen.  If we cannot ensure the timing requirement, say
the Tcon mode switching takes place after the second frame is generated
by DPU, the hardware could run into malfunction sometimes.  Based on
stress tests, the content shadow load done event for the first time we call
->atomic_flush() may not come after the CRTC enablement in the single
stream case and it looks like display data is not generated to the
down-stream encoder(hence, black screen).  This patch tunes enablement
sequence to correctly switch Tcon mode, according to the design team's
suggestions.  During the switching, we don't relinquish CPU to ensure the
sequence is straightforward to meet the timing requirement.  As we cannot
sleep during the switching, we take the pixel link enablement/disablement
operations(wrapped by a mutex in RPC call) out of framegen_enable/disable()
functions and put them at appropriate place.  This introduces additional
sequence modifications but should be safe.

Signed-off-by: Liu Ying <victor.liu@nxp.com>
drivers/gpu/drm/imx/dpu/dpu-crtc.c
drivers/gpu/imx/dpu/dpu-framegen.c
include/video/dpu.h

index 83e8eb9..b97a36b 100644 (file)
@@ -91,7 +91,7 @@ static void dpu_crtc_atomic_enable(struct drm_crtc *crtc,
        struct completion *m_safety_shdld_done, *s_safety_shdld_done;
        struct completion *m_content_shdld_done, *s_content_shdld_done;
        struct completion *m_dec_shdld_done, *s_dec_shdld_done;
-       unsigned long ret;
+       unsigned long ret, flags;
 
        drm_crtc_vblank_on(crtc);
 
@@ -126,9 +126,29 @@ static void dpu_crtc_atomic_enable(struct drm_crtc *crtc,
                extdst_pixengcfg_sync_trigger(m_plane_ed);
                framegen_shdtokgen(dpu_crtc->m_fg);
 
+               /* don't relinquish CPU until TCONs are set to operation mode */
+               local_irq_save(flags);
+               preempt_disable();
                /* First turn on the slave stream, second the master stream. */
                framegen_enable(dpu_crtc->s_fg);
                framegen_enable(dpu_crtc->m_fg);
+               /*
+                * TKT320590:
+                * Turn TCONs into operation mode as soon as the first dumb
+                * frame is generated by DPU from the master stream(we don't
+                * relinquish CPU to ensure this).  This makes DPRs/PRGs of
+                * the dual stream be able to evade the dumb frames of the
+                * dual stream respectively.
+                */
+               framegen_wait_for_frame_counter_moving(dpu_crtc->m_fg);
+               /* again, slave first, then master */
+               tcon_set_operation_mode(dpu_crtc->s_tcon);
+               tcon_set_operation_mode(dpu_crtc->m_tcon);
+               local_irq_restore(flags);
+               preempt_enable();
+
+               framegen_enable_pixel_link(dpu_crtc->s_fg);
+               framegen_enable_pixel_link(dpu_crtc->m_fg);
 
                if (dpu_crtc->aux_is_master) {
                        m_safety_shdld_done  = &aux_dpu_crtc->safety_shdld_done;
@@ -176,7 +196,23 @@ static void dpu_crtc_atomic_enable(struct drm_crtc *crtc,
                extdst_pixengcfg_sync_trigger(plane_ed);
                extdst_pixengcfg_sync_trigger(dpu_crtc->ed);
                framegen_shdtokgen(dpu_crtc->fg);
+
+               /* don't relinquish CPU until TCON is set to operation mode */
+               local_irq_save(flags);
+               preempt_disable();
                framegen_enable(dpu_crtc->fg);
+               /*
+                * TKT320590:
+                * Turn TCON into operation mode as soon as the first dumb
+                * frame is generated by DPU(we don't relinquish CPU to ensure
+                * this).  This makes DPR/PRG be able to evade the frame.
+                */
+               framegen_wait_for_frame_counter_moving(dpu_crtc->fg);
+               tcon_set_operation_mode(dpu_crtc->tcon);
+               local_irq_restore(flags);
+               preempt_enable();
+
+               framegen_enable_pixel_link(dpu_crtc->fg);
 
                shdld_done = &dpu_crtc->safety_shdld_done;
                ret = wait_for_completion_timeout(shdld_done, HZ);
@@ -212,24 +248,7 @@ static void dpu_crtc_atomic_enable(struct drm_crtc *crtc,
                crtc->state->event = NULL;
        }
 
-       /*
-        * TKT320590:
-        * Turn TCON into operation mode later after the first dumb frame is
-        * generated by DPU.  This makes DPR/PRG be able to evade the frame.
-        * However, it turns out we have to set the TCON into operation mode
-        * first and then wait for Framegen frame counter moving, otherwise,
-        * the display pipeline is likely to broken(If pixel combiner is used,
-        * one of the two display streams cannot be setup correctly sometimes.
-        * If pixel combiner is unused and prefetch engine is used, the first
-        * atomic flush after the enablement is likely to fail - content shadow
-        * load irq doesn't come.). This is a mysterious issue.
-        */
        if (dcstate->use_pc) {
-               tcon_set_operation_mode(dpu_crtc->m_tcon);
-               tcon_set_operation_mode(dpu_crtc->s_tcon);
-               framegen_wait_for_frame_counter_moving(dpu_crtc->m_fg);
-               framegen_wait_for_frame_counter_moving(dpu_crtc->s_fg);
-
                framegen_wait_for_secondary_syncup(dpu_crtc->m_fg);
                framegen_wait_for_secondary_syncup(dpu_crtc->s_fg);
 
@@ -244,9 +263,6 @@ static void dpu_crtc_atomic_enable(struct drm_crtc *crtc,
                                 "enable - slave FrameGen requests to read empty FIFO\n");
                }
        } else {
-               tcon_set_operation_mode(dpu_crtc->tcon);
-               framegen_wait_for_frame_counter_moving(dpu_crtc->fg);
-
                framegen_wait_for_secondary_syncup(dpu_crtc->fg);
 
                if (framegen_secondary_requests_to_read_empty_fifo(dpu_crtc->fg)) {
@@ -276,6 +292,9 @@ static void dpu_crtc_atomic_disable(struct drm_crtc *crtc,
        if (dcstate->use_pc) {
                tcon_disable_pc(dpu_crtc->tcon);
 
+               framegen_disable_pixel_link(dpu_crtc->m_fg);
+               framegen_disable_pixel_link(dpu_crtc->s_fg);
+
                /* First turn off the master stream, second the slave stream. */
                framegen_disable(dpu_crtc->m_fg);
                framegen_disable(dpu_crtc->s_fg);
@@ -286,6 +305,7 @@ static void dpu_crtc_atomic_disable(struct drm_crtc *crtc,
                framegen_disable_clock(dpu_crtc->stream_id ?
                                        dpu_crtc->aux_fg : dpu_crtc->fg);
        } else {
+               framegen_disable_pixel_link(dpu_crtc->fg);
                framegen_disable(dpu_crtc->fg);
                framegen_wait_done(dpu_crtc->fg, adjusted_mode);
                framegen_disable_clock(dpu_crtc->fg);
index 5085a77..3ec7930 100644 (file)
@@ -257,28 +257,36 @@ static void dpu_pixel_link_set_dc_sync_mode(int dpu_id, bool enable)
 }
 
 void framegen_enable(struct dpu_framegen *fg)
+{
+       dpu_fg_write(fg, FGEN, FGENABLE);
+}
+EXPORT_SYMBOL_GPL(framegen_enable);
+
+void framegen_disable(struct dpu_framegen *fg)
+{
+       dpu_fg_write(fg, 0, FGENABLE);
+}
+EXPORT_SYMBOL_GPL(framegen_disable);
+
+void framegen_enable_pixel_link(struct dpu_framegen *fg)
 {
        struct dpu_soc *dpu = fg->dpu;
        const struct dpu_devtype *devtype = dpu->devtype;
 
-       dpu_fg_write(fg, FGEN, FGENABLE);
-
        if (!(devtype->has_dual_ldb && fg->encoder_type_has_lvds))
                dpu_pixel_link_enable(dpu->id, fg->id);
 }
-EXPORT_SYMBOL_GPL(framegen_enable);
+EXPORT_SYMBOL_GPL(framegen_enable_pixel_link);
 
-void framegen_disable(struct dpu_framegen *fg)
+void framegen_disable_pixel_link(struct dpu_framegen *fg)
 {
        struct dpu_soc *dpu = fg->dpu;
        const struct dpu_devtype *devtype = dpu->devtype;
 
        if (!(devtype->has_dual_ldb && fg->encoder_type_has_lvds))
                dpu_pixel_link_disable(dpu->id, fg->id);
-
-       dpu_fg_write(fg, 0, FGENABLE);
 }
-EXPORT_SYMBOL_GPL(framegen_disable);
+EXPORT_SYMBOL_GPL(framegen_disable_pixel_link);
 
 void framegen_shdtokgen(struct dpu_framegen *fg)
 {
index 569b553..358a8bb 100644 (file)
@@ -621,6 +621,8 @@ void dpu_fw_put(struct dpu_fetchunit *fu);
 struct dpu_framegen;
 void framegen_enable(struct dpu_framegen *fg);
 void framegen_disable(struct dpu_framegen *fg);
+void framegen_enable_pixel_link(struct dpu_framegen *fg);
+void framegen_disable_pixel_link(struct dpu_framegen *fg);
 void framegen_shdtokgen(struct dpu_framegen *fg);
 void framegen_syncmode(struct dpu_framegen *fg, fgsyncmode_t mode);
 void