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: Samson 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: Alex Hung <alex.hung@amd.com> Signed-off-by: Alvin Lee <alvin.lee2@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
@@ -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)
|
||||
dc->res_pool->hubbub->funcs->force_pstate_change_control(
|
||||
dc->res_pool->hubbub, false, false);
|
||||
|
||||
@@ -340,6 +340,7 @@ struct timing_generator_funcs {
|
||||
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 (*wait_odm_doublebuffer_pending_clear)(struct timing_generator *tg);
|
||||
bool (*get_double_buffer_pending)(struct timing_generator *tg);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -562,7 +562,8 @@ struct dcn_optc_registers {
|
||||
type OTG_CRC_DATA_FORMAT;\
|
||||
type OTG_V_TOTAL_LAST_USED_BY_DRR;\
|
||||
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) \
|
||||
type OTG_H_TIMING_DIV_MODE_MANUAL;
|
||||
|
||||
@@ -297,6 +297,18 @@ static void optc32_set_drr(
|
||||
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 = {
|
||||
.validate_timing = optc1_validate_timing,
|
||||
.program_timing = optc1_program_timing,
|
||||
@@ -361,6 +373,7 @@ static struct timing_generator_funcs dcn32_tg_funcs = {
|
||||
.setup_manual_trigger = optc2_setup_manual_trigger,
|
||||
.get_hw_timing = optc1_get_hw_timing,
|
||||
.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)
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
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_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_FP2, 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
|
||||
void optc32_set_odm_bypass(struct timing_generator *optc,
|
||||
const struct dc_crtc_timing *dc_crtc_timing);
|
||||
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__ */
|
||||
|
||||
@@ -459,6 +459,7 @@ static struct timing_generator_funcs dcn401_tg_funcs = {
|
||||
.setup_manual_trigger = optc2_setup_manual_trigger,
|
||||
.get_hw_timing = optc1_get_hw_timing,
|
||||
.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)
|
||||
|
||||
@@ -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_GATE_DIS, 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(VTG0_CONTROL, VTG0_ENABLE, mask_sh),\
|
||||
SF(VTG0_CONTROL, VTG0_FP2, mask_sh),\
|
||||
|
||||
Reference in New Issue
Block a user