Commit 3c915431 authored by Wenjing Liu's avatar Wenjing Liu Committed by Alex Deucher

drm/amd/display: fix dscclk programming sequence on DCN401

[why]
The mux to switch between refclk and dto_dsc_clk is non double buffered.
However dto dsc clk's phase and modulo divider registers are currently
configured as double buffered update. This causes a problem when we switch to
use dto dsc clk and program phase and modulo in the same sequence. In this
sequence dsc clk is switched to dto but the clock divider programming doesn't
take effect until next frame. When we try to program DSCC registers, SMN bus
will hang because dto dsc clk divider phase is set to 0.

[how]
Configure phase and modulo to take effect immediately. Always switch to dto dsc
clk before DSC clock is unagted. Switch back to refclk after DSC clock is gated.
Acked-by: default avatarRodrigo Siqueira <rodrigo.siqueira@amd.com>
Reviewed-by: default avatarJerry Zuo <jerry.zuo@amd.com>
Signed-off-by: default avatarWenjing Liu <wenjing.liu@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent bc2fe69f
......@@ -346,11 +346,7 @@
type SYMCLK32_LE3_SRC_SEL;\
type SYMCLK32_LE2_EN;\
type SYMCLK32_LE3_EN;\
type DP_DTO_ENABLE[MAX_PIPES];\
type DSCCLK0_DTO_DB_EN;\
type DSCCLK1_DTO_DB_EN;\
type DSCCLK2_DTO_DB_EN;\
type DSCCLK3_DTO_DB_EN;
type DP_DTO_ENABLE[MAX_PIPES];
struct dccg_shift {
DCCG_REG_FIELD_LIST(uint8_t)
......
......@@ -730,35 +730,35 @@ void dccg401_init(struct dccg *dccg)
}
}
static void dccg401_set_dto_dscclk(struct dccg *dccg, uint32_t inst, bool enable)
static void dccg401_set_dto_dscclk(struct dccg *dccg, uint32_t inst)
{
struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
uint32_t phase = enable ? 1 : 0;
switch (inst) {
case 0:
REG_UPDATE_2(DSCCLK_DTO_CTRL, DSCCLK0_EN, 1, DSCCLK0_DTO_DB_EN, 1);
REG_UPDATE_2(DSCCLK0_DTO_PARAM,
DSCCLK0_DTO_PHASE, phase,
DSCCLK0_DTO_PHASE, 1,
DSCCLK0_DTO_MODULO, 1);
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK0_EN, 1);
break;
case 1:
REG_UPDATE_2(DSCCLK_DTO_CTRL, DSCCLK1_EN, 1, DSCCLK1_DTO_DB_EN, 1);
REG_UPDATE_2(DSCCLK1_DTO_PARAM,
DSCCLK1_DTO_PHASE, phase,
DSCCLK1_DTO_PHASE, 1,
DSCCLK1_DTO_MODULO, 1);
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK1_EN, 1);
break;
case 2:
REG_UPDATE_2(DSCCLK_DTO_CTRL, DSCCLK2_EN, 1, DSCCLK2_DTO_DB_EN, 1);
REG_UPDATE_2(DSCCLK2_DTO_PARAM,
DSCCLK2_DTO_PHASE, phase,
DSCCLK2_DTO_PHASE, 1,
DSCCLK2_DTO_MODULO, 1);
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK2_EN, 1);
break;
case 3:
REG_UPDATE_2(DSCCLK_DTO_CTRL, DSCCLK3_EN, 1, DSCCLK3_DTO_DB_EN, 1);
REG_UPDATE_2(DSCCLK3_DTO_PARAM,
DSCCLK3_DTO_PHASE, phase,
DSCCLK3_DTO_PHASE, 1,
DSCCLK3_DTO_MODULO, 1);
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK3_EN, 1);
break;
default:
BREAK_TO_DEBUGGER();
......@@ -774,15 +774,27 @@ static void dccg401_set_ref_dscclk(struct dccg *dccg,
switch (dsc_inst) {
case 0:
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK0_EN, 0);
REG_UPDATE_2(DSCCLK0_DTO_PARAM,
DSCCLK0_DTO_PHASE, 0,
DSCCLK0_DTO_MODULO, 0);
break;
case 1:
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK1_EN, 0);
REG_UPDATE_2(DSCCLK1_DTO_PARAM,
DSCCLK1_DTO_PHASE, 0,
DSCCLK1_DTO_MODULO, 0);
break;
case 2:
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK2_EN, 0);
REG_UPDATE_2(DSCCLK2_DTO_PARAM,
DSCCLK2_DTO_PHASE, 0,
DSCCLK2_DTO_MODULO, 0);
break;
case 3:
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK3_EN, 0);
REG_UPDATE_2(DSCCLK3_DTO_PARAM,
DSCCLK3_DTO_PHASE, 0,
DSCCLK3_DTO_MODULO, 0);
break;
default:
return;
......
......@@ -117,10 +117,6 @@
DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK1_EN, mask_sh),\
DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK2_EN, mask_sh),\
DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK3_EN, mask_sh),\
DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK0_DTO_DB_EN, mask_sh),\
DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK1_DTO_DB_EN, mask_sh),\
DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK2_DTO_DB_EN, mask_sh),\
DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK3_DTO_DB_EN, mask_sh),\
DCCG_SF(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_PHASE, mask_sh),\
DCCG_SF(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_MODULO, mask_sh),\
DCCG_SF(DSCCLK1_DTO_PARAM, DSCCLK1_DTO_PHASE, mask_sh),\
......
......@@ -2186,9 +2186,9 @@ static void post_unlock_reset_opp(struct dc *dc,
* yet power gated.
*/
dsc->funcs->dsc_wait_disconnect_pending_clear(dsc);
dsc->funcs->dsc_disable(dsc);
if (dccg->funcs->set_ref_dscclk)
dccg->funcs->set_ref_dscclk(dccg, dsc->inst);
dsc->funcs->dsc_disable(dsc);
}
}
}
......
......@@ -1029,24 +1029,20 @@ void dcn32_update_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % opp_cnt == 0);
dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt;
if (should_use_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, dsc->inst);
dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg);
dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst);
if (should_use_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, dsc->inst, true);
for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
struct display_stream_compressor *odm_dsc = odm_pipe->stream_res.dsc;
ASSERT(odm_dsc);
if (should_use_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, odm_dsc->inst);
odm_dsc->funcs->dsc_set_config(odm_dsc, &dsc_cfg, &dsc_optc_cfg);
odm_dsc->funcs->dsc_enable(odm_dsc, odm_pipe->stream_res.opp->inst);
if (should_use_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, odm_dsc->inst, true);
}
dsc_cfg.dc_dsc_cfg.num_slices_h *= opp_cnt;
dsc_cfg.pic_width *= opp_cnt;
optc_dsc_mode = dsc_optc_cfg.is_pixel_format_444 ? OPTC_DSC_ENABLED_444 : OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED;
/* Enable DSC in OPTC */
DC_LOG_DSC("Setting optc DSC config for tg instance %d:", pipe_ctx->stream_res.tg->inst);
pipe_ctx->stream_res.tg->funcs->set_dsc_config(pipe_ctx->stream_res.tg,
......@@ -1060,13 +1056,9 @@ void dcn32_update_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
OPTC_DSC_DISABLED, 0, 0);
/* only disconnect DSC block, DSC is disabled when OPP head pipe is reset */
if (dccg->funcs->set_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, pipe_ctx->stream_res.dsc->inst, false);
dsc->funcs->dsc_disable(pipe_ctx->stream_res.dsc);
dsc->funcs->dsc_disconnect(pipe_ctx->stream_res.dsc);
for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
ASSERT(odm_pipe->stream_res.dsc);
if (dccg->funcs->set_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, odm_pipe->stream_res.dsc->inst, false);
odm_pipe->stream_res.dsc->funcs->dsc_disconnect(odm_pipe->stream_res.dsc);
}
}
......@@ -1137,10 +1129,7 @@ void dcn32_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *
if (!pipe_ctx->next_odm_pipe && current_pipe_ctx->next_odm_pipe &&
current_pipe_ctx->next_odm_pipe->stream_res.dsc) {
struct display_stream_compressor *dsc = current_pipe_ctx->next_odm_pipe->stream_res.dsc;
struct dccg *dccg = dc->res_pool->dccg;
if (dccg->funcs->set_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, dsc->inst, false);
/* disconnect DSC block from stream */
dsc->funcs->dsc_disconnect(dsc);
}
......
......@@ -1542,7 +1542,6 @@ static void update_dsc_for_odm_change(struct dc *dc, struct dc_state *context,
struct pipe_ctx *old_pipe;
struct pipe_ctx *new_pipe;
struct pipe_ctx *old_opp_heads[MAX_PIPES];
struct dccg *dccg = dc->res_pool->dccg;
struct pipe_ctx *old_otg_master;
int old_opp_head_count = 0;
......@@ -1568,12 +1567,9 @@ static void update_dsc_for_odm_change(struct dc *dc, struct dc_state *context,
for (i = 0; i < old_opp_head_count; i++) {
old_pipe = old_opp_heads[i];
new_pipe = &context->res_ctx.pipe_ctx[old_pipe->pipe_idx];
if (old_pipe->stream_res.dsc && !new_pipe->stream_res.dsc) {
dccg->funcs->set_dto_dscclk(dccg,
old_pipe->stream_res.dsc->inst, false);
if (old_pipe->stream_res.dsc && !new_pipe->stream_res.dsc)
old_pipe->stream_res.dsc->funcs->dsc_disconnect(
old_pipe->stream_res.dsc);
}
}
}
}
......
......@@ -211,10 +211,7 @@ struct dccg_funcs {
struct dccg *dccg,
enum streamclk_source src,
uint32_t otg_inst);
void (*set_dto_dscclk)(
struct dccg *dccg,
uint32_t dsc_inst,
bool enable);
void (*set_dto_dscclk)(struct dccg *dccg, uint32_t dsc_inst);
void (*set_ref_dscclk)(struct dccg *dccg, uint32_t dsc_inst);
};
......
......@@ -817,17 +817,17 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % opp_cnt == 0);
dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt;
if (should_use_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, dsc->inst);
dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg);
dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst);
if (should_use_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, dsc->inst, true);
for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
struct display_stream_compressor *odm_dsc = odm_pipe->stream_res.dsc;
if (should_use_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, odm_dsc->inst);
odm_dsc->funcs->dsc_set_config(odm_dsc, &dsc_cfg, &dsc_optc_cfg);
odm_dsc->funcs->dsc_enable(odm_dsc, odm_pipe->stream_res.opp->inst);
if (should_use_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, odm_dsc->inst, true);
}
dsc_cfg.dc_dsc_cfg.num_slices_h *= opp_cnt;
dsc_cfg.pic_width *= opp_cnt;
......@@ -879,19 +879,32 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
}
/* disable DSC block */
if (dccg->funcs->set_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, pipe_ctx->stream_res.dsc->inst, false);
pipe_ctx->stream_res.dsc->funcs->dsc_disconnect(pipe_ctx->stream_res.dsc);
if (dccg->funcs->set_ref_dscclk)
dccg->funcs->set_ref_dscclk(dccg, pipe_ctx->stream_res.dsc->inst);
pipe_ctx->stream_res.dsc->funcs->dsc_disable(pipe_ctx->stream_res.dsc);
for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
if (dccg->funcs->set_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, odm_pipe->stream_res.dsc->inst, false);
for (odm_pipe = pipe_ctx; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
odm_pipe->stream_res.dsc->funcs->dsc_disconnect(odm_pipe->stream_res.dsc);
/*
* TODO - dsc_disconnect is a double buffered register.
* by the time we call dsc_disable, dsc may still remain
* connected to OPP. In this case OPTC will no longer
* get correct pixel data because DSCC is off. However
* we also can't wait for the disconnect pending
* complete, because this function can be called
* with/without OTG master lock acquired. When the lock
* is acquired we will never get pending complete until
* we release the lock later. So there is no easy way to
* solve this problem especially when the lock is
* acquired. DSC is a front end hw block it should be
* programmed as part of front end sequence, where the
* commit sequence without lock and update sequence
* with lock are completely separated. However because
* we are programming dsc as part of back end link
* programming sequence, we don't know if front end OPTC
* master lock is acquired. The back end should be
* agnostic to front end lock. DSC programming shouldn't
* belong to this sequence.
*/
odm_pipe->stream_res.dsc->funcs->dsc_disable(odm_pipe->stream_res.dsc);
if (dccg->funcs->set_ref_dscclk)
dccg->funcs->set_ref_dscclk(dccg, odm_pipe->stream_res.dsc->inst);
odm_pipe->stream_res.dsc->funcs->dsc_disable(odm_pipe->stream_res.dsc);
}
}
}
......
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