1 | /* |
2 | * Copyright 2022 Advanced Micro Devices, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | * |
22 | * Authors: AMD |
23 | * |
24 | */ |
25 | |
26 | /* FILE POLICY AND INTENDED USAGE: |
27 | * This file implements basic dp phy functionality such as enable/disable phy |
28 | * output and set lane/drive settings. This file is responsible for maintaining |
29 | * and update software state representing current phy status such as current |
30 | * link settings. |
31 | */ |
32 | |
33 | #include "link_dp_phy.h" |
34 | #include "link_dpcd.h" |
35 | #include "link_dp_training.h" |
36 | #include "link_dp_capability.h" |
37 | #include "clk_mgr.h" |
38 | #include "resource.h" |
39 | #include "link_enc_cfg.h" |
40 | #include "atomfirmware.h" |
41 | #define DC_LOGGER \ |
42 | link->ctx->logger |
43 | |
44 | void dpcd_write_rx_power_ctrl(struct dc_link *link, bool on) |
45 | { |
46 | uint8_t state; |
47 | |
48 | state = on ? DP_POWER_STATE_D0 : DP_POWER_STATE_D3; |
49 | |
50 | if (link->sync_lt_in_progress) |
51 | return; |
52 | |
53 | core_link_write_dpcd(link, DP_SET_POWER, data: &state, |
54 | size: sizeof(state)); |
55 | |
56 | } |
57 | |
58 | void dp_enable_link_phy( |
59 | struct dc_link *link, |
60 | const struct link_resource *link_res, |
61 | enum signal_type signal, |
62 | enum clock_source_id clock_source, |
63 | const struct dc_link_settings *link_settings) |
64 | { |
65 | link->cur_link_settings = *link_settings; |
66 | link->dc->hwss.enable_dp_link_output(link, link_res, signal, |
67 | clock_source, link_settings); |
68 | dpcd_write_rx_power_ctrl(link, on: true); |
69 | } |
70 | |
71 | void dp_disable_link_phy(struct dc_link *link, |
72 | const struct link_resource *link_res, |
73 | enum signal_type signal) |
74 | { |
75 | struct dc *dc = link->ctx->dc; |
76 | |
77 | if (!link->wa_flags.dp_keep_receiver_powered && |
78 | !link->skip_implict_edp_power_control) |
79 | dpcd_write_rx_power_ctrl(link, on: false); |
80 | |
81 | dc->hwss.disable_link_output(link, link_res, signal); |
82 | /* Clear current link setting.*/ |
83 | memset(&link->cur_link_settings, 0, |
84 | sizeof(link->cur_link_settings)); |
85 | |
86 | if (dc->clk_mgr->funcs->notify_link_rate_change) |
87 | dc->clk_mgr->funcs->notify_link_rate_change(dc->clk_mgr, link); |
88 | } |
89 | |
90 | static inline bool is_immediate_downstream(struct dc_link *link, uint32_t offset) |
91 | { |
92 | return (dp_parse_lttpr_repeater_count(lttpr_repeater_count: link->dpcd_caps.lttpr_caps.phy_repeater_cnt) == |
93 | offset); |
94 | } |
95 | |
96 | void dp_set_hw_lane_settings( |
97 | struct dc_link *link, |
98 | const struct link_resource *link_res, |
99 | const struct link_training_settings *link_settings, |
100 | uint32_t offset) |
101 | { |
102 | const struct link_hwss *link_hwss = get_link_hwss(link, link_res); |
103 | |
104 | // Don't return here if using FIXED_VS link HWSS and encoding is 128b/132b |
105 | if ((link_settings->lttpr_mode == LTTPR_MODE_NON_TRANSPARENT) && |
106 | !is_immediate_downstream(link, offset) && |
107 | (!(link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) || |
108 | link_dp_get_encoding_format(link_settings: &link_settings->link_settings) == DP_8b_10b_ENCODING)) |
109 | return; |
110 | |
111 | if (link_hwss->ext.set_dp_lane_settings) |
112 | link_hwss->ext.set_dp_lane_settings(link, link_res, |
113 | &link_settings->link_settings, |
114 | link_settings->hw_lane_settings); |
115 | |
116 | memmove(link->cur_lane_setting, |
117 | link_settings->hw_lane_settings, |
118 | sizeof(link->cur_lane_setting)); |
119 | } |
120 | |
121 | void dp_set_drive_settings( |
122 | struct dc_link *link, |
123 | const struct link_resource *link_res, |
124 | struct link_training_settings *lt_settings) |
125 | { |
126 | /* program ASIC PHY settings*/ |
127 | dp_set_hw_lane_settings(link, link_res, link_settings: lt_settings, offset: DPRX); |
128 | |
129 | dp_hw_to_dpcd_lane_settings(lt_settings, |
130 | hw_lane_settings: lt_settings->hw_lane_settings, |
131 | dpcd_lane_settings: lt_settings->dpcd_lane_settings); |
132 | |
133 | /* Notify DP sink the PHY settings from source */ |
134 | dpcd_set_lane_settings(link, link_training_setting: lt_settings, offset: DPRX); |
135 | } |
136 | |
137 | enum dc_status dp_set_fec_ready(struct dc_link *link, const struct link_resource *link_res, bool ready) |
138 | { |
139 | /* FEC has to be "set ready" before the link training. |
140 | * The policy is to always train with FEC |
141 | * if the sink supports it and leave it enabled on link. |
142 | * If FEC is not supported, disable it. |
143 | */ |
144 | struct link_encoder *link_enc = NULL; |
145 | enum dc_status status = DC_OK; |
146 | uint8_t fec_config = 0; |
147 | |
148 | link_enc = link_enc_cfg_get_link_enc(link); |
149 | ASSERT(link_enc); |
150 | |
151 | if (!dp_should_enable_fec(link)) |
152 | return status; |
153 | |
154 | if (link_enc->funcs->fec_set_ready && |
155 | link->dpcd_caps.fec_cap.bits.FEC_CAPABLE) { |
156 | if (ready) { |
157 | fec_config = 1; |
158 | status = core_link_write_dpcd(link, |
159 | DP_FEC_CONFIGURATION, |
160 | data: &fec_config, |
161 | size: sizeof(fec_config)); |
162 | if (status == DC_OK) { |
163 | link_enc->funcs->fec_set_ready(link_enc, true); |
164 | link->fec_state = dc_link_fec_ready; |
165 | } else { |
166 | link_enc->funcs->fec_set_ready(link_enc, false); |
167 | link->fec_state = dc_link_fec_not_ready; |
168 | dm_error("dpcd write failed to set fec_ready" ); |
169 | } |
170 | } else if (link->fec_state == dc_link_fec_ready) { |
171 | fec_config = 0; |
172 | status = core_link_write_dpcd(link, |
173 | DP_FEC_CONFIGURATION, |
174 | data: &fec_config, |
175 | size: sizeof(fec_config)); |
176 | link_enc->funcs->fec_set_ready(link_enc, false); |
177 | link->fec_state = dc_link_fec_not_ready; |
178 | } |
179 | } |
180 | |
181 | return status; |
182 | } |
183 | |
184 | void dp_set_fec_enable(struct dc_link *link, bool enable) |
185 | { |
186 | struct link_encoder *link_enc = NULL; |
187 | |
188 | link_enc = link_enc_cfg_get_link_enc(link); |
189 | ASSERT(link_enc); |
190 | |
191 | if (!dp_should_enable_fec(link)) |
192 | return; |
193 | |
194 | if (link_enc->funcs->fec_set_enable && |
195 | link->dpcd_caps.fec_cap.bits.FEC_CAPABLE) { |
196 | if (link->fec_state == dc_link_fec_ready && enable) { |
197 | /* Accord to DP spec, FEC enable sequence can first |
198 | * be transmitted anytime after 1000 LL codes have |
199 | * been transmitted on the link after link training |
200 | * completion. Using 1 lane RBR should have the maximum |
201 | * time for transmitting 1000 LL codes which is 6.173 us. |
202 | * So use 7 microseconds delay instead. |
203 | */ |
204 | udelay(7); |
205 | link_enc->funcs->fec_set_enable(link_enc, true); |
206 | link->fec_state = dc_link_fec_enabled; |
207 | } else if (link->fec_state == dc_link_fec_enabled && !enable) { |
208 | link_enc->funcs->fec_set_enable(link_enc, false); |
209 | link->fec_state = dc_link_fec_ready; |
210 | } |
211 | } |
212 | } |
213 | |
214 | |