Commit 4228900a authored by Alvin Lee's avatar Alvin Lee Committed by Alex Deucher

drm/amd/display: Wait for double buffer update on ODM changes

[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: default avatarSamson 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: default avatarAlex Hung <alex.hung@amd.com>
Signed-off-by: default avatarAlvin Lee <alvin.lee2@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 7210195f
...@@ -2227,6 +2227,29 @@ void dcn20_post_unlock_program_front_end( ...@@ -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) if (dc->res_pool->hubbub->funcs->force_pstate_change_control)
dc->res_pool->hubbub->funcs->force_pstate_change_control( dc->res_pool->hubbub->funcs->force_pstate_change_control(
dc->res_pool->hubbub, false, false); dc->res_pool->hubbub, false, false);
......
...@@ -340,6 +340,7 @@ struct timing_generator_funcs { ...@@ -340,6 +340,7 @@ struct timing_generator_funcs {
void (*wait_drr_doublebuffer_pending_clear)(struct timing_generator *tg); 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 (*set_long_vtotal)(struct timing_generator *optc, const struct long_vtotal_params *params);
void (*wait_odm_doublebuffer_pending_clear)(struct timing_generator *tg); void (*wait_odm_doublebuffer_pending_clear)(struct timing_generator *tg);
bool (*get_double_buffer_pending)(struct timing_generator *tg);
}; };
#endif #endif
...@@ -562,7 +562,8 @@ struct dcn_optc_registers { ...@@ -562,7 +562,8 @@ struct dcn_optc_registers {
type OTG_CRC_DATA_FORMAT;\ type OTG_CRC_DATA_FORMAT;\
type OTG_V_TOTAL_LAST_USED_BY_DRR;\ type OTG_V_TOTAL_LAST_USED_BY_DRR;\
type OTG_DRR_TIMING_DBUF_UPDATE_PENDING;\ 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) \ #define TG_REG_FIELD_LIST_DCN3_2(type) \
type OTG_H_TIMING_DIV_MODE_MANUAL; type OTG_H_TIMING_DIV_MODE_MANUAL;
......
...@@ -297,6 +297,18 @@ static void optc32_set_drr( ...@@ -297,6 +297,18 @@ static void optc32_set_drr(
optc32_setup_manual_trigger(optc); 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 = { static struct timing_generator_funcs dcn32_tg_funcs = {
.validate_timing = optc1_validate_timing, .validate_timing = optc1_validate_timing,
.program_timing = optc1_program_timing, .program_timing = optc1_program_timing,
...@@ -361,6 +373,7 @@ static struct timing_generator_funcs dcn32_tg_funcs = { ...@@ -361,6 +373,7 @@ static struct timing_generator_funcs dcn32_tg_funcs = {
.setup_manual_trigger = optc2_setup_manual_trigger, .setup_manual_trigger = optc2_setup_manual_trigger,
.get_hw_timing = optc1_get_hw_timing, .get_hw_timing = optc1_get_hw_timing,
.is_two_pixels_per_container = optc1_is_two_pixels_per_container, .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) void dcn32_timing_generator_init(struct optc *optc1)
......
...@@ -116,6 +116,7 @@ ...@@ -116,6 +116,7 @@
SF(ODM0_OPTC_INPUT_CLOCK_CONTROL, OPTC_INPUT_CLK_GATE_DIS, 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_UNDERFLOW_OCCURRED_STATUS, mask_sh),\
SF(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_UNDERFLOW_CLEAR, 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_ENABLE, mask_sh),\
SF(VTG0_CONTROL, VTG0_FP2, mask_sh),\ SF(VTG0_CONTROL, VTG0_FP2, mask_sh),\
SF(VTG0_CONTROL, VTG0_VCOUNT_INIT, 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 ...@@ -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, void optc32_set_odm_bypass(struct timing_generator *optc,
const struct dc_crtc_timing *dc_crtc_timing); const struct dc_crtc_timing *dc_crtc_timing);
void optc32_wait_odm_doublebuffer_pending_clear(struct timing_generator *tg); 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__ */ #endif /* __DC_OPTC_DCN32_H__ */
...@@ -459,6 +459,7 @@ static struct timing_generator_funcs dcn401_tg_funcs = { ...@@ -459,6 +459,7 @@ static struct timing_generator_funcs dcn401_tg_funcs = {
.setup_manual_trigger = optc2_setup_manual_trigger, .setup_manual_trigger = optc2_setup_manual_trigger,
.get_hw_timing = optc1_get_hw_timing, .get_hw_timing = optc1_get_hw_timing,
.is_two_pixels_per_container = optc1_is_two_pixels_per_container, .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) void dcn401_timing_generator_init(struct optc *optc1)
......
...@@ -94,6 +94,7 @@ ...@@ -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_ON, mask_sh),\
SF(ODM0_OPTC_INPUT_CLOCK_CONTROL, OPTC_INPUT_CLK_GATE_DIS, 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_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(ODM0_OPTC_INPUT_GLOBAL_CONTROL, OPTC_UNDERFLOW_CLEAR, mask_sh),\
SF(VTG0_CONTROL, VTG0_ENABLE, mask_sh),\ SF(VTG0_CONTROL, VTG0_ENABLE, mask_sh),\
SF(VTG0_CONTROL, VTG0_FP2, mask_sh),\ SF(VTG0_CONTROL, VTG0_FP2, mask_sh),\
......
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