drm/amd/display: Add odm seamless boot support
[WHY] Implement changes to transition from Pre-OS odm to Post-OS odm support. Seamless boot case is also considered. [HOW] Revised validation logic when marking for seamless boot. Init resources accordingly when Pre-OS has odm enabled. Reset odm and det size when transitioning Pre-OS odm to Post-OS non-odm to avoid corruption. Apply logic to set odm accordingly upon commit. Reviewed-by: Nicholas Kazlauskas <Nicholas.Kazlauskas@amd.com> Acked-by: Pavle Kotarac <Pavle.Kotarac@amd.com> Signed-off-by: Duncan Ma <Duncan.Ma@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
56b5b1c7b3
commit
663d2daeae
8 changed files with 112 additions and 25 deletions
|
@ -1569,11 +1569,24 @@ bool dc_validate_boot_timing(const struct dc *dc,
|
||||||
|
|
||||||
if (dc_is_dp_signal(link->connector_signal)) {
|
if (dc_is_dp_signal(link->connector_signal)) {
|
||||||
unsigned int pix_clk_100hz;
|
unsigned int pix_clk_100hz;
|
||||||
|
uint32_t numOdmPipes = 1;
|
||||||
|
uint32_t id_src[4] = {0};
|
||||||
|
|
||||||
dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz(
|
dc->res_pool->dp_clock_source->funcs->get_pixel_clk_frequency_100hz(
|
||||||
dc->res_pool->dp_clock_source,
|
dc->res_pool->dp_clock_source,
|
||||||
tg_inst, &pix_clk_100hz);
|
tg_inst, &pix_clk_100hz);
|
||||||
|
|
||||||
|
if (tg->funcs->get_optc_source)
|
||||||
|
tg->funcs->get_optc_source(tg,
|
||||||
|
&numOdmPipes, &id_src[0], &id_src[1]);
|
||||||
|
|
||||||
|
if (numOdmPipes == 2)
|
||||||
|
pix_clk_100hz *= 2;
|
||||||
|
if (numOdmPipes == 4)
|
||||||
|
pix_clk_100hz *= 4;
|
||||||
|
|
||||||
|
// Note: In rare cases, HW pixclk may differ from crtc's pixclk
|
||||||
|
// slightly due to rounding issues in 10 kHz units.
|
||||||
if (crtc_timing->pix_clk_100hz != pix_clk_100hz)
|
if (crtc_timing->pix_clk_100hz != pix_clk_100hz)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -2120,6 +2120,8 @@ static int acquire_resource_from_hw_enabled_state(
|
||||||
{
|
{
|
||||||
struct dc_link *link = stream->link;
|
struct dc_link *link = stream->link;
|
||||||
unsigned int i, inst, tg_inst = 0;
|
unsigned int i, inst, tg_inst = 0;
|
||||||
|
uint32_t numPipes = 1;
|
||||||
|
uint32_t id_src[4] = {0};
|
||||||
|
|
||||||
/* Check for enabled DIG to identify enabled display */
|
/* Check for enabled DIG to identify enabled display */
|
||||||
if (!link->link_enc->funcs->is_dig_enabled(link->link_enc))
|
if (!link->link_enc->funcs->is_dig_enabled(link->link_enc))
|
||||||
|
@ -2148,38 +2150,62 @@ static int acquire_resource_from_hw_enabled_state(
|
||||||
if (!res_ctx->pipe_ctx[tg_inst].stream) {
|
if (!res_ctx->pipe_ctx[tg_inst].stream) {
|
||||||
struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[tg_inst];
|
struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[tg_inst];
|
||||||
|
|
||||||
|
id_src[0] = tg_inst;
|
||||||
|
|
||||||
|
if (pipe_ctx->stream_res.tg->funcs->get_optc_source)
|
||||||
|
pipe_ctx->stream_res.tg->funcs->get_optc_source(pipe_ctx->stream_res.tg,
|
||||||
|
&numPipes, &id_src[0], &id_src[1]);
|
||||||
|
|
||||||
|
for (i = 0; i < numPipes; i++) {
|
||||||
|
//Check if src id invalid
|
||||||
|
if (id_src[i] == 0xf)
|
||||||
|
return -1;
|
||||||
|
|
||||||
pipe_ctx->stream_res.tg = pool->timing_generators[tg_inst];
|
pipe_ctx->stream_res.tg = pool->timing_generators[tg_inst];
|
||||||
pipe_ctx->plane_res.mi = pool->mis[tg_inst];
|
pipe_ctx->plane_res.mi = pool->mis[id_src[i]];
|
||||||
pipe_ctx->plane_res.hubp = pool->hubps[tg_inst];
|
pipe_ctx->plane_res.hubp = pool->hubps[id_src[i]];
|
||||||
pipe_ctx->plane_res.ipp = pool->ipps[tg_inst];
|
pipe_ctx->plane_res.ipp = pool->ipps[id_src[i]];
|
||||||
pipe_ctx->plane_res.xfm = pool->transforms[tg_inst];
|
pipe_ctx->plane_res.xfm = pool->transforms[id_src[i]];
|
||||||
pipe_ctx->plane_res.dpp = pool->dpps[tg_inst];
|
pipe_ctx->plane_res.dpp = pool->dpps[id_src[i]];
|
||||||
pipe_ctx->stream_res.opp = pool->opps[tg_inst];
|
pipe_ctx->stream_res.opp = pool->opps[id_src[i]];
|
||||||
|
|
||||||
if (pool->dpps[tg_inst]) {
|
if (pool->dpps[id_src[i]]) {
|
||||||
pipe_ctx->plane_res.mpcc_inst = pool->dpps[tg_inst]->inst;
|
pipe_ctx->plane_res.mpcc_inst = pool->dpps[id_src[i]]->inst;
|
||||||
|
|
||||||
// Read DPP->MPCC->OPP Pipe from HW State
|
|
||||||
if (pool->mpc->funcs->read_mpcc_state) {
|
if (pool->mpc->funcs->read_mpcc_state) {
|
||||||
struct mpcc_state s = {0};
|
struct mpcc_state s = {0};
|
||||||
|
|
||||||
pool->mpc->funcs->read_mpcc_state(pool->mpc, pipe_ctx->plane_res.mpcc_inst, &s);
|
pool->mpc->funcs->read_mpcc_state(pool->mpc, pipe_ctx->plane_res.mpcc_inst, &s);
|
||||||
|
|
||||||
if (s.dpp_id < MAX_MPCC)
|
if (s.dpp_id < MAX_MPCC)
|
||||||
pool->mpc->mpcc_array[pipe_ctx->plane_res.mpcc_inst].dpp_id = s.dpp_id;
|
pool->mpc->mpcc_array[pipe_ctx->plane_res.mpcc_inst].dpp_id =
|
||||||
|
s.dpp_id;
|
||||||
if (s.bot_mpcc_id < MAX_MPCC)
|
if (s.bot_mpcc_id < MAX_MPCC)
|
||||||
pool->mpc->mpcc_array[pipe_ctx->plane_res.mpcc_inst].mpcc_bot =
|
pool->mpc->mpcc_array[pipe_ctx->plane_res.mpcc_inst].mpcc_bot =
|
||||||
&pool->mpc->mpcc_array[s.bot_mpcc_id];
|
&pool->mpc->mpcc_array[s.bot_mpcc_id];
|
||||||
|
|
||||||
if (s.opp_id < MAX_OPP)
|
if (s.opp_id < MAX_OPP)
|
||||||
pipe_ctx->stream_res.opp->mpc_tree_params.opp_id = s.opp_id;
|
pipe_ctx->stream_res.opp->mpc_tree_params.opp_id = s.opp_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pipe_ctx->pipe_idx = tg_inst;
|
pipe_ctx->pipe_idx = id_src[i];
|
||||||
|
|
||||||
|
if (id_src[i] >= pool->timing_generator_count) {
|
||||||
|
id_src[i] = pool->timing_generator_count - 1;
|
||||||
|
pipe_ctx->stream_res.tg = pool->timing_generators[id_src[i]];
|
||||||
|
pipe_ctx->stream_res.opp = pool->opps[id_src[i]];
|
||||||
|
}
|
||||||
|
|
||||||
pipe_ctx->stream = stream;
|
pipe_ctx->stream = stream;
|
||||||
return tg_inst;
|
}
|
||||||
|
|
||||||
|
if (numPipes == 2) {
|
||||||
|
stream->apply_boot_odm_mode = dm_odm_combine_policy_2to1;
|
||||||
|
res_ctx->pipe_ctx[id_src[0]].next_odm_pipe = &res_ctx->pipe_ctx[id_src[1]];
|
||||||
|
res_ctx->pipe_ctx[id_src[0]].prev_odm_pipe = NULL;
|
||||||
|
res_ctx->pipe_ctx[id_src[1]].next_odm_pipe = NULL;
|
||||||
|
res_ctx->pipe_ctx[id_src[1]].prev_odm_pipe = &res_ctx->pipe_ctx[id_src[0]];
|
||||||
|
} else
|
||||||
|
stream->apply_boot_odm_mode = dm_odm_combine_mode_disabled;
|
||||||
|
|
||||||
|
return id_src[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -666,6 +666,7 @@ struct dc_debug_options {
|
||||||
uint32_t edid_read_retry_times;
|
uint32_t edid_read_retry_times;
|
||||||
bool remove_disconnect_edp;
|
bool remove_disconnect_edp;
|
||||||
unsigned int force_odm_combine; //bit vector based on otg inst
|
unsigned int force_odm_combine; //bit vector based on otg inst
|
||||||
|
unsigned int seamless_boot_odm_combine;
|
||||||
#if defined(CONFIG_DRM_AMD_DC_DCN)
|
#if defined(CONFIG_DRM_AMD_DC_DCN)
|
||||||
unsigned int force_odm_combine_4to1; //bit vector based on otg inst
|
unsigned int force_odm_combine_4to1; //bit vector based on otg inst
|
||||||
bool disable_z9_mpc;
|
bool disable_z9_mpc;
|
||||||
|
|
|
@ -246,6 +246,7 @@ struct dc_stream_state {
|
||||||
|
|
||||||
bool apply_edp_fast_boot_optimization;
|
bool apply_edp_fast_boot_optimization;
|
||||||
bool apply_seamless_boot_optimization;
|
bool apply_seamless_boot_optimization;
|
||||||
|
uint32_t apply_boot_odm_mode;
|
||||||
|
|
||||||
uint32_t stream_id;
|
uint32_t stream_id;
|
||||||
|
|
||||||
|
|
|
@ -1259,6 +1259,7 @@ void dcn10_init_pipes(struct dc *dc, struct dc_state *context)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct dce_hwseq *hws = dc->hwseq;
|
struct dce_hwseq *hws = dc->hwseq;
|
||||||
|
struct hubbub *hubbub = dc->res_pool->hubbub;
|
||||||
bool can_apply_seamless_boot = false;
|
bool can_apply_seamless_boot = false;
|
||||||
|
|
||||||
for (i = 0; i < context->stream_count; i++) {
|
for (i = 0; i < context->stream_count; i++) {
|
||||||
|
@ -1294,6 +1295,21 @@ void dcn10_init_pipes(struct dc *dc, struct dc_state *context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reset det size */
|
||||||
|
for (i = 0; i < dc->res_pool->pipe_count; i++) {
|
||||||
|
struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
|
||||||
|
struct hubp *hubp = dc->res_pool->hubps[i];
|
||||||
|
|
||||||
|
/* Do not need to reset for seamless boot */
|
||||||
|
if (pipe_ctx->stream != NULL && can_apply_seamless_boot)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (hubbub && hubp) {
|
||||||
|
if (hubbub->funcs->program_det_size)
|
||||||
|
hubbub->funcs->program_det_size(hubbub, hubp->inst, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* num_opp will be equal to number of mpcc */
|
/* num_opp will be equal to number of mpcc */
|
||||||
for (i = 0; i < dc->res_pool->res_cap->num_opp; i++) {
|
for (i = 0; i < dc->res_pool->res_cap->num_opp; i++) {
|
||||||
struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
|
struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i];
|
||||||
|
@ -1359,6 +1375,11 @@ void dcn10_init_pipes(struct dc *dc, struct dc_state *context)
|
||||||
pipe_ctx->stream_res.tg = NULL;
|
pipe_ctx->stream_res.tg = NULL;
|
||||||
pipe_ctx->plane_res.hubp = NULL;
|
pipe_ctx->plane_res.hubp = NULL;
|
||||||
|
|
||||||
|
if (tg->funcs->is_tg_enabled(tg)) {
|
||||||
|
if (tg->funcs->init_odm)
|
||||||
|
tg->funcs->init_odm(tg);
|
||||||
|
}
|
||||||
|
|
||||||
tg->funcs->tg_init(tg);
|
tg->funcs->tg_init(tg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,26 @@ void optc31_set_drr(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void optc3_init_odm(struct timing_generator *optc)
|
||||||
|
{
|
||||||
|
struct optc *optc1 = DCN10TG_FROM_TG(optc);
|
||||||
|
|
||||||
|
REG_SET_5(OPTC_DATA_SOURCE_SELECT, 0,
|
||||||
|
OPTC_NUM_OF_INPUT_SEGMENT, 0,
|
||||||
|
OPTC_SEG0_SRC_SEL, optc->inst,
|
||||||
|
OPTC_SEG1_SRC_SEL, 0xf,
|
||||||
|
OPTC_SEG2_SRC_SEL, 0xf,
|
||||||
|
OPTC_SEG3_SRC_SEL, 0xf
|
||||||
|
);
|
||||||
|
|
||||||
|
REG_SET(OTG_H_TIMING_CNTL, 0,
|
||||||
|
OTG_H_TIMING_DIV_MODE, 0);
|
||||||
|
|
||||||
|
REG_SET(OPTC_MEMORY_CONFIG, 0,
|
||||||
|
OPTC_MEM_SEL, 0);
|
||||||
|
optc1->opp_count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
static struct timing_generator_funcs dcn31_tg_funcs = {
|
static struct timing_generator_funcs dcn31_tg_funcs = {
|
||||||
.validate_timing = optc1_validate_timing,
|
.validate_timing = optc1_validate_timing,
|
||||||
.program_timing = optc1_program_timing,
|
.program_timing = optc1_program_timing,
|
||||||
|
@ -272,6 +292,7 @@ static struct timing_generator_funcs dcn31_tg_funcs = {
|
||||||
.program_manual_trigger = optc2_program_manual_trigger,
|
.program_manual_trigger = optc2_program_manual_trigger,
|
||||||
.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,
|
||||||
|
.init_odm = optc3_init_odm,
|
||||||
};
|
};
|
||||||
|
|
||||||
void dcn31_timing_generator_init(struct optc *optc1)
|
void dcn31_timing_generator_init(struct optc *optc1)
|
||||||
|
|
|
@ -258,4 +258,6 @@ void dcn31_timing_generator_init(struct optc *optc1);
|
||||||
|
|
||||||
void optc31_set_drr(struct timing_generator *optc, const struct drr_params *params);
|
void optc31_set_drr(struct timing_generator *optc, const struct drr_params *params);
|
||||||
|
|
||||||
|
void optc3_init_odm(struct timing_generator *optc);
|
||||||
|
|
||||||
#endif /* __DC_OPTC_DCN31_H__ */
|
#endif /* __DC_OPTC_DCN31_H__ */
|
||||||
|
|
|
@ -310,6 +310,8 @@ struct timing_generator_funcs {
|
||||||
uint32_t slave_pixel_clock_100Hz,
|
uint32_t slave_pixel_clock_100Hz,
|
||||||
uint8_t master_clock_divider,
|
uint8_t master_clock_divider,
|
||||||
uint8_t slave_clock_divider);
|
uint8_t slave_clock_divider);
|
||||||
|
|
||||||
|
void (*init_odm)(struct timing_generator *tg);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue