]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/msm/dpu: Reorder encoder kickoff for CWB
authorJessica Zhang <quic_jesszhan@quicinc.com>
Sat, 15 Feb 2025 00:14:36 +0000 (16:14 -0800)
committerDmitry Baryshkov <dmitry.baryshkov@linaro.org>
Wed, 5 Mar 2025 02:34:12 +0000 (04:34 +0200)
Add a helper that will handle the correct order of the encoder kickoffs
for concurrent writeback.

For concurrent writeback, the realtime encoder must always kickoff last
as it will call the trigger flush and start.

This avoids the following scenario where the writeback encoder
increments the pending kickoff count after the WB_DONE interrupt is
fired:

If the realtime encoder is kicked off first, the encoder kickoff will
flush/start the encoder and increment the pending kickoff count. The
WB_DONE interrupt then fires (before the writeback encoder is kicked
off). When the writeback encoder enters its kickoff, it will skip the
flush/start (due to CWB being enabled) and hit a frame done timeout
as the frame was kicked off (and the WB_DONE interrupt fired) without
the pending kickoff count being incremented.

In addition, the writeback timer should only start after the realtime
encoder is kicked off to ensure that we don't get timeouts when the
system has a heavy load (ex. when debug logs are enabled)

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Reviewed-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
Signed-off-by: Jessica Zhang <quic_jesszhan@quicinc.com>
Patchwork: https://patchwork.freedesktop.org/patch/637491/
Link: https://lore.kernel.org/r/20250214-concurrent-wb-v6-13-a44c293cf422@quicinc.com
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c

index e2bc223acd13dff8708190d1b9adec51fc10fb09..b8e1a8f2aad57a6fa83e8a9e0caeae704cadf54e 100644 (file)
@@ -953,6 +953,45 @@ static int _dpu_crtc_wait_for_frame_done(struct drm_crtc *crtc)
        return rc;
 }
 
+static int dpu_crtc_kickoff_clone_mode(struct drm_crtc *crtc)
+{
+       struct drm_encoder *encoder;
+       struct drm_encoder *rt_encoder = NULL, *wb_encoder;
+       struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc);
+
+       /* Find encoder for real time display */
+       drm_for_each_encoder_mask(encoder, crtc->dev,
+                                 crtc->state->encoder_mask) {
+               if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL)
+                       wb_encoder = encoder;
+               else
+                       rt_encoder = encoder;
+       }
+
+       if (!rt_encoder || !wb_encoder) {
+               DRM_DEBUG_ATOMIC("real time or wb encoder not found\n");
+               return -EINVAL;
+       }
+
+       dpu_encoder_prepare_for_kickoff(wb_encoder);
+       dpu_encoder_prepare_for_kickoff(rt_encoder);
+
+       dpu_vbif_clear_errors(dpu_kms);
+
+       /*
+        * Kickoff real time encoder last as it's the encoder that
+        * will do the flush
+        */
+       dpu_encoder_kickoff(wb_encoder);
+       dpu_encoder_kickoff(rt_encoder);
+
+       /* Don't start frame done timers until the kickoffs have finished */
+       dpu_encoder_start_frame_done_timer(wb_encoder);
+       dpu_encoder_start_frame_done_timer(rt_encoder);
+
+       return 0;
+}
+
 /**
  * dpu_crtc_commit_kickoff - trigger kickoff of the commit for this crtc
  * @crtc: Pointer to drm crtc object
@@ -981,13 +1020,27 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc)
                        goto end;
                }
        }
-       /*
-        * Encoder will flush/start now, unless it has a tx pending. If so, it
-        * may delay and flush at an irq event (e.g. ppdone)
-        */
-       drm_for_each_encoder_mask(encoder, crtc->dev,
-                                 crtc->state->encoder_mask)
-               dpu_encoder_prepare_for_kickoff(encoder);
+
+       if (drm_crtc_in_clone_mode(crtc->state)) {
+               if (dpu_crtc_kickoff_clone_mode(crtc))
+                       goto end;
+       } else {
+               /*
+                * Encoder will flush/start now, unless it has a tx pending.
+                * If so, it may delay and flush at an irq event (e.g. ppdone)
+                */
+               drm_for_each_encoder_mask(encoder, crtc->dev,
+                               crtc->state->encoder_mask)
+                       dpu_encoder_prepare_for_kickoff(encoder);
+
+               dpu_vbif_clear_errors(dpu_kms);
+
+               drm_for_each_encoder_mask(encoder, crtc->dev,
+                               crtc->state->encoder_mask) {
+                       dpu_encoder_kickoff(encoder);
+                       dpu_encoder_start_frame_done_timer(encoder);
+               }
+       }
 
        if (atomic_inc_return(&dpu_crtc->frame_pending) == 1) {
                /* acquire bandwidth and other resources */
@@ -997,13 +1050,6 @@ void dpu_crtc_commit_kickoff(struct drm_crtc *crtc)
 
        dpu_crtc->play_count++;
 
-       dpu_vbif_clear_errors(dpu_kms);
-
-       drm_for_each_encoder_mask(encoder, crtc->dev, crtc->state->encoder_mask) {
-               dpu_encoder_kickoff(encoder);
-               dpu_encoder_start_frame_done_timer(encoder);
-       }
-
        reinit_completion(&dpu_crtc->frame_done_comp);
 
 end: