Skip to content

Commit da2d16f

Browse files
Nicholas Kazlauskasalexdeucher
authored andcommitted
drm/amd/display: Fix IPS handshake for idle optimizations
[Why] Intermittent reboot hangs are observed introduced by "Improve x86 and dmub ips handshake". [How] Bring back the commit but fix the polling. Avoid hanging in place forever by bounding the delay and ensure that we still message DMCUB on IPS2 exit to notify driver idle has been cleared. Reviewed-by: Duncan Ma <duncan.ma@amd.com> Reviewed-by: Jun Lei <jun.lei@amd.com> Acked-by: Roman Li <roman.li@amd.com> Signed-off-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
1 parent 488bb99 commit da2d16f

10 files changed

Lines changed: 141 additions & 27 deletions

File tree

drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,34 @@ static void dcn35_set_low_power_state(struct clk_mgr *clk_mgr_base)
808808
}
809809
}
810810

811+
static void dcn35_set_idle_state(struct clk_mgr *clk_mgr_base, bool allow_idle)
812+
{
813+
struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
814+
struct dc *dc = clk_mgr_base->ctx->dc;
815+
uint32_t val = dcn35_smu_read_ips_scratch(clk_mgr);
816+
817+
if (dc->config.disable_ips == 0) {
818+
val |= DMUB_IPS1_ALLOW_MASK;
819+
val |= DMUB_IPS2_ALLOW_MASK;
820+
} else if (dc->config.disable_ips == DMUB_IPS_DISABLE_IPS1) {
821+
val = val & ~DMUB_IPS1_ALLOW_MASK;
822+
val = val & ~DMUB_IPS2_ALLOW_MASK;
823+
} else if (dc->config.disable_ips == DMUB_IPS_DISABLE_IPS2) {
824+
val |= DMUB_IPS1_ALLOW_MASK;
825+
val = val & ~DMUB_IPS2_ALLOW_MASK;
826+
} else if (dc->config.disable_ips == DMUB_IPS_DISABLE_IPS2_Z10) {
827+
val |= DMUB_IPS1_ALLOW_MASK;
828+
val |= DMUB_IPS2_ALLOW_MASK;
829+
}
830+
831+
if (!allow_idle) {
832+
val = val & ~DMUB_IPS1_ALLOW_MASK;
833+
val = val & ~DMUB_IPS2_ALLOW_MASK;
834+
}
835+
836+
dcn35_smu_write_ips_scratch(clk_mgr, val);
837+
}
838+
811839
static void dcn35_exit_low_power_state(struct clk_mgr *clk_mgr_base)
812840
{
813841
struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
@@ -827,6 +855,13 @@ static bool dcn35_is_ips_supported(struct clk_mgr *clk_mgr_base)
827855
return ips_supported;
828856
}
829857

