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" |
cdca3f21 | 29 | #include "atomfirmware.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" | |
d144b40a | 36 | #include "link/link_dp_dpia.h" |
a98cdd8c | 37 | #include "link/link_ddc.h" |
4562236b | 38 | #include "link_hwss.h" |
4370f72e | 39 | #include "link.h" |
7f93c1de | 40 | #include "opp.h" |
fb3466a4 | 41 | |
4562236b HW |
42 | #include "link_encoder.h" |
43 | #include "hw_sequencer.h" | |
44 | #include "resource.h" | |
6728b30c | 45 | #include "abm.h" |
4562236b | 46 | #include "fixed31_32.h" |
eaca91ee | 47 | #include "dpcd_defs.h" |
3548f073 | 48 | #include "dmcu.h" |
dc88b4a6 | 49 | #include "hw/clk_mgr.h" |
9dac88d8 | 50 | #include "dce/dmub_psr.h" |
cdca3f21 | 51 | #include "dmub/dmub_srv.h" |
d4caa72e | 52 | #include "inc/hw/panel_cntl.h" |
ede4f6da | 53 | #include "inc/link_enc_cfg.h" |
a28d0bac | 54 | #include "link/link_dpcd.h" |
5fed53c7 | 55 | #include "link/link_dp_trace.h" |
4370f72e | 56 | #include "link/link_hpd.h" |
630168a9 | 57 | #include "link/link_dp_training.h" |
94dfeaa4 | 58 | #include "link/link_dp_phy.h" |
d5a43956 | 59 | #include "link/link_dp_capability.h" |
4562236b | 60 | |
18b4f1a0 MS |
61 | #include "dc/dcn30/dcn30_vpg.h" |
62 | ||
5d4b05dd BL |
63 | #define DC_LOGGER_INIT(logger) |
64 | ||
4562236b | 65 | #define LINK_INFO(...) \ |
1296423b | 66 | DC_LOG_HW_HOTPLUG( \ |
4562236b HW |
67 | __VA_ARGS__) |
68 | ||
2f14bc89 CL |
69 | #define RETIMER_REDRIVER_INFO(...) \ |
70 | DC_LOG_RETIMER_REDRIVER( \ | |
71 | __VA_ARGS__) | |
4562236b HW |
72 | |
73 | /******************************************************************************* | |
74 | * Private functions | |
75 | ******************************************************************************/ | |
d9e32672 | 76 | static void dc_link_destruct(struct dc_link *link) |
4562236b HW |
77 | { |
78 | int i; | |
79 | ||
621514aa | 80 | if (link->hpd_gpio) { |
ac627caf CH |
81 | dal_gpio_destroy_irq(&link->hpd_gpio); |
82 | link->hpd_gpio = NULL; | |
83 | } | |
84 | ||
d0778ebf | 85 | if (link->ddc) |
a98cdd8c | 86 | link_destroy_ddc_service(&link->ddc); |
4562236b | 87 | |
d4caa72e AK |
88 | if (link->panel_cntl) |
89 | link->panel_cntl->funcs->destroy(&link->panel_cntl); | |
9da3d050 | 90 | |
e1f4328f | 91 | if (link->link_enc) { |
f42ef862 JK |
92 | /* Update link encoder resource tracking variables. These are used for |
93 | * the dynamic assignment of link encoders to streams. Virtual links | |
94 | * are not assigned encoder resources on creation. | |
e1f4328f | 95 | */ |
f42ef862 JK |
96 | if (link->link_id.id != CONNECTOR_ID_VIRTUAL) { |
97 | link->dc->res_pool->link_encoders[link->eng_id - ENGINE_ID_DIGA] = NULL; | |
98 | link->dc->res_pool->dig_link_enc_count--; | |
99 | } | |
4562236b | 100 | link->link_enc->funcs->destroy(&link->link_enc); |
e1f4328f | 101 | } |
4562236b | 102 | |
d0778ebf HW |
103 | if (link->local_sink) |
104 | dc_sink_release(link->local_sink); | |
4562236b | 105 | |
d0778ebf HW |
106 | for (i = 0; i < link->sink_count; ++i) |
107 | dc_sink_release(link->remote_sinks[i]); | |
4562236b HW |
108 | } |
109 | ||
cf3a2627 JL |
110 | bool dc_link_wait_for_t12(struct dc_link *link) |
111 | { | |
112 | if (link->connector_signal == SIGNAL_TYPE_EDP && link->dc->hwss.edp_wait_for_T12) { | |
113 | link->dc->hwss.edp_wait_for_T12(link); | |
114 | ||
115 | return true; | |
116 | } | |
117 | ||
118 | return false; | |
119 | } | |
120 | ||
2119aa17 DF |
121 | /** |
122 | * dc_link_detect_sink() - Determine if there is a sink connected | |
123 | * | |
ac492ec9 | 124 | * @link: pointer to the dc link |
2119aa17 DF |
125 | * @type: Returned connection type |
126 | * Does not detect downstream devices, such as MST sinks | |
127 | * or display connected through active dongles | |
128 | */ | |
fbbdadf2 | 129 | bool dc_link_detect_sink(struct dc_link *link, enum dc_connection_type *type) |
4562236b HW |
130 | { |
131 | uint32_t is_hpd_high = 0; | |
4562236b | 132 | |
11c3ee48 AD |
133 | if (link->connector_signal == SIGNAL_TYPE_LVDS) { |
134 | *type = dc_connection_single; | |
135 | return true; | |
136 | } | |
137 | ||
abe882a3 AK |
138 | if (link->connector_signal == SIGNAL_TYPE_EDP) { |
139 | /*in case it is not on*/ | |
a8201902 LM |
140 | if (!link->dc->config.edp_no_power_sequencing) |
141 | link->dc->hwss.edp_power_control(link, true); | |
0a6414e7 | 142 | link->dc->hwss.edp_wait_for_hpd_ready(link, true); |
abe882a3 | 143 | } |
0a6414e7 | 144 | |
99732e52 JK |
145 | /* Link may not have physical HPD pin. */ |
146 | if (link->ep_type != DISPLAY_ENDPOINT_PHY) { | |
fc0b067d | 147 | if (link->is_hpd_pending || !dc_link_dpia_query_hpd_status(link)) |
99732e52 | 148 | *type = dc_connection_none; |
6ef86fa8 MS |
149 | else |
150 | *type = dc_connection_single; | |
99732e52 JK |
151 | |
152 | return true; | |
153 | } | |
154 | ||
4370f72e | 155 | if (!query_hpd_status(link, &is_hpd_high)) |
4562236b HW |
156 | goto hpd_gpio_failure; |
157 | ||
4562236b HW |
158 | if (is_hpd_high) { |
159 | *type = dc_connection_single; | |
160 | /* TODO: need to do the actual detection */ | |
161 | } else { | |
162 | *type = dc_connection_none; | |
163 | } | |
164 | ||
165 | return true; | |
166 | ||
167 | hpd_gpio_failure: | |
168 | return false; | |
169 | } | |
170 | ||
621514aa | 171 | static enum ddc_transaction_type get_ddc_transaction_type(enum signal_type sink_signal) |
4562236b HW |
172 | { |
173 | enum ddc_transaction_type transaction_type = DDC_TRANSACTION_TYPE_NONE; | |
174 | ||
175 | switch (sink_signal) { | |
176 | case SIGNAL_TYPE_DVI_SINGLE_LINK: | |
177 | case SIGNAL_TYPE_DVI_DUAL_LINK: | |
178 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
179 | case SIGNAL_TYPE_LVDS: | |
180 | case SIGNAL_TYPE_RGB: | |
181 | transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
182 | break; | |
183 | ||
184 | case SIGNAL_TYPE_DISPLAY_PORT: | |
185 | case SIGNAL_TYPE_EDP: | |
186 | transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; | |
187 | break; | |
188 | ||
189 | case SIGNAL_TYPE_DISPLAY_PORT_MST: | |
190 | /* MST does not use I2COverAux, but there is the | |
191 | * SPECIAL use case for "immediate dwnstrm device | |
21764099 MW |
192 | * access" (EPR#370830). |
193 | */ | |
4562236b HW |
194 | transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; |
195 | break; | |
196 | ||
197 | default: | |
198 | break; | |
199 | } | |
200 | ||
201 | return transaction_type; | |
202 | } | |
203 | ||
621514aa MW |
204 | static enum signal_type get_basic_signal_type(struct graphics_object_id encoder, |
205 | struct graphics_object_id downstream) | |
4562236b HW |
206 | { |
207 | if (downstream.type == OBJECT_TYPE_CONNECTOR) { | |
208 | switch (downstream.id) { | |
209 | case CONNECTOR_ID_SINGLE_LINK_DVII: | |
210 | switch (encoder.id) { | |
211 | case ENCODER_ID_INTERNAL_DAC1: | |
212 | case ENCODER_ID_INTERNAL_KLDSCP_DAC1: | |
213 | case ENCODER_ID_INTERNAL_DAC2: | |
214 | case ENCODER_ID_INTERNAL_KLDSCP_DAC2: | |
215 | return SIGNAL_TYPE_RGB; | |
216 | default: | |
217 | return SIGNAL_TYPE_DVI_SINGLE_LINK; | |
218 | } | |
219 | break; | |
220 | case CONNECTOR_ID_DUAL_LINK_DVII: | |
221 | { | |
222 | switch (encoder.id) { | |
223 | case ENCODER_ID_INTERNAL_DAC1: | |
224 | case ENCODER_ID_INTERNAL_KLDSCP_DAC1: | |
225 | case ENCODER_ID_INTERNAL_DAC2: | |
226 | case ENCODER_ID_INTERNAL_KLDSCP_DAC2: | |
227 | return SIGNAL_TYPE_RGB; | |
228 | default: | |
229 | return SIGNAL_TYPE_DVI_DUAL_LINK; | |
230 | } | |
231 | } | |
232 | break; | |
233 | case CONNECTOR_ID_SINGLE_LINK_DVID: | |
234 | return SIGNAL_TYPE_DVI_SINGLE_LINK; | |
235 | case CONNECTOR_ID_DUAL_LINK_DVID: | |
236 | return SIGNAL_TYPE_DVI_DUAL_LINK; | |
237 | case CONNECTOR_ID_VGA: | |
238 | return SIGNAL_TYPE_RGB; | |
239 | case CONNECTOR_ID_HDMI_TYPE_A: | |
240 | return SIGNAL_TYPE_HDMI_TYPE_A; | |
241 | case CONNECTOR_ID_LVDS: | |
242 | return SIGNAL_TYPE_LVDS; | |
243 | case CONNECTOR_ID_DISPLAY_PORT: | |
8cb3c169 | 244 | case CONNECTOR_ID_USBC: |
4562236b HW |
245 | return SIGNAL_TYPE_DISPLAY_PORT; |
246 | case CONNECTOR_ID_EDP: | |
247 | return SIGNAL_TYPE_EDP; | |
248 | default: | |
249 | return SIGNAL_TYPE_NONE; | |
250 | } | |
251 | } else if (downstream.type == OBJECT_TYPE_ENCODER) { | |
252 | switch (downstream.id) { | |
253 | case ENCODER_ID_EXTERNAL_NUTMEG: | |
254 | case ENCODER_ID_EXTERNAL_TRAVIS: | |
255 | return SIGNAL_TYPE_DISPLAY_PORT; | |
256 | default: | |
257 | return SIGNAL_TYPE_NONE; | |
258 | } | |
259 | } | |
260 | ||
261 | return SIGNAL_TYPE_NONE; | |
262 | } | |
263 | ||
ac492ec9 | 264 | /* |
2119aa17 DF |
265 | * dc_link_is_dp_sink_present() - Check if there is a native DP |
266 | * or passive DP-HDMI dongle connected | |
4562236b | 267 | */ |
aac5db82 | 268 | bool dc_link_is_dp_sink_present(struct dc_link *link) |
4562236b HW |
269 | { |
270 | enum gpio_result gpio_result; | |
271 | uint32_t clock_pin = 0; | |
bd4905a9 | 272 | uint8_t retry = 0; |
4562236b HW |
273 | struct ddc *ddc; |
274 | ||
275 | enum connector_id connector_id = | |
276 | dal_graphics_object_id_get_connector_id(link->link_id); | |
277 | ||
278 | bool present = | |
279 | ((connector_id == CONNECTOR_ID_DISPLAY_PORT) || | |
8cb3c169 ST |
280 | (connector_id == CONNECTOR_ID_EDP) || |
281 | (connector_id == CONNECTOR_ID_USBC)); | |
4562236b | 282 | |
a98cdd8c | 283 | ddc = get_ddc_pin(link->ddc); |
4562236b HW |
284 | |
285 | if (!ddc) { | |
286 | BREAK_TO_DEBUGGER(); | |
287 | return present; | |
288 | } | |
289 | ||
290 | /* Open GPIO and set it to I2C mode */ | |
291 | /* Note: this GpioMode_Input will be converted | |
292 | * to GpioConfigType_I2cAuxDualMode in GPIO component, | |
21764099 MW |
293 | * which indicates we need additional delay |
294 | */ | |
4562236b | 295 | |
621514aa MW |
296 | if (dal_ddc_open(ddc, GPIO_MODE_INPUT, |
297 | GPIO_DDC_CONFIG_TYPE_MODE_I2C) != GPIO_RESULT_OK) { | |
8fb3a636 | 298 | dal_ddc_close(ddc); |
4562236b HW |
299 | |
300 | return present; | |
301 | } | |
302 | ||
bd4905a9 PH |
303 | /* |
304 | * Read GPIO: DP sink is present if both clock and data pins are zero | |
305 | * | |
306 | * [W/A] plug-unplug DP cable, sometimes customer board has | |
307 | * one short pulse on clk_pin(1V, < 1ms). DP will be config to HDMI/DVI | |
308 | * then monitor can't br light up. Add retry 3 times | |
309 | * But in real passive dongle, it need additional 3ms to detect | |
310 | */ | |
311 | do { | |
312 | gpio_result = dal_gpio_get_value(ddc->pin_clock, &clock_pin); | |
313 | ASSERT(gpio_result == GPIO_RESULT_OK); | |
314 | if (clock_pin) | |
315 | udelay(1000); | |
316 | else | |
317 | break; | |
318 | } while (retry++ < 3); | |
4562236b | 319 | |
4dfb0bad | 320 | present = (gpio_result == GPIO_RESULT_OK) && !clock_pin; |
4562236b HW |
321 | |
322 | dal_ddc_close(ddc); | |
323 | ||
324 | return present; | |
325 | } | |
326 | ||
327 | /* | |
328 | * @brief | |
329 | * Detect output sink type | |
330 | */ | |
621514aa MW |
331 | static enum signal_type link_detect_sink(struct dc_link *link, |
332 | enum dc_detect_reason reason) | |
4562236b | 333 | { |
99732e52 JK |
334 | enum signal_type result; |
335 | struct graphics_object_id enc_id; | |
336 | ||
337 | if (link->is_dig_mapping_flexible) | |
338 | enc_id = (struct graphics_object_id){.id = ENCODER_ID_UNKNOWN}; | |
339 | else | |
340 | enc_id = link->link_enc->id; | |
341 | result = get_basic_signal_type(enc_id, link->link_id); | |
342 | ||
343 | /* Use basic signal type for link without physical connector. */ | |
344 | if (link->ep_type != DISPLAY_ENDPOINT_PHY) | |
345 | return result; | |
4562236b HW |
346 | |
347 | /* Internal digital encoder will detect only dongles | |
21764099 MW |
348 | * that require digital signal |
349 | */ | |
4562236b HW |
350 | |
351 | /* Detection mechanism is different | |
352 | * for different native connectors. | |
353 | * LVDS connector supports only LVDS signal; | |
354 | * PCIE is a bus slot, the actual connector needs to be detected first; | |
355 | * eDP connector supports only eDP signal; | |
21764099 MW |
356 | * HDMI should check straps for audio |
357 | */ | |
4562236b HW |
358 | |
359 | /* PCIE detects the actual connector on add-on board */ | |
50dc581a | 360 | if (link->link_id.id == CONNECTOR_ID_PCIE) { |
4562236b | 361 | /* ZAZTODO implement PCIE add-on card detection */ |
50dc581a | 362 | } |
4562236b HW |
363 | |
364 | switch (link->link_id.id) { | |
365 | case CONNECTOR_ID_HDMI_TYPE_A: { | |
366 | /* check audio support: | |
21764099 MW |
367 | * if native HDMI is not supported, switch to DVI |
368 | */ | |
621514aa MW |
369 | struct audio_support *aud_support = |
370 | &link->dc->res_pool->audio_support; | |
4562236b HW |
371 | |
372 | if (!aud_support->hdmi_audio_native) | |
373 | if (link->link_id.id == CONNECTOR_ID_HDMI_TYPE_A) | |
374 | result = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
375 | } | |
376 | break; | |
8cb3c169 ST |
377 | case CONNECTOR_ID_DISPLAY_PORT: |
378 | case CONNECTOR_ID_USBC: { | |
8f38b66c HW |
379 | /* DP HPD short pulse. Passive DP dongle will not |
380 | * have short pulse | |
381 | */ | |
382 | if (reason != DETECT_REASON_HPDRX) { | |
383 | /* Check whether DP signal detected: if not - | |
384 | * we assume signal is DVI; it could be corrected | |
385 | * to HDMI after dongle detection | |
386 | */ | |
aac5db82 | 387 | if (!dm_helpers_is_dp_sink_present(link)) |
8f38b66c HW |
388 | result = SIGNAL_TYPE_DVI_SINGLE_LINK; |
389 | } | |
4562236b HW |
390 | } |
391 | break; | |
392 | default: | |
393 | break; | |
394 | } | |
395 | ||
396 | return result; | |
397 | } | |
398 | ||
621514aa MW |
399 | static enum signal_type decide_signal_from_strap_and_dongle_type(enum display_dongle_type dongle_type, |
400 | struct audio_support *audio_support) | |
4562236b HW |
401 | { |
402 | enum signal_type signal = SIGNAL_TYPE_NONE; | |
403 | ||
404 | switch (dongle_type) { | |
405 | case DISPLAY_DONGLE_DP_HDMI_DONGLE: | |
406 | if (audio_support->hdmi_audio_on_dongle) | |
621514aa | 407 | signal = SIGNAL_TYPE_HDMI_TYPE_A; |
4562236b HW |
408 | else |
409 | signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
410 | break; | |
411 | case DISPLAY_DONGLE_DP_DVI_DONGLE: | |
412 | signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
413 | break; | |
414 | case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE: | |
415 | if (audio_support->hdmi_audio_native) | |
416 | signal = SIGNAL_TYPE_HDMI_TYPE_A; | |
417 | else | |
418 | signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
419 | break; | |
420 | default: | |
421 | signal = SIGNAL_TYPE_NONE; | |
422 | break; | |
423 | } | |
424 | ||
425 | return signal; | |
426 | } | |
427 | ||
a98cdd8c WL |
428 | static bool i2c_read( |
429 | struct ddc_service *ddc, | |
430 | uint32_t address, | |
431 | uint8_t *buffer, | |
432 | uint32_t len) | |
433 | { | |
434 | uint8_t offs_data = 0; | |
435 | struct i2c_payload payloads[2] = { | |
436 | { | |
437 | .write = true, | |
438 | .address = address, | |
439 | .length = 1, | |
440 | .data = &offs_data }, | |
441 | { | |
442 | .write = false, | |
443 | .address = address, | |
444 | .length = len, | |
445 | .data = buffer } }; | |
446 | ||
447 | struct i2c_command command = { | |
448 | .payloads = payloads, | |
449 | .number_of_payloads = 2, | |
450 | .engine = DDC_I2C_COMMAND_ENGINE, | |
451 | .speed = ddc->ctx->dc->caps.i2c_speed_in_khz }; | |
452 | ||
453 | return dm_helpers_submit_i2c( | |
454 | ddc->ctx, | |
455 | ddc->link, | |
456 | &command); | |
457 | } | |
458 | ||
459 | enum { | |
460 | DP_SINK_CAP_SIZE = | |
461 | DP_EDP_CONFIGURATION_CAP - DP_DPCD_REV + 1 | |
462 | }; | |
463 | ||
464 | static void query_dp_dual_mode_adaptor( | |
465 | struct ddc_service *ddc, | |
466 | struct display_sink_capability *sink_cap) | |
467 | { | |
468 | uint8_t i; | |
469 | bool is_valid_hdmi_signature; | |
470 | enum display_dongle_type *dongle = &sink_cap->dongle_type; | |
471 | uint8_t type2_dongle_buf[DP_ADAPTOR_TYPE2_SIZE]; | |
472 | bool is_type2_dongle = false; | |
473 | int retry_count = 2; | |
474 | struct dp_hdmi_dongle_signature_data *dongle_signature; | |
475 | ||
476 | /* Assume we have no valid DP passive dongle connected */ | |
477 | *dongle = DISPLAY_DONGLE_NONE; | |
478 | sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_HDMI_SAFE_MAX_TMDS_CLK; | |
479 | ||
480 | /* Read DP-HDMI dongle I2c (no response interpreted as DP-DVI dongle)*/ | |
481 | if (!i2c_read( | |
482 | ddc, | |
483 | DP_HDMI_DONGLE_ADDRESS, | |
484 | type2_dongle_buf, | |
485 | sizeof(type2_dongle_buf))) { | |
486 | /* Passive HDMI dongles can sometimes fail here without retrying*/ | |
487 | while (retry_count > 0) { | |
488 | if (i2c_read(ddc, | |
489 | DP_HDMI_DONGLE_ADDRESS, | |
490 | type2_dongle_buf, | |
491 | sizeof(type2_dongle_buf))) | |
492 | break; | |
493 | retry_count--; | |
494 | } | |
495 | if (retry_count == 0) { | |
496 | *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE; | |
497 | sink_cap->max_hdmi_pixel_clock = DP_ADAPTOR_DVI_MAX_TMDS_CLK; | |
498 | ||
499 | CONN_DATA_DETECT(ddc->link, type2_dongle_buf, sizeof(type2_dongle_buf), | |
500 | "DP-DVI passive dongle %dMhz: ", | |
501 | DP_ADAPTOR_DVI_MAX_TMDS_CLK / 1000); | |
502 | return; | |
503 | } | |
504 | } | |
505 | ||
506 | /* Check if Type 2 dongle.*/ | |
507 | if (type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_ID] == DP_ADAPTOR_TYPE2_ID) | |
508 | is_type2_dongle = true; | |
509 | ||
510 | dongle_signature = | |
511 | (struct dp_hdmi_dongle_signature_data *)type2_dongle_buf; | |
512 | ||
513 | is_valid_hdmi_signature = true; | |
514 | ||
515 | /* Check EOT */ | |
516 | if (dongle_signature->eot != DP_HDMI_DONGLE_SIGNATURE_EOT) { | |
517 | is_valid_hdmi_signature = false; | |
518 | } | |
519 | ||
520 | /* Check signature */ | |
521 | for (i = 0; i < sizeof(dongle_signature->id); ++i) { | |
522 | /* If its not the right signature, | |
523 | * skip mismatch in subversion byte.*/ | |
524 | if (dongle_signature->id[i] != | |
525 | dp_hdmi_dongle_signature_str[i] && i != 3) { | |
526 | ||
527 | if (is_type2_dongle) { | |
528 | is_valid_hdmi_signature = false; | |
529 | break; | |
530 | } | |
531 | ||
532 | } | |
533 | } | |
534 | ||
535 | if (is_type2_dongle) { | |
536 | uint32_t max_tmds_clk = | |
537 | type2_dongle_buf[DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK]; | |
538 | ||
539 | max_tmds_clk = max_tmds_clk * 2 + max_tmds_clk / 2; | |
540 | ||
541 | if (0 == max_tmds_clk || | |
542 | max_tmds_clk < DP_ADAPTOR_TYPE2_MIN_TMDS_CLK || | |
543 | max_tmds_clk > DP_ADAPTOR_TYPE2_MAX_TMDS_CLK) { | |
544 | *dongle = DISPLAY_DONGLE_DP_DVI_DONGLE; | |
545 | ||
546 | CONN_DATA_DETECT(ddc->link, type2_dongle_buf, | |
547 | sizeof(type2_dongle_buf), | |
548 | "DP-DVI passive dongle %dMhz: ", | |
549 | DP_ADAPTOR_DVI_MAX_TMDS_CLK / 1000); | |
550 | } else { | |
551 | if (is_valid_hdmi_signature == true) { | |
552 | *dongle = DISPLAY_DONGLE_DP_HDMI_DONGLE; | |
553 | ||
554 | CONN_DATA_DETECT(ddc->link, type2_dongle_buf, | |
555 | sizeof(type2_dongle_buf), | |
556 | "Type 2 DP-HDMI passive dongle %dMhz: ", | |
557 | max_tmds_clk); | |
558 | } else { | |
559 | *dongle = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE; | |
560 | ||
561 | CONN_DATA_DETECT(ddc->link, type2_dongle_buf, | |
562 | sizeof(type2_dongle_buf), | |
563 | "Type 2 DP-HDMI passive dongle (no signature) %dMhz: ", | |
564 | max_tmds_clk); | |
565 | ||
566 | } | |
567 | ||
568 | /* Multiply by 1000 to convert to kHz. */ | |
569 | sink_cap->max_hdmi_pixel_clock = | |
570 | max_tmds_clk * 1000; | |
571 | } | |
572 | sink_cap->is_dongle_type_one = false; | |
573 | ||
574 | } else { | |
575 | if (is_valid_hdmi_signature == true) { | |
576 | *dongle = DISPLAY_DONGLE_DP_HDMI_DONGLE; | |
577 | ||
578 | CONN_DATA_DETECT(ddc->link, type2_dongle_buf, | |
579 | sizeof(type2_dongle_buf), | |
580 | "Type 1 DP-HDMI passive dongle %dMhz: ", | |
581 | sink_cap->max_hdmi_pixel_clock / 1000); | |
582 | } else { | |
583 | *dongle = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE; | |
584 | ||
585 | CONN_DATA_DETECT(ddc->link, type2_dongle_buf, | |
586 | sizeof(type2_dongle_buf), | |
587 | "Type 1 DP-HDMI passive dongle (no signature) %dMhz: ", | |
588 | sink_cap->max_hdmi_pixel_clock / 1000); | |
589 | } | |
590 | sink_cap->is_dongle_type_one = true; | |
591 | } | |
592 | ||
593 | return; | |
594 | } | |
595 | ||
621514aa MW |
596 | static enum signal_type dp_passive_dongle_detection(struct ddc_service *ddc, |
597 | struct display_sink_capability *sink_cap, | |
598 | struct audio_support *audio_support) | |
4562236b | 599 | { |
a98cdd8c | 600 | query_dp_dual_mode_adaptor(ddc, sink_cap); |
621514aa MW |
601 | |
602 | return decide_signal_from_strap_and_dongle_type(sink_cap->dongle_type, | |
603 | audio_support); | |
4562236b HW |
604 | } |
605 | ||
d0778ebf | 606 | static void link_disconnect_sink(struct dc_link *link) |
4562236b | 607 | { |
d0778ebf HW |
608 | if (link->local_sink) { |
609 | dc_sink_release(link->local_sink); | |
610 | link->local_sink = NULL; | |
4562236b HW |
611 | } |
612 | ||
613 | link->dpcd_sink_count = 0; | |
1e9653a4 | 614 | //link->dpcd_caps.dpcd_rev.raw = 0; |
4562236b HW |
615 | } |
616 | ||
eb815442 ST |
617 | static void link_disconnect_remap(struct dc_sink *prev_sink, struct dc_link *link) |
618 | { | |
619 | dc_sink_release(link->local_sink); | |
620 | link->local_sink = prev_sink; | |
621 | } | |
622 | ||
fe8db3bc | 623 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
0023b7ee | 624 | bool dc_link_is_hdcp14(struct dc_link *link, enum signal_type signal) |
5f869379 BL |
625 | { |
626 | bool ret = false; | |
627 | ||
0023b7ee | 628 | switch (signal) { |
5f869379 BL |
629 | case SIGNAL_TYPE_DISPLAY_PORT: |
630 | case SIGNAL_TYPE_DISPLAY_PORT_MST: | |
631 | ret = link->hdcp_caps.bcaps.bits.HDCP_CAPABLE; | |
632 | break; | |
633 | case SIGNAL_TYPE_DVI_SINGLE_LINK: | |
634 | case SIGNAL_TYPE_DVI_DUAL_LINK: | |
635 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
636 | /* HDMI doesn't tell us its HDCP(1.4) capability, so assume to always be capable, | |
637 | * we can poll for bksv but some displays have an issue with this. Since its so rare | |
638 | * for a display to not be 1.4 capable, this assumtion is ok | |
639 | */ | |
640 | ret = true; | |
641 | break; | |
642 | default: | |
643 | break; | |
644 | } | |
645 | return ret; | |
646 | } | |
647 | ||
0023b7ee | 648 | bool dc_link_is_hdcp22(struct dc_link *link, enum signal_type signal) |
5f869379 BL |
649 | { |
650 | bool ret = false; | |
651 | ||
0023b7ee | 652 | switch (signal) { |
5f869379 BL |
653 | case SIGNAL_TYPE_DISPLAY_PORT: |
654 | case SIGNAL_TYPE_DISPLAY_PORT_MST: | |
655 | ret = (link->hdcp_caps.bcaps.bits.HDCP_CAPABLE && | |
656 | link->hdcp_caps.rx_caps.fields.byte0.hdcp_capable && | |
657 | (link->hdcp_caps.rx_caps.fields.version == 0x2)) ? 1 : 0; | |
658 | break; | |
659 | case SIGNAL_TYPE_DVI_SINGLE_LINK: | |
660 | case SIGNAL_TYPE_DVI_DUAL_LINK: | |
661 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
662 | ret = (link->hdcp_caps.rx_caps.fields.version == 0x4) ? 1:0; | |
663 | break; | |
664 | default: | |
665 | break; | |
666 | } | |
667 | ||
668 | return ret; | |
669 | } | |
670 | ||
fe8db3bc BL |
671 | static void query_hdcp_capability(enum signal_type signal, struct dc_link *link) |
672 | { | |
673 | struct hdcp_protection_message msg22; | |
674 | struct hdcp_protection_message msg14; | |
675 | ||
676 | memset(&msg22, 0, sizeof(struct hdcp_protection_message)); | |
677 | memset(&msg14, 0, sizeof(struct hdcp_protection_message)); | |
678 | memset(link->hdcp_caps.rx_caps.raw, 0, | |
679 | sizeof(link->hdcp_caps.rx_caps.raw)); | |
680 | ||
681 | if ((link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && | |
682 | link->ddc->transaction_type == | |
683 | DDC_TRANSACTION_TYPE_I2C_OVER_AUX) || | |
684 | link->connector_signal == SIGNAL_TYPE_EDP) { | |
685 | msg22.data = link->hdcp_caps.rx_caps.raw; | |
686 | msg22.length = sizeof(link->hdcp_caps.rx_caps.raw); | |
687 | msg22.msg_id = HDCP_MESSAGE_ID_RX_CAPS; | |
688 | } else { | |
689 | msg22.data = &link->hdcp_caps.rx_caps.fields.version; | |
690 | msg22.length = sizeof(link->hdcp_caps.rx_caps.fields.version); | |
691 | msg22.msg_id = HDCP_MESSAGE_ID_HDCP2VERSION; | |
692 | } | |
693 | msg22.version = HDCP_VERSION_22; | |
694 | msg22.link = HDCP_LINK_PRIMARY; | |
695 | msg22.max_retries = 5; | |
696 | dc_process_hdcp_msg(signal, link, &msg22); | |
697 | ||
698 | if (signal == SIGNAL_TYPE_DISPLAY_PORT || signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { | |
fe8db3bc BL |
699 | msg14.data = &link->hdcp_caps.bcaps.raw; |
700 | msg14.length = sizeof(link->hdcp_caps.bcaps.raw); | |
701 | msg14.msg_id = HDCP_MESSAGE_ID_READ_BCAPS; | |
702 | msg14.version = HDCP_VERSION_14; | |
703 | msg14.link = HDCP_LINK_PRIMARY; | |
704 | msg14.max_retries = 5; | |
705 | ||
4202ef47 | 706 | dc_process_hdcp_msg(signal, link, &msg14); |
fe8db3bc BL |
707 | } |
708 | ||
709 | } | |
710 | #endif | |
711 | ||
7f7652ee | 712 | static void read_current_link_settings_on_detect(struct dc_link *link) |
5b7c0d8d | 713 | { |
c78abac9 | 714 | union lane_count_set lane_count_set = {0}; |
5b7c0d8d AK |
715 | uint8_t link_bw_set; |
716 | uint8_t link_rate_set; | |
abe882a3 AK |
717 | uint32_t read_dpcd_retry_cnt = 10; |
718 | enum dc_status status = DC_ERROR_UNEXPECTED; | |
719 | int i; | |
c78abac9 | 720 | union max_down_spread max_down_spread = {0}; |
5b7c0d8d AK |
721 | |
722 | // Read DPCD 00101h to find out the number of lanes currently set | |
abe882a3 | 723 | for (i = 0; i < read_dpcd_retry_cnt; i++) { |
621514aa MW |
724 | status = core_link_read_dpcd(link, |
725 | DP_LANE_COUNT_SET, | |
726 | &lane_count_set.raw, | |
727 | sizeof(lane_count_set)); | |
abe882a3 AK |
728 | /* First DPCD read after VDD ON can fail if the particular board |
729 | * does not have HPD pin wired correctly. So if DPCD read fails, | |
730 | * which it should never happen, retry a few times. Target worst | |
731 | * case scenario of 80 ms. | |
732 | */ | |
733 | if (status == DC_OK) { | |
621514aa MW |
734 | link->cur_link_settings.lane_count = |
735 | lane_count_set.bits.LANE_COUNT_SET; | |
abe882a3 AK |
736 | break; |
737 | } | |
738 | ||
3e10f319 | 739 | msleep(8); |
abe882a3 AK |
740 | } |
741 | ||
5b7c0d8d AK |
742 | // Read DPCD 00100h to find if standard link rates are set |
743 | core_link_read_dpcd(link, DP_LINK_BW_SET, | |
621514aa | 744 | &link_bw_set, sizeof(link_bw_set)); |
5b7c0d8d AK |
745 | |
746 | if (link_bw_set == 0) { | |
7f7652ee ML |
747 | if (link->connector_signal == SIGNAL_TYPE_EDP) { |
748 | /* If standard link rates are not being used, | |
749 | * Read DPCD 00115h to find the edp link rate set used | |
750 | */ | |
751 | core_link_read_dpcd(link, DP_LINK_RATE_SET, | |
621514aa | 752 | &link_rate_set, sizeof(link_rate_set)); |
7f7652ee ML |
753 | |
754 | // edp_supported_link_rates_count = 0 for DP | |
755 | if (link_rate_set < link->dpcd_caps.edp_supported_link_rates_count) { | |
756 | link->cur_link_settings.link_rate = | |
621514aa | 757 | link->dpcd_caps.edp_supported_link_rates[link_rate_set]; |
7f7652ee ML |
758 | link->cur_link_settings.link_rate_set = link_rate_set; |
759 | link->cur_link_settings.use_link_rate_set = true; | |
760 | } | |
761 | } else { | |
762 | // Link Rate not found. Seamless boot may not work. | |
763 | ASSERT(false); | |
5b7c0d8d AK |
764 | } |
765 | } else { | |
766 | link->cur_link_settings.link_rate = link_bw_set; | |
767 | link->cur_link_settings.use_link_rate_set = false; | |
768 | } | |
701c75ce DL |
769 | // Read DPCD 00003h to find the max down spread. |
770 | core_link_read_dpcd(link, DP_MAX_DOWNSPREAD, | |
621514aa | 771 | &max_down_spread.raw, sizeof(max_down_spread)); |
701c75ce DL |
772 | link->cur_link_settings.link_spread = |
773 | max_down_spread.bits.MAX_DOWN_SPREAD ? | |
774 | LINK_SPREAD_05_DOWNSPREAD_30KHZ : LINK_SPREAD_DISABLED; | |
5b7c0d8d AK |
775 | } |
776 | ||
5b2b2416 MW |
777 | static bool detect_dp(struct dc_link *link, |
778 | struct display_sink_capability *sink_caps, | |
5b2b2416 | 779 | enum dc_detect_reason reason) |
4562236b | 780 | { |
8a58e25b | 781 | struct audio_support *audio_support = &link->dc->res_pool->audio_support; |
5b2b2416 | 782 | |
8f38b66c | 783 | sink_caps->signal = link_detect_sink(link, reason); |
4562236b HW |
784 | sink_caps->transaction_type = |
785 | get_ddc_transaction_type(sink_caps->signal); | |
786 | ||
787 | if (sink_caps->transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { | |
788 | sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT; | |
cdb39798 YS |
789 | if (!detect_dp_sink_caps(link)) |
790 | return false; | |
4562236b | 791 | |
c282d951 | 792 | if (is_dp_branch_device(link)) |
36c9137b DZ |
793 | /* DP SST branch */ |
794 | link->type = dc_connection_sst_branch; | |
4562236b HW |
795 | } else { |
796 | /* DP passive dongles */ | |
d0778ebf | 797 | sink_caps->signal = dp_passive_dongle_detection(link->ddc, |
5b2b2416 MW |
798 | sink_caps, |
799 | audio_support); | |
efbde23a | 800 | link->dpcd_caps.dongle_type = sink_caps->dongle_type; |
4a897de1 | 801 | link->dpcd_caps.is_dongle_type_one = sink_caps->is_dongle_type_one; |
1e9653a4 | 802 | link->dpcd_caps.dpcd_rev.raw = 0; |
4562236b | 803 | } |
cdb39798 YS |
804 | |
805 | return true; | |
4562236b HW |
806 | } |
807 | ||
eb815442 ST |
808 | static bool is_same_edid(struct dc_edid *old_edid, struct dc_edid *new_edid) |
809 | { | |
810 | if (old_edid->length != new_edid->length) | |
811 | return false; | |
812 | ||
813 | if (new_edid->length == 0) | |
814 | return false; | |
815 | ||
ded3491b MW |
816 | return (memcmp(old_edid->raw_edid, |
817 | new_edid->raw_edid, new_edid->length) == 0); | |
eb815442 ST |
818 | } |
819 | ||
8ccf0e20 | 820 | static bool wait_for_entering_dp_alt_mode(struct dc_link *link) |
b5b1f455 | 821 | { |
b5b1f455 EY |
822 | /** |
823 | * something is terribly wrong if time out is > 200ms. (5Hz) | |
824 | * 500 microseconds * 400 tries us 200 ms | |
825 | **/ | |
826 | unsigned int sleep_time_in_microseconds = 500; | |
827 | unsigned int tries_allowed = 400; | |
828 | bool is_in_alt_mode; | |
829 | unsigned long long enter_timestamp; | |
830 | unsigned long long finish_timestamp; | |
831 | unsigned long long time_taken_in_ns; | |
832 | int tries_taken; | |
833 | ||
834 | DC_LOGGER_INIT(link->ctx->logger); | |
835 | ||
ded3491b | 836 | if (!link->link_enc->funcs->is_in_alt_mode) |
b5b1f455 EY |
837 | return true; |
838 | ||
839 | is_in_alt_mode = link->link_enc->funcs->is_in_alt_mode(link->link_enc); | |
bd314901 | 840 | DC_LOG_DC("DP Alt mode state on HPD: %d\n", is_in_alt_mode); |
b5b1f455 EY |
841 | |
842 | if (is_in_alt_mode) | |
843 | return true; | |
844 | ||
845 | enter_timestamp = dm_get_timestamp(link->ctx); | |
846 | ||
847 | for (tries_taken = 0; tries_taken < tries_allowed; tries_taken++) { | |
848 | udelay(sleep_time_in_microseconds); | |
849 | /* ask the link if alt mode is enabled, if so return ok */ | |
850 | if (link->link_enc->funcs->is_in_alt_mode(link->link_enc)) { | |
b5b1f455 | 851 | finish_timestamp = dm_get_timestamp(link->ctx); |
ded3491b MW |
852 | time_taken_in_ns = |
853 | dm_get_elapse_time_in_ns(link->ctx, | |
854 | finish_timestamp, | |
855 | enter_timestamp); | |
b5b1f455 | 856 | DC_LOG_WARNING("Alt mode entered finished after %llu ms\n", |
b859c579 | 857 | div_u64(time_taken_in_ns, 1000000)); |
b5b1f455 EY |
858 | return true; |
859 | } | |
b5b1f455 EY |
860 | } |
861 | finish_timestamp = dm_get_timestamp(link->ctx); | |
862 | time_taken_in_ns = dm_get_elapse_time_in_ns(link->ctx, finish_timestamp, | |
863 | enter_timestamp); | |
864 | DC_LOG_WARNING("Alt mode has timed out after %llu ms\n", | |
ded3491b | 865 | div_u64(time_taken_in_ns, 1000000)); |
b5b1f455 EY |
866 | return false; |
867 | } | |
868 | ||
c282d951 WL |
869 | static void apply_dpia_mst_dsc_always_on_wa(struct dc_link *link) |
870 | { | |
c282d951 WL |
871 | /* Apply work around for tunneled MST on certain USB4 docks. Always use DSC if dock |
872 | * reports DSC support. | |
873 | */ | |
874 | if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && | |
875 | link->type == dc_connection_mst_branch && | |
876 | link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_90CC24 && | |
31d64b82 | 877 | link->dpcd_caps.branch_hw_revision == DP_BRANCH_HW_REV_20 && |
c282d951 WL |
878 | link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT && |
879 | !link->dc->debug.dpia_debug.bits.disable_mst_dsc_work_around) | |
880 | link->wa_flags.dpia_mst_dsc_always_on = true; | |
c282d951 WL |
881 | } |
882 | ||
883 | static void revert_dpia_mst_dsc_always_on_wa(struct dc_link *link) | |
884 | { | |
885 | /* Disable work around which keeps DSC on for tunneled MST on certain USB4 docks. */ | |
886 | if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) | |
887 | link->wa_flags.dpia_mst_dsc_always_on = false; | |
888 | } | |
889 | ||
890 | static bool discover_dp_mst_topology(struct dc_link *link, enum dc_detect_reason reason) | |
891 | { | |
892 | DC_LOGGER_INIT(link->ctx->logger); | |
893 | ||
894 | LINK_INFO("link=%d, mst branch is now Connected\n", | |
895 | link->link_index); | |
896 | ||
c282d951 | 897 | link->type = dc_connection_mst_branch; |
d3d42110 JX |
898 | apply_dpia_mst_dsc_always_on_wa(link); |
899 | ||
c282d951 WL |
900 | dm_helpers_dp_update_branch_info(link->ctx, link); |
901 | if (dm_helpers_dp_mst_start_top_mgr(link->ctx, | |
b6500759 | 902 | link, (reason == DETECT_REASON_BOOT || reason == DETECT_REASON_RESUMEFROMS3S4))) { |
c282d951 WL |
903 | link_disconnect_sink(link); |
904 | } else { | |
905 | link->type = dc_connection_sst_branch; | |
906 | } | |
907 | ||
908 | return link->type == dc_connection_mst_branch; | |
909 | } | |
910 | ||
aec4706b | 911 | bool reset_cur_dp_mst_topology(struct dc_link *link) |
c282d951 WL |
912 | { |
913 | DC_LOGGER_INIT(link->ctx->logger); | |
914 | ||
915 | LINK_INFO("link=%d, mst branch is now Disconnected\n", | |
916 | link->link_index); | |
917 | ||
918 | revert_dpia_mst_dsc_always_on_wa(link); | |
cc67aae1 | 919 | return dm_helpers_dp_mst_stop_top_mgr(link->ctx, link); |
c282d951 WL |
920 | } |
921 | ||
922 | static bool should_prepare_phy_clocks_for_link_verification(const struct dc *dc, | |
923 | enum dc_detect_reason reason) | |
924 | { | |
925 | int i; | |
926 | bool can_apply_seamless_boot = false; | |
927 | ||
928 | for (i = 0; i < dc->current_state->stream_count; i++) { | |
929 | if (dc->current_state->streams[i]->apply_seamless_boot_optimization) { | |
930 | can_apply_seamless_boot = true; | |
931 | break; | |
932 | } | |
933 | } | |
934 | ||
935 | return !can_apply_seamless_boot && reason != DETECT_REASON_BOOT; | |
936 | } | |
937 | ||
938 | static void prepare_phy_clocks_for_destructive_link_verification(const struct dc *dc) | |
939 | { | |
c282d951 | 940 | dc_z10_restore(dc); |
c282d951 WL |
941 | clk_mgr_exit_optimized_pwr_state(dc, dc->clk_mgr); |
942 | } | |
943 | ||
944 | static void restore_phy_clocks_for_destructive_link_verification(const struct dc *dc) | |
945 | { | |
946 | clk_mgr_optimize_pwr_state(dc, dc->clk_mgr); | |
947 | } | |
948 | ||
949 | static void set_all_streams_dpms_off_for_link(struct dc_link *link) | |
950 | { | |
951 | int i; | |
952 | struct pipe_ctx *pipe_ctx; | |
953 | struct dc_stream_update stream_update; | |
954 | bool dpms_off = true; | |
2426d71c | 955 | struct link_resource link_res = {0}; |
c282d951 WL |
956 | |
957 | memset(&stream_update, 0, sizeof(stream_update)); | |
958 | stream_update.dpms_off = &dpms_off; | |
959 | ||
960 | for (i = 0; i < MAX_PIPES; i++) { | |
961 | pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i]; | |
962 | if (pipe_ctx && pipe_ctx->stream && !pipe_ctx->stream->dpms_off && | |
963 | pipe_ctx->stream->link == link && !pipe_ctx->prev_odm_pipe) { | |
964 | stream_update.stream = pipe_ctx->stream; | |
965 | dc_commit_updates_for_stream(link->ctx->dc, NULL, 0, | |
966 | pipe_ctx->stream, &stream_update, | |
967 | link->ctx->dc->current_state); | |
968 | } | |
969 | } | |
2426d71c WL |
970 | |
971 | /* link can be also enabled by vbios. In this case it is not recorded | |
972 | * in pipe_ctx. Disable link phy here to make sure it is completely off | |
973 | */ | |
974 | dp_disable_link_phy(link, &link_res, link->connector_signal); | |
c282d951 WL |
975 | } |
976 | ||
977 | static void verify_link_capability_destructive(struct dc_link *link, | |
978 | struct dc_sink *sink, | |
979 | enum dc_detect_reason reason) | |
980 | { | |
c282d951 WL |
981 | bool should_prepare_phy_clocks = |
982 | should_prepare_phy_clocks_for_link_verification(link->dc, reason); | |
983 | ||
984 | if (should_prepare_phy_clocks) | |
985 | prepare_phy_clocks_for_destructive_link_verification(link->dc); | |
986 | ||
c282d951 WL |
987 | if (dc_is_dp_signal(link->local_sink->sink_signal)) { |
988 | struct dc_link_settings known_limit_link_setting = | |
989 | dp_get_max_link_cap(link); | |
c282d951 | 990 | set_all_streams_dpms_off_for_link(link); |
c282d951 | 991 | dp_verify_link_cap_with_retries( |
2426d71c | 992 | link, &known_limit_link_setting, |
c282d951 WL |
993 | LINK_TRAINING_MAX_VERIFY_RETRY); |
994 | } else { | |
995 | ASSERT(0); | |
996 | } | |
997 | ||
998 | if (should_prepare_phy_clocks) | |
999 | restore_phy_clocks_for_destructive_link_verification(link->dc); | |
1000 | } | |
1001 | ||
1002 | static void verify_link_capability_non_destructive(struct dc_link *link) | |
1003 | { | |
1004 | if (dc_is_dp_signal(link->local_sink->sink_signal)) { | |
1005 | if (dc_is_embedded_signal(link->local_sink->sink_signal) || | |
1006 | link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) | |
1007 | /* TODO - should we check link encoder's max link caps here? | |
1008 | * How do we know which link encoder to check from? | |
1009 | */ | |
1010 | link->verified_link_cap = link->reported_link_cap; | |
1011 | else | |
1012 | link->verified_link_cap = dp_get_max_link_cap(link); | |
1013 | } | |
1014 | } | |
1015 | ||
1016 | static bool should_verify_link_capability_destructively(struct dc_link *link, | |
1017 | enum dc_detect_reason reason) | |
1018 | { | |
1019 | bool destrictive = false; | |
1020 | struct dc_link_settings max_link_cap; | |
1021 | bool is_link_enc_unavailable = link->link_enc && | |
1022 | link->dc->res_pool->funcs->link_encs_assign && | |
1023 | !link_enc_cfg_is_link_enc_avail( | |
1024 | link->ctx->dc, | |
1025 | link->link_enc->preferred_engine, | |
1026 | link); | |
1027 | ||
1028 | if (dc_is_dp_signal(link->local_sink->sink_signal)) { | |
1029 | max_link_cap = dp_get_max_link_cap(link); | |
1030 | destrictive = true; | |
1031 | ||
1032 | if (link->dc->debug.skip_detection_link_training || | |
1033 | dc_is_embedded_signal(link->local_sink->sink_signal) || | |
1034 | link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) { | |
1035 | destrictive = false; | |
d5a43956 | 1036 | } else if (link_dp_get_encoding_format(&max_link_cap) == |
c282d951 WL |
1037 | DP_8b_10b_ENCODING) { |
1038 | if (link->dpcd_caps.is_mst_capable || | |
1039 | is_link_enc_unavailable) { | |
1040 | destrictive = false; | |
1041 | } | |
1042 | } | |
a572f705 | 1043 | } |
c282d951 WL |
1044 | |
1045 | return destrictive; | |
1046 | } | |
1047 | ||
1048 | static void verify_link_capability(struct dc_link *link, struct dc_sink *sink, | |
1049 | enum dc_detect_reason reason) | |
1050 | { | |
1051 | if (should_verify_link_capability_destructively(link, reason)) | |
1052 | verify_link_capability_destructive(link, sink, reason); | |
1053 | else | |
1054 | verify_link_capability_non_destructive(link); | |
1055 | } | |
1056 | ||
1057 | ||
1058 | /** | |
1059 | * detect_link_and_local_sink() - Detect if a sink is attached to a given link | |
2119aa17 DF |
1060 | * |
1061 | * link->local_sink is created or destroyed as needed. | |
1062 | * | |
c282d951 | 1063 | * This does not create remote sinks. |
2119aa17 | 1064 | */ |
c282d951 | 1065 | static bool detect_link_and_local_sink(struct dc_link *link, |
dd80ad9b | 1066 | enum dc_detect_reason reason) |
4562236b | 1067 | { |
4562236b HW |
1068 | struct dc_sink_init_data sink_init_data = { 0 }; |
1069 | struct display_sink_capability sink_caps = { 0 }; | |
7335d956 | 1070 | uint32_t i; |
4562236b HW |
1071 | bool converter_disable_audio = false; |
1072 | struct audio_support *aud_support = &link->dc->res_pool->audio_support; | |
eb815442 | 1073 | bool same_edid = false; |
4562236b HW |
1074 | enum dc_edid_status edid_status; |
1075 | struct dc_context *dc_ctx = link->ctx; | |
a8201902 | 1076 | struct dc *dc = dc_ctx->dc; |
533ed6c7 | 1077 | struct dc_sink *sink = NULL; |
eb815442 ST |
1078 | struct dc_sink *prev_sink = NULL; |
1079 | struct dpcd_caps prev_dpcd_caps; | |
4562236b | 1080 | enum dc_connection_type new_connection_type = dc_connection_none; |
c797ede0 | 1081 | const uint32_t post_oui_delay = 30; // 30ms |
9ae1b27f | 1082 | |
5d4b05dd | 1083 | DC_LOGGER_INIT(link->ctx->logger); |
248cbed6 EB |
1084 | |
1085 | if (dc_is_virtual_signal(link->connector_signal)) | |
4562236b HW |
1086 | return false; |
1087 | ||
5ab991ba YLC |
1088 | if (((link->connector_signal == SIGNAL_TYPE_LVDS || |
1089 | link->connector_signal == SIGNAL_TYPE_EDP) && | |
1090 | (!link->dc->config.allow_edp_hotplug_detection)) && | |
1091 | link->local_sink) { | |
96577cf8 | 1092 | // need to re-write OUI and brightness in resume case |
83a3766b RM |
1093 | if (link->connector_signal == SIGNAL_TYPE_EDP && |
1094 | (link->dpcd_sink_ext_caps.bits.oled == 1)) { | |
96577cf8 | 1095 | dpcd_set_source_specific_data(link); |
c797ede0 | 1096 | msleep(post_oui_delay); |
ded3491b MW |
1097 | dc_link_set_default_brightness_aux(link); |
1098 | //TODO: use cached | |
96577cf8 HW |
1099 | } |
1100 | ||
abe882a3 | 1101 | return true; |
96577cf8 | 1102 | } |
abe882a3 | 1103 | |
ded3491b | 1104 | if (!dc_link_detect_sink(link, &new_connection_type)) { |
4562236b HW |
1105 | BREAK_TO_DEBUGGER(); |
1106 | return false; | |
1107 | } | |
1108 | ||
eb815442 | 1109 | prev_sink = link->local_sink; |
ded3491b | 1110 | if (prev_sink) { |
eb815442 ST |
1111 | dc_sink_retain(prev_sink); |
1112 | memcpy(&prev_dpcd_caps, &link->dpcd_caps, sizeof(struct dpcd_caps)); | |
1113 | } | |
dcf298c3 | 1114 | |
ded3491b | 1115 | link_disconnect_sink(link); |
4562236b | 1116 | if (new_connection_type != dc_connection_none) { |
d0778ebf | 1117 | link->type = new_connection_type; |
eed928dc | 1118 | link->link_state_valid = false; |
4562236b HW |
1119 | |
1120 | /* From Disconnected-to-Connected. */ | |
d0778ebf | 1121 | switch (link->connector_signal) { |
4562236b HW |
1122 | case SIGNAL_TYPE_HDMI_TYPE_A: { |
1123 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
1124 | if (aud_support->hdmi_audio_native) | |
1125 | sink_caps.signal = SIGNAL_TYPE_HDMI_TYPE_A; | |
1126 | else | |
1127 | sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
1128 | break; | |
1129 | } | |
1130 | ||
1131 | case SIGNAL_TYPE_DVI_SINGLE_LINK: { | |
1132 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
1133 | sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
1134 | break; | |
1135 | } | |
1136 | ||
1137 | case SIGNAL_TYPE_DVI_DUAL_LINK: { | |
1138 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
1139 | sink_caps.signal = SIGNAL_TYPE_DVI_DUAL_LINK; | |
1140 | break; | |
1141 | } | |
1142 | ||
11c3ee48 AD |
1143 | case SIGNAL_TYPE_LVDS: { |
1144 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
1145 | sink_caps.signal = SIGNAL_TYPE_LVDS; | |
1146 | break; | |
1147 | } | |
1148 | ||
4562236b | 1149 | case SIGNAL_TYPE_EDP: { |
96577cf8 HW |
1150 | read_current_link_settings_on_detect(link); |
1151 | ||
4654a2f7 | 1152 | detect_edp_sink_caps(link); |
5d593d68 | 1153 | read_current_link_settings_on_detect(link); |
a8201902 LM |
1154 | |
1155 | /* Disable power sequence on MIPI panel + converter | |
1156 | */ | |
1157 | if (dc->config.enable_mipi_converter_optimization && | |
1158 | dc_ctx->dce_version == DCN_VERSION_3_01 && | |
1159 | link->dpcd_caps.sink_dev_id == DP_BRANCH_DEVICE_ID_0022B9 && | |
1160 | memcmp(&link->dpcd_caps.branch_dev_name, DP_SINK_BRANCH_DEV_NAME_7580, | |
06f2f777 | 1161 | sizeof(link->dpcd_caps.branch_dev_name)) == 0) { |
a8201902 LM |
1162 | dc->config.edp_no_power_sequencing = true; |
1163 | ||
06f2f777 SJK |
1164 | if (!link->dpcd_caps.set_power_state_capable_edp) |
1165 | link->wa_flags.dp_keep_receiver_powered = true; | |
1166 | } | |
1167 | ||
7f7652ee | 1168 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; |
4562236b HW |
1169 | sink_caps.signal = SIGNAL_TYPE_EDP; |
1170 | break; | |
1171 | } | |
1172 | ||
1173 | case SIGNAL_TYPE_DISPLAY_PORT: { | |
b5b1f455 | 1174 | /* wa HPD high coming too early*/ |
99732e52 JK |
1175 | if (link->ep_type == DISPLAY_ENDPOINT_PHY && |
1176 | link->link_enc->features.flags.bits.DP_IS_USB_C == 1) { | |
b5b1f455 | 1177 | /* if alt mode times out, return false */ |
8ccf0e20 | 1178 | if (!wait_for_entering_dp_alt_mode(link)) |
b5b1f455 | 1179 | return false; |
b5b1f455 EY |
1180 | } |
1181 | ||
8a58e25b | 1182 | if (!detect_dp(link, &sink_caps, reason)) { |
ded3491b | 1183 | if (prev_sink) |
eb815442 | 1184 | dc_sink_release(prev_sink); |
cdb39798 | 1185 | return false; |
eb815442 | 1186 | } |
4562236b | 1187 | |
36c9137b DZ |
1188 | /* Active SST downstream branch device unplug*/ |
1189 | if (link->type == dc_connection_sst_branch && | |
ded3491b MW |
1190 | link->dpcd_caps.sink_count.bits.SINK_COUNT == 0) { |
1191 | if (prev_sink) | |
80adaebd | 1192 | /* Downstream unplug */ |
eb815442 | 1193 | dc_sink_release(prev_sink); |
4562236b | 1194 | return true; |
eb815442 | 1195 | } |
4562236b | 1196 | |
8a58e25b WL |
1197 | /* disable audio for non DP to HDMI active sst converter */ |
1198 | if (link->type == dc_connection_sst_branch && | |
1199 | is_dp_active_dongle(link) && | |
1200 | (link->dpcd_caps.dongle_type != | |
1201 | DISPLAY_DONGLE_DP_HDMI_CONVERTER)) | |
1202 | converter_disable_audio = true; | |
4562236b HW |
1203 | break; |
1204 | } | |
1205 | ||
1206 | default: | |
1207 | DC_ERROR("Invalid connector type! signal:%d\n", | |
ded3491b MW |
1208 | link->connector_signal); |
1209 | if (prev_sink) | |
eb815442 | 1210 | dc_sink_release(prev_sink); |
4562236b HW |
1211 | return false; |
1212 | } /* switch() */ | |
1213 | ||
1214 | if (link->dpcd_caps.sink_count.bits.SINK_COUNT) | |
ded3491b MW |
1215 | link->dpcd_sink_count = |
1216 | link->dpcd_caps.sink_count.bits.SINK_COUNT; | |
2a0b4d85 MT |
1217 | else |
1218 | link->dpcd_sink_count = 1; | |
4562236b | 1219 | |
a98cdd8c | 1220 | set_ddc_transaction_type(link->ddc, |
ded3491b | 1221 | sink_caps.transaction_type); |
4562236b | 1222 | |
ded3491b | 1223 | link->aux_mode = |
a98cdd8c | 1224 | link_is_in_aux_transaction_mode(link->ddc); |
7c7f5b15 | 1225 | |
d0778ebf | 1226 | sink_init_data.link = link; |
4562236b | 1227 | sink_init_data.sink_signal = sink_caps.signal; |
4562236b | 1228 | |
7d58e721 MT |
1229 | sink = dc_sink_create(&sink_init_data); |
1230 | if (!sink) { | |
1231 | DC_ERROR("Failed to create sink!\n"); | |
ded3491b | 1232 | if (prev_sink) |
eb815442 | 1233 | dc_sink_release(prev_sink); |
7d58e721 MT |
1234 | return false; |
1235 | } | |
4562236b | 1236 | |
ceb3dbb4 | 1237 | sink->link->dongle_max_pix_clk = sink_caps.max_hdmi_pixel_clock; |
dcf298c3 | 1238 | sink->converter_disable_audio = converter_disable_audio; |
7d58e721 | 1239 | |
dcd5fb82 | 1240 | /* dc_sink_create returns a new reference */ |
dcf298c3 | 1241 | link->local_sink = sink; |
4562236b | 1242 | |
ded3491b MW |
1243 | edid_status = dm_helpers_read_local_edid(link->ctx, |
1244 | link, sink); | |
4562236b HW |
1245 | |
1246 | switch (edid_status) { | |
1247 | case EDID_BAD_CHECKSUM: | |
1296423b | 1248 | DC_LOG_ERROR("EDID checksum invalid.\n"); |
4562236b | 1249 | break; |
906fbba2 DZ |
1250 | case EDID_PARTIAL_VALID: |
1251 | DC_LOG_ERROR("Partial EDID valid, abandon invalid blocks.\n"); | |
1252 | break; | |
4562236b | 1253 | case EDID_NO_RESPONSE: |
1296423b | 1254 | DC_LOG_ERROR("No EDID read.\n"); |
01dc285d HW |
1255 | /* |
1256 | * Abort detection for non-DP connectors if we have | |
1257 | * no EDID | |
1258 | * | |
1259 | * DP needs to report as connected if HDP is high | |
1260 | * even if we have no EDID in order to go to | |
1261 | * fail-safe mode | |
1262 | */ | |
16196776 | 1263 | if (dc_is_hdmi_signal(link->connector_signal) || |
219097df | 1264 | dc_is_dvi_signal(link->connector_signal)) { |
ded3491b | 1265 | if (prev_sink) |
219097df S |
1266 | dc_sink_release(prev_sink); |
1267 | ||
01dc285d | 1268 | return false; |
219097df | 1269 | } |
10a9accd I |
1270 | |
1271 | if (link->type == dc_connection_sst_branch && | |
1272 | link->dpcd_caps.dongle_type == | |
1273 | DISPLAY_DONGLE_DP_VGA_CONVERTER && | |
1274 | reason == DETECT_REASON_HPDRX) { | |
1275 | /* Abort detection for DP-VGA adapters when EDID | |
1276 | * can't be read and detection reason is VGA-side | |
1277 | * hotplug | |
1278 | */ | |
1279 | if (prev_sink) | |
1280 | dc_sink_release(prev_sink); | |
1281 | link_disconnect_sink(link); | |
1282 | ||
1283 | return true; | |
1284 | } | |
1285 | ||
910e834d | 1286 | break; |
4562236b HW |
1287 | default: |
1288 | break; | |
1289 | } | |
1290 | ||
eb815442 | 1291 | // Check if edid is the same |
ded3491b MW |
1292 | if ((prev_sink) && |
1293 | (edid_status == EDID_THE_SAME || edid_status == EDID_OK)) | |
1294 | same_edid = is_same_edid(&prev_sink->dc_edid, | |
1295 | &sink->dc_edid); | |
eb815442 | 1296 | |
5fbdb1f3 | 1297 | if (sink->edid_caps.panel_patch.skip_scdc_overwrite) |
a760fc1b ML |
1298 | link->ctx->dc->debug.hdmi20_disable = true; |
1299 | ||
0301ccba | 1300 | if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && |
ded3491b MW |
1301 | sink_caps.transaction_type == |
1302 | DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { | |
0301ccba | 1303 | /* |
1304 | * TODO debug why Dell 2413 doesn't like | |
1305 | * two link trainings | |
1306 | */ | |
fe8db3bc BL |
1307 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
1308 | query_hdcp_capability(sink->sink_signal, link); | |
1309 | #endif | |
0301ccba | 1310 | } else { |
1311 | // If edid is the same, then discard new sink and revert back to original sink | |
1312 | if (same_edid) { | |
1313 | link_disconnect_remap(prev_sink, link); | |
1314 | sink = prev_sink; | |
1315 | prev_sink = NULL; | |
0301ccba | 1316 | } |
fe8db3bc BL |
1317 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
1318 | query_hdcp_capability(sink->sink_signal, link); | |
1319 | #endif | |
eb815442 | 1320 | } |
4562236b | 1321 | |
0301ccba | 1322 | /* HDMI-DVI Dongle */ |
1323 | if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A && | |
ded3491b | 1324 | !sink->edid_caps.edid_hdmi) |
0301ccba | 1325 | sink->sink_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; |
1326 | ||
5fed53c7 LHM |
1327 | if (link->local_sink && dc_is_dp_signal(sink_caps.signal)) |
1328 | dp_trace_init(link); | |
1329 | ||
4562236b | 1330 | /* Connectivity log: detection */ |
a634913e | 1331 | for (i = 0; i < sink->dc_edid.length / DC_EDID_BLOCK_SIZE; i++) { |
4562236b | 1332 | CONN_DATA_DETECT(link, |
ded3491b MW |
1333 | &sink->dc_edid.raw_edid[i * DC_EDID_BLOCK_SIZE], |
1334 | DC_EDID_BLOCK_SIZE, | |
1335 | "%s: [Block %d] ", sink->edid_caps.display_name, i); | |
4562236b HW |
1336 | } |
1337 | ||
1296423b | 1338 | DC_LOG_DETECTION_EDID_PARSER("%s: " |
4562236b HW |
1339 | "manufacturer_id = %X, " |
1340 | "product_id = %X, " | |
1341 | "serial_number = %X, " | |
1342 | "manufacture_week = %d, " | |
1343 | "manufacture_year = %d, " | |
1344 | "display_name = %s, " | |
1345 | "speaker_flag = %d, " | |
1346 | "audio_mode_count = %d\n", | |
1347 | __func__, | |
b73a22d3 HW |
1348 | sink->edid_caps.manufacturer_id, |
1349 | sink->edid_caps.product_id, | |
1350 | sink->edid_caps.serial_number, | |
1351 | sink->edid_caps.manufacture_week, | |
1352 | sink->edid_caps.manufacture_year, | |
1353 | sink->edid_caps.display_name, | |
1354 | sink->edid_caps.speaker_flags, | |
1355 | sink->edid_caps.audio_mode_count); | |
1356 | ||
1357 | for (i = 0; i < sink->edid_caps.audio_mode_count; i++) { | |
1296423b | 1358 | DC_LOG_DETECTION_EDID_PARSER("%s: mode number = %d, " |
4562236b HW |
1359 | "format_code = %d, " |
1360 | "channel_count = %d, " | |
1361 | "sample_rate = %d, " | |
1362 | "sample_size = %d\n", | |
1363 | __func__, | |
1364 | i, | |
b73a22d3 HW |
1365 | sink->edid_caps.audio_modes[i].format_code, |
1366 | sink->edid_caps.audio_modes[i].channel_count, | |
1367 | sink->edid_caps.audio_modes[i].sample_rate, | |
1368 | sink->edid_caps.audio_modes[i].sample_size); | |
4562236b | 1369 | } |
c17a34e0 IC |
1370 | |
1371 | if (link->connector_signal == SIGNAL_TYPE_EDP) { | |
1178ac68 IC |
1372 | /* Init dc_panel_config by HW config */ |
1373 | if (dc_ctx->dc->res_pool->funcs->get_panel_config_defaults) | |
1374 | dc_ctx->dc->res_pool->funcs->get_panel_config_defaults(&link->panel_config); | |
1375 | /* Pickup base DM settings */ | |
eccff6cd | 1376 | dm_helpers_init_panel_settings(dc_ctx, &link->panel_config, sink); |
c17a34e0 IC |
1377 | // Override dc_panel_config if system has specific settings |
1378 | dm_helpers_override_panel_settings(dc_ctx, &link->panel_config); | |
1379 | } | |
1380 | ||
4562236b HW |
1381 | } else { |
1382 | /* From Connected-to-Disconnected. */ | |
dcf298c3 WL |
1383 | link->type = dc_connection_none; |
1384 | sink_caps.signal = SIGNAL_TYPE_NONE; | |
233d87a5 ST |
1385 | /* When we unplug a passive DP-HDMI dongle connection, dongle_max_pix_clk |
1386 | * is not cleared. If we emulate a DP signal on this connection, it thinks | |
1387 | * the dongle is still there and limits the number of modes we can emulate. | |
1388 | * Clear dongle_max_pix_clk on disconnect to fix this | |
1389 | */ | |
1390 | link->dongle_max_pix_clk = 0; | |
4a3ad932 | 1391 | |
c595fb05 | 1392 | dc_link_clear_dprx_states(link); |
5fed53c7 | 1393 | dp_trace_reset(link); |
4562236b HW |
1394 | } |
1395 | ||
8a58e25b | 1396 | LINK_INFO("link=%d, dc_sink_in=%p is now %s prev_sink=%p edid same=%d\n", |
ded3491b MW |
1397 | link->link_index, sink, |
1398 | (sink_caps.signal == | |
1399 | SIGNAL_TYPE_NONE ? "Disconnected" : "Connected"), | |
8a58e25b | 1400 | prev_sink, same_edid); |
eb815442 | 1401 | |
ded3491b | 1402 | if (prev_sink) |
eb815442 | 1403 | dc_sink_release(prev_sink); |
4562236b HW |
1404 | |
1405 | return true; | |
9ae1b27f JG |
1406 | } |
1407 | ||
1408 | bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) | |
1409 | { | |
c282d951 | 1410 | bool is_local_sink_detect_success; |
87e298d6 | 1411 | bool is_delegated_to_mst_top_mgr = false; |
c282d951 | 1412 | enum dc_connection_type pre_link_type = link->type; |
47c02af7 | 1413 | |
c282d951 | 1414 | is_local_sink_detect_success = detect_link_and_local_sink(link, reason); |
9ae1b27f | 1415 | |
c282d951 WL |
1416 | if (is_local_sink_detect_success && link->local_sink) |
1417 | verify_link_capability(link, link->local_sink, reason); | |
9ae1b27f | 1418 | |
c282d951 WL |
1419 | if (is_local_sink_detect_success && link->local_sink && |
1420 | dc_is_dp_signal(link->local_sink->sink_signal) && | |
1421 | link->dpcd_caps.is_mst_capable) | |
87e298d6 | 1422 | is_delegated_to_mst_top_mgr = discover_dp_mst_topology(link, reason); |
9ae1b27f | 1423 | |
c282d951 WL |
1424 | if (is_local_sink_detect_success && |
1425 | pre_link_type == dc_connection_mst_branch && | |
1426 | link->type != dc_connection_mst_branch) | |
87e298d6 | 1427 | is_delegated_to_mst_top_mgr = reset_cur_dp_mst_topology(link); |
9ae1b27f | 1428 | |
87e298d6 | 1429 | return is_local_sink_detect_success && !is_delegated_to_mst_top_mgr; |
4562236b HW |
1430 | } |
1431 | ||
d0778ebf | 1432 | static enum channel_id get_ddc_line(struct dc_link *link) |
4562236b HW |
1433 | { |
1434 | struct ddc *ddc; | |
f69e98a9 ST |
1435 | enum channel_id channel; |
1436 | ||
1437 | channel = CHANNEL_ID_UNKNOWN; | |
4562236b | 1438 | |
a98cdd8c | 1439 | ddc = get_ddc_pin(link->ddc); |
4562236b HW |
1440 | |
1441 | if (ddc) { | |
1442 | switch (dal_ddc_get_line(ddc)) { | |
1443 | case GPIO_DDC_LINE_DDC1: | |
1444 | channel = CHANNEL_ID_DDC1; | |
1445 | break; | |
1446 | case GPIO_DDC_LINE_DDC2: | |
1447 | channel = CHANNEL_ID_DDC2; | |
1448 | break; | |
1449 | case GPIO_DDC_LINE_DDC3: | |
1450 | channel = CHANNEL_ID_DDC3; | |
1451 | break; | |
1452 | case GPIO_DDC_LINE_DDC4: | |
1453 | channel = CHANNEL_ID_DDC4; | |
1454 | break; | |
1455 | case GPIO_DDC_LINE_DDC5: | |
1456 | channel = CHANNEL_ID_DDC5; | |
1457 | break; | |
1458 | case GPIO_DDC_LINE_DDC6: | |
1459 | channel = CHANNEL_ID_DDC6; | |
1460 | break; | |
1461 | case GPIO_DDC_LINE_DDC_VGA: | |
1462 | channel = CHANNEL_ID_DDC_VGA; | |
1463 | break; | |
1464 | case GPIO_DDC_LINE_I2C_PAD: | |
1465 | channel = CHANNEL_ID_I2C_PAD; | |
1466 | break; | |
1467 | default: | |
1468 | BREAK_TO_DEBUGGER(); | |
1469 | break; | |
1470 | } | |
1471 | } | |
1472 | ||
1473 | return channel; | |
1474 | } | |
1475 | ||
9ec420d8 | 1476 | static enum transmitter translate_encoder_to_transmitter(struct graphics_object_id encoder) |
4562236b HW |
1477 | { |
1478 | switch (encoder.id) { | |
1479 | case ENCODER_ID_INTERNAL_UNIPHY: | |
1480 | switch (encoder.enum_id) { | |
1481 | case ENUM_ID_1: | |
1482 | return TRANSMITTER_UNIPHY_A; | |
1483 | case ENUM_ID_2: | |
1484 | return TRANSMITTER_UNIPHY_B; | |
1485 | default: | |
1486 | return TRANSMITTER_UNKNOWN; | |
1487 | } | |
1488 | break; | |
1489 | case ENCODER_ID_INTERNAL_UNIPHY1: | |
1490 | switch (encoder.enum_id) { | |
1491 | case ENUM_ID_1: | |
1492 | return TRANSMITTER_UNIPHY_C; | |
1493 | case ENUM_ID_2: | |
1494 | return TRANSMITTER_UNIPHY_D; | |
1495 | default: | |
1496 | return TRANSMITTER_UNKNOWN; | |
1497 | } | |
1498 | break; | |
1499 | case ENCODER_ID_INTERNAL_UNIPHY2: | |
1500 | switch (encoder.enum_id) { | |
1501 | case ENUM_ID_1: | |
1502 | return TRANSMITTER_UNIPHY_E; | |
1503 | case ENUM_ID_2: | |
1504 | return TRANSMITTER_UNIPHY_F; | |
1505 | default: | |
1506 | return TRANSMITTER_UNKNOWN; | |
1507 | } | |
1508 | break; | |
1509 | case ENCODER_ID_INTERNAL_UNIPHY3: | |
1510 | switch (encoder.enum_id) { | |
1511 | case ENUM_ID_1: | |
1512 | return TRANSMITTER_UNIPHY_G; | |
1513 | default: | |
1514 | return TRANSMITTER_UNKNOWN; | |
1515 | } | |
1516 | break; | |
1517 | case ENCODER_ID_EXTERNAL_NUTMEG: | |
1518 | switch (encoder.enum_id) { | |
1519 | case ENUM_ID_1: | |
1520 | return TRANSMITTER_NUTMEG_CRT; | |
1521 | default: | |
1522 | return TRANSMITTER_UNKNOWN; | |
1523 | } | |
1524 | break; | |
1525 | case ENCODER_ID_EXTERNAL_TRAVIS: | |
1526 | switch (encoder.enum_id) { | |
1527 | case ENUM_ID_1: | |
1528 | return TRANSMITTER_TRAVIS_CRT; | |
1529 | case ENUM_ID_2: | |
1530 | return TRANSMITTER_TRAVIS_LCD; | |
1531 | default: | |
1532 | return TRANSMITTER_UNKNOWN; | |
1533 | } | |
1534 | break; | |
1535 | default: | |
1536 | return TRANSMITTER_UNKNOWN; | |
1537 | } | |
1538 | } | |
1539 | ||
9fa0fb77 MS |
1540 | static bool dc_link_construct_legacy(struct dc_link *link, |
1541 | const struct link_init_data *init_params) | |
4562236b HW |
1542 | { |
1543 | uint8_t i; | |
6bad8e4a | 1544 | struct ddc_service_init_data ddc_service_init_data = { 0 }; |
4562236b HW |
1545 | struct dc_context *dc_ctx = init_params->ctx; |
1546 | struct encoder_init_data enc_init_data = { 0 }; | |
d4caa72e | 1547 | struct panel_cntl_init_data panel_cntl_init_data = { 0 }; |
3a00c042 | 1548 | struct integrated_info *info; |
4562236b HW |
1549 | struct dc_bios *bios = init_params->dc->ctx->dc_bios; |
1550 | const struct dc_vbios_funcs *bp_funcs = bios->funcs; | |
c85ef99a | 1551 | struct bp_disp_connector_caps_info disp_connect_caps_info = { 0 }; |
9ec420d8 | 1552 | |
5d4b05dd | 1553 | DC_LOGGER_INIT(dc_ctx->logger); |
4562236b | 1554 | |
b4423a3d | 1555 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
3a00c042 LJ |
1556 | if (!info) |
1557 | goto create_fail; | |
1558 | ||
d0778ebf HW |
1559 | link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; |
1560 | link->irq_source_hpd_rx = DC_IRQ_SOURCE_INVALID; | |
4562236b HW |
1561 | |
1562 | link->link_status.dpcd_caps = &link->dpcd_caps; | |
1563 | ||
1564 | link->dc = init_params->dc; | |
1565 | link->ctx = dc_ctx; | |
d0778ebf | 1566 | link->link_index = init_params->link_index; |
4562236b | 1567 | |
9ec420d8 MW |
1568 | memset(&link->preferred_training_settings, 0, |
1569 | sizeof(struct dc_link_training_overrides)); | |
1570 | memset(&link->preferred_link_setting, 0, | |
1571 | sizeof(struct dc_link_settings)); | |
e0a6440a | 1572 | |
9ec420d8 MW |
1573 | link->link_id = |
1574 | bios->funcs->get_connector_id(bios, init_params->connector_index); | |
fb7b11e1 | 1575 | |
f42ef862 JK |
1576 | link->ep_type = DISPLAY_ENDPOINT_PHY; |
1577 | ||
64ff0882 | 1578 | DC_LOG_DC("BIOS object table - link_id: %d", link->link_id.id); |
c85ef99a YS |
1579 | |
1580 | if (bios->funcs->get_disp_connector_caps_info) { | |
1581 | bios->funcs->get_disp_connector_caps_info(bios, link->link_id, &disp_connect_caps_info); | |
1582 | link->is_internal_display = disp_connect_caps_info.INTERNAL_DISPLAY; | |
64ff0882 | 1583 | DC_LOG_DC("BIOS object table - is_internal_display: %d", link->is_internal_display); |
c85ef99a YS |
1584 | } |
1585 | ||
4562236b | 1586 | if (link->link_id.type != OBJECT_TYPE_CONNECTOR) { |
acbf7faa | 1587 | dm_output_to_console("%s: Invalid Connector ObjectID from Adapter Service for connector index:%d! type %d expected %d\n", |
9ec420d8 MW |
1588 | __func__, init_params->connector_index, |
1589 | link->link_id.type, OBJECT_TYPE_CONNECTOR); | |
4562236b HW |
1590 | goto create_fail; |
1591 | } | |
1592 | ||
66b198ff DL |
1593 | if (link->dc->res_pool->funcs->link_init) |
1594 | link->dc->res_pool->funcs->link_init(link); | |
1595 | ||
4370f72e | 1596 | link->hpd_gpio = link_get_hpd_gpio(link->ctx->dc_bios, link->link_id, |
9ec420d8 | 1597 | link->ctx->gpio_service); |
64ff0882 | 1598 | |
9ec420d8 | 1599 | if (link->hpd_gpio) { |
04612213 HW |
1600 | dal_gpio_open(link->hpd_gpio, GPIO_MODE_INTERRUPT); |
1601 | dal_gpio_unlock_pin(link->hpd_gpio); | |
ac627caf | 1602 | link->irq_source_hpd = dal_irq_get_source(link->hpd_gpio); |
64ff0882 GS |
1603 | |
1604 | DC_LOG_DC("BIOS object table - hpd_gpio id: %d", link->hpd_gpio->id); | |
1605 | DC_LOG_DC("BIOS object table - hpd_gpio en: %d", link->hpd_gpio->en); | |
04612213 | 1606 | } |
4562236b HW |
1607 | |
1608 | switch (link->link_id.id) { | |
1609 | case CONNECTOR_ID_HDMI_TYPE_A: | |
d0778ebf | 1610 | link->connector_signal = SIGNAL_TYPE_HDMI_TYPE_A; |
4562236b HW |
1611 | |
1612 | break; | |
1613 | case CONNECTOR_ID_SINGLE_LINK_DVID: | |
1614 | case CONNECTOR_ID_SINGLE_LINK_DVII: | |
d0778ebf | 1615 | link->connector_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; |
4562236b HW |
1616 | break; |
1617 | case CONNECTOR_ID_DUAL_LINK_DVID: | |
1618 | case CONNECTOR_ID_DUAL_LINK_DVII: | |
d0778ebf | 1619 | link->connector_signal = SIGNAL_TYPE_DVI_DUAL_LINK; |
4562236b HW |
1620 | break; |
1621 | case CONNECTOR_ID_DISPLAY_PORT: | |
8cb3c169 | 1622 | case CONNECTOR_ID_USBC: |
9ec420d8 | 1623 | link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; |
4562236b | 1624 | |
9ec420d8 | 1625 | if (link->hpd_gpio) |
d0778ebf | 1626 | link->irq_source_hpd_rx = |
ac627caf | 1627 | dal_irq_get_rx_source(link->hpd_gpio); |
4562236b HW |
1628 | |
1629 | break; | |
1630 | case CONNECTOR_ID_EDP: | |
d0778ebf | 1631 | link->connector_signal = SIGNAL_TYPE_EDP; |
4562236b | 1632 | |
9ec420d8 | 1633 | if (link->hpd_gpio) { |
5ab991ba YLC |
1634 | if (!link->dc->config.allow_edp_hotplug_detection) |
1635 | link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; | |
ab144f0b DL |
1636 | |
1637 | switch (link->dc->config.allow_edp_hotplug_detection) { | |
1638 | case 1: // only the 1st eDP handles hotplug | |
1639 | if (link->link_index == 0) | |
1640 | link->irq_source_hpd_rx = | |
1641 | dal_irq_get_rx_source(link->hpd_gpio); | |
1642 | else | |
1643 | link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; | |
1644 | break; | |
1645 | case 2: // only the 2nd eDP handles hotplug | |
1646 | if (link->link_index == 1) | |
1647 | link->irq_source_hpd_rx = | |
1648 | dal_irq_get_rx_source(link->hpd_gpio); | |
1649 | else | |
1650 | link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; | |
1651 | break; | |
1652 | default: | |
1653 | break; | |
1654 | } | |
4562236b | 1655 | } |
904fb6e0 | 1656 | |
4562236b | 1657 | break; |
11c3ee48 AD |
1658 | case CONNECTOR_ID_LVDS: |
1659 | link->connector_signal = SIGNAL_TYPE_LVDS; | |
1660 | break; | |
4562236b | 1661 | default: |
9ec420d8 MW |
1662 | DC_LOG_WARNING("Unsupported Connector type:%d!\n", |
1663 | link->link_id.id); | |
4562236b HW |
1664 | goto create_fail; |
1665 | } | |
1666 | ||
4562236b HW |
1667 | /* TODO: #DAL3 Implement id to str function.*/ |
1668 | LINK_INFO("Connector[%d] description:" | |
9ec420d8 MW |
1669 | "signal %d\n", |
1670 | init_params->connector_index, | |
1671 | link->connector_signal); | |
4562236b HW |
1672 | |
1673 | ddc_service_init_data.ctx = link->ctx; | |
1674 | ddc_service_init_data.id = link->link_id; | |
1675 | ddc_service_init_data.link = link; | |
a98cdd8c | 1676 | link->ddc = link_create_ddc_service(&ddc_service_init_data); |
4562236b | 1677 | |
9ec420d8 | 1678 | if (!link->ddc) { |
4562236b HW |
1679 | DC_ERROR("Failed to create ddc_service!\n"); |
1680 | goto ddc_create_fail; | |
1681 | } | |
1682 | ||
44a09e3d NK |
1683 | if (!link->ddc->ddc_pin) { |
1684 | DC_ERROR("Failed to get I2C info for connector!\n"); | |
1685 | goto ddc_create_fail; | |
1686 | } | |
1687 | ||
d0778ebf | 1688 | link->ddc_hw_inst = |
a98cdd8c | 1689 | dal_ddc_get_line(get_ddc_pin(link->ddc)); |
4562236b | 1690 | |
904fb6e0 | 1691 | |
d4caa72e | 1692 | if (link->dc->res_pool->funcs->panel_cntl_create && |
904fb6e0 AK |
1693 | (link->link_id.id == CONNECTOR_ID_EDP || |
1694 | link->link_id.id == CONNECTOR_ID_LVDS)) { | |
d4caa72e | 1695 | panel_cntl_init_data.ctx = dc_ctx; |
3306ace5 JW |
1696 | panel_cntl_init_data.inst = |
1697 | panel_cntl_init_data.ctx->dc_edp_id_count; | |
d4caa72e AK |
1698 | link->panel_cntl = |
1699 | link->dc->res_pool->funcs->panel_cntl_create( | |
1700 | &panel_cntl_init_data); | |
3306ace5 | 1701 | panel_cntl_init_data.ctx->dc_edp_id_count++; |
d4caa72e AK |
1702 | |
1703 | if (link->panel_cntl == NULL) { | |
1704 | DC_ERROR("Failed to create link panel_cntl!\n"); | |
1705 | goto panel_cntl_create_fail; | |
904fb6e0 AK |
1706 | } |
1707 | } | |
1708 | ||
4562236b | 1709 | enc_init_data.ctx = dc_ctx; |
9ec420d8 MW |
1710 | bp_funcs->get_src_obj(dc_ctx->dc_bios, link->link_id, 0, |
1711 | &enc_init_data.encoder); | |
4562236b HW |
1712 | enc_init_data.connector = link->link_id; |
1713 | enc_init_data.channel = get_ddc_line(link); | |
1714 | enc_init_data.hpd_source = get_hpd_line(link); | |
7a096334 | 1715 | |
d0778ebf | 1716 | link->hpd_src = enc_init_data.hpd_source; |
7a096334 | 1717 | |
4562236b | 1718 | enc_init_data.transmitter = |
9ec420d8 MW |
1719 | translate_encoder_to_transmitter(enc_init_data.encoder); |
1720 | link->link_enc = | |
e216431b | 1721 | link->dc->res_pool->funcs->link_enc_create(dc_ctx, &enc_init_data); |
4562236b | 1722 | |
9ec420d8 | 1723 | if (!link->link_enc) { |
4562236b HW |
1724 | DC_ERROR("Failed to create link encoder!\n"); |
1725 | goto link_enc_create_fail; | |
1726 | } | |
1727 | ||
d3abc78f | 1728 | DC_LOG_DC("BIOS object table - DP_IS_USB_C: %d", link->link_enc->features.flags.bits.DP_IS_USB_C); |
f01ee019 | 1729 | DC_LOG_DC("BIOS object table - IS_DP2_CAPABLE: %d", link->link_enc->features.flags.bits.IS_DP2_CAPABLE); |
d3abc78f | 1730 | |
e1f4328f JK |
1731 | /* Update link encoder tracking variables. These are used for the dynamic |
1732 | * assignment of link encoders to streams. | |
1733 | */ | |
f42ef862 JK |
1734 | link->eng_id = link->link_enc->preferred_engine; |
1735 | link->dc->res_pool->link_encoders[link->eng_id - ENGINE_ID_DIGA] = link->link_enc; | |
e1f4328f JK |
1736 | link->dc->res_pool->dig_link_enc_count++; |
1737 | ||
d0778ebf | 1738 | link->link_enc_hw_inst = link->link_enc->transmitter; |
4562236b HW |
1739 | |
1740 | for (i = 0; i < 4; i++) { | |
9ec420d8 MW |
1741 | if (bp_funcs->get_device_tag(dc_ctx->dc_bios, |
1742 | link->link_id, i, | |
1743 | &link->device_tag) != BP_RESULT_OK) { | |
4562236b HW |
1744 | DC_ERROR("Failed to find device tag!\n"); |
1745 | goto device_tag_fail; | |
1746 | } | |
1747 | ||
1748 | /* Look for device tag that matches connector signal, | |
1749 | * CRT for rgb, LCD for other supported signal tyes | |
1750 | */ | |
9ec420d8 MW |
1751 | if (!bp_funcs->is_device_id_supported(dc_ctx->dc_bios, |
1752 | link->device_tag.dev_id)) | |
4562236b | 1753 | continue; |
9ec420d8 MW |
1754 | if (link->device_tag.dev_id.device_type == DEVICE_TYPE_CRT && |
1755 | link->connector_signal != SIGNAL_TYPE_RGB) | |
4562236b | 1756 | continue; |
9ec420d8 MW |
1757 | if (link->device_tag.dev_id.device_type == DEVICE_TYPE_LCD && |
1758 | link->connector_signal == SIGNAL_TYPE_RGB) | |
4562236b | 1759 | continue; |
64ff0882 GS |
1760 | |
1761 | DC_LOG_DC("BIOS object table - device_tag.acpi_device: %d", link->device_tag.acpi_device); | |
1762 | DC_LOG_DC("BIOS object table - device_tag.dev_id.device_type: %d", link->device_tag.dev_id.device_type); | |
1763 | DC_LOG_DC("BIOS object table - device_tag.dev_id.enum_id: %d", link->device_tag.dev_id.enum_id); | |
4562236b HW |
1764 | break; |
1765 | } | |
1766 | ||
1767 | if (bios->integrated_info) | |
b4423a3d | 1768 | memcpy(info, bios->integrated_info, sizeof(*info)); |
4562236b HW |
1769 | |
1770 | /* Look for channel mapping corresponding to connector and device tag */ | |
1771 | for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; i++) { | |
1772 | struct external_display_path *path = | |
3a00c042 | 1773 | &info->ext_disp_conn_info.path[i]; |
1e8635ea | 1774 | |
9ec420d8 MW |
1775 | if (path->device_connector_id.enum_id == link->link_id.enum_id && |
1776 | path->device_connector_id.id == link->link_id.id && | |
1777 | path->device_connector_id.type == link->link_id.type) { | |
1778 | if (link->device_tag.acpi_device != 0 && | |
1779 | path->device_acpi_enum == link->device_tag.acpi_device) { | |
1e8635ea ZF |
1780 | link->ddi_channel_mapping = path->channel_mapping; |
1781 | link->chip_caps = path->caps; | |
64ff0882 GS |
1782 | DC_LOG_DC("BIOS object table - ddi_channel_mapping: 0x%04X", link->ddi_channel_mapping.raw); |
1783 | DC_LOG_DC("BIOS object table - chip_caps: %d", link->chip_caps); | |
1e8635ea | 1784 | } else if (path->device_tag == |
9ec420d8 | 1785 | link->device_tag.dev_id.raw_device_tag) { |
1e8635ea ZF |
1786 | link->ddi_channel_mapping = path->channel_mapping; |
1787 | link->chip_caps = path->caps; | |
64ff0882 GS |
1788 | DC_LOG_DC("BIOS object table - ddi_channel_mapping: 0x%04X", link->ddi_channel_mapping.raw); |
1789 | DC_LOG_DC("BIOS object table - chip_caps: %d", link->chip_caps); | |
1e8635ea | 1790 | } |
54fe00be GS |
1791 | |
1792 | if (link->chip_caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) { | |
1793 | link->bios_forced_drive_settings.VOLTAGE_SWING = | |
1794 | (info->ext_disp_conn_info.fixdpvoltageswing & 0x3); | |
1795 | link->bios_forced_drive_settings.PRE_EMPHASIS = | |
1796 | ((info->ext_disp_conn_info.fixdpvoltageswing >> 2) & 0x3); | |
1797 | } | |
1798 | ||
4562236b HW |
1799 | break; |
1800 | } | |
1801 | } | |
1802 | ||
09821499 IK |
1803 | if (bios->funcs->get_atom_dc_golden_table) |
1804 | bios->funcs->get_atom_dc_golden_table(bios); | |
1805 | ||
4562236b HW |
1806 | /* |
1807 | * TODO check if GPIO programmed correctly | |
1808 | * | |
1809 | * If GPIO isn't programmed correctly HPD might not rise or drain | |
1810 | * fast enough, leading to bounces. | |
1811 | */ | |
b0c4e977 | 1812 | program_hpd_filter(link); |
4562236b | 1813 | |
6651875a | 1814 | link->psr_settings.psr_vtotal_control_support = false; |
1cfbbdde | 1815 | link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED; |
d1ebfdd8 | 1816 | |
a4905435 | 1817 | DC_LOG_DC("BIOS object table - %s finished successfully.\n", __func__); |
616cf23b | 1818 | kfree(info); |
4562236b HW |
1819 | return true; |
1820 | device_tag_fail: | |
1821 | link->link_enc->funcs->destroy(&link->link_enc); | |
1822 | link_enc_create_fail: | |
d4caa72e AK |
1823 | if (link->panel_cntl != NULL) |
1824 | link->panel_cntl->funcs->destroy(&link->panel_cntl); | |
1825 | panel_cntl_create_fail: | |
a98cdd8c | 1826 | link_destroy_ddc_service(&link->ddc); |
4562236b HW |
1827 | ddc_create_fail: |
1828 | create_fail: | |
1829 | ||
9ec420d8 | 1830 | if (link->hpd_gpio) { |
ac627caf CH |
1831 | dal_gpio_destroy_irq(&link->hpd_gpio); |
1832 | link->hpd_gpio = NULL; | |
4562236b HW |
1833 | } |
1834 | ||
a4905435 | 1835 | DC_LOG_DC("BIOS object table - %s failed.\n", __func__); |
3a00c042 LJ |
1836 | kfree(info); |
1837 | ||
4562236b HW |
1838 | return false; |
1839 | } | |
1840 | ||
9fa0fb77 MS |
1841 | static bool dc_link_construct_dpia(struct dc_link *link, |
1842 | const struct link_init_data *init_params) | |
1843 | { | |
6bad8e4a | 1844 | struct ddc_service_init_data ddc_service_init_data = { 0 }; |
9fa0fb77 MS |
1845 | struct dc_context *dc_ctx = init_params->ctx; |
1846 | ||
1847 | DC_LOGGER_INIT(dc_ctx->logger); | |
1848 | ||
40fadb4c MS |
1849 | /* Initialized irq source for hpd and hpd rx */ |
1850 | link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; | |
1851 | link->irq_source_hpd_rx = DC_IRQ_SOURCE_INVALID; | |
9fa0fb77 MS |
1852 | link->link_status.dpcd_caps = &link->dpcd_caps; |
1853 | ||
1854 | link->dc = init_params->dc; | |
1855 | link->ctx = dc_ctx; | |
1856 | link->link_index = init_params->link_index; | |
1857 | ||
1858 | memset(&link->preferred_training_settings, 0, | |
1859 | sizeof(struct dc_link_training_overrides)); | |
1860 | memset(&link->preferred_link_setting, 0, | |
1861 | sizeof(struct dc_link_settings)); | |
1862 | ||
1863 | /* Dummy Init for linkid */ | |
1864 | link->link_id.type = OBJECT_TYPE_CONNECTOR; | |
1865 | link->link_id.id = CONNECTOR_ID_DISPLAY_PORT; | |
99447622 | 1866 | link->link_id.enum_id = ENUM_ID_1 + init_params->connector_index; |
9fa0fb77 MS |
1867 | link->is_internal_display = false; |
1868 | link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; | |
1869 | LINK_INFO("Connector[%d] description:signal %d\n", | |
1870 | init_params->connector_index, | |
1871 | link->connector_signal); | |
1872 | ||
698d0a6f | 1873 | link->ep_type = DISPLAY_ENDPOINT_USB4_DPIA; |
99447622 | 1874 | link->is_dig_mapping_flexible = true; |
698d0a6f | 1875 | |
9fa0fb77 MS |
1876 | /* TODO: Initialize link : funcs->link_init */ |
1877 | ||
1878 | ddc_service_init_data.ctx = link->ctx; | |
1879 | ddc_service_init_data.id = link->link_id; | |
1880 | ddc_service_init_data.link = link; | |
1881 | /* Set indicator for dpia link so that ddc won't be created */ | |
1882 | ddc_service_init_data.is_dpia_link = true; | |
1883 | ||
a98cdd8c | 1884 | link->ddc = link_create_ddc_service(&ddc_service_init_data); |
9fa0fb77 MS |
1885 | if (!link->ddc) { |
1886 | DC_ERROR("Failed to create ddc_service!\n"); | |
1887 | goto ddc_create_fail; | |
1888 | } | |
1889 | ||
1890 | /* Set dpia port index : 0 to number of dpia ports */ | |
1891 | link->ddc_hw_inst = init_params->connector_index; | |
1892 | ||
1893 | /* TODO: Create link encoder */ | |
1894 | ||
1895 | link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED; | |
1896 | ||
4b169ca3 JK |
1897 | /* Some docks seem to NAK I2C writes to segment pointer with mot=0. */ |
1898 | link->wa_flags.dp_mot_reset_segment = true; | |
1899 | ||
9fa0fb77 MS |
1900 | return true; |
1901 | ||
1902 | ddc_create_fail: | |
1903 | return false; | |
1904 | } | |
1905 | ||
1906 | static bool dc_link_construct(struct dc_link *link, | |
1907 | const struct link_init_data *init_params) | |
1908 | { | |
1909 | /* Handle dpia case */ | |
1910 | if (init_params->is_dpia_link) | |
1911 | return dc_link_construct_dpia(link, init_params); | |
1912 | else | |
1913 | return dc_link_construct_legacy(link, init_params); | |
1914 | } | |
4562236b HW |
1915 | /******************************************************************************* |
1916 | * Public functions | |
1917 | ******************************************************************************/ | |
d0778ebf | 1918 | struct dc_link *link_create(const struct link_init_data *init_params) |
4562236b | 1919 | { |
d0778ebf | 1920 | struct dc_link *link = |
2004f45e | 1921 | kzalloc(sizeof(*link), GFP_KERNEL); |
4562236b HW |
1922 | |
1923 | if (NULL == link) | |
1924 | goto alloc_fail; | |
1925 | ||
d9e32672 | 1926 | if (false == dc_link_construct(link, init_params)) |
4562236b HW |
1927 | goto construct_fail; |
1928 | ||
1929 | return link; | |
1930 | ||
1931 | construct_fail: | |
2004f45e | 1932 | kfree(link); |
4562236b HW |
1933 | |
1934 | alloc_fail: | |
1935 | return NULL; | |
1936 | } | |
1937 | ||
d0778ebf | 1938 | void link_destroy(struct dc_link **link) |
4562236b | 1939 | { |
d9e32672 | 1940 | dc_link_destruct(*link); |
2004f45e | 1941 | kfree(*link); |
4562236b HW |
1942 | *link = NULL; |
1943 | } | |
1944 | ||
4562236b HW |
1945 | static void enable_stream_features(struct pipe_ctx *pipe_ctx) |
1946 | { | |
0971c40e | 1947 | struct dc_stream_state *stream = pipe_ctx->stream; |
4562236b | 1948 | |
6016cd9d BG |
1949 | if (pipe_ctx->stream->signal != SIGNAL_TYPE_DISPLAY_PORT_MST) { |
1950 | struct dc_link *link = stream->link; | |
1951 | union down_spread_ctrl old_downspread; | |
1952 | union down_spread_ctrl new_downspread; | |
1953 | ||
0726ed30 YZ |
1954 | memset(&old_downspread, 0, sizeof(old_downspread)); |
1955 | ||
6016cd9d BG |
1956 | core_link_read_dpcd(link, DP_DOWNSPREAD_CTRL, |
1957 | &old_downspread.raw, sizeof(old_downspread)); | |
4562236b | 1958 | |
6016cd9d | 1959 | new_downspread.raw = old_downspread.raw; |
87943159 | 1960 | |
6016cd9d BG |
1961 | new_downspread.bits.IGNORE_MSA_TIMING_PARAM = |
1962 | (stream->ignore_msa_timing_param) ? 1 : 0; | |
4562236b | 1963 | |
6016cd9d BG |
1964 | if (new_downspread.raw != old_downspread.raw) { |
1965 | core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL, | |
1966 | &new_downspread.raw, sizeof(new_downspread)); | |
1967 | } | |
1968 | ||
1969 | } else { | |
1970 | dm_helpers_mst_enable_stream_features(stream); | |
87943159 | 1971 | } |
4562236b HW |
1972 | } |
1973 | ||
9100c359 MW |
1974 | static enum dc_status enable_link_dp(struct dc_state *state, |
1975 | struct pipe_ctx *pipe_ctx) | |
4562236b | 1976 | { |
0971c40e | 1977 | struct dc_stream_state *stream = pipe_ctx->stream; |
4562236b HW |
1978 | enum dc_status status; |
1979 | bool skip_video_pattern; | |
ceb3dbb4 | 1980 | struct dc_link *link = stream->link; |
017860c9 WL |
1981 | const struct dc_link_settings *link_settings = |
1982 | &pipe_ctx->link_config.dp_link_settings; | |
e0a6440a | 1983 | bool fec_enable; |
5ec43eda ML |
1984 | int i; |
1985 | bool apply_seamless_boot_optimization = false; | |
96577cf8 | 1986 | uint32_t bl_oled_enable_delay = 50; // in ms |
eccff6cd | 1987 | uint32_t post_oui_delay = 30; // 30ms |
82253671 JK |
1988 | /* Reduce link bandwidth between failed link training attempts. */ |
1989 | bool do_fallback = false; | |
5ec43eda ML |
1990 | |
1991 | // check for seamless boot | |
1992 | for (i = 0; i < state->stream_count; i++) { | |
1993 | if (state->streams[i]->apply_seamless_boot_optimization) { | |
1994 | apply_seamless_boot_optimization = true; | |
1995 | break; | |
1996 | } | |
1997 | } | |
4562236b | 1998 | |
edfb2693 JK |
1999 | /* Train with fallback when enabling DPIA link. Conventional links are |
2000 | * trained with fallback during sink detection. | |
2001 | */ | |
2002 | if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) | |
2003 | do_fallback = true; | |
2004 | ||
05692bb0 WL |
2005 | /* |
2006 | * Temporary w/a to get DP2.0 link rates to work with SST. | |
2007 | * TODO DP2.0 - Workaround: Remove w/a if and when the issue is resolved. | |
2008 | */ | |
d5a43956 | 2009 | if (link_dp_get_encoding_format(link_settings) == DP_128b_132b_ENCODING && |
05692bb0 WL |
2010 | pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT && |
2011 | link->dc->debug.set_mst_en_for_sst) { | |
f01ee019 FZ |
2012 | dp_enable_mst_on_sink(link, true); |
2013 | } | |
f01ee019 | 2014 | |
5b7c0d8d | 2015 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_EDP) { |
5b7c0d8d | 2016 | /*in case it is not on*/ |
a8201902 LM |
2017 | if (!link->dc->config.edp_no_power_sequencing) |
2018 | link->dc->hwss.edp_power_control(link, true); | |
5b7c0d8d | 2019 | link->dc->hwss.edp_wait_for_hpd_ready(link, true); |
15ae3b28 AK |
2020 | } |
2021 | ||
d5a43956 | 2022 | if (link_dp_get_encoding_format(link_settings) == DP_128b_132b_ENCODING) { |
f01ee019 FZ |
2023 | /* TODO - DP2.0 HW: calculate 32 symbol clock for HPO encoder */ |
2024 | } else { | |
2025 | pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = | |
017860c9 | 2026 | link_settings->link_rate * LINK_RATE_REF_FREQ_IN_KHZ; |
f01ee019 FZ |
2027 | if (state->clk_mgr && !apply_seamless_boot_optimization) |
2028 | state->clk_mgr->funcs->update_clocks(state->clk_mgr, | |
2029 | state, false); | |
2030 | } | |
4562236b | 2031 | |
96577cf8 HW |
2032 | // during mode switch we do DP_SET_POWER off then on, and OUI is lost |
2033 | dpcd_set_source_specific_data(link); | |
eccff6cd IC |
2034 | if (link->dpcd_sink_ext_caps.raw != 0) { |
2035 | post_oui_delay += link->panel_config.pps.extra_post_OUI_ms; | |
c797ede0 | 2036 | msleep(post_oui_delay); |
eccff6cd | 2037 | } |
96577cf8 | 2038 | |
4a3ad932 | 2039 | // similarly, mode switch can cause loss of cable ID |
c595fb05 | 2040 | dpcd_write_cable_id_to_dprx(link); |
4a3ad932 | 2041 | |
4562236b HW |
2042 | skip_video_pattern = true; |
2043 | ||
017860c9 | 2044 | if (link_settings->link_rate == LINK_RATE_LOW) |
9100c359 MW |
2045 | skip_video_pattern = false; |
2046 | ||
017860c9 | 2047 | if (perform_link_training_with_retries(link_settings, |
9100c359 MW |
2048 | skip_video_pattern, |
2049 | LINK_TRAINING_ATTEMPTS, | |
2050 | pipe_ctx, | |
82253671 JK |
2051 | pipe_ctx->stream->signal, |
2052 | do_fallback)) { | |
4562236b | 2053 | status = DC_OK; |
9100c359 | 2054 | } else { |
c0ba5ec7 | 2055 | status = DC_FAIL_DP_LINK_TRAINING; |
9100c359 | 2056 | } |
4562236b | 2057 | |
9100c359 | 2058 | if (link->preferred_training_settings.fec_enable) |
008a4016 NC |
2059 | fec_enable = *link->preferred_training_settings.fec_enable; |
2060 | else | |
2061 | fec_enable = true; | |
2062 | ||
d5a43956 | 2063 | if (link_dp_get_encoding_format(link_settings) == DP_8b_10b_ENCODING) |
f01ee019 | 2064 | dp_set_fec_enable(link, fec_enable); |
96577cf8 HW |
2065 | |
2066 | // during mode set we do DP_SET_POWER off then on, aux writes are lost | |
2067 | if (link->dpcd_sink_ext_caps.bits.oled == 1 || | |
2068 | link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1 || | |
2069 | link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1) { | |
2070 | dc_link_set_default_brightness_aux(link); // TODO: use cached if known | |
2071 | if (link->dpcd_sink_ext_caps.bits.oled == 1) | |
2072 | msleep(bl_oled_enable_delay); | |
2073 | dc_link_backlight_enable_aux(link, true); | |
2074 | } | |
2075 | ||
4562236b HW |
2076 | return status; |
2077 | } | |
2078 | ||
904623ee YS |
2079 | static enum dc_status enable_link_edp( |
2080 | struct dc_state *state, | |
2081 | struct pipe_ctx *pipe_ctx) | |
2082 | { | |
17048d89 | 2083 | return enable_link_dp(state, pipe_ctx); |
904623ee YS |
2084 | } |
2085 | ||
ab8db3e1 AG |
2086 | static enum dc_status enable_link_dp_mst( |
2087 | struct dc_state *state, | |
2088 | struct pipe_ctx *pipe_ctx) | |
4562236b | 2089 | { |
ceb3dbb4 | 2090 | struct dc_link *link = pipe_ctx->stream->link; |
4562236b HW |
2091 | |
2092 | /* sink signal type after MST branch is MST. Multiple MST sinks | |
2093 | * share one link. Link DP PHY is enable or training only once. | |
2094 | */ | |
07920450 | 2095 | if (link->link_status.link_active) |
4562236b HW |
2096 | return DC_OK; |
2097 | ||
9cc032b2 MT |
2098 | /* clear payload table */ |
2099 | dm_helpers_dp_mst_clear_payload_allocation_table(link->ctx, link); | |
2100 | ||
22051b63 | 2101 | /* to make sure the pending down rep can be processed |
9cc032b2 | 2102 | * before enabling the link |
22051b63 MT |
2103 | */ |
2104 | dm_helpers_dp_mst_poll_pending_down_reply(link->ctx, link); | |
2105 | ||
07c84c7a DW |
2106 | /* set the sink to MST mode before enabling the link */ |
2107 | dp_enable_mst_on_sink(link, true); | |
2108 | ||
ab8db3e1 | 2109 | return enable_link_dp(state, pipe_ctx); |
4562236b HW |
2110 | } |
2111 | ||
ebd1e719 LHM |
2112 | void dc_link_blank_all_dp_displays(struct dc *dc) |
2113 | { | |
2114 | unsigned int i; | |
2115 | uint8_t dpcd_power_state = '\0'; | |
2116 | enum dc_status status = DC_ERROR_UNEXPECTED; | |
2117 | ||
2118 | for (i = 0; i < dc->link_count; i++) { | |
2119 | if ((dc->links[i]->connector_signal != SIGNAL_TYPE_DISPLAY_PORT) || | |
2120 | (dc->links[i]->priv == NULL) || (dc->links[i]->local_sink == NULL)) | |
2121 | continue; | |
2122 | ||
2123 | /* DP 2.0 spec requires that we read LTTPR caps first */ | |
2124 | dp_retrieve_lttpr_cap(dc->links[i]); | |
2125 | /* if any of the displays are lit up turn them off */ | |
2126 | status = core_link_read_dpcd(dc->links[i], DP_SET_POWER, | |
2127 | &dpcd_power_state, sizeof(dpcd_power_state)); | |
2128 | ||
2129 | if (status == DC_OK && dpcd_power_state == DP_POWER_STATE_D0) | |
2130 | dc_link_blank_dp_stream(dc->links[i], true); | |
2131 | } | |
2132 | ||
2133 | } | |
2134 | ||
2d017189 DM |
2135 | void dc_link_blank_all_edp_displays(struct dc *dc) |
2136 | { | |
2137 | unsigned int i; | |
2138 | uint8_t dpcd_power_state = '\0'; | |
2139 | enum dc_status status = DC_ERROR_UNEXPECTED; | |
2140 | ||
2141 | for (i = 0; i < dc->link_count; i++) { | |
2142 | if ((dc->links[i]->connector_signal != SIGNAL_TYPE_EDP) || | |
2143 | (!dc->links[i]->edp_sink_present)) | |
2144 | continue; | |
2145 | ||
2146 | /* if any of the displays are lit up turn them off */ | |
2147 | status = core_link_read_dpcd(dc->links[i], DP_SET_POWER, | |
2148 | &dpcd_power_state, sizeof(dpcd_power_state)); | |
2149 | ||
2150 | if (status == DC_OK && dpcd_power_state == DP_POWER_STATE_D0) | |
2151 | dc_link_blank_dp_stream(dc->links[i], true); | |
2152 | } | |
2153 | } | |
2154 | ||
ebd1e719 LHM |
2155 | void dc_link_blank_dp_stream(struct dc_link *link, bool hw_init) |
2156 | { | |
2157 | unsigned int j; | |
2158 | struct dc *dc = link->ctx->dc; | |
2159 | enum signal_type signal = link->connector_signal; | |
2160 | ||
2161 | if ((signal == SIGNAL_TYPE_EDP) || | |
2162 | (signal == SIGNAL_TYPE_DISPLAY_PORT)) { | |
2163 | if (link->ep_type == DISPLAY_ENDPOINT_PHY && | |
2164 | link->link_enc->funcs->get_dig_frontend && | |
2165 | link->link_enc->funcs->is_dig_enabled(link->link_enc)) { | |
2166 | unsigned int fe = link->link_enc->funcs->get_dig_frontend(link->link_enc); | |
2167 | ||
2168 | if (fe != ENGINE_ID_UNKNOWN) | |
2169 | for (j = 0; j < dc->res_pool->stream_enc_count; j++) { | |
2170 | if (fe == dc->res_pool->stream_enc[j]->id) { | |
2171 | dc->res_pool->stream_enc[j]->funcs->dp_blank(link, | |
2172 | dc->res_pool->stream_enc[j]); | |
2173 | break; | |
2174 | } | |
2175 | } | |
2176 | } | |
2177 | ||
2178 | if ((!link->wa_flags.dp_keep_receiver_powered) || hw_init) | |
94dfeaa4 | 2179 | dc_link_dp_receiver_power_ctrl(link, false); |
ebd1e719 LHM |
2180 | } |
2181 | } | |
2182 | ||
1e8635ea ZF |
2183 | static bool get_ext_hdmi_settings(struct pipe_ctx *pipe_ctx, |
2184 | enum engine_id eng_id, | |
2185 | struct ext_hdmi_settings *settings) | |
2186 | { | |
2187 | bool result = false; | |
2188 | int i = 0; | |
2189 | struct integrated_info *integrated_info = | |
2190 | pipe_ctx->stream->ctx->dc_bios->integrated_info; | |
2191 | ||
2192 | if (integrated_info == NULL) | |
2193 | return false; | |
2194 | ||
2195 | /* | |
2196 | * Get retimer settings from sbios for passing SI eye test for DCE11 | |
2197 | * The setting values are varied based on board revision and port id | |
2198 | * Therefore the setting values of each ports is passed by sbios. | |
2199 | */ | |
2200 | ||
2201 | // Check if current bios contains ext Hdmi settings | |
2202 | if (integrated_info->gpu_cap_info & 0x20) { | |
2203 | switch (eng_id) { | |
2204 | case ENGINE_ID_DIGA: | |
2205 | settings->slv_addr = integrated_info->dp0_ext_hdmi_slv_addr; | |
2206 | settings->reg_num = integrated_info->dp0_ext_hdmi_6g_reg_num; | |
2207 | settings->reg_num_6g = integrated_info->dp0_ext_hdmi_6g_reg_num; | |
2208 | memmove(settings->reg_settings, | |
2209 | integrated_info->dp0_ext_hdmi_reg_settings, | |
2210 | sizeof(integrated_info->dp0_ext_hdmi_reg_settings)); | |
2211 | memmove(settings->reg_settings_6g, | |
2212 | integrated_info->dp0_ext_hdmi_6g_reg_settings, | |
2213 | sizeof(integrated_info->dp0_ext_hdmi_6g_reg_settings)); | |
2214 | result = true; | |
2215 | break; | |
2216 | case ENGINE_ID_DIGB: | |
2217 | settings->slv_addr = integrated_info->dp1_ext_hdmi_slv_addr; | |
2218 | settings->reg_num = integrated_info->dp1_ext_hdmi_6g_reg_num; | |
2219 | settings->reg_num_6g = integrated_info->dp1_ext_hdmi_6g_reg_num; | |
2220 | memmove(settings->reg_settings, | |
2221 | integrated_info->dp1_ext_hdmi_reg_settings, | |
2222 | sizeof(integrated_info->dp1_ext_hdmi_reg_settings)); | |
2223 | memmove(settings->reg_settings_6g, | |
2224 | integrated_info->dp1_ext_hdmi_6g_reg_settings, | |
2225 | sizeof(integrated_info->dp1_ext_hdmi_6g_reg_settings)); | |
2226 | result = true; | |
2227 | break; | |
2228 | case ENGINE_ID_DIGC: | |
2229 | settings->slv_addr = integrated_info->dp2_ext_hdmi_slv_addr; | |
2230 | settings->reg_num = integrated_info->dp2_ext_hdmi_6g_reg_num; | |
2231 | settings->reg_num_6g = integrated_info->dp2_ext_hdmi_6g_reg_num; | |
2232 | memmove(settings->reg_settings, | |
2233 | integrated_info->dp2_ext_hdmi_reg_settings, | |
2234 | sizeof(integrated_info->dp2_ext_hdmi_reg_settings)); | |
2235 | memmove(settings->reg_settings_6g, | |
2236 | integrated_info->dp2_ext_hdmi_6g_reg_settings, | |
2237 | sizeof(integrated_info->dp2_ext_hdmi_6g_reg_settings)); | |
2238 | result = true; | |
2239 | break; | |
4e527c01 AJ |
2240 | case ENGINE_ID_DIGD: |
2241 | settings->slv_addr = integrated_info->dp3_ext_hdmi_slv_addr; | |
2242 | settings->reg_num = integrated_info->dp3_ext_hdmi_6g_reg_num; | |
2243 | settings->reg_num_6g = integrated_info->dp3_ext_hdmi_6g_reg_num; | |
2244 | memmove(settings->reg_settings, | |
2245 | integrated_info->dp3_ext_hdmi_reg_settings, | |
2246 | sizeof(integrated_info->dp3_ext_hdmi_reg_settings)); | |
2247 | memmove(settings->reg_settings_6g, | |
2248 | integrated_info->dp3_ext_hdmi_6g_reg_settings, | |
2249 | sizeof(integrated_info->dp3_ext_hdmi_6g_reg_settings)); | |
2250 | result = true; | |
2251 | break; | |
1e8635ea ZF |
2252 | default: |
2253 | break; | |
2254 | } | |
2255 | ||
2256 | if (result == true) { | |
2257 | // Validate settings from bios integrated info table | |
2258 | if (settings->slv_addr == 0) | |
2259 | return false; | |
2260 | if (settings->reg_num > 9) | |
2261 | return false; | |
2262 | if (settings->reg_num_6g > 3) | |
2263 | return false; | |
2264 | ||
2265 | for (i = 0; i < settings->reg_num; i++) { | |
2266 | if (settings->reg_settings[i].i2c_reg_index > 0x20) | |
2267 | return false; | |
2268 | } | |
2269 | ||
2270 | for (i = 0; i < settings->reg_num_6g; i++) { | |
2271 | if (settings->reg_settings_6g[i].i2c_reg_index > 0x20) | |
2272 | return false; | |
2273 | } | |
2274 | } | |
2275 | } | |
2276 | ||
2277 | return result; | |
2278 | } | |
2279 | ||
2280 | static bool i2c_write(struct pipe_ctx *pipe_ctx, | |
2281 | uint8_t address, uint8_t *buffer, uint32_t length) | |
2282 | { | |
2283 | struct i2c_command cmd = {0}; | |
2284 | struct i2c_payload payload = {0}; | |
2285 | ||
2286 | memset(&payload, 0, sizeof(payload)); | |
2287 | memset(&cmd, 0, sizeof(cmd)); | |
2288 | ||
2289 | cmd.number_of_payloads = 1; | |
2290 | cmd.engine = I2C_COMMAND_ENGINE_DEFAULT; | |
2291 | cmd.speed = pipe_ctx->stream->ctx->dc->caps.i2c_speed_in_khz; | |
2292 | ||
2293 | payload.address = address; | |
2294 | payload.data = buffer; | |
2295 | payload.length = length; | |
2296 | payload.write = true; | |
2297 | cmd.payloads = &payload; | |
2298 | ||
c85e6e54 | 2299 | if (dm_helpers_submit_i2c(pipe_ctx->stream->ctx, |
ceb3dbb4 | 2300 | pipe_ctx->stream->link, &cmd)) |
1e8635ea ZF |
2301 | return true; |
2302 | ||
2303 | return false; | |
2304 | } | |
2305 | ||
2306 | static void write_i2c_retimer_setting( | |
2307 | struct pipe_ctx *pipe_ctx, | |
2308 | bool is_vga_mode, | |
2309 | bool is_over_340mhz, | |
2310 | struct ext_hdmi_settings *settings) | |
2311 | { | |
2312 | uint8_t slave_address = (settings->slv_addr >> 1); | |
2313 | uint8_t buffer[2]; | |
2314 | const uint8_t apply_rx_tx_change = 0x4; | |
2315 | uint8_t offset = 0xA; | |
2316 | uint8_t value = 0; | |
2317 | int i = 0; | |
2318 | bool i2c_success = false; | |
2f14bc89 | 2319 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
1e8635ea ZF |
2320 | |
2321 | memset(&buffer, 0, sizeof(buffer)); | |
2322 | ||
2323 | /* Start Ext-Hdmi programming*/ | |
2324 | ||
2325 | for (i = 0; i < settings->reg_num; i++) { | |
2326 | /* Apply 3G settings */ | |
2327 | if (settings->reg_settings[i].i2c_reg_index <= 0x20) { | |
2328 | ||
2329 | buffer[0] = settings->reg_settings[i].i2c_reg_index; | |
2330 | buffer[1] = settings->reg_settings[i].i2c_reg_val; | |
2331 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2332 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2333 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
2334 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", | |
2335 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
2336 | |
2337 | if (!i2c_success) | |
a0e40018 | 2338 | goto i2c_write_fail; |
1e8635ea ZF |
2339 | |
2340 | /* Based on DP159 specs, APPLY_RX_TX_CHANGE bit in 0x0A | |
2341 | * needs to be set to 1 on every 0xA-0xC write. | |
2342 | */ | |
2343 | if (settings->reg_settings[i].i2c_reg_index == 0xA || | |
2344 | settings->reg_settings[i].i2c_reg_index == 0xB || | |
2345 | settings->reg_settings[i].i2c_reg_index == 0xC) { | |
2346 | ||
2347 | /* Query current value from offset 0xA */ | |
2348 | if (settings->reg_settings[i].i2c_reg_index == 0xA) | |
2349 | value = settings->reg_settings[i].i2c_reg_val; | |
2350 | else { | |
2351 | i2c_success = | |
a98cdd8c | 2352 | link_query_ddc_data( |
ceb3dbb4 | 2353 | pipe_ctx->stream->link->ddc, |
1e8635ea ZF |
2354 | slave_address, &offset, 1, &value, 1); |
2355 | if (!i2c_success) | |
a0e40018 | 2356 | goto i2c_write_fail; |
1e8635ea ZF |
2357 | } |
2358 | ||
2359 | buffer[0] = offset; | |
2360 | /* Set APPLY_RX_TX_CHANGE bit to 1 */ | |
2361 | buffer[1] = value | apply_rx_tx_change; | |
2362 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2363 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2364 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
2365 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
2366 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2367 | if (!i2c_success) |
a0e40018 | 2368 | goto i2c_write_fail; |
1e8635ea ZF |
2369 | } |
2370 | } | |
2371 | } | |
2372 | ||
2373 | /* Apply 3G settings */ | |
2374 | if (is_over_340mhz) { | |
2375 | for (i = 0; i < settings->reg_num_6g; i++) { | |
2376 | /* Apply 3G settings */ | |
2377 | if (settings->reg_settings[i].i2c_reg_index <= 0x20) { | |
2378 | ||
2379 | buffer[0] = settings->reg_settings_6g[i].i2c_reg_index; | |
2380 | buffer[1] = settings->reg_settings_6g[i].i2c_reg_val; | |
2381 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2382 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2383 | RETIMER_REDRIVER_INFO("above 340Mhz: retimer write to slave_address = 0x%x,\ |
2384 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
2385 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
2386 | |
2387 | if (!i2c_success) | |
a0e40018 | 2388 | goto i2c_write_fail; |
1e8635ea ZF |
2389 | |
2390 | /* Based on DP159 specs, APPLY_RX_TX_CHANGE bit in 0x0A | |
2391 | * needs to be set to 1 on every 0xA-0xC write. | |
2392 | */ | |
2393 | if (settings->reg_settings_6g[i].i2c_reg_index == 0xA || | |
2394 | settings->reg_settings_6g[i].i2c_reg_index == 0xB || | |
2395 | settings->reg_settings_6g[i].i2c_reg_index == 0xC) { | |
2396 | ||
2397 | /* Query current value from offset 0xA */ | |
2398 | if (settings->reg_settings_6g[i].i2c_reg_index == 0xA) | |
2399 | value = settings->reg_settings_6g[i].i2c_reg_val; | |
2400 | else { | |
2401 | i2c_success = | |
a98cdd8c | 2402 | link_query_ddc_data( |
ceb3dbb4 | 2403 | pipe_ctx->stream->link->ddc, |
1e8635ea ZF |
2404 | slave_address, &offset, 1, &value, 1); |
2405 | if (!i2c_success) | |
a0e40018 | 2406 | goto i2c_write_fail; |
1e8635ea ZF |
2407 | } |
2408 | ||
2409 | buffer[0] = offset; | |
2410 | /* Set APPLY_RX_TX_CHANGE bit to 1 */ | |
2411 | buffer[1] = value | apply_rx_tx_change; | |
2412 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2413 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2414 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
2415 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
2416 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2417 | if (!i2c_success) |
a0e40018 | 2418 | goto i2c_write_fail; |
1e8635ea ZF |
2419 | } |
2420 | } | |
2421 | } | |
2422 | } | |
2423 | ||
2424 | if (is_vga_mode) { | |
2425 | /* Program additional settings if using 640x480 resolution */ | |
2426 | ||
2427 | /* Write offset 0xFF to 0x01 */ | |
2428 | buffer[0] = 0xff; | |
2429 | buffer[1] = 0x01; | |
2430 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2431 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2432 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
2433 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
2434 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2435 | if (!i2c_success) |
a0e40018 | 2436 | goto i2c_write_fail; |
1e8635ea ZF |
2437 | |
2438 | /* Write offset 0x00 to 0x23 */ | |
2439 | buffer[0] = 0x00; | |
2440 | buffer[1] = 0x23; | |
2441 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2442 | buffer, sizeof(buffer)); | |
2f14bc89 | 2443 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
c2af2a42 | 2444 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", |
2f14bc89 | 2445 | slave_address, buffer[0], buffer[1], i2c_success?1:0); |
1e8635ea | 2446 | if (!i2c_success) |
a0e40018 | 2447 | goto i2c_write_fail; |
1e8635ea ZF |
2448 | |
2449 | /* Write offset 0xff to 0x00 */ | |
2450 | buffer[0] = 0xff; | |
2451 | buffer[1] = 0x00; | |
2452 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2453 | buffer, sizeof(buffer)); | |
2f14bc89 | 2454 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
c2af2a42 | 2455 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", |
2f14bc89 | 2456 | slave_address, buffer[0], buffer[1], i2c_success?1:0); |
1e8635ea | 2457 | if (!i2c_success) |
a0e40018 | 2458 | goto i2c_write_fail; |
1e8635ea ZF |
2459 | |
2460 | } | |
a0e40018 RS |
2461 | |
2462 | return; | |
2463 | ||
2464 | i2c_write_fail: | |
2465 | DC_LOG_DEBUG("Set retimer failed"); | |
1e8635ea ZF |
2466 | } |
2467 | ||
2468 | static void write_i2c_default_retimer_setting( | |
2469 | struct pipe_ctx *pipe_ctx, | |
2470 | bool is_vga_mode, | |
2471 | bool is_over_340mhz) | |
2472 | { | |
2473 | uint8_t slave_address = (0xBA >> 1); | |
2474 | uint8_t buffer[2]; | |
2475 | bool i2c_success = false; | |
2f14bc89 | 2476 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
1e8635ea ZF |
2477 | |
2478 | memset(&buffer, 0, sizeof(buffer)); | |
2479 | ||
2480 | /* Program Slave Address for tuning single integrity */ | |
2481 | /* Write offset 0x0A to 0x13 */ | |
2482 | buffer[0] = 0x0A; | |
2483 | buffer[1] = 0x13; | |
2484 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2485 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2486 | RETIMER_REDRIVER_INFO("retimer writes default setting to slave_address = 0x%x,\ |
2487 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
2488 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2489 | if (!i2c_success) |
a0e40018 | 2490 | goto i2c_write_fail; |
1e8635ea ZF |
2491 | |
2492 | /* Write offset 0x0A to 0x17 */ | |
2493 | buffer[0] = 0x0A; | |
2494 | buffer[1] = 0x17; | |
2495 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2496 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2497 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
2498 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
2499 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2500 | if (!i2c_success) |
a0e40018 | 2501 | goto i2c_write_fail; |
1e8635ea ZF |
2502 | |
2503 | /* Write offset 0x0B to 0xDA or 0xD8 */ | |
2504 | buffer[0] = 0x0B; | |
2505 | buffer[1] = is_over_340mhz ? 0xDA : 0xD8; | |
2506 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2507 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2508 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
2509 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
2510 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2511 | if (!i2c_success) |
a0e40018 | 2512 | goto i2c_write_fail; |
1e8635ea ZF |
2513 | |
2514 | /* Write offset 0x0A to 0x17 */ | |
2515 | buffer[0] = 0x0A; | |
2516 | buffer[1] = 0x17; | |
2517 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2518 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2519 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
2520 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", | |
2521 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2522 | if (!i2c_success) |
a0e40018 | 2523 | goto i2c_write_fail; |
1e8635ea ZF |
2524 | |
2525 | /* Write offset 0x0C to 0x1D or 0x91 */ | |
2526 | buffer[0] = 0x0C; | |
2527 | buffer[1] = is_over_340mhz ? 0x1D : 0x91; | |
2528 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2529 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2530 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
2531 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
2532 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2533 | if (!i2c_success) |
a0e40018 | 2534 | goto i2c_write_fail; |
1e8635ea ZF |
2535 | |
2536 | /* Write offset 0x0A to 0x17 */ | |
2537 | buffer[0] = 0x0A; | |
2538 | buffer[1] = 0x17; | |
2539 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2540 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2541 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
2542 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
2543 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2544 | if (!i2c_success) |
a0e40018 | 2545 | goto i2c_write_fail; |
1e8635ea ZF |
2546 | |
2547 | ||
2548 | if (is_vga_mode) { | |
2549 | /* Program additional settings if using 640x480 resolution */ | |
2550 | ||
2551 | /* Write offset 0xFF to 0x01 */ | |
2552 | buffer[0] = 0xff; | |
2553 | buffer[1] = 0x01; | |
2554 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2555 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2556 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
2557 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
2558 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2559 | if (!i2c_success) |
a0e40018 | 2560 | goto i2c_write_fail; |
1e8635ea ZF |
2561 | |
2562 | /* Write offset 0x00 to 0x23 */ | |
2563 | buffer[0] = 0x00; | |
2564 | buffer[1] = 0x23; | |
2565 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2566 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2567 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
2568 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", | |
2569 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2570 | if (!i2c_success) |
a0e40018 | 2571 | goto i2c_write_fail; |
1e8635ea ZF |
2572 | |
2573 | /* Write offset 0xff to 0x00 */ | |
2574 | buffer[0] = 0xff; | |
2575 | buffer[1] = 0x00; | |
2576 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2577 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2578 | RETIMER_REDRIVER_INFO("retimer write default setting to slave_addr = 0x%x,\ |
2579 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d end here\n", | |
2580 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea | 2581 | if (!i2c_success) |
a0e40018 | 2582 | goto i2c_write_fail; |
1e8635ea | 2583 | } |
a0e40018 RS |
2584 | |
2585 | return; | |
2586 | ||
2587 | i2c_write_fail: | |
2588 | DC_LOG_DEBUG("Set default retimer failed"); | |
1e8635ea ZF |
2589 | } |
2590 | ||
2591 | static void write_i2c_redriver_setting( | |
2592 | struct pipe_ctx *pipe_ctx, | |
2593 | bool is_over_340mhz) | |
2594 | { | |
2595 | uint8_t slave_address = (0xF0 >> 1); | |
2596 | uint8_t buffer[16]; | |
2597 | bool i2c_success = false; | |
2f14bc89 | 2598 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
1e8635ea ZF |
2599 | |
2600 | memset(&buffer, 0, sizeof(buffer)); | |
2601 | ||
2602 | // Program Slave Address for tuning single integrity | |
2603 | buffer[3] = 0x4E; | |
2604 | buffer[4] = 0x4E; | |
2605 | buffer[5] = 0x4E; | |
2606 | buffer[6] = is_over_340mhz ? 0x4E : 0x4A; | |
2607 | ||
2608 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
2609 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
2610 | RETIMER_REDRIVER_INFO("redriver write 0 to all 16 reg offset expect following:\n\ |
2611 | \t slave_addr = 0x%x, offset[3] = 0x%x, offset[4] = 0x%x,\ | |
2612 | offset[5] = 0x%x,offset[6] is_over_340mhz = 0x%x,\ | |
2613 | i2c_success = %d\n", | |
2614 | slave_address, buffer[3], buffer[4], buffer[5], buffer[6], i2c_success?1:0); | |
1e8635ea ZF |
2615 | |
2616 | if (!i2c_success) | |
a0e40018 | 2617 | DC_LOG_DEBUG("Set redriver failed"); |
1e8635ea ZF |
2618 | } |
2619 | ||
ef30f441 WL |
2620 | static void disable_link(struct dc_link *link, const struct link_resource *link_res, |
2621 | enum signal_type signal) | |
61f14c5b LL |
2622 | { |
2623 | /* | |
2624 | * TODO: implement call for dp_set_hw_test_pattern | |
2625 | * it is needed for compliance testing | |
2626 | */ | |
2627 | ||
2628 | /* Here we need to specify that encoder output settings | |
2629 | * need to be calculated as for the set mode, | |
2630 | * it will lead to querying dynamic link capabilities | |
2631 | * which should be done before enable output | |
2632 | */ | |
2633 | ||
2634 | if (dc_is_dp_signal(signal)) { | |
2635 | /* SST DP, eDP */ | |
f01ee019 | 2636 | struct dc_link_settings link_settings = link->cur_link_settings; |
61f14c5b | 2637 | if (dc_is_dp_sst_signal(signal)) |
ef30f441 | 2638 | dp_disable_link_phy(link, link_res, signal); |
61f14c5b | 2639 | else |
ef30f441 | 2640 | dp_disable_link_phy_mst(link, link_res, signal); |
61f14c5b LL |
2641 | |
2642 | if (dc_is_dp_sst_signal(signal) || | |
2643 | link->mst_stream_alloc_table.stream_count == 0) { | |
d5a43956 | 2644 | if (link_dp_get_encoding_format(&link_settings) == DP_8b_10b_ENCODING) { |
f01ee019 | 2645 | dp_set_fec_enable(link, false); |
ef30f441 | 2646 | dp_set_fec_ready(link, link_res, false); |
f01ee019 | 2647 | } |
61f14c5b | 2648 | } |
9c75891f WL |
2649 | } else if (signal != SIGNAL_TYPE_VIRTUAL) { |
2650 | link->dc->hwss.disable_link_output(link, link_res, signal); | |
61f14c5b LL |
2651 | } |
2652 | ||
2653 | if (signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { | |
2654 | /* MST disable link only when no stream use the link */ | |
2655 | if (link->mst_stream_alloc_table.stream_count <= 0) | |
2656 | link->link_status.link_active = false; | |
2657 | } else { | |
2658 | link->link_status.link_active = false; | |
2659 | } | |
2660 | } | |
2661 | ||
4562236b HW |
2662 | static void enable_link_hdmi(struct pipe_ctx *pipe_ctx) |
2663 | { | |
0971c40e | 2664 | struct dc_stream_state *stream = pipe_ctx->stream; |
ceb3dbb4 | 2665 | struct dc_link *link = stream->link; |
cc4d99b8 | 2666 | enum dc_color_depth display_color_depth; |
1e8635ea ZF |
2667 | enum engine_id eng_id; |
2668 | struct ext_hdmi_settings settings = {0}; | |
2669 | bool is_over_340mhz = false; | |
2670 | bool is_vga_mode = (stream->timing.h_addressable == 640) | |
2671 | && (stream->timing.v_addressable == 480); | |
9c75891f | 2672 | struct dc *dc = pipe_ctx->stream->ctx->dc; |
1e8635ea | 2673 | |
321f65a6 | 2674 | if (stream->phy_pix_clk == 0) |
380604e2 | 2675 | stream->phy_pix_clk = stream->timing.pix_clk_100hz / 10; |
1e8635ea ZF |
2676 | if (stream->phy_pix_clk > 340000) |
2677 | is_over_340mhz = true; | |
2678 | ||
2679 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) { | |
ceb3dbb4 | 2680 | unsigned short masked_chip_caps = pipe_ctx->stream->link->chip_caps & |
3e5e2215 | 2681 | EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK; |
cc57306f | 2682 | if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_TISN65DP159RSBT) { |
1e8635ea ZF |
2683 | /* DP159, Retimer settings */ |
2684 | eng_id = pipe_ctx->stream_res.stream_enc->id; | |
2685 | ||
2686 | if (get_ext_hdmi_settings(pipe_ctx, eng_id, &settings)) { | |
2687 | write_i2c_retimer_setting(pipe_ctx, | |
2688 | is_vga_mode, is_over_340mhz, &settings); | |
2689 | } else { | |
2690 | write_i2c_default_retimer_setting(pipe_ctx, | |
2691 | is_vga_mode, is_over_340mhz); | |
2692 | } | |
cc57306f | 2693 | } else if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_PI3EQX1204) { |
1e8635ea ZF |
2694 | /* PI3EQX1204, Redriver settings */ |
2695 | write_i2c_redriver_setting(pipe_ctx, is_over_340mhz); | |
2696 | } | |
2697 | } | |
4562236b HW |
2698 | |
2699 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) | |
a98cdd8c | 2700 | write_scdc_data( |
ceb3dbb4 | 2701 | stream->link->ddc, |
4562236b | 2702 | stream->phy_pix_clk, |
4fa086b9 | 2703 | stream->timing.flags.LTE_340MCSC_SCRAMBLE); |
4562236b | 2704 | |
ceb3dbb4 | 2705 | memset(&stream->link->cur_link_settings, 0, |
4562236b HW |
2706 | sizeof(struct dc_link_settings)); |
2707 | ||
4fa086b9 LSL |
2708 | display_color_depth = stream->timing.display_color_depth; |
2709 | if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) | |
cc4d99b8 CL |
2710 | display_color_depth = COLOR_DEPTH_888; |
2711 | ||
9c75891f WL |
2712 | dc->hwss.enable_tmds_link_output( |
2713 | link, | |
2714 | &pipe_ctx->link_res, | |
2715 | pipe_ctx->stream->signal, | |
4562236b | 2716 | pipe_ctx->clock_source->id, |
cc4d99b8 | 2717 | display_color_depth, |
4562236b HW |
2718 | stream->phy_pix_clk); |
2719 | ||
3a9aeadb | 2720 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) |
a98cdd8c | 2721 | read_scdc_data(link->ddc); |
4562236b HW |
2722 | } |
2723 | ||
11c3ee48 AD |
2724 | static void enable_link_lvds(struct pipe_ctx *pipe_ctx) |
2725 | { | |
2726 | struct dc_stream_state *stream = pipe_ctx->stream; | |
ceb3dbb4 | 2727 | struct dc_link *link = stream->link; |
9c75891f | 2728 | struct dc *dc = stream->ctx->dc; |
11c3ee48 AD |
2729 | |
2730 | if (stream->phy_pix_clk == 0) | |
380604e2 | 2731 | stream->phy_pix_clk = stream->timing.pix_clk_100hz / 10; |
11c3ee48 | 2732 | |
ceb3dbb4 | 2733 | memset(&stream->link->cur_link_settings, 0, |
11c3ee48 | 2734 | sizeof(struct dc_link_settings)); |
9c75891f WL |
2735 | dc->hwss.enable_lvds_link_output( |
2736 | link, | |
2737 | &pipe_ctx->link_res, | |
11c3ee48 AD |
2738 | pipe_ctx->clock_source->id, |
2739 | stream->phy_pix_clk); | |
2740 | ||
2741 | } | |
2742 | ||
90bb21cb MA |
2743 | bool dc_power_alpm_dpcd_enable(struct dc_link *link, bool enable) |
2744 | { | |
2745 | bool ret = false; | |
2746 | union dpcd_alpm_configuration alpm_config; | |
2747 | ||
2748 | if (link->psr_settings.psr_version == DC_PSR_VERSION_SU_1) { | |
2749 | memset(&alpm_config, 0, sizeof(alpm_config)); | |
2750 | ||
2751 | alpm_config.bits.ENABLE = (enable ? true : false); | |
2752 | ret = dm_helpers_dp_write_dpcd(link->ctx, link, | |
2753 | DP_RECEIVER_ALPM_CONFIG, &alpm_config.raw, | |
2754 | sizeof(alpm_config.raw)); | |
2755 | } | |
2756 | return ret; | |
2757 | } | |
2758 | ||
4562236b | 2759 | /****************************enable_link***********************************/ |
ab8db3e1 AG |
2760 | static enum dc_status enable_link( |
2761 | struct dc_state *state, | |
2762 | struct pipe_ctx *pipe_ctx) | |
4562236b HW |
2763 | { |
2764 | enum dc_status status = DC_ERROR_UNEXPECTED; | |
61f14c5b LL |
2765 | struct dc_stream_state *stream = pipe_ctx->stream; |
2766 | struct dc_link *link = stream->link; | |
2767 | ||
2768 | /* There's some scenarios where driver is unloaded with display | |
2769 | * still enabled. When driver is reloaded, it may cause a display | |
2770 | * to not light up if there is a mismatch between old and new | |
2771 | * link settings. Need to call disable first before enabling at | |
2772 | * new link settings. | |
2773 | */ | |
2774 | if (link->link_status.link_active) { | |
ef30f441 | 2775 | disable_link(link, &pipe_ctx->link_res, pipe_ctx->stream->signal); |
61f14c5b LL |
2776 | } |
2777 | ||
4562236b HW |
2778 | switch (pipe_ctx->stream->signal) { |
2779 | case SIGNAL_TYPE_DISPLAY_PORT: | |
ab8db3e1 | 2780 | status = enable_link_dp(state, pipe_ctx); |
4562236b | 2781 | break; |
904623ee YS |
2782 | case SIGNAL_TYPE_EDP: |
2783 | status = enable_link_edp(state, pipe_ctx); | |
2784 | break; | |
4562236b | 2785 | case SIGNAL_TYPE_DISPLAY_PORT_MST: |
ab8db3e1 | 2786 | status = enable_link_dp_mst(state, pipe_ctx); |
4562236b HW |
2787 | msleep(200); |
2788 | break; | |
2789 | case SIGNAL_TYPE_DVI_SINGLE_LINK: | |
2790 | case SIGNAL_TYPE_DVI_DUAL_LINK: | |
2791 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
2792 | enable_link_hdmi(pipe_ctx); | |
2793 | status = DC_OK; | |
2794 | break; | |
11c3ee48 AD |
2795 | case SIGNAL_TYPE_LVDS: |
2796 | enable_link_lvds(pipe_ctx); | |
2797 | status = DC_OK; | |
2798 | break; | |
4562236b HW |
2799 | case SIGNAL_TYPE_VIRTUAL: |
2800 | status = DC_OK; | |
2801 | break; | |
2802 | default: | |
2803 | break; | |
2804 | } | |
2805 | ||
293b9160 AK |
2806 | if (status == DC_OK) |
2807 | pipe_ctx->stream->link->link_status.link_active = true; | |
2808 | ||
4562236b HW |
2809 | return status; |
2810 | } | |
2811 | ||
162f8078 ML |
2812 | static uint32_t get_timing_pixel_clock_100hz(const struct dc_crtc_timing *timing) |
2813 | { | |
2814 | ||
2815 | uint32_t pxl_clk = timing->pix_clk_100hz; | |
2816 | ||
2817 | if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) | |
2818 | pxl_clk /= 2; | |
2819 | else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) | |
2820 | pxl_clk = pxl_clk * 2 / 3; | |
2821 | ||
2822 | if (timing->display_color_depth == COLOR_DEPTH_101010) | |
2823 | pxl_clk = pxl_clk * 10 / 8; | |
2824 | else if (timing->display_color_depth == COLOR_DEPTH_121212) | |
2825 | pxl_clk = pxl_clk * 12 / 8; | |
2826 | ||
2827 | return pxl_clk; | |
2828 | } | |
2829 | ||
73da927b | 2830 | static bool dp_active_dongle_validate_timing( |
6bffebc9 | 2831 | const struct dc_crtc_timing *timing, |
f110892e | 2832 | const struct dpcd_caps *dpcd_caps) |
6bffebc9 | 2833 | { |
f110892e HW |
2834 | const struct dc_dongle_caps *dongle_caps = &dpcd_caps->dongle_caps; |
2835 | ||
2836 | switch (dpcd_caps->dongle_type) { | |
2837 | case DISPLAY_DONGLE_DP_VGA_CONVERTER: | |
2838 | case DISPLAY_DONGLE_DP_DVI_CONVERTER: | |
2839 | case DISPLAY_DONGLE_DP_DVI_DONGLE: | |
2840 | if (timing->pixel_encoding == PIXEL_ENCODING_RGB) | |
2841 | return true; | |
2842 | else | |
2843 | return false; | |
2844 | default: | |
2845 | break; | |
2846 | } | |
6bffebc9 | 2847 | |
f01ee019 FZ |
2848 | if (dpcd_caps->dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER && |
2849 | dongle_caps->extendedCapValid == true) { | |
d9eb8fea WL |
2850 | /* Check Pixel Encoding */ |
2851 | switch (timing->pixel_encoding) { | |
2852 | case PIXEL_ENCODING_RGB: | |
2853 | case PIXEL_ENCODING_YCBCR444: | |
2854 | break; | |
2855 | case PIXEL_ENCODING_YCBCR422: | |
2856 | if (!dongle_caps->is_dp_hdmi_ycbcr422_pass_through) | |
2857 | return false; | |
2858 | break; | |
2859 | case PIXEL_ENCODING_YCBCR420: | |
2860 | if (!dongle_caps->is_dp_hdmi_ycbcr420_pass_through) | |
2861 | return false; | |
2862 | break; | |
2863 | default: | |
2864 | /* Invalid Pixel Encoding*/ | |
6bffebc9 | 2865 | return false; |
d9eb8fea | 2866 | } |
6bffebc9 | 2867 | |
d9eb8fea WL |
2868 | switch (timing->display_color_depth) { |
2869 | case COLOR_DEPTH_666: | |
2870 | case COLOR_DEPTH_888: | |
2871 | /*888 and 666 should always be supported*/ | |
2872 | break; | |
2873 | case COLOR_DEPTH_101010: | |
2874 | if (dongle_caps->dp_hdmi_max_bpc < 10) | |
2875 | return false; | |
2876 | break; | |
2877 | case COLOR_DEPTH_121212: | |
2878 | if (dongle_caps->dp_hdmi_max_bpc < 12) | |
2879 | return false; | |
2880 | break; | |
2881 | case COLOR_DEPTH_141414: | |
2882 | case COLOR_DEPTH_161616: | |
2883 | default: | |
2884 | /* These color depths are currently not supported */ | |
6bffebc9 | 2885 | return false; |
d9eb8fea | 2886 | } |
6bffebc9 | 2887 | |
91dcfe5f DV |
2888 | /* Check 3D format */ |
2889 | switch (timing->timing_3d_format) { | |
2890 | case TIMING_3D_FORMAT_NONE: | |
2891 | case TIMING_3D_FORMAT_FRAME_ALTERNATE: | |
2892 | /*Only frame alternate 3D is supported on active dongle*/ | |
2893 | break; | |
2894 | default: | |
2895 | /*other 3D formats are not supported due to bad infoframe translation */ | |
2896 | return false; | |
2897 | } | |
2898 | ||
c022375a | 2899 | #if defined(CONFIG_DRM_AMD_DC_DCN) |
d9eb8fea WL |
2900 | if (dongle_caps->dp_hdmi_frl_max_link_bw_in_kbps > 0) { // DP to HDMI FRL converter |
2901 | struct dc_crtc_timing outputTiming = *timing; | |
c022375a | 2902 | |
d9eb8fea WL |
2903 | if (timing->flags.DSC && !timing->dsc_cfg.is_frl) |
2904 | /* DP input has DSC, HDMI FRL output doesn't have DSC, remove DSC from output timing */ | |
2905 | outputTiming.flags.DSC = 0; | |
2906 | if (dc_bandwidth_in_kbps_from_timing(&outputTiming) > dongle_caps->dp_hdmi_frl_max_link_bw_in_kbps) | |
2907 | return false; | |
2908 | } else { // DP to HDMI TMDS converter | |
2909 | if (get_timing_pixel_clock_100hz(timing) > (dongle_caps->dp_hdmi_max_pixel_clk_in_khz * 10)) | |
2910 | return false; | |
2911 | } | |
2912 | #else | |
c022375a FZ |
2913 | if (get_timing_pixel_clock_100hz(timing) > (dongle_caps->dp_hdmi_max_pixel_clk_in_khz * 10)) |
2914 | return false; | |
c022375a | 2915 | #endif |
f01ee019 FZ |
2916 | } |
2917 | ||
928adbf6 WL |
2918 | if (dpcd_caps->channel_coding_cap.bits.DP_128b_132b_SUPPORTED == 0 && |
2919 | dpcd_caps->dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT == 0 && | |
2920 | dongle_caps->dfp_cap_ext.supported) { | |
f01ee019 FZ |
2921 | |
2922 | if (dongle_caps->dfp_cap_ext.max_pixel_rate_in_mps < (timing->pix_clk_100hz / 10000)) | |
2923 | return false; | |
2924 | ||
2925 | if (dongle_caps->dfp_cap_ext.max_video_h_active_width < timing->h_addressable) | |
2926 | return false; | |
2927 | ||
2928 | if (dongle_caps->dfp_cap_ext.max_video_v_active_height < timing->v_addressable) | |
2929 | return false; | |
2930 | ||
2931 | if (timing->pixel_encoding == PIXEL_ENCODING_RGB) { | |
2932 | if (!dongle_caps->dfp_cap_ext.encoding_format_caps.support_rgb) | |
2933 | return false; | |
2934 | if (timing->display_color_depth == COLOR_DEPTH_666 && | |
2935 | !dongle_caps->dfp_cap_ext.rgb_color_depth_caps.support_6bpc) | |
2936 | return false; | |
2937 | else if (timing->display_color_depth == COLOR_DEPTH_888 && | |
2938 | !dongle_caps->dfp_cap_ext.rgb_color_depth_caps.support_8bpc) | |
2939 | return false; | |
2940 | else if (timing->display_color_depth == COLOR_DEPTH_101010 && | |
2941 | !dongle_caps->dfp_cap_ext.rgb_color_depth_caps.support_10bpc) | |
2942 | return false; | |
2943 | else if (timing->display_color_depth == COLOR_DEPTH_121212 && | |
2944 | !dongle_caps->dfp_cap_ext.rgb_color_depth_caps.support_12bpc) | |
2945 | return false; | |
2946 | else if (timing->display_color_depth == COLOR_DEPTH_161616 && | |
2947 | !dongle_caps->dfp_cap_ext.rgb_color_depth_caps.support_16bpc) | |
2948 | return false; | |
2949 | } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) { | |
2950 | if (!dongle_caps->dfp_cap_ext.encoding_format_caps.support_rgb) | |
2951 | return false; | |
2952 | if (timing->display_color_depth == COLOR_DEPTH_888 && | |
2953 | !dongle_caps->dfp_cap_ext.ycbcr444_color_depth_caps.support_8bpc) | |
2954 | return false; | |
2955 | else if (timing->display_color_depth == COLOR_DEPTH_101010 && | |
2956 | !dongle_caps->dfp_cap_ext.ycbcr444_color_depth_caps.support_10bpc) | |
2957 | return false; | |
2958 | else if (timing->display_color_depth == COLOR_DEPTH_121212 && | |
2959 | !dongle_caps->dfp_cap_ext.ycbcr444_color_depth_caps.support_12bpc) | |
2960 | return false; | |
2961 | else if (timing->display_color_depth == COLOR_DEPTH_161616 && | |
2962 | !dongle_caps->dfp_cap_ext.ycbcr444_color_depth_caps.support_16bpc) | |
2963 | return false; | |
2964 | } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) { | |
2965 | if (!dongle_caps->dfp_cap_ext.encoding_format_caps.support_rgb) | |
2966 | return false; | |
2967 | if (timing->display_color_depth == COLOR_DEPTH_888 && | |
2968 | !dongle_caps->dfp_cap_ext.ycbcr422_color_depth_caps.support_8bpc) | |
2969 | return false; | |
2970 | else if (timing->display_color_depth == COLOR_DEPTH_101010 && | |
2971 | !dongle_caps->dfp_cap_ext.ycbcr422_color_depth_caps.support_10bpc) | |
2972 | return false; | |
2973 | else if (timing->display_color_depth == COLOR_DEPTH_121212 && | |
2974 | !dongle_caps->dfp_cap_ext.ycbcr422_color_depth_caps.support_12bpc) | |
2975 | return false; | |
2976 | else if (timing->display_color_depth == COLOR_DEPTH_161616 && | |
2977 | !dongle_caps->dfp_cap_ext.ycbcr422_color_depth_caps.support_16bpc) | |
2978 | return false; | |
2979 | } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) { | |
2980 | if (!dongle_caps->dfp_cap_ext.encoding_format_caps.support_rgb) | |
2981 | return false; | |
2982 | if (timing->display_color_depth == COLOR_DEPTH_888 && | |
2983 | !dongle_caps->dfp_cap_ext.ycbcr420_color_depth_caps.support_8bpc) | |
2984 | return false; | |
2985 | else if (timing->display_color_depth == COLOR_DEPTH_101010 && | |
2986 | !dongle_caps->dfp_cap_ext.ycbcr420_color_depth_caps.support_10bpc) | |
2987 | return false; | |
2988 | else if (timing->display_color_depth == COLOR_DEPTH_121212 && | |
2989 | !dongle_caps->dfp_cap_ext.ycbcr420_color_depth_caps.support_12bpc) | |
2990 | return false; | |
2991 | else if (timing->display_color_depth == COLOR_DEPTH_161616 && | |
2992 | !dongle_caps->dfp_cap_ext.ycbcr420_color_depth_caps.support_16bpc) | |
2993 | return false; | |
2994 | } | |
2995 | } | |
f01ee019 | 2996 | |
6bffebc9 EY |
2997 | return true; |
2998 | } | |
2999 | ||
4562236b | 3000 | enum dc_status dc_link_validate_mode_timing( |
0971c40e | 3001 | const struct dc_stream_state *stream, |
d0778ebf | 3002 | struct dc_link *link, |
4562236b HW |
3003 | const struct dc_crtc_timing *timing) |
3004 | { | |
380604e2 | 3005 | uint32_t max_pix_clk = stream->link->dongle_max_pix_clk * 10; |
f110892e | 3006 | struct dpcd_caps *dpcd_caps = &link->dpcd_caps; |
4562236b HW |
3007 | |
3008 | /* A hack to avoid failing any modes for EDID override feature on | |
3009 | * topology change such as lower quality cable for DP or different dongle | |
3010 | */ | |
95d620ad | 3011 | if (link->remote_sinks[0] && link->remote_sinks[0]->sink_signal == SIGNAL_TYPE_VIRTUAL) |
4562236b HW |
3012 | return DC_OK; |
3013 | ||
6bffebc9 | 3014 | /* Passive Dongle */ |
162f8078 | 3015 | if (max_pix_clk != 0 && get_timing_pixel_clock_100hz(timing) > max_pix_clk) |
6bffebc9 EY |
3016 | return DC_EXCEED_DONGLE_CAP; |
3017 | ||
3018 | /* Active Dongle*/ | |
f110892e | 3019 | if (!dp_active_dongle_validate_timing(timing, dpcd_caps)) |
6bffebc9 | 3020 | return DC_EXCEED_DONGLE_CAP; |
4562236b HW |
3021 | |
3022 | switch (stream->signal) { | |
3023 | case SIGNAL_TYPE_EDP: | |
3024 | case SIGNAL_TYPE_DISPLAY_PORT: | |
3025 | if (!dp_validate_mode_timing( | |
3026 | link, | |
3027 | timing)) | |
3028 | return DC_NO_DP_LINK_BANDWIDTH; | |
3029 | break; | |
3030 | ||
3031 | default: | |
3032 | break; | |
3033 | } | |
3034 | ||
3035 | return DC_OK; | |
3036 | } | |
3037 | ||
4dc0b814 YS |
3038 | static struct abm *get_abm_from_stream_res(const struct dc_link *link) |
3039 | { | |
3040 | int i; | |
823e4bd6 | 3041 | struct dc *dc = NULL; |
4dc0b814 YS |
3042 | struct abm *abm = NULL; |
3043 | ||
823e4bd6 KW |
3044 | if (!link || !link->ctx) |
3045 | return NULL; | |
3046 | ||
3047 | dc = link->ctx->dc; | |
3048 | ||
4dc0b814 YS |
3049 | for (i = 0; i < MAX_PIPES; i++) { |
3050 | struct pipe_ctx pipe_ctx = dc->current_state->res_ctx.pipe_ctx[i]; | |
3051 | struct dc_stream_state *stream = pipe_ctx.stream; | |
3052 | ||
3053 | if (stream && stream->link == link) { | |
3054 | abm = pipe_ctx.stream_res.abm; | |
3055 | break; | |
3056 | } | |
3057 | } | |
3058 | return abm; | |
3059 | } | |
3060 | ||
620a0d27 DF |
3061 | int dc_link_get_backlight_level(const struct dc_link *link) |
3062 | { | |
4dc0b814 | 3063 | struct abm *abm = get_abm_from_stream_res(link); |
9e0d55ae HW |
3064 | struct panel_cntl *panel_cntl = link->panel_cntl; |
3065 | struct dc *dc = link->ctx->dc; | |
3066 | struct dmcu *dmcu = dc->res_pool->dmcu; | |
3067 | bool fw_set_brightness = true; | |
3068 | ||
3069 | if (dmcu) | |
3070 | fw_set_brightness = dmcu->funcs->is_dmcu_initialized(dmcu); | |
3071 | ||
3072 | if (!fw_set_brightness && panel_cntl->funcs->get_current_backlight) | |
3073 | return panel_cntl->funcs->get_current_backlight(panel_cntl); | |
3074 | else if (abm != NULL && abm->funcs->get_current_backlight != NULL) | |
3075 | return (int) abm->funcs->get_current_backlight(abm); | |
3076 | else | |
620a0d27 | 3077 | return DC_ERROR_UNEXPECTED; |
620a0d27 | 3078 | } |
4562236b | 3079 | |
fefe92fe AK |
3080 | int dc_link_get_target_backlight_pwm(const struct dc_link *link) |
3081 | { | |
4dc0b814 | 3082 | struct abm *abm = get_abm_from_stream_res(link); |
fefe92fe AK |
3083 | |
3084 | if (abm == NULL || abm->funcs->get_target_backlight == NULL) | |
3085 | return DC_ERROR_UNEXPECTED; | |
3086 | ||
3087 | return (int) abm->funcs->get_target_backlight(abm); | |
3088 | } | |
3089 | ||
3ba01817 YS |
3090 | static struct pipe_ctx *get_pipe_from_link(const struct dc_link *link) |
3091 | { | |
3092 | int i; | |
3093 | struct dc *dc = link->ctx->dc; | |
3094 | struct pipe_ctx *pipe_ctx = NULL; | |
3095 | ||
3096 | for (i = 0; i < MAX_PIPES; i++) { | |
3097 | if (dc->current_state->res_ctx.pipe_ctx[i].stream) { | |
3098 | if (dc->current_state->res_ctx.pipe_ctx[i].stream->link == link) { | |
3099 | pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i]; | |
3100 | break; | |
3101 | } | |
3102 | } | |
3103 | } | |
3104 | ||
3105 | return pipe_ctx; | |
3106 | } | |
3107 | ||
262485a5 AK |
3108 | bool dc_link_set_backlight_level(const struct dc_link *link, |
3109 | uint32_t backlight_pwm_u16_16, | |
923fe495 | 3110 | uint32_t frame_ramp) |
4562236b | 3111 | { |
2b77dcc5 | 3112 | struct dc *dc = link->ctx->dc; |
23bfb331 | 3113 | |
4b0e95d1 | 3114 | DC_LOGGER_INIT(link->ctx->logger); |
262485a5 AK |
3115 | DC_LOG_BACKLIGHT("New Backlight level: %d (0x%X)\n", |
3116 | backlight_pwm_u16_16, backlight_pwm_u16_16); | |
4562236b | 3117 | |
d0778ebf | 3118 | if (dc_is_embedded_signal(link->connector_signal)) { |
3ba01817 | 3119 | struct pipe_ctx *pipe_ctx = get_pipe_from_link(link); |
4b0e95d1 | 3120 | |
3ba01817 YS |
3121 | if (pipe_ctx) { |
3122 | /* Disable brightness ramping when the display is blanked | |
3123 | * as it can hang the DMCU | |
3124 | */ | |
3125 | if (pipe_ctx->plane_state == NULL) | |
3126 | frame_ramp = 0; | |
3127 | } else { | |
3ba01817 YS |
3128 | return false; |
3129 | } | |
4b0e95d1 YS |
3130 | |
3131 | dc->hwss.set_backlight_level( | |
3132 | pipe_ctx, | |
262485a5 | 3133 | backlight_pwm_u16_16, |
4b0e95d1 | 3134 | frame_ramp); |
4562236b | 3135 | } |
4562236b HW |
3136 | return true; |
3137 | } | |
3138 | ||
e5dfcd27 RC |
3139 | bool dc_link_set_psr_allow_active(struct dc_link *link, const bool *allow_active, |
3140 | bool wait, bool force_static, const unsigned int *power_opts) | |
4562236b | 3141 | { |
2b77dcc5 AK |
3142 | struct dc *dc = link->ctx->dc; |
3143 | struct dmcu *dmcu = dc->res_pool->dmcu; | |
4c1a1335 | 3144 | struct dmub_psr *psr = dc->res_pool->psr; |
f56c837a | 3145 | unsigned int panel_inst; |
3548f073 | 3146 | |
1d496907 KK |
3147 | if (psr == NULL && force_static) |
3148 | return false; | |
3149 | ||
f56c837a ML |
3150 | if (!dc_get_edp_link_panel_inst(dc, link, &panel_inst)) |
3151 | return false; | |
3152 | ||
7aeb2e47 | 3153 | if ((allow_active != NULL) && (*allow_active == true) && (link->type == dc_connection_none)) { |
1b5c30bf ME |
3154 | // Don't enter PSR if panel is not connected |
3155 | return false; | |
3156 | } | |
3157 | ||
e5dfcd27 RC |
3158 | /* Set power optimization flag */ |
3159 | if (power_opts && link->psr_settings.psr_power_opt != *power_opts) { | |
3160 | link->psr_settings.psr_power_opt = *power_opts; | |
3161 | ||
3162 | if (psr != NULL && link->psr_settings.psr_feature_enabled && psr->funcs->psr_set_power_opt) | |
f0d0c391 | 3163 | psr->funcs->psr_set_power_opt(psr, link->psr_settings.psr_power_opt, panel_inst); |
e5dfcd27 RC |
3164 | } |
3165 | ||
aa4be416 EK |
3166 | if (psr != NULL && link->psr_settings.psr_feature_enabled && |
3167 | force_static && psr->funcs->psr_force_static) | |
3168 | psr->funcs->psr_force_static(psr, panel_inst); | |
3169 | ||
e5dfcd27 RC |
3170 | /* Enable or Disable PSR */ |
3171 | if (allow_active && link->psr_settings.psr_allow_active != *allow_active) { | |
3172 | link->psr_settings.psr_allow_active = *allow_active; | |
3173 | ||
e5dfcd27 RC |
3174 | if (!link->psr_settings.psr_allow_active) |
3175 | dc_z10_restore(dc); | |
4074bc3f | 3176 | |
e5dfcd27 | 3177 | if (psr != NULL && link->psr_settings.psr_feature_enabled) { |
e5dfcd27 RC |
3178 | psr->funcs->psr_enable(psr, link->psr_settings.psr_allow_active, wait, panel_inst); |
3179 | } else if ((dmcu != NULL && dmcu->funcs->is_dmcu_initialized(dmcu)) && | |
3180 | link->psr_settings.psr_feature_enabled) | |
3181 | dmcu->funcs->set_psr_enable(dmcu, link->psr_settings.psr_allow_active, wait); | |
3182 | else | |
3183 | return false; | |
3184 | } | |
4562236b | 3185 | |
4562236b HW |
3186 | return true; |
3187 | } | |
3188 | ||
1d496907 | 3189 | bool dc_link_get_psr_state(const struct dc_link *link, enum dc_psr_state *state) |
e0d08a40 | 3190 | { |
2b77dcc5 AK |
3191 | struct dc *dc = link->ctx->dc; |
3192 | struct dmcu *dmcu = dc->res_pool->dmcu; | |
4c1a1335 | 3193 | struct dmub_psr *psr = dc->res_pool->psr; |
f56c837a ML |
3194 | unsigned int panel_inst; |
3195 | ||
3196 | if (!dc_get_edp_link_panel_inst(dc, link, &panel_inst)) | |
3197 | return false; | |
e0d08a40 | 3198 | |
d1ebfdd8 | 3199 | if (psr != NULL && link->psr_settings.psr_feature_enabled) |
f56c837a | 3200 | psr->funcs->psr_get_state(psr, state, panel_inst); |
d1ebfdd8 | 3201 | else if (dmcu != NULL && link->psr_settings.psr_feature_enabled) |
1d496907 | 3202 | dmcu->funcs->get_psr_state(dmcu, state); |
e0d08a40 RL |
3203 | |
3204 | return true; | |
3205 | } | |
3206 | ||
5ab5e4e6 NC |
3207 | static inline enum physical_phy_id |
3208 | transmitter_to_phy_id(enum transmitter transmitter_value) | |
3209 | { | |
3210 | switch (transmitter_value) { | |
3211 | case TRANSMITTER_UNIPHY_A: | |
3212 | return PHYLD_0; | |
3213 | case TRANSMITTER_UNIPHY_B: | |
3214 | return PHYLD_1; | |
3215 | case TRANSMITTER_UNIPHY_C: | |
3216 | return PHYLD_2; | |
3217 | case TRANSMITTER_UNIPHY_D: | |
3218 | return PHYLD_3; | |
3219 | case TRANSMITTER_UNIPHY_E: | |
3220 | return PHYLD_4; | |
3221 | case TRANSMITTER_UNIPHY_F: | |
3222 | return PHYLD_5; | |
3223 | case TRANSMITTER_NUTMEG_CRT: | |
3224 | return PHYLD_6; | |
3225 | case TRANSMITTER_TRAVIS_CRT: | |
3226 | return PHYLD_7; | |
3227 | case TRANSMITTER_TRAVIS_LCD: | |
3228 | return PHYLD_8; | |
3229 | case TRANSMITTER_UNIPHY_G: | |
3230 | return PHYLD_9; | |
3231 | case TRANSMITTER_COUNT: | |
3232 | return PHYLD_COUNT; | |
3233 | case TRANSMITTER_UNKNOWN: | |
3234 | return PHYLD_UNKNOWN; | |
3235 | default: | |
3236 | WARN_ONCE(1, "Unknown transmitter value %d\n", | |
3237 | transmitter_value); | |
3238 | return PHYLD_UNKNOWN; | |
3239 | } | |
3240 | } | |
3241 | ||
e0d08a40 RL |
3242 | bool dc_link_setup_psr(struct dc_link *link, |
3243 | const struct dc_stream_state *stream, struct psr_config *psr_config, | |
3244 | struct psr_context *psr_context) | |
3245 | { | |
2b77dcc5 | 3246 | struct dc *dc; |
e0d08a40 | 3247 | struct dmcu *dmcu; |
4c1a1335 | 3248 | struct dmub_psr *psr; |
e0d08a40 | 3249 | int i; |
f56c837a | 3250 | unsigned int panel_inst; |
e0d08a40 RL |
3251 | /* updateSinkPsrDpcdConfig*/ |
3252 | union dpcd_psr_configuration psr_configuration; | |
6651875a | 3253 | union dpcd_sink_active_vtotal_control_mode vtotal_control = {0}; |
e0d08a40 RL |
3254 | |
3255 | psr_context->controllerId = CONTROLLER_ID_UNDEFINED; | |
3256 | ||
3257 | if (!link) | |
3258 | return false; | |
3259 | ||
2b77dcc5 AK |
3260 | dc = link->ctx->dc; |
3261 | dmcu = dc->res_pool->dmcu; | |
4c1a1335 | 3262 | psr = dc->res_pool->psr; |
e0d08a40 | 3263 | |
4c1a1335 | 3264 | if (!dmcu && !psr) |
e0d08a40 RL |
3265 | return false; |
3266 | ||
f56c837a ML |
3267 | if (!dc_get_edp_link_panel_inst(dc, link, &panel_inst)) |
3268 | return false; | |
3269 | ||
e0d08a40 RL |
3270 | |
3271 | memset(&psr_configuration, 0, sizeof(psr_configuration)); | |
3272 | ||
3273 | psr_configuration.bits.ENABLE = 1; | |
3274 | psr_configuration.bits.CRC_VERIFICATION = 1; | |
3275 | psr_configuration.bits.FRAME_CAPTURE_INDICATION = | |
3276 | psr_config->psr_frame_capture_indication_req; | |
3277 | ||
3278 | /* Check for PSR v2*/ | |
6d1044a0 | 3279 | if (link->psr_settings.psr_version == DC_PSR_VERSION_SU_1) { |
e0d08a40 RL |
3280 | /* For PSR v2 selective update. |
3281 | * Indicates whether sink should start capturing | |
3282 | * immediately following active scan line, | |
3283 | * or starting with the 2nd active scan line. | |
3284 | */ | |
3285 | psr_configuration.bits.LINE_CAPTURE_INDICATION = 0; | |
3286 | /*For PSR v2, determines whether Sink should generate | |
3287 | * IRQ_HPD when CRC mismatch is detected. | |
3288 | */ | |
3289 | psr_configuration.bits.IRQ_HPD_WITH_CRC_ERROR = 1; | |
6d1044a0 DZ |
3290 | /* For PSR v2, set the bit when the Source device will |
3291 | * be enabling PSR2 operation. | |
3292 | */ | |
3293 | psr_configuration.bits.ENABLE_PSR2 = 1; | |
3294 | /* For PSR v2, the Sink device must be able to receive | |
3295 | * SU region updates early in the frame time. | |
3296 | */ | |
3297 | psr_configuration.bits.EARLY_TRANSPORT_ENABLE = 1; | |
e0d08a40 RL |
3298 | } |
3299 | ||
3300 | dm_helpers_dp_write_dpcd( | |
3301 | link->ctx, | |
3302 | link, | |
3303 | 368, | |
3304 | &psr_configuration.raw, | |
3305 | sizeof(psr_configuration.raw)); | |
3306 | ||
6d1044a0 | 3307 | if (link->psr_settings.psr_version == DC_PSR_VERSION_SU_1) { |
90bb21cb | 3308 | dc_power_alpm_dpcd_enable(link, true); |
a35806b3 DZ |
3309 | psr_context->su_granularity_required = |
3310 | psr_config->su_granularity_required; | |
3311 | psr_context->su_y_granularity = | |
3312 | psr_config->su_y_granularity; | |
32c453f1 DZ |
3313 | psr_context->line_time_in_us = |
3314 | psr_config->line_time_in_us; | |
6651875a DZ |
3315 | |
3316 | if (link->psr_settings.psr_vtotal_control_support) { | |
3317 | psr_context->rate_control_caps = psr_config->rate_control_caps; | |
3318 | vtotal_control.bits.ENABLE = true; | |
3319 | core_link_write_dpcd(link, DP_SINK_PSR_ACTIVE_VTOTAL_CONTROL_MODE, | |
3320 | &vtotal_control.raw, sizeof(vtotal_control.raw)); | |
3321 | } | |
6d1044a0 DZ |
3322 | } |
3323 | ||
e0d08a40 RL |
3324 | psr_context->channel = link->ddc->ddc_pin->hw_info.ddc_channel; |
3325 | psr_context->transmitterId = link->link_enc->transmitter; | |
3326 | psr_context->engineId = link->link_enc->preferred_engine; | |
3327 | ||
3328 | for (i = 0; i < MAX_PIPES; i++) { | |
2b77dcc5 | 3329 | if (dc->current_state->res_ctx.pipe_ctx[i].stream |
e0d08a40 RL |
3330 | == stream) { |
3331 | /* dmcu -1 for all controller id values, | |
3332 | * therefore +1 here | |
3333 | */ | |
3334 | psr_context->controllerId = | |
2b77dcc5 | 3335 | dc->current_state->res_ctx. |
e0d08a40 RL |
3336 | pipe_ctx[i].stream_res.tg->inst + 1; |
3337 | break; | |
3338 | } | |
3339 | } | |
3340 | ||
3341 | /* Hardcoded for now. Can be Pcie or Uniphy (or Unknown)*/ | |
3342 | psr_context->phyType = PHY_TYPE_UNIPHY; | |
3343 | /*PhyId is associated with the transmitter id*/ | |
5ab5e4e6 NC |
3344 | psr_context->smuPhyId = |
3345 | transmitter_to_phy_id(link->link_enc->transmitter); | |
e0d08a40 RL |
3346 | |
3347 | psr_context->crtcTimingVerticalTotal = stream->timing.v_total; | |
5b5abe95 | 3348 | psr_context->vsync_rate_hz = div64_u64(div64_u64((stream-> |
e0d08a40 RL |
3349 | timing.pix_clk_100hz * 100), |
3350 | stream->timing.v_total), | |
3351 | stream->timing.h_total); | |
3352 | ||
3353 | psr_context->psrSupportedDisplayConfig = true; | |
3354 | psr_context->psrExitLinkTrainingRequired = | |
3355 | psr_config->psr_exit_link_training_required; | |
3356 | psr_context->sdpTransmitLineNumDeadline = | |
3357 | psr_config->psr_sdp_transmit_line_num_deadline; | |
3358 | psr_context->psrFrameCaptureIndicationReq = | |
3359 | psr_config->psr_frame_capture_indication_req; | |
3360 | ||
3361 | psr_context->skipPsrWaitForPllLock = 0; /* only = 1 in KV */ | |
3362 | ||
3363 | psr_context->numberOfControllers = | |
3364 | link->dc->res_pool->timing_generator_count; | |
3365 | ||
3366 | psr_context->rfb_update_auto_en = true; | |
3367 | ||
3368 | /* 2 frames before enter PSR. */ | |
3369 | psr_context->timehyst_frames = 2; | |
3370 | /* half a frame | |
3371 | * (units in 100 lines, i.e. a value of 1 represents 100 lines) | |
3372 | */ | |
3373 | psr_context->hyst_lines = stream->timing.v_total / 2 / 100; | |
3374 | psr_context->aux_repeats = 10; | |
3375 | ||
3376 | psr_context->psr_level.u32all = 0; | |
3377 | ||
e0d08a40 | 3378 | /*skip power down the single pipe since it blocks the cstate*/ |
8fe44c08 | 3379 | #if defined(CONFIG_DRM_AMD_DC_DCN) |
cbaf919f | 3380 | if (link->ctx->asic_id.chip_family >= FAMILY_RV) { |
501867d0 QZ |
3381 | switch(link->ctx->asic_id.chip_family) { |
3382 | case FAMILY_YELLOW_CARP: | |
3383 | case AMDGPU_FAMILY_GC_10_3_6: | |
08ebadfc | 3384 | case AMDGPU_FAMILY_GC_11_0_1: |
00812bfc | 3385 | if (dc->debug.disable_z10 || dc->debug.psr_skip_crtc_disable) |
8cab4ef0 | 3386 | psr_context->psr_level.bits.SKIP_CRTC_DISABLE = true; |
501867d0 QZ |
3387 | break; |
3388 | default: | |
3389 | psr_context->psr_level.bits.SKIP_CRTC_DISABLE = true; | |
3390 | break; | |
3391 | } | |
cbaf919f NK |
3392 | } |
3393 | #else | |
2ff3cf82 | 3394 | if (link->ctx->asic_id.chip_family >= FAMILY_RV) |
e0d08a40 | 3395 | psr_context->psr_level.bits.SKIP_CRTC_DISABLE = true; |
cbaf919f | 3396 | #endif |
e0d08a40 RL |
3397 | |
3398 | /* SMU will perform additional powerdown sequence. | |
3399 | * For unsupported ASICs, set psr_level flag to skip PSR | |
3400 | * static screen notification to SMU. | |
3401 | * (Always set for DAL2, did not check ASIC) | |
3402 | */ | |
3403 | psr_context->allow_smu_optimizations = psr_config->allow_smu_optimizations; | |
175f0971 | 3404 | psr_context->allow_multi_disp_optimizations = psr_config->allow_multi_disp_optimizations; |
e0d08a40 RL |
3405 | |
3406 | /* Complete PSR entry before aborting to prevent intermittent | |
3407 | * freezes on certain eDPs | |
3408 | */ | |
3409 | psr_context->psr_level.bits.DISABLE_PSR_ENTRY_ABORT = 1; | |
3410 | ||
c7eac19e DZ |
3411 | /* enable ALPM */ |
3412 | psr_context->psr_level.bits.DISABLE_ALPM = 0; | |
3413 | psr_context->psr_level.bits.ALPM_DEFAULT_PD_MODE = 1; | |
3414 | ||
e0d08a40 RL |
3415 | /* Controls additional delay after remote frame capture before |
3416 | * continuing power down, default = 0 | |
3417 | */ | |
3418 | psr_context->frame_delay = 0; | |
3419 | ||
99c04671 | 3420 | if (psr) { |
f56c837a ML |
3421 | link->psr_settings.psr_feature_enabled = psr->funcs->psr_copy_settings(psr, |
3422 | link, psr_context, panel_inst); | |
99c04671 EK |
3423 | link->psr_settings.psr_power_opt = 0; |
3424 | link->psr_settings.psr_allow_active = 0; | |
3425 | } | |
4c1a1335 | 3426 | else |
d1ebfdd8 | 3427 | link->psr_settings.psr_feature_enabled = dmcu->funcs->setup_psr(dmcu, link, psr_context); |
e0d08a40 RL |
3428 | |
3429 | /* psr_enabled == 0 indicates setup_psr did not succeed, but this | |
3430 | * should not happen since firmware should be running at this point | |
3431 | */ | |
d1ebfdd8 | 3432 | if (link->psr_settings.psr_feature_enabled == 0) |
e0d08a40 RL |
3433 | ASSERT(0); |
3434 | ||
3435 | return true; | |
3436 | ||
3437 | } | |
3438 | ||
b8e0b3d6 WW |
3439 | void dc_link_get_psr_residency(const struct dc_link *link, uint32_t *residency) |
3440 | { | |
3441 | struct dc *dc = link->ctx->dc; | |
3442 | struct dmub_psr *psr = dc->res_pool->psr; | |
74b4afad ML |
3443 | unsigned int panel_inst; |
3444 | ||
3445 | if (!dc_get_edp_link_panel_inst(dc, link, &panel_inst)) | |
3446 | return; | |
b8e0b3d6 | 3447 | |
74b4afad | 3448 | /* PSR residency measurements only supported on DMCUB */ |
b8e0b3d6 | 3449 | if (psr != NULL && link->psr_settings.psr_feature_enabled) |
74b4afad | 3450 | psr->funcs->psr_get_residency(psr, residency, panel_inst); |
b8e0b3d6 WW |
3451 | else |
3452 | *residency = 0; | |
3453 | } | |
3454 | ||
6651875a DZ |
3455 | bool dc_link_set_sink_vtotal_in_psr_active(const struct dc_link *link, uint16_t psr_vtotal_idle, uint16_t psr_vtotal_su) |
3456 | { | |
3457 | struct dc *dc = link->ctx->dc; | |
3458 | struct dmub_psr *psr = dc->res_pool->psr; | |
3459 | ||
3460 | if (psr == NULL || !link->psr_settings.psr_feature_enabled || !link->psr_settings.psr_vtotal_control_support) | |
3461 | return false; | |
3462 | ||
3463 | psr->funcs->psr_set_sink_vtotal_in_psr_active(psr, psr_vtotal_idle, psr_vtotal_su); | |
3464 | ||
3465 | return true; | |
3466 | } | |
3467 | ||
d0778ebf | 3468 | const struct dc_link_status *dc_link_get_status(const struct dc_link *link) |
4562236b | 3469 | { |
4562236b HW |
3470 | return &link->link_status; |
3471 | } | |
3472 | ||
d0778ebf | 3473 | void core_link_resume(struct dc_link *link) |
4562236b | 3474 | { |
d0778ebf | 3475 | if (link->connector_signal != SIGNAL_TYPE_VIRTUAL) |
b0c4e977 | 3476 | program_hpd_filter(link); |
4562236b HW |
3477 | } |
3478 | ||
0971c40e | 3479 | static struct fixed31_32 get_pbn_per_slot(struct dc_stream_state *stream) |
4562236b | 3480 | { |
332c1191 | 3481 | struct fixed31_32 mbytes_per_sec; |
fe798de5 CP |
3482 | uint32_t link_rate_in_mbytes_per_sec = dc_link_bandwidth_kbps(stream->link, |
3483 | &stream->link->cur_link_settings); | |
332c1191 NC |
3484 | link_rate_in_mbytes_per_sec /= 8000; /* Kbits to MBytes */ |
3485 | ||
3486 | mbytes_per_sec = dc_fixpt_from_int(link_rate_in_mbytes_per_sec); | |
3487 | ||
3488 | return dc_fixpt_div_int(mbytes_per_sec, 54); | |
4562236b HW |
3489 | } |
3490 | ||
786b4061 | 3491 | static struct fixed31_32 get_pbn_from_bw_in_kbps(uint64_t kbps) |
4562236b | 3492 | { |
4562236b | 3493 | struct fixed31_32 peak_kbps; |
8532467c ML |
3494 | uint32_t numerator = 0; |
3495 | uint32_t denominator = 1; | |
4562236b | 3496 | |
4562236b HW |
3497 | /* |
3498 | * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006 | |
3499 | * The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on | |
3500 | * common multiplier to render an integer PBN for all link rate/lane | |
3501 | * counts combinations | |
3502 | * calculate | |
3503 | * peak_kbps *= (1006/1000) | |
3504 | * peak_kbps *= (64/54) | |
3505 | * peak_kbps *= 8 convert to bytes | |
3506 | */ | |
3507 | ||
3508 | numerator = 64 * PEAK_FACTOR_X1000; | |
3509 | denominator = 54 * 8 * 1000 * 1000; | |
3510 | kbps *= numerator; | |
eb0e5154 | 3511 | peak_kbps = dc_fixpt_from_fraction(kbps, denominator); |
4562236b HW |
3512 | |
3513 | return peak_kbps; | |
3514 | } | |
3515 | ||
786b4061 WL |
3516 | static struct fixed31_32 get_pbn_from_timing(struct pipe_ctx *pipe_ctx) |
3517 | { | |
3518 | uint64_t kbps; | |
3519 | ||
3520 | kbps = dc_bandwidth_in_kbps_from_timing(&pipe_ctx->stream->timing); | |
3521 | return get_pbn_from_bw_in_kbps(kbps); | |
3522 | } | |
3523 | ||
4562236b | 3524 | static void update_mst_stream_alloc_table( |
d0778ebf | 3525 | struct dc_link *link, |
4562236b | 3526 | struct stream_encoder *stream_enc, |
d740e0bf | 3527 | struct hpo_dp_stream_encoder *hpo_dp_stream_enc, // TODO: Rename stream_enc to dio_stream_enc? |
8c5e9bbb | 3528 | const struct dc_dp_mst_stream_allocation_table *proposed_table) |
4562236b | 3529 | { |
c78abac9 | 3530 | struct link_mst_stream_allocation work_table[MAX_CONTROLLER_NUM] = { 0 }; |
4562236b HW |
3531 | struct link_mst_stream_allocation *dc_alloc; |
3532 | ||
3533 | int i; | |
3534 | int j; | |
3535 | ||
3536 | /* if DRM proposed_table has more than one new payload */ | |
3537 | ASSERT(proposed_table->stream_count - | |
3538 | link->mst_stream_alloc_table.stream_count < 2); | |
3539 | ||
d0778ebf | 3540 | /* copy proposed_table to link, add stream encoder */ |
4562236b HW |
3541 | for (i = 0; i < proposed_table->stream_count; i++) { |
3542 | ||
3543 | for (j = 0; j < link->mst_stream_alloc_table.stream_count; j++) { | |
3544 | dc_alloc = | |
3545 | &link->mst_stream_alloc_table.stream_allocations[j]; | |
3546 | ||
3547 | if (dc_alloc->vcp_id == | |
3548 | proposed_table->stream_allocations[i].vcp_id) { | |
3549 | ||
3550 | work_table[i] = *dc_alloc; | |
786b4061 | 3551 | work_table[i].slot_count = proposed_table->stream_allocations[i].slot_count; |
4562236b HW |
3552 | break; /* exit j loop */ |
3553 | } | |
3554 | } | |
3555 | ||
3556 | /* new vcp_id */ | |
3557 | if (j == link->mst_stream_alloc_table.stream_count) { | |
3558 | work_table[i].vcp_id = | |
3559 | proposed_table->stream_allocations[i].vcp_id; | |
3560 | work_table[i].slot_count = | |
3561 | proposed_table->stream_allocations[i].slot_count; | |
3562 | work_table[i].stream_enc = stream_enc; | |
d740e0bf | 3563 | work_table[i].hpo_dp_stream_enc = hpo_dp_stream_enc; |
4562236b HW |
3564 | } |
3565 | } | |
3566 | ||
3567 | /* update link->mst_stream_alloc_table with work_table */ | |
3568 | link->mst_stream_alloc_table.stream_count = | |
3569 | proposed_table->stream_count; | |
3570 | for (i = 0; i < MAX_CONTROLLER_NUM; i++) | |
3571 | link->mst_stream_alloc_table.stream_allocations[i] = | |
3572 | work_table[i]; | |
3573 | } | |
d9eb8fea | 3574 | |
cc67aae1 WL |
3575 | static void remove_stream_from_alloc_table( |
3576 | struct dc_link *link, | |
3577 | struct stream_encoder *dio_stream_enc, | |
3578 | struct hpo_dp_stream_encoder *hpo_dp_stream_enc) | |
3579 | { | |
3580 | int i = 0; | |
3581 | struct link_mst_stream_allocation_table *table = | |
3582 | &link->mst_stream_alloc_table; | |
3583 | ||
3584 | if (hpo_dp_stream_enc) { | |
3585 | for (; i < table->stream_count; i++) | |
3586 | if (hpo_dp_stream_enc == table->stream_allocations[i].hpo_dp_stream_enc) | |
3587 | break; | |
3588 | } else { | |
3589 | for (; i < table->stream_count; i++) | |
3590 | if (dio_stream_enc == table->stream_allocations[i].stream_enc) | |
3591 | break; | |
3592 | } | |
3593 | ||
3594 | if (i < table->stream_count) { | |
3595 | i++; | |
3596 | for (; i < table->stream_count; i++) | |
3597 | table->stream_allocations[i-1] = table->stream_allocations[i]; | |
3598 | memset(&table->stream_allocations[table->stream_count-1], 0, | |
3599 | sizeof(struct link_mst_stream_allocation)); | |
3600 | table->stream_count--; | |
3601 | } | |
3602 | } | |
3603 | ||
356af2f3 GS |
3604 | static void dc_log_vcp_x_y(const struct dc_link *link, struct fixed31_32 avg_time_slots_per_mtp) |
3605 | { | |
3606 | const uint32_t VCP_Y_PRECISION = 1000; | |
3607 | uint64_t vcp_x, vcp_y; | |
3608 | ||
3609 | // Add 0.5*(1/VCP_Y_PRECISION) to round up to decimal precision | |
3610 | avg_time_slots_per_mtp = dc_fixpt_add( | |
3611 | avg_time_slots_per_mtp, dc_fixpt_from_fraction(1, 2 * VCP_Y_PRECISION)); | |
3612 | ||
3613 | vcp_x = dc_fixpt_floor(avg_time_slots_per_mtp); | |
3614 | vcp_y = dc_fixpt_floor( | |
3615 | dc_fixpt_mul_int( | |
3616 | dc_fixpt_sub_int(avg_time_slots_per_mtp, dc_fixpt_floor(avg_time_slots_per_mtp)), | |
3617 | VCP_Y_PRECISION)); | |
3618 | ||
3619 | if (link->type == dc_connection_mst_branch) | |
3620 | DC_LOG_DP2("MST Update Payload: set_throttled_vcp_size slot X.Y for MST stream " | |
3621 | "X: %lld Y: %lld/%d", vcp_x, vcp_y, VCP_Y_PRECISION); | |
3622 | else | |
3623 | DC_LOG_DP2("SST Update Payload: set_throttled_vcp_size slot X.Y for SST stream " | |
3624 | "X: %lld Y: %lld/%d", vcp_x, vcp_y, VCP_Y_PRECISION); | |
3625 | } | |
3626 | ||
f01ee019 FZ |
3627 | /* |
3628 | * Payload allocation/deallocation for SST introduced in DP2.0 | |
3629 | */ | |
240e6d25 IB |
3630 | static enum dc_status dc_link_update_sst_payload(struct pipe_ctx *pipe_ctx, |
3631 | bool allocate) | |
f01ee019 FZ |
3632 | { |
3633 | struct dc_stream_state *stream = pipe_ctx->stream; | |
3634 | struct dc_link *link = stream->link; | |
f01ee019 FZ |
3635 | struct link_mst_stream_allocation_table proposed_table = {0}; |
3636 | struct fixed31_32 avg_time_slots_per_mtp; | |
5279e091 | 3637 | const struct dc_link_settings empty_link_settings = {0}; |
9d8033d6 | 3638 | const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); |
f01ee019 FZ |
3639 | DC_LOGGER_INIT(link->ctx->logger); |
3640 | ||
3641 | /* slot X.Y for SST payload deallocate */ | |
3642 | if (!allocate) { | |
3643 | avg_time_slots_per_mtp = dc_fixpt_from_int(0); | |
3644 | ||
356af2f3 | 3645 | dc_log_vcp_x_y(link, avg_time_slots_per_mtp); |
f01ee019 | 3646 | |
9d8033d6 WL |
3647 | if (link_hwss->ext.set_throttled_vcp_size) |
3648 | link_hwss->ext.set_throttled_vcp_size(pipe_ctx, | |
3649 | avg_time_slots_per_mtp); | |
3650 | if (link_hwss->ext.set_hblank_min_symbol_width) | |
3651 | link_hwss->ext.set_hblank_min_symbol_width(pipe_ctx, | |
5279e091 WL |
3652 | &empty_link_settings, |
3653 | avg_time_slots_per_mtp); | |
f01ee019 FZ |
3654 | } |
3655 | ||
3656 | /* calculate VC payload and update branch with new payload allocation table*/ | |
3657 | if (!dpcd_write_128b_132b_sst_payload_allocation_table( | |
3658 | stream, | |
3659 | link, | |
3660 | &proposed_table, | |
3661 | allocate)) { | |
3662 | DC_LOG_ERROR("SST Update Payload: Failed to update " | |
3663 | "allocation table for " | |
3664 | "pipe idx: %d\n", | |
3665 | pipe_ctx->pipe_idx); | |
e5309d7f | 3666 | return DC_FAIL_DP_PAYLOAD_ALLOCATION; |
f01ee019 FZ |
3667 | } |
3668 | ||
a5b79943 | 3669 | proposed_table.stream_allocations[0].hpo_dp_stream_enc = pipe_ctx->stream_res.hpo_dp_stream_enc; |
f01ee019 FZ |
3670 | |
3671 | ASSERT(proposed_table.stream_count == 1); | |
3672 | ||
3673 | //TODO - DP2.0 Logging: Instead of hpo_dp_stream_enc pointer, log instance id | |
3674 | DC_LOG_DP2("SST Update Payload: hpo_dp_stream_enc: %p " | |
3675 | "vcp_id: %d " | |
3676 | "slot_count: %d\n", | |
3677 | (void *) proposed_table.stream_allocations[0].hpo_dp_stream_enc, | |
3678 | proposed_table.stream_allocations[0].vcp_id, | |
3679 | proposed_table.stream_allocations[0].slot_count); | |
3680 | ||
3681 | /* program DP source TX for payload */ | |
a5b79943 | 3682 | link_hwss->ext.update_stream_allocation_table(link, &pipe_ctx->link_res, |
f01ee019 FZ |
3683 | &proposed_table); |
3684 | ||
3685 | /* poll for ACT handled */ | |
3686 | if (!dpcd_poll_for_allocation_change_trigger(link)) { | |
3687 | // Failures will result in blackscreen and errors logged | |
3688 | BREAK_TO_DEBUGGER(); | |
3689 | } | |
3690 | ||
3691 | /* slot X.Y for SST payload allocate */ | |
d5a43956 | 3692 | if (allocate && link_dp_get_encoding_format(&link->cur_link_settings) == |
c371b0d1 | 3693 | DP_128b_132b_ENCODING) { |
f01ee019 FZ |
3694 | avg_time_slots_per_mtp = calculate_sst_avg_time_slots_per_mtp(stream, link); |
3695 | ||
356af2f3 | 3696 | dc_log_vcp_x_y(link, avg_time_slots_per_mtp); |
f01ee019 | 3697 | |
9d8033d6 WL |
3698 | if (link_hwss->ext.set_throttled_vcp_size) |
3699 | link_hwss->ext.set_throttled_vcp_size(pipe_ctx, | |
3700 | avg_time_slots_per_mtp); | |
3701 | if (link_hwss->ext.set_hblank_min_symbol_width) | |
3702 | link_hwss->ext.set_hblank_min_symbol_width(pipe_ctx, | |
5279e091 WL |
3703 | &link->cur_link_settings, |
3704 | avg_time_slots_per_mtp); | |
f01ee019 FZ |
3705 | } |
3706 | ||
3707 | /* Always return DC_OK. | |
3708 | * If part of sequence fails, log failure(s) and show blackscreen | |
3709 | */ | |
3710 | return DC_OK; | |
3711 | } | |
4562236b HW |
3712 | |
3713 | /* convert link_mst_stream_alloc_table to dm dp_mst_stream_alloc_table | |
3714 | * because stream_encoder is not exposed to dm | |
3715 | */ | |
48af9b91 | 3716 | enum dc_status dc_link_allocate_mst_payload(struct pipe_ctx *pipe_ctx) |
4562236b | 3717 | { |
0971c40e | 3718 | struct dc_stream_state *stream = pipe_ctx->stream; |
ceb3dbb4 | 3719 | struct dc_link *link = stream->link; |
8c5e9bbb | 3720 | struct dc_dp_mst_stream_allocation_table proposed_table = {0}; |
4562236b HW |
3721 | struct fixed31_32 avg_time_slots_per_mtp; |
3722 | struct fixed31_32 pbn; | |
3723 | struct fixed31_32 pbn_per_slot; | |
2fcb2697 | 3724 | int i; |
48af9b91 | 3725 | enum act_return_status ret; |
9d8033d6 | 3726 | const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); |
5d4b05dd | 3727 | DC_LOGGER_INIT(link->ctx->logger); |
4562236b HW |
3728 | |
3729 | /* enable_link_dp_mst already check link->enabled_stream_count | |
3730 | * and stream is in link->stream[]. This is called during set mode, | |
3731 | * stream_enc is available. | |
3732 | */ | |
3733 | ||
3734 | /* get calculate VC payload for stream: stream_alloc */ | |
3735 | if (dm_helpers_dp_mst_write_payload_allocation_table( | |
3736 | stream->ctx, | |
4fa086b9 | 3737 | stream, |
4562236b | 3738 | &proposed_table, |
d9eb8fea | 3739 | true)) |
4562236b | 3740 | update_mst_stream_alloc_table( |
d740e0bf FZ |
3741 | link, |
3742 | pipe_ctx->stream_res.stream_enc, | |
3743 | pipe_ctx->stream_res.hpo_dp_stream_enc, | |
3744 | &proposed_table); | |
4562236b | 3745 | else |
1296423b | 3746 | DC_LOG_WARNING("Failed to update" |
4562236b HW |
3747 | "MST allocation table for" |
3748 | "pipe idx:%d\n", | |
3749 | pipe_ctx->pipe_idx); | |
3750 | ||
1296423b | 3751 | DC_LOG_MST("%s " |
4562236b HW |
3752 | "stream_count: %d: \n ", |
3753 | __func__, | |
3754 | link->mst_stream_alloc_table.stream_count); | |
3755 | ||
3756 | for (i = 0; i < MAX_CONTROLLER_NUM; i++) { | |
3032deb5 | 3757 | DC_LOG_MST("stream_enc[%d]: %p " |
d740e0bf | 3758 | "stream[%d].hpo_dp_stream_enc: %p " |
4562236b HW |
3759 | "stream[%d].vcp_id: %d " |
3760 | "stream[%d].slot_count: %d\n", | |
3761 | i, | |
3032deb5 | 3762 | (void *) link->mst_stream_alloc_table.stream_allocations[i].stream_enc, |
4562236b | 3763 | i, |
d740e0bf FZ |
3764 | (void *) link->mst_stream_alloc_table.stream_allocations[i].hpo_dp_stream_enc, |
3765 | i, | |
4562236b HW |
3766 | link->mst_stream_alloc_table.stream_allocations[i].vcp_id, |
3767 | i, | |
3768 | link->mst_stream_alloc_table.stream_allocations[i].slot_count); | |
3769 | } | |
3770 | ||
3771 | ASSERT(proposed_table.stream_count > 0); | |
3772 | ||
3773 | /* program DP source TX for payload */ | |
a5b79943 | 3774 | if (link_hwss->ext.update_stream_allocation_table == NULL || |
d5a43956 | 3775 | link_dp_get_encoding_format(&link->cur_link_settings) == DP_UNKNOWN_ENCODING) { |
d740e0bf FZ |
3776 | DC_LOG_ERROR("Failure: unknown encoding format\n"); |
3777 | return DC_ERROR_UNEXPECTED; | |
3778 | } | |
4562236b | 3779 | |
a5b79943 WL |
3780 | link_hwss->ext.update_stream_allocation_table(link, |
3781 | &pipe_ctx->link_res, | |
3782 | &link->mst_stream_alloc_table); | |
3783 | ||
4562236b | 3784 | /* send down message */ |
48af9b91 | 3785 | ret = dm_helpers_dp_mst_poll_for_allocation_change_trigger( |
4562236b | 3786 | stream->ctx, |
4fa086b9 | 3787 | stream); |
4562236b | 3788 | |
48af9b91 AL |
3789 | if (ret != ACT_LINK_LOST) { |
3790 | dm_helpers_dp_mst_send_payload_allocation( | |
3791 | stream->ctx, | |
3792 | stream, | |
3793 | true); | |
3794 | } | |
4562236b HW |
3795 | |
3796 | /* slot X.Y for only current stream */ | |
3797 | pbn_per_slot = get_pbn_per_slot(stream); | |
7dd4f4df AT |
3798 | if (pbn_per_slot.value == 0) { |
3799 | DC_LOG_ERROR("Failure: pbn_per_slot==0 not allowed. Cannot continue, returning DC_UNSUPPORTED_VALUE.\n"); | |
3800 | return DC_UNSUPPORTED_VALUE; | |
3801 | } | |
4562236b | 3802 | pbn = get_pbn_from_timing(pipe_ctx); |
eb0e5154 | 3803 | avg_time_slots_per_mtp = dc_fixpt_div(pbn, pbn_per_slot); |
4562236b | 3804 | |
5279e091 WL |
3805 | dc_log_vcp_x_y(link, avg_time_slots_per_mtp); |
3806 | ||
9d8033d6 WL |
3807 | if (link_hwss->ext.set_throttled_vcp_size) |
3808 | link_hwss->ext.set_throttled_vcp_size(pipe_ctx, avg_time_slots_per_mtp); | |
3809 | if (link_hwss->ext.set_hblank_min_symbol_width) | |
3810 | link_hwss->ext.set_hblank_min_symbol_width(pipe_ctx, | |
5279e091 | 3811 | &link->cur_link_settings, |
d740e0bf | 3812 | avg_time_slots_per_mtp); |
d740e0bf FZ |
3813 | |
3814 | return DC_OK; | |
3815 | ||
3816 | } | |
3817 | ||
d740e0bf FZ |
3818 | enum dc_status dc_link_reduce_mst_payload(struct pipe_ctx *pipe_ctx, uint32_t bw_in_kbps) |
3819 | { | |
3820 | struct dc_stream_state *stream = pipe_ctx->stream; | |
3821 | struct dc_link *link = stream->link; | |
3822 | struct fixed31_32 avg_time_slots_per_mtp; | |
3823 | struct fixed31_32 pbn; | |
3824 | struct fixed31_32 pbn_per_slot; | |
8c5e9bbb | 3825 | struct dc_dp_mst_stream_allocation_table proposed_table = {0}; |
d740e0bf | 3826 | uint8_t i; |
9d8033d6 | 3827 | const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); |
d740e0bf FZ |
3828 | DC_LOGGER_INIT(link->ctx->logger); |
3829 | ||
3830 | /* decrease throttled vcp size */ | |
3831 | pbn_per_slot = get_pbn_per_slot(stream); | |
3832 | pbn = get_pbn_from_bw_in_kbps(bw_in_kbps); | |
3833 | avg_time_slots_per_mtp = dc_fixpt_div(pbn, pbn_per_slot); | |
3834 | ||
9d8033d6 WL |
3835 | if (link_hwss->ext.set_throttled_vcp_size) |
3836 | link_hwss->ext.set_throttled_vcp_size(pipe_ctx, avg_time_slots_per_mtp); | |
3837 | if (link_hwss->ext.set_hblank_min_symbol_width) | |
3838 | link_hwss->ext.set_hblank_min_symbol_width(pipe_ctx, | |
5279e091 | 3839 | &link->cur_link_settings, |
d740e0bf FZ |
3840 | avg_time_slots_per_mtp); |
3841 | ||
3842 | /* send ALLOCATE_PAYLOAD sideband message with updated pbn */ | |
3843 | dm_helpers_dp_mst_send_payload_allocation( | |
3844 | stream->ctx, | |
3845 | stream, | |
3846 | true); | |
3847 | ||
3848 | /* notify immediate branch device table update */ | |
3849 | if (dm_helpers_dp_mst_write_payload_allocation_table( | |
3850 | stream->ctx, | |
3851 | stream, | |
3852 | &proposed_table, | |
3853 | true)) { | |
3854 | /* update mst stream allocation table software state */ | |
3855 | update_mst_stream_alloc_table( | |
3856 | link, | |
3857 | pipe_ctx->stream_res.stream_enc, | |
3858 | pipe_ctx->stream_res.hpo_dp_stream_enc, | |
3859 | &proposed_table); | |
3860 | } else { | |
3861 | DC_LOG_WARNING("Failed to update" | |
3862 | "MST allocation table for" | |
3863 | "pipe idx:%d\n", | |
3864 | pipe_ctx->pipe_idx); | |
3865 | } | |
3866 | ||
3867 | DC_LOG_MST("%s " | |
3868 | "stream_count: %d: \n ", | |
3869 | __func__, | |
3870 | link->mst_stream_alloc_table.stream_count); | |
3871 | ||
3872 | for (i = 0; i < MAX_CONTROLLER_NUM; i++) { | |
3873 | DC_LOG_MST("stream_enc[%d]: %p " | |
f18368b6 | 3874 | "stream[%d].hpo_dp_stream_enc: %p " |
d740e0bf FZ |
3875 | "stream[%d].vcp_id: %d " |
3876 | "stream[%d].slot_count: %d\n", | |
3877 | i, | |
3878 | (void *) link->mst_stream_alloc_table.stream_allocations[i].stream_enc, | |
3879 | i, | |
f18368b6 WL |
3880 | (void *) link->mst_stream_alloc_table.stream_allocations[i].hpo_dp_stream_enc, |
3881 | i, | |
d740e0bf FZ |
3882 | link->mst_stream_alloc_table.stream_allocations[i].vcp_id, |
3883 | i, | |
3884 | link->mst_stream_alloc_table.stream_allocations[i].slot_count); | |
3885 | } | |
3886 | ||
3887 | ASSERT(proposed_table.stream_count > 0); | |
3888 | ||
3889 | /* update mst stream allocation table hardware state */ | |
a5b79943 | 3890 | if (link_hwss->ext.update_stream_allocation_table == NULL || |
d5a43956 | 3891 | link_dp_get_encoding_format(&link->cur_link_settings) == DP_UNKNOWN_ENCODING) { |
a5b79943 WL |
3892 | DC_LOG_ERROR("Failure: unknown encoding format\n"); |
3893 | return DC_ERROR_UNEXPECTED; | |
3894 | } | |
3895 | ||
3896 | link_hwss->ext.update_stream_allocation_table(link, &pipe_ctx->link_res, | |
d740e0bf FZ |
3897 | &link->mst_stream_alloc_table); |
3898 | ||
3899 | /* poll for immediate branch device ACT handled */ | |
0b58162e | 3900 | dm_helpers_dp_mst_poll_for_allocation_change_trigger( |
d740e0bf FZ |
3901 | stream->ctx, |
3902 | stream); | |
4562236b HW |
3903 | |
3904 | return DC_OK; | |
d740e0bf FZ |
3905 | } |
3906 | ||
3907 | enum dc_status dc_link_increase_mst_payload(struct pipe_ctx *pipe_ctx, uint32_t bw_in_kbps) | |
3908 | { | |
3909 | struct dc_stream_state *stream = pipe_ctx->stream; | |
3910 | struct dc_link *link = stream->link; | |
3911 | struct fixed31_32 avg_time_slots_per_mtp; | |
3912 | struct fixed31_32 pbn; | |
3913 | struct fixed31_32 pbn_per_slot; | |
8c5e9bbb | 3914 | struct dc_dp_mst_stream_allocation_table proposed_table = {0}; |
d740e0bf FZ |
3915 | uint8_t i; |
3916 | enum act_return_status ret; | |
9d8033d6 | 3917 | const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); |
d740e0bf FZ |
3918 | DC_LOGGER_INIT(link->ctx->logger); |
3919 | ||
3920 | /* notify immediate branch device table update */ | |
3921 | if (dm_helpers_dp_mst_write_payload_allocation_table( | |
3922 | stream->ctx, | |
3923 | stream, | |
3924 | &proposed_table, | |
3925 | true)) { | |
3926 | /* update mst stream allocation table software state */ | |
3927 | update_mst_stream_alloc_table( | |
3928 | link, | |
3929 | pipe_ctx->stream_res.stream_enc, | |
3930 | pipe_ctx->stream_res.hpo_dp_stream_enc, | |
3931 | &proposed_table); | |
3932 | } | |
3933 | ||
3934 | DC_LOG_MST("%s " | |
3935 | "stream_count: %d: \n ", | |
3936 | __func__, | |
3937 | link->mst_stream_alloc_table.stream_count); | |
3938 | ||
3939 | for (i = 0; i < MAX_CONTROLLER_NUM; i++) { | |
3940 | DC_LOG_MST("stream_enc[%d]: %p " | |
f18368b6 | 3941 | "stream[%d].hpo_dp_stream_enc: %p " |
d740e0bf FZ |
3942 | "stream[%d].vcp_id: %d " |
3943 | "stream[%d].slot_count: %d\n", | |
3944 | i, | |
3945 | (void *) link->mst_stream_alloc_table.stream_allocations[i].stream_enc, | |
3946 | i, | |
f18368b6 WL |
3947 | (void *) link->mst_stream_alloc_table.stream_allocations[i].hpo_dp_stream_enc, |
3948 | i, | |
d740e0bf FZ |
3949 | link->mst_stream_alloc_table.stream_allocations[i].vcp_id, |
3950 | i, | |
3951 | link->mst_stream_alloc_table.stream_allocations[i].slot_count); | |
3952 | } | |
3953 | ||
3954 | ASSERT(proposed_table.stream_count > 0); | |
4562236b | 3955 | |
d740e0bf | 3956 | /* update mst stream allocation table hardware state */ |
990cad0e | 3957 | if (link_hwss->ext.update_stream_allocation_table == NULL || |
d5a43956 | 3958 | link_dp_get_encoding_format(&link->cur_link_settings) == DP_UNKNOWN_ENCODING) { |
990cad0e WL |
3959 | DC_LOG_ERROR("Failure: unknown encoding format\n"); |
3960 | return DC_ERROR_UNEXPECTED; | |
3961 | } | |
3962 | ||
3963 | link_hwss->ext.update_stream_allocation_table(link, &pipe_ctx->link_res, | |
d740e0bf FZ |
3964 | &link->mst_stream_alloc_table); |
3965 | ||
3966 | /* poll for immediate branch device ACT handled */ | |
3967 | ret = dm_helpers_dp_mst_poll_for_allocation_change_trigger( | |
3968 | stream->ctx, | |
3969 | stream); | |
3970 | ||
3971 | if (ret != ACT_LINK_LOST) { | |
3972 | /* send ALLOCATE_PAYLOAD sideband message with updated pbn */ | |
3973 | dm_helpers_dp_mst_send_payload_allocation( | |
3974 | stream->ctx, | |
3975 | stream, | |
3976 | true); | |
3977 | } | |
3978 | ||
3979 | /* increase throttled vcp size */ | |
3980 | pbn = get_pbn_from_bw_in_kbps(bw_in_kbps); | |
3981 | pbn_per_slot = get_pbn_per_slot(stream); | |
3982 | avg_time_slots_per_mtp = dc_fixpt_div(pbn, pbn_per_slot); | |
3983 | ||
9d8033d6 WL |
3984 | if (link_hwss->ext.set_throttled_vcp_size) |
3985 | link_hwss->ext.set_throttled_vcp_size(pipe_ctx, avg_time_slots_per_mtp); | |
3986 | if (link_hwss->ext.set_hblank_min_symbol_width) | |
3987 | link_hwss->ext.set_hblank_min_symbol_width(pipe_ctx, | |
5279e091 | 3988 | &link->cur_link_settings, |
d740e0bf FZ |
3989 | avg_time_slots_per_mtp); |
3990 | ||
3991 | return DC_OK; | |
4562236b HW |
3992 | } |
3993 | ||
3994 | static enum dc_status deallocate_mst_payload(struct pipe_ctx *pipe_ctx) | |
3995 | { | |
0971c40e | 3996 | struct dc_stream_state *stream = pipe_ctx->stream; |
ceb3dbb4 | 3997 | struct dc_link *link = stream->link; |
8c5e9bbb | 3998 | struct dc_dp_mst_stream_allocation_table proposed_table = {0}; |
eb0e5154 | 3999 | struct fixed31_32 avg_time_slots_per_mtp = dc_fixpt_from_int(0); |
2fcb2697 | 4000 | int i; |
d0778ebf | 4001 | bool mst_mode = (link->type == dc_connection_mst_branch); |
9d8033d6 | 4002 | const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); |
5279e091 | 4003 | const struct dc_link_settings empty_link_settings = {0}; |
5d4b05dd | 4004 | DC_LOGGER_INIT(link->ctx->logger); |
4562236b HW |
4005 | |
4006 | /* deallocate_mst_payload is called before disable link. When mode or | |
4007 | * disable/enable monitor, new stream is created which is not in link | |
4008 | * stream[] yet. For this, payload is not allocated yet, so de-alloc | |
4009 | * should not done. For new mode set, map_resources will get engine | |
4010 | * for new stream, so stream_enc->id should be validated until here. | |
4011 | */ | |
4012 | ||
4013 | /* slot X.Y */ | |
9d8033d6 WL |
4014 | if (link_hwss->ext.set_throttled_vcp_size) |
4015 | link_hwss->ext.set_throttled_vcp_size(pipe_ctx, avg_time_slots_per_mtp); | |
4016 | if (link_hwss->ext.set_hblank_min_symbol_width) | |
4017 | link_hwss->ext.set_hblank_min_symbol_width(pipe_ctx, | |
5279e091 | 4018 | &empty_link_settings, |
d740e0bf | 4019 | avg_time_slots_per_mtp); |
4562236b | 4020 | |
4562236b | 4021 | if (mst_mode) { |
cc67aae1 WL |
4022 | /* when link is in mst mode, reply on mst manager to remove |
4023 | * payload | |
4024 | */ | |
4562236b HW |
4025 | if (dm_helpers_dp_mst_write_payload_allocation_table( |
4026 | stream->ctx, | |
4fa086b9 | 4027 | stream, |
4562236b | 4028 | &proposed_table, |
cc67aae1 | 4029 | false)) |
4562236b | 4030 | |
d740e0bf | 4031 | update_mst_stream_alloc_table( |
cc67aae1 WL |
4032 | link, |
4033 | pipe_ctx->stream_res.stream_enc, | |
4034 | pipe_ctx->stream_res.hpo_dp_stream_enc, | |
4035 | &proposed_table); | |
4036 | else | |
4037 | DC_LOG_WARNING("Failed to update" | |
4038 | "MST allocation table for" | |
4039 | "pipe idx:%d\n", | |
4040 | pipe_ctx->pipe_idx); | |
4041 | } else { | |
4042 | /* when link is no longer in mst mode (mst hub unplugged), | |
4043 | * remove payload with default dc logic | |
4044 | */ | |
4045 | remove_stream_from_alloc_table(link, pipe_ctx->stream_res.stream_enc, | |
4046 | pipe_ctx->stream_res.hpo_dp_stream_enc); | |
4562236b HW |
4047 | } |
4048 | ||
1296423b | 4049 | DC_LOG_MST("%s" |
4562236b HW |
4050 | "stream_count: %d: ", |
4051 | __func__, | |
4052 | link->mst_stream_alloc_table.stream_count); | |
4053 | ||
4054 | for (i = 0; i < MAX_CONTROLLER_NUM; i++) { | |
d740e0bf FZ |
4055 | DC_LOG_MST("stream_enc[%d]: %p " |
4056 | "stream[%d].hpo_dp_stream_enc: %p " | |
4057 | "stream[%d].vcp_id: %d " | |
4058 | "stream[%d].slot_count: %d\n", | |
4059 | i, | |
4060 | (void *) link->mst_stream_alloc_table.stream_allocations[i].stream_enc, | |
4061 | i, | |
4062 | (void *) link->mst_stream_alloc_table.stream_allocations[i].hpo_dp_stream_enc, | |
4063 | i, | |
4064 | link->mst_stream_alloc_table.stream_allocations[i].vcp_id, | |
4065 | i, | |
4066 | link->mst_stream_alloc_table.stream_allocations[i].slot_count); | |
4562236b HW |
4067 | } |
4068 | ||
a5b79943 WL |
4069 | /* update mst stream allocation table hardware state */ |
4070 | if (link_hwss->ext.update_stream_allocation_table == NULL || | |
d5a43956 | 4071 | link_dp_get_encoding_format(&link->cur_link_settings) == DP_UNKNOWN_ENCODING) { |
9ff28ab4 | 4072 | DC_LOG_DEBUG("Unknown encoding format\n"); |
d740e0bf FZ |
4073 | return DC_ERROR_UNEXPECTED; |
4074 | } | |
4562236b | 4075 | |
a5b79943 WL |
4076 | link_hwss->ext.update_stream_allocation_table(link, &pipe_ctx->link_res, |
4077 | &link->mst_stream_alloc_table); | |
4078 | ||
4562236b HW |
4079 | if (mst_mode) { |
4080 | dm_helpers_dp_mst_poll_for_allocation_change_trigger( | |
4081 | stream->ctx, | |
4fa086b9 | 4082 | stream); |
4562236b HW |
4083 | |
4084 | dm_helpers_dp_mst_send_payload_allocation( | |
4085 | stream->ctx, | |
4fa086b9 | 4086 | stream, |
4562236b HW |
4087 | false); |
4088 | } | |
4089 | ||
4090 | return DC_OK; | |
4091 | } | |
ffdaeb1f | 4092 | |
ffdaeb1f | 4093 | |
d462fcf5 BL |
4094 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
4095 | static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off) | |
4096 | { | |
4097 | struct cp_psp *cp_psp = &pipe_ctx->stream->ctx->cp_psp; | |
64d283cb | 4098 | struct link_encoder *link_enc = NULL; |
580013b2 WL |
4099 | struct cp_psp_stream_config config = {0}; |
4100 | enum dp_panel_mode panel_mode = | |
4101 | dp_get_panel_mode(pipe_ctx->stream->link); | |
64d283cb | 4102 | |
580013b2 WL |
4103 | if (cp_psp == NULL || cp_psp->funcs.update_stream_config == NULL) |
4104 | return; | |
d462fcf5 | 4105 | |
66d58bf7 | 4106 | link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link); |
580013b2 WL |
4107 | ASSERT(link_enc); |
4108 | if (link_enc == NULL) | |
4109 | return; | |
cfd3f70e | 4110 | |
580013b2 WL |
4111 | /* otg instance */ |
4112 | config.otg_inst = (uint8_t) pipe_ctx->stream_res.tg->inst; | |
ffd89aa9 | 4113 | |
580013b2 WL |
4114 | /* dig front end */ |
4115 | config.dig_fe = (uint8_t) pipe_ctx->stream_res.stream_enc->stream_enc_inst; | |
ffd89aa9 | 4116 | |
580013b2 WL |
4117 | /* stream encoder index */ |
4118 | config.stream_enc_idx = pipe_ctx->stream_res.stream_enc->id - ENGINE_ID_DIGA; | |
d5a43956 | 4119 | if (link_is_dp_128b_132b_signal(pipe_ctx)) |
580013b2 WL |
4120 | config.stream_enc_idx = |
4121 | pipe_ctx->stream_res.hpo_dp_stream_enc->id - ENGINE_ID_HPO_DP_0; | |
ffd89aa9 | 4122 | |
580013b2 WL |
4123 | /* dig back end */ |
4124 | config.dig_be = pipe_ctx->stream->link->link_enc_hw_inst; | |
4125 | ||
4126 | /* link encoder index */ | |
4127 | config.link_enc_idx = link_enc->transmitter - TRANSMITTER_UNIPHY_A; | |
d5a43956 | 4128 | if (link_is_dp_128b_132b_signal(pipe_ctx)) |
580013b2 | 4129 | config.link_enc_idx = pipe_ctx->link_res.hpo_dp_link_enc->inst; |
d9eb8fea | 4130 | |
84ebd73e MG |
4131 | /* dio output index is dpia index for DPIA endpoint & dcio index by default */ |
4132 | if (pipe_ctx->stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) | |
4133 | config.dio_output_idx = pipe_ctx->stream->link->link_id.enum_id - ENUM_ID_1; | |
4134 | else | |
4135 | config.dio_output_idx = link_enc->transmitter - TRANSMITTER_UNIPHY_A; | |
4136 | ||
580013b2 WL |
4137 | |
4138 | /* phy index */ | |
4139 | config.phy_idx = resource_transmitter_to_phy_idx( | |
4140 | pipe_ctx->stream->link->dc, link_enc->transmitter); | |
4141 | if (pipe_ctx->stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) | |
4142 | /* USB4 DPIA doesn't use PHY in our soc, initialize it to 0 */ | |
4143 | config.phy_idx = 0; | |
4144 | ||
4145 | /* stream properties */ | |
4146 | config.assr_enabled = (panel_mode == DP_PANEL_MODE_EDP) ? 1 : 0; | |
4147 | config.mst_enabled = (pipe_ctx->stream->signal == | |
4148 | SIGNAL_TYPE_DISPLAY_PORT_MST) ? 1 : 0; | |
d5a43956 | 4149 | config.dp2_enabled = link_is_dp_128b_132b_signal(pipe_ctx) ? 1 : 0; |
580013b2 WL |
4150 | config.usb4_enabled = (pipe_ctx->stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) ? |
4151 | 1 : 0; | |
4152 | config.dpms_off = dpms_off; | |
4153 | ||
4154 | /* dm stream context */ | |
4155 | config.dm_stream_ctx = pipe_ctx->stream->dm_stream_context; | |
4156 | ||
4157 | cp_psp->funcs.update_stream_config(cp_psp->handle, &config); | |
d462fcf5 BL |
4158 | } |
4159 | #endif | |
4562236b | 4160 | |
f01ee019 FZ |
4161 | static void fpga_dp_hpo_enable_link_and_stream(struct dc_state *state, struct pipe_ctx *pipe_ctx) |
4162 | { | |
4163 | struct dc *dc = pipe_ctx->stream->ctx->dc; | |
4164 | struct dc_stream_state *stream = pipe_ctx->stream; | |
4165 | struct link_mst_stream_allocation_table proposed_table = {0}; | |
4166 | struct fixed31_32 avg_time_slots_per_mtp; | |
4167 | uint8_t req_slot_count = 0; | |
4168 | uint8_t vc_id = 1; /// VC ID always 1 for SST | |
017860c9 | 4169 | struct dc_link_settings link_settings = pipe_ctx->link_config.dp_link_settings; |
9d8033d6 | 4170 | const struct link_hwss *link_hwss = get_link_hwss(stream->link, &pipe_ctx->link_res); |
f01ee019 FZ |
4171 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
4172 | ||
f01ee019 FZ |
4173 | stream->link->cur_link_settings = link_settings; |
4174 | ||
e8702d0b WL |
4175 | if (link_hwss->ext.enable_dp_link_output) |
4176 | link_hwss->ext.enable_dp_link_output(stream->link, &pipe_ctx->link_res, | |
4177 | stream->signal, pipe_ctx->clock_source->id, | |
4178 | &link_settings); | |
f01ee019 FZ |
4179 | |
4180 | #ifdef DIAGS_BUILD | |
4181 | /* Workaround for FPGA HPO capture DP link data: | |
4182 | * HPO capture will set link to active mode | |
4183 | * This workaround is required to get a capture from start of frame | |
4184 | */ | |
4185 | if (!dc->debug.fpga_hpo_capture_en) { | |
4186 | struct encoder_set_dp_phy_pattern_param params = {0}; | |
4187 | params.dp_phy_pattern = DP_TEST_PATTERN_VIDEO_MODE; | |
4188 | ||
4189 | /* Set link active */ | |
4190 | stream->link->hpo_dp_link_enc->funcs->set_link_test_pattern( | |
4191 | stream->link->hpo_dp_link_enc, | |
4192 | ¶ms); | |
4193 | } | |
4194 | #endif | |
4195 | ||
4196 | /* Enable DP_STREAM_ENC */ | |
4197 | dc->hwss.enable_stream(pipe_ctx); | |
4198 | ||
4199 | /* Set DPS PPS SDP (AKA "info frames") */ | |
4200 | if (pipe_ctx->stream->timing.flags.DSC) { | |
253a5591 | 4201 | dp_set_dsc_pps_sdp(pipe_ctx, true, true); |
f01ee019 FZ |
4202 | } |
4203 | ||
4204 | /* Allocate Payload */ | |
4205 | if ((stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) && (state->stream_count > 1)) { | |
4206 | // MST case | |
4207 | uint8_t i; | |
4208 | ||
4209 | proposed_table.stream_count = state->stream_count; | |
4210 | for (i = 0; i < state->stream_count; i++) { | |
4211 | avg_time_slots_per_mtp = calculate_sst_avg_time_slots_per_mtp(state->streams[i], state->streams[i]->link); | |
4212 | req_slot_count = dc_fixpt_ceil(avg_time_slots_per_mtp); | |
4213 | proposed_table.stream_allocations[i].slot_count = req_slot_count; | |
4214 | proposed_table.stream_allocations[i].vcp_id = i+1; | |
4215 | /* NOTE: This makes assumption that pipe_ctx index is same as stream index */ | |
4216 | proposed_table.stream_allocations[i].hpo_dp_stream_enc = state->res_ctx.pipe_ctx[i].stream_res.hpo_dp_stream_enc; | |
4217 | } | |
4218 | } else { | |
4219 | // SST case | |
4220 | avg_time_slots_per_mtp = calculate_sst_avg_time_slots_per_mtp(stream, stream->link); | |
4221 | req_slot_count = dc_fixpt_ceil(avg_time_slots_per_mtp); | |
4222 | proposed_table.stream_count = 1; /// Always 1 stream for SST | |
4223 | proposed_table.stream_allocations[0].slot_count = req_slot_count; | |
4224 | proposed_table.stream_allocations[0].vcp_id = vc_id; | |
4225 | proposed_table.stream_allocations[0].hpo_dp_stream_enc = pipe_ctx->stream_res.hpo_dp_stream_enc; | |
4226 | } | |
4227 | ||
a5b79943 WL |
4228 | link_hwss->ext.update_stream_allocation_table(stream->link, |
4229 | &pipe_ctx->link_res, | |
f01ee019 FZ |
4230 | &proposed_table); |
4231 | ||
9d8033d6 WL |
4232 | if (link_hwss->ext.set_throttled_vcp_size) |
4233 | link_hwss->ext.set_throttled_vcp_size(pipe_ctx, avg_time_slots_per_mtp); | |
f01ee019 FZ |
4234 | |
4235 | dc->hwss.unblank_stream(pipe_ctx, &stream->link->cur_link_settings); | |
14e2739c | 4236 | dc->hwss.enable_audio_stream(pipe_ctx); |
f01ee019 | 4237 | } |
f01ee019 | 4238 | |
ab8db3e1 AG |
4239 | void core_link_enable_stream( |
4240 | struct dc_state *state, | |
4241 | struct pipe_ctx *pipe_ctx) | |
4562236b | 4242 | { |
2b77dcc5 | 4243 | struct dc *dc = pipe_ctx->stream->ctx->dc; |
1e7e86c4 | 4244 | struct dc_stream_state *stream = pipe_ctx->stream; |
3550d622 | 4245 | struct dc_link *link = stream->sink->link; |
4cac1e6d | 4246 | enum dc_status status; |
64d283cb | 4247 | struct link_encoder *link_enc; |
c06f670f | 4248 | enum otg_out_mux_dest otg_out_dest = OUT_MUX_DIO; |
18b4f1a0 | 4249 | struct vpg *vpg = pipe_ctx->stream_res.stream_enc->vpg; |
eff5e115 | 4250 | const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); |
b0d88890 | 4251 | |
d5a43956 | 4252 | if (link_is_dp_128b_132b_signal(pipe_ctx)) |
b0d88890 | 4253 | vpg = pipe_ctx->stream_res.hpo_dp_stream_enc->vpg; |
d9eb8fea | 4254 | |
5d4b05dd | 4255 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
4cac1e6d | 4256 | |
fa39f936 HW |
4257 | if (pipe_ctx->stream->sink) { |
4258 | if (pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL && | |
4259 | pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_NONE) { | |
4260 | DC_LOG_DC("%s pipe_ctx dispname=%s signal=%x\n", __func__, | |
4261 | pipe_ctx->stream->sink->edid_caps.display_name, | |
4262 | pipe_ctx->stream->signal); | |
4263 | } | |
4264 | } | |
4265 | ||
39063de9 | 4266 | if (!IS_DIAG_DC(dc->ctx->dce_environment) && |
ef5a7d26 WL |
4267 | dc_is_virtual_signal(pipe_ctx->stream->signal)) |
4268 | return; | |
4269 | ||
66d58bf7 | 4270 | link_enc = link_enc_cfg_get_link_enc(link); |
64d283cb JK |
4271 | ASSERT(link_enc); |
4272 | ||
f01ee019 | 4273 | if (!dc_is_virtual_signal(pipe_ctx->stream->signal) |
d5a43956 | 4274 | && !link_is_dp_128b_132b_signal(pipe_ctx)) { |
64d283cb JK |
4275 | if (link_enc) |
4276 | link_enc->funcs->setup( | |
4277 | link_enc, | |
4278 | pipe_ctx->stream->signal); | |
1e7e86c4 ST |
4279 | } |
4280 | ||
eed928dc CL |
4281 | pipe_ctx->stream->link->link_state_valid = true; |
4282 | ||
eff5e115 | 4283 | if (pipe_ctx->stream_res.tg->funcs->set_out_mux) { |
d5a43956 | 4284 | if (link_is_dp_128b_132b_signal(pipe_ctx)) |
eff5e115 WL |
4285 | otg_out_dest = OUT_MUX_HPO_DP; |
4286 | else | |
4287 | otg_out_dest = OUT_MUX_DIO; | |
c06f670f | 4288 | pipe_ctx->stream_res.tg->funcs->set_out_mux(pipe_ctx->stream_res.tg, otg_out_dest); |
eff5e115 | 4289 | } |
be547111 | 4290 | |
eff5e115 | 4291 | link_hwss->setup_stream_attribute(pipe_ctx); |
11c3ee48 | 4292 | |
2b77dcc5 | 4293 | if (!IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) { |
d2d7885f AK |
4294 | bool apply_edp_fast_boot_optimization = |
4295 | pipe_ctx->stream->apply_edp_fast_boot_optimization; | |
4296 | ||
4297 | pipe_ctx->stream->apply_edp_fast_boot_optimization = false; | |
4298 | ||
18b4f1a0 MS |
4299 | // Enable VPG before building infoframe |
4300 | if (vpg && vpg->funcs->vpg_poweron) | |
4301 | vpg->funcs->vpg_poweron(vpg); | |
18b4f1a0 | 4302 | |
aa9c4abe | 4303 | resource_build_info_frame(pipe_ctx); |
2b77dcc5 | 4304 | dc->hwss.update_info_frame(pipe_ctx); |
aa9c4abe | 4305 | |
3550d622 LHM |
4306 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) |
4307 | dp_source_sequence_trace(link, DPCD_SOURCE_SEQ_AFTER_UPDATE_INFO_FRAME); | |
4308 | ||
d2d7885f AK |
4309 | /* Do not touch link on seamless boot optimization. */ |
4310 | if (pipe_ctx->stream->apply_seamless_boot_optimization) { | |
4311 | pipe_ctx->stream->dpms_off = false; | |
e664609e AW |
4312 | |
4313 | /* Still enable stream features & audio on seamless boot for DP external displays */ | |
4314 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT) { | |
4315 | enable_stream_features(pipe_ctx); | |
14e2739c | 4316 | dc->hwss.enable_audio_stream(pipe_ctx); |
e664609e AW |
4317 | } |
4318 | ||
d462fcf5 BL |
4319 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
4320 | update_psp_stream_config(pipe_ctx, false); | |
4321 | #endif | |
d2d7885f AK |
4322 | return; |
4323 | } | |
4324 | ||
aa9c4abe NC |
4325 | /* eDP lit up by bios already, no need to enable again. */ |
4326 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_EDP && | |
8cf9575d | 4327 | apply_edp_fast_boot_optimization && |
8acd9754 | 4328 | !pipe_ctx->stream->timing.flags.DSC && |
4329 | !pipe_ctx->next_odm_pipe) { | |
aa9c4abe | 4330 | pipe_ctx->stream->dpms_off = false; |
d462fcf5 BL |
4331 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
4332 | update_psp_stream_config(pipe_ctx, false); | |
4333 | #endif | |
aa9c4abe NC |
4334 | return; |
4335 | } | |
4cac1e6d | 4336 | |
aa9c4abe NC |
4337 | if (pipe_ctx->stream->dpms_off) |
4338 | return; | |
f0362823 | 4339 | |
c7783a6e NI |
4340 | /* Have to setup DSC before DIG FE and BE are connected (which happens before the |
4341 | * link training). This is to make sure the bandwidth sent to DIG BE won't be | |
4342 | * bigger than what the link and/or DIG BE can handle. VBID[6]/CompressedStream_flag | |
4343 | * will be automatically set at a later time when the video is enabled | |
4344 | * (DP_VID_STREAM_EN = 1). | |
4345 | */ | |
4346 | if (pipe_ctx->stream->timing.flags.DSC) { | |
4347 | if (dc_is_dp_signal(pipe_ctx->stream->signal) || | |
4348 | dc_is_virtual_signal(pipe_ctx->stream->signal)) | |
4349 | dp_set_dsc_enable(pipe_ctx, true); | |
4350 | ||
4351 | } | |
4352 | ||
aa9c4abe | 4353 | status = enable_link(state, pipe_ctx); |
c0ba5ec7 | 4354 | |
aa9c4abe | 4355 | if (status != DC_OK) { |
1296423b | 4356 | DC_LOG_WARNING("enabling link %u failed: %d\n", |
ceb3dbb4 | 4357 | pipe_ctx->stream->link->link_index, |
c0ba5ec7 KC |
4358 | status); |
4359 | ||
4360 | /* Abort stream enable *unless* the failure was due to | |
4361 | * DP link training - some DP monitors will recover and | |
00f713c6 EY |
4362 | * show the stream anyway. But MST displays can't proceed |
4363 | * without link training. | |
c0ba5ec7 | 4364 | */ |
00f713c6 EY |
4365 | if (status != DC_FAIL_DP_LINK_TRAINING || |
4366 | pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { | |
bca5bea4 | 4367 | if (false == stream->link->link_status.link_active) |
ef30f441 WL |
4368 | disable_link(stream->link, &pipe_ctx->link_res, |
4369 | pipe_ctx->stream->signal); | |
c0ba5ec7 KC |
4370 | BREAK_TO_DEBUGGER(); |
4371 | return; | |
4372 | } | |
aa9c4abe | 4373 | } |
4562236b | 4374 | |
aa9c4abe NC |
4375 | /* turn off otg test pattern if enable */ |
4376 | if (pipe_ctx->stream_res.tg->funcs->set_test_pattern) | |
4377 | pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg, | |
4378 | CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, | |
4379 | COLOR_DEPTH_UNDEFINED); | |
71021265 | 4380 | |
be547111 BL |
4381 | /* This second call is needed to reconfigure the DIG |
4382 | * as a workaround for the incorrect value being applied | |
4383 | * from transmitter control. | |
4384 | */ | |
f01ee019 | 4385 | if (!(dc_is_virtual_signal(pipe_ctx->stream->signal) || |
d5a43956 | 4386 | link_is_dp_128b_132b_signal(pipe_ctx))) |
64d283cb JK |
4387 | if (link_enc) |
4388 | link_enc->funcs->setup( | |
4389 | link_enc, | |
4390 | pipe_ctx->stream->signal); | |
be547111 | 4391 | |
2b77dcc5 | 4392 | dc->hwss.enable_stream(pipe_ctx); |
4562236b | 4393 | |
1a9e3d45 | 4394 | /* Set DPS PPS SDP (AKA "info frames") */ |
606b3551 DL |
4395 | if (pipe_ctx->stream->timing.flags.DSC) { |
4396 | if (dc_is_dp_signal(pipe_ctx->stream->signal) || | |
8cf9575d MH |
4397 | dc_is_virtual_signal(pipe_ctx->stream->signal)) { |
4398 | dp_set_dsc_on_rx(pipe_ctx, true); | |
253a5591 | 4399 | dp_set_dsc_pps_sdp(pipe_ctx, true, true); |
8cf9575d | 4400 | } |
97bda032 | 4401 | } |
1a9e3d45 NC |
4402 | |
4403 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) | |
48af9b91 | 4404 | dc_link_allocate_mst_payload(pipe_ctx); |
f01ee019 | 4405 | else if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT && |
d5a43956 | 4406 | link_is_dp_128b_132b_signal(pipe_ctx)) |
f01ee019 | 4407 | dc_link_update_sst_payload(pipe_ctx, true); |
1a9e3d45 | 4408 | |
2b77dcc5 | 4409 | dc->hwss.unblank_stream(pipe_ctx, |
ceb3dbb4 | 4410 | &pipe_ctx->stream->link->cur_link_settings); |
aa9c4abe | 4411 | |
e2c9529f JC |
4412 | if (stream->sink_patches.delay_ignore_msa > 0) |
4413 | msleep(stream->sink_patches.delay_ignore_msa); | |
dc326f61 | 4414 | |
14fee4ca JA |
4415 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) |
4416 | enable_stream_features(pipe_ctx); | |
d462fcf5 BL |
4417 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
4418 | update_psp_stream_config(pipe_ctx, false); | |
4419 | #endif | |
c0794a3b RC |
4420 | |
4421 | dc->hwss.enable_audio_stream(pipe_ctx); | |
4422 | ||
2b77dcc5 | 4423 | } else { // if (IS_FPGA_MAXIMUS_DC(dc->ctx->dce_environment)) |
d5a43956 | 4424 | if (link_is_dp_128b_132b_signal(pipe_ctx)) |
f01ee019 | 4425 | fpga_dp_hpo_enable_link_and_stream(state, pipe_ctx); |
97bda032 HW |
4426 | if (dc_is_dp_signal(pipe_ctx->stream->signal) || |
4427 | dc_is_virtual_signal(pipe_ctx->stream->signal)) | |
2f752e91 | 4428 | dp_set_dsc_enable(pipe_ctx, true); |
97bda032 | 4429 | } |
4cf7c427 | 4430 | |
98dd398a | 4431 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) { |
4cf7c427 JX |
4432 | core_link_set_avmute(pipe_ctx, false); |
4433 | } | |
4562236b HW |
4434 | } |
4435 | ||
57430404 | 4436 | void core_link_disable_stream(struct pipe_ctx *pipe_ctx) |
4562236b | 4437 | { |
2b77dcc5 | 4438 | struct dc *dc = pipe_ctx->stream->ctx->dc; |
ab0cb022 | 4439 | struct dc_stream_state *stream = pipe_ctx->stream; |
052fa7e8 | 4440 | struct dc_link *link = stream->sink->link; |
18b4f1a0 | 4441 | struct vpg *vpg = pipe_ctx->stream_res.stream_enc->vpg; |
b0d88890 | 4442 | |
d5a43956 | 4443 | if (link_is_dp_128b_132b_signal(pipe_ctx)) |
b0d88890 | 4444 | vpg = pipe_ctx->stream_res.hpo_dp_stream_enc->vpg; |
4562236b | 4445 | |
fa39f936 HW |
4446 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
4447 | ||
4448 | if (pipe_ctx->stream->sink) { | |
4449 | if (pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL && | |
4450 | pipe_ctx->stream->sink->sink_signal != SIGNAL_TYPE_NONE) { | |
4451 | DC_LOG_DC("%s pipe_ctx dispname=%s signal=%x\n", __func__, | |
4452 | pipe_ctx->stream->sink->edid_caps.display_name, | |
4453 | pipe_ctx->stream->signal); | |
4454 | } | |
4455 | } | |
4456 | ||
39063de9 | 4457 | if (!IS_DIAG_DC(dc->ctx->dce_environment) && |
ef5a7d26 WL |
4458 | dc_is_virtual_signal(pipe_ctx->stream->signal)) |
4459 | return; | |
4460 | ||
74d021b5 BS |
4461 | if (!pipe_ctx->stream->sink->edid_caps.panel_patch.skip_avmute) { |
4462 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) | |
4463 | core_link_set_avmute(pipe_ctx, true); | |
4cf7c427 JX |
4464 | } |
4465 | ||
c0794a3b RC |
4466 | dc->hwss.disable_audio_stream(pipe_ctx); |
4467 | ||
d462fcf5 BL |
4468 | #if defined(CONFIG_DRM_AMD_DC_HDCP) |
4469 | update_psp_stream_config(pipe_ctx, true); | |
4470 | #endif | |
b61f0562 | 4471 | dc->hwss.blank_stream(pipe_ctx); |
d462fcf5 | 4472 | |
4562236b HW |
4473 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) |
4474 | deallocate_mst_payload(pipe_ctx); | |
f01ee019 | 4475 | else if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT && |
d5a43956 | 4476 | link_is_dp_128b_132b_signal(pipe_ctx)) |
f01ee019 | 4477 | dc_link_update_sst_payload(pipe_ctx, false); |
4562236b | 4478 | |
052fa7e8 CL |
4479 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) { |
4480 | struct ext_hdmi_settings settings = {0}; | |
4481 | enum engine_id eng_id = pipe_ctx->stream_res.stream_enc->id; | |
ab0cb022 | 4482 | |
052fa7e8 CL |
4483 | unsigned short masked_chip_caps = link->chip_caps & |
4484 | EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK; | |
4485 | //Need to inform that sink is going to use legacy HDMI mode. | |
a98cdd8c | 4486 | write_scdc_data( |
052fa7e8 CL |
4487 | link->ddc, |
4488 | 165000,//vbios only handles 165Mhz. | |
4489 | false); | |
4490 | if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_TISN65DP159RSBT) { | |
4491 | /* DP159, Retimer settings */ | |
4492 | if (get_ext_hdmi_settings(pipe_ctx, eng_id, &settings)) | |
4493 | write_i2c_retimer_setting(pipe_ctx, | |
4494 | false, false, &settings); | |
4495 | else | |
4496 | write_i2c_default_retimer_setting(pipe_ctx, | |
4497 | false, false); | |
4498 | } else if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_PI3EQX1204) { | |
4499 | /* PI3EQX1204, Redriver settings */ | |
4500 | write_i2c_redriver_setting(pipe_ctx, false); | |
4501 | } | |
4502 | } | |
4562236b | 4503 | |
f01ee019 | 4504 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT && |
d5a43956 | 4505 | !link_is_dp_128b_132b_signal(pipe_ctx)) { |
f01ee019 FZ |
4506 | |
4507 | /* In DP1.x SST mode, our encoder will go to TPS1 | |
4508 | * when link is on but stream is off. | |
4509 | * Disabling link before stream will avoid exposing TPS1 pattern | |
4510 | * during the disable sequence as it will confuse some receivers | |
4511 | * state machine. | |
4512 | * In DP2 or MST mode, our encoder will stay video active | |
4513 | */ | |
ef30f441 | 4514 | disable_link(pipe_ctx->stream->link, &pipe_ctx->link_res, pipe_ctx->stream->signal); |
f01ee019 FZ |
4515 | dc->hwss.disable_stream(pipe_ctx); |
4516 | } else { | |
4517 | dc->hwss.disable_stream(pipe_ctx); | |
ef30f441 | 4518 | disable_link(pipe_ctx->stream->link, &pipe_ctx->link_res, pipe_ctx->stream->signal); |
f01ee019 | 4519 | } |
eec3303d | 4520 | |
606b3551 DL |
4521 | if (pipe_ctx->stream->timing.flags.DSC) { |
4522 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) | |
4523 | dp_set_dsc_enable(pipe_ctx, false); | |
97bda032 | 4524 | } |
d5a43956 | 4525 | if (link_is_dp_128b_132b_signal(pipe_ctx)) { |
f01ee019 FZ |
4526 | if (pipe_ctx->stream_res.tg->funcs->set_out_mux) |
4527 | pipe_ctx->stream_res.tg->funcs->set_out_mux(pipe_ctx->stream_res.tg, OUT_MUX_DIO); | |
4528 | } | |
18b4f1a0 | 4529 | |
18b4f1a0 MS |
4530 | if (vpg && vpg->funcs->vpg_powerdown) |
4531 | vpg->funcs->vpg_powerdown(vpg); | |
4562236b HW |
4532 | } |
4533 | ||
15e17335 CL |
4534 | void core_link_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) |
4535 | { | |
2b77dcc5 | 4536 | struct dc *dc = pipe_ctx->stream->ctx->dc; |
15e17335 | 4537 | |
a280a71f | 4538 | if (!dc_is_hdmi_signal(pipe_ctx->stream->signal)) |
15e17335 CL |
4539 | return; |
4540 | ||
2b77dcc5 | 4541 | dc->hwss.set_avmute(pipe_ctx, enable); |
15e17335 CL |
4542 | } |
4543 | ||
fe798de5 CP |
4544 | void dc_link_set_drive_settings(struct dc *dc, |
4545 | struct link_training_settings *lt_settings, | |
4546 | const struct dc_link *link) | |
4547 | { | |
4548 | ||
4549 | int i; | |
f6a3795d | 4550 | struct link_resource link_res; |
fe798de5 | 4551 | |
f6a3795d WL |
4552 | for (i = 0; i < dc->link_count; i++) |
4553 | if (dc->links[i] == link) | |
4554 | break; | |
fe798de5 | 4555 | |
f6a3795d | 4556 | if (i >= dc->link_count) |
fe798de5 | 4557 | ASSERT_CRITICAL(false); |
f6a3795d WL |
4558 | |
4559 | dc_link_get_cur_link_res(link, &link_res); | |
4560 | dc_link_dp_set_drive_settings(dc->links[i], &link_res, lt_settings); | |
fe798de5 CP |
4561 | } |
4562 | ||
fe798de5 CP |
4563 | void dc_link_set_preferred_link_settings(struct dc *dc, |
4564 | struct dc_link_settings *link_setting, | |
4565 | struct dc_link *link) | |
4566 | { | |
4567 | int i; | |
4568 | struct pipe_ctx *pipe; | |
4569 | struct dc_stream_state *link_stream; | |
4570 | struct dc_link_settings store_settings = *link_setting; | |
4571 | ||
4572 | link->preferred_link_setting = store_settings; | |
4573 | ||
4574 | /* Retrain with preferred link settings only relevant for | |
4575 | * DP signal type | |
08900ab7 | 4576 | * Check for non-DP signal or if passive dongle present |
fe798de5 | 4577 | */ |
08900ab7 ST |
4578 | if (!dc_is_dp_signal(link->connector_signal) || |
4579 | link->dongle_max_pix_clk > 0) | |
fe798de5 CP |
4580 | return; |
4581 | ||
4582 | for (i = 0; i < MAX_PIPES; i++) { | |
4583 | pipe = &dc->current_state->res_ctx.pipe_ctx[i]; | |
4584 | if (pipe->stream && pipe->stream->link) { | |
252f3d95 HW |
4585 | if (pipe->stream->link == link) { |
4586 | link_stream = pipe->stream; | |
fe798de5 | 4587 | break; |
252f3d95 | 4588 | } |
fe798de5 CP |
4589 | } |
4590 | } | |
4591 | ||
4592 | /* Stream not found */ | |
4593 | if (i == MAX_PIPES) | |
4594 | return; | |
4595 | ||
fe798de5 CP |
4596 | /* Cannot retrain link if backend is off */ |
4597 | if (link_stream->dpms_off) | |
4598 | return; | |
4599 | ||
d5a43956 | 4600 | if (link_decide_link_settings(link_stream, &store_settings)) |
fe798de5 CP |
4601 | dp_retrain_link_dp_test(link, &store_settings, false); |
4602 | } | |
4603 | ||
e0a6440a DG |
4604 | void dc_link_set_preferred_training_settings(struct dc *dc, |
4605 | struct dc_link_settings *link_setting, | |
4606 | struct dc_link_training_overrides *lt_overrides, | |
4607 | struct dc_link *link, | |
4608 | bool skip_immediate_retrain) | |
4609 | { | |
4610 | if (lt_overrides != NULL) | |
4611 | link->preferred_training_settings = *lt_overrides; | |
4612 | else | |
4613 | memset(&link->preferred_training_settings, 0, sizeof(link->preferred_training_settings)); | |
4614 | ||
4615 | if (link_setting != NULL) { | |
4616 | link->preferred_link_setting = *link_setting; | |
4617 | } else { | |
4618 | link->preferred_link_setting.lane_count = LANE_COUNT_UNKNOWN; | |
4619 | link->preferred_link_setting.link_rate = LINK_RATE_UNKNOWN; | |
4620 | } | |
4621 | ||
ea192af5 MS |
4622 | if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && |
4623 | link->type == dc_connection_mst_branch) | |
4624 | dm_helpers_dp_mst_update_branch_bandwidth(dc->ctx, link); | |
4625 | ||
e0a6440a DG |
4626 | /* Retrain now, or wait until next stream update to apply */ |
4627 | if (skip_immediate_retrain == false) | |
4628 | dc_link_set_preferred_link_settings(dc, &link->preferred_link_setting, link); | |
4629 | } | |
4630 | ||
fe798de5 CP |
4631 | void dc_link_set_test_pattern(struct dc_link *link, |
4632 | enum dp_test_pattern test_pattern, | |
2057b7e1 | 4633 | enum dp_test_pattern_color_space test_pattern_color_space, |
fe798de5 CP |
4634 | const struct link_training_settings *p_link_settings, |
4635 | const unsigned char *p_custom_pattern, | |
4636 | unsigned int cust_pattern_size) | |
4637 | { | |
4638 | if (link != NULL) | |
4639 | dc_link_dp_set_test_pattern( | |
4640 | link, | |
4641 | test_pattern, | |
2057b7e1 | 4642 | test_pattern_color_space, |
fe798de5 CP |
4643 | p_link_settings, |
4644 | p_custom_pattern, | |
4645 | cust_pattern_size); | |
4646 | } | |
4647 | ||
4648 | uint32_t dc_link_bandwidth_kbps( | |
4649 | const struct dc_link *link, | |
4650 | const struct dc_link_settings *link_setting) | |
4651 | { | |
f01ee019 FZ |
4652 | uint32_t total_data_bw_efficiency_x10000 = 0; |
4653 | uint32_t link_rate_per_lane_kbps = 0; | |
4654 | ||
d5a43956 | 4655 | switch (link_dp_get_encoding_format(link_setting)) { |
f01ee019 FZ |
4656 | case DP_8b_10b_ENCODING: |
4657 | /* For 8b/10b encoding: | |
4658 | * link rate is defined in the unit of LINK_RATE_REF_FREQ_IN_KHZ per DP byte per lane. | |
4659 | * data bandwidth efficiency is 80% with additional 3% overhead if FEC is supported. | |
4660 | */ | |
4661 | link_rate_per_lane_kbps = link_setting->link_rate * LINK_RATE_REF_FREQ_IN_KHZ * BITS_PER_DP_BYTE; | |
4662 | total_data_bw_efficiency_x10000 = DATA_EFFICIENCY_8b_10b_x10000; | |
4663 | if (dc_link_should_enable_fec(link)) { | |
4664 | total_data_bw_efficiency_x10000 /= 100; | |
4665 | total_data_bw_efficiency_x10000 *= DATA_EFFICIENCY_8b_10b_FEC_EFFICIENCY_x100; | |
4666 | } | |
4667 | break; | |
4668 | case DP_128b_132b_ENCODING: | |
4669 | /* For 128b/132b encoding: | |
4670 | * link rate is defined in the unit of 10mbps per lane. | |
4671 | * total data bandwidth efficiency is always 96.71%. | |
4672 | */ | |
4673 | link_rate_per_lane_kbps = link_setting->link_rate * 10000; | |
4674 | total_data_bw_efficiency_x10000 = DATA_EFFICIENCY_128b_132b_x10000; | |
4675 | break; | |
4676 | default: | |
4677 | break; | |
4678 | } | |
4679 | ||
4680 | /* overall effective link bandwidth = link rate per lane * lane count * total data bandwidth efficiency */ | |
4681 | return link_rate_per_lane_kbps * link_setting->lane_count / 10000 * total_data_bw_efficiency_x10000; | |
fe798de5 CP |
4682 | } |
4683 | ||
91a9ead0 MM |
4684 | uint32_t dc_bandwidth_in_kbps_from_timing( |
4685 | const struct dc_crtc_timing *timing) | |
4686 | { | |
4687 | uint32_t bits_per_channel = 0; | |
4688 | uint32_t kbps; | |
4689 | ||
4690 | #if defined(CONFIG_DRM_AMD_DC_DCN) | |
4691 | if (timing->flags.DSC) | |
4692 | return dc_dsc_stream_bandwidth_in_kbps(timing, | |
4693 | timing->dsc_cfg.bits_per_pixel, | |
4694 | timing->dsc_cfg.num_slices_h, | |
4695 | timing->dsc_cfg.is_dp); | |
433e5dec | 4696 | #endif /* CONFIG_DRM_AMD_DC_DCN */ |
91a9ead0 MM |
4697 | |
4698 | switch (timing->display_color_depth) { | |
4699 | case COLOR_DEPTH_666: | |
4700 | bits_per_channel = 6; | |
4701 | break; | |
4702 | case COLOR_DEPTH_888: | |
4703 | bits_per_channel = 8; | |
4704 | break; | |
4705 | case COLOR_DEPTH_101010: | |
4706 | bits_per_channel = 10; | |
4707 | break; | |
4708 | case COLOR_DEPTH_121212: | |
4709 | bits_per_channel = 12; | |
4710 | break; | |
4711 | case COLOR_DEPTH_141414: | |
4712 | bits_per_channel = 14; | |
4713 | break; | |
4714 | case COLOR_DEPTH_161616: | |
4715 | bits_per_channel = 16; | |
4716 | break; | |
4717 | default: | |
4718 | ASSERT(bits_per_channel != 0); | |
4719 | bits_per_channel = 8; | |
4720 | break; | |
4721 | } | |
4722 | ||
4723 | kbps = timing->pix_clk_100hz / 10; | |
4724 | kbps *= bits_per_channel; | |
4725 | ||
4726 | if (timing->flags.Y_ONLY != 1) { | |
4727 | /*Only YOnly make reduce bandwidth by 1/3 compares to RGB*/ | |
4728 | kbps *= 3; | |
4729 | if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) | |
4730 | kbps /= 2; | |
4731 | else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) | |
4732 | kbps = kbps * 2 / 3; | |
4733 | } | |
4734 | ||
4735 | return kbps; | |
4736 | ||
4737 | } | |
ef30f441 | 4738 | |
f6a3795d WL |
4739 | void dc_link_get_cur_link_res(const struct dc_link *link, |
4740 | struct link_resource *link_res) | |
ef30f441 WL |
4741 | { |
4742 | int i; | |
4743 | struct pipe_ctx *pipe = NULL; | |
f6a3795d WL |
4744 | |
4745 | memset(link_res, 0, sizeof(*link_res)); | |
ef30f441 WL |
4746 | |
4747 | for (i = 0; i < MAX_PIPES; i++) { | |
4748 | pipe = &link->dc->current_state->res_ctx.pipe_ctx[i]; | |
4749 | if (pipe->stream && pipe->stream->link && pipe->top_pipe == NULL) { | |
4750 | if (pipe->stream->link == link) { | |
f6a3795d | 4751 | *link_res = pipe->link_res; |
ef30f441 WL |
4752 | break; |
4753 | } | |
4754 | } | |
4755 | } | |
4756 | ||
ef30f441 | 4757 | } |
4c3adc0b WL |
4758 | |
4759 | /** | |
4760 | * dc_get_cur_link_res_map() - take a snapshot of current link resource allocation state | |
4761 | * @dc: pointer to dc of the dm calling this | |
4762 | * @map: a dc link resource snapshot defined internally to dc. | |
4763 | * | |
4764 | * DM needs to capture a snapshot of current link resource allocation mapping | |
4765 | * and store it in its persistent storage. | |
4766 | * | |
4767 | * Some of the link resource is using first come first serve policy. | |
4768 | * The allocation mapping depends on original hotplug order. This information | |
4769 | * is lost after driver is loaded next time. The snapshot is used in order to | |
4770 | * restore link resource to its previous state so user will get consistent | |
4771 | * link capability allocation across reboot. | |
4772 | * | |
4773 | * Return: none (void function) | |
4774 | * | |
4775 | */ | |
4776 | void dc_get_cur_link_res_map(const struct dc *dc, uint32_t *map) | |
4777 | { | |
4c3adc0b | 4778 | struct dc_link *link; |
9506b8d9 | 4779 | uint32_t i; |
4c3adc0b WL |
4780 | uint32_t hpo_dp_recycle_map = 0; |
4781 | ||
4782 | *map = 0; | |
4783 | ||
4784 | if (dc->caps.dp_hpo) { | |
4785 | for (i = 0; i < dc->caps.max_links; i++) { | |
4786 | link = dc->links[i]; | |
4787 | if (link->link_status.link_active && | |
d5a43956 WL |
4788 | link_dp_get_encoding_format(&link->reported_link_cap) == DP_128b_132b_ENCODING && |
4789 | link_dp_get_encoding_format(&link->cur_link_settings) != DP_128b_132b_ENCODING) | |
4c3adc0b WL |
4790 | /* hpo dp link encoder is considered as recycled, when RX reports 128b/132b encoding capability |
4791 | * but current link doesn't use it. | |
4792 | */ | |
4793 | hpo_dp_recycle_map |= (1 << i); | |
4794 | } | |
4795 | *map |= (hpo_dp_recycle_map << LINK_RES_HPO_DP_REC_MAP__SHIFT); | |
4796 | } | |
4c3adc0b WL |
4797 | } |
4798 | ||
4799 | /** | |
4800 | * dc_restore_link_res_map() - restore link resource allocation state from a snapshot | |
4801 | * @dc: pointer to dc of the dm calling this | |
4802 | * @map: a dc link resource snapshot defined internally to dc. | |
4803 | * | |
4804 | * DM needs to call this function after initial link detection on boot and | |
4805 | * before first commit streams to restore link resource allocation state | |
4806 | * from previous boot session. | |
4807 | * | |
4808 | * Some of the link resource is using first come first serve policy. | |
4809 | * The allocation mapping depends on original hotplug order. This information | |
4810 | * is lost after driver is loaded next time. The snapshot is used in order to | |
4811 | * restore link resource to its previous state so user will get consistent | |
4812 | * link capability allocation across reboot. | |
4813 | * | |
4814 | * Return: none (void function) | |
4815 | * | |
4816 | */ | |
4817 | void dc_restore_link_res_map(const struct dc *dc, uint32_t *map) | |
4818 | { | |
4c3adc0b | 4819 | struct dc_link *link; |
9506b8d9 | 4820 | uint32_t i; |
4c3adc0b WL |
4821 | unsigned int available_hpo_dp_count; |
4822 | uint32_t hpo_dp_recycle_map = (*map & LINK_RES_HPO_DP_REC_MAP__MASK) | |
4823 | >> LINK_RES_HPO_DP_REC_MAP__SHIFT; | |
4824 | ||
4825 | if (dc->caps.dp_hpo) { | |
4826 | available_hpo_dp_count = dc->res_pool->hpo_dp_link_enc_count; | |
4827 | /* remove excess 128b/132b encoding support for not recycled links */ | |
4828 | for (i = 0; i < dc->caps.max_links; i++) { | |
4829 | if ((hpo_dp_recycle_map & (1 << i)) == 0) { | |
4830 | link = dc->links[i]; | |
4831 | if (link->type != dc_connection_none && | |
d5a43956 | 4832 | link_dp_get_encoding_format(&link->verified_link_cap) == DP_128b_132b_ENCODING) { |
4c3adc0b WL |
4833 | if (available_hpo_dp_count > 0) |
4834 | available_hpo_dp_count--; | |
4835 | else | |
4836 | /* remove 128b/132b encoding capability by limiting verified link rate to HBR3 */ | |
4837 | link->verified_link_cap.link_rate = LINK_RATE_HIGH3; | |
4838 | } | |
4839 | } | |
4840 | } | |
4841 | /* remove excess 128b/132b encoding support for recycled links */ | |
4842 | for (i = 0; i < dc->caps.max_links; i++) { | |
4843 | if ((hpo_dp_recycle_map & (1 << i)) != 0) { | |
4844 | link = dc->links[i]; | |
4845 | if (link->type != dc_connection_none && | |
d5a43956 | 4846 | link_dp_get_encoding_format(&link->verified_link_cap) == DP_128b_132b_ENCODING) { |
4c3adc0b WL |
4847 | if (available_hpo_dp_count > 0) |
4848 | available_hpo_dp_count--; | |
4849 | else | |
4850 | /* remove 128b/132b encoding capability by limiting verified link rate to HBR3 */ | |
4851 | link->verified_link_cap.link_rate = LINK_RATE_HIGH3; | |
4852 | } | |
4853 | } | |
4854 | } | |
4855 | } | |
4c3adc0b | 4856 | } |