mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 05:41:55 +00:00
drm/amd/display: Implement wait_for_odm_update_pending_complete
[WHY] Odm update is doubled buffered. We need to wait for ODM update to be completed before optimizing bandwidth or programming new udpates. [HOW] implement wait_for_odm_update_pending_complete function to wait for: 1. odm configuration update is no longer pending in timing generator. 2. no pending dpg pattern update for each active OPP. Cc: Mario Limonciello <mario.limonciello@amd.com> Cc: Alex Deucher <alexander.deucher@amd.com> Cc: stable@vger.kernel.org Reviewed-by: Alvin Lee <alvin.lee2@amd.com> Acked-by: Alex Hung <alex.hung@amd.com> Signed-off-by: Wenjing Liu <wenjing.liu@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
94040c2cbb
commit
2d7f3d1a58
@ -1302,6 +1302,54 @@ static void disable_vbios_mode_if_required(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wait_for_blank_complete - wait for all active OPPs to finish pending blank
|
||||
* pattern updates
|
||||
*
|
||||
* @dc: [in] dc reference
|
||||
* @context: [in] hardware context in use
|
||||
*/
|
||||
static void wait_for_blank_complete(struct dc *dc,
|
||||
struct dc_state *context)
|
||||
{
|
||||
struct pipe_ctx *opp_head;
|
||||
struct dce_hwseq *hws = dc->hwseq;
|
||||
int i;
|
||||
|
||||
if (!hws->funcs.wait_for_blank_complete)
|
||||
return;
|
||||
|
||||
for (i = 0; i < MAX_PIPES; i++) {
|
||||
opp_head = &context->res_ctx.pipe_ctx[i];
|
||||
|
||||
if (!resource_is_pipe_type(opp_head, OPP_HEAD) ||
|
||||
dc_state_get_pipe_subvp_type(context, opp_head) == SUBVP_PHANTOM)
|
||||
continue;
|
||||
|
||||
hws->funcs.wait_for_blank_complete(opp_head->stream_res.opp);
|
||||
}
|
||||
}
|
||||
|
||||
static void wait_for_odm_update_pending_complete(struct dc *dc, struct dc_state *context)
|
||||
{
|
||||
struct pipe_ctx *otg_master;
|
||||
struct timing_generator *tg;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_PIPES; i++) {
|
||||
otg_master = &context->res_ctx.pipe_ctx[i];
|
||||
if (!resource_is_pipe_type(otg_master, OTG_MASTER) ||
|
||||
dc_state_get_pipe_subvp_type(context, otg_master) == SUBVP_PHANTOM)
|
||||
continue;
|
||||
tg = otg_master->stream_res.tg;
|
||||
if (tg->funcs->wait_odm_doublebuffer_pending_clear)
|
||||
tg->funcs->wait_odm_doublebuffer_pending_clear(tg);
|
||||
}
|
||||
|
||||
/* ODM update may require to reprogram blank pattern for each OPP */
|
||||
wait_for_blank_complete(dc, context);
|
||||
}
|
||||
|
||||
static void wait_for_no_pipes_pending(struct dc *dc, struct dc_state *context)
|
||||
{
|
||||
int i;
|
||||
@ -1993,6 +2041,11 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
|
||||
context->stream_count == 0) {
|
||||
/* Must wait for no flips to be pending before doing optimize bw */
|
||||
wait_for_no_pipes_pending(dc, context);
|
||||
/*
|
||||
* optimized dispclk depends on ODM setup. Need to wait for ODM
|
||||
* update pending complete before optimizing bandwidth.
|
||||
*/
|
||||
wait_for_odm_update_pending_complete(dc, context);
|
||||
/* pplib is notified if disp_num changed */
|
||||
dc->hwss.optimize_bandwidth(dc, context);
|
||||
/* Need to do otg sync again as otg could be out of sync due to otg
|
||||
@ -3496,7 +3549,7 @@ static void commit_planes_for_stream_fast(struct dc *dc,
|
||||
top_pipe_to_program->stream->update_flags.raw = 0;
|
||||
}
|
||||
|
||||
static void wait_for_outstanding_hw_updates(struct dc *dc, const struct dc_state *dc_context)
|
||||
static void wait_for_outstanding_hw_updates(struct dc *dc, struct dc_state *dc_context)
|
||||
{
|
||||
/*
|
||||
* This function calls HWSS to wait for any potentially double buffered
|
||||
@ -3534,6 +3587,7 @@ static void wait_for_outstanding_hw_updates(struct dc *dc, const struct dc_state
|
||||
}
|
||||
}
|
||||
}
|
||||
wait_for_odm_update_pending_complete(dc, dc_context);
|
||||
}
|
||||
|
||||
static void commit_planes_for_stream(struct dc *dc,
|
||||
|
@ -384,6 +384,7 @@ static const struct opp_funcs dcn10_opp_funcs = {
|
||||
.opp_set_disp_pattern_generator = NULL,
|
||||
.opp_program_dpg_dimensions = NULL,
|
||||
.dpg_is_blanked = NULL,
|
||||
.dpg_is_pending = NULL,
|
||||
.opp_destroy = opp1_destroy
|
||||
};
|
||||
|
||||
|
@ -337,6 +337,19 @@ bool opp2_dpg_is_blanked(struct output_pixel_processor *opp)
|
||||
(double_buffer_pending == 0);
|
||||
}
|
||||
|
||||
bool opp2_dpg_is_pending(struct output_pixel_processor *opp)
|
||||
{
|
||||
struct dcn20_opp *oppn20 = TO_DCN20_OPP(opp);
|
||||
uint32_t double_buffer_pending;
|
||||
uint32_t dpg_en;
|
||||
|
||||
REG_GET(DPG_CONTROL, DPG_EN, &dpg_en);
|
||||
|
||||
REG_GET(DPG_STATUS, DPG_DOUBLE_BUFFER_PENDING, &double_buffer_pending);
|
||||
|
||||
return (dpg_en == 1 && double_buffer_pending == 1);
|
||||
}
|
||||
|
||||
void opp2_program_left_edge_extra_pixel (
|
||||
struct output_pixel_processor *opp,
|
||||
bool count)
|
||||
@ -363,6 +376,7 @@ static struct opp_funcs dcn20_opp_funcs = {
|
||||
.opp_set_disp_pattern_generator = opp2_set_disp_pattern_generator,
|
||||
.opp_program_dpg_dimensions = opp2_program_dpg_dimensions,
|
||||
.dpg_is_blanked = opp2_dpg_is_blanked,
|
||||
.dpg_is_pending = opp2_dpg_is_pending,
|
||||
.opp_dpg_set_blank_color = opp2_dpg_set_blank_color,
|
||||
.opp_destroy = opp1_destroy,
|
||||
.opp_program_left_edge_extra_pixel = opp2_program_left_edge_extra_pixel,
|
||||
|
@ -159,6 +159,8 @@ void opp2_program_dpg_dimensions(
|
||||
|
||||
bool opp2_dpg_is_blanked(struct output_pixel_processor *opp);
|
||||
|
||||
bool opp2_dpg_is_pending(struct output_pixel_processor *opp);
|
||||
|
||||
void opp2_dpg_set_blank_color(
|
||||
struct output_pixel_processor *opp,
|
||||
const struct tg_color *color);
|
||||
|
@ -50,6 +50,7 @@ static struct opp_funcs dcn201_opp_funcs = {
|
||||
.opp_set_disp_pattern_generator = opp2_set_disp_pattern_generator,
|
||||
.opp_program_dpg_dimensions = opp2_program_dpg_dimensions,
|
||||
.dpg_is_blanked = opp2_dpg_is_blanked,
|
||||
.dpg_is_pending = opp2_dpg_is_pending,
|
||||
.opp_dpg_set_blank_color = opp2_dpg_set_blank_color,
|
||||
.opp_destroy = opp1_destroy,
|
||||
.opp_program_left_edge_extra_pixel = opp2_program_left_edge_extra_pixel,
|
||||
|
@ -2452,7 +2452,7 @@ bool dcn20_wait_for_blank_complete(
|
||||
int counter;
|
||||
|
||||
for (counter = 0; counter < 1000; counter++) {
|
||||
if (opp->funcs->dpg_is_blanked(opp))
|
||||
if (!opp->funcs->dpg_is_pending(opp))
|
||||
break;
|
||||
|
||||
udelay(100);
|
||||
@ -2463,7 +2463,7 @@ bool dcn20_wait_for_blank_complete(
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return opp->funcs->dpg_is_blanked(opp);
|
||||
}
|
||||
|
||||
bool dcn20_dmdata_status_done(struct pipe_ctx *pipe_ctx)
|
||||
|
@ -337,6 +337,9 @@ struct opp_funcs {
|
||||
bool (*dpg_is_blanked)(
|
||||
struct output_pixel_processor *opp);
|
||||
|
||||
bool (*dpg_is_pending)(struct output_pixel_processor *opp);
|
||||
|
||||
|
||||
void (*opp_dpg_set_blank_color)(
|
||||
struct output_pixel_processor *opp,
|
||||
const struct tg_color *color);
|
||||
|
@ -331,6 +331,7 @@ struct timing_generator_funcs {
|
||||
|
||||
void (*init_odm)(struct timing_generator *tg);
|
||||
void (*wait_drr_doublebuffer_pending_clear)(struct timing_generator *tg);
|
||||
void (*wait_odm_doublebuffer_pending_clear)(struct timing_generator *tg);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -557,7 +557,8 @@ struct dcn_optc_registers {
|
||||
type OTG_CRC_DATA_STREAM_SPLIT_MODE;\
|
||||
type OTG_CRC_DATA_FORMAT;\
|
||||
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;
|
||||
|
||||
#define TG_REG_FIELD_LIST_DCN3_2(type) \
|
||||
type OTG_H_TIMING_DIV_MODE_MANUAL;
|
||||
|
@ -122,6 +122,13 @@ void optc32_get_odm_combine_segments(struct timing_generator *tg, int *odm_combi
|
||||
}
|
||||
}
|
||||
|
||||
void optc32_wait_odm_doublebuffer_pending_clear(struct timing_generator *tg)
|
||||
{
|
||||
struct optc *optc1 = DCN10TG_FROM_TG(tg);
|
||||
|
||||
REG_WAIT(OTG_DOUBLE_BUFFER_CONTROL, OTG_H_TIMING_DIV_MODE_DB_UPDATE_PENDING, 0, 2, 50000);
|
||||
}
|
||||
|
||||
void optc32_set_h_timing_div_manual_mode(struct timing_generator *optc, bool manual_mode)
|
||||
{
|
||||
struct optc *optc1 = DCN10TG_FROM_TG(optc);
|
||||
@ -345,6 +352,7 @@ static struct timing_generator_funcs dcn32_tg_funcs = {
|
||||
.set_odm_bypass = optc32_set_odm_bypass,
|
||||
.set_odm_combine = optc32_set_odm_combine,
|
||||
.get_odm_combine_segments = optc32_get_odm_combine_segments,
|
||||
.wait_odm_doublebuffer_pending_clear = optc32_wait_odm_doublebuffer_pending_clear,
|
||||
.set_h_timing_div_manual_mode = optc32_set_h_timing_div_manual_mode,
|
||||
.get_optc_source = optc2_get_optc_source,
|
||||
.set_out_mux = optc3_set_out_mux,
|
||||
|
@ -183,5 +183,6 @@ void optc32_set_h_timing_div_manual_mode(struct timing_generator *optc, bool man
|
||||
void optc32_get_odm_combine_segments(struct timing_generator *tg, int *odm_combine_segments);
|
||||
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);
|
||||
|
||||
#endif /* __DC_OPTC_DCN32_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user