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: Hersen Wu <hersenwu@amd.com> Acked-by: Mikita Lipski <mikita.lipski@amd.com> Signed-off-by: Qingqing Zhuo <qingqing.zhuo@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Родитель
d020970959
Коммит
2a50edbf10
|
@ -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,
|
||||||
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,
|
||||||
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(
|
||||||
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 @@
|
||||||
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)
|
||||||
*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 {
|
||||||
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);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче