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 59d1719..9d1db74 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 0b9c045..d254d0b 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 652fa89..513676a 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.*/