]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
drm/amd/display: Cover edge-case when changing DISPCLK WDIVIDER
authorWesley Chalmers <Wesley.Chalmers@amd.com>
Thu, 6 May 2021 21:43:42 +0000 (17:43 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Tue, 15 Jun 2021 21:25:41 +0000 (17:25 -0400)
[WHY]
When changing the DISPCLK_WDIVIDER value from 126 to 127, the change in
clock rate is too great for the FIFOs to handle. This can cause visible
corruption during clock change.

HW has handed down this register sequence to fix the issue.

[HOW]
The sequence, from HW:
a. 127 -> 126
Read  DIG_FIFO_CAL_AVERAGE_LEVEL
FIFO level N = DIG_FIFO_CAL_AVERAGE_LEVEL / 4
Set DCCG_FIFO_ERRDET_OVR_EN = 1
Write 1 to OTGx_DROP_PIXEL for (N-4) times
Set DCCG_FIFO_ERRDET_OVR_EN = 0
Write DENTIST_DISPCLK_RDIVIDER = 126

Because of frequency stepping, sequence a can be executed to change the
divider from 127 to any other divider value.

b. 126 -> 127
Read  DIG_FIFO_CAL_AVERAGE_LEVEL
FIFO level N = DIG_FIFO_CAL_AVERAGE_LEVEL / 4
Set DCCG_FIFO_ERRDET_OVR_EN = 1
Write 1 to OTGx_ADD_PIXEL for (12-N) times
Set DCCG_FIFO_ERRDET_OVR_EN = 0
Write DENTIST_DISPCLK_RDIVIDER = 127

Because of frequency stepping, divider must first be set from any other
divider value to 126 before executing sequence b.

Signed-off-by: Wesley Chalmers <Wesley.Chalmers@amd.com>
Reviewed-by: Dmytro Laktyushkin <Dmytro.Laktyushkin@amd.com>
Acked-by: Anson Jacob <Anson.Jacob@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/clk_mgr/dcn20/dcn20_clk_mgr.c
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.h
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c

index 59d17195bc22319744e7a39776bb902b9d04554b..9d1db74de36d7b6ca5d42d500e2426c86a1497ff 100644 (file)
@@ -123,7 +123,7 @@ void dcn20_update_clocks_update_dpp_dto(struct clk_mgr_internal *clk_mgr,
        }
 }
 
-void dcn20_update_clocks_update_dentist(struct clk_mgr_internal *clk_mgr)
+void dcn20_update_clocks_update_dentist(struct clk_mgr_internal *clk_mgr, struct dc_state *context)
 {
        int dpp_divider = DENTIST_DIVIDER_RANGE_SCALE_FACTOR
                        * clk_mgr->base.dentist_vco_freq_khz / clk_mgr->base.clks.dppclk_khz;
@@ -132,6 +132,68 @@ void dcn20_update_clocks_update_dentist(struct clk_mgr_internal *clk_mgr)
 
        uint32_t dppclk_wdivider = dentist_get_did_from_divider(dpp_divider);
        uint32_t dispclk_wdivider = dentist_get_did_from_divider(disp_divider);
+       uint32_t current_dispclk_wdivider;
+       uint32_t i;
+
+       REG_GET(DENTIST_DISPCLK_CNTL,
+                       DENTIST_DISPCLK_WDIVIDER, &current_dispclk_wdivider);
+
+       /* When changing divider to or from 127, some extra programming is required to prevent corruption */
+       if (current_dispclk_wdivider == 127 && dispclk_wdivider != 127) {
+               for (i = 0; i < clk_mgr->base.ctx->dc->res_pool->pipe_count; i++) {
+                       struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
+                       uint32_t fifo_level;
+                       struct dccg *dccg = clk_mgr->base.ctx->dc->res_pool->dccg;
+                       struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc;
+                       int32_t N;
+                       int32_t j;
+
+                       if (!pipe_ctx->stream)
+                               continue;
+                       /* Virtual encoders don't have this function */
+                       if (!stream_enc->funcs->get_fifo_cal_average_level)
+                               continue;
+                       fifo_level = stream_enc->funcs->get_fifo_cal_average_level(
+                                       stream_enc);
+                       N = fifo_level / 4;
+                       dccg->funcs->set_fifo_errdet_ovr_en(
+                                       dccg,
+                                       true);
+                       for (j = 0; j < N - 4; j++)
+                               dccg->funcs->otg_drop_pixel(
+                                               dccg,
+                                               pipe_ctx->stream_res.tg->inst);
+                       dccg->funcs->set_fifo_errdet_ovr_en(
+                                       dccg,
+                                       false);
+               }
+       } else if (dispclk_wdivider == 127 && current_dispclk_wdivider != 127) {
+               REG_UPDATE(DENTIST_DISPCLK_CNTL,
+                               DENTIST_DISPCLK_WDIVIDER, 126);
+               REG_WAIT(DENTIST_DISPCLK_CNTL, DENTIST_DISPCLK_CHG_DONE, 1, 50, 100);
+               for (i = 0; i < clk_mgr->base.ctx->dc->res_pool->pipe_count; i++) {
+                       struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
+                       struct dccg *dccg = clk_mgr->base.ctx->dc->res_pool->dccg;
+                       struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc;
+                       uint32_t fifo_level;
+                       int32_t N;
+                       int32_t j;
+
+                       if (!pipe_ctx->stream)
+                               continue;
+                       /* Virtual encoders don't have this function */
+                       if (!stream_enc->funcs->get_fifo_cal_average_level)
+                               continue;
+                       fifo_level = stream_enc->funcs->get_fifo_cal_average_level(
+                                       stream_enc);
+                       N = fifo_level / 4;
+                       dccg->funcs->set_fifo_errdet_ovr_en(dccg, true);
+                       for (j = 0; j < 12 - N; j++)
+                               dccg->funcs->otg_add_pixel(dccg,
+                                               pipe_ctx->stream_res.tg->inst);
+                       dccg->funcs->set_fifo_errdet_ovr_en(dccg, false);
+               }
+       }
 
        REG_UPDATE(DENTIST_DISPCLK_CNTL,
                        DENTIST_DISPCLK_WDIVIDER, dispclk_wdivider);
@@ -251,11 +313,11 @@ void dcn2_update_clocks(struct clk_mgr *clk_mgr_base,
                if (dpp_clock_lowered) {
                        // if clock is being lowered, increase DTO before lowering refclk
                        dcn20_update_clocks_update_dpp_dto(clk_mgr, context, safe_to_lower);
-                       dcn20_update_clocks_update_dentist(clk_mgr);
+                       dcn20_update_clocks_update_dentist(clk_mgr, context);
                } else {
                        // if clock is being raised, increase refclk before lowering DTO
                        if (update_dppclk || update_dispclk)
-                               dcn20_update_clocks_update_dentist(clk_mgr);
+                               dcn20_update_clocks_update_dentist(clk_mgr, context);
                        // always update dtos unless clock is lowered and not safe to lower
                        dcn20_update_clocks_update_dpp_dto(clk_mgr, context, safe_to_lower);
                }
index 0b9c045b0c8e944bc5bfa0c7ea84b8b8a0502c5e..d254d0b6fba16574e7a6e3a0874d659e40a4ae11 100644 (file)
@@ -50,7 +50,8 @@ void dcn2_get_clock(struct clk_mgr *clk_mgr,
                        enum dc_clock_type clock_type,
                        struct dc_clock_config *clock_cfg);
 
-void dcn20_update_clocks_update_dentist(struct clk_mgr_internal *clk_mgr);
+void dcn20_update_clocks_update_dentist(struct clk_mgr_internal *clk_mgr,
+                                       struct dc_state *context);
 
 void dcn2_read_clocks_from_hw_dentist(struct clk_mgr *clk_mgr_base);
 
index 652fa89fae5fe20d39768ee0c9ba01c169f38007..513676a6f52bc2751376b31bf690d6564baa1ff1 100644 (file)
@@ -334,11 +334,11 @@ static void dcn3_update_clocks(struct clk_mgr *clk_mgr_base,
                if (dpp_clock_lowered) {
                        /* if clock is being lowered, increase DTO before lowering refclk */
                        dcn20_update_clocks_update_dpp_dto(clk_mgr, context, safe_to_lower);
-                       dcn20_update_clocks_update_dentist(clk_mgr);
+                       dcn20_update_clocks_update_dentist(clk_mgr, context);
                } else {
                        /* if clock is being raised, increase refclk before lowering DTO */
                        if (update_dppclk || update_dispclk)
-                               dcn20_update_clocks_update_dentist(clk_mgr);
+                               dcn20_update_clocks_update_dentist(clk_mgr, context);
                        /* There is a check inside dcn20_update_clocks_update_dpp_dto which ensures
                         * that we do not lower dto when it is not safe to lower. We do not need to
                         * compare the current and new dppclk before calling this function.*/