drm/amd/display: Implement work around for optc underflow.
authorYongqiang Sun <yongqiang.sun@amd.com>
Wed, 15 Nov 2017 21:21:34 +0000 (16:21 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Thu, 14 Dec 2017 15:52:51 +0000 (10:52 -0500)
Work around for a hw bug causing optc underflow if blank data
double buffer disable and remove mpcc.
Checking optc status after otg unlock, after wait mpcc idle
check status again, if optc underflow just happens after wait
mpcc idle, clear underflow status and enable blank data double
buffer.

Signed-off-by: Yongqiang Sun <yongqiang.sun@amd.com>
Reviewed-by: Tony Cheng <Tony.Cheng@amd.com>
Acked-by: Harry Wentland <harry.wentland@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_resource.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_timing_generator.c
drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
drivers/gpu/drm/amd/display/dc/inc/hw_sequencer.h

index e08808b7e2d6fc5ed1d45783d4a0b78016aba8c5..8e2520ba6bed7c3d62e2158611c12a1180828ba5 100644 (file)
@@ -425,6 +425,34 @@ static void bios_golden_init(struct dc *dc)
        }
 }
 
+static void false_optc_underflow_wa(
+               struct dc *dc,
+               const struct dc_stream_state *stream,
+               struct timing_generator *tg)
+{
+       int i;
+       bool underflow;
+
+       if (!dc->hwseq->wa.false_optc_underflow)
+               return;
+
+       underflow = tg->funcs->is_optc_underflow_occurred(tg);
+
+       for (i = 0; i < dc->res_pool->pipe_count; i++) {
+               struct pipe_ctx *old_pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
+
+               if (old_pipe_ctx->stream != stream)
+                       continue;
+
+               dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, old_pipe_ctx);
+       }
+
+       tg->funcs->set_blank_data_double_buffer(tg, true);
+
+       if (tg->funcs->is_optc_underflow_occurred(tg) && !underflow)
+               tg->funcs->clear_optc_underflow(tg);
+}
+
 static enum dc_status dcn10_prog_pixclk_crtc_otg(
                struct pipe_ctx *pipe_ctx,
                struct dc_state *context,
@@ -493,8 +521,11 @@ static enum dc_status dcn10_prog_pixclk_crtc_otg(
                        pipe_ctx->stream_res.tg,
                        &black_color);
 
-       pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, true);
-       hwss_wait_for_blank_complete(pipe_ctx->stream_res.tg);
+       if (!pipe_ctx->stream_res.tg->funcs->is_blanked(pipe_ctx->stream_res.tg)) {
+               pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, true);
+               hwss_wait_for_blank_complete(pipe_ctx->stream_res.tg);
+               false_optc_underflow_wa(dc, pipe_ctx->stream, pipe_ctx->stream_res.tg);
+       }
 
        /* VTG is  within DCHUB command block. DCFCLK is always on */
        if (false == pipe_ctx->stream_res.tg->funcs->enable_crtc(pipe_ctx->stream_res.tg)) {
@@ -2252,6 +2283,9 @@ static void dcn10_apply_ctx_for_surface(
 
        tg->funcs->unlock(tg);
 
+       if (num_planes == 0)
+               false_optc_underflow_wa(dc, stream, tg);
+
        for (i = 0; i < dc->res_pool->pipe_count; i++) {
                struct pipe_ctx *old_pipe_ctx =
                                &dc->current_state->res_ctx.pipe_ctx[i];
index 10cce51d31d2a08b87e5e7675d4d8a4664d802fc..a9a5d176cb7015f84706ac09d2853fdbbba4e041 100644 (file)
@@ -678,6 +678,7 @@ static struct dce_hwseq *dcn10_hwseq_create(
                hws->shifts = &hwseq_shift;
                hws->masks = &hwseq_mask;
                hws->wa.DEGVIDCN10_253 = true;
+               hws->wa.false_optc_underflow = true;
        }
        return hws;
 }
index 73ff78f9cae1cc1868afb23b3b10e34c26bfd54f..4940fdbc6e8062d7f9601741e5e801aace5e1010 100644 (file)
@@ -336,13 +336,6 @@ static void tgn10_blank_crtc(struct timing_generator *tg)
                        OTG_BLANK_DATA_EN, 1,
                        OTG_BLANK_DE_MODE, 0);
 
-       /* todo: why are we waiting for BLANK_DATA_EN?  shouldn't we be waiting
-        * for status?
-        */
-       REG_WAIT(OTG_BLANK_CONTROL,
-                       OTG_BLANK_DATA_EN, 1,
-                       1, 100000);
-
        tgn10_set_blank_data_double_buffer(tg, false);
 }
 
@@ -1199,14 +1192,19 @@ void tgn10_read_otg_state(struct dcn10_timing_generator *tgn10,
                        OPTC_UNDERFLOW_OCCURRED_STATUS, &s->underflow_occurred_status);
 }
 
-static void tgn10_tg_init(struct timing_generator *tg)
+static void tgn10_clear_optc_underflow(struct timing_generator *tg)
 {
        struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg);
 
-       tgn10_set_blank_data_double_buffer(tg, true);
        REG_UPDATE(OPTC_INPUT_GLOBAL_CONTROL, OPTC_UNDERFLOW_CLEAR, 1);
 }
 
+static void tgn10_tg_init(struct timing_generator *tg)
+{
+       tgn10_set_blank_data_double_buffer(tg, true);
+       tgn10_clear_optc_underflow(tg);
+}
+
 static bool tgn10_is_tg_enabled(struct timing_generator *tg)
 {
        struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg);
@@ -1217,6 +1215,19 @@ static bool tgn10_is_tg_enabled(struct timing_generator *tg)
        return (otg_enabled != 0);
 
 }
+
+static bool tgn10_is_optc_underflow_occurred(struct timing_generator *tg)
+{
+       struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg);
+       uint32_t underflow_occurred = 0;
+
+       REG_GET(OPTC_INPUT_GLOBAL_CONTROL,
+                       OPTC_UNDERFLOW_OCCURRED_STATUS,
+                       &underflow_occurred);
+
+       return (underflow_occurred == 1);
+}
+
 static const struct timing_generator_funcs dcn10_tg_funcs = {
                .validate_timing = tgn10_validate_timing,
                .program_timing = tgn10_program_timing,
@@ -1249,6 +1260,8 @@ static const struct timing_generator_funcs dcn10_tg_funcs = {
                .set_blank_data_double_buffer = tgn10_set_blank_data_double_buffer,
                .tg_init = tgn10_tg_init,
                .is_tg_enabled = tgn10_is_tg_enabled,
+               .is_optc_underflow_occurred = tgn10_is_optc_underflow_occurred,
+               .clear_optc_underflow = tgn10_clear_optc_underflow,
 };
 
 void dcn10_timing_generator_init(struct dcn10_timing_generator *tgn10)
index 860259913d7863336686d2ac1f2e6de17eee79df..e5c7e0e1db140c337ae70307530ad23a267b65ab 100644 (file)
@@ -187,6 +187,8 @@ struct timing_generator_funcs {
 
        void (*tg_init)(struct timing_generator *tg);
        bool (*is_tg_enabled)(struct timing_generator *tg);
+       bool (*is_optc_underflow_occurred)(struct timing_generator *tg);
+       void (*clear_optc_underflow)(struct timing_generator *tg);
 };
 
 #endif
index 5dc4ecf618ff4afe82f8c0e499b08472dc404fcb..03431134c0881f65c14fb6dfe6497c9bc6db3d00 100644 (file)
@@ -40,6 +40,7 @@ enum pipe_gating_control {
 struct dce_hwseq_wa {
        bool blnd_crtc_trigger;
        bool DEGVIDCN10_253;
+       bool false_optc_underflow;
 };
 
 struct hwseq_wa_state {