Rev 6937 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6937 | serge | 1 | /* |
2 | * Copyright © 2008-2015 Intel Corporation |
||
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 (including the next |
||
12 | * paragraph) shall be included in all copies or substantial portions of the |
||
13 | * Software. |
||
14 | * |
||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||
21 | * IN THE SOFTWARE. |
||
22 | */ |
||
23 | |||
24 | #include "intel_drv.h" |
||
25 | |||
26 | static void |
||
27 | intel_get_adjust_train(struct intel_dp *intel_dp, |
||
28 | const uint8_t link_status[DP_LINK_STATUS_SIZE]) |
||
29 | { |
||
30 | uint8_t v = 0; |
||
31 | uint8_t p = 0; |
||
32 | int lane; |
||
33 | uint8_t voltage_max; |
||
34 | uint8_t preemph_max; |
||
35 | |||
36 | for (lane = 0; lane < intel_dp->lane_count; lane++) { |
||
37 | uint8_t this_v = drm_dp_get_adjust_request_voltage(link_status, lane); |
||
38 | uint8_t this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane); |
||
39 | |||
40 | if (this_v > v) |
||
41 | v = this_v; |
||
42 | if (this_p > p) |
||
43 | p = this_p; |
||
44 | } |
||
45 | |||
46 | voltage_max = intel_dp_voltage_max(intel_dp); |
||
47 | if (v >= voltage_max) |
||
48 | v = voltage_max | DP_TRAIN_MAX_SWING_REACHED; |
||
49 | |||
50 | preemph_max = intel_dp_pre_emphasis_max(intel_dp, v); |
||
51 | if (p >= preemph_max) |
||
52 | p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; |
||
53 | |||
54 | for (lane = 0; lane < 4; lane++) |
||
55 | intel_dp->train_set[lane] = v | p; |
||
56 | } |
||
57 | |||
58 | static bool |
||
59 | intel_dp_set_link_train(struct intel_dp *intel_dp, |
||
60 | uint8_t dp_train_pat) |
||
61 | { |
||
62 | uint8_t buf[sizeof(intel_dp->train_set) + 1]; |
||
63 | int ret, len; |
||
64 | |||
65 | intel_dp_program_link_training_pattern(intel_dp, dp_train_pat); |
||
66 | |||
67 | buf[0] = dp_train_pat; |
||
68 | if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) == |
||
69 | DP_TRAINING_PATTERN_DISABLE) { |
||
70 | /* don't write DP_TRAINING_LANEx_SET on disable */ |
||
71 | len = 1; |
||
72 | } else { |
||
73 | /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */ |
||
74 | memcpy(buf + 1, intel_dp->train_set, intel_dp->lane_count); |
||
75 | len = intel_dp->lane_count + 1; |
||
76 | } |
||
77 | |||
78 | ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_PATTERN_SET, |
||
79 | buf, len); |
||
80 | |||
81 | return ret == len; |
||
82 | } |
||
83 | |||
84 | static bool |
||
85 | intel_dp_reset_link_train(struct intel_dp *intel_dp, |
||
86 | uint8_t dp_train_pat) |
||
87 | { |
||
7144 | serge | 88 | memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set)); |
6937 | serge | 89 | intel_dp_set_signal_levels(intel_dp); |
90 | return intel_dp_set_link_train(intel_dp, dp_train_pat); |
||
91 | } |
||
92 | |||
93 | static bool |
||
94 | intel_dp_update_link_train(struct intel_dp *intel_dp) |
||
95 | { |
||
96 | int ret; |
||
97 | |||
98 | intel_dp_set_signal_levels(intel_dp); |
||
99 | |||
100 | ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET, |
||
101 | intel_dp->train_set, intel_dp->lane_count); |
||
102 | |||
103 | return ret == intel_dp->lane_count; |
||
104 | } |
||
105 | |||
106 | /* Enable corresponding port and start training pattern 1 */ |
||
107 | static void |
||
108 | intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp) |
||
109 | { |
||
110 | int i; |
||
111 | uint8_t voltage; |
||
112 | int voltage_tries, loop_tries; |
||
113 | uint8_t link_config[2]; |
||
114 | uint8_t link_bw, rate_select; |
||
115 | |||
116 | if (intel_dp->prepare_link_retrain) |
||
117 | intel_dp->prepare_link_retrain(intel_dp); |
||
118 | |||
119 | intel_dp_compute_rate(intel_dp, intel_dp->link_rate, |
||
120 | &link_bw, &rate_select); |
||
121 | |||
122 | /* Write the link configuration data */ |
||
123 | link_config[0] = link_bw; |
||
124 | link_config[1] = intel_dp->lane_count; |
||
125 | if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) |
||
126 | link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; |
||
127 | drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2); |
||
128 | if (intel_dp->num_sink_rates) |
||
129 | drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET, |
||
130 | &rate_select, 1); |
||
131 | |||
132 | link_config[0] = 0; |
||
133 | link_config[1] = DP_SET_ANSI_8B10B; |
||
134 | drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2); |
||
135 | |||
136 | intel_dp->DP |= DP_PORT_EN; |
||
137 | |||
138 | /* clock recovery */ |
||
139 | if (!intel_dp_reset_link_train(intel_dp, |
||
140 | DP_TRAINING_PATTERN_1 | |
||
141 | DP_LINK_SCRAMBLING_DISABLE)) { |
||
142 | DRM_ERROR("failed to enable link training\n"); |
||
143 | return; |
||
144 | } |
||
145 | |||
146 | voltage = 0xff; |
||
147 | voltage_tries = 0; |
||
148 | loop_tries = 0; |
||
149 | for (;;) { |
||
150 | uint8_t link_status[DP_LINK_STATUS_SIZE]; |
||
151 | |||
152 | drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd); |
||
153 | if (!intel_dp_get_link_status(intel_dp, link_status)) { |
||
154 | DRM_ERROR("failed to get link status\n"); |
||
155 | break; |
||
156 | } |
||
157 | |||
158 | if (drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { |
||
159 | DRM_DEBUG_KMS("clock recovery OK\n"); |
||
160 | break; |
||
161 | } |
||
162 | |||
163 | |||
164 | /* Check to see if we've tried the max voltage */ |
||
165 | for (i = 0; i < intel_dp->lane_count; i++) |
||
166 | if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) |
||
167 | break; |
||
168 | if (i == intel_dp->lane_count) { |
||
169 | ++loop_tries; |
||
170 | if (loop_tries == 5) { |
||
171 | DRM_ERROR("too many full retries, give up\n"); |
||
172 | break; |
||
173 | } |
||
174 | intel_dp_reset_link_train(intel_dp, |
||
175 | DP_TRAINING_PATTERN_1 | |
||
176 | DP_LINK_SCRAMBLING_DISABLE); |
||
177 | voltage_tries = 0; |
||
178 | continue; |
||
179 | } |
||
180 | |||
181 | /* Check to see if we've tried the same voltage 5 times */ |
||
182 | if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { |
||
183 | ++voltage_tries; |
||
184 | if (voltage_tries == 5) { |
||
185 | DRM_ERROR("too many voltage retries, give up\n"); |
||
186 | break; |
||
187 | } |
||
188 | } else |
||
189 | voltage_tries = 0; |
||
190 | voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; |
||
191 | |||
192 | /* Update training set as requested by target */ |
||
193 | intel_get_adjust_train(intel_dp, link_status); |
||
194 | if (!intel_dp_update_link_train(intel_dp)) { |
||
195 | DRM_ERROR("failed to update link training\n"); |
||
196 | break; |
||
197 | } |
||
198 | } |
||
199 | } |
||
200 | |||
201 | /* |
||
202 | * Pick training pattern for channel equalization. Training Pattern 3 for HBR2 |
||
203 | * or 1.2 devices that support it, Training Pattern 2 otherwise. |
||
204 | */ |
||
205 | static u32 intel_dp_training_pattern(struct intel_dp *intel_dp) |
||
206 | { |
||
207 | u32 training_pattern = DP_TRAINING_PATTERN_2; |
||
208 | bool source_tps3, sink_tps3; |
||
209 | |||
210 | /* |
||
211 | * Intel platforms that support HBR2 also support TPS3. TPS3 support is |
||
212 | * also mandatory for downstream devices that support HBR2. However, not |
||
213 | * all sinks follow the spec. |
||
214 | * |
||
215 | * Due to WaDisableHBR2 SKL < B0 is the only exception where TPS3 is |
||
216 | * supported in source but still not enabled. |
||
217 | */ |
||
218 | source_tps3 = intel_dp_source_supports_hbr2(intel_dp); |
||
219 | sink_tps3 = drm_dp_tps3_supported(intel_dp->dpcd); |
||
220 | |||
221 | if (source_tps3 && sink_tps3) { |
||
222 | training_pattern = DP_TRAINING_PATTERN_3; |
||
223 | } else if (intel_dp->link_rate == 540000) { |
||
224 | if (!source_tps3) |
||
225 | DRM_DEBUG_KMS("5.4 Gbps link rate without source HBR2/TPS3 support\n"); |
||
226 | if (!sink_tps3) |
||
227 | DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3 support\n"); |
||
228 | } |
||
229 | |||
230 | return training_pattern; |
||
231 | } |
||
232 | |||
233 | static void |
||
234 | intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp) |
||
235 | { |
||
236 | bool channel_eq = false; |
||
237 | int tries, cr_tries; |
||
238 | u32 training_pattern; |
||
239 | |||
240 | training_pattern = intel_dp_training_pattern(intel_dp); |
||
241 | |||
242 | /* channel equalization */ |
||
243 | if (!intel_dp_set_link_train(intel_dp, |
||
244 | training_pattern | |
||
245 | DP_LINK_SCRAMBLING_DISABLE)) { |
||
246 | DRM_ERROR("failed to start channel equalization\n"); |
||
247 | return; |
||
248 | } |
||
249 | |||
250 | tries = 0; |
||
251 | cr_tries = 0; |
||
252 | channel_eq = false; |
||
253 | for (;;) { |
||
254 | uint8_t link_status[DP_LINK_STATUS_SIZE]; |
||
255 | |||
256 | if (cr_tries > 5) { |
||
257 | DRM_ERROR("failed to train DP, aborting\n"); |
||
258 | break; |
||
259 | } |
||
260 | |||
261 | drm_dp_link_train_channel_eq_delay(intel_dp->dpcd); |
||
262 | if (!intel_dp_get_link_status(intel_dp, link_status)) { |
||
263 | DRM_ERROR("failed to get link status\n"); |
||
264 | break; |
||
265 | } |
||
266 | |||
267 | /* Make sure clock is still ok */ |
||
268 | if (!drm_dp_clock_recovery_ok(link_status, |
||
269 | intel_dp->lane_count)) { |
||
270 | intel_dp_link_training_clock_recovery(intel_dp); |
||
271 | intel_dp_set_link_train(intel_dp, |
||
272 | training_pattern | |
||
273 | DP_LINK_SCRAMBLING_DISABLE); |
||
274 | cr_tries++; |
||
275 | continue; |
||
276 | } |
||
277 | |||
278 | if (drm_dp_channel_eq_ok(link_status, |
||
279 | intel_dp->lane_count)) { |
||
280 | channel_eq = true; |
||
281 | break; |
||
282 | } |
||
283 | |||
284 | /* Try 5 times, then try clock recovery if that fails */ |
||
285 | if (tries > 5) { |
||
286 | intel_dp_link_training_clock_recovery(intel_dp); |
||
287 | intel_dp_set_link_train(intel_dp, |
||
288 | training_pattern | |
||
289 | DP_LINK_SCRAMBLING_DISABLE); |
||
290 | tries = 0; |
||
291 | cr_tries++; |
||
292 | continue; |
||
293 | } |
||
294 | |||
295 | /* Update training set as requested by target */ |
||
296 | intel_get_adjust_train(intel_dp, link_status); |
||
297 | if (!intel_dp_update_link_train(intel_dp)) { |
||
298 | DRM_ERROR("failed to update link training\n"); |
||
299 | break; |
||
300 | } |
||
301 | ++tries; |
||
302 | } |
||
303 | |||
304 | intel_dp_set_idle_link_train(intel_dp); |
||
305 | |||
7144 | serge | 306 | if (channel_eq) |
6937 | serge | 307 | DRM_DEBUG_KMS("Channel EQ done. DP Training successful\n"); |
308 | } |
||
309 | |||
310 | void intel_dp_stop_link_train(struct intel_dp *intel_dp) |
||
311 | { |
||
312 | intel_dp_set_link_train(intel_dp, |
||
313 | DP_TRAINING_PATTERN_DISABLE); |
||
314 | } |
||
315 | |||
316 | void |
||
317 | intel_dp_start_link_train(struct intel_dp *intel_dp) |
||
318 | { |
||
319 | intel_dp_link_training_clock_recovery(intel_dp); |
||
320 | intel_dp_link_training_channel_equalization(intel_dp); |
||
321 | }>>>> |