Commit 2a50edbf authored by Qingqing Zhuo's avatar Qingqing Zhuo Committed by Alex Deucher

drm/amd/display: Apply w/a for hard hang on HPD

[Why]
HPD disable and enable sequences are not mutually exclusive
on Linux. For HPDs that spans under 1s (i.e. HPD low = 1s),
part of the disable sequence (specifically, a request to SMU
to lower refclk) could come right before the call to PHY
enablement, causing DMUB to access an irresponsive PHY
and thus a hard hang on the system.

[How]
Disable 48mhz refclk off when there is any HPD status in
connected state.
Reviewed-by: default avatarHersen Wu <hersenwu@amd.com>
Acked-by: default avatarMikita Lipski <mikita.lipski@amd.com>
Signed-off-by: default avatarQingqing Zhuo <qingqing.zhuo@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent d0209709
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include "clk/clk_10_0_2_sh_mask.h" #include "clk/clk_10_0_2_sh_mask.h"
#include "renoir_ip_offset.h" #include "renoir_ip_offset.h"
#include "irq/dcn21/irq_service_dcn21.h"
/* Constants */ /* Constants */
...@@ -129,9 +130,11 @@ void rn_update_clocks(struct clk_mgr *clk_mgr_base, ...@@ -129,9 +130,11 @@ void rn_update_clocks(struct clk_mgr *clk_mgr_base,
struct dc_clocks *new_clocks = &context->bw_ctx.bw.dcn.clk; struct dc_clocks *new_clocks = &context->bw_ctx.bw.dcn.clk;
struct dc *dc = clk_mgr_base->ctx->dc; struct dc *dc = clk_mgr_base->ctx->dc;
int display_count; int display_count;
int irq_src;
bool update_dppclk = false; bool update_dppclk = false;
bool update_dispclk = false; bool update_dispclk = false;
bool dpp_clock_lowered = false; bool dpp_clock_lowered = false;
uint32_t hpd_state;
struct dmcu *dmcu = clk_mgr_base->ctx->dc->res_pool->dmcu; struct dmcu *dmcu = clk_mgr_base->ctx->dc->res_pool->dmcu;
...@@ -147,8 +150,15 @@ void rn_update_clocks(struct clk_mgr *clk_mgr_base, ...@@ -147,8 +150,15 @@ void rn_update_clocks(struct clk_mgr *clk_mgr_base,
if (clk_mgr_base->clks.pwr_state != DCN_PWR_STATE_LOW_POWER) { if (clk_mgr_base->clks.pwr_state != DCN_PWR_STATE_LOW_POWER) {
display_count = rn_get_active_display_cnt_wa(dc, context); display_count = rn_get_active_display_cnt_wa(dc, context);
for (irq_src = DC_IRQ_SOURCE_HPD1; irq_src <= DC_IRQ_SOURCE_HPD5; irq_src++) {
hpd_state = dal_get_hpd_state_dcn21(dc->res_pool->irqs, irq_src);
if (hpd_state)
break;
}
/* if we can go lower, go lower */ /* if we can go lower, go lower */
if (display_count == 0) { if (display_count == 0 && !hpd_state) {
rn_vbios_smu_set_dcn_low_power_state(clk_mgr, DCN_PWR_STATE_LOW_POWER); rn_vbios_smu_set_dcn_low_power_state(clk_mgr, DCN_PWR_STATE_LOW_POWER);
/* update power state */ /* update power state */
clk_mgr_base->clks.pwr_state = DCN_PWR_STATE_LOW_POWER; clk_mgr_base->clks.pwr_state = DCN_PWR_STATE_LOW_POWER;
......
...@@ -135,6 +135,31 @@ enum dc_irq_source to_dal_irq_source_dcn21( ...@@ -135,6 +135,31 @@ enum dc_irq_source to_dal_irq_source_dcn21(
return DC_IRQ_SOURCE_INVALID; return DC_IRQ_SOURCE_INVALID;
} }
uint32_t dal_get_hpd_state_dcn21(struct irq_service *irq_service, enum dc_irq_source source)
{
const struct irq_source_info *info;
uint32_t addr;
uint32_t value;
uint32_t current_status;
info = find_irq_source_info(irq_service, source);
if (!info)
return 0;
addr = info->status_reg;
if (!addr)
return 0;
value = dm_read_reg(irq_service->ctx, addr);
current_status =
get_reg_field_value(
value,
HPD0_DC_HPD_INT_STATUS,
DC_HPD_SENSE);
return current_status;
}
static bool hpd_ack( static bool hpd_ack(
struct irq_service *irq_service, struct irq_service *irq_service,
const struct irq_source_info *info) const struct irq_source_info *info)
......
...@@ -31,4 +31,6 @@ ...@@ -31,4 +31,6 @@
struct irq_service *dal_irq_service_dcn21_create( struct irq_service *dal_irq_service_dcn21_create(
struct irq_service_init_data *init_data); struct irq_service_init_data *init_data);
uint32_t dal_get_hpd_state_dcn21(struct irq_service *irq_service, enum dc_irq_source source);
#endif #endif
...@@ -79,7 +79,7 @@ void dal_irq_service_destroy(struct irq_service **irq_service) ...@@ -79,7 +79,7 @@ void dal_irq_service_destroy(struct irq_service **irq_service)
*irq_service = NULL; *irq_service = NULL;
} }
static const struct irq_source_info *find_irq_source_info( const struct irq_source_info *find_irq_source_info(
struct irq_service *irq_service, struct irq_service *irq_service,
enum dc_irq_source source) enum dc_irq_source source)
{ {
......
...@@ -69,6 +69,10 @@ struct irq_service { ...@@ -69,6 +69,10 @@ struct irq_service {
const struct irq_service_funcs *funcs; const struct irq_service_funcs *funcs;
}; };
const struct irq_source_info *find_irq_source_info(
struct irq_service *irq_service,
enum dc_irq_source source);
void dal_irq_service_construct( void dal_irq_service_construct(
struct irq_service *irq_service, struct irq_service *irq_service,
struct irq_service_init_data *init_data); struct irq_service_init_data *init_data);
......
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