]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/amd/display: Wait for double buffer update on ODM changes
authorAlvin Lee <alvin.lee2@amd.com>
Mon, 10 Jun 2024 16:34:25 +0000 (12:34 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 27 Jun 2024 21:10:37 +0000 (17:10 -0400)
[WHAT & HOW]
We must wait for ODM double buffer updates to complete
before exiting the pipe update sequence or we may reduce
DISPCLK and hit some transient underflow (pixel rate is
reduced before the pipes have ODM enabled).

Reviewed-by: Samson Tam <samson.tam@amd.com>
Cc: Mario Limonciello <mario.limonciello@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: stable@vger.kernel.org
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Alvin Lee <alvin.lee2@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c
drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.h
drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.h
drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.h

index 4d359bb9b1ec00b254f8d5256653a340b520e2c4..36797ed7ad8ca0454c09c3cba8561ffc2df37571 100644 (file)
@@ -2227,6 +2227,29 @@ void dcn20_post_unlock_program_front_end(
                }
        }
 
+       for (i = 0; i < dc->res_pool->pipe_count; i++) {
+               struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
+               struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];
+
+               /* When going from a smaller ODM slice count to larger, we must ensure double
+                * buffer update completes before we return to ensure we don't reduce DISPCLK
+                * before we've transitioned to 2:1 or 4:1
+                */
+               if (resource_is_pipe_type(old_pipe, OTG_MASTER) && resource_is_pipe_type(pipe, OTG_MASTER) &&
+                               resource_get_odm_slice_count(old_pipe) < resource_get_odm_slice_count(pipe) &&
+                               dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_PHANTOM) {
+                       int j = 0;
+                       struct timing_generator *tg = pipe->stream_res.tg;
+
+
+                       if (tg->funcs->get_double_buffer_pending) {
+                               for (j = 0; j < TIMEOUT_FOR_PIPE_ENABLE_US / polling_interval_us
+                               && tg->funcs->get_double_buffer_pending(tg); j++)
+                                       udelay(polling_interval_us);
+                       }
+               }
+       }
+
        if (dc->res_pool->hubbub->funcs->force_pstate_change_control)
                dc->res_pool->hubbub->funcs->force_pstate_change_control(
                                dc->res_pool->hubbub, false, false);
index cd4826f329c15e1938d31eb9edbb296d9c9cb116..0f453452234cef12e05072839b93f749b24aca61 100644 (file)
@@ -340,6 +340,7 @@ struct timing_generator_funcs {
        void (*wait_drr_doublebuffer_pending_clear)(struct timing_generator *tg);
        void (*set_long_vtotal)(struct timing_generator *optc, const struct long_vtotal_params *params);
        void (*wait_odm_doublebuffer_pending_clear)(struct timing_generator *tg);
+       bool (*get_double_buffer_pending)(struct timing_generator *tg);
 };
 
 #endif
index e3e70c1db040e230c197d5550db26c82caff50c2..369a13244e5eccaa43ad52bc9970643b7e813fc9 100644 (file)
@@ -562,7 +562,8 @@ struct dcn_optc_registers {
        type OTG_CRC_DATA_FORMAT;\
        type OTG_V_TOTAL_LAST_USED_BY_DRR;\
        type OTG_DRR_TIMING_DBUF_UPDATE_PENDING;\
-       type OTG_H_TIMING_DIV_MODE_DB_UPDATE_PENDING;
+       type OTG_H_TIMING_DIV_MODE_DB_UPDATE_PENDING;\
+       type OPTC_DOUBLE_BUFFER_PENDING;\
 
 #define TG_REG_FIELD_LIST_DCN3_2(type) \
        type OTG_H_TIMING_DIV_MODE_MANUAL;
index 6c837409df4298da8e764456fb6cadb90100eceb..00094f0e8470659bcead7361ccc12af0c27f2c33 100644 (file)
@@ -297,6 +297,18 @@ static void optc32_set_drr(
        optc32_setup_manual_trigger(optc);
 }
 
+bool optc32_get_double_buffer_pending(struct timing_generator *optc)
+{
+       struct optc *optc1 = DCN10TG_FROM_TG(optc);
+       uint32_t update_pending = 0;
+
+       REG_GET(OPTC_INPUT_GLOBAL_CONTROL,
+                       OPTC_DOUBLE_BUFFER_PENDING,
+                       &update_pending);
+
+       return (update_pending == 1);
+}
+
 static struct timing_generator_funcs dcn32_tg_funcs = {
                .validate_timing = optc1_validate_timing,
                .program_timing = optc1_program_timing,
@@ -361,6 +373,7 @@ static struct timing_generator_funcs dcn32_tg_funcs = {
                .setup_manual_trigger = optc2_setup_manual_trigger,
                .get_hw_timing = optc1_get_hw_timing,
                .is_two_pixels_per_container = optc1_is_two_pixels_per_container,
+               .get_double_buffer_pending = optc32_get_double_buffer_pending,
 };
 
 void dcn32_timing_generator_init(struct optc *optc1)
index 0c2c14695561961212aff800f100ac7160d04ad3..665d7c52f67cd34d4cc3d247491b1a03320d243e 100644 (file)
        SF(ODM0_OPTC_INPUT_CLOCK_CONTROL, OPTC_INPUT_CLK_GATE_DIS, mask_sh),\
        SF(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_UNDERFLOW_OCCURRED_STATUS, mask_sh),\
        SF(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_UNDERFLOW_CLEAR, mask_sh),\
+       SF(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_DOUBLE_BUFFER_PENDING, mask_sh),\
        SF(VTG0_CONTROL, VTG0_ENABLE, mask_sh),\
        SF(VTG0_CONTROL, VTG0_FP2, mask_sh),\
        SF(VTG0_CONTROL, VTG0_VCOUNT_INIT, mask_sh),\
@@ -184,5 +185,6 @@ void optc32_get_odm_combine_segments(struct timing_generator *tg, int *odm_combi
 void optc32_set_odm_bypass(struct timing_generator *optc,
                const struct dc_crtc_timing *dc_crtc_timing);
 void optc32_wait_odm_doublebuffer_pending_clear(struct timing_generator *tg);
+bool optc32_get_double_buffer_pending(struct timing_generator *optc);
 
 #endif /* __DC_OPTC_DCN32_H__ */
index fd1c8b45c40e8f23e0c3d3e9996f0726dce84fcb..9f5c2efa7560b8331bf0e53c5ecdba48229fb08b 100644 (file)
@@ -459,6 +459,7 @@ static struct timing_generator_funcs dcn401_tg_funcs = {
                .setup_manual_trigger = optc2_setup_manual_trigger,
                .get_hw_timing = optc1_get_hw_timing,
                .is_two_pixels_per_container = optc1_is_two_pixels_per_container,
+               .get_double_buffer_pending = optc32_get_double_buffer_pending,
 };
 
 void dcn401_timing_generator_init(struct optc *optc1)
index 1671fdd5061cfb1ba03dafd13666bdb0f9f4a116..3114ecef332a5c45c59cfa19cb54c21e3dcfd8d5 100644 (file)
@@ -94,6 +94,7 @@
        SF(ODM0_OPTC_INPUT_CLOCK_CONTROL, OPTC_INPUT_CLK_ON, mask_sh),\
        SF(ODM0_OPTC_INPUT_CLOCK_CONTROL, OPTC_INPUT_CLK_GATE_DIS, mask_sh),\
        SF(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_UNDERFLOW_OCCURRED_STATUS, mask_sh),\
+       SF(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_DOUBLE_BUFFER_PENDING, mask_sh),\
        SF(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_UNDERFLOW_CLEAR, mask_sh),\
        SF(VTG0_CONTROL, VTG0_ENABLE, mask_sh),\
        SF(VTG0_CONTROL, VTG0_FP2, mask_sh),\