Commit 5cc2687c authored by Yongqiang Sun's avatar Yongqiang Sun Committed by Alex Deucher

drm/amd/display: Implement work around for optc underflow.

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: default avatarYongqiang Sun <yongqiang.sun@amd.com>
Reviewed-by: default avatarTony Cheng <Tony.Cheng@amd.com>
Acked-by: default avatarHarry Wentland <harry.wentland@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 82c026d1
...@@ -425,6 +425,34 @@ static void bios_golden_init(struct dc *dc) ...@@ -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( static enum dc_status dcn10_prog_pixclk_crtc_otg(
struct pipe_ctx *pipe_ctx, struct pipe_ctx *pipe_ctx,
struct dc_state *context, struct dc_state *context,
...@@ -493,8 +521,11 @@ static enum dc_status dcn10_prog_pixclk_crtc_otg( ...@@ -493,8 +521,11 @@ static enum dc_status dcn10_prog_pixclk_crtc_otg(
pipe_ctx->stream_res.tg, pipe_ctx->stream_res.tg,
&black_color); &black_color);
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); pipe_ctx->stream_res.tg->funcs->set_blank(pipe_ctx->stream_res.tg, true);
hwss_wait_for_blank_complete(pipe_ctx->stream_res.tg); 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 */ /* 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)) { 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( ...@@ -2252,6 +2283,9 @@ static void dcn10_apply_ctx_for_surface(
tg->funcs->unlock(tg); 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++) { for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct pipe_ctx *old_pipe_ctx = struct pipe_ctx *old_pipe_ctx =
&dc->current_state->res_ctx.pipe_ctx[i]; &dc->current_state->res_ctx.pipe_ctx[i];
......
...@@ -678,6 +678,7 @@ static struct dce_hwseq *dcn10_hwseq_create( ...@@ -678,6 +678,7 @@ static struct dce_hwseq *dcn10_hwseq_create(
hws->shifts = &hwseq_shift; hws->shifts = &hwseq_shift;
hws->masks = &hwseq_mask; hws->masks = &hwseq_mask;
hws->wa.DEGVIDCN10_253 = true; hws->wa.DEGVIDCN10_253 = true;
hws->wa.false_optc_underflow = true;
} }
return hws; return hws;
} }
......
...@@ -336,13 +336,6 @@ static void tgn10_blank_crtc(struct timing_generator *tg) ...@@ -336,13 +336,6 @@ static void tgn10_blank_crtc(struct timing_generator *tg)
OTG_BLANK_DATA_EN, 1, OTG_BLANK_DATA_EN, 1,
OTG_BLANK_DE_MODE, 0); 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); tgn10_set_blank_data_double_buffer(tg, false);
} }
...@@ -1199,14 +1192,19 @@ void tgn10_read_otg_state(struct dcn10_timing_generator *tgn10, ...@@ -1199,14 +1192,19 @@ void tgn10_read_otg_state(struct dcn10_timing_generator *tgn10,
OPTC_UNDERFLOW_OCCURRED_STATUS, &s->underflow_occurred_status); 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); 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); 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) static bool tgn10_is_tg_enabled(struct timing_generator *tg)
{ {
struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg); struct dcn10_timing_generator *tgn10 = DCN10TG_FROM_TG(tg);
...@@ -1217,6 +1215,19 @@ static bool tgn10_is_tg_enabled(struct timing_generator *tg) ...@@ -1217,6 +1215,19 @@ static bool tgn10_is_tg_enabled(struct timing_generator *tg)
return (otg_enabled != 0); 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 = { static const struct timing_generator_funcs dcn10_tg_funcs = {
.validate_timing = tgn10_validate_timing, .validate_timing = tgn10_validate_timing,
.program_timing = tgn10_program_timing, .program_timing = tgn10_program_timing,
...@@ -1249,6 +1260,8 @@ static const struct timing_generator_funcs dcn10_tg_funcs = { ...@@ -1249,6 +1260,8 @@ static const struct timing_generator_funcs dcn10_tg_funcs = {
.set_blank_data_double_buffer = tgn10_set_blank_data_double_buffer, .set_blank_data_double_buffer = tgn10_set_blank_data_double_buffer,
.tg_init = tgn10_tg_init, .tg_init = tgn10_tg_init,
.is_tg_enabled = tgn10_is_tg_enabled, .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) void dcn10_timing_generator_init(struct dcn10_timing_generator *tgn10)
......
...@@ -187,6 +187,8 @@ struct timing_generator_funcs { ...@@ -187,6 +187,8 @@ struct timing_generator_funcs {
void (*tg_init)(struct timing_generator *tg); void (*tg_init)(struct timing_generator *tg);
bool (*is_tg_enabled)(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 #endif
...@@ -40,6 +40,7 @@ enum pipe_gating_control { ...@@ -40,6 +40,7 @@ enum pipe_gating_control {
struct dce_hwseq_wa { struct dce_hwseq_wa {
bool blnd_crtc_trigger; bool blnd_crtc_trigger;
bool DEGVIDCN10_253; bool DEGVIDCN10_253;
bool false_optc_underflow;
}; };
struct hwseq_wa_state { struct hwseq_wa_state {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment