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:
		
							parent
							
								
									d020970959
								
							
						
					
					
						commit
						2a50edbf10
					
				| @ -42,6 +42,7 @@ | ||||
| #include "clk/clk_10_0_2_sh_mask.h" | ||||
| #include "renoir_ip_offset.h" | ||||
| 
 | ||||
| #include "irq/dcn21/irq_service_dcn21.h" | ||||
| 
 | ||||
| /* 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 *dc = clk_mgr_base->ctx->dc; | ||||
| 	int display_count; | ||||
| 	int irq_src; | ||||
| 	bool update_dppclk = false; | ||||
| 	bool update_dispclk = false; | ||||
| 	bool dpp_clock_lowered = false; | ||||
| 	uint32_t hpd_state; | ||||
| 
 | ||||
| 	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) { | ||||
| 
 | ||||
| 			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 (display_count == 0) { | ||||
| 			if (display_count == 0 && !hpd_state) { | ||||
| 				rn_vbios_smu_set_dcn_low_power_state(clk_mgr, DCN_PWR_STATE_LOW_POWER); | ||||
| 				/* update power state */ | ||||
| 				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; | ||||
| } | ||||
| 
 | ||||
| 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( | ||||
| 	struct irq_service *irq_service, | ||||
| 	const struct irq_source_info *info) | ||||
|  | ||||
| @ -31,4 +31,6 @@ | ||||
| struct irq_service *dal_irq_service_dcn21_create( | ||||
| 	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 | ||||
|  | ||||
| @ -79,7 +79,7 @@ void dal_irq_service_destroy(struct irq_service **irq_service) | ||||
| 	*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, | ||||
| 	enum dc_irq_source source) | ||||
| { | ||||
|  | ||||
| @ -69,6 +69,10 @@ struct irq_service { | ||||
| 	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( | ||||
| 	struct irq_service *irq_service, | ||||
| 	struct irq_service_init_data *init_data); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user