]> git.baikalelectronics.ru Git - kernel.git/commitdiff
drm/amd/display: Defer cursor lock until after VUPDATE
authorNicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Mon, 4 May 2020 20:49:28 +0000 (16:49 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 21 May 2020 16:48:43 +0000 (12:48 -0400)
[Why]
We dropped the delay after changed the cursor functions locking the
entire pipe to locking just the CURSOR registers to fix page flip
stuttering - this introduced cursor stuttering instead, and an underflow
issue.

The cursor update can be delayed indefinitely if the cursor update
repeatedly happens right around VUPDATE.

The underflow issue can happen if we do a viewport update on a pipe
on the same frame where a cursor update happens around VUPDATE - the
old cursor registers are retained which can be in an invalid position.

This can cause a pipe hang and indefinite underflow.

[How]
The complex, ideal solution to the problem would be a software
triple buffering mechanism from the DM layer to program only one cursor
update per frame just before VUPDATE.

The simple workaround until we have that infrastructure in place is
this change - bring back the delay until VUPDATE before locking, but
with some corrections to the calculations.

This didn't work for all timings before because the calculation for
VUPDATE was wrong - it was using the offset from VSTARTUP instead and
didn't correctly handle the case where VUPDATE could be in the back
porch.

Add a new hardware sequencer function to use the existing helper to
calculate the real VUPDATE start and VUPDATE end - VUPDATE can last
multiple lines after all.

Change the udelay to incorporate the width of VUPDATE as well.

Signed-off-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Reviewed-by: Aric Cyr <Aric.Cyr@amd.com>
Acked-by: Rodrigo Siqueira <Rodrigo.Siqueira@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.h
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_init.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_init.c
drivers/gpu/drm/amd/display/dc/dcn21/dcn21_init.c
drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h

index daf6977b5fb5bbaed425dc3473584ae217085f6c..f59cf60fb78a9c67479ab81a5ec0f6978d8de53a 100644 (file)
@@ -1683,12 +1683,79 @@ void dcn10_pipe_control_lock(
                hws->funcs.verify_allow_pstate_change_high(dc);
 }
 
+/**
+ * delay_cursor_until_vupdate() - Delay cursor update if too close to VUPDATE.
+ *
+ * Software keepout workaround to prevent cursor update locking from stalling
+ * out cursor updates indefinitely or from old values from being retained in
+ * the case where the viewport changes in the same frame as the cursor.
+ *
+ * The idea is to calculate the remaining time from VPOS to VUPDATE. If it's
+ * too close to VUPDATE, then stall out until VUPDATE finishes.
+ *
+ * TODO: Optimize cursor programming to be once per frame before VUPDATE
+ *       to avoid the need for this workaround.
+ */
+static void delay_cursor_until_vupdate(struct dc *dc, struct pipe_ctx *pipe_ctx)
+{
+       struct dc_stream_state *stream = pipe_ctx->stream;
+       struct crtc_position position;
+       uint32_t vupdate_start, vupdate_end;
+       unsigned int lines_to_vupdate, us_to_vupdate, vpos;
+       unsigned int us_per_line, us_vupdate;
+
+       if (!dc->hwss.calc_vupdate_position || !dc->hwss.get_position)
+               return;
+
+       if (!pipe_ctx->stream_res.stream_enc || !pipe_ctx->stream_res.tg)
+               return;
+
+       dc->hwss.calc_vupdate_position(dc, pipe_ctx, &vupdate_start,
+                                      &vupdate_end);
+
+       dc->hwss.get_position(&pipe_ctx, 1, &position);
+       vpos = position.vertical_count;
+
+       /* Avoid wraparound calculation issues */
+       vupdate_start += stream->timing.v_total;
+       vupdate_end += stream->timing.v_total;
+       vpos += stream->timing.v_total;
+
+       if (vpos <= vupdate_start) {
+               /* VPOS is in VACTIVE or back porch. */
+               lines_to_vupdate = vupdate_start - vpos;
+       } else if (vpos > vupdate_end) {
+               /* VPOS is in the front porch. */
+               return;
+       } else {
+               /* VPOS is in VUPDATE. */
+               lines_to_vupdate = 0;
+       }
+
+       /* Calculate time until VUPDATE in microseconds. */
+       us_per_line =
+               stream->timing.h_total * 10000u / stream->timing.pix_clk_100hz;
+       us_to_vupdate = lines_to_vupdate * us_per_line;
+
+       /* 70 us is a conservative estimate of cursor update time*/
+       if (us_to_vupdate > 70)
+               return;
+
+       /* Stall out until the cursor update completes. */
+       us_vupdate = (vupdate_end - vupdate_start + 1) * us_per_line;
+       udelay(us_to_vupdate + us_vupdate);
+}
+
 void dcn10_cursor_lock(struct dc *dc, struct pipe_ctx *pipe, bool lock)
 {
        /* cursor lock is per MPCC tree, so only need to lock one pipe per stream */
        if (!pipe || pipe->top_pipe)
                return;
 
+       /* Prevent cursor lock from stalling out cursor updates. */
+       if (lock)
+               delay_cursor_until_vupdate(dc, pipe);
+
        dc->res_pool->mpc->funcs->cursor_lock(dc->res_pool->mpc,
                        pipe->stream_res.opp->inst, lock);
 }
@@ -3302,7 +3369,7 @@ int dcn10_get_vupdate_offset_from_vsync(struct pipe_ctx *pipe_ctx)
        return vertical_line_start;
 }
 
-static void dcn10_calc_vupdate_position(
+void dcn10_calc_vupdate_position(
                struct dc *dc,
                struct pipe_ctx *pipe_ctx,
                uint32_t *start_line,
index af51424315d5ca9f4c38b0102c22a7a9c20e6c4e..42b6e016d71e8d0582acf3a8c8191bf7b9a0077a 100644 (file)
@@ -34,6 +34,11 @@ struct dc;
 void dcn10_hw_sequencer_construct(struct dc *dc);
 
 int dcn10_get_vupdate_offset_from_vsync(struct pipe_ctx *pipe_ctx);
+void dcn10_calc_vupdate_position(
+               struct dc *dc,
+               struct pipe_ctx *pipe_ctx,
+               uint32_t *start_line,
+               uint32_t *end_line);
 void dcn10_setup_vupdate_interrupt(struct dc *dc, struct pipe_ctx *pipe_ctx);
 enum dc_status dcn10_enable_stream_timing(
                struct pipe_ctx *pipe_ctx,
index 897a3d25685a2ce985fea97b7369c9fa31981f99..7cb8c3fb266563841d819bebec1571782deeb966 100644 (file)
@@ -72,6 +72,7 @@ static const struct hw_sequencer_funcs dcn10_funcs = {
        .set_clock = dcn10_set_clock,
        .get_clock = dcn10_get_clock,
        .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
+       .calc_vupdate_position = dcn10_calc_vupdate_position,
        .set_backlight_level = dce110_set_backlight_level,
        .set_abm_immediate_disable = dce110_set_abm_immediate_disable,
 };
index a8bcd747d7ba2293597278089f5d4542cff0a80d..2fbde4241559f11c6ffa8e9cea56a02e60d98077 100644 (file)
@@ -83,6 +83,7 @@ static const struct hw_sequencer_funcs dcn20_funcs = {
        .init_vm_ctx = dcn20_init_vm_ctx,
        .set_flip_control_gsl = dcn20_set_flip_control_gsl,
        .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
+       .calc_vupdate_position = dcn10_calc_vupdate_position,
        .set_backlight_level = dce110_set_backlight_level,
        .set_abm_immediate_disable = dce110_set_abm_immediate_disable,
 };
index e97dfaa656e93bf57be23501691932125fb2efa8..a5baef7e7a7d681f372ccb1c701b3e5ba2759fd2 100644 (file)
@@ -86,6 +86,7 @@ static const struct hw_sequencer_funcs dcn21_funcs = {
        .optimize_pwr_state = dcn21_optimize_pwr_state,
        .exit_optimized_pwr_state = dcn21_exit_optimized_pwr_state,
        .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
+       .calc_vupdate_position = dcn10_calc_vupdate_position,
        .power_down = dce110_power_down,
        .set_backlight_level = dce110_set_backlight_level,
        .set_abm_immediate_disable = dce110_set_abm_immediate_disable,
index 3b2ea9bdb62c2522457d6f3bf22085df5da40251..8e72f077e5520e590074f13acf7abcbdf25184c7 100644 (file)
@@ -96,6 +96,11 @@ struct hw_sequencer_funcs {
        void (*get_position)(struct pipe_ctx **pipe_ctx, int num_pipes,
                        struct crtc_position *position);
        int (*get_vupdate_offset_from_vsync)(struct pipe_ctx *pipe_ctx);
+       void (*calc_vupdate_position)(
+                       struct dc *dc,
+                       struct pipe_ctx *pipe_ctx,
+                       uint32_t *start_line,
+                       uint32_t *end_line);
        void (*enable_per_frame_crtc_position_reset)(struct dc *dc,
                        int group_size, struct pipe_ctx *grouped_pipes[]);
        void (*enable_timing_synchronization)(struct dc *dc,