]>
Commit | Line | Data |
---|---|---|
4562236b HW |
1 | /* |
2 | * Copyright 2012-15 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 | ||
841d0023 SR |
26 | #include <linux/slab.h> |
27 | ||
4562236b | 28 | #include "dm_services.h" |
cc57306f | 29 | #include "atom.h" |
4562236b HW |
30 | #include "dm_helpers.h" |
31 | #include "dc.h" | |
4562236b HW |
32 | #include "grph_object_id.h" |
33 | #include "gpio_service_interface.h" | |
34 | #include "core_status.h" | |
35 | #include "dc_link_dp.h" | |
36 | #include "dc_link_ddc.h" | |
37 | #include "link_hwss.h" | |
7f93c1de | 38 | #include "opp.h" |
fb3466a4 | 39 | |
4562236b HW |
40 | #include "link_encoder.h" |
41 | #include "hw_sequencer.h" | |
42 | #include "resource.h" | |
6728b30c | 43 | #include "abm.h" |
4562236b | 44 | #include "fixed31_32.h" |
eaca91ee | 45 | #include "dpcd_defs.h" |
3548f073 | 46 | #include "dmcu.h" |
dc88b4a6 | 47 | #include "hw/clk_mgr.h" |
4c1a1335 | 48 | #include "../dce/dmub_psr.h" |
4562236b | 49 | |
5d4b05dd BL |
50 | #define DC_LOGGER_INIT(logger) |
51 | ||
4562236b | 52 | |
4562236b | 53 | #define LINK_INFO(...) \ |
1296423b | 54 | DC_LOG_HW_HOTPLUG( \ |
4562236b HW |
55 | __VA_ARGS__) |
56 | ||
2f14bc89 CL |
57 | #define RETIMER_REDRIVER_INFO(...) \ |
58 | DC_LOG_RETIMER_REDRIVER( \ | |
59 | __VA_ARGS__) | |
4562236b HW |
60 | /******************************************************************************* |
61 | * Private structures | |
62 | ******************************************************************************/ | |
63 | ||
64 | enum { | |
824474ba BL |
65 | PEAK_FACTOR_X1000 = 1006, |
66 | /* | |
67 | * Some receivers fail to train on first try and are good | |
68 | * on subsequent tries. 2 retries should be plenty. If we | |
69 | * don't have a successful training then we don't expect to | |
70 | * ever get one. | |
71 | */ | |
72 | LINK_TRAINING_MAX_VERIFY_RETRY = 2 | |
4562236b HW |
73 | }; |
74 | ||
75 | /******************************************************************************* | |
76 | * Private functions | |
77 | ******************************************************************************/ | |
d9e32672 | 78 | static void dc_link_destruct(struct dc_link *link) |
4562236b HW |
79 | { |
80 | int i; | |
81 | ||
ac627caf | 82 | if (link->hpd_gpio != NULL) { |
ac627caf CH |
83 | dal_gpio_destroy_irq(&link->hpd_gpio); |
84 | link->hpd_gpio = NULL; | |
85 | } | |
86 | ||
d0778ebf HW |
87 | if (link->ddc) |
88 | dal_ddc_service_destroy(&link->ddc); | |
4562236b HW |
89 | |
90 | if(link->link_enc) | |
91 | link->link_enc->funcs->destroy(&link->link_enc); | |
92 | ||
d0778ebf HW |
93 | if (link->local_sink) |
94 | dc_sink_release(link->local_sink); | |
4562236b | 95 | |
d0778ebf HW |
96 | for (i = 0; i < link->sink_count; ++i) |
97 | dc_sink_release(link->remote_sinks[i]); | |
4562236b HW |
98 | } |
99 | ||
87401969 AJ |
100 | struct gpio *get_hpd_gpio(struct dc_bios *dcb, |
101 | struct graphics_object_id link_id, | |
102 | struct gpio_service *gpio_service) | |
4562236b HW |
103 | { |
104 | enum bp_result bp_result; | |
4562236b HW |
105 | struct graphics_object_hpd_info hpd_info; |
106 | struct gpio_pin_info pin_info; | |
107 | ||
87401969 | 108 | if (dcb->funcs->get_hpd_info(dcb, link_id, &hpd_info) != BP_RESULT_OK) |
4562236b HW |
109 | return NULL; |
110 | ||
111 | bp_result = dcb->funcs->get_gpio_pin_info(dcb, | |
112 | hpd_info.hpd_int_gpio_uid, &pin_info); | |
113 | ||
114 | if (bp_result != BP_RESULT_OK) { | |
115 | ASSERT(bp_result == BP_RESULT_NORECORD); | |
116 | return NULL; | |
117 | } | |
118 | ||
119 | return dal_gpio_service_create_irq( | |
87401969 | 120 | gpio_service, |
4562236b HW |
121 | pin_info.offset, |
122 | pin_info.mask); | |
123 | } | |
124 | ||
125 | /* | |
126 | * Function: program_hpd_filter | |
127 | * | |
128 | * @brief | |
129 | * Programs HPD filter on associated HPD line | |
130 | * | |
131 | * @param [in] delay_on_connect_in_ms: Connect filter timeout | |
132 | * @param [in] delay_on_disconnect_in_ms: Disconnect filter timeout | |
133 | * | |
134 | * @return | |
135 | * true on success, false otherwise | |
136 | */ | |
137 | static bool program_hpd_filter( | |
b0c4e977 | 138 | const struct dc_link *link) |
4562236b HW |
139 | { |
140 | bool result = false; | |
141 | ||
142 | struct gpio *hpd; | |
143 | ||
144 | int delay_on_connect_in_ms = 0; | |
145 | int delay_on_disconnect_in_ms = 0; | |
146 | ||
9a6995ce JB |
147 | if (link->is_hpd_filter_disabled) |
148 | return false; | |
4562236b | 149 | /* Verify feature is supported */ |
d0778ebf | 150 | switch (link->connector_signal) { |
4562236b HW |
151 | case SIGNAL_TYPE_DVI_SINGLE_LINK: |
152 | case SIGNAL_TYPE_DVI_DUAL_LINK: | |
153 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
154 | /* Program hpd filter */ | |
155 | delay_on_connect_in_ms = 500; | |
b0c4e977 | 156 | delay_on_disconnect_in_ms = 100; |
4562236b HW |
157 | break; |
158 | case SIGNAL_TYPE_DISPLAY_PORT: | |
159 | case SIGNAL_TYPE_DISPLAY_PORT_MST: | |
160 | /* Program hpd filter to allow DP signal to settle */ | |
161 | /* 500: not able to detect MST <-> SST switch as HPD is low for | |
162 | * only 100ms on DELL U2413 | |
163 | * 0: some passive dongle still show aux mode instead of i2c | |
164 | * 20-50:not enough to hide bouncing HPD with passive dongle. | |
165 | * also see intermittent i2c read issues. | |
166 | */ | |
167 | delay_on_connect_in_ms = 80; | |
168 | delay_on_disconnect_in_ms = 0; | |
169 | break; | |
170 | case SIGNAL_TYPE_LVDS: | |
171 | case SIGNAL_TYPE_EDP: | |
172 | default: | |
173 | /* Don't program hpd filter */ | |
174 | return false; | |
175 | } | |
176 | ||
177 | /* Obtain HPD handle */ | |
87401969 | 178 | hpd = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); |
4562236b HW |
179 | |
180 | if (!hpd) | |
181 | return result; | |
182 | ||
183 | /* Setup HPD filtering */ | |
184 | if (dal_gpio_open(hpd, GPIO_MODE_INTERRUPT) == GPIO_RESULT_OK) { | |
185 | struct gpio_hpd_config config; | |
186 | ||
187 | config.delay_on_connect = delay_on_connect_in_ms; | |
188 | config.delay_on_disconnect = delay_on_disconnect_in_ms; | |
189 | ||
190 | dal_irq_setup_hpd_filter(hpd, &config); | |
191 | ||
192 | dal_gpio_close(hpd); | |
193 | ||
194 | result = true; | |
195 | } else { | |
196 | ASSERT_CRITICAL(false); | |
197 | } | |
198 | ||
199 | /* Release HPD handle */ | |
200 | dal_gpio_destroy_irq(&hpd); | |
201 | ||
202 | return result; | |
203 | } | |
204 | ||
2119aa17 DF |
205 | /** |
206 | * dc_link_detect_sink() - Determine if there is a sink connected | |
207 | * | |
208 | * @type: Returned connection type | |
209 | * Does not detect downstream devices, such as MST sinks | |
210 | * or display connected through active dongles | |
211 | */ | |
fbbdadf2 | 212 | bool dc_link_detect_sink(struct dc_link *link, enum dc_connection_type *type) |
4562236b HW |
213 | { |
214 | uint32_t is_hpd_high = 0; | |
215 | struct gpio *hpd_pin; | |
216 | ||
11c3ee48 AD |
217 | if (link->connector_signal == SIGNAL_TYPE_LVDS) { |
218 | *type = dc_connection_single; | |
219 | return true; | |
220 | } | |
221 | ||
abe882a3 AK |
222 | if (link->connector_signal == SIGNAL_TYPE_EDP) { |
223 | /*in case it is not on*/ | |
224 | link->dc->hwss.edp_power_control(link, true); | |
0a6414e7 | 225 | link->dc->hwss.edp_wait_for_hpd_ready(link, true); |
abe882a3 | 226 | } |
0a6414e7 | 227 | |
4562236b | 228 | /* todo: may need to lock gpio access */ |
87401969 | 229 | hpd_pin = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); |
4562236b HW |
230 | if (hpd_pin == NULL) |
231 | goto hpd_gpio_failure; | |
232 | ||
233 | dal_gpio_open(hpd_pin, GPIO_MODE_INTERRUPT); | |
234 | dal_gpio_get_value(hpd_pin, &is_hpd_high); | |
235 | dal_gpio_close(hpd_pin); | |
236 | dal_gpio_destroy_irq(&hpd_pin); | |
237 | ||
238 | if (is_hpd_high) { | |
239 | *type = dc_connection_single; | |
240 | /* TODO: need to do the actual detection */ | |
241 | } else { | |
242 | *type = dc_connection_none; | |
243 | } | |
244 | ||
245 | return true; | |
246 | ||
247 | hpd_gpio_failure: | |
248 | return false; | |
249 | } | |
250 | ||
44858055 | 251 | static enum ddc_transaction_type get_ddc_transaction_type( |
4562236b HW |
252 | enum signal_type sink_signal) |
253 | { | |
254 | enum ddc_transaction_type transaction_type = DDC_TRANSACTION_TYPE_NONE; | |
255 | ||
256 | switch (sink_signal) { | |
257 | case SIGNAL_TYPE_DVI_SINGLE_LINK: | |
258 | case SIGNAL_TYPE_DVI_DUAL_LINK: | |
259 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
260 | case SIGNAL_TYPE_LVDS: | |
261 | case SIGNAL_TYPE_RGB: | |
262 | transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
263 | break; | |
264 | ||
265 | case SIGNAL_TYPE_DISPLAY_PORT: | |
266 | case SIGNAL_TYPE_EDP: | |
267 | transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; | |
268 | break; | |
269 | ||
270 | case SIGNAL_TYPE_DISPLAY_PORT_MST: | |
271 | /* MST does not use I2COverAux, but there is the | |
272 | * SPECIAL use case for "immediate dwnstrm device | |
273 | * access" (EPR#370830). */ | |
274 | transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; | |
275 | break; | |
276 | ||
277 | default: | |
278 | break; | |
279 | } | |
280 | ||
281 | return transaction_type; | |
282 | } | |
283 | ||
284 | static enum signal_type get_basic_signal_type( | |
285 | struct graphics_object_id encoder, | |
286 | struct graphics_object_id downstream) | |
287 | { | |
288 | if (downstream.type == OBJECT_TYPE_CONNECTOR) { | |
289 | switch (downstream.id) { | |
290 | case CONNECTOR_ID_SINGLE_LINK_DVII: | |
291 | switch (encoder.id) { | |
292 | case ENCODER_ID_INTERNAL_DAC1: | |
293 | case ENCODER_ID_INTERNAL_KLDSCP_DAC1: | |
294 | case ENCODER_ID_INTERNAL_DAC2: | |
295 | case ENCODER_ID_INTERNAL_KLDSCP_DAC2: | |
296 | return SIGNAL_TYPE_RGB; | |
297 | default: | |
298 | return SIGNAL_TYPE_DVI_SINGLE_LINK; | |
299 | } | |
300 | break; | |
301 | case CONNECTOR_ID_DUAL_LINK_DVII: | |
302 | { | |
303 | switch (encoder.id) { | |
304 | case ENCODER_ID_INTERNAL_DAC1: | |
305 | case ENCODER_ID_INTERNAL_KLDSCP_DAC1: | |
306 | case ENCODER_ID_INTERNAL_DAC2: | |
307 | case ENCODER_ID_INTERNAL_KLDSCP_DAC2: | |
308 | return SIGNAL_TYPE_RGB; | |
309 | default: | |
310 | return SIGNAL_TYPE_DVI_DUAL_LINK; | |
311 | } | |
312 | } | |
313 | break; | |
314 | case CONNECTOR_ID_SINGLE_LINK_DVID: | |
315 | return SIGNAL_TYPE_DVI_SINGLE_LINK; | |
316 | case CONNECTOR_ID_DUAL_LINK_DVID: | |
317 | return SIGNAL_TYPE_DVI_DUAL_LINK; | |
318 | case CONNECTOR_ID_VGA: | |
319 | return SIGNAL_TYPE_RGB; | |
320 | case CONNECTOR_ID_HDMI_TYPE_A: | |
321 | return SIGNAL_TYPE_HDMI_TYPE_A; | |
322 | case CONNECTOR_ID_LVDS: | |
323 | return SIGNAL_TYPE_LVDS; | |
324 | case CONNECTOR_ID_DISPLAY_PORT: | |
325 | return SIGNAL_TYPE_DISPLAY_PORT; | |
326 | case CONNECTOR_ID_EDP: | |
327 | return SIGNAL_TYPE_EDP; | |
328 | default: | |
329 | return SIGNAL_TYPE_NONE; | |
330 | } | |
331 | } else if (downstream.type == OBJECT_TYPE_ENCODER) { | |
332 | switch (downstream.id) { | |
333 | case ENCODER_ID_EXTERNAL_NUTMEG: | |
334 | case ENCODER_ID_EXTERNAL_TRAVIS: | |
335 | return SIGNAL_TYPE_DISPLAY_PORT; | |
336 | default: | |
337 | return SIGNAL_TYPE_NONE; | |
338 | } | |
339 | } | |
340 | ||
341 | return SIGNAL_TYPE_NONE; | |
342 | } | |
343 | ||
2119aa17 DF |
344 | /** |
345 | * dc_link_is_dp_sink_present() - Check if there is a native DP | |
346 | * or passive DP-HDMI dongle connected | |
4562236b | 347 | */ |
aac5db82 | 348 | bool dc_link_is_dp_sink_present(struct dc_link *link) |
4562236b HW |
349 | { |
350 | enum gpio_result gpio_result; | |
351 | uint32_t clock_pin = 0; | |
bd4905a9 | 352 | uint8_t retry = 0; |
4562236b HW |
353 | struct ddc *ddc; |
354 | ||
355 | enum connector_id connector_id = | |
356 | dal_graphics_object_id_get_connector_id(link->link_id); | |
357 | ||
358 | bool present = | |
359 | ((connector_id == CONNECTOR_ID_DISPLAY_PORT) || | |
360 | (connector_id == CONNECTOR_ID_EDP)); | |
361 | ||
d0778ebf | 362 | ddc = dal_ddc_service_get_ddc_pin(link->ddc); |
4562236b HW |
363 | |
364 | if (!ddc) { | |
365 | BREAK_TO_DEBUGGER(); | |
366 | return present; | |
367 | } | |
368 | ||
369 | /* Open GPIO and set it to I2C mode */ | |
370 | /* Note: this GpioMode_Input will be converted | |
371 | * to GpioConfigType_I2cAuxDualMode in GPIO component, | |
372 | * which indicates we need additional delay */ | |
373 | ||
374 | if (GPIO_RESULT_OK != dal_ddc_open( | |
375 | ddc, GPIO_MODE_INPUT, GPIO_DDC_CONFIG_TYPE_MODE_I2C)) { | |
8fb3a636 | 376 | dal_ddc_close(ddc); |
4562236b HW |
377 | |
378 | return present; | |
379 | } | |
380 | ||
bd4905a9 PH |
381 | /* |
382 | * Read GPIO: DP sink is present if both clock and data pins are zero | |
383 | * | |
384 | * [W/A] plug-unplug DP cable, sometimes customer board has | |
385 | * one short pulse on clk_pin(1V, < 1ms). DP will be config to HDMI/DVI | |
386 | * then monitor can't br light up. Add retry 3 times | |
387 | * But in real passive dongle, it need additional 3ms to detect | |
388 | */ | |
389 | do { | |
390 | gpio_result = dal_gpio_get_value(ddc->pin_clock, &clock_pin); | |
391 | ASSERT(gpio_result == GPIO_RESULT_OK); | |
392 | if (clock_pin) | |
393 | udelay(1000); | |
394 | else | |
395 | break; | |
396 | } while (retry++ < 3); | |
4562236b | 397 | |
4dfb0bad | 398 | present = (gpio_result == GPIO_RESULT_OK) && !clock_pin; |
4562236b HW |
399 | |
400 | dal_ddc_close(ddc); | |
401 | ||
402 | return present; | |
403 | } | |
404 | ||
405 | /* | |
406 | * @brief | |
407 | * Detect output sink type | |
408 | */ | |
8f38b66c HW |
409 | static enum signal_type link_detect_sink( |
410 | struct dc_link *link, | |
411 | enum dc_detect_reason reason) | |
4562236b HW |
412 | { |
413 | enum signal_type result = get_basic_signal_type( | |
414 | link->link_enc->id, link->link_id); | |
415 | ||
416 | /* Internal digital encoder will detect only dongles | |
417 | * that require digital signal */ | |
418 | ||
419 | /* Detection mechanism is different | |
420 | * for different native connectors. | |
421 | * LVDS connector supports only LVDS signal; | |
422 | * PCIE is a bus slot, the actual connector needs to be detected first; | |
423 | * eDP connector supports only eDP signal; | |
424 | * HDMI should check straps for audio */ | |
425 | ||
426 | /* PCIE detects the actual connector on add-on board */ | |
427 | ||
428 | if (link->link_id.id == CONNECTOR_ID_PCIE) { | |
429 | /* ZAZTODO implement PCIE add-on card detection */ | |
430 | } | |
431 | ||
432 | switch (link->link_id.id) { | |
433 | case CONNECTOR_ID_HDMI_TYPE_A: { | |
434 | /* check audio support: | |
435 | * if native HDMI is not supported, switch to DVI */ | |
436 | struct audio_support *aud_support = &link->dc->res_pool->audio_support; | |
437 | ||
438 | if (!aud_support->hdmi_audio_native) | |
439 | if (link->link_id.id == CONNECTOR_ID_HDMI_TYPE_A) | |
440 | result = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
441 | } | |
442 | break; | |
443 | case CONNECTOR_ID_DISPLAY_PORT: { | |
8f38b66c HW |
444 | /* DP HPD short pulse. Passive DP dongle will not |
445 | * have short pulse | |
446 | */ | |
447 | if (reason != DETECT_REASON_HPDRX) { | |
448 | /* Check whether DP signal detected: if not - | |
449 | * we assume signal is DVI; it could be corrected | |
450 | * to HDMI after dongle detection | |
451 | */ | |
aac5db82 | 452 | if (!dm_helpers_is_dp_sink_present(link)) |
8f38b66c HW |
453 | result = SIGNAL_TYPE_DVI_SINGLE_LINK; |
454 | } | |
4562236b HW |
455 | } |
456 | break; | |
457 | default: | |
458 | break; | |
459 | } | |
460 | ||
461 | return result; | |
462 | } | |
463 | ||
464 | static enum signal_type decide_signal_from_strap_and_dongle_type( | |
465 | enum display_dongle_type dongle_type, | |
466 | struct audio_support *audio_support) | |
467 | { | |
468 | enum signal_type signal = SIGNAL_TYPE_NONE; | |
469 | ||
470 | switch (dongle_type) { | |
471 | case DISPLAY_DONGLE_DP_HDMI_DONGLE: | |
472 | if (audio_support->hdmi_audio_on_dongle) | |
473 | signal = SIGNAL_TYPE_HDMI_TYPE_A; | |
474 | else | |
475 | signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
476 | break; | |
477 | case DISPLAY_DONGLE_DP_DVI_DONGLE: | |
478 | signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
479 | break; | |
480 | case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE: | |
481 | if (audio_support->hdmi_audio_native) | |
482 | signal = SIGNAL_TYPE_HDMI_TYPE_A; | |
483 | else | |
484 | signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
485 | break; | |
486 | default: | |
487 | signal = SIGNAL_TYPE_NONE; | |
488 | break; | |
489 | } | |
490 | ||
491 | return signal; | |
492 | } | |
493 | ||
494 | static enum signal_type dp_passive_dongle_detection( | |
495 | struct ddc_service *ddc, | |
496 | struct display_sink_capability *sink_cap, | |
497 | struct audio_support *audio_support) | |
498 | { | |
499 | dal_ddc_service_i2c_query_dp_dual_mode_adaptor( | |
500 | ddc, sink_cap); | |
501 | return decide_signal_from_strap_and_dongle_type( | |
502 | sink_cap->dongle_type, | |
503 | audio_support); | |
504 | } | |
505 | ||
d0778ebf | 506 | static void link_disconnect_sink(struct dc_link *link) |
4562236b | 507 | { |
d0778ebf HW |
508 | if (link->local_sink) { |
509 | dc_sink_release(link->local_sink); | |
510 | link->local_sink = NULL; | |
4562236b HW |
511 | } |
512 | ||
513 | link->dpcd_sink_count = 0; | |
514 | } | |
515 | ||
eb815442 ST |
516 | static void link_disconnect_remap(struct dc_sink *prev_sink, struct dc_link *link) |
517 | { | |
518 | dc_sink_release(link->local_sink); | |
519 | link->local_sink = prev_sink; | |
520 | } | |
521 | ||
522 | ||
7f7652ee | 523 | static void read_current_link_settings_on_detect(struct dc_link *link) |
5b7c0d8d AK |
524 | { |
525 | union lane_count_set lane_count_set = { {0} }; | |
526 | uint8_t link_bw_set; | |
527 | uint8_t link_rate_set; | |
abe882a3 AK |
528 | uint32_t read_dpcd_retry_cnt = 10; |
529 | enum dc_status status = DC_ERROR_UNEXPECTED; | |
530 | int i; | |
701c75ce | 531 | union max_down_spread max_down_spread = { {0} }; |
5b7c0d8d AK |
532 | |
533 | // Read DPCD 00101h to find out the number of lanes currently set | |
abe882a3 AK |
534 | for (i = 0; i < read_dpcd_retry_cnt; i++) { |
535 | status = core_link_read_dpcd( | |
536 | link, | |
537 | DP_LANE_COUNT_SET, | |
538 | &lane_count_set.raw, | |
539 | sizeof(lane_count_set)); | |
540 | /* First DPCD read after VDD ON can fail if the particular board | |
541 | * does not have HPD pin wired correctly. So if DPCD read fails, | |
542 | * which it should never happen, retry a few times. Target worst | |
543 | * case scenario of 80 ms. | |
544 | */ | |
545 | if (status == DC_OK) { | |
546 | link->cur_link_settings.lane_count = lane_count_set.bits.LANE_COUNT_SET; | |
547 | break; | |
548 | } | |
549 | ||
3e10f319 | 550 | msleep(8); |
abe882a3 AK |
551 | } |
552 | ||
5b7c0d8d AK |
553 | // Read DPCD 00100h to find if standard link rates are set |
554 | core_link_read_dpcd(link, DP_LINK_BW_SET, | |
555 | &link_bw_set, sizeof(link_bw_set)); | |
556 | ||
557 | if (link_bw_set == 0) { | |
7f7652ee ML |
558 | if (link->connector_signal == SIGNAL_TYPE_EDP) { |
559 | /* If standard link rates are not being used, | |
560 | * Read DPCD 00115h to find the edp link rate set used | |
561 | */ | |
562 | core_link_read_dpcd(link, DP_LINK_RATE_SET, | |
563 | &link_rate_set, sizeof(link_rate_set)); | |
564 | ||
565 | // edp_supported_link_rates_count = 0 for DP | |
566 | if (link_rate_set < link->dpcd_caps.edp_supported_link_rates_count) { | |
567 | link->cur_link_settings.link_rate = | |
568 | link->dpcd_caps.edp_supported_link_rates[link_rate_set]; | |
569 | link->cur_link_settings.link_rate_set = link_rate_set; | |
570 | link->cur_link_settings.use_link_rate_set = true; | |
571 | } | |
572 | } else { | |
573 | // Link Rate not found. Seamless boot may not work. | |
574 | ASSERT(false); | |
5b7c0d8d AK |
575 | } |
576 | } else { | |
577 | link->cur_link_settings.link_rate = link_bw_set; | |
578 | link->cur_link_settings.use_link_rate_set = false; | |
579 | } | |
701c75ce DL |
580 | // Read DPCD 00003h to find the max down spread. |
581 | core_link_read_dpcd(link, DP_MAX_DOWNSPREAD, | |
582 | &max_down_spread.raw, sizeof(max_down_spread)); | |
583 | link->cur_link_settings.link_spread = | |
584 | max_down_spread.bits.MAX_DOWN_SPREAD ? | |
585 | LINK_SPREAD_05_DOWNSPREAD_30KHZ : LINK_SPREAD_DISABLED; | |
5b7c0d8d AK |
586 | } |
587 | ||
cdb39798 | 588 | static bool detect_dp( |
d0778ebf | 589 | struct dc_link *link, |
4562236b HW |
590 | struct display_sink_capability *sink_caps, |
591 | bool *converter_disable_audio, | |
592 | struct audio_support *audio_support, | |
8f38b66c | 593 | enum dc_detect_reason reason) |
4562236b | 594 | { |
8f38b66c HW |
595 | bool boot = false; |
596 | sink_caps->signal = link_detect_sink(link, reason); | |
4562236b HW |
597 | sink_caps->transaction_type = |
598 | get_ddc_transaction_type(sink_caps->signal); | |
599 | ||
600 | if (sink_caps->transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { | |
601 | sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT; | |
cdb39798 YS |
602 | if (!detect_dp_sink_caps(link)) |
603 | return false; | |
4562236b | 604 | |
4562236b HW |
605 | if (is_mst_supported(link)) { |
606 | sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT_MST; | |
e4ba6335 | 607 | link->type = dc_connection_mst_branch; |
4562236b | 608 | |
0a145446 EY |
609 | dal_ddc_service_set_transaction_type( |
610 | link->ddc, | |
611 | sink_caps->transaction_type); | |
612 | ||
4562236b HW |
613 | /* |
614 | * This call will initiate MST topology discovery. Which | |
615 | * will detect MST ports and add new DRM connector DRM | |
616 | * framework. Then read EDID via remote i2c over aux. In | |
617 | * the end, will notify DRM detect result and save EDID | |
618 | * into DRM framework. | |
619 | * | |
620 | * .detect is called by .fill_modes. | |
621 | * .fill_modes is called by user mode ioctl | |
622 | * DRM_IOCTL_MODE_GETCONNECTOR. | |
623 | * | |
624 | * .get_modes is called by .fill_modes. | |
625 | * | |
626 | * call .get_modes, AMDGPU DM implementation will create | |
627 | * new dc_sink and add to dc_link. For long HPD plug | |
628 | * in/out, MST has its own handle. | |
629 | * | |
630 | * Therefore, just after dc_create, link->sink is not | |
631 | * created for MST until user mode app calls | |
632 | * DRM_IOCTL_MODE_GETCONNECTOR. | |
633 | * | |
634 | * Need check ->sink usages in case ->sink = NULL | |
635 | * TODO: s3 resume check | |
636 | */ | |
8f38b66c HW |
637 | if (reason == DETECT_REASON_BOOT) |
638 | boot = true; | |
4562236b | 639 | |
2068afe6 NC |
640 | dm_helpers_dp_update_branch_info( |
641 | link->ctx, | |
642 | link); | |
643 | ||
e4ba6335 | 644 | if (!dm_helpers_dp_mst_start_top_mgr( |
4562236b | 645 | link->ctx, |
d0778ebf | 646 | link, boot)) { |
4562236b | 647 | /* MST not supported */ |
e4ba6335 | 648 | link->type = dc_connection_single; |
4562236b HW |
649 | sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT; |
650 | } | |
651 | } | |
58fe8990 HW |
652 | |
653 | if (link->type != dc_connection_mst_branch && | |
654 | is_dp_active_dongle(link)) { | |
655 | /* DP active dongles */ | |
656 | link->type = dc_connection_active_dongle; | |
657 | if (!link->dpcd_caps.sink_count.bits.SINK_COUNT) { | |
658 | /* | |
659 | * active dongle unplug processing for short irq | |
660 | */ | |
661 | link_disconnect_sink(link); | |
cdb39798 | 662 | return true; |
58fe8990 HW |
663 | } |
664 | ||
665 | if (link->dpcd_caps.dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER) | |
666 | *converter_disable_audio = true; | |
667 | } | |
4562236b HW |
668 | } else { |
669 | /* DP passive dongles */ | |
d0778ebf | 670 | sink_caps->signal = dp_passive_dongle_detection(link->ddc, |
4562236b HW |
671 | sink_caps, |
672 | audio_support); | |
673 | } | |
cdb39798 YS |
674 | |
675 | return true; | |
4562236b HW |
676 | } |
677 | ||
eb815442 ST |
678 | static bool is_same_edid(struct dc_edid *old_edid, struct dc_edid *new_edid) |
679 | { | |
680 | if (old_edid->length != new_edid->length) | |
681 | return false; | |
682 | ||
683 | if (new_edid->length == 0) | |
684 | return false; | |
685 | ||
686 | return (memcmp(old_edid->raw_edid, new_edid->raw_edid, new_edid->length) == 0); | |
687 | } | |
688 | ||
b77095d5 | 689 | static bool wait_for_alt_mode(struct dc_link *link) |
b5b1f455 EY |
690 | { |
691 | ||
692 | /** | |
693 | * something is terribly wrong if time out is > 200ms. (5Hz) | |
694 | * 500 microseconds * 400 tries us 200 ms | |
695 | **/ | |
696 | unsigned int sleep_time_in_microseconds = 500; | |
697 | unsigned int tries_allowed = 400; | |
698 | bool is_in_alt_mode; | |
699 | unsigned long long enter_timestamp; | |
700 | unsigned long long finish_timestamp; | |
701 | unsigned long long time_taken_in_ns; | |
702 | int tries_taken; | |
703 | ||
704 | DC_LOGGER_INIT(link->ctx->logger); | |
705 | ||
706 | if (link->link_enc->funcs->is_in_alt_mode == NULL) | |
707 | return true; | |
708 | ||
709 | is_in_alt_mode = link->link_enc->funcs->is_in_alt_mode(link->link_enc); | |
710 | DC_LOG_WARNING("DP Alt mode state on HPD: %d\n", is_in_alt_mode); | |
711 | ||
712 | if (is_in_alt_mode) | |
713 | return true; | |
714 | ||
715 | enter_timestamp = dm_get_timestamp(link->ctx); | |
716 | ||
717 | for (tries_taken = 0; tries_taken < tries_allowed; tries_taken++) { | |
718 | udelay(sleep_time_in_microseconds); | |
719 | /* ask the link if alt mode is enabled, if so return ok */ | |
720 | if (link->link_enc->funcs->is_in_alt_mode(link->link_enc)) { | |
721 | ||
722 | finish_timestamp = dm_get_timestamp(link->ctx); | |
723 | time_taken_in_ns = dm_get_elapse_time_in_ns( | |
724 | link->ctx, finish_timestamp, enter_timestamp); | |
725 | DC_LOG_WARNING("Alt mode entered finished after %llu ms\n", | |
b859c579 | 726 | div_u64(time_taken_in_ns, 1000000)); |
b5b1f455 EY |
727 | return true; |
728 | } | |
729 | ||
730 | } | |
731 | finish_timestamp = dm_get_timestamp(link->ctx); | |
732 | time_taken_in_ns = dm_get_elapse_time_in_ns(link->ctx, finish_timestamp, | |
733 | enter_timestamp); | |
734 | DC_LOG_WARNING("Alt mode has timed out after %llu ms\n", | |
b859c579 | 735 | div_u64(time_taken_in_ns, 1000000)); |
b5b1f455 EY |
736 | return false; |
737 | } | |
738 | ||
2119aa17 DF |
739 | /** |
740 | * dc_link_detect() - Detect if a sink is attached to a given link | |
741 | * | |
742 | * link->local_sink is created or destroyed as needed. | |
743 | * | |
744 | * This does not create remote sinks but will trigger DM | |
745 | * to start MST detection if a branch is detected. | |
746 | */ | |
dd80ad9b Y |
747 | static bool dc_link_detect_helper(struct dc_link *link, |
748 | enum dc_detect_reason reason) | |
4562236b | 749 | { |
4562236b HW |
750 | struct dc_sink_init_data sink_init_data = { 0 }; |
751 | struct display_sink_capability sink_caps = { 0 }; | |
752 | uint8_t i; | |
753 | bool converter_disable_audio = false; | |
754 | struct audio_support *aud_support = &link->dc->res_pool->audio_support; | |
eb815442 | 755 | bool same_edid = false; |
4562236b HW |
756 | enum dc_edid_status edid_status; |
757 | struct dc_context *dc_ctx = link->ctx; | |
533ed6c7 | 758 | struct dc_sink *sink = NULL; |
eb815442 ST |
759 | struct dc_sink *prev_sink = NULL; |
760 | struct dpcd_caps prev_dpcd_caps; | |
761 | bool same_dpcd = true; | |
4562236b | 762 | enum dc_connection_type new_connection_type = dc_connection_none; |
7f7652ee | 763 | bool perform_dp_seamless_boot = false; |
9ae1b27f | 764 | |
5d4b05dd | 765 | DC_LOGGER_INIT(link->ctx->logger); |
248cbed6 EB |
766 | |
767 | if (dc_is_virtual_signal(link->connector_signal)) | |
4562236b HW |
768 | return false; |
769 | ||
abe882a3 AK |
770 | if ((link->connector_signal == SIGNAL_TYPE_LVDS || |
771 | link->connector_signal == SIGNAL_TYPE_EDP) && | |
772 | link->local_sink) | |
773 | return true; | |
774 | ||
fbbdadf2 | 775 | if (false == dc_link_detect_sink(link, &new_connection_type)) { |
4562236b HW |
776 | BREAK_TO_DEBUGGER(); |
777 | return false; | |
778 | } | |
779 | ||
eb815442 ST |
780 | prev_sink = link->local_sink; |
781 | if (prev_sink != NULL) { | |
782 | dc_sink_retain(prev_sink); | |
783 | memcpy(&prev_dpcd_caps, &link->dpcd_caps, sizeof(struct dpcd_caps)); | |
784 | } | |
dcf298c3 WL |
785 | link_disconnect_sink(link); |
786 | ||
4562236b | 787 | if (new_connection_type != dc_connection_none) { |
d0778ebf | 788 | link->type = new_connection_type; |
eed928dc | 789 | link->link_state_valid = false; |
4562236b HW |
790 | |
791 | /* From Disconnected-to-Connected. */ | |
d0778ebf | 792 | switch (link->connector_signal) { |
4562236b HW |
793 | case SIGNAL_TYPE_HDMI_TYPE_A: { |
794 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
795 | if (aud_support->hdmi_audio_native) | |
796 | sink_caps.signal = SIGNAL_TYPE_HDMI_TYPE_A; | |
797 | else | |
798 | sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
799 | break; | |
800 | } | |
801 | ||
802 | case SIGNAL_TYPE_DVI_SINGLE_LINK: { | |
803 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
804 | sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
805 | break; | |
806 | } | |
807 | ||
808 | case SIGNAL_TYPE_DVI_DUAL_LINK: { | |
809 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
810 | sink_caps.signal = SIGNAL_TYPE_DVI_DUAL_LINK; | |
811 | break; | |
812 | } | |
813 | ||
11c3ee48 AD |
814 | case SIGNAL_TYPE_LVDS: { |
815 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
816 | sink_caps.signal = SIGNAL_TYPE_LVDS; | |
817 | break; | |
818 | } | |
819 | ||
4562236b | 820 | case SIGNAL_TYPE_EDP: { |
4654a2f7 | 821 | detect_edp_sink_caps(link); |
5d593d68 | 822 | read_current_link_settings_on_detect(link); |
7f7652ee | 823 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; |
4562236b HW |
824 | sink_caps.signal = SIGNAL_TYPE_EDP; |
825 | break; | |
826 | } | |
827 | ||
828 | case SIGNAL_TYPE_DISPLAY_PORT: { | |
7f7652ee | 829 | |
b5b1f455 EY |
830 | /* wa HPD high coming too early*/ |
831 | if (link->link_enc->features.flags.bits.DP_IS_USB_C == 1) { | |
832 | ||
833 | /* if alt mode times out, return false */ | |
834 | if (wait_for_alt_mode(link) == false) { | |
835 | return false; | |
836 | } | |
837 | } | |
838 | ||
cdb39798 | 839 | if (!detect_dp( |
4562236b HW |
840 | link, |
841 | &sink_caps, | |
842 | &converter_disable_audio, | |
eb815442 ST |
843 | aud_support, reason)) { |
844 | if (prev_sink != NULL) | |
845 | dc_sink_release(prev_sink); | |
cdb39798 | 846 | return false; |
eb815442 | 847 | } |
4562236b | 848 | |
eb815442 ST |
849 | // Check if dpcp block is the same |
850 | if (prev_sink != NULL) { | |
851 | if (memcmp(&link->dpcd_caps, &prev_dpcd_caps, sizeof(struct dpcd_caps))) | |
852 | same_dpcd = false; | |
853 | } | |
da2f84ba | 854 | /* Active dongle downstream unplug*/ |
a504ad26 HH |
855 | if (link->type == dc_connection_active_dongle && |
856 | link->dpcd_caps.sink_count.bits.SINK_COUNT == 0) { | |
da2f84ba | 857 | if (prev_sink != NULL) |
80adaebd | 858 | /* Downstream unplug */ |
eb815442 | 859 | dc_sink_release(prev_sink); |
4562236b | 860 | return true; |
eb815442 | 861 | } |
4562236b | 862 | |
d0778ebf | 863 | if (link->type == dc_connection_mst_branch) { |
4562236b | 864 | LINK_INFO("link=%d, mst branch is now Connected\n", |
d0778ebf | 865 | link->link_index); |
3f1f74f4 JZ |
866 | /* Need to setup mst link_cap struct here |
867 | * otherwise dc_link_detect() will leave mst link_cap | |
868 | * empty which leads to allocate_mst_payload() has "0" | |
eb0e5154 | 869 | * pbn_per_slot value leading to exception on dc_fixpt_div() |
3f1f74f4 | 870 | */ |
f537d474 LH |
871 | dp_verify_mst_link_cap(link); |
872 | ||
eb815442 ST |
873 | if (prev_sink != NULL) |
874 | dc_sink_release(prev_sink); | |
4562236b HW |
875 | return false; |
876 | } | |
877 | ||
7f7652ee ML |
878 | // For seamless boot, to skip verify link cap, we read UEFI settings and set them as verified. |
879 | if (reason == DETECT_REASON_BOOT && | |
880 | dc_ctx->dc->config.power_down_display_on_boot == false && | |
881 | link->link_status.link_active == true) | |
882 | perform_dp_seamless_boot = true; | |
883 | ||
884 | if (perform_dp_seamless_boot) { | |
885 | read_current_link_settings_on_detect(link); | |
886 | link->verified_link_cap = link->reported_link_cap; | |
887 | } | |
888 | ||
4562236b HW |
889 | break; |
890 | } | |
891 | ||
892 | default: | |
893 | DC_ERROR("Invalid connector type! signal:%d\n", | |
d0778ebf | 894 | link->connector_signal); |
eb815442 ST |
895 | if (prev_sink != NULL) |
896 | dc_sink_release(prev_sink); | |
4562236b HW |
897 | return false; |
898 | } /* switch() */ | |
899 | ||
900 | if (link->dpcd_caps.sink_count.bits.SINK_COUNT) | |
901 | link->dpcd_sink_count = link->dpcd_caps.sink_count. | |
902 | bits.SINK_COUNT; | |
2a0b4d85 MT |
903 | else |
904 | link->dpcd_sink_count = 1; | |
4562236b HW |
905 | |
906 | dal_ddc_service_set_transaction_type( | |
d0778ebf | 907 | link->ddc, |
4562236b HW |
908 | sink_caps.transaction_type); |
909 | ||
d0778ebf HW |
910 | link->aux_mode = dal_ddc_service_is_in_aux_transaction_mode( |
911 | link->ddc); | |
7c7f5b15 | 912 | |
d0778ebf | 913 | sink_init_data.link = link; |
4562236b | 914 | sink_init_data.sink_signal = sink_caps.signal; |
4562236b | 915 | |
7d58e721 MT |
916 | sink = dc_sink_create(&sink_init_data); |
917 | if (!sink) { | |
918 | DC_ERROR("Failed to create sink!\n"); | |
eb815442 ST |
919 | if (prev_sink != NULL) |
920 | dc_sink_release(prev_sink); | |
7d58e721 MT |
921 | return false; |
922 | } | |
4562236b | 923 | |
ceb3dbb4 | 924 | sink->link->dongle_max_pix_clk = sink_caps.max_hdmi_pixel_clock; |
dcf298c3 | 925 | sink->converter_disable_audio = converter_disable_audio; |
7d58e721 | 926 | |
dcd5fb82 | 927 | /* dc_sink_create returns a new reference */ |
dcf298c3 | 928 | link->local_sink = sink; |
4562236b | 929 | |
dcf298c3 | 930 | edid_status = dm_helpers_read_local_edid( |
7c7f5b15 | 931 | link->ctx, |
d0778ebf | 932 | link, |
b73a22d3 | 933 | sink); |
4562236b HW |
934 | |
935 | switch (edid_status) { | |
936 | case EDID_BAD_CHECKSUM: | |
1296423b | 937 | DC_LOG_ERROR("EDID checksum invalid.\n"); |
4562236b HW |
938 | break; |
939 | case EDID_NO_RESPONSE: | |
1296423b | 940 | DC_LOG_ERROR("No EDID read.\n"); |
01dc285d HW |
941 | |
942 | /* | |
943 | * Abort detection for non-DP connectors if we have | |
944 | * no EDID | |
945 | * | |
946 | * DP needs to report as connected if HDP is high | |
947 | * even if we have no EDID in order to go to | |
948 | * fail-safe mode | |
949 | */ | |
16196776 | 950 | if (dc_is_hdmi_signal(link->connector_signal) || |
219097df S |
951 | dc_is_dvi_signal(link->connector_signal)) { |
952 | if (prev_sink != NULL) | |
953 | dc_sink_release(prev_sink); | |
954 | ||
01dc285d | 955 | return false; |
219097df | 956 | } |
4562236b HW |
957 | default: |
958 | break; | |
959 | } | |
960 | ||
eb815442 ST |
961 | // Check if edid is the same |
962 | if ((prev_sink != NULL) && ((edid_status == EDID_THE_SAME) || (edid_status == EDID_OK))) | |
963 | same_edid = is_same_edid(&prev_sink->dc_edid, &sink->dc_edid); | |
964 | ||
0301ccba | 965 | if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && |
3eb6d7ac | 966 | sink_caps.transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { |
0301ccba | 967 | /* |
968 | * TODO debug why Dell 2413 doesn't like | |
969 | * two link trainings | |
970 | */ | |
3f1f74f4 | 971 | |
7f7652ee ML |
972 | // verify link cap for SST non-seamless boot |
973 | if (!perform_dp_seamless_boot) | |
974 | dp_verify_link_cap_with_retries(link, | |
975 | &link->reported_link_cap, | |
976 | LINK_TRAINING_MAX_VERIFY_RETRY); | |
0301ccba | 977 | } else { |
978 | // If edid is the same, then discard new sink and revert back to original sink | |
979 | if (same_edid) { | |
980 | link_disconnect_remap(prev_sink, link); | |
981 | sink = prev_sink; | |
982 | prev_sink = NULL; | |
983 | ||
984 | } | |
eb815442 | 985 | } |
4562236b | 986 | |
0301ccba | 987 | /* HDMI-DVI Dongle */ |
988 | if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A && | |
989 | !sink->edid_caps.edid_hdmi) | |
990 | sink->sink_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
991 | ||
4562236b | 992 | /* Connectivity log: detection */ |
a634913e | 993 | for (i = 0; i < sink->dc_edid.length / DC_EDID_BLOCK_SIZE; i++) { |
4562236b | 994 | CONN_DATA_DETECT(link, |
a634913e JA |
995 | &sink->dc_edid.raw_edid[i * DC_EDID_BLOCK_SIZE], |
996 | DC_EDID_BLOCK_SIZE, | |
b73a22d3 | 997 | "%s: [Block %d] ", sink->edid_caps.display_name, i); |
4562236b HW |
998 | } |
999 | ||
1296423b | 1000 | DC_LOG_DETECTION_EDID_PARSER("%s: " |
4562236b HW |
1001 | "manufacturer_id = %X, " |
1002 | "product_id = %X, " | |
1003 | "serial_number = %X, " | |
1004 | "manufacture_week = %d, " | |
1005 | "manufacture_year = %d, " | |
1006 | "display_name = %s, " | |
1007 | "speaker_flag = %d, " | |
1008 | "audio_mode_count = %d\n", | |
1009 | __func__, | |
b73a22d3 HW |
1010 | sink->edid_caps.manufacturer_id, |
1011 | sink->edid_caps.product_id, | |
1012 | sink->edid_caps.serial_number, | |
1013 | sink->edid_caps.manufacture_week, | |
1014 | sink->edid_caps.manufacture_year, | |
1015 | sink->edid_caps.display_name, | |
1016 | sink->edid_caps.speaker_flags, | |
1017 | sink->edid_caps.audio_mode_count); | |
1018 | ||
1019 | for (i = 0; i < sink->edid_caps.audio_mode_count; i++) { | |
1296423b | 1020 | DC_LOG_DETECTION_EDID_PARSER("%s: mode number = %d, " |
4562236b HW |
1021 | "format_code = %d, " |
1022 | "channel_count = %d, " | |
1023 | "sample_rate = %d, " | |
1024 | "sample_size = %d\n", | |
1025 | __func__, | |
1026 | i, | |
b73a22d3 HW |
1027 | sink->edid_caps.audio_modes[i].format_code, |
1028 | sink->edid_caps.audio_modes[i].channel_count, | |
1029 | sink->edid_caps.audio_modes[i].sample_rate, | |
1030 | sink->edid_caps.audio_modes[i].sample_size); | |
4562236b HW |
1031 | } |
1032 | ||
1033 | } else { | |
1034 | /* From Connected-to-Disconnected. */ | |
d0778ebf | 1035 | if (link->type == dc_connection_mst_branch) { |
4562236b | 1036 | LINK_INFO("link=%d, mst branch is now Disconnected\n", |
d0778ebf | 1037 | link->link_index); |
dcf298c3 | 1038 | |
d0778ebf | 1039 | dm_helpers_dp_mst_stop_top_mgr(link->ctx, link); |
4562236b HW |
1040 | |
1041 | link->mst_stream_alloc_table.stream_count = 0; | |
1042 | memset(link->mst_stream_alloc_table.stream_allocations, 0, sizeof(link->mst_stream_alloc_table.stream_allocations)); | |
1043 | } | |
1044 | ||
dcf298c3 WL |
1045 | link->type = dc_connection_none; |
1046 | sink_caps.signal = SIGNAL_TYPE_NONE; | |
233d87a5 ST |
1047 | /* When we unplug a passive DP-HDMI dongle connection, dongle_max_pix_clk |
1048 | * is not cleared. If we emulate a DP signal on this connection, it thinks | |
1049 | * the dongle is still there and limits the number of modes we can emulate. | |
1050 | * Clear dongle_max_pix_clk on disconnect to fix this | |
1051 | */ | |
1052 | link->dongle_max_pix_clk = 0; | |
4562236b HW |
1053 | } |
1054 | ||
eb815442 | 1055 | LINK_INFO("link=%d, dc_sink_in=%p is now %s prev_sink=%p dpcd same=%d edid same=%d\n", |
b73a22d3 | 1056 | link->link_index, sink, |
4562236b | 1057 | (sink_caps.signal == SIGNAL_TYPE_NONE ? |
eb815442 ST |
1058 | "Disconnected":"Connected"), prev_sink, |
1059 | same_dpcd, same_edid); | |
1060 | ||
1061 | if (prev_sink != NULL) | |
1062 | dc_sink_release(prev_sink); | |
4562236b HW |
1063 | |
1064 | return true; | |
9ae1b27f JG |
1065 | |
1066 | } | |
1067 | ||
1068 | bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) | |
1069 | { | |
1070 | const struct dc *dc = link->dc; | |
1071 | bool ret; | |
9ae1b27f | 1072 | |
ab4a4072 EY |
1073 | /* get out of low power state */ |
1074 | clk_mgr_exit_optimized_pwr_state(dc, dc->clk_mgr); | |
9ae1b27f JG |
1075 | |
1076 | ret = dc_link_detect_helper(link, reason); | |
1077 | ||
ab4a4072 EY |
1078 | /* Go back to power optimized state */ |
1079 | clk_mgr_optimize_pwr_state(dc, dc->clk_mgr); | |
9ae1b27f JG |
1080 | |
1081 | return ret; | |
4562236b HW |
1082 | } |
1083 | ||
16f4c695 CH |
1084 | bool dc_link_get_hpd_state(struct dc_link *dc_link) |
1085 | { | |
16f4c695 CH |
1086 | uint32_t state; |
1087 | ||
ac627caf CH |
1088 | dal_gpio_lock_pin(dc_link->hpd_gpio); |
1089 | dal_gpio_get_value(dc_link->hpd_gpio, &state); | |
1090 | dal_gpio_unlock_pin(dc_link->hpd_gpio); | |
16f4c695 CH |
1091 | |
1092 | return state; | |
1093 | } | |
1094 | ||
4562236b | 1095 | static enum hpd_source_id get_hpd_line( |
d0778ebf | 1096 | struct dc_link *link) |
4562236b HW |
1097 | { |
1098 | struct gpio *hpd; | |
1099 | enum hpd_source_id hpd_id = HPD_SOURCEID_UNKNOWN; | |
1100 | ||
87401969 | 1101 | hpd = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); |
4562236b HW |
1102 | |
1103 | if (hpd) { | |
1104 | switch (dal_irq_get_source(hpd)) { | |
1105 | case DC_IRQ_SOURCE_HPD1: | |
1106 | hpd_id = HPD_SOURCEID1; | |
1107 | break; | |
1108 | case DC_IRQ_SOURCE_HPD2: | |
1109 | hpd_id = HPD_SOURCEID2; | |
1110 | break; | |
1111 | case DC_IRQ_SOURCE_HPD3: | |
1112 | hpd_id = HPD_SOURCEID3; | |
1113 | break; | |
1114 | case DC_IRQ_SOURCE_HPD4: | |
1115 | hpd_id = HPD_SOURCEID4; | |
1116 | break; | |
1117 | case DC_IRQ_SOURCE_HPD5: | |
1118 | hpd_id = HPD_SOURCEID5; | |
1119 | break; | |
1120 | case DC_IRQ_SOURCE_HPD6: | |
1121 | hpd_id = HPD_SOURCEID6; | |
1122 | break; | |
1123 | default: | |
1124 | BREAK_TO_DEBUGGER(); | |
1125 | break; | |
1126 | } | |
1127 | ||
1128 | dal_gpio_destroy_irq(&hpd); | |
1129 | } | |
1130 | ||
1131 | return hpd_id; | |
1132 | } | |
1133 | ||
d0778ebf | 1134 | static enum channel_id get_ddc_line(struct dc_link *link) |
4562236b HW |
1135 | { |
1136 | struct ddc *ddc; | |
1137 | enum channel_id channel = CHANNEL_ID_UNKNOWN; | |
1138 | ||
d0778ebf | 1139 | ddc = dal_ddc_service_get_ddc_pin(link->ddc); |
4562236b HW |
1140 | |
1141 | if (ddc) { | |
1142 | switch (dal_ddc_get_line(ddc)) { | |
1143 | case GPIO_DDC_LINE_DDC1: | |
1144 | channel = CHANNEL_ID_DDC1; | |
1145 | break; | |
1146 | case GPIO_DDC_LINE_DDC2: | |
1147 | channel = CHANNEL_ID_DDC2; | |
1148 | break; | |
1149 | case GPIO_DDC_LINE_DDC3: | |
1150 | channel = CHANNEL_ID_DDC3; | |
1151 | break; | |
1152 | case GPIO_DDC_LINE_DDC4: | |
1153 | channel = CHANNEL_ID_DDC4; | |
1154 | break; | |
1155 | case GPIO_DDC_LINE_DDC5: | |
1156 | channel = CHANNEL_ID_DDC5; | |
1157 | break; | |
1158 | case GPIO_DDC_LINE_DDC6: | |
1159 | channel = CHANNEL_ID_DDC6; | |
1160 | break; | |
1161 | case GPIO_DDC_LINE_DDC_VGA: | |
1162 | channel = CHANNEL_ID_DDC_VGA; | |
1163 | break; | |
1164 | case GPIO_DDC_LINE_I2C_PAD: | |
1165 | channel = CHANNEL_ID_I2C_PAD; | |
1166 | break; | |
1167 | default: | |
1168 | BREAK_TO_DEBUGGER(); | |
1169 | break; | |
1170 | } | |
1171 | } | |
1172 | ||
1173 | return channel; | |
1174 | } | |
1175 | ||
1176 | static enum transmitter translate_encoder_to_transmitter( | |
1177 | struct graphics_object_id encoder) | |
1178 | { | |
1179 | switch (encoder.id) { | |
1180 | case ENCODER_ID_INTERNAL_UNIPHY: | |
1181 | switch (encoder.enum_id) { | |
1182 | case ENUM_ID_1: | |
1183 | return TRANSMITTER_UNIPHY_A; | |
1184 | case ENUM_ID_2: | |
1185 | return TRANSMITTER_UNIPHY_B; | |
1186 | default: | |
1187 | return TRANSMITTER_UNKNOWN; | |
1188 | } | |
1189 | break; | |
1190 | case ENCODER_ID_INTERNAL_UNIPHY1: | |
1191 | switch (encoder.enum_id) { | |
1192 | case ENUM_ID_1: | |
1193 | return TRANSMITTER_UNIPHY_C; | |
1194 | case ENUM_ID_2: | |
1195 | return TRANSMITTER_UNIPHY_D; | |
1196 | default: | |
1197 | return TRANSMITTER_UNKNOWN; | |
1198 | } | |
1199 | break; | |
1200 | case ENCODER_ID_INTERNAL_UNIPHY2: | |
1201 | switch (encoder.enum_id) { | |
1202 | case ENUM_ID_1: | |
1203 | return TRANSMITTER_UNIPHY_E; | |
1204 | case ENUM_ID_2: | |
1205 | return TRANSMITTER_UNIPHY_F; | |
1206 | default: | |
1207 | return TRANSMITTER_UNKNOWN; | |
1208 | } | |
1209 | break; | |
1210 | case ENCODER_ID_INTERNAL_UNIPHY3: | |
1211 | switch (encoder.enum_id) { | |
1212 | case ENUM_ID_1: | |
1213 | return TRANSMITTER_UNIPHY_G; | |
1214 | default: | |
1215 | return TRANSMITTER_UNKNOWN; | |
1216 | } | |
1217 | break; | |
1218 | case ENCODER_ID_EXTERNAL_NUTMEG: | |
1219 | switch (encoder.enum_id) { | |
1220 | case ENUM_ID_1: | |
1221 | return TRANSMITTER_NUTMEG_CRT; | |
1222 | default: | |
1223 | return TRANSMITTER_UNKNOWN; | |
1224 | } | |
1225 | break; | |
1226 | case ENCODER_ID_EXTERNAL_TRAVIS: | |
1227 | switch (encoder.enum_id) { | |
1228 | case ENUM_ID_1: | |
1229 | return TRANSMITTER_TRAVIS_CRT; | |
1230 | case ENUM_ID_2: | |
1231 | return TRANSMITTER_TRAVIS_LCD; | |
1232 | default: | |
1233 | return TRANSMITTER_UNKNOWN; | |
1234 | } | |
1235 | break; | |
1236 | default: | |
1237 | return TRANSMITTER_UNKNOWN; | |
1238 | } | |
1239 | } | |
1240 | ||
d9e32672 | 1241 | static bool dc_link_construct( |
d0778ebf | 1242 | struct dc_link *link, |
4562236b HW |
1243 | const struct link_init_data *init_params) |
1244 | { | |
1245 | uint8_t i; | |
c2e218dd | 1246 | struct ddc_service_init_data ddc_service_init_data = { { 0 } }; |
4562236b HW |
1247 | struct dc_context *dc_ctx = init_params->ctx; |
1248 | struct encoder_init_data enc_init_data = { 0 }; | |
1249 | struct integrated_info info = {{{ 0 }}}; | |
1250 | struct dc_bios *bios = init_params->dc->ctx->dc_bios; | |
1251 | const struct dc_vbios_funcs *bp_funcs = bios->funcs; | |
5d4b05dd | 1252 | DC_LOGGER_INIT(dc_ctx->logger); |
4562236b | 1253 | |
d0778ebf HW |
1254 | link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; |
1255 | link->irq_source_hpd_rx = DC_IRQ_SOURCE_INVALID; | |
4562236b HW |
1256 | |
1257 | link->link_status.dpcd_caps = &link->dpcd_caps; | |
1258 | ||
1259 | link->dc = init_params->dc; | |
1260 | link->ctx = dc_ctx; | |
d0778ebf | 1261 | link->link_index = init_params->link_index; |
4562236b | 1262 | |
e0a6440a DG |
1263 | memset(&link->preferred_training_settings, 0, sizeof(struct dc_link_training_overrides)); |
1264 | memset(&link->preferred_link_setting, 0, sizeof(struct dc_link_settings)); | |
1265 | ||
4562236b | 1266 | link->link_id = bios->funcs->get_connector_id(bios, init_params->connector_index); |
fb7b11e1 | 1267 | |
4562236b | 1268 | if (link->link_id.type != OBJECT_TYPE_CONNECTOR) { |
acbf7faa | 1269 | dm_output_to_console("%s: Invalid Connector ObjectID from Adapter Service for connector index:%d! type %d expected %d\n", |
63b371ec HW |
1270 | __func__, init_params->connector_index, |
1271 | link->link_id.type, OBJECT_TYPE_CONNECTOR); | |
4562236b HW |
1272 | goto create_fail; |
1273 | } | |
1274 | ||
66b198ff DL |
1275 | if (link->dc->res_pool->funcs->link_init) |
1276 | link->dc->res_pool->funcs->link_init(link); | |
1277 | ||
ac627caf | 1278 | link->hpd_gpio = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); |
04612213 HW |
1279 | if (link->hpd_gpio != NULL) { |
1280 | dal_gpio_open(link->hpd_gpio, GPIO_MODE_INTERRUPT); | |
1281 | dal_gpio_unlock_pin(link->hpd_gpio); | |
ac627caf | 1282 | link->irq_source_hpd = dal_irq_get_source(link->hpd_gpio); |
04612213 | 1283 | } |
4562236b HW |
1284 | |
1285 | switch (link->link_id.id) { | |
1286 | case CONNECTOR_ID_HDMI_TYPE_A: | |
d0778ebf | 1287 | link->connector_signal = SIGNAL_TYPE_HDMI_TYPE_A; |
4562236b HW |
1288 | |
1289 | break; | |
1290 | case CONNECTOR_ID_SINGLE_LINK_DVID: | |
1291 | case CONNECTOR_ID_SINGLE_LINK_DVII: | |
d0778ebf | 1292 | link->connector_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; |
4562236b HW |
1293 | break; |
1294 | case CONNECTOR_ID_DUAL_LINK_DVID: | |
1295 | case CONNECTOR_ID_DUAL_LINK_DVII: | |
d0778ebf | 1296 | link->connector_signal = SIGNAL_TYPE_DVI_DUAL_LINK; |
4562236b HW |
1297 | break; |
1298 | case CONNECTOR_ID_DISPLAY_PORT: | |
d0778ebf | 1299 | link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; |
4562236b | 1300 | |
ac627caf | 1301 | if (link->hpd_gpio != NULL) |
d0778ebf | 1302 | link->irq_source_hpd_rx = |
ac627caf | 1303 | dal_irq_get_rx_source(link->hpd_gpio); |
4562236b HW |
1304 | |
1305 | break; | |
1306 | case CONNECTOR_ID_EDP: | |
d0778ebf | 1307 | link->connector_signal = SIGNAL_TYPE_EDP; |
4562236b | 1308 | |
ac627caf | 1309 | if (link->hpd_gpio != NULL) { |
d0778ebf HW |
1310 | link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; |
1311 | link->irq_source_hpd_rx = | |
ac627caf | 1312 | dal_irq_get_rx_source(link->hpd_gpio); |
4562236b HW |
1313 | } |
1314 | break; | |
11c3ee48 AD |
1315 | case CONNECTOR_ID_LVDS: |
1316 | link->connector_signal = SIGNAL_TYPE_LVDS; | |
1317 | break; | |
4562236b | 1318 | default: |
1296423b | 1319 | DC_LOG_WARNING("Unsupported Connector type:%d!\n", link->link_id.id); |
4562236b HW |
1320 | goto create_fail; |
1321 | } | |
1322 | ||
4562236b HW |
1323 | /* TODO: #DAL3 Implement id to str function.*/ |
1324 | LINK_INFO("Connector[%d] description:" | |
1325 | "signal %d\n", | |
1326 | init_params->connector_index, | |
d0778ebf | 1327 | link->connector_signal); |
4562236b HW |
1328 | |
1329 | ddc_service_init_data.ctx = link->ctx; | |
1330 | ddc_service_init_data.id = link->link_id; | |
1331 | ddc_service_init_data.link = link; | |
d0778ebf | 1332 | link->ddc = dal_ddc_service_create(&ddc_service_init_data); |
4562236b | 1333 | |
d0778ebf | 1334 | if (link->ddc == NULL) { |
4562236b HW |
1335 | DC_ERROR("Failed to create ddc_service!\n"); |
1336 | goto ddc_create_fail; | |
1337 | } | |
1338 | ||
d0778ebf | 1339 | link->ddc_hw_inst = |
4562236b | 1340 | dal_ddc_get_line( |
d0778ebf | 1341 | dal_ddc_service_get_ddc_pin(link->ddc)); |
4562236b HW |
1342 | |
1343 | enc_init_data.ctx = dc_ctx; | |
1344 | bp_funcs->get_src_obj(dc_ctx->dc_bios, link->link_id, 0, &enc_init_data.encoder); | |
1345 | enc_init_data.connector = link->link_id; | |
1346 | enc_init_data.channel = get_ddc_line(link); | |
1347 | enc_init_data.hpd_source = get_hpd_line(link); | |
7a096334 | 1348 | |
d0778ebf | 1349 | link->hpd_src = enc_init_data.hpd_source; |
7a096334 | 1350 | |
4562236b HW |
1351 | enc_init_data.transmitter = |
1352 | translate_encoder_to_transmitter(enc_init_data.encoder); | |
1353 | link->link_enc = link->dc->res_pool->funcs->link_enc_create( | |
1354 | &enc_init_data); | |
1355 | ||
3a9aeadb | 1356 | if (link->link_enc == NULL) { |
4562236b HW |
1357 | DC_ERROR("Failed to create link encoder!\n"); |
1358 | goto link_enc_create_fail; | |
1359 | } | |
1360 | ||
d0778ebf | 1361 | link->link_enc_hw_inst = link->link_enc->transmitter; |
4562236b HW |
1362 | |
1363 | for (i = 0; i < 4; i++) { | |
1364 | if (BP_RESULT_OK != | |
1365 | bp_funcs->get_device_tag(dc_ctx->dc_bios, link->link_id, i, &link->device_tag)) { | |
1366 | DC_ERROR("Failed to find device tag!\n"); | |
1367 | goto device_tag_fail; | |
1368 | } | |
1369 | ||
1370 | /* Look for device tag that matches connector signal, | |
1371 | * CRT for rgb, LCD for other supported signal tyes | |
1372 | */ | |
1373 | if (!bp_funcs->is_device_id_supported(dc_ctx->dc_bios, link->device_tag.dev_id)) | |
1374 | continue; | |
1375 | if (link->device_tag.dev_id.device_type == DEVICE_TYPE_CRT | |
d0778ebf | 1376 | && link->connector_signal != SIGNAL_TYPE_RGB) |
4562236b HW |
1377 | continue; |
1378 | if (link->device_tag.dev_id.device_type == DEVICE_TYPE_LCD | |
d0778ebf | 1379 | && link->connector_signal == SIGNAL_TYPE_RGB) |
4562236b | 1380 | continue; |
4562236b HW |
1381 | break; |
1382 | } | |
1383 | ||
1384 | if (bios->integrated_info) | |
1385 | info = *bios->integrated_info; | |
1386 | ||
1387 | /* Look for channel mapping corresponding to connector and device tag */ | |
1388 | for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; i++) { | |
1389 | struct external_display_path *path = | |
1390 | &info.ext_disp_conn_info.path[i]; | |
1391 | if (path->device_connector_id.enum_id == link->link_id.enum_id | |
1392 | && path->device_connector_id.id == link->link_id.id | |
1e8635ea ZF |
1393 | && path->device_connector_id.type == link->link_id.type) { |
1394 | ||
1395 | if (link->device_tag.acpi_device != 0 | |
1396 | && path->device_acpi_enum == link->device_tag.acpi_device) { | |
1397 | link->ddi_channel_mapping = path->channel_mapping; | |
1398 | link->chip_caps = path->caps; | |
1399 | } else if (path->device_tag == | |
1400 | link->device_tag.dev_id.raw_device_tag) { | |
1401 | link->ddi_channel_mapping = path->channel_mapping; | |
1402 | link->chip_caps = path->caps; | |
1403 | } | |
4562236b HW |
1404 | break; |
1405 | } | |
1406 | } | |
1407 | ||
1408 | /* | |
1409 | * TODO check if GPIO programmed correctly | |
1410 | * | |
1411 | * If GPIO isn't programmed correctly HPD might not rise or drain | |
1412 | * fast enough, leading to bounces. | |
1413 | */ | |
b0c4e977 | 1414 | program_hpd_filter(link); |
4562236b HW |
1415 | |
1416 | return true; | |
1417 | device_tag_fail: | |
1418 | link->link_enc->funcs->destroy(&link->link_enc); | |
1419 | link_enc_create_fail: | |
d0778ebf | 1420 | dal_ddc_service_destroy(&link->ddc); |
4562236b HW |
1421 | ddc_create_fail: |
1422 | create_fail: | |
1423 | ||
ac627caf CH |
1424 | if (link->hpd_gpio != NULL) { |
1425 | dal_gpio_destroy_irq(&link->hpd_gpio); | |
1426 | link->hpd_gpio = NULL; | |
4562236b HW |
1427 | } |
1428 | ||
1429 | return false; | |
1430 | } | |
1431 | ||
1432 | /******************************************************************************* | |
1433 | * Public functions | |
1434 | ******************************************************************************/ | |
d0778ebf | 1435 | struct dc_link *link_create(const struct link_init_data *init_params) |
4562236b | 1436 | { |
d0778ebf | 1437 | struct dc_link *link = |
2004f45e | 1438 | kzalloc(sizeof(*link), GFP_KERNEL); |
4562236b HW |
1439 | |
1440 | if (NULL == link) | |
1441 | goto alloc_fail; | |
1442 | ||
d9e32672 | 1443 | if (false == dc_link_construct(link, init_params)) |
4562236b HW |
1444 | goto construct_fail; |
1445 | ||
1446 | return link; | |
1447 | ||
1448 | construct_fail: | |
2004f45e | 1449 | kfree(link); |
4562236b HW |
1450 | |
1451 | alloc_fail: | |
1452 | return NULL; | |
1453 | } | |
1454 | ||
d0778ebf | 1455 | void link_destroy(struct dc_link **link) |
4562236b | 1456 | { |
d9e32672 | 1457 | dc_link_destruct(*link); |
2004f45e | 1458 | kfree(*link); |
4562236b HW |
1459 | *link = NULL; |
1460 | } | |
1461 | ||
4562236b HW |
1462 | static void enable_stream_features(struct pipe_ctx *pipe_ctx) |
1463 | { | |
0971c40e | 1464 | struct dc_stream_state *stream = pipe_ctx->stream; |
ceb3dbb4 | 1465 | struct dc_link *link = stream->link; |
87943159 AK |
1466 | union down_spread_ctrl old_downspread; |
1467 | union down_spread_ctrl new_downspread; | |
4562236b | 1468 | |
3a340294 | 1469 | core_link_read_dpcd(link, DP_DOWNSPREAD_CTRL, |
87943159 | 1470 | &old_downspread.raw, sizeof(old_downspread)); |
4562236b | 1471 | |
87943159 AK |
1472 | new_downspread.raw = old_downspread.raw; |
1473 | ||
1474 | new_downspread.bits.IGNORE_MSA_TIMING_PARAM = | |
4fa086b9 | 1475 | (stream->ignore_msa_timing_param) ? 1 : 0; |
4562236b | 1476 | |
87943159 AK |
1477 | if (new_downspread.raw != old_downspread.raw) { |
1478 | core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL, | |
1479 | &new_downspread.raw, sizeof(new_downspread)); | |
1480 | } | |
4562236b HW |
1481 | } |
1482 | ||
ab8db3e1 AG |
1483 | static enum dc_status enable_link_dp( |
1484 | struct dc_state *state, | |
1485 | struct pipe_ctx *pipe_ctx) | |
4562236b | 1486 | { |
0971c40e | 1487 | struct dc_stream_state *stream = pipe_ctx->stream; |
4562236b HW |
1488 | enum dc_status status; |
1489 | bool skip_video_pattern; | |
ceb3dbb4 | 1490 | struct dc_link *link = stream->link; |
4562236b | 1491 | struct dc_link_settings link_settings = {0}; |
e0a6440a | 1492 | bool fec_enable; |
5ec43eda ML |
1493 | int i; |
1494 | bool apply_seamless_boot_optimization = false; | |
1495 | ||
1496 | // check for seamless boot | |
1497 | for (i = 0; i < state->stream_count; i++) { | |
1498 | if (state->streams[i]->apply_seamless_boot_optimization) { | |
1499 | apply_seamless_boot_optimization = true; | |
1500 | break; | |
1501 | } | |
1502 | } | |
4562236b HW |
1503 | |
1504 | /* get link settings for video mode timing */ | |
1505 | decide_link_settings(stream, &link_settings); | |
1506 | ||
5b7c0d8d | 1507 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_EDP) { |
5b7c0d8d AK |
1508 | /*in case it is not on*/ |
1509 | link->dc->hwss.edp_power_control(link, true); | |
1510 | link->dc->hwss.edp_wait_for_hpd_ready(link, true); | |
15ae3b28 AK |
1511 | } |
1512 | ||
24f7dd7e DL |
1513 | pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = |
1514 | link_settings.link_rate * LINK_RATE_REF_FREQ_IN_KHZ; | |
ed476602 | 1515 | if (state->clk_mgr && !apply_seamless_boot_optimization) |
5ec43eda | 1516 | state->clk_mgr->funcs->update_clocks(state->clk_mgr, state, false); |
4562236b | 1517 | |
4562236b HW |
1518 | skip_video_pattern = true; |
1519 | ||
1520 | if (link_settings.link_rate == LINK_RATE_LOW) | |
1521 | skip_video_pattern = false; | |
1522 | ||
832aa63b | 1523 | if (perform_link_training_with_retries( |
4562236b HW |
1524 | &link_settings, |
1525 | skip_video_pattern, | |
832aa63b PH |
1526 | LINK_TRAINING_ATTEMPTS, |
1527 | pipe_ctx, | |
1528 | pipe_ctx->stream->signal)) { | |
d0778ebf | 1529 | link->cur_link_settings = link_settings; |
4562236b HW |
1530 | status = DC_OK; |
1531 | } | |
1532 | else | |
c0ba5ec7 | 1533 | status = DC_FAIL_DP_LINK_TRAINING; |
4562236b | 1534 | |
008a4016 NC |
1535 | if (link->preferred_training_settings.fec_enable != NULL) |
1536 | fec_enable = *link->preferred_training_settings.fec_enable; | |
1537 | else | |
1538 | fec_enable = true; | |
1539 | ||
e0a6440a | 1540 | dp_set_fec_enable(link, fec_enable); |
4562236b HW |
1541 | return status; |
1542 | } | |
1543 | ||
904623ee YS |
1544 | static enum dc_status enable_link_edp( |
1545 | struct dc_state *state, | |
1546 | struct pipe_ctx *pipe_ctx) | |
1547 | { | |
1548 | enum dc_status status; | |
904623ee YS |
1549 | |
1550 | status = enable_link_dp(state, pipe_ctx); | |
1551 | ||
904623ee YS |
1552 | return status; |
1553 | } | |
1554 | ||
ab8db3e1 AG |
1555 | static enum dc_status enable_link_dp_mst( |
1556 | struct dc_state *state, | |
1557 | struct pipe_ctx *pipe_ctx) | |
4562236b | 1558 | { |
ceb3dbb4 | 1559 | struct dc_link *link = pipe_ctx->stream->link; |
4562236b HW |
1560 | |
1561 | /* sink signal type after MST branch is MST. Multiple MST sinks | |
1562 | * share one link. Link DP PHY is enable or training only once. | |
1563 | */ | |
d0778ebf | 1564 | if (link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) |
4562236b HW |
1565 | return DC_OK; |
1566 | ||
9cc032b2 MT |
1567 | /* clear payload table */ |
1568 | dm_helpers_dp_mst_clear_payload_allocation_table(link->ctx, link); | |
1569 | ||
22051b63 | 1570 | /* to make sure the pending down rep can be processed |
9cc032b2 | 1571 | * before enabling the link |
22051b63 MT |
1572 | */ |
1573 | dm_helpers_dp_mst_poll_pending_down_reply(link->ctx, link); | |
1574 | ||
07c84c7a DW |
1575 | /* set the sink to MST mode before enabling the link */ |
1576 | dp_enable_mst_on_sink(link, true); | |
1577 | ||
ab8db3e1 | 1578 | return enable_link_dp(state, pipe_ctx); |
4562236b HW |
1579 | } |
1580 | ||
1e8635ea ZF |
1581 | static bool get_ext_hdmi_settings(struct pipe_ctx *pipe_ctx, |
1582 | enum engine_id eng_id, | |
1583 | struct ext_hdmi_settings *settings) | |
1584 | { | |
1585 | bool result = false; | |
1586 | int i = 0; | |
1587 | struct integrated_info *integrated_info = | |
1588 | pipe_ctx->stream->ctx->dc_bios->integrated_info; | |
1589 | ||
1590 | if (integrated_info == NULL) | |
1591 | return false; | |
1592 | ||
1593 | /* | |
1594 | * Get retimer settings from sbios for passing SI eye test for DCE11 | |
1595 | * The setting values are varied based on board revision and port id | |
1596 | * Therefore the setting values of each ports is passed by sbios. | |
1597 | */ | |
1598 | ||
1599 | // Check if current bios contains ext Hdmi settings | |
1600 | if (integrated_info->gpu_cap_info & 0x20) { | |
1601 | switch (eng_id) { | |
1602 | case ENGINE_ID_DIGA: | |
1603 | settings->slv_addr = integrated_info->dp0_ext_hdmi_slv_addr; | |
1604 | settings->reg_num = integrated_info->dp0_ext_hdmi_6g_reg_num; | |
1605 | settings->reg_num_6g = integrated_info->dp0_ext_hdmi_6g_reg_num; | |
1606 | memmove(settings->reg_settings, | |
1607 | integrated_info->dp0_ext_hdmi_reg_settings, | |
1608 | sizeof(integrated_info->dp0_ext_hdmi_reg_settings)); | |
1609 | memmove(settings->reg_settings_6g, | |
1610 | integrated_info->dp0_ext_hdmi_6g_reg_settings, | |
1611 | sizeof(integrated_info->dp0_ext_hdmi_6g_reg_settings)); | |
1612 | result = true; | |
1613 | break; | |
1614 | case ENGINE_ID_DIGB: | |
1615 | settings->slv_addr = integrated_info->dp1_ext_hdmi_slv_addr; | |
1616 | settings->reg_num = integrated_info->dp1_ext_hdmi_6g_reg_num; | |
1617 | settings->reg_num_6g = integrated_info->dp1_ext_hdmi_6g_reg_num; | |
1618 | memmove(settings->reg_settings, | |
1619 | integrated_info->dp1_ext_hdmi_reg_settings, | |
1620 | sizeof(integrated_info->dp1_ext_hdmi_reg_settings)); | |
1621 | memmove(settings->reg_settings_6g, | |
1622 | integrated_info->dp1_ext_hdmi_6g_reg_settings, | |
1623 | sizeof(integrated_info->dp1_ext_hdmi_6g_reg_settings)); | |
1624 | result = true; | |
1625 | break; | |
1626 | case ENGINE_ID_DIGC: | |
1627 | settings->slv_addr = integrated_info->dp2_ext_hdmi_slv_addr; | |
1628 | settings->reg_num = integrated_info->dp2_ext_hdmi_6g_reg_num; | |
1629 | settings->reg_num_6g = integrated_info->dp2_ext_hdmi_6g_reg_num; | |
1630 | memmove(settings->reg_settings, | |
1631 | integrated_info->dp2_ext_hdmi_reg_settings, | |
1632 | sizeof(integrated_info->dp2_ext_hdmi_reg_settings)); | |
1633 | memmove(settings->reg_settings_6g, | |
1634 | integrated_info->dp2_ext_hdmi_6g_reg_settings, | |
1635 | sizeof(integrated_info->dp2_ext_hdmi_6g_reg_settings)); | |
1636 | result = true; | |
1637 | break; | |
4e527c01 AJ |
1638 | case ENGINE_ID_DIGD: |
1639 | settings->slv_addr = integrated_info->dp3_ext_hdmi_slv_addr; | |
1640 | settings->reg_num = integrated_info->dp3_ext_hdmi_6g_reg_num; | |
1641 | settings->reg_num_6g = integrated_info->dp3_ext_hdmi_6g_reg_num; | |
1642 | memmove(settings->reg_settings, | |
1643 | integrated_info->dp3_ext_hdmi_reg_settings, | |
1644 | sizeof(integrated_info->dp3_ext_hdmi_reg_settings)); | |
1645 | memmove(settings->reg_settings_6g, | |
1646 | integrated_info->dp3_ext_hdmi_6g_reg_settings, | |
1647 | sizeof(integrated_info->dp3_ext_hdmi_6g_reg_settings)); | |
1648 | result = true; | |
1649 | break; | |
1e8635ea ZF |
1650 | default: |
1651 | break; | |
1652 | } | |
1653 | ||
1654 | if (result == true) { | |
1655 | // Validate settings from bios integrated info table | |
1656 | if (settings->slv_addr == 0) | |
1657 | return false; | |
1658 | if (settings->reg_num > 9) | |
1659 | return false; | |
1660 | if (settings->reg_num_6g > 3) | |
1661 | return false; | |
1662 | ||
1663 | for (i = 0; i < settings->reg_num; i++) { | |
1664 | if (settings->reg_settings[i].i2c_reg_index > 0x20) | |
1665 | return false; | |
1666 | } | |
1667 | ||
1668 | for (i = 0; i < settings->reg_num_6g; i++) { | |
1669 | if (settings->reg_settings_6g[i].i2c_reg_index > 0x20) | |
1670 | return false; | |
1671 | } | |
1672 | } | |
1673 | } | |
1674 | ||
1675 | return result; | |
1676 | } | |
1677 | ||
1678 | static bool i2c_write(struct pipe_ctx *pipe_ctx, | |
1679 | uint8_t address, uint8_t *buffer, uint32_t length) | |
1680 | { | |
1681 | struct i2c_command cmd = {0}; | |
1682 | struct i2c_payload payload = {0}; | |
1683 | ||
1684 | memset(&payload, 0, sizeof(payload)); | |
1685 | memset(&cmd, 0, sizeof(cmd)); | |
1686 | ||
1687 | cmd.number_of_payloads = 1; | |
1688 | cmd.engine = I2C_COMMAND_ENGINE_DEFAULT; | |
1689 | cmd.speed = pipe_ctx->stream->ctx->dc->caps.i2c_speed_in_khz; | |
1690 | ||
1691 | payload.address = address; | |
1692 | payload.data = buffer; | |
1693 | payload.length = length; | |
1694 | payload.write = true; | |
1695 | cmd.payloads = &payload; | |
1696 | ||
c85e6e54 | 1697 | if (dm_helpers_submit_i2c(pipe_ctx->stream->ctx, |
ceb3dbb4 | 1698 | pipe_ctx->stream->link, &cmd)) |
1e8635ea ZF |
1699 | return true; |
1700 | ||
1701 | return false; | |
1702 | } | |
1703 | ||
1704 | static void write_i2c_retimer_setting( | |
1705 | struct pipe_ctx *pipe_ctx, | |
1706 | bool is_vga_mode, | |
1707 | bool is_over_340mhz, | |
1708 | struct ext_hdmi_settings *settings) | |
1709 | { | |
1710 | uint8_t slave_address = (settings->slv_addr >> 1); | |
1711 | uint8_t buffer[2]; | |
1712 | const uint8_t apply_rx_tx_change = 0x4; | |
1713 | uint8_t offset = 0xA; | |
1714 | uint8_t value = 0; | |
1715 | int i = 0; | |
1716 | bool i2c_success = false; | |
2f14bc89 | 1717 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
1e8635ea ZF |
1718 | |
1719 | memset(&buffer, 0, sizeof(buffer)); | |
1720 | ||
1721 | /* Start Ext-Hdmi programming*/ | |
1722 | ||
1723 | for (i = 0; i < settings->reg_num; i++) { | |
1724 | /* Apply 3G settings */ | |
1725 | if (settings->reg_settings[i].i2c_reg_index <= 0x20) { | |
1726 | ||
1727 | buffer[0] = settings->reg_settings[i].i2c_reg_index; | |
1728 | buffer[1] = settings->reg_settings[i].i2c_reg_val; | |
1729 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1730 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1731 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
1732 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", | |
1733 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1734 | |
1735 | if (!i2c_success) | |
1736 | /* Write failure */ | |
1737 | ASSERT(i2c_success); | |
1738 | ||
1739 | /* Based on DP159 specs, APPLY_RX_TX_CHANGE bit in 0x0A | |
1740 | * needs to be set to 1 on every 0xA-0xC write. | |
1741 | */ | |
1742 | if (settings->reg_settings[i].i2c_reg_index == 0xA || | |
1743 | settings->reg_settings[i].i2c_reg_index == 0xB || | |
1744 | settings->reg_settings[i].i2c_reg_index == 0xC) { | |
1745 | ||
1746 | /* Query current value from offset 0xA */ | |
1747 | if (settings->reg_settings[i].i2c_reg_index == 0xA) | |
1748 | value = settings->reg_settings[i].i2c_reg_val; | |
1749 | else { | |
1750 | i2c_success = | |
1751 | dal_ddc_service_query_ddc_data( | |
ceb3dbb4 | 1752 | pipe_ctx->stream->link->ddc, |
1e8635ea ZF |
1753 | slave_address, &offset, 1, &value, 1); |
1754 | if (!i2c_success) | |
1755 | /* Write failure */ | |
1756 | ASSERT(i2c_success); | |
1757 | } | |
1758 | ||
1759 | buffer[0] = offset; | |
1760 | /* Set APPLY_RX_TX_CHANGE bit to 1 */ | |
1761 | buffer[1] = value | apply_rx_tx_change; | |
1762 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1763 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1764 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
1765 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1766 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1767 | if (!i2c_success) |
1768 | /* Write failure */ | |
1769 | ASSERT(i2c_success); | |
1770 | } | |
1771 | } | |
1772 | } | |
1773 | ||
1774 | /* Apply 3G settings */ | |
1775 | if (is_over_340mhz) { | |
1776 | for (i = 0; i < settings->reg_num_6g; i++) { | |
1777 | /* Apply 3G settings */ | |
1778 | if (settings->reg_settings[i].i2c_reg_index <= 0x20) { | |
1779 | ||
1780 | buffer[0] = settings->reg_settings_6g[i].i2c_reg_index; | |
1781 | buffer[1] = settings->reg_settings_6g[i].i2c_reg_val; | |
1782 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1783 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1784 | RETIMER_REDRIVER_INFO("above 340Mhz: retimer write to slave_address = 0x%x,\ |
1785 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1786 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1787 | |
1788 | if (!i2c_success) | |
1789 | /* Write failure */ | |
1790 | ASSERT(i2c_success); | |
1791 | ||
1792 | /* Based on DP159 specs, APPLY_RX_TX_CHANGE bit in 0x0A | |
1793 | * needs to be set to 1 on every 0xA-0xC write. | |
1794 | */ | |
1795 | if (settings->reg_settings_6g[i].i2c_reg_index == 0xA || | |
1796 | settings->reg_settings_6g[i].i2c_reg_index == 0xB || | |
1797 | settings->reg_settings_6g[i].i2c_reg_index == 0xC) { | |
1798 | ||
1799 | /* Query current value from offset 0xA */ | |
1800 | if (settings->reg_settings_6g[i].i2c_reg_index == 0xA) | |
1801 | value = settings->reg_settings_6g[i].i2c_reg_val; | |
1802 | else { | |
1803 | i2c_success = | |
1804 | dal_ddc_service_query_ddc_data( | |
ceb3dbb4 | 1805 | pipe_ctx->stream->link->ddc, |
1e8635ea ZF |
1806 | slave_address, &offset, 1, &value, 1); |
1807 | if (!i2c_success) | |
1808 | /* Write failure */ | |
1809 | ASSERT(i2c_success); | |
1810 | } | |
1811 | ||
1812 | buffer[0] = offset; | |
1813 | /* Set APPLY_RX_TX_CHANGE bit to 1 */ | |
1814 | buffer[1] = value | apply_rx_tx_change; | |
1815 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1816 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1817 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
1818 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1819 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1820 | if (!i2c_success) |
1821 | /* Write failure */ | |
1822 | ASSERT(i2c_success); | |
1823 | } | |
1824 | } | |
1825 | } | |
1826 | } | |
1827 | ||
1828 | if (is_vga_mode) { | |
1829 | /* Program additional settings if using 640x480 resolution */ | |
1830 | ||
1831 | /* Write offset 0xFF to 0x01 */ | |
1832 | buffer[0] = 0xff; | |
1833 | buffer[1] = 0x01; | |
1834 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1835 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1836 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
1837 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1838 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1839 | if (!i2c_success) |
1840 | /* Write failure */ | |
1841 | ASSERT(i2c_success); | |
1842 | ||
1843 | /* Write offset 0x00 to 0x23 */ | |
1844 | buffer[0] = 0x00; | |
1845 | buffer[1] = 0x23; | |
1846 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1847 | buffer, sizeof(buffer)); | |
2f14bc89 | 1848 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
c2af2a42 | 1849 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", |
2f14bc89 | 1850 | slave_address, buffer[0], buffer[1], i2c_success?1:0); |
1e8635ea ZF |
1851 | if (!i2c_success) |
1852 | /* Write failure */ | |
1853 | ASSERT(i2c_success); | |
1854 | ||
1855 | /* Write offset 0xff to 0x00 */ | |
1856 | buffer[0] = 0xff; | |
1857 | buffer[1] = 0x00; | |
1858 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1859 | buffer, sizeof(buffer)); | |
2f14bc89 | 1860 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
c2af2a42 | 1861 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", |
2f14bc89 | 1862 | slave_address, buffer[0], buffer[1], i2c_success?1:0); |
1e8635ea ZF |
1863 | if (!i2c_success) |
1864 | /* Write failure */ | |
1865 | ASSERT(i2c_success); | |
1866 | ||
1867 | } | |
1868 | } | |
1869 | ||
1870 | static void write_i2c_default_retimer_setting( | |
1871 | struct pipe_ctx *pipe_ctx, | |
1872 | bool is_vga_mode, | |
1873 | bool is_over_340mhz) | |
1874 | { | |
1875 | uint8_t slave_address = (0xBA >> 1); | |
1876 | uint8_t buffer[2]; | |
1877 | bool i2c_success = false; | |
2f14bc89 | 1878 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
1e8635ea ZF |
1879 | |
1880 | memset(&buffer, 0, sizeof(buffer)); | |
1881 | ||
1882 | /* Program Slave Address for tuning single integrity */ | |
1883 | /* Write offset 0x0A to 0x13 */ | |
1884 | buffer[0] = 0x0A; | |
1885 | buffer[1] = 0x13; | |
1886 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1887 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1888 | RETIMER_REDRIVER_INFO("retimer writes default setting to slave_address = 0x%x,\ |
1889 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1890 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1891 | if (!i2c_success) |
1892 | /* Write failure */ | |
1893 | ASSERT(i2c_success); | |
1894 | ||
1895 | /* Write offset 0x0A to 0x17 */ | |
1896 | buffer[0] = 0x0A; | |
1897 | buffer[1] = 0x17; | |
1898 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1899 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1900 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1901 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1902 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1903 | if (!i2c_success) |
1904 | /* Write failure */ | |
1905 | ASSERT(i2c_success); | |
1906 | ||
1907 | /* Write offset 0x0B to 0xDA or 0xD8 */ | |
1908 | buffer[0] = 0x0B; | |
1909 | buffer[1] = is_over_340mhz ? 0xDA : 0xD8; | |
1910 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1911 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1912 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1913 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1914 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1915 | if (!i2c_success) |
1916 | /* Write failure */ | |
1917 | ASSERT(i2c_success); | |
1918 | ||
1919 | /* Write offset 0x0A to 0x17 */ | |
1920 | buffer[0] = 0x0A; | |
1921 | buffer[1] = 0x17; | |
1922 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1923 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1924 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1925 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", | |
1926 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1927 | if (!i2c_success) |
1928 | /* Write failure */ | |
1929 | ASSERT(i2c_success); | |
1930 | ||
1931 | /* Write offset 0x0C to 0x1D or 0x91 */ | |
1932 | buffer[0] = 0x0C; | |
1933 | buffer[1] = is_over_340mhz ? 0x1D : 0x91; | |
1934 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1935 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1936 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1937 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1938 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1939 | if (!i2c_success) |
1940 | /* Write failure */ | |
1941 | ASSERT(i2c_success); | |
1942 | ||
1943 | /* Write offset 0x0A to 0x17 */ | |
1944 | buffer[0] = 0x0A; | |
1945 | buffer[1] = 0x17; | |
1946 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1947 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1948 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1949 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1950 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1951 | if (!i2c_success) |
1952 | /* Write failure */ | |
1953 | ASSERT(i2c_success); | |
1954 | ||
1955 | ||
1956 | if (is_vga_mode) { | |
1957 | /* Program additional settings if using 640x480 resolution */ | |
1958 | ||
1959 | /* Write offset 0xFF to 0x01 */ | |
1960 | buffer[0] = 0xff; | |
1961 | buffer[1] = 0x01; | |
1962 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1963 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1964 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1965 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1966 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1967 | if (!i2c_success) |
1968 | /* Write failure */ | |
1969 | ASSERT(i2c_success); | |
1970 | ||
1971 | /* Write offset 0x00 to 0x23 */ | |
1972 | buffer[0] = 0x00; | |
1973 | buffer[1] = 0x23; | |
1974 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1975 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1976 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1977 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", | |
1978 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1979 | if (!i2c_success) |
1980 | /* Write failure */ | |
1981 | ASSERT(i2c_success); | |
1982 | ||
1983 | /* Write offset 0xff to 0x00 */ | |
1984 | buffer[0] = 0xff; | |
1985 | buffer[1] = 0x00; | |
1986 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1987 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1988 | RETIMER_REDRIVER_INFO("retimer write default setting to slave_addr = 0x%x,\ |
1989 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d end here\n", | |
1990 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1991 | if (!i2c_success) |
1992 | /* Write failure */ | |
1993 | ASSERT(i2c_success); | |
1994 | } | |
1995 | } | |
1996 | ||
1997 | static void write_i2c_redriver_setting( | |
1998 | struct pipe_ctx *pipe_ctx, | |
1999 | bool is_over_340mhz) | |
2000 | { | |
2001 | uint8_t slave_address = (0xF0 >> 1); | |
2002 | uint8_t buffer[16]; | |
2003 | bool i2c_success = false; | |
2f14bc89 | 2004 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
1e8635ea ZF |
2005 | |
2006 | memset(&buffer, 0, sizeof(buffer)); | |
2007 | ||
2008 | // Program Slave Address for tuning single integrity | |
2009 | buffer[3] = 0x4E; | |
2010 | buffer[4] = 0x4E; | |
2011 | buffer[5] = 0x4E; | |
2012 | buffer[6] = is_over_340mhz ? 0x4E : 0x4A; | |
2013 | ||
2014 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2015 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2016 | RETIMER_REDRIVER_INFO("redriver write 0 to all 16 reg offset expect following:\n\ |
2017 | \t slave_addr = 0x%x, offset[3] = 0x%x, offset[4] = 0x%x,\ | |
2018 | offset[5] = 0x%x,offset[6] is_over_340mhz = 0x%x,\ | |
2019 | i2c_success = %d\n", | |
2020 | slave_address, buffer[3], buffer[4], buffer[5], buffer[6], i2c_success?1:0); | |
1e8635ea ZF |
2021 | |
2022 | if (!i2c_success) | |
2023 | /* Write failure */ | |
2024 | ASSERT(i2c_success); | |
2025 | } | |
2026 | ||
61f14c5b LL |
2027 | static void disable_link(struct dc_link *link, enum signal_type signal) |
2028 | { | |
2029 | /* | |
2030 | * TODO: implement call for dp_set_hw_test_pattern | |
2031 | * it is needed for compliance testing | |
2032 | */ | |
2033 | ||
2034 | /* Here we need to specify that encoder output settings | |
2035 | * need to be calculated as for the set mode, | |
2036 | * it will lead to querying dynamic link capabilities | |
2037 | * which should be done before enable output | |
2038 | */ | |
2039 | ||
2040 | if (dc_is_dp_signal(signal)) { | |
2041 | /* SST DP, eDP */ | |
2042 | if (dc_is_dp_sst_signal(signal)) | |
2043 | dp_disable_link_phy(link, signal); | |
2044 | else | |
2045 | dp_disable_link_phy_mst(link, signal); | |
2046 | ||
2047 | if (dc_is_dp_sst_signal(signal) || | |
2048 | link->mst_stream_alloc_table.stream_count == 0) { | |
2049 | dp_set_fec_enable(link, false); | |
2050 | dp_set_fec_ready(link, false); | |
2051 | } | |
2052 | } else { | |
2053 | if (signal != SIGNAL_TYPE_VIRTUAL) | |
2054 | link->link_enc->funcs->disable_output(link->link_enc, signal); | |
2055 | } | |
2056 | ||
2057 | if (signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { | |
2058 | /* MST disable link only when no stream use the link */ | |
2059 | if (link->mst_stream_alloc_table.stream_count <= 0) | |
2060 | link->link_status.link_active = false; | |
2061 | } else { | |
2062 | link->link_status.link_active = false; | |
2063 | } | |
2064 | } | |
2065 | ||
4562236b HW |
2066 | static void enable_link_hdmi(struct pipe_ctx *pipe_ctx) |
2067 | { | |
0971c40e | 2068 | struct dc_stream_state *stream = pipe_ctx->stream; |
ceb3dbb4 | 2069 | struct dc_link *link = stream->link; |
cc4d99b8 | 2070 | enum dc_color_depth display_color_depth; |
1e8635ea ZF |
2071 | enum engine_id eng_id; |
2072 | struct ext_hdmi_settings settings = {0}; | |
2073 | bool is_over_340mhz = false; | |
2074 | bool is_vga_mode = (stream->timing.h_addressable == 640) | |
2075 | && (stream->timing.v_addressable == 480); | |
2076 | ||
321f65a6 | 2077 | if (stream->phy_pix_clk == 0) |
380604e2 | 2078 | stream->phy_pix_clk = stream->timing.pix_clk_100hz / 10; |
1e8635ea ZF |
2079 | if (stream->phy_pix_clk > 340000) |
2080 | is_over_340mhz = true; | |
2081 | ||
2082 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) { | |
ceb3dbb4 | 2083 | unsigned short masked_chip_caps = pipe_ctx->stream->link->chip_caps & |
3e5e2215 | 2084 | EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK; |
cc57306f | 2085 | if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_TISN65DP159RSBT) { |
1e8635ea ZF |
2086 | /* DP159, Retimer settings */ |
2087 | eng_id = pipe_ctx->stream_res.stream_enc->id; | |
2088 | ||
2089 | if (get_ext_hdmi_settings(pipe_ctx, eng_id, &settings)) { | |
2090 | write_i2c_retimer_setting(pipe_ctx, | |
2091 | is_vga_mode, is_over_340mhz, &settings); | |
2092 | } else { | |
2093 | write_i2c_default_retimer_setting(pipe_ctx, | |
2094 | is_vga_mode, is_over_340mhz); | |
2095 | } | |
cc57306f | 2096 | } else if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_PI3EQX1204) { |
1e8635ea ZF |
2097 | /* PI3EQX1204, Redriver settings */ |
2098 | write_i2c_redriver_setting(pipe_ctx, is_over_340mhz); | |
2099 | } | |
2100 | } | |
4562236b HW |
2101 | |
2102 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) | |
2103 | dal_ddc_service_write_scdc_data( | |
ceb3dbb4 | 2104 | stream->link->ddc, |
4562236b | 2105 | stream->phy_pix_clk, |
4fa086b9 | 2106 | stream->timing.flags.LTE_340MCSC_SCRAMBLE); |
4562236b | 2107 | |
ceb3dbb4 | 2108 | memset(&stream->link->cur_link_settings, 0, |
4562236b HW |
2109 | sizeof(struct dc_link_settings)); |
2110 | ||
4fa086b9 LSL |
2111 | display_color_depth = stream->timing.display_color_depth; |
2112 | if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) | |
cc4d99b8 CL |
2113 | display_color_depth = COLOR_DEPTH_888; |
2114 | ||
4562236b HW |
2115 | link->link_enc->funcs->enable_tmds_output( |
2116 | link->link_enc, | |
2117 | pipe_ctx->clock_source->id, | |
cc4d99b8 | 2118 | display_color_depth, |
3d5bae9e | 2119 | pipe_ctx->stream->signal, |
4562236b HW |
2120 | stream->phy_pix_clk); |
2121 | ||
3a9aeadb | 2122 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) |
d0778ebf | 2123 | dal_ddc_service_read_scdc_data(link->ddc); |
4562236b HW |
2124 | } |
2125 | ||
11c3ee48 AD |
2126 | static void enable_link_lvds(struct pipe_ctx *pipe_ctx) |
2127 | { | |
2128 | struct dc_stream_state *stream = pipe_ctx->stream; | |
ceb3dbb4 | 2129 | struct dc_link *link = stream->link; |
11c3ee48 AD |
2130 | |
2131 | if (stream->phy_pix_clk == 0) | |
380604e2 | 2132 | stream->phy_pix_clk = stream->timing.pix_clk_100hz / 10; |
11c3ee48 | 2133 | |
ceb3dbb4 | 2134 | memset(&stream->link->cur_link_settings, 0, |
11c3ee48 AD |
2135 | sizeof(struct dc_link_settings)); |
2136 | ||
2137 | link->link_enc->funcs->enable_lvds_output( | |
2138 | link->link_enc, | |
2139 | pipe_ctx->clock_source->id, | |
2140 | stream->phy_pix_clk); | |
2141 | ||
2142 | } | |
2143 | ||
4562236b | 2144 | /****************************enable_link***********************************/ |
ab8db3e1 AG |
2145 | static enum dc_status enable_link( |
2146 | struct dc_state *state, | |
2147 | struct pipe_ctx *pipe_ctx) | |
4562236b HW |
2148 | { |
2149 | enum dc_status status = DC_ERROR_UNEXPECTED; | |
61f14c5b LL |
2150 | struct dc_stream_state *stream = pipe_ctx->stream; |
2151 | struct dc_link *link = stream->link; | |
2152 | ||
2153 | /* There's some scenarios where driver is unloaded with display | |
2154 | * still enabled. When driver is reloaded, it may cause a display | |
2155 | * to not light up if there is a mismatch between old and new | |
2156 | * link settings. Need to call disable first before enabling at | |
2157 | * new link settings. | |
2158 | */ | |
2159 | if (link->link_status.link_active) { | |
2160 | disable_link(link, pipe_ctx->stream->signal); | |
2161 | } | |
2162 | ||
4562236b HW |
2163 | switch (pipe_ctx->stream->signal) { |
2164 | case SIGNAL_TYPE_DISPLAY_PORT: | |
ab8db3e1 | 2165 | status = enable_link_dp(state, pipe_ctx); |
4562236b | 2166 | break; |
904623ee YS |
2167 | case SIGNAL_TYPE_EDP: |
2168 | status = enable_link_edp(state, pipe_ctx); | |
2169 | break; | |
4562236b | 2170 | case SIGNAL_TYPE_DISPLAY_PORT_MST: |
ab8db3e1 | 2171 | status = enable_link_dp_mst(state, pipe_ctx); |
4562236b HW |
2172 | msleep(200); |
2173 | break; | |
2174 | case SIGNAL_TYPE_DVI_SINGLE_LINK: | |
2175 | case SIGNAL_TYPE_DVI_DUAL_LINK: | |
2176 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
2177 | enable_link_hdmi(pipe_ctx); | |
2178 | status = DC_OK; | |
2179 | break; | |
11c3ee48 AD |
2180 | case SIGNAL_TYPE_LVDS: |
2181 | enable_link_lvds(pipe_ctx); | |
2182 | status = DC_OK; | |
2183 | break; | |
4562236b HW |
2184 | case SIGNAL_TYPE_VIRTUAL: |
2185 | status = DC_OK; | |
2186 | break; | |
2187 | default: | |
2188 | break; | |
2189 | } | |
2190 | ||
293b9160 AK |
2191 | if (status == DC_OK) |
2192 | pipe_ctx->stream->link->link_status.link_active = true; | |
2193 | ||
4562236b HW |
2194 | return status; |
2195 | } | |
2196 | ||
162f8078 ML |
2197 | static uint32_t get_timing_pixel_clock_100hz(const struct dc_crtc_timing *timing) |
2198 | { | |
2199 | ||
2200 | uint32_t pxl_clk = timing->pix_clk_100hz; | |
2201 | ||
2202 | if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) | |
2203 | pxl_clk /= 2; | |
2204 | else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) | |
2205 | pxl_clk = pxl_clk * 2 / 3; | |
2206 | ||
2207 | if (timing->display_color_depth == COLOR_DEPTH_101010) | |
2208 | pxl_clk = pxl_clk * 10 / 8; | |
2209 | else if (timing->display_color_depth == COLOR_DEPTH_121212) | |
2210 | pxl_clk = pxl_clk * 12 / 8; | |
2211 | ||
2212 | return pxl_clk; | |
2213 | } | |
2214 | ||
73da927b | 2215 | static bool dp_active_dongle_validate_timing( |
6bffebc9 | 2216 | const struct dc_crtc_timing *timing, |
f110892e | 2217 | const struct dpcd_caps *dpcd_caps) |
6bffebc9 | 2218 | { |
f110892e HW |
2219 | const struct dc_dongle_caps *dongle_caps = &dpcd_caps->dongle_caps; |
2220 | ||
2221 | switch (dpcd_caps->dongle_type) { | |
2222 | case DISPLAY_DONGLE_DP_VGA_CONVERTER: | |
2223 | case DISPLAY_DONGLE_DP_DVI_CONVERTER: | |
2224 | case DISPLAY_DONGLE_DP_DVI_DONGLE: | |
2225 | if (timing->pixel_encoding == PIXEL_ENCODING_RGB) | |
2226 | return true; | |
2227 | else | |
2228 | return false; | |
2229 | default: | |
2230 | break; | |
2231 | } | |
6bffebc9 | 2232 | |
dd998291 | 2233 | if (dpcd_caps->dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER || |
6bffebc9 EY |
2234 | dongle_caps->extendedCapValid == false) |
2235 | return true; | |
2236 | ||
2237 | /* Check Pixel Encoding */ | |
2238 | switch (timing->pixel_encoding) { | |
2239 | case PIXEL_ENCODING_RGB: | |
2240 | case PIXEL_ENCODING_YCBCR444: | |
2241 | break; | |
2242 | case PIXEL_ENCODING_YCBCR422: | |
2243 | if (!dongle_caps->is_dp_hdmi_ycbcr422_pass_through) | |
2244 | return false; | |
2245 | break; | |
2246 | case PIXEL_ENCODING_YCBCR420: | |
2247 | if (!dongle_caps->is_dp_hdmi_ycbcr420_pass_through) | |
2248 | return false; | |
2249 | break; | |
2250 | default: | |
2251 | /* Invalid Pixel Encoding*/ | |
2252 | return false; | |
2253 | } | |
2254 | ||
6bffebc9 EY |
2255 | switch (timing->display_color_depth) { |
2256 | case COLOR_DEPTH_666: | |
2257 | case COLOR_DEPTH_888: | |
2258 | /*888 and 666 should always be supported*/ | |
2259 | break; | |
2260 | case COLOR_DEPTH_101010: | |
2261 | if (dongle_caps->dp_hdmi_max_bpc < 10) | |
2262 | return false; | |
6bffebc9 EY |
2263 | break; |
2264 | case COLOR_DEPTH_121212: | |
2265 | if (dongle_caps->dp_hdmi_max_bpc < 12) | |
2266 | return false; | |
6bffebc9 | 2267 | break; |
6bffebc9 EY |
2268 | case COLOR_DEPTH_141414: |
2269 | case COLOR_DEPTH_161616: | |
2270 | default: | |
2271 | /* These color depths are currently not supported */ | |
2272 | return false; | |
2273 | } | |
2274 | ||
e5490464 | 2275 | if (get_timing_pixel_clock_100hz(timing) > (dongle_caps->dp_hdmi_max_pixel_clk_in_khz * 10)) |
6bffebc9 EY |
2276 | return false; |
2277 | ||
2278 | return true; | |
2279 | } | |
2280 | ||
4562236b | 2281 | enum dc_status dc_link_validate_mode_timing( |
0971c40e | 2282 | const struct dc_stream_state *stream, |
d0778ebf | 2283 | struct dc_link *link, |
4562236b HW |
2284 | const struct dc_crtc_timing *timing) |
2285 | { | |
380604e2 | 2286 | uint32_t max_pix_clk = stream->link->dongle_max_pix_clk * 10; |
f110892e | 2287 | struct dpcd_caps *dpcd_caps = &link->dpcd_caps; |
4562236b HW |
2288 | |
2289 | /* A hack to avoid failing any modes for EDID override feature on | |
2290 | * topology change such as lower quality cable for DP or different dongle | |
2291 | */ | |
d0778ebf | 2292 | if (link->remote_sinks[0]) |
4562236b HW |
2293 | return DC_OK; |
2294 | ||
6bffebc9 | 2295 | /* Passive Dongle */ |
162f8078 | 2296 | if (max_pix_clk != 0 && get_timing_pixel_clock_100hz(timing) > max_pix_clk) |
6bffebc9 EY |
2297 | return DC_EXCEED_DONGLE_CAP; |
2298 | ||
2299 | /* Active Dongle*/ | |
f110892e | 2300 | if (!dp_active_dongle_validate_timing(timing, dpcd_caps)) |
6bffebc9 | 2301 | return DC_EXCEED_DONGLE_CAP; |
4562236b HW |
2302 | |
2303 | switch (stream->signal) { | |
2304 | case SIGNAL_TYPE_EDP: | |
2305 | case SIGNAL_TYPE_DISPLAY_PORT: | |
2306 | if (!dp_validate_mode_timing( | |
2307 | link, | |
2308 | timing)) | |
2309 | return DC_NO_DP_LINK_BANDWIDTH; | |
2310 | break; | |
2311 | ||
2312 | default: | |
2313 | break; | |
2314 | } | |
2315 | ||
2316 | return DC_OK; | |
2317 | } | |
2318 | ||
620a0d27 DF |
2319 | int dc_link_get_backlight_level(const struct dc_link *link) |
2320 | { | |
2321 | struct abm *abm = link->ctx->dc->res_pool->abm; | |
2322 | ||
262485a5 | 2323 | if (abm == NULL || abm->funcs->get_current_backlight == NULL) |
620a0d27 DF |
2324 | return DC_ERROR_UNEXPECTED; |
2325 | ||
262485a5 | 2326 | return (int) abm->funcs->get_current_backlight(abm); |
620a0d27 | 2327 | } |
4562236b | 2328 | |
262485a5 AK |
2329 | bool dc_link_set_backlight_level(const struct dc_link *link, |
2330 | uint32_t backlight_pwm_u16_16, | |
923fe495 | 2331 | uint32_t frame_ramp) |
4562236b | 2332 | { |
2b77dcc5 AK |
2333 | struct dc *dc = link->ctx->dc; |
2334 | struct abm *abm = dc->res_pool->abm; | |
2335 | struct dmcu *dmcu = dc->res_pool->dmcu; | |
4562236b | 2336 | unsigned int controller_id = 0; |
23bfb331 | 2337 | bool use_smooth_brightness = true; |
4562236b | 2338 | int i; |
5d4b05dd | 2339 | DC_LOGGER_INIT(link->ctx->logger); |
4562236b | 2340 | |
23bfb331 AK |
2341 | if ((dmcu == NULL) || |
2342 | (abm == NULL) || | |
262485a5 | 2343 | (abm->funcs->set_backlight_level_pwm == NULL)) |
6728b30c | 2344 | return false; |
4562236b | 2345 | |
23bfb331 AK |
2346 | use_smooth_brightness = dmcu->funcs->is_dmcu_initialized(dmcu); |
2347 | ||
262485a5 AK |
2348 | DC_LOG_BACKLIGHT("New Backlight level: %d (0x%X)\n", |
2349 | backlight_pwm_u16_16, backlight_pwm_u16_16); | |
4562236b | 2350 | |
d0778ebf | 2351 | if (dc_is_embedded_signal(link->connector_signal)) { |
6ccda157 | 2352 | for (i = 0; i < MAX_PIPES; i++) { |
2b77dcc5 AK |
2353 | if (dc->current_state->res_ctx.pipe_ctx[i].stream) { |
2354 | if (dc->current_state->res_ctx. | |
ceb3dbb4 | 2355 | pipe_ctx[i].stream->link |
5a42dc2b | 2356 | == link) { |
6728b30c AK |
2357 | /* DMCU -1 for all controller id values, |
2358 | * therefore +1 here | |
2359 | */ | |
2360 | controller_id = | |
2b77dcc5 | 2361 | dc->current_state-> |
6b670fa9 | 2362 | res_ctx.pipe_ctx[i].stream_res.tg->inst + |
6728b30c | 2363 | 1; |
5a42dc2b ZYL |
2364 | |
2365 | /* Disable brightness ramping when the display is blanked | |
2366 | * as it can hang the DMCU | |
2367 | */ | |
2b77dcc5 | 2368 | if (dc->current_state->res_ctx.pipe_ctx[i].plane_state == NULL) |
5a42dc2b ZYL |
2369 | frame_ramp = 0; |
2370 | } | |
6728b30c | 2371 | } |
4562236b | 2372 | } |
262485a5 | 2373 | abm->funcs->set_backlight_level_pwm( |
6728b30c | 2374 | abm, |
262485a5 | 2375 | backlight_pwm_u16_16, |
6728b30c | 2376 | frame_ramp, |
23bfb331 AK |
2377 | controller_id, |
2378 | use_smooth_brightness); | |
4562236b | 2379 | } |
4562236b HW |
2380 | |
2381 | return true; | |
2382 | } | |
2383 | ||
ab892598 RL |
2384 | bool dc_link_set_abm_disable(const struct dc_link *link) |
2385 | { | |
2b77dcc5 AK |
2386 | struct dc *dc = link->ctx->dc; |
2387 | struct abm *abm = dc->res_pool->abm; | |
ab892598 | 2388 | |
262485a5 | 2389 | if ((abm == NULL) || (abm->funcs->set_backlight_level_pwm == NULL)) |
ab892598 RL |
2390 | return false; |
2391 | ||
2392 | abm->funcs->set_abm_immediate_disable(abm); | |
2393 | ||
2394 | return true; | |
2395 | } | |
2396 | ||
ab4a4072 | 2397 | bool dc_link_set_psr_allow_active(struct dc_link *link, bool allow_active, bool wait) |
4562236b | 2398 | { |
2b77dcc5 AK |
2399 | struct dc *dc = link->ctx->dc; |
2400 | struct dmcu *dmcu = dc->res_pool->dmcu; | |
4c1a1335 | 2401 | struct dmub_psr *psr = dc->res_pool->psr; |
3548f073 | 2402 | |
4c1a1335 WW |
2403 | if ((psr != NULL) && link->psr_feature_enabled) |
2404 | psr->funcs->set_psr_enable(psr, allow_active); | |
2405 | else if ((dmcu != NULL && dmcu->funcs->is_dmcu_initialized(dmcu)) && link->psr_feature_enabled) | |
ab4a4072 EY |
2406 | dmcu->funcs->set_psr_enable(dmcu, allow_active, wait); |
2407 | ||
2408 | link->psr_allow_active = allow_active; | |
4562236b | 2409 | |
4562236b HW |
2410 | return true; |
2411 | } | |
2412 | ||
e0d08a40 RL |
2413 | bool dc_link_get_psr_state(const struct dc_link *link, uint32_t *psr_state) |
2414 | { | |
2b77dcc5 AK |
2415 | struct dc *dc = link->ctx->dc; |
2416 | struct dmcu *dmcu = dc->res_pool->dmcu; | |
4c1a1335 | 2417 | struct dmub_psr *psr = dc->res_pool->psr; |
e0d08a40 | 2418 | |
4c1a1335 WW |
2419 | if (psr != NULL && link->psr_feature_enabled) |
2420 | psr->funcs->get_psr_state(psr_state); | |
2421 | else if (dmcu != NULL && link->psr_feature_enabled) | |
e0d08a40 RL |
2422 | dmcu->funcs->get_psr_state(dmcu, psr_state); |
2423 | ||
2424 | return true; | |
2425 | } | |
2426 | ||
5ab5e4e6 NC |
2427 | static inline enum physical_phy_id |
2428 | transmitter_to_phy_id(enum transmitter transmitter_value) | |
2429 | { | |
2430 | switch (transmitter_value) { | |
2431 | case TRANSMITTER_UNIPHY_A: | |
2432 | return PHYLD_0; | |
2433 | case TRANSMITTER_UNIPHY_B: | |
2434 | return PHYLD_1; | |
2435 | case TRANSMITTER_UNIPHY_C: | |
2436 | return PHYLD_2; | |
2437 | case TRANSMITTER_UNIPHY_D: | |
2438 | return PHYLD_3; | |
2439 | case TRANSMITTER_UNIPHY_E: | |
2440 | return PHYLD_4; | |
2441 | case TRANSMITTER_UNIPHY_F: | |
2442 | return PHYLD_5; | |
2443 | case TRANSMITTER_NUTMEG_CRT: | |
2444 | return PHYLD_6; | |
2445 | case TRANSMITTER_TRAVIS_CRT: | |
2446 | return PHYLD_7; | |
2447 | case TRANSMITTER_TRAVIS_LCD: | |
2448 | return PHYLD_8; | |
2449 | case TRANSMITTER_UNIPHY_G: | |
2450 | return PHYLD_9; | |
2451 | case TRANSMITTER_COUNT: | |
2452 | return PHYLD_COUNT; | |
2453 | case TRANSMITTER_UNKNOWN: | |
2454 | return PHYLD_UNKNOWN; | |
2455 | default: | |
2456 | WARN_ONCE(1, "Unknown transmitter value %d\n", | |
2457 | transmitter_value); | |
2458 | return PHYLD_UNKNOWN; | |
2459 | } | |
2460 | } | |
2461 | ||
e0d08a40 RL |
2462 | bool dc_link_setup_psr(struct dc_link *link, |
2463 | const struct dc_stream_state *stream, struct psr_config *psr_config, | |
2464 | struct psr_context *psr_context) | |
2465 | { | |
2b77dcc5 | 2466 | struct dc *dc; |
e0d08a40 | 2467 | struct dmcu *dmcu; |
4c1a1335 | 2468 | struct dmub_psr *psr; |
e0d08a40 RL |
2469 | int i; |
2470 | /* updateSinkPsrDpcdConfig*/ | |
2471 | union dpcd_psr_configuration psr_configuration; | |
2472 | ||
2473 | psr_context->controllerId = CONTROLLER_ID_UNDEFINED; | |
2474 | ||
2475 | if (!link) | |
2476 | return false; | |
2477 | ||
2b77dcc5 AK |
2478 | dc = link->ctx->dc; |
2479 | dmcu = dc->res_pool->dmcu; | |
4c1a1335 | 2480 | psr = dc->res_pool->psr; |
e0d08a40 | 2481 | |
4c1a1335 | 2482 | if (!dmcu && !psr) |
e0d08a40 RL |
2483 | return false; |
2484 | ||
2485 | ||
2486 | memset(&psr_configuration, 0, sizeof(psr_configuration)); | |
2487 | ||
2488 | psr_configuration.bits.ENABLE = 1; | |
2489 | psr_configuration.bits.CRC_VERIFICATION = 1; | |
2490 | psr_configuration.bits.FRAME_CAPTURE_INDICATION = | |
2491 | psr_config->psr_frame_capture_indication_req; | |
2492 | ||
2493 | /* Check for PSR v2*/ | |
2494 | if (psr_config->psr_version == 0x2) { | |
2495 | /* For PSR v2 selective update. | |
2496 | * Indicates whether sink should start capturing | |
2497 | * immediately following active scan line, | |
2498 | * or starting with the 2nd active scan line. | |
2499 | */ | |
2500 | psr_configuration.bits.LINE_CAPTURE_INDICATION = 0; | |
2501 | /*For PSR v2, determines whether Sink should generate | |
2502 | * IRQ_HPD when CRC mismatch is detected. | |
2503 | */ | |
2504 | psr_configuration.bits.IRQ_HPD_WITH_CRC_ERROR = 1; | |
2505 | } | |
2506 | ||
2507 | dm_helpers_dp_write_dpcd( | |
2508 | link->ctx, | |
2509 | link, | |
2510 | 368, | |
2511 | &psr_configuration.raw, | |
2512 | sizeof(psr_configuration.raw)); | |
2513 | ||
2514 | psr_context->channel = link->ddc->ddc_pin->hw_info.ddc_channel; | |
2515 | psr_context->transmitterId = link->link_enc->transmitter; | |
2516 | psr_context->engineId = link->link_enc->preferred_engine; | |
2517 | ||
2518 | for (i = 0; i < MAX_PIPES; i++) { | |
2b77dcc5 | 2519 | if (dc->current_state->res_ctx.pipe_ctx[i].stream |
e0d08a40 RL |
2520 | == stream) { |
2521 | /* dmcu -1 for all controller id values, | |
2522 | * therefore +1 here | |
2523 | */ | |
2524 | psr_context->controllerId = | |
2b77dcc5 | 2525 | dc->current_state->res_ctx. |
e0d08a40 RL |
2526 | pipe_ctx[i].stream_res.tg->inst + 1; |
2527 | break; | |
2528 | } | |
2529 | } | |
2530 | ||
2531 | /* Hardcoded for now. Can be Pcie or Uniphy (or Unknown)*/ | |
2532 | psr_context->phyType = PHY_TYPE_UNIPHY; | |
2533 | /*PhyId is associated with the transmitter id*/ | |
5ab5e4e6 NC |
2534 | psr_context->smuPhyId = |
2535 | transmitter_to_phy_id(link->link_enc->transmitter); | |
e0d08a40 RL |
2536 | |
2537 | psr_context->crtcTimingVerticalTotal = stream->timing.v_total; | |
5b5abe95 | 2538 | psr_context->vsync_rate_hz = div64_u64(div64_u64((stream-> |
e0d08a40 RL |
2539 | timing.pix_clk_100hz * 100), |
2540 | stream->timing.v_total), | |
2541 | stream->timing.h_total); | |
2542 | ||
2543 | psr_context->psrSupportedDisplayConfig = true; | |
2544 | psr_context->psrExitLinkTrainingRequired = | |
2545 | psr_config->psr_exit_link_training_required; | |
2546 | psr_context->sdpTransmitLineNumDeadline = | |
2547 | psr_config->psr_sdp_transmit_line_num_deadline; | |
2548 | psr_context->psrFrameCaptureIndicationReq = | |
2549 | psr_config->psr_frame_capture_indication_req; | |
2550 | ||
2551 | psr_context->skipPsrWaitForPllLock = 0; /* only = 1 in KV */ | |
2552 | ||
2553 | psr_context->numberOfControllers = | |
2554 | link->dc->res_pool->timing_generator_count; | |
2555 | ||
2556 | psr_context->rfb_update_auto_en = true; | |
2557 | ||
2558 | /* 2 frames before enter PSR. */ | |
2559 | psr_context->timehyst_frames = 2; | |
2560 | /* half a frame | |
2561 | * (units in 100 lines, i.e. a value of 1 represents 100 lines) | |
2562 | */ | |
2563 | psr_context->hyst_lines = stream->timing.v_total / 2 / 100; | |
2564 | psr_context->aux_repeats = 10; | |
2565 | ||
2566 | psr_context->psr_level.u32all = 0; | |
2567 | ||
b86a1aa3 | 2568 | #if defined(CONFIG_DRM_AMD_DC_DCN) |
e0d08a40 RL |
2569 | /*skip power down the single pipe since it blocks the cstate*/ |
2570 | if (ASICREV_IS_RAVEN(link->ctx->asic_id.hw_internal_rev)) | |
2571 | psr_context->psr_level.bits.SKIP_CRTC_DISABLE = true; | |
2572 | #endif | |
2573 | ||
2574 | /* SMU will perform additional powerdown sequence. | |
2575 | * For unsupported ASICs, set psr_level flag to skip PSR | |
2576 | * static screen notification to SMU. | |
2577 | * (Always set for DAL2, did not check ASIC) | |
2578 | */ | |
2579 | psr_context->allow_smu_optimizations = psr_config->allow_smu_optimizations; | |
2580 | ||
2581 | /* Complete PSR entry before aborting to prevent intermittent | |
2582 | * freezes on certain eDPs | |
2583 | */ | |
2584 | psr_context->psr_level.bits.DISABLE_PSR_ENTRY_ABORT = 1; | |
2585 | ||
2586 | /* Controls additional delay after remote frame capture before | |
2587 | * continuing power down, default = 0 | |
2588 | */ | |
2589 | psr_context->frame_delay = 0; | |
2590 | ||
4c1a1335 WW |
2591 | if (psr) |
2592 | link->psr_feature_enabled = psr->funcs->setup_psr(psr, link, psr_context); | |
2593 | else | |
2594 | link->psr_feature_enabled = dmcu->funcs->setup_psr(dmcu, link, psr_context); | |
e0d08a40 RL |
2595 | |
2596 | /* psr_enabled == 0 indicates setup_psr did not succeed, but this | |
2597 | * should not happen since firmware should be running at this point | |
2598 | */ | |
2599 | if (link->psr_feature_enabled == 0) | |
2600 | ASSERT(0); | |
2601 | ||
2602 | return true; | |
2603 | ||
2604 | } | |
2605 | ||
d0778ebf | 2606 | const struct dc_link_status *dc_link_get_status(const struct dc_link *link) |
4562236b | 2607 | { |
4562236b HW |
2608 | return &link->link_status; |
2609 | } | |
2610 | ||
d0778ebf | 2611 | void core_link_resume(struct dc_link *link) |
4562236b | 2612 | { |
d0778ebf | 2613 | if (link->connector_signal != SIGNAL_TYPE_VIRTUAL) |
b0c4e977 | 2614 | program_hpd_filter(link); |
4562236b HW |
2615 | } |
2616 | ||
0971c40e | 2617 | static struct fixed31_32 get_pbn_per_slot(struct dc_stream_state *stream) |
4562236b | 2618 | { |
332c1191 | 2619 | struct fixed31_32 mbytes_per_sec; |
fe798de5 CP |
2620 | uint32_t link_rate_in_mbytes_per_sec = dc_link_bandwidth_kbps(stream->link, |
2621 | &stream->link->cur_link_settings); | |
332c1191 NC |
2622 | link_rate_in_mbytes_per_sec /= 8000; /* Kbits to MBytes */ |
2623 | ||
2624 | mbytes_per_sec = dc_fixpt_from_int(link_rate_in_mbytes_per_sec); | |
2625 | ||
2626 | return dc_fixpt_div_int(mbytes_per_sec, 54); | |
4562236b HW |
2627 | } |
2628 | ||
4562236b HW |
2629 | static struct fixed31_32 get_pbn_from_timing(struct pipe_ctx *pipe_ctx) |
2630 | { | |
4562236b HW |
2631 | uint64_t kbps; |
2632 | struct fixed31_32 peak_kbps; | |
2633 | uint32_t numerator; | |
2634 | uint32_t denominator; | |
2635 | ||
e49f6936 | 2636 | kbps = dc_bandwidth_in_kbps_from_timing(&pipe_ctx->stream->timing); |
4562236b HW |
2637 | |
2638 | /* | |
2639 | * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006 | |
2640 | * The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on | |
2641 | * common multiplier to render an integer PBN for all link rate/lane | |
2642 | * counts combinations | |
2643 | * calculate | |
2644 | * peak_kbps *= (1006/1000) | |
2645 | * peak_kbps *= (64/54) | |
2646 | * peak_kbps *= 8 convert to bytes | |
2647 | */ | |
2648 | ||
2649 | numerator = 64 * PEAK_FACTOR_X1000; | |
2650 | denominator = 54 * 8 * 1000 * 1000; | |
2651 | kbps *= numerator; | |
eb0e5154 | 2652 | peak_kbps = dc_fixpt_from_fraction(kbps, denominator); |
4562236b HW |
2653 | |
2654 | return peak_kbps; | |
2655 | } | |
2656 | ||
2657 | static void update_mst_stream_alloc_table( | |
d0778ebf | 2658 | struct dc_link *link, |
4562236b HW |
2659 | struct stream_encoder *stream_enc, |
2660 | const struct dp_mst_stream_allocation_table *proposed_table) | |
2661 | { | |
2662 | struct link_mst_stream_allocation work_table[MAX_CONTROLLER_NUM] = { | |
2663 | { 0 } }; | |
2664 | struct link_mst_stream_allocation *dc_alloc; | |
2665 | ||
2666 | int i; | |
2667 | int j; | |
2668 | ||
2669 | /* if DRM proposed_table has more than one new payload */ | |
2670 | ASSERT(proposed_table->stream_count - | |
2671 | link->mst_stream_alloc_table.stream_count < 2); | |
2672 | ||
d0778ebf | 2673 | /* copy proposed_table to link, add stream encoder */ |
4562236b HW |
2674 | for (i = 0; i < proposed_table->stream_count; i++) { |
2675 | ||
2676 | for (j = 0; j < link->mst_stream_alloc_table.stream_count; j++) { | |
2677 | dc_alloc = | |
2678 | &link->mst_stream_alloc_table.stream_allocations[j]; | |
2679 | ||
2680 | if (dc_alloc->vcp_id == | |
2681 | proposed_table->stream_allocations[i].vcp_id) { | |
2682 | ||
2683 | work_table[i] = *dc_alloc; | |
2684 | break; /* exit j loop */ | |
2685 | } | |
2686 | } | |
2687 | ||
2688 | /* new vcp_id */ | |
2689 | if (j == link->mst_stream_alloc_table.stream_count) { | |
2690 | work_table[i].vcp_id = | |
2691 | proposed_table->stream_allocations[i].vcp_id; | |
2692 | work_table[i].slot_count = | |
2693 | proposed_table->stream_allocations[i].slot_count; | |
2694 | work_table[i].stream_enc = stream_enc; | |
2695 | } | |
2696 | } | |
2697 | ||
2698 | /* update link->mst_stream_alloc_table with work_table */ | |
2699 | link->mst_stream_alloc_table.stream_count = | |
2700 | proposed_table->stream_count; | |
2701 | for (i = 0; i < MAX_CONTROLLER_NUM; i++) | |
2702 | link->mst_stream_alloc_table.stream_allocations[i] = | |
2703 | work_table[i]; | |
2704 | } | |
2705 | ||
2706 | /* convert link_mst_stream_alloc_table to dm dp_mst_stream_alloc_table | |
2707 | * because stream_encoder is not exposed to dm | |
2708 | */ | |
48af9b91 | 2709 | enum dc_status dc_link_allocate_mst_payload(struct pipe_ctx *pipe_ctx) |
4562236b | 2710 | { |
0971c40e | 2711 | struct dc_stream_state *stream = pipe_ctx->stream; |
ceb3dbb4 | 2712 | struct dc_link *link = stream->link; |
4562236b | 2713 | struct link_encoder *link_encoder = link->link_enc; |
8e9c4c8c | 2714 | struct stream_encoder *stream_encoder = pipe_ctx->stream_res.stream_enc; |
4562236b HW |
2715 | struct dp_mst_stream_allocation_table proposed_table = {0}; |
2716 | struct fixed31_32 avg_time_slots_per_mtp; | |
2717 | struct fixed31_32 pbn; | |
2718 | struct fixed31_32 pbn_per_slot; | |
2719 | uint8_t i; | |
48af9b91 | 2720 | enum act_return_status ret; |
5d4b05dd | 2721 | DC_LOGGER_INIT(link->ctx->logger); |
4562236b HW |
2722 | |
2723 | /* enable_link_dp_mst already check link->enabled_stream_count | |
2724 | * and stream is in link->stream[]. This is called during set mode, | |
2725 | * stream_enc is available. | |
2726 | */ | |
2727 | ||
2728 | /* get calculate VC payload for stream: stream_alloc */ | |
2729 | if (dm_helpers_dp_mst_write_payload_allocation_table( | |
2730 | stream->ctx, | |
4fa086b9 | 2731 | stream, |
4562236b HW |
2732 | &proposed_table, |
2733 | true)) { | |
2734 | update_mst_stream_alloc_table( | |
8e9c4c8c | 2735 | link, pipe_ctx->stream_res.stream_enc, &proposed_table); |
4562236b HW |
2736 | } |
2737 | else | |
1296423b | 2738 | DC_LOG_WARNING("Failed to update" |
4562236b HW |
2739 | "MST allocation table for" |
2740 | "pipe idx:%d\n", | |
2741 | pipe_ctx->pipe_idx); | |
2742 | ||
1296423b | 2743 | DC_LOG_MST("%s " |
4562236b HW |
2744 | "stream_count: %d: \n ", |
2745 | __func__, | |
2746 | link->mst_stream_alloc_table.stream_count); | |
2747 | ||
2748 | for (i = 0; i < MAX_CONTROLLER_NUM; i++) { | |
3032deb5 | 2749 | DC_LOG_MST("stream_enc[%d]: %p " |
4562236b HW |
2750 | "stream[%d].vcp_id: %d " |
2751 | "stream[%d].slot_count: %d\n", | |
2752 | i, | |
3032deb5 | 2753 | (void *) link->mst_stream_alloc_table.stream_allocations[i].stream_enc, |
4562236b HW |
2754 | i, |
2755 | link->mst_stream_alloc_table.stream_allocations[i].vcp_id, | |
2756 | i, | |
2757 | link->mst_stream_alloc_table.stream_allocations[i].slot_count); | |
2758 | } | |
2759 | ||
2760 | ASSERT(proposed_table.stream_count > 0); | |
2761 | ||
2762 | /* program DP source TX for payload */ | |
2763 | link_encoder->funcs->update_mst_stream_allocation_table( | |
2764 | link_encoder, | |
2765 | &link->mst_stream_alloc_table); | |
2766 | ||
2767 | /* send down message */ | |
48af9b91 | 2768 | ret = dm_helpers_dp_mst_poll_for_allocation_change_trigger( |
4562236b | 2769 | stream->ctx, |
4fa086b9 | 2770 | stream); |
4562236b | 2771 | |
48af9b91 AL |
2772 | if (ret != ACT_LINK_LOST) { |
2773 | dm_helpers_dp_mst_send_payload_allocation( | |
2774 | stream->ctx, | |
2775 | stream, | |
2776 | true); | |
2777 | } | |
4562236b HW |
2778 | |
2779 | /* slot X.Y for only current stream */ | |
2780 | pbn_per_slot = get_pbn_per_slot(stream); | |
2781 | pbn = get_pbn_from_timing(pipe_ctx); | |
eb0e5154 | 2782 | avg_time_slots_per_mtp = dc_fixpt_div(pbn, pbn_per_slot); |
4562236b HW |
2783 | |
2784 | stream_encoder->funcs->set_mst_bandwidth( | |
2785 | stream_encoder, | |
2786 | avg_time_slots_per_mtp); | |
2787 | ||
2788 | return DC_OK; | |
2789 | ||
2790 | } | |
2791 | ||
2792 | static enum dc_status deallocate_mst_payload(struct pipe_ctx *pipe_ctx) | |
2793 | { | |
0971c40e | 2794 | struct dc_stream_state *stream = pipe_ctx->stream; |
ceb3dbb4 | 2795 | struct dc_link *link = stream->link; |
4562236b | 2796 | struct link_encoder *link_encoder = link->link_enc; |
8e9c4c8c | 2797 | struct stream_encoder *stream_encoder = pipe_ctx->stream_res.stream_enc; |
4562236b | 2798 | struct dp_mst_stream_allocation_table proposed_table = {0}; |
eb0e5154 | 2799 | struct fixed31_32 avg_time_slots_per_mtp = dc_fixpt_from_int(0); |
4562236b | 2800 | uint8_t i; |
d0778ebf | 2801 | bool mst_mode = (link->type == dc_connection_mst_branch); |
5d4b05dd | 2802 | DC_LOGGER_INIT(link->ctx->logger); |
4562236b HW |
2803 | |
2804 | /* deallocate_mst_payload is called before disable link. When mode or | |
2805 | * disable/enable monitor, new stream is created which is not in link | |
2806 | * stream[] yet. For this, payload is not allocated yet, so de-alloc | |
2807 | * should not done. For new mode set, map_resources will get engine | |
2808 | * for new stream, so stream_enc->id should be validated until here. | |
2809 | */ | |
2810 | ||
2811 | /* slot X.Y */ | |
2812 | stream_encoder->funcs->set_mst_bandwidth( | |
2813 | stream_encoder, | |
2814 | avg_time_slots_per_mtp); | |
2815 | ||
2816 | /* TODO: which component is responsible for remove payload table? */ | |
2817 | if (mst_mode) { | |
2818 | if (dm_helpers_dp_mst_write_payload_allocation_table( | |
2819 | stream->ctx, | |
4fa086b9 | 2820 | stream, |
4562236b HW |
2821 | &proposed_table, |
2822 | false)) { | |
2823 | ||
2824 | update_mst_stream_alloc_table( | |
8e9c4c8c | 2825 | link, pipe_ctx->stream_res.stream_enc, &proposed_table); |
4562236b HW |
2826 | } |
2827 | else { | |
1296423b | 2828 | DC_LOG_WARNING("Failed to update" |
4562236b HW |
2829 | "MST allocation table for" |
2830 | "pipe idx:%d\n", | |
2831 | pipe_ctx->pipe_idx); | |
2832 | } | |
2833 | } | |
2834 | ||
1296423b | 2835 | DC_LOG_MST("%s" |
4562236b HW |
2836 | "stream_count: %d: ", |
2837 | __func__, | |
2838 | link->mst_stream_alloc_table.stream_count); | |
2839 | ||
2840 | for (i = 0; i < MAX_CONTROLLER_NUM; i++) { | |
3032deb5 | 2841 | DC_LOG_MST("stream_enc[%d]: %p " |
4562236b HW |
2842 | "stream[%d].vcp_id: %d " |
2843 | "stream[%d].slot_count: %d\n", | |
2844 | i, | |
3032deb5 | 2845 | (void *) link->mst_stream_alloc_table.stream_allocations[i].stream_enc, |
4562236b HW |
2846 | i, |
2847 | link->mst_stream_alloc_table.stream_allocations[i].vcp_id, | |
2848 | i, | |
2849 | link->mst_stream_alloc_table.stream_allocations[i].slot_count); | |
2850 | } | |
2851 | ||
2852 | link_encoder->funcs->update_mst_stream_allocation_table( | |
2853 | link_encoder, | |
2854 | &link->mst_stream_alloc_table); | |
2855 | ||
2856 | if (mst_mode) { | |
2857 | dm_helpers_dp_mst_poll_for_allocation_change_trigger( | |
2858 | stream->ctx, | |
4fa086b9 | 2859 | stream); |
4562236b HW |
2860 | |
2861 | dm_helpers_dp_mst_send_payload_allocation( | |
2862 | stream->ctx, | |
4fa086b9 | 2863 | stream, |
4562236b HW |
2864 | false); |
2865 | } | |
2866 | ||
2867 | return DC_OK; | |
2868 | } | |
ffdaeb1f PH |
2869 | |
2870 | enum dc_status dc_link_reallocate_mst_payload(struct dc_link *link) | |
2871 | { | |
2872 | int i; | |
2873 | struct pipe_ctx *pipe_ctx; | |
2874 | ||
2875 | // Clear all of MST payload then reallocate | |
2876 | for (i = 0; i < MAX_PIPES; i++) { | |
2877 | pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i]; | |
14ae7665 PH |
2878 | |
2879 | /* driver enable split pipe for external monitors | |
2880 | * we have to check pipe_ctx is split pipe or not | |
2881 | * If it's split pipe, driver using top pipe to | |
2882 | * reaallocate. | |
2883 | */ | |
2884 | if (!pipe_ctx || pipe_ctx->top_pipe) | |
2885 | continue; | |
2886 | ||
2887 | if (pipe_ctx->stream && pipe_ctx->stream->link == link && | |
ffdaeb1f PH |
2888 | pipe_ctx->stream->dpms_off == false && |
2889 | pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { | |
2890 | deallocate_mst_payload(pipe_ctx); | |
2891 | } | |
2892 | } | |
2893 | ||
2894 | for (i = 0; i < MAX_PIPES; i++) { | |
2895 | pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i]; | |
14ae7665 PH |
2896 | |
2897 | if (!pipe_ctx || pipe_ctx->top_pipe) | |
2898 | continue; | |
2899 | ||
2900 | if (pipe_ctx->stream && pipe_ctx->stream->link == link && | |
ffdaeb1f PH |
2901 | pipe_ctx->stream->dpms_off == false && |
2902 | pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { | |
2903 | /* enable/disable PHY will clear connection between BE and FE | |
2904 | * need to restore it. | |
2905 | */ | |
2906 | link->link_enc->funcs->connect_dig_be_to_fe(link->link_enc, | |
2907 | pipe_ctx->stream_res.stream_enc->id, true); | |
2908 | dc_link_allocate_mst_payload(pipe_ctx); | |
2909 | } | |
2910 | } | |
2911 | ||
2912 | return DC_OK; | |
2913 | } | |
2914 | ||
d462fcf5 BL |
2915 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
2916 | static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off) | |
2917 | { | |
2918 | struct cp_psp *cp_psp = &pipe_ctx->stream->ctx->cp_psp; | |
2919 | if (cp_psp && cp_psp->funcs.update_stream_config) { | |
2920 | struct cp_psp_stream_config config; | |
2921 | ||
2922 | memset(&config, 0, sizeof(config)); | |
2923 | ||
2924 | config.otg_inst = (uint8_t) pipe_ctx->stream_res.tg->inst; | |
2925 | config.stream_enc_inst = (uint8_t) pipe_ctx->stream_res.stream_enc->id; | |
2926 | config.link_enc_inst = pipe_ctx->stream->link->link_enc_hw_inst; | |
2927 | config.dpms_off = dpms_off; | |
2928 | config.dm_stream_ctx = pipe_ctx->stream->dm_stream_context; | |
2929 | cp_psp->funcs.update_stream_config(cp_psp->handle, &config); | |
2930 | } | |
2931 | } | |
2932 | #endif | |
4562236b | 2933 | |
ab8db3e1 AG |
2934 | void core_link_enable_stream( |
2935 | struct dc_state *state, | |
2936 | struct pipe_ctx *pipe_ctx) | |
4562236b | 2937 | { |
2b77dcc5 | 2938 | struct dc *dc = pipe_ctx->stream->ctx->dc; |
1e7e86c4 | 2939 | struct dc_stream_state *stream = pipe_ctx->stream; |
4cac1e6d | 2940 | enum dc_status status; |
5d4b05dd | 2941 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
4cac1e6d | 2942 | |
2b77dcc5 | 2943 | if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment) && |
ef5a7d26 WL |
2944 | dc_is_virtual_signal(pipe_ctx->stream->signal)) |
2945 | return; | |
2946 | ||
248cbed6 | 2947 | if (!dc_is_virtual_signal(pipe_ctx->stream->signal)) { |
ceb3dbb4 JL |
2948 | stream->link->link_enc->funcs->setup( |
2949 | stream->link->link_enc, | |
1e7e86c4 ST |
2950 | pipe_ctx->stream->signal); |
2951 | pipe_ctx->stream_res.stream_enc->funcs->setup_stereo_sync( | |
2952 | pipe_ctx->stream_res.stream_enc, | |
2953 | pipe_ctx->stream_res.tg->inst, | |
2954 | stream->timing.timing_3d_format != TIMING_3D_FORMAT_NONE); | |
2955 | } | |
2956 | ||
2957 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) | |
2958 | pipe_ctx->stream_res.stream_enc->funcs->dp_set_stream_attribute( | |
2959 | pipe_ctx->stream_res.stream_enc, | |
2960 | &stream->timing, | |
bb1cb98e | 2961 | stream->output_color_space, |
5ed78cd6 | 2962 | stream->use_vsc_sdp_for_colorimetry, |
bb1cb98e | 2963 | stream->link->dpcd_caps.dprx_feature.bits.SST_SPLIT_SDP_CAP); |
1e7e86c4 | 2964 | |
02553f57 | 2965 | if (dc_is_hdmi_tmds_signal(pipe_ctx->stream->signal)) |
1e7e86c4 ST |
2966 | pipe_ctx->stream_res.stream_enc->funcs->hdmi_set_stream_attribute( |
2967 | pipe_ctx->stream_res.stream_enc, | |
2968 | &stream->timing, | |
2969 | stream->phy_pix_clk, | |
2970 | pipe_ctx->stream_res.audio != NULL); | |
2971 | ||
eed928dc CL |
2972 | pipe_ctx->stream->link->link_state_valid = true; |
2973 | ||
1e7e86c4 ST |
2974 | if (dc_is_dvi_signal(pipe_ctx->stream->signal)) |
2975 | pipe_ctx->stream_res.stream_enc->funcs->dvi_set_stream_attribute( | |
2976 | pipe_ctx->stream_res.stream_enc, | |
2977 | &stream->timing, | |
2978 | (pipe_ctx->stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) ? | |
2979 | true : false); | |
2980 | ||
11c3ee48 AD |
2981 | if (dc_is_lvds_signal(pipe_ctx->stream->signal)) |
2982 | pipe_ctx->stream_res.stream_enc->funcs->lvds_set_stream_attribute( | |
2983 | pipe_ctx->stream_res.stream_enc, | |
2984 | &stream->timing); | |
2985 | ||
2b77dcc5 | 2986 | if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { |
d2d7885f AK |
2987 | bool apply_edp_fast_boot_optimization = |
2988 | pipe_ctx->stream->apply_edp_fast_boot_optimization; | |
2989 | ||
2990 | pipe_ctx->stream->apply_edp_fast_boot_optimization = false; | |
2991 | ||
aa9c4abe | 2992 | resource_build_info_frame(pipe_ctx); |
2b77dcc5 | 2993 | dc->hwss.update_info_frame(pipe_ctx); |
aa9c4abe | 2994 | |
d2d7885f AK |
2995 | /* Do not touch link on seamless boot optimization. */ |
2996 | if (pipe_ctx->stream->apply_seamless_boot_optimization) { | |
2997 | pipe_ctx->stream->dpms_off = false; | |
d462fcf5 BL |
2998 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
2999 | update_psp_stream_config(pipe_ctx, false); | |
3000 | #endif | |
d2d7885f AK |
3001 | return; |
3002 | } | |
3003 | ||
aa9c4abe NC |
3004 | /* eDP lit up by bios already, no need to enable again. */ |
3005 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_EDP && | |
d2d7885f | 3006 | apply_edp_fast_boot_optimization) { |
aa9c4abe | 3007 | pipe_ctx->stream->dpms_off = false; |
d462fcf5 BL |
3008 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
3009 | update_psp_stream_config(pipe_ctx, false); | |
3010 | #endif | |
aa9c4abe NC |
3011 | return; |
3012 | } | |
4cac1e6d | 3013 | |
aa9c4abe NC |
3014 | if (pipe_ctx->stream->dpms_off) |
3015 | return; | |
f0362823 | 3016 | |
aa9c4abe | 3017 | status = enable_link(state, pipe_ctx); |
c0ba5ec7 | 3018 | |
aa9c4abe | 3019 | if (status != DC_OK) { |
1296423b | 3020 | DC_LOG_WARNING("enabling link %u failed: %d\n", |
ceb3dbb4 | 3021 | pipe_ctx->stream->link->link_index, |
c0ba5ec7 KC |
3022 | status); |
3023 | ||
3024 | /* Abort stream enable *unless* the failure was due to | |
3025 | * DP link training - some DP monitors will recover and | |
00f713c6 EY |
3026 | * show the stream anyway. But MST displays can't proceed |
3027 | * without link training. | |
c0ba5ec7 | 3028 | */ |
00f713c6 EY |
3029 | if (status != DC_FAIL_DP_LINK_TRAINING || |
3030 | pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { | |
c0ba5ec7 KC |
3031 | BREAK_TO_DEBUGGER(); |
3032 | return; | |
3033 | } | |
aa9c4abe | 3034 | } |
4562236b | 3035 | |
2b77dcc5 | 3036 | dc->hwss.enable_audio_stream(pipe_ctx); |
1a05873f | 3037 | |
aa9c4abe NC |
3038 | /* turn off otg test pattern if enable */ |
3039 | if (pipe_ctx->stream_res.tg->funcs->set_test_pattern) | |
3040 | pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg, | |
3041 | CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, | |
3042 | COLOR_DEPTH_UNDEFINED); | |
71021265 | 3043 | |
1a9e3d45 NC |
3044 | if (pipe_ctx->stream->timing.flags.DSC) { |
3045 | if (dc_is_dp_signal(pipe_ctx->stream->signal) || | |
3046 | dc_is_virtual_signal(pipe_ctx->stream->signal)) | |
3047 | dp_set_dsc_enable(pipe_ctx, true); | |
3048 | } | |
2b77dcc5 | 3049 | dc->hwss.enable_stream(pipe_ctx); |
4562236b | 3050 | |
1a9e3d45 | 3051 | /* Set DPS PPS SDP (AKA "info frames") */ |
606b3551 DL |
3052 | if (pipe_ctx->stream->timing.flags.DSC) { |
3053 | if (dc_is_dp_signal(pipe_ctx->stream->signal) || | |
3054 | dc_is_virtual_signal(pipe_ctx->stream->signal)) | |
1a9e3d45 | 3055 | dp_set_dsc_pps_sdp(pipe_ctx, true); |
97bda032 | 3056 | } |
1a9e3d45 NC |
3057 | |
3058 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) | |
48af9b91 | 3059 | dc_link_allocate_mst_payload(pipe_ctx); |
1a9e3d45 | 3060 | |
2b77dcc5 | 3061 | dc->hwss.unblank_stream(pipe_ctx, |
ceb3dbb4 | 3062 | &pipe_ctx->stream->link->cur_link_settings); |
aa9c4abe | 3063 | |
14fee4ca JA |
3064 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) |
3065 | enable_stream_features(pipe_ctx); | |
d462fcf5 BL |
3066 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
3067 | update_psp_stream_config(pipe_ctx, false); | |
3068 | #endif | |
2b77dcc5 | 3069 | } else { // if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) |
97bda032 HW |
3070 | if (dc_is_dp_signal(pipe_ctx->stream->signal) || |
3071 | dc_is_virtual_signal(pipe_ctx->stream->signal)) | |
3072 | dp_set_dsc_enable(pipe_ctx, true); | |
3073 | ||
3074 | } | |
4562236b HW |
3075 | } |
3076 | ||
57430404 | 3077 | void core_link_disable_stream(struct pipe_ctx *pipe_ctx) |
4562236b | 3078 | { |
2b77dcc5 | 3079 | struct dc *dc = pipe_ctx->stream->ctx->dc; |
ab0cb022 | 3080 | struct dc_stream_state *stream = pipe_ctx->stream; |
052fa7e8 | 3081 | struct dc_link *link = stream->sink->link; |
4562236b | 3082 | |
2b77dcc5 | 3083 | if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment) && |
ef5a7d26 WL |
3084 | dc_is_virtual_signal(pipe_ctx->stream->signal)) |
3085 | return; | |
3086 | ||
d462fcf5 BL |
3087 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
3088 | update_psp_stream_config(pipe_ctx, true); | |
3089 | #endif | |
3090 | ||
2b77dcc5 | 3091 | dc->hwss.blank_stream(pipe_ctx); |
8c9d90ee | 3092 | |
4562236b HW |
3093 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) |
3094 | deallocate_mst_payload(pipe_ctx); | |
3095 | ||
052fa7e8 CL |
3096 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) { |
3097 | struct ext_hdmi_settings settings = {0}; | |
3098 | enum engine_id eng_id = pipe_ctx->stream_res.stream_enc->id; | |
ab0cb022 | 3099 | |
052fa7e8 CL |
3100 | unsigned short masked_chip_caps = link->chip_caps & |
3101 | EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK; | |
3102 | //Need to inform that sink is going to use legacy HDMI mode. | |
3103 | dal_ddc_service_write_scdc_data( | |
3104 | link->ddc, | |
3105 | 165000,//vbios only handles 165Mhz. | |
3106 | false); | |
3107 | if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_TISN65DP159RSBT) { | |
3108 | /* DP159, Retimer settings */ | |
3109 | if (get_ext_hdmi_settings(pipe_ctx, eng_id, &settings)) | |
3110 | write_i2c_retimer_setting(pipe_ctx, | |
3111 | false, false, &settings); | |
3112 | else | |
3113 | write_i2c_default_retimer_setting(pipe_ctx, | |
3114 | false, false); | |
3115 | } else if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_PI3EQX1204) { | |
3116 | /* PI3EQX1204, Redriver settings */ | |
3117 | write_i2c_redriver_setting(pipe_ctx, false); | |
3118 | } | |
3119 | } | |
2b77dcc5 | 3120 | dc->hwss.disable_stream(pipe_ctx); |
4562236b | 3121 | |
ceb3dbb4 | 3122 | disable_link(pipe_ctx->stream->link, pipe_ctx->stream->signal); |
606b3551 DL |
3123 | if (pipe_ctx->stream->timing.flags.DSC) { |
3124 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) | |
3125 | dp_set_dsc_enable(pipe_ctx, false); | |
97bda032 | 3126 | } |
4562236b HW |
3127 | } |
3128 | ||
15e17335 CL |
3129 | void core_link_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) |
3130 | { | |
2b77dcc5 | 3131 | struct dc *dc = pipe_ctx->stream->ctx->dc; |
15e17335 | 3132 | |
a280a71f | 3133 | if (!dc_is_hdmi_signal(pipe_ctx->stream->signal)) |
15e17335 CL |
3134 | return; |
3135 | ||
2b77dcc5 | 3136 | dc->hwss.set_avmute(pipe_ctx, enable); |
15e17335 CL |
3137 | } |
3138 | ||
a27f1996 YAS |
3139 | /** |
3140 | ***************************************************************************** | |
3141 | * Function: dc_link_enable_hpd_filter | |
3142 | * | |
3143 | * @brief | |
3144 | * If enable is true, programs HPD filter on associated HPD line using | |
3145 | * delay_on_disconnect/delay_on_connect values dependent on | |
3146 | * link->connector_signal | |
3147 | * | |
3148 | * If enable is false, programs HPD filter on associated HPD line with no | |
3149 | * delays on connect or disconnect | |
3150 | * | |
3151 | * @param [in] link: pointer to the dc link | |
3152 | * @param [in] enable: boolean specifying whether to enable hbd | |
3153 | ***************************************************************************** | |
3154 | */ | |
11fffe45 | 3155 | void dc_link_enable_hpd_filter(struct dc_link *link, bool enable) |
9a6995ce JB |
3156 | { |
3157 | struct gpio *hpd; | |
3158 | ||
11fffe45 JB |
3159 | if (enable) { |
3160 | link->is_hpd_filter_disabled = false; | |
3161 | program_hpd_filter(link); | |
3162 | } else { | |
9a6995ce JB |
3163 | link->is_hpd_filter_disabled = true; |
3164 | /* Obtain HPD handle */ | |
3165 | hpd = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); | |
3166 | ||
3167 | if (!hpd) | |
3168 | return; | |
3169 | ||
3170 | /* Setup HPD filtering */ | |
3171 | if (dal_gpio_open(hpd, GPIO_MODE_INTERRUPT) == GPIO_RESULT_OK) { | |
3172 | struct gpio_hpd_config config; | |
3173 | ||
3174 | config.delay_on_connect = 0; | |
3175 | config.delay_on_disconnect = 0; | |
3176 | ||
3177 | dal_irq_setup_hpd_filter(hpd, &config); | |
3178 | ||
3179 | dal_gpio_close(hpd); | |
3180 | } else { | |
3181 | ASSERT_CRITICAL(false); | |
3182 | } | |
3183 | /* Release HPD handle */ | |
3184 | dal_gpio_destroy_irq(&hpd); | |
3185 | } | |
3186 | } | |
3187 | ||
e49f6936 WL |
3188 | uint32_t dc_bandwidth_in_kbps_from_timing( |
3189 | const struct dc_crtc_timing *timing) | |
3190 | { | |
3191 | uint32_t bits_per_channel = 0; | |
3192 | uint32_t kbps; | |
3193 | ||
97bda032 HW |
3194 | if (timing->flags.DSC) { |
3195 | kbps = (timing->pix_clk_100hz * timing->dsc_cfg.bits_per_pixel); | |
3196 | kbps = kbps / 160 + ((kbps % 160) ? 1 : 0); | |
3197 | return kbps; | |
3198 | } | |
97bda032 | 3199 | |
e49f6936 WL |
3200 | switch (timing->display_color_depth) { |
3201 | case COLOR_DEPTH_666: | |
3202 | bits_per_channel = 6; | |
3203 | break; | |
3204 | case COLOR_DEPTH_888: | |
3205 | bits_per_channel = 8; | |
3206 | break; | |
3207 | case COLOR_DEPTH_101010: | |
3208 | bits_per_channel = 10; | |
3209 | break; | |
3210 | case COLOR_DEPTH_121212: | |
3211 | bits_per_channel = 12; | |
3212 | break; | |
3213 | case COLOR_DEPTH_141414: | |
3214 | bits_per_channel = 14; | |
3215 | break; | |
3216 | case COLOR_DEPTH_161616: | |
3217 | bits_per_channel = 16; | |
3218 | break; | |
3219 | default: | |
3220 | break; | |
3221 | } | |
3222 | ||
3223 | ASSERT(bits_per_channel != 0); | |
3224 | ||
3225 | kbps = timing->pix_clk_100hz / 10; | |
3226 | kbps *= bits_per_channel; | |
3227 | ||
3228 | if (timing->flags.Y_ONLY != 1) { | |
3229 | /*Only YOnly make reduce bandwidth by 1/3 compares to RGB*/ | |
3230 | kbps *= 3; | |
3231 | if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) | |
3232 | kbps /= 2; | |
3233 | else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) | |
3234 | kbps = kbps * 2 / 3; | |
3235 | } | |
3236 | ||
3237 | return kbps; | |
3238 | ||
3239 | } | |
fe798de5 CP |
3240 | |
3241 | void dc_link_set_drive_settings(struct dc *dc, | |
3242 | struct link_training_settings *lt_settings, | |
3243 | const struct dc_link *link) | |
3244 | { | |
3245 | ||
3246 | int i; | |
3247 | ||
3248 | for (i = 0; i < dc->link_count; i++) { | |
3249 | if (dc->links[i] == link) | |
3250 | break; | |
3251 | } | |
3252 | ||
3253 | if (i >= dc->link_count) | |
3254 | ASSERT_CRITICAL(false); | |
3255 | ||
3256 | dc_link_dp_set_drive_settings(dc->links[i], lt_settings); | |
3257 | } | |
3258 | ||
3259 | void dc_link_perform_link_training(struct dc *dc, | |
3260 | struct dc_link_settings *link_setting, | |
3261 | bool skip_video_pattern) | |
3262 | { | |
3263 | int i; | |
3264 | ||
3265 | for (i = 0; i < dc->link_count; i++) | |
3266 | dc_link_dp_perform_link_training( | |
3267 | dc->links[i], | |
3268 | link_setting, | |
3269 | skip_video_pattern); | |
3270 | } | |
3271 | ||
3272 | void dc_link_set_preferred_link_settings(struct dc *dc, | |
3273 | struct dc_link_settings *link_setting, | |
3274 | struct dc_link *link) | |
3275 | { | |
3276 | int i; | |
3277 | struct pipe_ctx *pipe; | |
3278 | struct dc_stream_state *link_stream; | |
3279 | struct dc_link_settings store_settings = *link_setting; | |
3280 | ||
3281 | link->preferred_link_setting = store_settings; | |
3282 | ||
3283 | /* Retrain with preferred link settings only relevant for | |
3284 | * DP signal type | |
08900ab7 | 3285 | * Check for non-DP signal or if passive dongle present |
fe798de5 | 3286 | */ |
08900ab7 ST |
3287 | if (!dc_is_dp_signal(link->connector_signal) || |
3288 | link->dongle_max_pix_clk > 0) | |
fe798de5 CP |
3289 | return; |
3290 | ||
3291 | for (i = 0; i < MAX_PIPES; i++) { | |
3292 | pipe = &dc->current_state->res_ctx.pipe_ctx[i]; | |
3293 | if (pipe->stream && pipe->stream->link) { | |
252f3d95 HW |
3294 | if (pipe->stream->link == link) { |
3295 | link_stream = pipe->stream; | |
fe798de5 | 3296 | break; |
252f3d95 | 3297 | } |
fe798de5 CP |
3298 | } |
3299 | } | |
3300 | ||
3301 | /* Stream not found */ | |
3302 | if (i == MAX_PIPES) | |
3303 | return; | |
3304 | ||
fe798de5 CP |
3305 | /* Cannot retrain link if backend is off */ |
3306 | if (link_stream->dpms_off) | |
3307 | return; | |
3308 | ||
252f3d95 | 3309 | decide_link_settings(link_stream, &store_settings); |
fe798de5 CP |
3310 | |
3311 | if ((store_settings.lane_count != LANE_COUNT_UNKNOWN) && | |
3312 | (store_settings.link_rate != LINK_RATE_UNKNOWN)) | |
3313 | dp_retrain_link_dp_test(link, &store_settings, false); | |
3314 | } | |
3315 | ||
e0a6440a DG |
3316 | void dc_link_set_preferred_training_settings(struct dc *dc, |
3317 | struct dc_link_settings *link_setting, | |
3318 | struct dc_link_training_overrides *lt_overrides, | |
3319 | struct dc_link *link, | |
3320 | bool skip_immediate_retrain) | |
3321 | { | |
3322 | if (lt_overrides != NULL) | |
3323 | link->preferred_training_settings = *lt_overrides; | |
3324 | else | |
3325 | memset(&link->preferred_training_settings, 0, sizeof(link->preferred_training_settings)); | |
3326 | ||
3327 | if (link_setting != NULL) { | |
3328 | link->preferred_link_setting = *link_setting; | |
3329 | } else { | |
3330 | link->preferred_link_setting.lane_count = LANE_COUNT_UNKNOWN; | |
3331 | link->preferred_link_setting.link_rate = LINK_RATE_UNKNOWN; | |
3332 | } | |
3333 | ||
3334 | /* Retrain now, or wait until next stream update to apply */ | |
3335 | if (skip_immediate_retrain == false) | |
3336 | dc_link_set_preferred_link_settings(dc, &link->preferred_link_setting, link); | |
3337 | } | |
3338 | ||
fe798de5 CP |
3339 | void dc_link_enable_hpd(const struct dc_link *link) |
3340 | { | |
3341 | dc_link_dp_enable_hpd(link); | |
3342 | } | |
3343 | ||
3344 | void dc_link_disable_hpd(const struct dc_link *link) | |
3345 | { | |
3346 | dc_link_dp_disable_hpd(link); | |
3347 | } | |
3348 | ||
fe798de5 CP |
3349 | void dc_link_set_test_pattern(struct dc_link *link, |
3350 | enum dp_test_pattern test_pattern, | |
2057b7e1 | 3351 | enum dp_test_pattern_color_space test_pattern_color_space, |
fe798de5 CP |
3352 | const struct link_training_settings *p_link_settings, |
3353 | const unsigned char *p_custom_pattern, | |
3354 | unsigned int cust_pattern_size) | |
3355 | { | |
3356 | if (link != NULL) | |
3357 | dc_link_dp_set_test_pattern( | |
3358 | link, | |
3359 | test_pattern, | |
2057b7e1 | 3360 | test_pattern_color_space, |
fe798de5 CP |
3361 | p_link_settings, |
3362 | p_custom_pattern, | |
3363 | cust_pattern_size); | |
3364 | } | |
3365 | ||
3366 | uint32_t dc_link_bandwidth_kbps( | |
3367 | const struct dc_link *link, | |
3368 | const struct dc_link_settings *link_setting) | |
3369 | { | |
3370 | uint32_t link_bw_kbps = | |
3371 | link_setting->link_rate * LINK_RATE_REF_FREQ_IN_KHZ; /* bytes per sec */ | |
3372 | ||
3373 | link_bw_kbps *= 8; /* 8 bits per byte*/ | |
3374 | link_bw_kbps *= link_setting->lane_count; | |
3375 | ||
bedbbe6a CP |
3376 | if (link->dpcd_caps.fec_cap.bits.FEC_CAPABLE) { |
3377 | /* Account for FEC overhead. | |
3378 | * We have to do it based on caps, | |
3379 | * and not based on FEC being set ready, | |
3380 | * because FEC is set ready too late in | |
3381 | * the process to correctly be picked up | |
3382 | * by mode enumeration. | |
3383 | * | |
3384 | * There's enough zeros at the end of 'kbps' | |
3385 | * that make the below operation 100% precise | |
3386 | * for our purposes. | |
3387 | * 'long long' makes it work even for HDMI 2.1 | |
3388 | * max bandwidth (and much, much bigger bandwidths | |
3389 | * than that, actually). | |
3390 | * | |
3391 | * NOTE: Reducing link BW by 3% may not be precise | |
3392 | * because it may be a stream BT that increases by 3%, and so | |
3393 | * 1/1.03 = 0.970873 factor should have been used instead, | |
3394 | * but the difference is minimal and is in a safe direction, | |
3395 | * which all works well around potential ambiguity of DP 1.4a spec. | |
3396 | */ | |
5f65ae34 AB |
3397 | link_bw_kbps = mul_u64_u32_shr(BIT_ULL(32) * 970LL / 1000, |
3398 | link_bw_kbps, 32); | |
bedbbe6a | 3399 | } |
bedbbe6a | 3400 | |
fe798de5 CP |
3401 | return link_bw_kbps; |
3402 | ||
3403 | } | |
3404 | ||
3405 | const struct dc_link_settings *dc_link_get_link_cap( | |
3406 | const struct dc_link *link) | |
3407 | { | |
3408 | if (link->preferred_link_setting.lane_count != LANE_COUNT_UNKNOWN && | |
3409 | link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) | |
3410 | return &link->preferred_link_setting; | |
3411 | return &link->verified_link_cap; | |
3412 | } | |
8547058b LH |
3413 | |
3414 | void dc_link_overwrite_extended_receiver_cap( | |
3415 | struct dc_link *link) | |
3416 | { | |
3417 | dp_overwrite_extended_receiver_cap(link); | |
3418 | } | |
3419 |