858+
static uint32_t dcn35_get_idle_state(struct clk_mgr *clk_mgr_base)
859+
{
860+
struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
861+
862+
return dcn35_smu_read_ips_scratch(clk_mgr);
863+
}
864+
830865
static void dcn35_init_clocks_fpga(struct clk_mgr *clk_mgr)
831866
{
832867
dcn35_init_clocks(clk_mgr);
@@ -914,6 +949,8 @@ static struct clk_mgr_funcs dcn35_funcs = {
914949
.set_low_power_state = dcn35_set_low_power_state,
915950
.exit_low_power_state = dcn35_exit_low_power_state,
916951
.is_ips_supported = dcn35_is_ips_supported,
952+
.set_idle_state = dcn35_set_idle_state,
953+
.get_idle_state = dcn35_get_idle_state
917954
};
918955

919956
struct clk_mgr_funcs dcn35_fpga_funcs = {

drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_smu.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,9 +444,9 @@ void dcn35_vbios_smu_enable_48mhz_tmdp_refclk_pwrdwn(struct clk_mgr_internal *cl
444444
enable);
445445
}
446446

447-
void dcn35_smu_exit_low_power_state(struct clk_mgr_internal *clk_mgr)
447+
int dcn35_smu_exit_low_power_state(struct clk_mgr_internal *clk_mgr)
448448
{
449-
dcn35_smu_send_msg_with_param(
449+
return dcn35_smu_send_msg_with_param(
450450
clk_mgr,
451451
VBIOSSMC_MSG_DispPsrExit,
452452
0);
@@ -459,3 +459,13 @@ int dcn35_smu_get_ips_supported(struct clk_mgr_internal *clk_mgr)
459459
VBIOSSMC_MSG_QueryIPS2Support,
460460
0);
461461
}
462+
463+
void dcn35_smu_write_ips_scratch(struct clk_mgr_internal *clk_mgr, uint32_t param)
464+
{
465+
REG_WRITE(MP1_SMN_C2PMSG_71, param);
466+
}
467+
468+
uint32_t dcn35_smu_read_ips_scratch(struct clk_mgr_internal *clk_mgr)
469+
{
470+
return REG_READ(MP1_SMN_C2PMSG_71);
471+
}

drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_smu.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,10 @@ void dcn35_smu_set_zstate_support(struct clk_mgr_internal *clk_mgr, enum dcn_zst
194194
void dcn35_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable);
195195
void dcn35_vbios_smu_enable_48mhz_tmdp_refclk_pwrdwn(struct clk_mgr_internal *clk_mgr, bool enable);
196196

197-
void dcn35_smu_exit_low_power_state(struct clk_mgr_internal *clk_mgr);
197+
int dcn35_smu_exit_low_power_state(struct clk_mgr_internal *clk_mgr);
198198
int dcn35_smu_get_ips_supported(struct clk_mgr_internal *clk_mgr);
199199
int dcn35_smu_get_dtbclk(struct clk_mgr_internal *clk_mgr);
200200
int dcn35_smu_get_dprefclk(struct clk_mgr_internal *clk_mgr);
201+
void dcn35_smu_write_ips_scratch(struct clk_mgr_internal *clk_mgr, uint32_t param);
202+
uint32_t dcn35_smu_read_ips_scratch(struct clk_mgr_internal *clk_mgr);
201203
#endif /* DAL_DC_35_SMU_H_ */

drivers/gpu/drm/amd/display/dc/dc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,8 @@ struct dc_debug_options {
975975
bool replay_skip_crtc_disabled;
976976
bool ignore_pg;/*do nothing, let pmfw control it*/
977977
bool psp_disabled_wa;
978+
unsigned int ips2_eval_delay_us;
979+
unsigned int ips2_entry_delay_us;
978980
};
979981

980982
struct gpu_info_soc_bounding_box_v1_0;

drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,31 +1100,80 @@ void dc_dmub_srv_notify_idle(const struct dc *dc, bool allow_idle)
11001100

11011101
cmd.idle_opt_notify_idle.cntl_data.driver_idle = allow_idle;
11021102

1103-
dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
1103+
if (allow_idle) {
1104+
if (dc->hwss.set_idle_state)
1105+
dc->hwss.set_idle_state(dc, true);
1106+
}
11041107

1105-
if (allow_idle)
1106-
udelay(500);
1108+
dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
11071109
}
11081110

11091111
void dc_dmub_srv_exit_low_power_state(const struct dc *dc)
11101112
{
1113+
const uint32_t max_num_polls = 10000;
1114+
uint32_t allow_state = 0;
1115+
uint32_t commit_state = 0;
1116+
uint32_t i;
1117+
11111118
if (dc->debug.dmcub_emulation)
11121119
return;
11131120

11141121
if (!dc->idle_optimizations_allowed)
11151122
return;
11161123

1117-
// Tell PMFW to exit low power state
1118-
if (dc->clk_mgr->funcs->exit_low_power_state)
1119-
dc->clk_mgr->funcs->exit_low_power_state(dc->clk_mgr);
1124+
if (dc->hwss.get_idle_state &&
1125+
dc->hwss.set_idle_state &&
1126+
dc->clk_mgr->funcs->exit_low_power_state) {
1127+
1128+
allow_state = dc->hwss.get_idle_state(dc);
1129+
dc->hwss.set_idle_state(dc, false);
1130+
1131+
if (allow_state & DMUB_IPS2_ALLOW_MASK) {
1132+
// Wait for evaluation time
1133+
udelay(dc->debug.ips2_eval_delay_us);
1134+
commit_state = dc->hwss.get_idle_state(dc);
1135+
if (commit_state & DMUB_IPS2_COMMIT_MASK) {
1136+
// Tell PMFW to exit low power state
1137+
dc->clk_mgr->funcs->exit_low_power_state(dc->clk_mgr);
1138+
1139+
// Wait for IPS2 entry upper bound
1140+
udelay(dc->debug.ips2_entry_delay_us);
1141+
dc->clk_mgr->funcs->exit_low_power_state(dc->clk_mgr);
1142+
1143+
for (i = 0; i < max_num_polls; ++i) {
1144+
commit_state = dc->hwss.get_idle_state(dc);
1145+
if (!(commit_state & DMUB_IPS2_COMMIT_MASK))
1146+
break;
1147+
1148+
udelay(1);
1149+
}
1150+
ASSERT(i < max_num_polls);
1151+
1152+
if (!dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true))
1153+
ASSERT(0);
1154+
1155+
/* TODO: See if we can return early here - IPS2 should go
1156+
* back directly to IPS0 and clear the flags, but it will
1157+
* be safer to directly notify DMCUB of this.
1158+
*/
1159+
allow_state = dc->hwss.get_idle_state(dc);
1160+
}
1161+
}
11201162

1121-
// Wait for dmcub to load up
1122-
dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true);
1163+
dc_dmub_srv_notify_idle(dc, false);
1164+
if (allow_state & DMUB_IPS1_ALLOW_MASK) {
1165+
for (i = 0; i < max_num_polls; ++i) {
1166+
commit_state = dc->hwss.get_idle_state(dc);
1167+
if (!(commit_state & DMUB_IPS1_COMMIT_MASK))
1168+
break;
11231169

1124-
// Notify dmcub disallow idle
1125-
dc_dmub_srv_notify_idle(dc, false);
1170+
udelay(1);
1171+
}
1172+
ASSERT(i < max_num_polls);
1173+
}
1174+
}
11261175

1127-
// Confirm dmu is powered up
1128-
dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true);
1176+
if (!dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true))
1177+
ASSERT(0);
11291178
}
11301179

drivers/gpu/drm/amd/display/dc/dcn35/dcn35_init.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ static const struct hw_sequencer_funcs dcn35_funcs = {
120120
.calc_blocks_to_ungate = dcn35_calc_blocks_to_ungate,
121121
.block_power_control = dcn35_block_power_control,
122122
.root_clock_control = dcn35_root_clock_control,
123+
.set_idle_state = dcn35_set_idle_state,
124+
.get_idle_state = dcn35_get_idle_state
123125
};
124126

125127
static const struct hwseq_private_funcs dcn35_private_funcs = {

drivers/gpu/drm/amd/display/dc/dcn35/dcn35_resource.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,8 @@ static const struct dc_debug_options debug_defaults_drv = {
748748
.disable_z10 = false,
749749
.ignore_pg = true,
750750
.psp_disabled_wa = true,
751+
.ips2_eval_delay_us = 200,
752+
.ips2_entry_delay_us = 400
751753
};
752754

753755
static const struct dc_panel_config panel_config_defaults = {

drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -648,18 +648,10 @@ bool dcn35_apply_idle_power_optimizations(struct dc *dc, bool enable)
648648

649649
// TODO: review other cases when idle optimization is allowed
650650

651-
if (!enable) {
652-
// Tell PMFW to exit low power state
653-
if (dc->clk_mgr->funcs->exit_low_power_state)
654-
dc->clk_mgr->funcs->exit_low_power_state(dc->clk_mgr);
655-
656-
dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true);
657-
}
658-
659-
dc_dmub_srv_notify_idle(dc, enable);
660-
661651
if (!enable)
662-
dc_dmub_srv_is_hw_pwr_up(dc->ctx->dmub_srv, true);
652+
dc_dmub_srv_exit_low_power_state(dc);
653+
else
654+
dc_dmub_srv_notify_idle(dc, enable);
663655

664656
return true;
665657
}
@@ -1193,3 +1185,19 @@ void dcn35_optimize_bandwidth(
11931185
dc->hwss.root_clock_control(dc, &pg_update_state, false);
11941186
}
11951187
}
1188+
1189+
void dcn35_set_idle_state(const struct dc *dc, bool allow_idle)
1190+
{
1191+
// TODO: Find a more suitable communcation
1192+
if (dc->clk_mgr->funcs->set_idle_state)
1193+
dc->clk_mgr->funcs->set_idle_state(dc->clk_mgr, allow_idle);
1194+
}
1195+
1196+
uint32_t dcn35_get_idle_state(const struct dc *dc)
1197+
{
1198+
// TODO: Find a more suitable communcation
1199+
if (dc->clk_mgr->funcs->get_idle_state)
1200+
return dc->clk_mgr->funcs->get_idle_state(dc->clk_mgr);
1201+
1202+
return 0;
1203+
}

drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,7 @@ void dcn35_dsc_pg_control(
8181
struct dce_hwseq *hws,
8282
unsigned int dsc_inst,
8383
bool power_on);
84+
85+
void dcn35_set_idle_state(const struct dc *dc, bool allow_idle);
86+
uint32_t dcn35_get_idle_state(const struct dc *dc);
8487
#endif /* __DC_HWSS_DCN35_H__ */

drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,6 @@ struct hw_sequencer_funcs {
420420
struct pg_block_update *update_state, bool power_on);
421421
void (*set_idle_state)(const struct dc *dc, bool allow_idle);
422422
uint32_t (*get_idle_state)(const struct dc *dc);
423-
424423
bool (*is_pipe_topology_transition_seamless)(struct dc *dc,
425424
const struct dc_state *cur_ctx,
426425
const struct dc_state *new_ctx);

0 commit comments

Comments
 (0)