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 | #include "link_dp_cts.h" |
26 | #include "link/link_resource.h" |
27 | #include "link/protocols/link_dpcd.h" |
28 | #include "link/protocols/link_dp_training.h" |
29 | #include "link/protocols/link_dp_phy.h" |
30 | #include "link/protocols/link_dp_training_fixed_vs_pe_retimer.h" |
31 | #include "link/protocols/link_dp_capability.h" |
32 | #include "link/link_dpms.h" |
33 | #include "resource.h" |
34 | #include "dm_helpers.h" |
35 | #include "dc_dmub_srv.h" |
36 | #include "dce/dmub_hw_lock_mgr.h" |
37 | |
38 | #define DC_LOGGER \ |
39 | link->ctx->logger |
40 | |
41 | static enum dc_link_rate get_link_rate_from_test_link_rate(uint8_t test_rate) |
42 | { |
43 | switch (test_rate) { |
44 | case DP_TEST_LINK_RATE_RBR: |
45 | return LINK_RATE_LOW; |
46 | case DP_TEST_LINK_RATE_HBR: |
47 | return LINK_RATE_HIGH; |
48 | case DP_TEST_LINK_RATE_HBR2: |
49 | return LINK_RATE_HIGH2; |
50 | case DP_TEST_LINK_RATE_HBR3: |
51 | return LINK_RATE_HIGH3; |
52 | case DP_TEST_LINK_RATE_UHBR10: |
53 | return LINK_RATE_UHBR10; |
54 | case DP_TEST_LINK_RATE_UHBR20: |
55 | return LINK_RATE_UHBR20; |
56 | case DP_TEST_LINK_RATE_UHBR13_5_LEGACY: |
57 | case DP_TEST_LINK_RATE_UHBR13_5: |
58 | return LINK_RATE_UHBR13_5; |
59 | default: |
60 | return LINK_RATE_UNKNOWN; |
61 | } |
62 | } |
63 | |
64 | static void dp_retrain_link_dp_test(struct dc_link *link, |
65 | struct dc_link_settings *link_setting, |
66 | bool skip_video_pattern) |
67 | { |
68 | struct pipe_ctx *pipes[MAX_PIPES]; |
69 | struct dc_state *state = link->dc->current_state; |
70 | uint8_t count; |
71 | int i; |
72 | |
73 | udelay(100); |
74 | |
75 | link_get_master_pipes_with_dpms_on(link, state, count: &count, pipes); |
76 | |
77 | for (i = 0; i < count; i++) { |
78 | link_set_dpms_off(pipe_ctx: pipes[i]); |
79 | pipes[i]->link_config.dp_link_settings = *link_setting; |
80 | update_dp_encoder_resources_for_test_harness( |
81 | dc: link->dc, |
82 | context: state, |
83 | pipe_ctx: pipes[i]); |
84 | } |
85 | |
86 | for (i = count-1; i >= 0; i--) |
87 | link_set_dpms_on(state, pipe_ctx: pipes[i]); |
88 | } |
89 | |
90 | static void dp_test_send_link_training(struct dc_link *link) |
91 | { |
92 | struct dc_link_settings link_settings = {0}; |
93 | uint8_t test_rate = 0; |
94 | |
95 | core_link_read_dpcd( |
96 | link, |
97 | DP_TEST_LANE_COUNT, |
98 | data: (unsigned char *)(&link_settings.lane_count), |
99 | size: 1); |
100 | core_link_read_dpcd( |
101 | link, |
102 | DP_TEST_LINK_RATE, |
103 | data: &test_rate, |
104 | size: 1); |
105 | link_settings.link_rate = get_link_rate_from_test_link_rate(test_rate); |
106 | |
107 | if (link_settings.link_rate == LINK_RATE_UNKNOWN) { |
108 | DC_LOG_ERROR("%s: Invalid test link rate." , __func__); |
109 | ASSERT(0); |
110 | } |
111 | |
112 | /* Set preferred link settings */ |
113 | link->verified_link_cap.lane_count = link_settings.lane_count; |
114 | link->verified_link_cap.link_rate = link_settings.link_rate; |
115 | |
116 | dp_retrain_link_dp_test(link, link_setting: &link_settings, skip_video_pattern: false); |
117 | } |
118 | |
119 | static void dp_test_get_audio_test_data(struct dc_link *link, bool disable_video) |
120 | { |
121 | union audio_test_mode dpcd_test_mode = {0}; |
122 | struct audio_test_pattern_type dpcd_pattern_type = {0}; |
123 | union audio_test_pattern_period dpcd_pattern_period[AUDIO_CHANNELS_COUNT] = {0}; |
124 | enum dp_test_pattern test_pattern = DP_TEST_PATTERN_AUDIO_OPERATOR_DEFINED; |
125 | |
126 | struct pipe_ctx *pipes = link->dc->current_state->res_ctx.pipe_ctx; |
127 | struct pipe_ctx *pipe_ctx = &pipes[0]; |
128 | unsigned int channel_count; |
129 | unsigned int channel = 0; |
130 | unsigned int modes = 0; |
131 | unsigned int sampling_rate_in_hz = 0; |
132 | |
133 | // get audio test mode and test pattern parameters |
134 | core_link_read_dpcd( |
135 | link, |
136 | DP_TEST_AUDIO_MODE, |
137 | data: &dpcd_test_mode.raw, |
138 | size: sizeof(dpcd_test_mode)); |
139 | |
140 | core_link_read_dpcd( |
141 | link, |
142 | DP_TEST_AUDIO_PATTERN_TYPE, |
143 | data: &dpcd_pattern_type.value, |
144 | size: sizeof(dpcd_pattern_type)); |
145 | |
146 | channel_count = min(dpcd_test_mode.bits.channel_count + 1, AUDIO_CHANNELS_COUNT); |
147 | |
148 | // read pattern periods for requested channels when sawTooth pattern is requested |
149 | if (dpcd_pattern_type.value == AUDIO_TEST_PATTERN_SAWTOOTH || |
150 | dpcd_pattern_type.value == AUDIO_TEST_PATTERN_OPERATOR_DEFINED) { |
151 | |
152 | test_pattern = (dpcd_pattern_type.value == AUDIO_TEST_PATTERN_SAWTOOTH) ? |
153 | DP_TEST_PATTERN_AUDIO_SAWTOOTH : DP_TEST_PATTERN_AUDIO_OPERATOR_DEFINED; |
154 | // read period for each channel |
155 | for (channel = 0; channel < channel_count; channel++) { |
156 | core_link_read_dpcd( |
157 | link, |
158 | DP_TEST_AUDIO_PERIOD_CH1 + channel, |
159 | data: &dpcd_pattern_period[channel].raw, |
160 | size: sizeof(dpcd_pattern_period[channel])); |
161 | } |
162 | } |
163 | |
164 | // translate sampling rate |
165 | switch (dpcd_test_mode.bits.sampling_rate) { |
166 | case AUDIO_SAMPLING_RATE_32KHZ: |
167 | sampling_rate_in_hz = 32000; |
168 | break; |
169 | case AUDIO_SAMPLING_RATE_44_1KHZ: |
170 | sampling_rate_in_hz = 44100; |
171 | break; |
172 | case AUDIO_SAMPLING_RATE_48KHZ: |
173 | sampling_rate_in_hz = 48000; |
174 | break; |
175 | case AUDIO_SAMPLING_RATE_88_2KHZ: |
176 | sampling_rate_in_hz = 88200; |
177 | break; |
178 | case AUDIO_SAMPLING_RATE_96KHZ: |
179 | sampling_rate_in_hz = 96000; |
180 | break; |
181 | case AUDIO_SAMPLING_RATE_176_4KHZ: |
182 | sampling_rate_in_hz = 176400; |
183 | break; |
184 | case AUDIO_SAMPLING_RATE_192KHZ: |
185 | sampling_rate_in_hz = 192000; |
186 | break; |
187 | default: |
188 | sampling_rate_in_hz = 0; |
189 | break; |
190 | } |
191 | |
192 | link->audio_test_data.flags.test_requested = 1; |
193 | link->audio_test_data.flags.disable_video = disable_video; |
194 | link->audio_test_data.sampling_rate = sampling_rate_in_hz; |
195 | link->audio_test_data.channel_count = channel_count; |
196 | link->audio_test_data.pattern_type = test_pattern; |
197 | |
198 | if (test_pattern == DP_TEST_PATTERN_AUDIO_SAWTOOTH) { |
199 | for (modes = 0; modes < pipe_ctx->stream->audio_info.mode_count; modes++) { |
200 | link->audio_test_data.pattern_period[modes] = dpcd_pattern_period[modes].bits.pattern_period; |
201 | } |
202 | } |
203 | } |
204 | |
205 | /* TODO Raven hbr2 compliance eye output is unstable |
206 | * (toggling on and off) with debugger break |
207 | * This caueses intermittent PHY automation failure |
208 | * Need to look into the root cause */ |
209 | static void dp_test_send_phy_test_pattern(struct dc_link *link) |
210 | { |
211 | union phy_test_pattern dpcd_test_pattern; |
212 | union lane_adjust dpcd_lane_adjustment[2]; |
213 | unsigned char dpcd_post_cursor_2_adjustment = 0; |
214 | unsigned char test_pattern_buffer[ |
215 | (DP_TEST_264BIT_CUSTOM_PATTERN_263_256 - |
216 | DP_TEST_264BIT_CUSTOM_PATTERN_7_0)+1] = {0}; |
217 | unsigned int test_pattern_size = 0; |
218 | enum dp_test_pattern test_pattern; |
219 | union lane_adjust dpcd_lane_adjust; |
220 | unsigned int lane; |
221 | struct link_training_settings link_training_settings; |
222 | unsigned char no_preshoot = 0; |
223 | unsigned char no_deemphasis = 0; |
224 | |
225 | dpcd_test_pattern.raw = 0; |
226 | memset(dpcd_lane_adjustment, 0, sizeof(dpcd_lane_adjustment)); |
227 | memset(&link_training_settings, 0, sizeof(link_training_settings)); |
228 | |
229 | /* get phy test pattern and pattern parameters from DP receiver */ |
230 | core_link_read_dpcd( |
231 | link, |
232 | DP_PHY_TEST_PATTERN, |
233 | data: &dpcd_test_pattern.raw, |
234 | size: sizeof(dpcd_test_pattern)); |
235 | core_link_read_dpcd( |
236 | link, |
237 | DP_ADJUST_REQUEST_LANE0_1, |
238 | data: &dpcd_lane_adjustment[0].raw, |
239 | size: sizeof(dpcd_lane_adjustment)); |
240 | |
241 | /* prepare link training settings */ |
242 | link_training_settings.link_settings = link->cur_link_settings; |
243 | |
244 | link_training_settings.lttpr_mode = dp_decide_lttpr_mode(link, link_setting: &link->cur_link_settings); |
245 | |
246 | if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && |
247 | link_training_settings.lttpr_mode == LTTPR_MODE_TRANSPARENT) |
248 | dp_fixed_vs_pe_read_lane_adjust( |
249 | link, |
250 | dpcd_lane_adjust: link_training_settings.dpcd_lane_settings); |
251 | |
252 | /*get post cursor 2 parameters |
253 | * For DP 1.1a or eariler, this DPCD register's value is 0 |
254 | * For DP 1.2 or later: |
255 | * Bits 1:0 = POST_CURSOR2_LANE0; Bits 3:2 = POST_CURSOR2_LANE1 |
256 | * Bits 5:4 = POST_CURSOR2_LANE2; Bits 7:6 = POST_CURSOR2_LANE3 |
257 | */ |
258 | core_link_read_dpcd( |
259 | link, |
260 | DP_ADJUST_REQUEST_POST_CURSOR2, |
261 | data: &dpcd_post_cursor_2_adjustment, |
262 | size: sizeof(dpcd_post_cursor_2_adjustment)); |
263 | |
264 | /* translate request */ |
265 | switch (dpcd_test_pattern.bits.PATTERN) { |
266 | case PHY_TEST_PATTERN_D10_2: |
267 | test_pattern = DP_TEST_PATTERN_D102; |
268 | break; |
269 | case PHY_TEST_PATTERN_SYMBOL_ERROR: |
270 | test_pattern = DP_TEST_PATTERN_SYMBOL_ERROR; |
271 | break; |
272 | case PHY_TEST_PATTERN_PRBS7: |
273 | test_pattern = DP_TEST_PATTERN_PRBS7; |
274 | break; |
275 | case PHY_TEST_PATTERN_80BIT_CUSTOM: |
276 | test_pattern = DP_TEST_PATTERN_80BIT_CUSTOM; |
277 | break; |
278 | case PHY_TEST_PATTERN_CP2520_1: |
279 | /* CP2520 pattern is unstable, temporarily use TPS4 instead */ |
280 | test_pattern = (link->dc->caps.force_dp_tps4_for_cp2520 == 1) ? |
281 | DP_TEST_PATTERN_TRAINING_PATTERN4 : |
282 | DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE; |
283 | break; |
284 | case PHY_TEST_PATTERN_CP2520_2: |
285 | /* CP2520 pattern is unstable, temporarily use TPS4 instead */ |
286 | test_pattern = (link->dc->caps.force_dp_tps4_for_cp2520 == 1) ? |
287 | DP_TEST_PATTERN_TRAINING_PATTERN4 : |
288 | DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE; |
289 | break; |
290 | case PHY_TEST_PATTERN_CP2520_3: |
291 | test_pattern = DP_TEST_PATTERN_TRAINING_PATTERN4; |
292 | break; |
293 | case PHY_TEST_PATTERN_128b_132b_TPS1: |
294 | test_pattern = DP_TEST_PATTERN_128b_132b_TPS1; |
295 | break; |
296 | case PHY_TEST_PATTERN_128b_132b_TPS2: |
297 | test_pattern = DP_TEST_PATTERN_128b_132b_TPS2; |
298 | break; |
299 | case PHY_TEST_PATTERN_PRBS9: |
300 | test_pattern = DP_TEST_PATTERN_PRBS9; |
301 | break; |
302 | case PHY_TEST_PATTERN_PRBS11: |
303 | test_pattern = DP_TEST_PATTERN_PRBS11; |
304 | break; |
305 | case PHY_TEST_PATTERN_PRBS15: |
306 | test_pattern = DP_TEST_PATTERN_PRBS15; |
307 | break; |
308 | case PHY_TEST_PATTERN_PRBS23: |
309 | test_pattern = DP_TEST_PATTERN_PRBS23; |
310 | break; |
311 | case PHY_TEST_PATTERN_PRBS31: |
312 | test_pattern = DP_TEST_PATTERN_PRBS31; |
313 | break; |
314 | case PHY_TEST_PATTERN_264BIT_CUSTOM: |
315 | test_pattern = DP_TEST_PATTERN_264BIT_CUSTOM; |
316 | break; |
317 | case PHY_TEST_PATTERN_SQUARE: |
318 | test_pattern = DP_TEST_PATTERN_SQUARE; |
319 | break; |
320 | case PHY_TEST_PATTERN_SQUARE_PRESHOOT_DISABLED: |
321 | test_pattern = DP_TEST_PATTERN_SQUARE_PRESHOOT_DISABLED; |
322 | no_preshoot = 1; |
323 | break; |
324 | case PHY_TEST_PATTERN_SQUARE_DEEMPHASIS_DISABLED: |
325 | test_pattern = DP_TEST_PATTERN_SQUARE_DEEMPHASIS_DISABLED; |
326 | no_deemphasis = 1; |
327 | break; |
328 | case PHY_TEST_PATTERN_SQUARE_PRESHOOT_DEEMPHASIS_DISABLED: |
329 | test_pattern = DP_TEST_PATTERN_SQUARE_PRESHOOT_DEEMPHASIS_DISABLED; |
330 | no_preshoot = 1; |
331 | no_deemphasis = 1; |
332 | break; |
333 | default: |
334 | test_pattern = DP_TEST_PATTERN_VIDEO_MODE; |
335 | break; |
336 | } |
337 | |
338 | if (test_pattern == DP_TEST_PATTERN_80BIT_CUSTOM) { |
339 | test_pattern_size = (DP_TEST_80BIT_CUSTOM_PATTERN_79_72 - |
340 | DP_TEST_80BIT_CUSTOM_PATTERN_7_0) + 1; |
341 | core_link_read_dpcd( |
342 | link, |
343 | DP_TEST_80BIT_CUSTOM_PATTERN_7_0, |
344 | data: test_pattern_buffer, |
345 | size: test_pattern_size); |
346 | } |
347 | |
348 | if (IS_DP_PHY_SQUARE_PATTERN(test_pattern)) { |
349 | test_pattern_size = 1; // Square pattern data is 1 byte (DP spec) |
350 | core_link_read_dpcd( |
351 | link, |
352 | DP_PHY_SQUARE_PATTERN, |
353 | data: test_pattern_buffer, |
354 | size: test_pattern_size); |
355 | } |
356 | |
357 | if (test_pattern == DP_TEST_PATTERN_264BIT_CUSTOM) { |
358 | test_pattern_size = (DP_TEST_264BIT_CUSTOM_PATTERN_263_256- |
359 | DP_TEST_264BIT_CUSTOM_PATTERN_7_0) + 1; |
360 | core_link_read_dpcd( |
361 | link, |
362 | DP_TEST_264BIT_CUSTOM_PATTERN_7_0, |
363 | data: test_pattern_buffer, |
364 | size: test_pattern_size); |
365 | } |
366 | |
367 | for (lane = 0; lane < |
368 | (unsigned int)(link->cur_link_settings.lane_count); |
369 | lane++) { |
370 | dpcd_lane_adjust.raw = |
371 | dp_get_nibble_at_index(buf: &dpcd_lane_adjustment[0].raw, index: lane); |
372 | if (link_dp_get_encoding_format(link_settings: &link->cur_link_settings) == |
373 | DP_8b_10b_ENCODING) { |
374 | link_training_settings.hw_lane_settings[lane].VOLTAGE_SWING = |
375 | (enum dc_voltage_swing) |
376 | (dpcd_lane_adjust.bits.VOLTAGE_SWING_LANE); |
377 | link_training_settings.hw_lane_settings[lane].PRE_EMPHASIS = |
378 | (enum dc_pre_emphasis) |
379 | (dpcd_lane_adjust.bits.PRE_EMPHASIS_LANE); |
380 | link_training_settings.hw_lane_settings[lane].POST_CURSOR2 = |
381 | (enum dc_post_cursor2) |
382 | ((dpcd_post_cursor_2_adjustment >> (lane * 2)) & 0x03); |
383 | } else if (link_dp_get_encoding_format(link_settings: &link->cur_link_settings) == |
384 | DP_128b_132b_ENCODING) { |
385 | link_training_settings.hw_lane_settings[lane].FFE_PRESET.settings.level = |
386 | dpcd_lane_adjust.tx_ffe.PRESET_VALUE; |
387 | link_training_settings.hw_lane_settings[lane].FFE_PRESET.settings.no_preshoot = no_preshoot; |
388 | link_training_settings.hw_lane_settings[lane].FFE_PRESET.settings.no_deemphasis = no_deemphasis; |
389 | } |
390 | } |
391 | |
392 | dp_hw_to_dpcd_lane_settings(lt_settings: &link_training_settings, |
393 | hw_lane_settings: link_training_settings.hw_lane_settings, |
394 | dpcd_lane_settings: link_training_settings.dpcd_lane_settings); |
395 | /*Usage: Measure DP physical lane signal |
396 | * by DP SI test equipment automatically. |
397 | * PHY test pattern request is generated by equipment via HPD interrupt. |
398 | * HPD needs to be active all the time. HPD should be active |
399 | * all the time. Do not touch it. |
400 | * forward request to DS |
401 | */ |
402 | dp_set_test_pattern( |
403 | link, |
404 | test_pattern, |
405 | test_pattern_color_space: DP_TEST_PATTERN_COLOR_SPACE_UNDEFINED, |
406 | p_link_settings: &link_training_settings, |
407 | p_custom_pattern: test_pattern_buffer, |
408 | cust_pattern_size: test_pattern_size); |
409 | } |
410 | |
411 | static void set_crtc_test_pattern(struct dc_link *link, |
412 | struct pipe_ctx *pipe_ctx, |
413 | enum dp_test_pattern test_pattern, |
414 | enum dp_test_pattern_color_space test_pattern_color_space) |
415 | { |
416 | enum controller_dp_test_pattern controller_test_pattern; |
417 | enum dc_color_depth color_depth = pipe_ctx-> |
418 | stream->timing.display_color_depth; |
419 | struct bit_depth_reduction_params params; |
420 | struct output_pixel_processor *opp = pipe_ctx->stream_res.opp; |
421 | struct pipe_ctx *odm_pipe; |
422 | struct test_pattern_params *tp_params; |
423 | |
424 | memset(¶ms, 0, sizeof(params)); |
425 | |
426 | resource_build_test_pattern_params(res_ctx: &link->dc->current_state->res_ctx, |
427 | pipe_ctx); |
428 | controller_test_pattern = pipe_ctx->stream_res.test_pattern_params.test_pattern; |
429 | |
430 | switch (test_pattern) { |
431 | case DP_TEST_PATTERN_COLOR_SQUARES: |
432 | case DP_TEST_PATTERN_COLOR_SQUARES_CEA: |
433 | case DP_TEST_PATTERN_VERTICAL_BARS: |
434 | case DP_TEST_PATTERN_HORIZONTAL_BARS: |
435 | case DP_TEST_PATTERN_COLOR_RAMP: |
436 | { |
437 | /* disable bit depth reduction */ |
438 | pipe_ctx->stream->bit_depth_params = params; |
439 | if (pipe_ctx->stream_res.tg->funcs->set_test_pattern) { |
440 | opp->funcs->opp_program_bit_depth_reduction(opp, ¶ms); |
441 | pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg, |
442 | controller_test_pattern, color_depth); |
443 | } else if (link->dc->hwss.set_disp_pattern_generator) { |
444 | enum controller_dp_color_space controller_color_space; |
445 | struct output_pixel_processor *odm_opp; |
446 | |
447 | controller_color_space = pipe_ctx->stream_res.test_pattern_params.color_space; |
448 | |
449 | if (controller_color_space == CONTROLLER_DP_COLOR_SPACE_UDEFINED) { |
450 | DC_LOG_ERROR("%s: Color space must be defined for test pattern" , __func__); |
451 | ASSERT(0); |
452 | } |
453 | |
454 | odm_pipe = pipe_ctx; |
455 | while (odm_pipe) { |
456 | tp_params = &odm_pipe->stream_res.test_pattern_params; |
457 | odm_opp = odm_pipe->stream_res.opp; |
458 | odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms); |
459 | link->dc->hwss.set_disp_pattern_generator(link->dc, |
460 | odm_pipe, |
461 | tp_params->test_pattern, |
462 | tp_params->color_space, |
463 | tp_params->color_depth, |
464 | NULL, |
465 | tp_params->width, |
466 | tp_params->height, |
467 | tp_params->offset); |
468 | odm_pipe = odm_pipe->next_odm_pipe; |
469 | } |
470 | } |
471 | } |
472 | break; |
473 | case DP_TEST_PATTERN_VIDEO_MODE: |
474 | { |
475 | /* restore bitdepth reduction */ |
476 | resource_build_bit_depth_reduction_params(stream: pipe_ctx->stream, fmt_bit_depth: ¶ms); |
477 | pipe_ctx->stream->bit_depth_params = params; |
478 | if (pipe_ctx->stream_res.tg->funcs->set_test_pattern) { |
479 | opp->funcs->opp_program_bit_depth_reduction(opp, ¶ms); |
480 | pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg, |
481 | CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, |
482 | color_depth); |
483 | } else if (link->dc->hwss.set_disp_pattern_generator) { |
484 | struct output_pixel_processor *odm_opp; |
485 | |
486 | odm_pipe = pipe_ctx; |
487 | while (odm_pipe) { |
488 | tp_params = &odm_pipe->stream_res.test_pattern_params; |
489 | odm_opp = odm_pipe->stream_res.opp; |
490 | odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms); |
491 | link->dc->hwss.set_disp_pattern_generator(link->dc, |
492 | odm_pipe, |
493 | tp_params->test_pattern, |
494 | tp_params->color_space, |
495 | tp_params->color_depth, |
496 | NULL, |
497 | tp_params->width, |
498 | tp_params->height, |
499 | tp_params->offset); |
500 | odm_pipe = odm_pipe->next_odm_pipe; |
501 | } |
502 | } |
503 | } |
504 | break; |
505 | |
506 | default: |
507 | break; |
508 | } |
509 | } |
510 | |
511 | void dp_handle_automated_test(struct dc_link *link) |
512 | { |
513 | union test_request test_request; |
514 | union test_response test_response; |
515 | |
516 | memset(&test_request, 0, sizeof(test_request)); |
517 | memset(&test_response, 0, sizeof(test_response)); |
518 | |
519 | core_link_read_dpcd( |
520 | link, |
521 | DP_TEST_REQUEST, |
522 | data: &test_request.raw, |
523 | size: sizeof(union test_request)); |
524 | if (test_request.bits.LINK_TRAINING) { |
525 | /* ACK first to let DP RX test box monitor LT sequence */ |
526 | test_response.bits.ACK = 1; |
527 | core_link_write_dpcd( |
528 | link, |
529 | DP_TEST_RESPONSE, |
530 | data: &test_response.raw, |
531 | size: sizeof(test_response)); |
532 | dp_test_send_link_training(link); |
533 | /* no acknowledge request is needed again */ |
534 | test_response.bits.ACK = 0; |
535 | } |
536 | if (test_request.bits.LINK_TEST_PATTRN) { |
537 | union test_misc dpcd_test_params; |
538 | union link_test_pattern dpcd_test_pattern; |
539 | |
540 | memset(&dpcd_test_pattern, 0, sizeof(dpcd_test_pattern)); |
541 | memset(&dpcd_test_params, 0, sizeof(dpcd_test_params)); |
542 | |
543 | /* get link test pattern and pattern parameters */ |
544 | core_link_read_dpcd( |
545 | link, |
546 | DP_TEST_PATTERN, |
547 | data: &dpcd_test_pattern.raw, |
548 | size: sizeof(dpcd_test_pattern)); |
549 | core_link_read_dpcd( |
550 | link, |
551 | DP_TEST_MISC0, |
552 | data: &dpcd_test_params.raw, |
553 | size: sizeof(dpcd_test_params)); |
554 | test_response.bits.ACK = dm_helpers_dp_handle_test_pattern_request(ctx: link->ctx, link, |
555 | dpcd_test_pattern, dpcd_test_params) ? 1 : 0; |
556 | } |
557 | |
558 | if (test_request.bits.AUDIO_TEST_PATTERN) { |
559 | dp_test_get_audio_test_data(link, disable_video: test_request.bits.TEST_AUDIO_DISABLED_VIDEO); |
560 | test_response.bits.ACK = 1; |
561 | } |
562 | |
563 | if (test_request.bits.PHY_TEST_PATTERN) { |
564 | dp_test_send_phy_test_pattern(link); |
565 | test_response.bits.ACK = 1; |
566 | } |
567 | |
568 | /* send request acknowledgment */ |
569 | if (test_response.bits.ACK) |
570 | core_link_write_dpcd( |
571 | link, |
572 | DP_TEST_RESPONSE, |
573 | data: &test_response.raw, |
574 | size: sizeof(test_response)); |
575 | } |
576 | |
577 | bool dp_set_test_pattern( |
578 | struct dc_link *link, |
579 | enum dp_test_pattern test_pattern, |
580 | enum dp_test_pattern_color_space test_pattern_color_space, |
581 | const struct link_training_settings *p_link_settings, |
582 | const unsigned char *p_custom_pattern, |
583 | unsigned int cust_pattern_size) |
584 | { |
585 | const struct link_hwss *link_hwss; |
586 | struct pipe_ctx *pipes = link->dc->current_state->res_ctx.pipe_ctx; |
587 | struct pipe_ctx *pipe_ctx = NULL; |
588 | unsigned int lane; |
589 | unsigned int i; |
590 | unsigned char link_qual_pattern[LANE_COUNT_DP_MAX] = {0}; |
591 | union dpcd_training_pattern training_pattern; |
592 | enum dpcd_phy_test_patterns pattern; |
593 | |
594 | memset(&training_pattern, 0, sizeof(training_pattern)); |
595 | |
596 | for (i = 0; i < MAX_PIPES; i++) { |
597 | if (pipes[i].stream == NULL) |
598 | continue; |
599 | |
600 | if (resource_is_pipe_type(pipe_ctx: &pipes[i], type: OTG_MASTER) && |
601 | pipes[i].stream->link == link) { |
602 | pipe_ctx = &pipes[i]; |
603 | break; |
604 | } |
605 | } |
606 | |
607 | if (pipe_ctx == NULL) |
608 | return false; |
609 | |
610 | link->pending_test_pattern = test_pattern; |
611 | |
612 | /* Reset CRTC Test Pattern if it is currently running and request is VideoMode */ |
613 | if (link->test_pattern_enabled && test_pattern == |
614 | DP_TEST_PATTERN_VIDEO_MODE) { |
615 | /* Set CRTC Test Pattern */ |
616 | set_crtc_test_pattern(link, pipe_ctx, test_pattern, test_pattern_color_space); |
617 | dp_set_hw_test_pattern(link, link_res: &pipe_ctx->link_res, test_pattern, |
618 | custom_pattern: (uint8_t *)p_custom_pattern, |
619 | custom_pattern_size: (uint32_t)cust_pattern_size); |
620 | |
621 | /* Unblank Stream */ |
622 | link->dc->hwss.unblank_stream( |
623 | pipe_ctx, |
624 | &link->verified_link_cap); |
625 | /* TODO:m_pHwss->MuteAudioEndpoint |
626 | * (pPathMode->pDisplayPath, false); |
627 | */ |
628 | |
629 | /* Reset Test Pattern state */ |
630 | link->test_pattern_enabled = false; |
631 | link->current_test_pattern = test_pattern; |
632 | link->pending_test_pattern = DP_TEST_PATTERN_UNSUPPORTED; |
633 | |
634 | return true; |
635 | } |
636 | |
637 | /* Check for PHY Test Patterns */ |
638 | if (IS_DP_PHY_PATTERN(test_pattern)) { |
639 | /* Set DPCD Lane Settings before running test pattern */ |
640 | if (p_link_settings != NULL) { |
641 | if ((link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) && |
642 | p_link_settings->lttpr_mode == LTTPR_MODE_TRANSPARENT) { |
643 | dp_fixed_vs_pe_set_retimer_lane_settings( |
644 | link, |
645 | dpcd_lane_adjust: p_link_settings->dpcd_lane_settings, |
646 | lane_count: p_link_settings->link_settings.lane_count); |
647 | } else { |
648 | dp_set_hw_lane_settings(link, link_res: &pipe_ctx->link_res, link_settings: p_link_settings, offset: DPRX); |
649 | } |
650 | dpcd_set_lane_settings(link, link_training_setting: p_link_settings, offset: DPRX); |
651 | } |
652 | |
653 | /* Blank stream if running test pattern */ |
654 | if (test_pattern != DP_TEST_PATTERN_VIDEO_MODE) { |
655 | /*TODO: |
656 | * m_pHwss-> |
657 | * MuteAudioEndpoint(pPathMode->pDisplayPath, true); |
658 | */ |
659 | /* Blank stream */ |
660 | link->dc->hwss.blank_stream(pipe_ctx); |
661 | } |
662 | |
663 | dp_set_hw_test_pattern(link, link_res: &pipe_ctx->link_res, test_pattern, |
664 | custom_pattern: (uint8_t *)p_custom_pattern, |
665 | custom_pattern_size: (uint32_t)cust_pattern_size); |
666 | |
667 | if (test_pattern != DP_TEST_PATTERN_VIDEO_MODE) { |
668 | /* Set Test Pattern state */ |
669 | link->test_pattern_enabled = true; |
670 | link->current_test_pattern = test_pattern; |
671 | link->pending_test_pattern = DP_TEST_PATTERN_UNSUPPORTED; |
672 | if (p_link_settings != NULL) |
673 | dpcd_set_link_settings(link, |
674 | lt_settings: p_link_settings); |
675 | } |
676 | |
677 | switch (test_pattern) { |
678 | case DP_TEST_PATTERN_VIDEO_MODE: |
679 | pattern = PHY_TEST_PATTERN_NONE; |
680 | break; |
681 | case DP_TEST_PATTERN_D102: |
682 | pattern = PHY_TEST_PATTERN_D10_2; |
683 | break; |
684 | case DP_TEST_PATTERN_SYMBOL_ERROR: |
685 | pattern = PHY_TEST_PATTERN_SYMBOL_ERROR; |
686 | break; |
687 | case DP_TEST_PATTERN_PRBS7: |
688 | pattern = PHY_TEST_PATTERN_PRBS7; |
689 | break; |
690 | case DP_TEST_PATTERN_80BIT_CUSTOM: |
691 | pattern = PHY_TEST_PATTERN_80BIT_CUSTOM; |
692 | break; |
693 | case DP_TEST_PATTERN_CP2520_1: |
694 | pattern = PHY_TEST_PATTERN_CP2520_1; |
695 | break; |
696 | case DP_TEST_PATTERN_CP2520_2: |
697 | pattern = PHY_TEST_PATTERN_CP2520_2; |
698 | break; |
699 | case DP_TEST_PATTERN_CP2520_3: |
700 | pattern = PHY_TEST_PATTERN_CP2520_3; |
701 | break; |
702 | case DP_TEST_PATTERN_128b_132b_TPS1: |
703 | pattern = PHY_TEST_PATTERN_128b_132b_TPS1; |
704 | break; |
705 | case DP_TEST_PATTERN_128b_132b_TPS2: |
706 | pattern = PHY_TEST_PATTERN_128b_132b_TPS2; |
707 | break; |
708 | case DP_TEST_PATTERN_PRBS9: |
709 | pattern = PHY_TEST_PATTERN_PRBS9; |
710 | break; |
711 | case DP_TEST_PATTERN_PRBS11: |
712 | pattern = PHY_TEST_PATTERN_PRBS11; |
713 | break; |
714 | case DP_TEST_PATTERN_PRBS15: |
715 | pattern = PHY_TEST_PATTERN_PRBS15; |
716 | break; |
717 | case DP_TEST_PATTERN_PRBS23: |
718 | pattern = PHY_TEST_PATTERN_PRBS23; |
719 | break; |
720 | case DP_TEST_PATTERN_PRBS31: |
721 | pattern = PHY_TEST_PATTERN_PRBS31; |
722 | break; |
723 | case DP_TEST_PATTERN_264BIT_CUSTOM: |
724 | pattern = PHY_TEST_PATTERN_264BIT_CUSTOM; |
725 | break; |
726 | case DP_TEST_PATTERN_SQUARE: |
727 | pattern = PHY_TEST_PATTERN_SQUARE; |
728 | break; |
729 | case DP_TEST_PATTERN_SQUARE_PRESHOOT_DISABLED: |
730 | pattern = PHY_TEST_PATTERN_SQUARE_PRESHOOT_DISABLED; |
731 | break; |
732 | case DP_TEST_PATTERN_SQUARE_DEEMPHASIS_DISABLED: |
733 | pattern = PHY_TEST_PATTERN_SQUARE_DEEMPHASIS_DISABLED; |
734 | break; |
735 | case DP_TEST_PATTERN_SQUARE_PRESHOOT_DEEMPHASIS_DISABLED: |
736 | pattern = PHY_TEST_PATTERN_SQUARE_PRESHOOT_DEEMPHASIS_DISABLED; |
737 | break; |
738 | default: |
739 | return false; |
740 | } |
741 | |
742 | if (test_pattern == DP_TEST_PATTERN_VIDEO_MODE |
743 | /*TODO:&& !pPathMode->pDisplayPath->IsTargetPoweredOn()*/) |
744 | return false; |
745 | |
746 | if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_12) { |
747 | if (IS_DP_PHY_SQUARE_PATTERN(test_pattern)) |
748 | core_link_write_dpcd(link, |
749 | DP_LINK_SQUARE_PATTERN, |
750 | data: p_custom_pattern, |
751 | size: 1); |
752 | |
753 | /* tell receiver that we are sending qualification |
754 | * pattern DP 1.2 or later - DP receiver's link quality |
755 | * pattern is set using DPCD LINK_QUAL_LANEx_SET |
756 | * register (0x10B~0x10E)\ |
757 | */ |
758 | for (lane = 0; lane < LANE_COUNT_DP_MAX; lane++) |
759 | link_qual_pattern[lane] = |
760 | (unsigned char)(pattern); |
761 | |
762 | core_link_write_dpcd(link, |
763 | DP_LINK_QUAL_LANE0_SET, |
764 | data: link_qual_pattern, |
765 | size: sizeof(link_qual_pattern)); |
766 | } else if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_10 || |
767 | link->dpcd_caps.dpcd_rev.raw == 0) { |
768 | /* tell receiver that we are sending qualification |
769 | * pattern DP 1.1a or earlier - DP receiver's link |
770 | * quality pattern is set using |
771 | * DPCD TRAINING_PATTERN_SET -> LINK_QUAL_PATTERN_SET |
772 | * register (0x102). We will use v_1.3 when we are |
773 | * setting test pattern for DP 1.1. |
774 | */ |
775 | core_link_read_dpcd(link, DP_TRAINING_PATTERN_SET, |
776 | data: &training_pattern.raw, |
777 | size: sizeof(training_pattern)); |
778 | training_pattern.v1_3.LINK_QUAL_PATTERN_SET = pattern; |
779 | core_link_write_dpcd(link, DP_TRAINING_PATTERN_SET, |
780 | data: &training_pattern.raw, |
781 | size: sizeof(training_pattern)); |
782 | } |
783 | } else { |
784 | enum dc_color_space color_space = COLOR_SPACE_UNKNOWN; |
785 | |
786 | switch (test_pattern_color_space) { |
787 | case DP_TEST_PATTERN_COLOR_SPACE_RGB: |
788 | color_space = COLOR_SPACE_SRGB; |
789 | if (test_pattern == DP_TEST_PATTERN_COLOR_SQUARES_CEA) |
790 | color_space = COLOR_SPACE_SRGB_LIMITED; |
791 | break; |
792 | |
793 | case DP_TEST_PATTERN_COLOR_SPACE_YCBCR601: |
794 | color_space = COLOR_SPACE_YCBCR601; |
795 | if (test_pattern == DP_TEST_PATTERN_COLOR_SQUARES_CEA) |
796 | color_space = COLOR_SPACE_YCBCR601_LIMITED; |
797 | break; |
798 | case DP_TEST_PATTERN_COLOR_SPACE_YCBCR709: |
799 | color_space = COLOR_SPACE_YCBCR709; |
800 | if (test_pattern == DP_TEST_PATTERN_COLOR_SQUARES_CEA) |
801 | color_space = COLOR_SPACE_YCBCR709_LIMITED; |
802 | break; |
803 | default: |
804 | break; |
805 | } |
806 | |
807 | if (pipe_ctx->stream_res.tg->funcs->lock_doublebuffer_enable) { |
808 | if (pipe_ctx->stream && should_use_dmub_lock(link: pipe_ctx->stream->link)) { |
809 | union dmub_hw_lock_flags hw_locks = { 0 }; |
810 | struct dmub_hw_lock_inst_flags inst_flags = { 0 }; |
811 | |
812 | hw_locks.bits.lock_dig = 1; |
813 | inst_flags.dig_inst = pipe_ctx->stream_res.tg->inst; |
814 | |
815 | dmub_hw_lock_mgr_cmd(dmub_srv: link->ctx->dmub_srv, |
816 | lock: true, |
817 | hw_locks: &hw_locks, |
818 | inst_flags: &inst_flags); |
819 | } else |
820 | pipe_ctx->stream_res.tg->funcs->lock_doublebuffer_enable( |
821 | pipe_ctx->stream_res.tg); |
822 | } |
823 | |
824 | pipe_ctx->stream_res.tg->funcs->lock(pipe_ctx->stream_res.tg); |
825 | /* update MSA to requested color space */ |
826 | link_hwss = get_link_hwss(link, link_res: &pipe_ctx->link_res); |
827 | pipe_ctx->stream->output_color_space = color_space; |
828 | link_hwss->setup_stream_attribute(pipe_ctx); |
829 | |
830 | if (pipe_ctx->stream->use_vsc_sdp_for_colorimetry) { |
831 | if (test_pattern == DP_TEST_PATTERN_COLOR_SQUARES_CEA) |
832 | pipe_ctx->stream->vsc_infopacket.sb[17] |= (1 << 7); // sb17 bit 7 Dynamic Range: 0 = VESA range, 1 = CTA range |
833 | else |
834 | pipe_ctx->stream->vsc_infopacket.sb[17] &= ~(1 << 7); |
835 | |
836 | if (color_space == COLOR_SPACE_YCBCR601_LIMITED) |
837 | pipe_ctx->stream->vsc_infopacket.sb[16] &= 0xf0; |
838 | else if (color_space == COLOR_SPACE_YCBCR709_LIMITED) |
839 | pipe_ctx->stream->vsc_infopacket.sb[16] |= 1; |
840 | |
841 | resource_build_info_frame(pipe_ctx); |
842 | link->dc->hwss.update_info_frame(pipe_ctx); |
843 | } |
844 | |
845 | /* CRTC Patterns */ |
846 | set_crtc_test_pattern(link, pipe_ctx, test_pattern, test_pattern_color_space); |
847 | pipe_ctx->stream_res.tg->funcs->unlock(pipe_ctx->stream_res.tg); |
848 | pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, |
849 | CRTC_STATE_VACTIVE); |
850 | pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, |
851 | CRTC_STATE_VBLANK); |
852 | pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, |
853 | CRTC_STATE_VACTIVE); |
854 | |
855 | if (pipe_ctx->stream_res.tg->funcs->lock_doublebuffer_disable) { |
856 | if (pipe_ctx->stream && should_use_dmub_lock(link: pipe_ctx->stream->link)) { |
857 | union dmub_hw_lock_flags hw_locks = { 0 }; |
858 | struct dmub_hw_lock_inst_flags inst_flags = { 0 }; |
859 | |
860 | hw_locks.bits.lock_dig = 1; |
861 | inst_flags.dig_inst = pipe_ctx->stream_res.tg->inst; |
862 | |
863 | dmub_hw_lock_mgr_cmd(dmub_srv: link->ctx->dmub_srv, |
864 | lock: false, |
865 | hw_locks: &hw_locks, |
866 | inst_flags: &inst_flags); |
867 | } else |
868 | pipe_ctx->stream_res.tg->funcs->lock_doublebuffer_disable( |
869 | pipe_ctx->stream_res.tg); |
870 | } |
871 | |
872 | /* Set Test Pattern state */ |
873 | link->test_pattern_enabled = true; |
874 | link->current_test_pattern = test_pattern; |
875 | link->pending_test_pattern = DP_TEST_PATTERN_UNSUPPORTED; |
876 | } |
877 | |
878 | return true; |
879 | } |
880 | |
881 | void dp_set_preferred_link_settings(struct dc *dc, |
882 | struct dc_link_settings *link_setting, |
883 | struct dc_link *link) |
884 | { |
885 | int i; |
886 | struct pipe_ctx *pipe; |
887 | struct dc_stream_state *link_stream; |
888 | struct dc_link_settings store_settings = *link_setting; |
889 | |
890 | link->preferred_link_setting = store_settings; |
891 | |
892 | /* Retrain with preferred link settings only relevant for |
893 | * DP signal type |
894 | * Check for non-DP signal or if passive dongle present |
895 | */ |
896 | if (!dc_is_dp_signal(signal: link->connector_signal) || |
897 | link->dongle_max_pix_clk > 0) |
898 | return; |
899 | |
900 | for (i = 0; i < MAX_PIPES; i++) { |
901 | pipe = &dc->current_state->res_ctx.pipe_ctx[i]; |
902 | if (pipe->stream && pipe->stream->link) { |
903 | if (pipe->stream->link == link) { |
904 | link_stream = pipe->stream; |
905 | break; |
906 | } |
907 | } |
908 | } |
909 | |
910 | /* Stream not found */ |
911 | if (i == MAX_PIPES) |
912 | return; |
913 | |
914 | /* Cannot retrain link if backend is off */ |
915 | if (link_stream->dpms_off) |
916 | return; |
917 | |
918 | if (link_decide_link_settings(stream: link_stream, link_setting: &store_settings)) |
919 | dp_retrain_link_dp_test(link, link_setting: &store_settings, skip_video_pattern: false); |
920 | } |
921 | |
922 | void dp_set_preferred_training_settings(struct dc *dc, |
923 | struct dc_link_settings *link_setting, |
924 | struct dc_link_training_overrides *lt_overrides, |
925 | struct dc_link *link, |
926 | bool skip_immediate_retrain) |
927 | { |
928 | if (lt_overrides != NULL) |
929 | link->preferred_training_settings = *lt_overrides; |
930 | else |
931 | memset(&link->preferred_training_settings, 0, sizeof(link->preferred_training_settings)); |
932 | |
933 | if (link_setting != NULL) { |
934 | link->preferred_link_setting = *link_setting; |
935 | } else { |
936 | link->preferred_link_setting.lane_count = LANE_COUNT_UNKNOWN; |
937 | link->preferred_link_setting.link_rate = LINK_RATE_UNKNOWN; |
938 | } |
939 | |
940 | if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && |
941 | link->type == dc_connection_mst_branch) |
942 | dm_helpers_dp_mst_update_branch_bandwidth(ctx: dc->ctx, link); |
943 | |
944 | /* Retrain now, or wait until next stream update to apply */ |
945 | if (skip_immediate_retrain == false) |
946 | dp_set_preferred_link_settings(dc, link_setting: &link->preferred_link_setting, link); |
947 | } |
948 | |