1bc33f5e5SWenjing Liu /*
2bc33f5e5SWenjing Liu  * Copyright 2022 Advanced Micro Devices, Inc.
3bc33f5e5SWenjing Liu  *
4bc33f5e5SWenjing Liu  * Permission is hereby granted, free of charge, to any person obtaining a
5bc33f5e5SWenjing Liu  * copy of this software and associated documentation files (the "Software"),
6bc33f5e5SWenjing Liu  * to deal in the Software without restriction, including without limitation
7bc33f5e5SWenjing Liu  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bc33f5e5SWenjing Liu  * and/or sell copies of the Software, and to permit persons to whom the
9bc33f5e5SWenjing Liu  * Software is furnished to do so, subject to the following conditions:
10bc33f5e5SWenjing Liu  *
11bc33f5e5SWenjing Liu  * The above copyright notice and this permission notice shall be included in
12bc33f5e5SWenjing Liu  * all copies or substantial portions of the Software.
13bc33f5e5SWenjing Liu  *
14bc33f5e5SWenjing Liu  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15bc33f5e5SWenjing Liu  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16bc33f5e5SWenjing Liu  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17bc33f5e5SWenjing Liu  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18bc33f5e5SWenjing Liu  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19bc33f5e5SWenjing Liu  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20bc33f5e5SWenjing Liu  * OTHER DEALINGS IN THE SOFTWARE.
21bc33f5e5SWenjing Liu  *
22bc33f5e5SWenjing Liu  * Authors: AMD
23bc33f5e5SWenjing Liu  *
24bc33f5e5SWenjing Liu  */
25bc33f5e5SWenjing Liu 
26bc33f5e5SWenjing Liu /* FILE POLICY AND INTENDED USAGE:
27bc33f5e5SWenjing Liu  * This file implements basic dp phy functionality such as enable/disable phy
28bc33f5e5SWenjing Liu  * output and set lane/drive settings. This file is responsible for maintaining
29bc33f5e5SWenjing Liu  * and update software state representing current phy status such as current
30bc33f5e5SWenjing Liu  * link settings.
31bc33f5e5SWenjing Liu  */
32bc33f5e5SWenjing Liu 
33bc33f5e5SWenjing Liu #include "link_dp_phy.h"
34bc33f5e5SWenjing Liu #include "link_dpcd.h"
35bc33f5e5SWenjing Liu #include "link_dp_training.h"
36bc33f5e5SWenjing Liu #include "link_dp_capability.h"
37bc33f5e5SWenjing Liu #include "clk_mgr.h"
38bc33f5e5SWenjing Liu #include "resource.h"
3954618888SWenjing Liu #include "link_enc_cfg.h"
40*bb46122dSMichael Strauss #include "atomfirmware.h"
41bc33f5e5SWenjing Liu #define DC_LOGGER \
42bc33f5e5SWenjing Liu 	link->ctx->logger
43bc33f5e5SWenjing Liu 
dpcd_write_rx_power_ctrl(struct dc_link * link,bool on)44202a3816SWenjing Liu void dpcd_write_rx_power_ctrl(struct dc_link *link, bool on)
45bc33f5e5SWenjing Liu {
46bc33f5e5SWenjing Liu 	uint8_t state;
47bc33f5e5SWenjing Liu 
48bc33f5e5SWenjing Liu 	state = on ? DP_POWER_STATE_D0 : DP_POWER_STATE_D3;
49bc33f5e5SWenjing Liu 
50bc33f5e5SWenjing Liu 	if (link->sync_lt_in_progress)
51bc33f5e5SWenjing Liu 		return;
52bc33f5e5SWenjing Liu 
53bc33f5e5SWenjing Liu 	core_link_write_dpcd(link, DP_SET_POWER, &state,
54bc33f5e5SWenjing Liu 						 sizeof(state));
55bc33f5e5SWenjing Liu 
56bc33f5e5SWenjing Liu }
57bc33f5e5SWenjing Liu 
dp_enable_link_phy(struct dc_link * link,const struct link_resource * link_res,enum signal_type signal,enum clock_source_id clock_source,const struct dc_link_settings * link_settings)58bc33f5e5SWenjing Liu void dp_enable_link_phy(
59bc33f5e5SWenjing Liu 	struct dc_link *link,
60bc33f5e5SWenjing Liu 	const struct link_resource *link_res,
61bc33f5e5SWenjing Liu 	enum signal_type signal,
62bc33f5e5SWenjing Liu 	enum clock_source_id clock_source,
63bc33f5e5SWenjing Liu 	const struct dc_link_settings *link_settings)
64bc33f5e5SWenjing Liu {
65bc33f5e5SWenjing Liu 	link->cur_link_settings = *link_settings;
66bc33f5e5SWenjing Liu 	link->dc->hwss.enable_dp_link_output(link, link_res, signal,
67bc33f5e5SWenjing Liu 			clock_source, link_settings);
68788c6e2cSWenjing Liu 	dpcd_write_rx_power_ctrl(link, true);
69bc33f5e5SWenjing Liu }
70bc33f5e5SWenjing Liu 
dp_disable_link_phy(struct dc_link * link,const struct link_resource * link_res,enum signal_type signal)71bc33f5e5SWenjing Liu void dp_disable_link_phy(struct dc_link *link,
72bc33f5e5SWenjing Liu 		const struct link_resource *link_res,
73bc33f5e5SWenjing Liu 		enum signal_type signal)
74bc33f5e5SWenjing Liu {
75bc33f5e5SWenjing Liu 	struct dc  *dc = link->ctx->dc;
76bc33f5e5SWenjing Liu 
7745f98fccSIan Chen 	if (!link->wa_flags.dp_keep_receiver_powered &&
7845f98fccSIan Chen 		!link->skip_implict_edp_power_control)
79788c6e2cSWenjing Liu 		dpcd_write_rx_power_ctrl(link, false);
80bc33f5e5SWenjing Liu 
81bc33f5e5SWenjing Liu 	dc->hwss.disable_link_output(link, link_res, signal);
82bc33f5e5SWenjing Liu 	/* Clear current link setting.*/
83bc33f5e5SWenjing Liu 	memset(&link->cur_link_settings, 0,
84bc33f5e5SWenjing Liu 			sizeof(link->cur_link_settings));
85bc33f5e5SWenjing Liu 
86bc33f5e5SWenjing Liu 	if (dc->clk_mgr->funcs->notify_link_rate_change)
87bc33f5e5SWenjing Liu 		dc->clk_mgr->funcs->notify_link_rate_change(dc->clk_mgr, link);
88bc33f5e5SWenjing Liu }
89bc33f5e5SWenjing Liu 
is_immediate_downstream(struct dc_link * link,uint32_t offset)90bc33f5e5SWenjing Liu static inline bool is_immediate_downstream(struct dc_link *link, uint32_t offset)
91bc33f5e5SWenjing Liu {
92bc33f5e5SWenjing Liu 	return (dp_parse_lttpr_repeater_count(link->dpcd_caps.lttpr_caps.phy_repeater_cnt) ==
93bc33f5e5SWenjing Liu 			offset);
94bc33f5e5SWenjing Liu }
95bc33f5e5SWenjing Liu 
dp_set_hw_lane_settings(struct dc_link * link,const struct link_resource * link_res,const struct link_training_settings * link_settings,uint32_t offset)96bc33f5e5SWenjing Liu void dp_set_hw_lane_settings(
97bc33f5e5SWenjing Liu 	struct dc_link *link,
98bc33f5e5SWenjing Liu 	const struct link_resource *link_res,
99bc33f5e5SWenjing Liu 	const struct link_training_settings *link_settings,
100bc33f5e5SWenjing Liu 	uint32_t offset)
101bc33f5e5SWenjing Liu {
102bc33f5e5SWenjing Liu 	const struct link_hwss *link_hwss = get_link_hwss(link, link_res);
103bc33f5e5SWenjing Liu 
104*bb46122dSMichael Strauss 	// Don't return here if using FIXED_VS link HWSS and encoding is 128b/132b
105bc33f5e5SWenjing Liu 	if ((link_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) &&
106*bb46122dSMichael Strauss 			!is_immediate_downstream(link, offset) &&
107*bb46122dSMichael Strauss 			(!(link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) ||
108*bb46122dSMichael Strauss 			link_dp_get_encoding_format(&link_settings->link_settings) == DP_8b_10b_ENCODING))
109bc33f5e5SWenjing Liu 		return;
110bc33f5e5SWenjing Liu 
111bc33f5e5SWenjing Liu 	if (link_hwss->ext.set_dp_lane_settings)
112bc33f5e5SWenjing Liu 		link_hwss->ext.set_dp_lane_settings(link, link_res,
113bc33f5e5SWenjing Liu 				&link_settings->link_settings,
114bc33f5e5SWenjing Liu 				link_settings->hw_lane_settings);
115bc33f5e5SWenjing Liu 
116bc33f5e5SWenjing Liu 	memmove(link->cur_lane_setting,
117bc33f5e5SWenjing Liu 			link_settings->hw_lane_settings,
118bc33f5e5SWenjing Liu 			sizeof(link->cur_lane_setting));
119bc33f5e5SWenjing Liu }
120bc33f5e5SWenjing Liu 
dp_set_drive_settings(struct dc_link * link,const struct link_resource * link_res,struct link_training_settings * lt_settings)121bc33f5e5SWenjing Liu void dp_set_drive_settings(
122bc33f5e5SWenjing Liu 	struct dc_link *link,
123bc33f5e5SWenjing Liu 	const struct link_resource *link_res,
124bc33f5e5SWenjing Liu 	struct link_training_settings *lt_settings)
125bc33f5e5SWenjing Liu {
126bc33f5e5SWenjing Liu 	/* program ASIC PHY settings*/
127bc33f5e5SWenjing Liu 	dp_set_hw_lane_settings(link, link_res, lt_settings, DPRX);
128bc33f5e5SWenjing Liu 
129bc33f5e5SWenjing Liu 	dp_hw_to_dpcd_lane_settings(lt_settings,
130bc33f5e5SWenjing Liu 			lt_settings->hw_lane_settings,
131bc33f5e5SWenjing Liu 			lt_settings->dpcd_lane_settings);
132bc33f5e5SWenjing Liu 
133bc33f5e5SWenjing Liu 	/* Notify DP sink the PHY settings from source */
134bc33f5e5SWenjing Liu 	dpcd_set_lane_settings(link, lt_settings, DPRX);
135bc33f5e5SWenjing Liu }
13654618888SWenjing Liu 
dp_set_fec_ready(struct dc_link * link,const struct link_resource * link_res,bool ready)13754618888SWenjing Liu enum dc_status dp_set_fec_ready(struct dc_link *link, const struct link_resource *link_res, bool ready)
13854618888SWenjing Liu {
13954618888SWenjing Liu 	/* FEC has to be "set ready" before the link training.
14054618888SWenjing Liu 	 * The policy is to always train with FEC
14154618888SWenjing Liu 	 * if the sink supports it and leave it enabled on link.
14254618888SWenjing Liu 	 * If FEC is not supported, disable it.
14354618888SWenjing Liu 	 */
14454618888SWenjing Liu 	struct link_encoder *link_enc = NULL;
14554618888SWenjing Liu 	enum dc_status status = DC_OK;
14654618888SWenjing Liu 	uint8_t fec_config = 0;
14754618888SWenjing Liu 
14854618888SWenjing Liu 	link_enc = link_enc_cfg_get_link_enc(link);
14954618888SWenjing Liu 	ASSERT(link_enc);
15054618888SWenjing Liu 
151788c6e2cSWenjing Liu 	if (!dp_should_enable_fec(link))
15254618888SWenjing Liu 		return status;
15354618888SWenjing Liu 
15454618888SWenjing Liu 	if (link_enc->funcs->fec_set_ready &&
15554618888SWenjing Liu 			link->dpcd_caps.fec_cap.bits.FEC_CAPABLE) {
15654618888SWenjing Liu 		if (ready) {
15754618888SWenjing Liu 			fec_config = 1;
15854618888SWenjing Liu 			status = core_link_write_dpcd(link,
15954618888SWenjing Liu 					DP_FEC_CONFIGURATION,
16054618888SWenjing Liu 					&fec_config,
16154618888SWenjing Liu 					sizeof(fec_config));
16254618888SWenjing Liu 			if (status == DC_OK) {
16354618888SWenjing Liu 				link_enc->funcs->fec_set_ready(link_enc, true);
16454618888SWenjing Liu 				link->fec_state = dc_link_fec_ready;
16554618888SWenjing Liu 			} else {
16654618888SWenjing Liu 				link_enc->funcs->fec_set_ready(link_enc, false);
16754618888SWenjing Liu 				link->fec_state = dc_link_fec_not_ready;
16854618888SWenjing Liu 				dm_error("dpcd write failed to set fec_ready");
16954618888SWenjing Liu 			}
17054618888SWenjing Liu 		} else if (link->fec_state == dc_link_fec_ready) {
17154618888SWenjing Liu 			fec_config = 0;
17254618888SWenjing Liu 			status = core_link_write_dpcd(link,
17354618888SWenjing Liu 					DP_FEC_CONFIGURATION,
17454618888SWenjing Liu 					&fec_config,
17554618888SWenjing Liu 					sizeof(fec_config));
17654618888SWenjing Liu 			link_enc->funcs->fec_set_ready(link_enc, false);
17754618888SWenjing Liu 			link->fec_state = dc_link_fec_not_ready;
17854618888SWenjing Liu 		}
17954618888SWenjing Liu 	}
18054618888SWenjing Liu 
18154618888SWenjing Liu 	return status;
18254618888SWenjing Liu }
18354618888SWenjing Liu 
dp_set_fec_enable(struct dc_link * link,bool enable)18454618888SWenjing Liu void dp_set_fec_enable(struct dc_link *link, bool enable)
18554618888SWenjing Liu {
18654618888SWenjing Liu 	struct link_encoder *link_enc = NULL;
18754618888SWenjing Liu 
18854618888SWenjing Liu 	link_enc = link_enc_cfg_get_link_enc(link);
18954618888SWenjing Liu 	ASSERT(link_enc);
19054618888SWenjing Liu 
191788c6e2cSWenjing Liu 	if (!dp_should_enable_fec(link))
19254618888SWenjing Liu 		return;
19354618888SWenjing Liu 
19454618888SWenjing Liu 	if (link_enc->funcs->fec_set_enable &&
19554618888SWenjing Liu 			link->dpcd_caps.fec_cap.bits.FEC_CAPABLE) {
19654618888SWenjing Liu 		if (link->fec_state == dc_link_fec_ready && enable) {
19754618888SWenjing Liu 			/* Accord to DP spec, FEC enable sequence can first
19854618888SWenjing Liu 			 * be transmitted anytime after 1000 LL codes have
19954618888SWenjing Liu 			 * been transmitted on the link after link training
20054618888SWenjing Liu 			 * completion. Using 1 lane RBR should have the maximum
20154618888SWenjing Liu 			 * time for transmitting 1000 LL codes which is 6.173 us.
20254618888SWenjing Liu 			 * So use 7 microseconds delay instead.
20354618888SWenjing Liu 			 */
20454618888SWenjing Liu 			udelay(7);
20554618888SWenjing Liu 			link_enc->funcs->fec_set_enable(link_enc, true);
20654618888SWenjing Liu 			link->fec_state = dc_link_fec_enabled;
20754618888SWenjing Liu 		} else if (link->fec_state == dc_link_fec_enabled && !enable) {
20854618888SWenjing Liu 			link_enc->funcs->fec_set_enable(link_enc, false);
20954618888SWenjing Liu 			link->fec_state = dc_link_fec_ready;
21054618888SWenjing Liu 		}
21154618888SWenjing Liu 	}
21254618888SWenjing Liu }
21354618888SWenjing Liu 
214