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 | ||
26 | #include "dm_services.h" | |
cc57306f | 27 | #include "atom.h" |
4562236b HW |
28 | #include "dm_helpers.h" |
29 | #include "dc.h" | |
4562236b HW |
30 | #include "grph_object_id.h" |
31 | #include "gpio_service_interface.h" | |
32 | #include "core_status.h" | |
33 | #include "dc_link_dp.h" | |
34 | #include "dc_link_ddc.h" | |
35 | #include "link_hwss.h" | |
7f93c1de | 36 | #include "opp.h" |
fb3466a4 | 37 | |
4562236b HW |
38 | #include "link_encoder.h" |
39 | #include "hw_sequencer.h" | |
40 | #include "resource.h" | |
6728b30c | 41 | #include "abm.h" |
4562236b | 42 | #include "fixed31_32.h" |
eaca91ee | 43 | #include "dpcd_defs.h" |
3548f073 | 44 | #include "dmcu.h" |
4562236b | 45 | |
5d4b05dd BL |
46 | #define DC_LOGGER_INIT(logger) |
47 | ||
4562236b | 48 | |
4562236b | 49 | #define LINK_INFO(...) \ |
1296423b | 50 | DC_LOG_HW_HOTPLUG( \ |
4562236b HW |
51 | __VA_ARGS__) |
52 | ||
2f14bc89 CL |
53 | #define RETIMER_REDRIVER_INFO(...) \ |
54 | DC_LOG_RETIMER_REDRIVER( \ | |
55 | __VA_ARGS__) | |
4562236b HW |
56 | /******************************************************************************* |
57 | * Private structures | |
58 | ******************************************************************************/ | |
59 | ||
60 | enum { | |
61 | LINK_RATE_REF_FREQ_IN_MHZ = 27, | |
824474ba BL |
62 | PEAK_FACTOR_X1000 = 1006, |
63 | /* | |
64 | * Some receivers fail to train on first try and are good | |
65 | * on subsequent tries. 2 retries should be plenty. If we | |
66 | * don't have a successful training then we don't expect to | |
67 | * ever get one. | |
68 | */ | |
69 | LINK_TRAINING_MAX_VERIFY_RETRY = 2 | |
4562236b HW |
70 | }; |
71 | ||
72 | /******************************************************************************* | |
73 | * Private functions | |
74 | ******************************************************************************/ | |
d0778ebf | 75 | static void destruct(struct dc_link *link) |
4562236b HW |
76 | { |
77 | int i; | |
78 | ||
d0778ebf HW |
79 | if (link->ddc) |
80 | dal_ddc_service_destroy(&link->ddc); | |
4562236b HW |
81 | |
82 | if(link->link_enc) | |
83 | link->link_enc->funcs->destroy(&link->link_enc); | |
84 | ||
d0778ebf HW |
85 | if (link->local_sink) |
86 | dc_sink_release(link->local_sink); | |
4562236b | 87 | |
d0778ebf HW |
88 | for (i = 0; i < link->sink_count; ++i) |
89 | dc_sink_release(link->remote_sinks[i]); | |
4562236b HW |
90 | } |
91 | ||
87401969 AJ |
92 | struct gpio *get_hpd_gpio(struct dc_bios *dcb, |
93 | struct graphics_object_id link_id, | |
94 | struct gpio_service *gpio_service) | |
4562236b HW |
95 | { |
96 | enum bp_result bp_result; | |
4562236b HW |
97 | struct graphics_object_hpd_info hpd_info; |
98 | struct gpio_pin_info pin_info; | |
99 | ||
87401969 | 100 | if (dcb->funcs->get_hpd_info(dcb, link_id, &hpd_info) != BP_RESULT_OK) |
4562236b HW |
101 | return NULL; |
102 | ||
103 | bp_result = dcb->funcs->get_gpio_pin_info(dcb, | |
104 | hpd_info.hpd_int_gpio_uid, &pin_info); | |
105 | ||
106 | if (bp_result != BP_RESULT_OK) { | |
107 | ASSERT(bp_result == BP_RESULT_NORECORD); | |
108 | return NULL; | |
109 | } | |
110 | ||
111 | return dal_gpio_service_create_irq( | |
87401969 | 112 | gpio_service, |
4562236b HW |
113 | pin_info.offset, |
114 | pin_info.mask); | |
115 | } | |
116 | ||
117 | /* | |
118 | * Function: program_hpd_filter | |
119 | * | |
120 | * @brief | |
121 | * Programs HPD filter on associated HPD line | |
122 | * | |
123 | * @param [in] delay_on_connect_in_ms: Connect filter timeout | |
124 | * @param [in] delay_on_disconnect_in_ms: Disconnect filter timeout | |
125 | * | |
126 | * @return | |
127 | * true on success, false otherwise | |
128 | */ | |
129 | static bool program_hpd_filter( | |
b0c4e977 | 130 | const struct dc_link *link) |
4562236b HW |
131 | { |
132 | bool result = false; | |
133 | ||
134 | struct gpio *hpd; | |
135 | ||
136 | int delay_on_connect_in_ms = 0; | |
137 | int delay_on_disconnect_in_ms = 0; | |
138 | ||
9a6995ce JB |
139 | if (link->is_hpd_filter_disabled) |
140 | return false; | |
4562236b | 141 | /* Verify feature is supported */ |
d0778ebf | 142 | switch (link->connector_signal) { |
4562236b HW |
143 | case SIGNAL_TYPE_DVI_SINGLE_LINK: |
144 | case SIGNAL_TYPE_DVI_DUAL_LINK: | |
145 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
146 | /* Program hpd filter */ | |
147 | delay_on_connect_in_ms = 500; | |
b0c4e977 | 148 | delay_on_disconnect_in_ms = 100; |
4562236b HW |
149 | break; |
150 | case SIGNAL_TYPE_DISPLAY_PORT: | |
151 | case SIGNAL_TYPE_DISPLAY_PORT_MST: | |
152 | /* Program hpd filter to allow DP signal to settle */ | |
153 | /* 500: not able to detect MST <-> SST switch as HPD is low for | |
154 | * only 100ms on DELL U2413 | |
155 | * 0: some passive dongle still show aux mode instead of i2c | |
156 | * 20-50:not enough to hide bouncing HPD with passive dongle. | |
157 | * also see intermittent i2c read issues. | |
158 | */ | |
159 | delay_on_connect_in_ms = 80; | |
160 | delay_on_disconnect_in_ms = 0; | |
161 | break; | |
162 | case SIGNAL_TYPE_LVDS: | |
163 | case SIGNAL_TYPE_EDP: | |
164 | default: | |
165 | /* Don't program hpd filter */ | |
166 | return false; | |
167 | } | |
168 | ||
169 | /* Obtain HPD handle */ | |
87401969 | 170 | hpd = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); |
4562236b HW |
171 | |
172 | if (!hpd) | |
173 | return result; | |
174 | ||
175 | /* Setup HPD filtering */ | |
176 | if (dal_gpio_open(hpd, GPIO_MODE_INTERRUPT) == GPIO_RESULT_OK) { | |
177 | struct gpio_hpd_config config; | |
178 | ||
179 | config.delay_on_connect = delay_on_connect_in_ms; | |
180 | config.delay_on_disconnect = delay_on_disconnect_in_ms; | |
181 | ||
182 | dal_irq_setup_hpd_filter(hpd, &config); | |
183 | ||
184 | dal_gpio_close(hpd); | |
185 | ||
186 | result = true; | |
187 | } else { | |
188 | ASSERT_CRITICAL(false); | |
189 | } | |
190 | ||
191 | /* Release HPD handle */ | |
192 | dal_gpio_destroy_irq(&hpd); | |
193 | ||
194 | return result; | |
195 | } | |
196 | ||
2119aa17 DF |
197 | /** |
198 | * dc_link_detect_sink() - Determine if there is a sink connected | |
199 | * | |
200 | * @type: Returned connection type | |
201 | * Does not detect downstream devices, such as MST sinks | |
202 | * or display connected through active dongles | |
203 | */ | |
fbbdadf2 | 204 | bool dc_link_detect_sink(struct dc_link *link, enum dc_connection_type *type) |
4562236b HW |
205 | { |
206 | uint32_t is_hpd_high = 0; | |
207 | struct gpio *hpd_pin; | |
208 | ||
11c3ee48 AD |
209 | if (link->connector_signal == SIGNAL_TYPE_LVDS) { |
210 | *type = dc_connection_single; | |
211 | return true; | |
212 | } | |
213 | ||
0a6414e7 DZ |
214 | if (link->connector_signal == SIGNAL_TYPE_EDP) |
215 | link->dc->hwss.edp_wait_for_hpd_ready(link, true); | |
216 | ||
4562236b | 217 | /* todo: may need to lock gpio access */ |
87401969 | 218 | hpd_pin = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); |
4562236b HW |
219 | if (hpd_pin == NULL) |
220 | goto hpd_gpio_failure; | |
221 | ||
222 | dal_gpio_open(hpd_pin, GPIO_MODE_INTERRUPT); | |
223 | dal_gpio_get_value(hpd_pin, &is_hpd_high); | |
224 | dal_gpio_close(hpd_pin); | |
225 | dal_gpio_destroy_irq(&hpd_pin); | |
226 | ||
227 | if (is_hpd_high) { | |
228 | *type = dc_connection_single; | |
229 | /* TODO: need to do the actual detection */ | |
230 | } else { | |
231 | *type = dc_connection_none; | |
232 | } | |
233 | ||
234 | return true; | |
235 | ||
236 | hpd_gpio_failure: | |
237 | return false; | |
238 | } | |
239 | ||
44858055 | 240 | static enum ddc_transaction_type get_ddc_transaction_type( |
4562236b HW |
241 | enum signal_type sink_signal) |
242 | { | |
243 | enum ddc_transaction_type transaction_type = DDC_TRANSACTION_TYPE_NONE; | |
244 | ||
245 | switch (sink_signal) { | |
246 | case SIGNAL_TYPE_DVI_SINGLE_LINK: | |
247 | case SIGNAL_TYPE_DVI_DUAL_LINK: | |
248 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
249 | case SIGNAL_TYPE_LVDS: | |
250 | case SIGNAL_TYPE_RGB: | |
251 | transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
252 | break; | |
253 | ||
254 | case SIGNAL_TYPE_DISPLAY_PORT: | |
255 | case SIGNAL_TYPE_EDP: | |
256 | transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; | |
257 | break; | |
258 | ||
259 | case SIGNAL_TYPE_DISPLAY_PORT_MST: | |
260 | /* MST does not use I2COverAux, but there is the | |
261 | * SPECIAL use case for "immediate dwnstrm device | |
262 | * access" (EPR#370830). */ | |
263 | transaction_type = DDC_TRANSACTION_TYPE_I2C_OVER_AUX; | |
264 | break; | |
265 | ||
266 | default: | |
267 | break; | |
268 | } | |
269 | ||
270 | return transaction_type; | |
271 | } | |
272 | ||
273 | static enum signal_type get_basic_signal_type( | |
274 | struct graphics_object_id encoder, | |
275 | struct graphics_object_id downstream) | |
276 | { | |
277 | if (downstream.type == OBJECT_TYPE_CONNECTOR) { | |
278 | switch (downstream.id) { | |
279 | case CONNECTOR_ID_SINGLE_LINK_DVII: | |
280 | switch (encoder.id) { | |
281 | case ENCODER_ID_INTERNAL_DAC1: | |
282 | case ENCODER_ID_INTERNAL_KLDSCP_DAC1: | |
283 | case ENCODER_ID_INTERNAL_DAC2: | |
284 | case ENCODER_ID_INTERNAL_KLDSCP_DAC2: | |
285 | return SIGNAL_TYPE_RGB; | |
286 | default: | |
287 | return SIGNAL_TYPE_DVI_SINGLE_LINK; | |
288 | } | |
289 | break; | |
290 | case CONNECTOR_ID_DUAL_LINK_DVII: | |
291 | { | |
292 | switch (encoder.id) { | |
293 | case ENCODER_ID_INTERNAL_DAC1: | |
294 | case ENCODER_ID_INTERNAL_KLDSCP_DAC1: | |
295 | case ENCODER_ID_INTERNAL_DAC2: | |
296 | case ENCODER_ID_INTERNAL_KLDSCP_DAC2: | |
297 | return SIGNAL_TYPE_RGB; | |
298 | default: | |
299 | return SIGNAL_TYPE_DVI_DUAL_LINK; | |
300 | } | |
301 | } | |
302 | break; | |
303 | case CONNECTOR_ID_SINGLE_LINK_DVID: | |
304 | return SIGNAL_TYPE_DVI_SINGLE_LINK; | |
305 | case CONNECTOR_ID_DUAL_LINK_DVID: | |
306 | return SIGNAL_TYPE_DVI_DUAL_LINK; | |
307 | case CONNECTOR_ID_VGA: | |
308 | return SIGNAL_TYPE_RGB; | |
309 | case CONNECTOR_ID_HDMI_TYPE_A: | |
310 | return SIGNAL_TYPE_HDMI_TYPE_A; | |
311 | case CONNECTOR_ID_LVDS: | |
312 | return SIGNAL_TYPE_LVDS; | |
313 | case CONNECTOR_ID_DISPLAY_PORT: | |
314 | return SIGNAL_TYPE_DISPLAY_PORT; | |
315 | case CONNECTOR_ID_EDP: | |
316 | return SIGNAL_TYPE_EDP; | |
317 | default: | |
318 | return SIGNAL_TYPE_NONE; | |
319 | } | |
320 | } else if (downstream.type == OBJECT_TYPE_ENCODER) { | |
321 | switch (downstream.id) { | |
322 | case ENCODER_ID_EXTERNAL_NUTMEG: | |
323 | case ENCODER_ID_EXTERNAL_TRAVIS: | |
324 | return SIGNAL_TYPE_DISPLAY_PORT; | |
325 | default: | |
326 | return SIGNAL_TYPE_NONE; | |
327 | } | |
328 | } | |
329 | ||
330 | return SIGNAL_TYPE_NONE; | |
331 | } | |
332 | ||
2119aa17 DF |
333 | /** |
334 | * dc_link_is_dp_sink_present() - Check if there is a native DP | |
335 | * or passive DP-HDMI dongle connected | |
4562236b | 336 | */ |
aac5db82 | 337 | bool dc_link_is_dp_sink_present(struct dc_link *link) |
4562236b HW |
338 | { |
339 | enum gpio_result gpio_result; | |
340 | uint32_t clock_pin = 0; | |
bd4905a9 | 341 | uint8_t retry = 0; |
4562236b HW |
342 | struct ddc *ddc; |
343 | ||
344 | enum connector_id connector_id = | |
345 | dal_graphics_object_id_get_connector_id(link->link_id); | |
346 | ||
347 | bool present = | |
348 | ((connector_id == CONNECTOR_ID_DISPLAY_PORT) || | |
349 | (connector_id == CONNECTOR_ID_EDP)); | |
350 | ||
d0778ebf | 351 | ddc = dal_ddc_service_get_ddc_pin(link->ddc); |
4562236b HW |
352 | |
353 | if (!ddc) { | |
354 | BREAK_TO_DEBUGGER(); | |
355 | return present; | |
356 | } | |
357 | ||
358 | /* Open GPIO and set it to I2C mode */ | |
359 | /* Note: this GpioMode_Input will be converted | |
360 | * to GpioConfigType_I2cAuxDualMode in GPIO component, | |
361 | * which indicates we need additional delay */ | |
362 | ||
363 | if (GPIO_RESULT_OK != dal_ddc_open( | |
364 | ddc, GPIO_MODE_INPUT, GPIO_DDC_CONFIG_TYPE_MODE_I2C)) { | |
365 | dal_gpio_destroy_ddc(&ddc); | |
366 | ||
367 | return present; | |
368 | } | |
369 | ||
bd4905a9 PH |
370 | /* |
371 | * Read GPIO: DP sink is present if both clock and data pins are zero | |
372 | * | |
373 | * [W/A] plug-unplug DP cable, sometimes customer board has | |
374 | * one short pulse on clk_pin(1V, < 1ms). DP will be config to HDMI/DVI | |
375 | * then monitor can't br light up. Add retry 3 times | |
376 | * But in real passive dongle, it need additional 3ms to detect | |
377 | */ | |
378 | do { | |
379 | gpio_result = dal_gpio_get_value(ddc->pin_clock, &clock_pin); | |
380 | ASSERT(gpio_result == GPIO_RESULT_OK); | |
381 | if (clock_pin) | |
382 | udelay(1000); | |
383 | else | |
384 | break; | |
385 | } while (retry++ < 3); | |
4562236b | 386 | |
4dfb0bad | 387 | present = (gpio_result == GPIO_RESULT_OK) && !clock_pin; |
4562236b HW |
388 | |
389 | dal_ddc_close(ddc); | |
390 | ||
391 | return present; | |
392 | } | |
393 | ||
394 | /* | |
395 | * @brief | |
396 | * Detect output sink type | |
397 | */ | |
8f38b66c HW |
398 | static enum signal_type link_detect_sink( |
399 | struct dc_link *link, | |
400 | enum dc_detect_reason reason) | |
4562236b HW |
401 | { |
402 | enum signal_type result = get_basic_signal_type( | |
403 | link->link_enc->id, link->link_id); | |
404 | ||
405 | /* Internal digital encoder will detect only dongles | |
406 | * that require digital signal */ | |
407 | ||
408 | /* Detection mechanism is different | |
409 | * for different native connectors. | |
410 | * LVDS connector supports only LVDS signal; | |
411 | * PCIE is a bus slot, the actual connector needs to be detected first; | |
412 | * eDP connector supports only eDP signal; | |
413 | * HDMI should check straps for audio */ | |
414 | ||
415 | /* PCIE detects the actual connector on add-on board */ | |
416 | ||
417 | if (link->link_id.id == CONNECTOR_ID_PCIE) { | |
418 | /* ZAZTODO implement PCIE add-on card detection */ | |
419 | } | |
420 | ||
421 | switch (link->link_id.id) { | |
422 | case CONNECTOR_ID_HDMI_TYPE_A: { | |
423 | /* check audio support: | |
424 | * if native HDMI is not supported, switch to DVI */ | |
425 | struct audio_support *aud_support = &link->dc->res_pool->audio_support; | |
426 | ||
427 | if (!aud_support->hdmi_audio_native) | |
428 | if (link->link_id.id == CONNECTOR_ID_HDMI_TYPE_A) | |
429 | result = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
430 | } | |
431 | break; | |
432 | case CONNECTOR_ID_DISPLAY_PORT: { | |
8f38b66c HW |
433 | /* DP HPD short pulse. Passive DP dongle will not |
434 | * have short pulse | |
435 | */ | |
436 | if (reason != DETECT_REASON_HPDRX) { | |
437 | /* Check whether DP signal detected: if not - | |
438 | * we assume signal is DVI; it could be corrected | |
439 | * to HDMI after dongle detection | |
440 | */ | |
aac5db82 | 441 | if (!dm_helpers_is_dp_sink_present(link)) |
8f38b66c HW |
442 | result = SIGNAL_TYPE_DVI_SINGLE_LINK; |
443 | } | |
4562236b HW |
444 | } |
445 | break; | |
446 | default: | |
447 | break; | |
448 | } | |
449 | ||
450 | return result; | |
451 | } | |
452 | ||
453 | static enum signal_type decide_signal_from_strap_and_dongle_type( | |
454 | enum display_dongle_type dongle_type, | |
455 | struct audio_support *audio_support) | |
456 | { | |
457 | enum signal_type signal = SIGNAL_TYPE_NONE; | |
458 | ||
459 | switch (dongle_type) { | |
460 | case DISPLAY_DONGLE_DP_HDMI_DONGLE: | |
461 | if (audio_support->hdmi_audio_on_dongle) | |
462 | signal = SIGNAL_TYPE_HDMI_TYPE_A; | |
463 | else | |
464 | signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
465 | break; | |
466 | case DISPLAY_DONGLE_DP_DVI_DONGLE: | |
467 | signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
468 | break; | |
469 | case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE: | |
470 | if (audio_support->hdmi_audio_native) | |
471 | signal = SIGNAL_TYPE_HDMI_TYPE_A; | |
472 | else | |
473 | signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
474 | break; | |
475 | default: | |
476 | signal = SIGNAL_TYPE_NONE; | |
477 | break; | |
478 | } | |
479 | ||
480 | return signal; | |
481 | } | |
482 | ||
483 | static enum signal_type dp_passive_dongle_detection( | |
484 | struct ddc_service *ddc, | |
485 | struct display_sink_capability *sink_cap, | |
486 | struct audio_support *audio_support) | |
487 | { | |
488 | dal_ddc_service_i2c_query_dp_dual_mode_adaptor( | |
489 | ddc, sink_cap); | |
490 | return decide_signal_from_strap_and_dongle_type( | |
491 | sink_cap->dongle_type, | |
492 | audio_support); | |
493 | } | |
494 | ||
d0778ebf | 495 | static void link_disconnect_sink(struct dc_link *link) |
4562236b | 496 | { |
d0778ebf HW |
497 | if (link->local_sink) { |
498 | dc_sink_release(link->local_sink); | |
499 | link->local_sink = NULL; | |
4562236b HW |
500 | } |
501 | ||
502 | link->dpcd_sink_count = 0; | |
503 | } | |
504 | ||
eb815442 ST |
505 | static void link_disconnect_remap(struct dc_sink *prev_sink, struct dc_link *link) |
506 | { | |
507 | dc_sink_release(link->local_sink); | |
508 | link->local_sink = prev_sink; | |
509 | } | |
510 | ||
511 | ||
cdb39798 | 512 | static bool detect_dp( |
d0778ebf | 513 | struct dc_link *link, |
4562236b HW |
514 | struct display_sink_capability *sink_caps, |
515 | bool *converter_disable_audio, | |
516 | struct audio_support *audio_support, | |
8f38b66c | 517 | enum dc_detect_reason reason) |
4562236b | 518 | { |
8f38b66c HW |
519 | bool boot = false; |
520 | sink_caps->signal = link_detect_sink(link, reason); | |
4562236b HW |
521 | sink_caps->transaction_type = |
522 | get_ddc_transaction_type(sink_caps->signal); | |
523 | ||
524 | if (sink_caps->transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { | |
525 | sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT; | |
cdb39798 YS |
526 | if (!detect_dp_sink_caps(link)) |
527 | return false; | |
4562236b | 528 | |
4562236b HW |
529 | if (is_mst_supported(link)) { |
530 | sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT_MST; | |
e4ba6335 | 531 | link->type = dc_connection_mst_branch; |
4562236b | 532 | |
0a145446 EY |
533 | dal_ddc_service_set_transaction_type( |
534 | link->ddc, | |
535 | sink_caps->transaction_type); | |
536 | ||
4562236b HW |
537 | /* |
538 | * This call will initiate MST topology discovery. Which | |
539 | * will detect MST ports and add new DRM connector DRM | |
540 | * framework. Then read EDID via remote i2c over aux. In | |
541 | * the end, will notify DRM detect result and save EDID | |
542 | * into DRM framework. | |
543 | * | |
544 | * .detect is called by .fill_modes. | |
545 | * .fill_modes is called by user mode ioctl | |
546 | * DRM_IOCTL_MODE_GETCONNECTOR. | |
547 | * | |
548 | * .get_modes is called by .fill_modes. | |
549 | * | |
550 | * call .get_modes, AMDGPU DM implementation will create | |
551 | * new dc_sink and add to dc_link. For long HPD plug | |
552 | * in/out, MST has its own handle. | |
553 | * | |
554 | * Therefore, just after dc_create, link->sink is not | |
555 | * created for MST until user mode app calls | |
556 | * DRM_IOCTL_MODE_GETCONNECTOR. | |
557 | * | |
558 | * Need check ->sink usages in case ->sink = NULL | |
559 | * TODO: s3 resume check | |
560 | */ | |
8f38b66c HW |
561 | if (reason == DETECT_REASON_BOOT) |
562 | boot = true; | |
4562236b | 563 | |
2068afe6 NC |
564 | dm_helpers_dp_update_branch_info( |
565 | link->ctx, | |
566 | link); | |
567 | ||
e4ba6335 | 568 | if (!dm_helpers_dp_mst_start_top_mgr( |
4562236b | 569 | link->ctx, |
d0778ebf | 570 | link, boot)) { |
4562236b | 571 | /* MST not supported */ |
e4ba6335 | 572 | link->type = dc_connection_single; |
4562236b HW |
573 | sink_caps->signal = SIGNAL_TYPE_DISPLAY_PORT; |
574 | } | |
575 | } | |
58fe8990 HW |
576 | |
577 | if (link->type != dc_connection_mst_branch && | |
578 | is_dp_active_dongle(link)) { | |
579 | /* DP active dongles */ | |
580 | link->type = dc_connection_active_dongle; | |
581 | if (!link->dpcd_caps.sink_count.bits.SINK_COUNT) { | |
582 | /* | |
583 | * active dongle unplug processing for short irq | |
584 | */ | |
585 | link_disconnect_sink(link); | |
cdb39798 | 586 | return true; |
58fe8990 HW |
587 | } |
588 | ||
589 | if (link->dpcd_caps.dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER) | |
590 | *converter_disable_audio = true; | |
591 | } | |
4562236b HW |
592 | } else { |
593 | /* DP passive dongles */ | |
d0778ebf | 594 | sink_caps->signal = dp_passive_dongle_detection(link->ddc, |
4562236b HW |
595 | sink_caps, |
596 | audio_support); | |
597 | } | |
cdb39798 YS |
598 | |
599 | return true; | |
4562236b HW |
600 | } |
601 | ||
eb815442 ST |
602 | static bool is_same_edid(struct dc_edid *old_edid, struct dc_edid *new_edid) |
603 | { | |
604 | if (old_edid->length != new_edid->length) | |
605 | return false; | |
606 | ||
607 | if (new_edid->length == 0) | |
608 | return false; | |
609 | ||
610 | return (memcmp(old_edid->raw_edid, new_edid->raw_edid, new_edid->length) == 0); | |
611 | } | |
612 | ||
2119aa17 DF |
613 | /** |
614 | * dc_link_detect() - Detect if a sink is attached to a given link | |
615 | * | |
616 | * link->local_sink is created or destroyed as needed. | |
617 | * | |
618 | * This does not create remote sinks but will trigger DM | |
619 | * to start MST detection if a branch is detected. | |
620 | */ | |
8f38b66c | 621 | bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) |
4562236b | 622 | { |
4562236b HW |
623 | struct dc_sink_init_data sink_init_data = { 0 }; |
624 | struct display_sink_capability sink_caps = { 0 }; | |
625 | uint8_t i; | |
626 | bool converter_disable_audio = false; | |
627 | struct audio_support *aud_support = &link->dc->res_pool->audio_support; | |
eb815442 | 628 | bool same_edid = false; |
4562236b HW |
629 | enum dc_edid_status edid_status; |
630 | struct dc_context *dc_ctx = link->ctx; | |
533ed6c7 | 631 | struct dc_sink *sink = NULL; |
eb815442 ST |
632 | struct dc_sink *prev_sink = NULL; |
633 | struct dpcd_caps prev_dpcd_caps; | |
634 | bool same_dpcd = true; | |
4562236b | 635 | enum dc_connection_type new_connection_type = dc_connection_none; |
5d4b05dd | 636 | DC_LOGGER_INIT(link->ctx->logger); |
d0778ebf | 637 | if (link->connector_signal == SIGNAL_TYPE_VIRTUAL) |
4562236b HW |
638 | return false; |
639 | ||
fbbdadf2 | 640 | if (false == dc_link_detect_sink(link, &new_connection_type)) { |
4562236b HW |
641 | BREAK_TO_DEBUGGER(); |
642 | return false; | |
643 | } | |
644 | ||
d0778ebf HW |
645 | if (link->connector_signal == SIGNAL_TYPE_EDP && |
646 | link->local_sink) | |
4562236b HW |
647 | return true; |
648 | ||
11c3ee48 AD |
649 | if (link->connector_signal == SIGNAL_TYPE_LVDS && |
650 | link->local_sink) | |
651 | return true; | |
652 | ||
eb815442 ST |
653 | prev_sink = link->local_sink; |
654 | if (prev_sink != NULL) { | |
655 | dc_sink_retain(prev_sink); | |
656 | memcpy(&prev_dpcd_caps, &link->dpcd_caps, sizeof(struct dpcd_caps)); | |
657 | } | |
dcf298c3 WL |
658 | link_disconnect_sink(link); |
659 | ||
4562236b | 660 | if (new_connection_type != dc_connection_none) { |
d0778ebf | 661 | link->type = new_connection_type; |
4562236b HW |
662 | |
663 | /* From Disconnected-to-Connected. */ | |
d0778ebf | 664 | switch (link->connector_signal) { |
4562236b HW |
665 | case SIGNAL_TYPE_HDMI_TYPE_A: { |
666 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
667 | if (aud_support->hdmi_audio_native) | |
668 | sink_caps.signal = SIGNAL_TYPE_HDMI_TYPE_A; | |
669 | else | |
670 | sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
671 | break; | |
672 | } | |
673 | ||
674 | case SIGNAL_TYPE_DVI_SINGLE_LINK: { | |
675 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
676 | sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
677 | break; | |
678 | } | |
679 | ||
680 | case SIGNAL_TYPE_DVI_DUAL_LINK: { | |
681 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
682 | sink_caps.signal = SIGNAL_TYPE_DVI_DUAL_LINK; | |
683 | break; | |
684 | } | |
685 | ||
11c3ee48 AD |
686 | case SIGNAL_TYPE_LVDS: { |
687 | sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C; | |
688 | sink_caps.signal = SIGNAL_TYPE_LVDS; | |
689 | break; | |
690 | } | |
691 | ||
4562236b | 692 | case SIGNAL_TYPE_EDP: { |
4654a2f7 | 693 | detect_edp_sink_caps(link); |
4562236b HW |
694 | sink_caps.transaction_type = |
695 | DDC_TRANSACTION_TYPE_I2C_OVER_AUX; | |
696 | sink_caps.signal = SIGNAL_TYPE_EDP; | |
697 | break; | |
698 | } | |
699 | ||
700 | case SIGNAL_TYPE_DISPLAY_PORT: { | |
cdb39798 | 701 | if (!detect_dp( |
4562236b HW |
702 | link, |
703 | &sink_caps, | |
704 | &converter_disable_audio, | |
eb815442 ST |
705 | aud_support, reason)) { |
706 | if (prev_sink != NULL) | |
707 | dc_sink_release(prev_sink); | |
cdb39798 | 708 | return false; |
eb815442 | 709 | } |
4562236b | 710 | |
eb815442 ST |
711 | // Check if dpcp block is the same |
712 | if (prev_sink != NULL) { | |
713 | if (memcmp(&link->dpcd_caps, &prev_dpcd_caps, sizeof(struct dpcd_caps))) | |
714 | same_dpcd = false; | |
715 | } | |
80adaebd | 716 | /* Active dongle plug in without display or downstream unplug*/ |
d0778ebf | 717 | if (link->type == dc_connection_active_dongle |
4562236b | 718 | && link->dpcd_caps.sink_count. |
eb815442 | 719 | bits.SINK_COUNT == 0) { |
80adaebd EY |
720 | if (prev_sink != NULL) { |
721 | /* Downstream unplug */ | |
eb815442 | 722 | dc_sink_release(prev_sink); |
80adaebd EY |
723 | } else { |
724 | /* Empty dongle plug in */ | |
725 | for (i = 0; i < LINK_TRAINING_MAX_VERIFY_RETRY; i++) { | |
726 | int fail_count = 0; | |
727 | ||
728 | dp_verify_link_cap(link, | |
729 | &link->reported_link_cap, | |
730 | &fail_count); | |
731 | ||
732 | if (fail_count == 0) | |
733 | break; | |
734 | } | |
735 | } | |
4562236b | 736 | return true; |
eb815442 | 737 | } |
4562236b | 738 | |
d0778ebf | 739 | if (link->type == dc_connection_mst_branch) { |
4562236b | 740 | LINK_INFO("link=%d, mst branch is now Connected\n", |
d0778ebf | 741 | link->link_index); |
3f1f74f4 JZ |
742 | /* Need to setup mst link_cap struct here |
743 | * otherwise dc_link_detect() will leave mst link_cap | |
744 | * empty which leads to allocate_mst_payload() has "0" | |
eb0e5154 | 745 | * pbn_per_slot value leading to exception on dc_fixpt_div() |
3f1f74f4 JZ |
746 | */ |
747 | link->verified_link_cap = link->reported_link_cap; | |
eb815442 ST |
748 | if (prev_sink != NULL) |
749 | dc_sink_release(prev_sink); | |
4562236b HW |
750 | return false; |
751 | } | |
752 | ||
753 | break; | |
754 | } | |
755 | ||
756 | default: | |
757 | DC_ERROR("Invalid connector type! signal:%d\n", | |
d0778ebf | 758 | link->connector_signal); |
eb815442 ST |
759 | if (prev_sink != NULL) |
760 | dc_sink_release(prev_sink); | |
4562236b HW |
761 | return false; |
762 | } /* switch() */ | |
763 | ||
764 | if (link->dpcd_caps.sink_count.bits.SINK_COUNT) | |
765 | link->dpcd_sink_count = link->dpcd_caps.sink_count. | |
766 | bits.SINK_COUNT; | |
2a0b4d85 MT |
767 | else |
768 | link->dpcd_sink_count = 1; | |
4562236b HW |
769 | |
770 | dal_ddc_service_set_transaction_type( | |
d0778ebf | 771 | link->ddc, |
4562236b HW |
772 | sink_caps.transaction_type); |
773 | ||
d0778ebf HW |
774 | link->aux_mode = dal_ddc_service_is_in_aux_transaction_mode( |
775 | link->ddc); | |
7c7f5b15 | 776 | |
d0778ebf | 777 | sink_init_data.link = link; |
4562236b | 778 | sink_init_data.sink_signal = sink_caps.signal; |
4562236b | 779 | |
7d58e721 MT |
780 | sink = dc_sink_create(&sink_init_data); |
781 | if (!sink) { | |
782 | DC_ERROR("Failed to create sink!\n"); | |
eb815442 ST |
783 | if (prev_sink != NULL) |
784 | dc_sink_release(prev_sink); | |
7d58e721 MT |
785 | return false; |
786 | } | |
4562236b | 787 | |
ceb3dbb4 | 788 | sink->link->dongle_max_pix_clk = sink_caps.max_hdmi_pixel_clock; |
dcf298c3 | 789 | sink->converter_disable_audio = converter_disable_audio; |
7d58e721 | 790 | |
dcf298c3 | 791 | link->local_sink = sink; |
4562236b | 792 | |
dcf298c3 | 793 | edid_status = dm_helpers_read_local_edid( |
7c7f5b15 | 794 | link->ctx, |
d0778ebf | 795 | link, |
b73a22d3 | 796 | sink); |
4562236b HW |
797 | |
798 | switch (edid_status) { | |
799 | case EDID_BAD_CHECKSUM: | |
1296423b | 800 | DC_LOG_ERROR("EDID checksum invalid.\n"); |
4562236b HW |
801 | break; |
802 | case EDID_NO_RESPONSE: | |
1296423b | 803 | DC_LOG_ERROR("No EDID read.\n"); |
01dc285d HW |
804 | |
805 | /* | |
806 | * Abort detection for non-DP connectors if we have | |
807 | * no EDID | |
808 | * | |
809 | * DP needs to report as connected if HDP is high | |
810 | * even if we have no EDID in order to go to | |
811 | * fail-safe mode | |
812 | */ | |
16196776 | 813 | if (dc_is_hdmi_signal(link->connector_signal) || |
219097df S |
814 | dc_is_dvi_signal(link->connector_signal)) { |
815 | if (prev_sink != NULL) | |
816 | dc_sink_release(prev_sink); | |
817 | ||
01dc285d | 818 | return false; |
219097df | 819 | } |
4562236b HW |
820 | default: |
821 | break; | |
822 | } | |
823 | ||
eb815442 ST |
824 | // Check if edid is the same |
825 | if ((prev_sink != NULL) && ((edid_status == EDID_THE_SAME) || (edid_status == EDID_OK))) | |
826 | same_edid = is_same_edid(&prev_sink->dc_edid, &sink->dc_edid); | |
827 | ||
0301ccba | 828 | if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && |
829 | sink_caps.transaction_type == DDC_TRANSACTION_TYPE_I2C_OVER_AUX && | |
830 | reason != DETECT_REASON_HPDRX) { | |
831 | /* | |
832 | * TODO debug why Dell 2413 doesn't like | |
833 | * two link trainings | |
834 | */ | |
3f1f74f4 | 835 | |
0301ccba | 836 | /* deal with non-mst cases */ |
837 | for (i = 0; i < LINK_TRAINING_MAX_VERIFY_RETRY; i++) { | |
838 | int fail_count = 0; | |
824474ba | 839 | |
0301ccba | 840 | dp_verify_link_cap(link, |
841 | &link->reported_link_cap, | |
842 | &fail_count); | |
824474ba | 843 | |
0301ccba | 844 | if (fail_count == 0) |
845 | break; | |
eb815442 | 846 | } |
f334073a | 847 | |
0301ccba | 848 | } else { |
849 | // If edid is the same, then discard new sink and revert back to original sink | |
850 | if (same_edid) { | |
851 | link_disconnect_remap(prev_sink, link); | |
852 | sink = prev_sink; | |
853 | prev_sink = NULL; | |
854 | ||
855 | } | |
eb815442 | 856 | } |
4562236b | 857 | |
0301ccba | 858 | /* HDMI-DVI Dongle */ |
859 | if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A && | |
860 | !sink->edid_caps.edid_hdmi) | |
861 | sink->sink_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | |
862 | ||
4562236b | 863 | /* Connectivity log: detection */ |
b73a22d3 | 864 | for (i = 0; i < sink->dc_edid.length / EDID_BLOCK_SIZE; i++) { |
4562236b | 865 | CONN_DATA_DETECT(link, |
b73a22d3 | 866 | &sink->dc_edid.raw_edid[i * EDID_BLOCK_SIZE], |
4562236b | 867 | EDID_BLOCK_SIZE, |
b73a22d3 | 868 | "%s: [Block %d] ", sink->edid_caps.display_name, i); |
4562236b HW |
869 | } |
870 | ||
1296423b | 871 | DC_LOG_DETECTION_EDID_PARSER("%s: " |
4562236b HW |
872 | "manufacturer_id = %X, " |
873 | "product_id = %X, " | |
874 | "serial_number = %X, " | |
875 | "manufacture_week = %d, " | |
876 | "manufacture_year = %d, " | |
877 | "display_name = %s, " | |
878 | "speaker_flag = %d, " | |
879 | "audio_mode_count = %d\n", | |
880 | __func__, | |
b73a22d3 HW |
881 | sink->edid_caps.manufacturer_id, |
882 | sink->edid_caps.product_id, | |
883 | sink->edid_caps.serial_number, | |
884 | sink->edid_caps.manufacture_week, | |
885 | sink->edid_caps.manufacture_year, | |
886 | sink->edid_caps.display_name, | |
887 | sink->edid_caps.speaker_flags, | |
888 | sink->edid_caps.audio_mode_count); | |
889 | ||
890 | for (i = 0; i < sink->edid_caps.audio_mode_count; i++) { | |
1296423b | 891 | DC_LOG_DETECTION_EDID_PARSER("%s: mode number = %d, " |
4562236b HW |
892 | "format_code = %d, " |
893 | "channel_count = %d, " | |
894 | "sample_rate = %d, " | |
895 | "sample_size = %d\n", | |
896 | __func__, | |
897 | i, | |
b73a22d3 HW |
898 | sink->edid_caps.audio_modes[i].format_code, |
899 | sink->edid_caps.audio_modes[i].channel_count, | |
900 | sink->edid_caps.audio_modes[i].sample_rate, | |
901 | sink->edid_caps.audio_modes[i].sample_size); | |
4562236b HW |
902 | } |
903 | ||
904 | } else { | |
905 | /* From Connected-to-Disconnected. */ | |
d0778ebf | 906 | if (link->type == dc_connection_mst_branch) { |
4562236b | 907 | LINK_INFO("link=%d, mst branch is now Disconnected\n", |
d0778ebf | 908 | link->link_index); |
dcf298c3 | 909 | |
d0778ebf | 910 | dm_helpers_dp_mst_stop_top_mgr(link->ctx, link); |
4562236b HW |
911 | |
912 | link->mst_stream_alloc_table.stream_count = 0; | |
913 | memset(link->mst_stream_alloc_table.stream_allocations, 0, sizeof(link->mst_stream_alloc_table.stream_allocations)); | |
914 | } | |
915 | ||
dcf298c3 WL |
916 | link->type = dc_connection_none; |
917 | sink_caps.signal = SIGNAL_TYPE_NONE; | |
4562236b HW |
918 | } |
919 | ||
eb815442 | 920 | LINK_INFO("link=%d, dc_sink_in=%p is now %s prev_sink=%p dpcd same=%d edid same=%d\n", |
b73a22d3 | 921 | link->link_index, sink, |
4562236b | 922 | (sink_caps.signal == SIGNAL_TYPE_NONE ? |
eb815442 ST |
923 | "Disconnected":"Connected"), prev_sink, |
924 | same_dpcd, same_edid); | |
925 | ||
926 | if (prev_sink != NULL) | |
927 | dc_sink_release(prev_sink); | |
4562236b HW |
928 | |
929 | return true; | |
930 | } | |
931 | ||
16f4c695 CH |
932 | bool dc_link_get_hpd_state(struct dc_link *dc_link) |
933 | { | |
934 | struct gpio *hpd_pin; | |
935 | uint32_t state; | |
936 | ||
937 | hpd_pin = get_hpd_gpio(dc_link->ctx->dc_bios, | |
938 | dc_link->link_id, dc_link->ctx->gpio_service); | |
939 | if (hpd_pin == NULL) | |
940 | ASSERT(false); | |
941 | ||
942 | dal_gpio_open(hpd_pin, GPIO_MODE_INTERRUPT); | |
943 | dal_gpio_get_value(hpd_pin, &state); | |
944 | dal_gpio_close(hpd_pin); | |
945 | dal_gpio_destroy_irq(&hpd_pin); | |
946 | ||
947 | return state; | |
948 | } | |
949 | ||
4562236b | 950 | static enum hpd_source_id get_hpd_line( |
d0778ebf | 951 | struct dc_link *link) |
4562236b HW |
952 | { |
953 | struct gpio *hpd; | |
954 | enum hpd_source_id hpd_id = HPD_SOURCEID_UNKNOWN; | |
955 | ||
87401969 | 956 | hpd = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); |
4562236b HW |
957 | |
958 | if (hpd) { | |
959 | switch (dal_irq_get_source(hpd)) { | |
960 | case DC_IRQ_SOURCE_HPD1: | |
961 | hpd_id = HPD_SOURCEID1; | |
962 | break; | |
963 | case DC_IRQ_SOURCE_HPD2: | |
964 | hpd_id = HPD_SOURCEID2; | |
965 | break; | |
966 | case DC_IRQ_SOURCE_HPD3: | |
967 | hpd_id = HPD_SOURCEID3; | |
968 | break; | |
969 | case DC_IRQ_SOURCE_HPD4: | |
970 | hpd_id = HPD_SOURCEID4; | |
971 | break; | |
972 | case DC_IRQ_SOURCE_HPD5: | |
973 | hpd_id = HPD_SOURCEID5; | |
974 | break; | |
975 | case DC_IRQ_SOURCE_HPD6: | |
976 | hpd_id = HPD_SOURCEID6; | |
977 | break; | |
978 | default: | |
979 | BREAK_TO_DEBUGGER(); | |
980 | break; | |
981 | } | |
982 | ||
983 | dal_gpio_destroy_irq(&hpd); | |
984 | } | |
985 | ||
986 | return hpd_id; | |
987 | } | |
988 | ||
d0778ebf | 989 | static enum channel_id get_ddc_line(struct dc_link *link) |
4562236b HW |
990 | { |
991 | struct ddc *ddc; | |
992 | enum channel_id channel = CHANNEL_ID_UNKNOWN; | |
993 | ||
d0778ebf | 994 | ddc = dal_ddc_service_get_ddc_pin(link->ddc); |
4562236b HW |
995 | |
996 | if (ddc) { | |
997 | switch (dal_ddc_get_line(ddc)) { | |
998 | case GPIO_DDC_LINE_DDC1: | |
999 | channel = CHANNEL_ID_DDC1; | |
1000 | break; | |
1001 | case GPIO_DDC_LINE_DDC2: | |
1002 | channel = CHANNEL_ID_DDC2; | |
1003 | break; | |
1004 | case GPIO_DDC_LINE_DDC3: | |
1005 | channel = CHANNEL_ID_DDC3; | |
1006 | break; | |
1007 | case GPIO_DDC_LINE_DDC4: | |
1008 | channel = CHANNEL_ID_DDC4; | |
1009 | break; | |
1010 | case GPIO_DDC_LINE_DDC5: | |
1011 | channel = CHANNEL_ID_DDC5; | |
1012 | break; | |
1013 | case GPIO_DDC_LINE_DDC6: | |
1014 | channel = CHANNEL_ID_DDC6; | |
1015 | break; | |
1016 | case GPIO_DDC_LINE_DDC_VGA: | |
1017 | channel = CHANNEL_ID_DDC_VGA; | |
1018 | break; | |
1019 | case GPIO_DDC_LINE_I2C_PAD: | |
1020 | channel = CHANNEL_ID_I2C_PAD; | |
1021 | break; | |
1022 | default: | |
1023 | BREAK_TO_DEBUGGER(); | |
1024 | break; | |
1025 | } | |
1026 | } | |
1027 | ||
1028 | return channel; | |
1029 | } | |
1030 | ||
1031 | static enum transmitter translate_encoder_to_transmitter( | |
1032 | struct graphics_object_id encoder) | |
1033 | { | |
1034 | switch (encoder.id) { | |
1035 | case ENCODER_ID_INTERNAL_UNIPHY: | |
1036 | switch (encoder.enum_id) { | |
1037 | case ENUM_ID_1: | |
1038 | return TRANSMITTER_UNIPHY_A; | |
1039 | case ENUM_ID_2: | |
1040 | return TRANSMITTER_UNIPHY_B; | |
1041 | default: | |
1042 | return TRANSMITTER_UNKNOWN; | |
1043 | } | |
1044 | break; | |
1045 | case ENCODER_ID_INTERNAL_UNIPHY1: | |
1046 | switch (encoder.enum_id) { | |
1047 | case ENUM_ID_1: | |
1048 | return TRANSMITTER_UNIPHY_C; | |
1049 | case ENUM_ID_2: | |
1050 | return TRANSMITTER_UNIPHY_D; | |
1051 | default: | |
1052 | return TRANSMITTER_UNKNOWN; | |
1053 | } | |
1054 | break; | |
1055 | case ENCODER_ID_INTERNAL_UNIPHY2: | |
1056 | switch (encoder.enum_id) { | |
1057 | case ENUM_ID_1: | |
1058 | return TRANSMITTER_UNIPHY_E; | |
1059 | case ENUM_ID_2: | |
1060 | return TRANSMITTER_UNIPHY_F; | |
1061 | default: | |
1062 | return TRANSMITTER_UNKNOWN; | |
1063 | } | |
1064 | break; | |
1065 | case ENCODER_ID_INTERNAL_UNIPHY3: | |
1066 | switch (encoder.enum_id) { | |
1067 | case ENUM_ID_1: | |
1068 | return TRANSMITTER_UNIPHY_G; | |
1069 | default: | |
1070 | return TRANSMITTER_UNKNOWN; | |
1071 | } | |
1072 | break; | |
1073 | case ENCODER_ID_EXTERNAL_NUTMEG: | |
1074 | switch (encoder.enum_id) { | |
1075 | case ENUM_ID_1: | |
1076 | return TRANSMITTER_NUTMEG_CRT; | |
1077 | default: | |
1078 | return TRANSMITTER_UNKNOWN; | |
1079 | } | |
1080 | break; | |
1081 | case ENCODER_ID_EXTERNAL_TRAVIS: | |
1082 | switch (encoder.enum_id) { | |
1083 | case ENUM_ID_1: | |
1084 | return TRANSMITTER_TRAVIS_CRT; | |
1085 | case ENUM_ID_2: | |
1086 | return TRANSMITTER_TRAVIS_LCD; | |
1087 | default: | |
1088 | return TRANSMITTER_UNKNOWN; | |
1089 | } | |
1090 | break; | |
1091 | default: | |
1092 | return TRANSMITTER_UNKNOWN; | |
1093 | } | |
1094 | } | |
1095 | ||
1096 | static bool construct( | |
d0778ebf | 1097 | struct dc_link *link, |
4562236b HW |
1098 | const struct link_init_data *init_params) |
1099 | { | |
1100 | uint8_t i; | |
1101 | struct gpio *hpd_gpio = NULL; | |
c2e218dd | 1102 | struct ddc_service_init_data ddc_service_init_data = { { 0 } }; |
4562236b HW |
1103 | struct dc_context *dc_ctx = init_params->ctx; |
1104 | struct encoder_init_data enc_init_data = { 0 }; | |
1105 | struct integrated_info info = {{{ 0 }}}; | |
1106 | struct dc_bios *bios = init_params->dc->ctx->dc_bios; | |
1107 | const struct dc_vbios_funcs *bp_funcs = bios->funcs; | |
5d4b05dd | 1108 | DC_LOGGER_INIT(dc_ctx->logger); |
4562236b | 1109 | |
d0778ebf HW |
1110 | link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; |
1111 | link->irq_source_hpd_rx = DC_IRQ_SOURCE_INVALID; | |
4562236b HW |
1112 | |
1113 | link->link_status.dpcd_caps = &link->dpcd_caps; | |
1114 | ||
1115 | link->dc = init_params->dc; | |
1116 | link->ctx = dc_ctx; | |
d0778ebf | 1117 | link->link_index = init_params->link_index; |
4562236b HW |
1118 | |
1119 | link->link_id = bios->funcs->get_connector_id(bios, init_params->connector_index); | |
fb7b11e1 | 1120 | |
4562236b | 1121 | if (link->link_id.type != OBJECT_TYPE_CONNECTOR) { |
63b371ec HW |
1122 | dm_error("%s: Invalid Connector ObjectID from Adapter Service for connector index:%d! type %d expected %d\n", |
1123 | __func__, init_params->connector_index, | |
1124 | link->link_id.type, OBJECT_TYPE_CONNECTOR); | |
4562236b HW |
1125 | goto create_fail; |
1126 | } | |
1127 | ||
66b198ff DL |
1128 | if (link->dc->res_pool->funcs->link_init) |
1129 | link->dc->res_pool->funcs->link_init(link); | |
1130 | ||
87401969 | 1131 | hpd_gpio = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); |
4562236b HW |
1132 | |
1133 | if (hpd_gpio != NULL) | |
d0778ebf | 1134 | link->irq_source_hpd = dal_irq_get_source(hpd_gpio); |
4562236b HW |
1135 | |
1136 | switch (link->link_id.id) { | |
1137 | case CONNECTOR_ID_HDMI_TYPE_A: | |
d0778ebf | 1138 | link->connector_signal = SIGNAL_TYPE_HDMI_TYPE_A; |
4562236b HW |
1139 | |
1140 | break; | |
1141 | case CONNECTOR_ID_SINGLE_LINK_DVID: | |
1142 | case CONNECTOR_ID_SINGLE_LINK_DVII: | |
d0778ebf | 1143 | link->connector_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; |
4562236b HW |
1144 | break; |
1145 | case CONNECTOR_ID_DUAL_LINK_DVID: | |
1146 | case CONNECTOR_ID_DUAL_LINK_DVII: | |
d0778ebf | 1147 | link->connector_signal = SIGNAL_TYPE_DVI_DUAL_LINK; |
4562236b HW |
1148 | break; |
1149 | case CONNECTOR_ID_DISPLAY_PORT: | |
d0778ebf | 1150 | link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; |
4562236b HW |
1151 | |
1152 | if (hpd_gpio != NULL) | |
d0778ebf | 1153 | link->irq_source_hpd_rx = |
4562236b HW |
1154 | dal_irq_get_rx_source(hpd_gpio); |
1155 | ||
1156 | break; | |
1157 | case CONNECTOR_ID_EDP: | |
d0778ebf | 1158 | link->connector_signal = SIGNAL_TYPE_EDP; |
4562236b HW |
1159 | |
1160 | if (hpd_gpio != NULL) { | |
d0778ebf HW |
1161 | link->irq_source_hpd = DC_IRQ_SOURCE_INVALID; |
1162 | link->irq_source_hpd_rx = | |
4562236b HW |
1163 | dal_irq_get_rx_source(hpd_gpio); |
1164 | } | |
1165 | break; | |
11c3ee48 AD |
1166 | case CONNECTOR_ID_LVDS: |
1167 | link->connector_signal = SIGNAL_TYPE_LVDS; | |
1168 | break; | |
4562236b | 1169 | default: |
1296423b | 1170 | DC_LOG_WARNING("Unsupported Connector type:%d!\n", link->link_id.id); |
4562236b HW |
1171 | goto create_fail; |
1172 | } | |
1173 | ||
1174 | if (hpd_gpio != NULL) { | |
1175 | dal_gpio_destroy_irq(&hpd_gpio); | |
1176 | hpd_gpio = NULL; | |
1177 | } | |
1178 | ||
1179 | /* TODO: #DAL3 Implement id to str function.*/ | |
1180 | LINK_INFO("Connector[%d] description:" | |
1181 | "signal %d\n", | |
1182 | init_params->connector_index, | |
d0778ebf | 1183 | link->connector_signal); |
4562236b HW |
1184 | |
1185 | ddc_service_init_data.ctx = link->ctx; | |
1186 | ddc_service_init_data.id = link->link_id; | |
1187 | ddc_service_init_data.link = link; | |
d0778ebf | 1188 | link->ddc = dal_ddc_service_create(&ddc_service_init_data); |
4562236b | 1189 | |
d0778ebf | 1190 | if (link->ddc == NULL) { |
4562236b HW |
1191 | DC_ERROR("Failed to create ddc_service!\n"); |
1192 | goto ddc_create_fail; | |
1193 | } | |
1194 | ||
d0778ebf | 1195 | link->ddc_hw_inst = |
4562236b | 1196 | dal_ddc_get_line( |
d0778ebf | 1197 | dal_ddc_service_get_ddc_pin(link->ddc)); |
4562236b HW |
1198 | |
1199 | enc_init_data.ctx = dc_ctx; | |
1200 | bp_funcs->get_src_obj(dc_ctx->dc_bios, link->link_id, 0, &enc_init_data.encoder); | |
1201 | enc_init_data.connector = link->link_id; | |
1202 | enc_init_data.channel = get_ddc_line(link); | |
1203 | enc_init_data.hpd_source = get_hpd_line(link); | |
7a096334 | 1204 | |
d0778ebf | 1205 | link->hpd_src = enc_init_data.hpd_source; |
7a096334 | 1206 | |
4562236b HW |
1207 | enc_init_data.transmitter = |
1208 | translate_encoder_to_transmitter(enc_init_data.encoder); | |
1209 | link->link_enc = link->dc->res_pool->funcs->link_enc_create( | |
1210 | &enc_init_data); | |
1211 | ||
1212 | if( link->link_enc == NULL) { | |
1213 | DC_ERROR("Failed to create link encoder!\n"); | |
1214 | goto link_enc_create_fail; | |
1215 | } | |
1216 | ||
d0778ebf | 1217 | link->link_enc_hw_inst = link->link_enc->transmitter; |
4562236b HW |
1218 | |
1219 | for (i = 0; i < 4; i++) { | |
1220 | if (BP_RESULT_OK != | |
1221 | bp_funcs->get_device_tag(dc_ctx->dc_bios, link->link_id, i, &link->device_tag)) { | |
1222 | DC_ERROR("Failed to find device tag!\n"); | |
1223 | goto device_tag_fail; | |
1224 | } | |
1225 | ||
1226 | /* Look for device tag that matches connector signal, | |
1227 | * CRT for rgb, LCD for other supported signal tyes | |
1228 | */ | |
1229 | if (!bp_funcs->is_device_id_supported(dc_ctx->dc_bios, link->device_tag.dev_id)) | |
1230 | continue; | |
1231 | if (link->device_tag.dev_id.device_type == DEVICE_TYPE_CRT | |
d0778ebf | 1232 | && link->connector_signal != SIGNAL_TYPE_RGB) |
4562236b HW |
1233 | continue; |
1234 | if (link->device_tag.dev_id.device_type == DEVICE_TYPE_LCD | |
d0778ebf | 1235 | && link->connector_signal == SIGNAL_TYPE_RGB) |
4562236b | 1236 | continue; |
4562236b HW |
1237 | break; |
1238 | } | |
1239 | ||
1240 | if (bios->integrated_info) | |
1241 | info = *bios->integrated_info; | |
1242 | ||
1243 | /* Look for channel mapping corresponding to connector and device tag */ | |
1244 | for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; i++) { | |
1245 | struct external_display_path *path = | |
1246 | &info.ext_disp_conn_info.path[i]; | |
1247 | if (path->device_connector_id.enum_id == link->link_id.enum_id | |
1248 | && path->device_connector_id.id == link->link_id.id | |
1e8635ea ZF |
1249 | && path->device_connector_id.type == link->link_id.type) { |
1250 | ||
1251 | if (link->device_tag.acpi_device != 0 | |
1252 | && path->device_acpi_enum == link->device_tag.acpi_device) { | |
1253 | link->ddi_channel_mapping = path->channel_mapping; | |
1254 | link->chip_caps = path->caps; | |
1255 | } else if (path->device_tag == | |
1256 | link->device_tag.dev_id.raw_device_tag) { | |
1257 | link->ddi_channel_mapping = path->channel_mapping; | |
1258 | link->chip_caps = path->caps; | |
1259 | } | |
4562236b HW |
1260 | break; |
1261 | } | |
1262 | } | |
1263 | ||
1264 | /* | |
1265 | * TODO check if GPIO programmed correctly | |
1266 | * | |
1267 | * If GPIO isn't programmed correctly HPD might not rise or drain | |
1268 | * fast enough, leading to bounces. | |
1269 | */ | |
b0c4e977 | 1270 | program_hpd_filter(link); |
4562236b HW |
1271 | |
1272 | return true; | |
1273 | device_tag_fail: | |
1274 | link->link_enc->funcs->destroy(&link->link_enc); | |
1275 | link_enc_create_fail: | |
d0778ebf | 1276 | dal_ddc_service_destroy(&link->ddc); |
4562236b HW |
1277 | ddc_create_fail: |
1278 | create_fail: | |
1279 | ||
1280 | if (hpd_gpio != NULL) { | |
1281 | dal_gpio_destroy_irq(&hpd_gpio); | |
1282 | } | |
1283 | ||
1284 | return false; | |
1285 | } | |
1286 | ||
1287 | /******************************************************************************* | |
1288 | * Public functions | |
1289 | ******************************************************************************/ | |
d0778ebf | 1290 | struct dc_link *link_create(const struct link_init_data *init_params) |
4562236b | 1291 | { |
d0778ebf | 1292 | struct dc_link *link = |
2004f45e | 1293 | kzalloc(sizeof(*link), GFP_KERNEL); |
4562236b HW |
1294 | |
1295 | if (NULL == link) | |
1296 | goto alloc_fail; | |
1297 | ||
1298 | if (false == construct(link, init_params)) | |
1299 | goto construct_fail; | |
1300 | ||
1301 | return link; | |
1302 | ||
1303 | construct_fail: | |
2004f45e | 1304 | kfree(link); |
4562236b HW |
1305 | |
1306 | alloc_fail: | |
1307 | return NULL; | |
1308 | } | |
1309 | ||
d0778ebf | 1310 | void link_destroy(struct dc_link **link) |
4562236b HW |
1311 | { |
1312 | destruct(*link); | |
2004f45e | 1313 | kfree(*link); |
4562236b HW |
1314 | *link = NULL; |
1315 | } | |
1316 | ||
1317 | static void dpcd_configure_panel_mode( | |
d0778ebf | 1318 | struct dc_link *link, |
4562236b HW |
1319 | enum dp_panel_mode panel_mode) |
1320 | { | |
1321 | union dpcd_edp_config edp_config_set; | |
1322 | bool panel_mode_edp = false; | |
5d4b05dd BL |
1323 | DC_LOGGER_INIT(link->ctx->logger); |
1324 | ||
4562236b HW |
1325 | memset(&edp_config_set, '\0', sizeof(union dpcd_edp_config)); |
1326 | ||
1327 | if (DP_PANEL_MODE_DEFAULT != panel_mode) { | |
1328 | ||
1329 | switch (panel_mode) { | |
1330 | case DP_PANEL_MODE_EDP: | |
1331 | case DP_PANEL_MODE_SPECIAL: | |
1332 | panel_mode_edp = true; | |
1333 | break; | |
1334 | ||
1335 | default: | |
1336 | break; | |
1337 | } | |
1338 | ||
1339 | /*set edp panel mode in receiver*/ | |
1340 | core_link_read_dpcd( | |
1341 | link, | |
3a340294 | 1342 | DP_EDP_CONFIGURATION_SET, |
4562236b HW |
1343 | &edp_config_set.raw, |
1344 | sizeof(edp_config_set.raw)); | |
1345 | ||
1346 | if (edp_config_set.bits.PANEL_MODE_EDP | |
1347 | != panel_mode_edp) { | |
1348 | enum ddc_result result = DDC_RESULT_UNKNOWN; | |
1349 | ||
1350 | edp_config_set.bits.PANEL_MODE_EDP = | |
1351 | panel_mode_edp; | |
1352 | result = core_link_write_dpcd( | |
1353 | link, | |
3a340294 | 1354 | DP_EDP_CONFIGURATION_SET, |
4562236b HW |
1355 | &edp_config_set.raw, |
1356 | sizeof(edp_config_set.raw)); | |
1357 | ||
1358 | ASSERT(result == DDC_RESULT_SUCESSFULL); | |
1359 | } | |
1360 | } | |
1296423b | 1361 | DC_LOG_DETECTION_DP_CAPS("Link: %d eDP panel mode supported: %d " |
4562236b | 1362 | "eDP panel mode enabled: %d \n", |
d0778ebf | 1363 | link->link_index, |
4562236b HW |
1364 | link->dpcd_caps.panel_mode_edp, |
1365 | panel_mode_edp); | |
1366 | } | |
1367 | ||
1368 | static void enable_stream_features(struct pipe_ctx *pipe_ctx) | |
1369 | { | |
0971c40e | 1370 | struct dc_stream_state *stream = pipe_ctx->stream; |
ceb3dbb4 | 1371 | struct dc_link *link = stream->link; |
87943159 AK |
1372 | union down_spread_ctrl old_downspread; |
1373 | union down_spread_ctrl new_downspread; | |
4562236b | 1374 | |
3a340294 | 1375 | core_link_read_dpcd(link, DP_DOWNSPREAD_CTRL, |
87943159 | 1376 | &old_downspread.raw, sizeof(old_downspread)); |
4562236b | 1377 | |
87943159 AK |
1378 | new_downspread.raw = old_downspread.raw; |
1379 | ||
1380 | new_downspread.bits.IGNORE_MSA_TIMING_PARAM = | |
4fa086b9 | 1381 | (stream->ignore_msa_timing_param) ? 1 : 0; |
4562236b | 1382 | |
87943159 AK |
1383 | if (new_downspread.raw != old_downspread.raw) { |
1384 | core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL, | |
1385 | &new_downspread.raw, sizeof(new_downspread)); | |
1386 | } | |
4562236b HW |
1387 | } |
1388 | ||
ab8db3e1 AG |
1389 | static enum dc_status enable_link_dp( |
1390 | struct dc_state *state, | |
1391 | struct pipe_ctx *pipe_ctx) | |
4562236b | 1392 | { |
0971c40e | 1393 | struct dc_stream_state *stream = pipe_ctx->stream; |
4562236b HW |
1394 | enum dc_status status; |
1395 | bool skip_video_pattern; | |
ceb3dbb4 | 1396 | struct dc_link *link = stream->link; |
4562236b HW |
1397 | struct dc_link_settings link_settings = {0}; |
1398 | enum dp_panel_mode panel_mode; | |
4562236b HW |
1399 | |
1400 | /* get link settings for video mode timing */ | |
1401 | decide_link_settings(stream, &link_settings); | |
1402 | ||
24f7dd7e DL |
1403 | pipe_ctx->stream_res.pix_clk_params.requested_sym_clk = |
1404 | link_settings.link_rate * LINK_RATE_REF_FREQ_IN_KHZ; | |
1405 | state->dccg->funcs->update_clocks(state->dccg, state, false); | |
4562236b HW |
1406 | |
1407 | dp_enable_link_phy( | |
1408 | link, | |
1409 | pipe_ctx->stream->signal, | |
1410 | pipe_ctx->clock_source->id, | |
1411 | &link_settings); | |
1412 | ||
ceb3dbb4 JL |
1413 | if (stream->sink_patches.dppowerup_delay > 0) { |
1414 | int delay_dp_power_up_in_ms = stream->sink_patches.dppowerup_delay; | |
b686ee20 MT |
1415 | |
1416 | msleep(delay_dp_power_up_in_ms); | |
1417 | } | |
1418 | ||
4562236b HW |
1419 | panel_mode = dp_get_panel_mode(link); |
1420 | dpcd_configure_panel_mode(link, panel_mode); | |
1421 | ||
1422 | skip_video_pattern = true; | |
1423 | ||
1424 | if (link_settings.link_rate == LINK_RATE_LOW) | |
1425 | skip_video_pattern = false; | |
1426 | ||
1427 | if (perform_link_training_with_retries( | |
1428 | link, | |
1429 | &link_settings, | |
1430 | skip_video_pattern, | |
1431 | LINK_TRAINING_ATTEMPTS)) { | |
d0778ebf | 1432 | link->cur_link_settings = link_settings; |
4562236b HW |
1433 | status = DC_OK; |
1434 | } | |
1435 | else | |
c0ba5ec7 | 1436 | status = DC_FAIL_DP_LINK_TRAINING; |
4562236b | 1437 | |
4562236b HW |
1438 | return status; |
1439 | } | |
1440 | ||
904623ee YS |
1441 | static enum dc_status enable_link_edp( |
1442 | struct dc_state *state, | |
1443 | struct pipe_ctx *pipe_ctx) | |
1444 | { | |
1445 | enum dc_status status; | |
1446 | struct dc_stream_state *stream = pipe_ctx->stream; | |
ceb3dbb4 | 1447 | struct dc_link *link = stream->link; |
41b49742 | 1448 | /*in case it is not on*/ |
904623ee YS |
1449 | link->dc->hwss.edp_power_control(link, true); |
1450 | link->dc->hwss.edp_wait_for_hpd_ready(link, true); | |
1451 | ||
1452 | status = enable_link_dp(state, pipe_ctx); | |
1453 | ||
904623ee YS |
1454 | |
1455 | return status; | |
1456 | } | |
1457 | ||
ab8db3e1 AG |
1458 | static enum dc_status enable_link_dp_mst( |
1459 | struct dc_state *state, | |
1460 | struct pipe_ctx *pipe_ctx) | |
4562236b | 1461 | { |
ceb3dbb4 | 1462 | struct dc_link *link = pipe_ctx->stream->link; |
4562236b HW |
1463 | |
1464 | /* sink signal type after MST branch is MST. Multiple MST sinks | |
1465 | * share one link. Link DP PHY is enable or training only once. | |
1466 | */ | |
d0778ebf | 1467 | if (link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) |
4562236b HW |
1468 | return DC_OK; |
1469 | ||
fd92ac1b HW |
1470 | /* clear payload table */ |
1471 | dm_helpers_dp_mst_clear_payload_allocation_table(link->ctx, link); | |
1472 | ||
07c84c7a DW |
1473 | /* set the sink to MST mode before enabling the link */ |
1474 | dp_enable_mst_on_sink(link, true); | |
1475 | ||
ab8db3e1 | 1476 | return enable_link_dp(state, pipe_ctx); |
4562236b HW |
1477 | } |
1478 | ||
1e8635ea ZF |
1479 | static bool get_ext_hdmi_settings(struct pipe_ctx *pipe_ctx, |
1480 | enum engine_id eng_id, | |
1481 | struct ext_hdmi_settings *settings) | |
1482 | { | |
1483 | bool result = false; | |
1484 | int i = 0; | |
1485 | struct integrated_info *integrated_info = | |
1486 | pipe_ctx->stream->ctx->dc_bios->integrated_info; | |
1487 | ||
1488 | if (integrated_info == NULL) | |
1489 | return false; | |
1490 | ||
1491 | /* | |
1492 | * Get retimer settings from sbios for passing SI eye test for DCE11 | |
1493 | * The setting values are varied based on board revision and port id | |
1494 | * Therefore the setting values of each ports is passed by sbios. | |
1495 | */ | |
1496 | ||
1497 | // Check if current bios contains ext Hdmi settings | |
1498 | if (integrated_info->gpu_cap_info & 0x20) { | |
1499 | switch (eng_id) { | |
1500 | case ENGINE_ID_DIGA: | |
1501 | settings->slv_addr = integrated_info->dp0_ext_hdmi_slv_addr; | |
1502 | settings->reg_num = integrated_info->dp0_ext_hdmi_6g_reg_num; | |
1503 | settings->reg_num_6g = integrated_info->dp0_ext_hdmi_6g_reg_num; | |
1504 | memmove(settings->reg_settings, | |
1505 | integrated_info->dp0_ext_hdmi_reg_settings, | |
1506 | sizeof(integrated_info->dp0_ext_hdmi_reg_settings)); | |
1507 | memmove(settings->reg_settings_6g, | |
1508 | integrated_info->dp0_ext_hdmi_6g_reg_settings, | |
1509 | sizeof(integrated_info->dp0_ext_hdmi_6g_reg_settings)); | |
1510 | result = true; | |
1511 | break; | |
1512 | case ENGINE_ID_DIGB: | |
1513 | settings->slv_addr = integrated_info->dp1_ext_hdmi_slv_addr; | |
1514 | settings->reg_num = integrated_info->dp1_ext_hdmi_6g_reg_num; | |
1515 | settings->reg_num_6g = integrated_info->dp1_ext_hdmi_6g_reg_num; | |
1516 | memmove(settings->reg_settings, | |
1517 | integrated_info->dp1_ext_hdmi_reg_settings, | |
1518 | sizeof(integrated_info->dp1_ext_hdmi_reg_settings)); | |
1519 | memmove(settings->reg_settings_6g, | |
1520 | integrated_info->dp1_ext_hdmi_6g_reg_settings, | |
1521 | sizeof(integrated_info->dp1_ext_hdmi_6g_reg_settings)); | |
1522 | result = true; | |
1523 | break; | |
1524 | case ENGINE_ID_DIGC: | |
1525 | settings->slv_addr = integrated_info->dp2_ext_hdmi_slv_addr; | |
1526 | settings->reg_num = integrated_info->dp2_ext_hdmi_6g_reg_num; | |
1527 | settings->reg_num_6g = integrated_info->dp2_ext_hdmi_6g_reg_num; | |
1528 | memmove(settings->reg_settings, | |
1529 | integrated_info->dp2_ext_hdmi_reg_settings, | |
1530 | sizeof(integrated_info->dp2_ext_hdmi_reg_settings)); | |
1531 | memmove(settings->reg_settings_6g, | |
1532 | integrated_info->dp2_ext_hdmi_6g_reg_settings, | |
1533 | sizeof(integrated_info->dp2_ext_hdmi_6g_reg_settings)); | |
1534 | result = true; | |
1535 | break; | |
4e527c01 AJ |
1536 | case ENGINE_ID_DIGD: |
1537 | settings->slv_addr = integrated_info->dp3_ext_hdmi_slv_addr; | |
1538 | settings->reg_num = integrated_info->dp3_ext_hdmi_6g_reg_num; | |
1539 | settings->reg_num_6g = integrated_info->dp3_ext_hdmi_6g_reg_num; | |
1540 | memmove(settings->reg_settings, | |
1541 | integrated_info->dp3_ext_hdmi_reg_settings, | |
1542 | sizeof(integrated_info->dp3_ext_hdmi_reg_settings)); | |
1543 | memmove(settings->reg_settings_6g, | |
1544 | integrated_info->dp3_ext_hdmi_6g_reg_settings, | |
1545 | sizeof(integrated_info->dp3_ext_hdmi_6g_reg_settings)); | |
1546 | result = true; | |
1547 | break; | |
1e8635ea ZF |
1548 | default: |
1549 | break; | |
1550 | } | |
1551 | ||
1552 | if (result == true) { | |
1553 | // Validate settings from bios integrated info table | |
1554 | if (settings->slv_addr == 0) | |
1555 | return false; | |
1556 | if (settings->reg_num > 9) | |
1557 | return false; | |
1558 | if (settings->reg_num_6g > 3) | |
1559 | return false; | |
1560 | ||
1561 | for (i = 0; i < settings->reg_num; i++) { | |
1562 | if (settings->reg_settings[i].i2c_reg_index > 0x20) | |
1563 | return false; | |
1564 | } | |
1565 | ||
1566 | for (i = 0; i < settings->reg_num_6g; i++) { | |
1567 | if (settings->reg_settings_6g[i].i2c_reg_index > 0x20) | |
1568 | return false; | |
1569 | } | |
1570 | } | |
1571 | } | |
1572 | ||
1573 | return result; | |
1574 | } | |
1575 | ||
1576 | static bool i2c_write(struct pipe_ctx *pipe_ctx, | |
1577 | uint8_t address, uint8_t *buffer, uint32_t length) | |
1578 | { | |
1579 | struct i2c_command cmd = {0}; | |
1580 | struct i2c_payload payload = {0}; | |
1581 | ||
1582 | memset(&payload, 0, sizeof(payload)); | |
1583 | memset(&cmd, 0, sizeof(cmd)); | |
1584 | ||
1585 | cmd.number_of_payloads = 1; | |
1586 | cmd.engine = I2C_COMMAND_ENGINE_DEFAULT; | |
1587 | cmd.speed = pipe_ctx->stream->ctx->dc->caps.i2c_speed_in_khz; | |
1588 | ||
1589 | payload.address = address; | |
1590 | payload.data = buffer; | |
1591 | payload.length = length; | |
1592 | payload.write = true; | |
1593 | cmd.payloads = &payload; | |
1594 | ||
c85e6e54 | 1595 | if (dm_helpers_submit_i2c(pipe_ctx->stream->ctx, |
ceb3dbb4 | 1596 | pipe_ctx->stream->link, &cmd)) |
1e8635ea ZF |
1597 | return true; |
1598 | ||
1599 | return false; | |
1600 | } | |
1601 | ||
1602 | static void write_i2c_retimer_setting( | |
1603 | struct pipe_ctx *pipe_ctx, | |
1604 | bool is_vga_mode, | |
1605 | bool is_over_340mhz, | |
1606 | struct ext_hdmi_settings *settings) | |
1607 | { | |
1608 | uint8_t slave_address = (settings->slv_addr >> 1); | |
1609 | uint8_t buffer[2]; | |
1610 | const uint8_t apply_rx_tx_change = 0x4; | |
1611 | uint8_t offset = 0xA; | |
1612 | uint8_t value = 0; | |
1613 | int i = 0; | |
1614 | bool i2c_success = false; | |
2f14bc89 | 1615 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
1e8635ea ZF |
1616 | |
1617 | memset(&buffer, 0, sizeof(buffer)); | |
1618 | ||
1619 | /* Start Ext-Hdmi programming*/ | |
1620 | ||
1621 | for (i = 0; i < settings->reg_num; i++) { | |
1622 | /* Apply 3G settings */ | |
1623 | if (settings->reg_settings[i].i2c_reg_index <= 0x20) { | |
1624 | ||
1625 | buffer[0] = settings->reg_settings[i].i2c_reg_index; | |
1626 | buffer[1] = settings->reg_settings[i].i2c_reg_val; | |
1627 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1628 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1629 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
1630 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", | |
1631 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1632 | |
1633 | if (!i2c_success) | |
1634 | /* Write failure */ | |
1635 | ASSERT(i2c_success); | |
1636 | ||
1637 | /* Based on DP159 specs, APPLY_RX_TX_CHANGE bit in 0x0A | |
1638 | * needs to be set to 1 on every 0xA-0xC write. | |
1639 | */ | |
1640 | if (settings->reg_settings[i].i2c_reg_index == 0xA || | |
1641 | settings->reg_settings[i].i2c_reg_index == 0xB || | |
1642 | settings->reg_settings[i].i2c_reg_index == 0xC) { | |
1643 | ||
1644 | /* Query current value from offset 0xA */ | |
1645 | if (settings->reg_settings[i].i2c_reg_index == 0xA) | |
1646 | value = settings->reg_settings[i].i2c_reg_val; | |
1647 | else { | |
1648 | i2c_success = | |
1649 | dal_ddc_service_query_ddc_data( | |
ceb3dbb4 | 1650 | pipe_ctx->stream->link->ddc, |
1e8635ea ZF |
1651 | slave_address, &offset, 1, &value, 1); |
1652 | if (!i2c_success) | |
1653 | /* Write failure */ | |
1654 | ASSERT(i2c_success); | |
1655 | } | |
1656 | ||
1657 | buffer[0] = offset; | |
1658 | /* Set APPLY_RX_TX_CHANGE bit to 1 */ | |
1659 | buffer[1] = value | apply_rx_tx_change; | |
1660 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1661 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1662 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
1663 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1664 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1665 | if (!i2c_success) |
1666 | /* Write failure */ | |
1667 | ASSERT(i2c_success); | |
1668 | } | |
1669 | } | |
1670 | } | |
1671 | ||
1672 | /* Apply 3G settings */ | |
1673 | if (is_over_340mhz) { | |
1674 | for (i = 0; i < settings->reg_num_6g; i++) { | |
1675 | /* Apply 3G settings */ | |
1676 | if (settings->reg_settings[i].i2c_reg_index <= 0x20) { | |
1677 | ||
1678 | buffer[0] = settings->reg_settings_6g[i].i2c_reg_index; | |
1679 | buffer[1] = settings->reg_settings_6g[i].i2c_reg_val; | |
1680 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1681 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1682 | RETIMER_REDRIVER_INFO("above 340Mhz: retimer write to slave_address = 0x%x,\ |
1683 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1684 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1685 | |
1686 | if (!i2c_success) | |
1687 | /* Write failure */ | |
1688 | ASSERT(i2c_success); | |
1689 | ||
1690 | /* Based on DP159 specs, APPLY_RX_TX_CHANGE bit in 0x0A | |
1691 | * needs to be set to 1 on every 0xA-0xC write. | |
1692 | */ | |
1693 | if (settings->reg_settings_6g[i].i2c_reg_index == 0xA || | |
1694 | settings->reg_settings_6g[i].i2c_reg_index == 0xB || | |
1695 | settings->reg_settings_6g[i].i2c_reg_index == 0xC) { | |
1696 | ||
1697 | /* Query current value from offset 0xA */ | |
1698 | if (settings->reg_settings_6g[i].i2c_reg_index == 0xA) | |
1699 | value = settings->reg_settings_6g[i].i2c_reg_val; | |
1700 | else { | |
1701 | i2c_success = | |
1702 | dal_ddc_service_query_ddc_data( | |
ceb3dbb4 | 1703 | pipe_ctx->stream->link->ddc, |
1e8635ea ZF |
1704 | slave_address, &offset, 1, &value, 1); |
1705 | if (!i2c_success) | |
1706 | /* Write failure */ | |
1707 | ASSERT(i2c_success); | |
1708 | } | |
1709 | ||
1710 | buffer[0] = offset; | |
1711 | /* Set APPLY_RX_TX_CHANGE bit to 1 */ | |
1712 | buffer[1] = value | apply_rx_tx_change; | |
1713 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1714 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1715 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
1716 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1717 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1718 | if (!i2c_success) |
1719 | /* Write failure */ | |
1720 | ASSERT(i2c_success); | |
1721 | } | |
1722 | } | |
1723 | } | |
1724 | } | |
1725 | ||
1726 | if (is_vga_mode) { | |
1727 | /* Program additional settings if using 640x480 resolution */ | |
1728 | ||
1729 | /* Write offset 0xFF to 0x01 */ | |
1730 | buffer[0] = 0xff; | |
1731 | buffer[1] = 0x01; | |
1732 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1733 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1734 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
1735 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1736 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1737 | if (!i2c_success) |
1738 | /* Write failure */ | |
1739 | ASSERT(i2c_success); | |
1740 | ||
1741 | /* Write offset 0x00 to 0x23 */ | |
1742 | buffer[0] = 0x00; | |
1743 | buffer[1] = 0x23; | |
1744 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1745 | buffer, sizeof(buffer)); | |
2f14bc89 | 1746 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
c2af2a42 | 1747 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", |
2f14bc89 | 1748 | slave_address, buffer[0], buffer[1], i2c_success?1:0); |
1e8635ea ZF |
1749 | if (!i2c_success) |
1750 | /* Write failure */ | |
1751 | ASSERT(i2c_success); | |
1752 | ||
1753 | /* Write offset 0xff to 0x00 */ | |
1754 | buffer[0] = 0xff; | |
1755 | buffer[1] = 0x00; | |
1756 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1757 | buffer, sizeof(buffer)); | |
2f14bc89 | 1758 | RETIMER_REDRIVER_INFO("retimer write to slave_address = 0x%x,\ |
c2af2a42 | 1759 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", |
2f14bc89 | 1760 | slave_address, buffer[0], buffer[1], i2c_success?1:0); |
1e8635ea ZF |
1761 | if (!i2c_success) |
1762 | /* Write failure */ | |
1763 | ASSERT(i2c_success); | |
1764 | ||
1765 | } | |
1766 | } | |
1767 | ||
1768 | static void write_i2c_default_retimer_setting( | |
1769 | struct pipe_ctx *pipe_ctx, | |
1770 | bool is_vga_mode, | |
1771 | bool is_over_340mhz) | |
1772 | { | |
1773 | uint8_t slave_address = (0xBA >> 1); | |
1774 | uint8_t buffer[2]; | |
1775 | bool i2c_success = false; | |
2f14bc89 | 1776 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
1e8635ea ZF |
1777 | |
1778 | memset(&buffer, 0, sizeof(buffer)); | |
1779 | ||
1780 | /* Program Slave Address for tuning single integrity */ | |
1781 | /* Write offset 0x0A to 0x13 */ | |
1782 | buffer[0] = 0x0A; | |
1783 | buffer[1] = 0x13; | |
1784 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1785 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1786 | RETIMER_REDRIVER_INFO("retimer writes default setting to slave_address = 0x%x,\ |
1787 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1788 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1789 | if (!i2c_success) |
1790 | /* Write failure */ | |
1791 | ASSERT(i2c_success); | |
1792 | ||
1793 | /* Write offset 0x0A to 0x17 */ | |
1794 | buffer[0] = 0x0A; | |
1795 | buffer[1] = 0x17; | |
1796 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1797 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1798 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1799 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1800 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1801 | if (!i2c_success) |
1802 | /* Write failure */ | |
1803 | ASSERT(i2c_success); | |
1804 | ||
1805 | /* Write offset 0x0B to 0xDA or 0xD8 */ | |
1806 | buffer[0] = 0x0B; | |
1807 | buffer[1] = is_over_340mhz ? 0xDA : 0xD8; | |
1808 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1809 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1810 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1811 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1812 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1813 | if (!i2c_success) |
1814 | /* Write failure */ | |
1815 | ASSERT(i2c_success); | |
1816 | ||
1817 | /* Write offset 0x0A to 0x17 */ | |
1818 | buffer[0] = 0x0A; | |
1819 | buffer[1] = 0x17; | |
1820 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1821 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1822 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1823 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", | |
1824 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1825 | if (!i2c_success) |
1826 | /* Write failure */ | |
1827 | ASSERT(i2c_success); | |
1828 | ||
1829 | /* Write offset 0x0C to 0x1D or 0x91 */ | |
1830 | buffer[0] = 0x0C; | |
1831 | buffer[1] = is_over_340mhz ? 0x1D : 0x91; | |
1832 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1833 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1834 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1835 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1836 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1837 | if (!i2c_success) |
1838 | /* Write failure */ | |
1839 | ASSERT(i2c_success); | |
1840 | ||
1841 | /* Write offset 0x0A to 0x17 */ | |
1842 | buffer[0] = 0x0A; | |
1843 | buffer[1] = 0x17; | |
1844 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1845 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1846 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1847 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1848 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1849 | if (!i2c_success) |
1850 | /* Write failure */ | |
1851 | ASSERT(i2c_success); | |
1852 | ||
1853 | ||
1854 | if (is_vga_mode) { | |
1855 | /* Program additional settings if using 640x480 resolution */ | |
1856 | ||
1857 | /* Write offset 0xFF to 0x01 */ | |
1858 | buffer[0] = 0xff; | |
1859 | buffer[1] = 0x01; | |
1860 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1861 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1862 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1863 | offset = 0x%x, reg_val = 0x%x, i2c_success = %d\n", | |
1864 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1865 | if (!i2c_success) |
1866 | /* Write failure */ | |
1867 | ASSERT(i2c_success); | |
1868 | ||
1869 | /* Write offset 0x00 to 0x23 */ | |
1870 | buffer[0] = 0x00; | |
1871 | buffer[1] = 0x23; | |
1872 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1873 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1874 | RETIMER_REDRIVER_INFO("retimer write to slave_addr = 0x%x,\ |
1875 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d\n", | |
1876 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1877 | if (!i2c_success) |
1878 | /* Write failure */ | |
1879 | ASSERT(i2c_success); | |
1880 | ||
1881 | /* Write offset 0xff to 0x00 */ | |
1882 | buffer[0] = 0xff; | |
1883 | buffer[1] = 0x00; | |
1884 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1885 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1886 | RETIMER_REDRIVER_INFO("retimer write default setting to slave_addr = 0x%x,\ |
1887 | offset = 0x%x, reg_val= 0x%x, i2c_success = %d end here\n", | |
1888 | slave_address, buffer[0], buffer[1], i2c_success?1:0); | |
1e8635ea ZF |
1889 | if (!i2c_success) |
1890 | /* Write failure */ | |
1891 | ASSERT(i2c_success); | |
1892 | } | |
1893 | } | |
1894 | ||
1895 | static void write_i2c_redriver_setting( | |
1896 | struct pipe_ctx *pipe_ctx, | |
1897 | bool is_over_340mhz) | |
1898 | { | |
1899 | uint8_t slave_address = (0xF0 >> 1); | |
1900 | uint8_t buffer[16]; | |
1901 | bool i2c_success = false; | |
2f14bc89 | 1902 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
1e8635ea ZF |
1903 | |
1904 | memset(&buffer, 0, sizeof(buffer)); | |
1905 | ||
1906 | // Program Slave Address for tuning single integrity | |
1907 | buffer[3] = 0x4E; | |
1908 | buffer[4] = 0x4E; | |
1909 | buffer[5] = 0x4E; | |
1910 | buffer[6] = is_over_340mhz ? 0x4E : 0x4A; | |
1911 | ||
1912 | i2c_success = i2c_write(pipe_ctx, slave_address, | |
1913 | buffer, sizeof(buffer)); | |
2f14bc89 CL |
1914 | RETIMER_REDRIVER_INFO("redriver write 0 to all 16 reg offset expect following:\n\ |
1915 | \t slave_addr = 0x%x, offset[3] = 0x%x, offset[4] = 0x%x,\ | |
1916 | offset[5] = 0x%x,offset[6] is_over_340mhz = 0x%x,\ | |
1917 | i2c_success = %d\n", | |
1918 | slave_address, buffer[3], buffer[4], buffer[5], buffer[6], i2c_success?1:0); | |
1e8635ea ZF |
1919 | |
1920 | if (!i2c_success) | |
1921 | /* Write failure */ | |
1922 | ASSERT(i2c_success); | |
1923 | } | |
1924 | ||
4562236b HW |
1925 | static void enable_link_hdmi(struct pipe_ctx *pipe_ctx) |
1926 | { | |
0971c40e | 1927 | struct dc_stream_state *stream = pipe_ctx->stream; |
ceb3dbb4 | 1928 | struct dc_link *link = stream->link; |
cc4d99b8 | 1929 | enum dc_color_depth display_color_depth; |
1e8635ea ZF |
1930 | enum engine_id eng_id; |
1931 | struct ext_hdmi_settings settings = {0}; | |
1932 | bool is_over_340mhz = false; | |
1933 | bool is_vga_mode = (stream->timing.h_addressable == 640) | |
1934 | && (stream->timing.v_addressable == 480); | |
1935 | ||
321f65a6 CL |
1936 | if (stream->phy_pix_clk == 0) |
1937 | stream->phy_pix_clk = stream->timing.pix_clk_khz; | |
1e8635ea ZF |
1938 | if (stream->phy_pix_clk > 340000) |
1939 | is_over_340mhz = true; | |
1940 | ||
1941 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) { | |
ceb3dbb4 | 1942 | unsigned short masked_chip_caps = pipe_ctx->stream->link->chip_caps & |
3e5e2215 | 1943 | EXT_DISPLAY_PATH_CAPS__EXT_CHIP_MASK; |
cc57306f | 1944 | if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_TISN65DP159RSBT) { |
1e8635ea ZF |
1945 | /* DP159, Retimer settings */ |
1946 | eng_id = pipe_ctx->stream_res.stream_enc->id; | |
1947 | ||
1948 | if (get_ext_hdmi_settings(pipe_ctx, eng_id, &settings)) { | |
1949 | write_i2c_retimer_setting(pipe_ctx, | |
1950 | is_vga_mode, is_over_340mhz, &settings); | |
1951 | } else { | |
1952 | write_i2c_default_retimer_setting(pipe_ctx, | |
1953 | is_vga_mode, is_over_340mhz); | |
1954 | } | |
cc57306f | 1955 | } else if (masked_chip_caps == EXT_DISPLAY_PATH_CAPS__HDMI20_PI3EQX1204) { |
1e8635ea ZF |
1956 | /* PI3EQX1204, Redriver settings */ |
1957 | write_i2c_redriver_setting(pipe_ctx, is_over_340mhz); | |
1958 | } | |
1959 | } | |
4562236b HW |
1960 | |
1961 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) | |
1962 | dal_ddc_service_write_scdc_data( | |
ceb3dbb4 | 1963 | stream->link->ddc, |
4562236b | 1964 | stream->phy_pix_clk, |
4fa086b9 | 1965 | stream->timing.flags.LTE_340MCSC_SCRAMBLE); |
4562236b | 1966 | |
ceb3dbb4 | 1967 | memset(&stream->link->cur_link_settings, 0, |
4562236b HW |
1968 | sizeof(struct dc_link_settings)); |
1969 | ||
4fa086b9 LSL |
1970 | display_color_depth = stream->timing.display_color_depth; |
1971 | if (stream->timing.pixel_encoding == PIXEL_ENCODING_YCBCR422) | |
cc4d99b8 CL |
1972 | display_color_depth = COLOR_DEPTH_888; |
1973 | ||
4562236b HW |
1974 | link->link_enc->funcs->enable_tmds_output( |
1975 | link->link_enc, | |
1976 | pipe_ctx->clock_source->id, | |
cc4d99b8 | 1977 | display_color_depth, |
3d5bae9e | 1978 | pipe_ctx->stream->signal, |
4562236b HW |
1979 | stream->phy_pix_clk); |
1980 | ||
1981 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) | |
d0778ebf | 1982 | dal_ddc_service_read_scdc_data(link->ddc); |
4562236b HW |
1983 | } |
1984 | ||
11c3ee48 AD |
1985 | static void enable_link_lvds(struct pipe_ctx *pipe_ctx) |
1986 | { | |
1987 | struct dc_stream_state *stream = pipe_ctx->stream; | |
ceb3dbb4 | 1988 | struct dc_link *link = stream->link; |
11c3ee48 AD |
1989 | |
1990 | if (stream->phy_pix_clk == 0) | |
1991 | stream->phy_pix_clk = stream->timing.pix_clk_khz; | |
1992 | ||
ceb3dbb4 | 1993 | memset(&stream->link->cur_link_settings, 0, |
11c3ee48 AD |
1994 | sizeof(struct dc_link_settings)); |
1995 | ||
1996 | link->link_enc->funcs->enable_lvds_output( | |
1997 | link->link_enc, | |
1998 | pipe_ctx->clock_source->id, | |
1999 | stream->phy_pix_clk); | |
2000 | ||
2001 | } | |
2002 | ||
4562236b | 2003 | /****************************enable_link***********************************/ |
ab8db3e1 AG |
2004 | static enum dc_status enable_link( |
2005 | struct dc_state *state, | |
2006 | struct pipe_ctx *pipe_ctx) | |
4562236b HW |
2007 | { |
2008 | enum dc_status status = DC_ERROR_UNEXPECTED; | |
2009 | switch (pipe_ctx->stream->signal) { | |
2010 | case SIGNAL_TYPE_DISPLAY_PORT: | |
ab8db3e1 | 2011 | status = enable_link_dp(state, pipe_ctx); |
4562236b | 2012 | break; |
904623ee YS |
2013 | case SIGNAL_TYPE_EDP: |
2014 | status = enable_link_edp(state, pipe_ctx); | |
2015 | break; | |
4562236b | 2016 | case SIGNAL_TYPE_DISPLAY_PORT_MST: |
ab8db3e1 | 2017 | status = enable_link_dp_mst(state, pipe_ctx); |
4562236b HW |
2018 | msleep(200); |
2019 | break; | |
2020 | case SIGNAL_TYPE_DVI_SINGLE_LINK: | |
2021 | case SIGNAL_TYPE_DVI_DUAL_LINK: | |
2022 | case SIGNAL_TYPE_HDMI_TYPE_A: | |
2023 | enable_link_hdmi(pipe_ctx); | |
2024 | status = DC_OK; | |
2025 | break; | |
11c3ee48 AD |
2026 | case SIGNAL_TYPE_LVDS: |
2027 | enable_link_lvds(pipe_ctx); | |
2028 | status = DC_OK; | |
2029 | break; | |
4562236b HW |
2030 | case SIGNAL_TYPE_VIRTUAL: |
2031 | status = DC_OK; | |
2032 | break; | |
2033 | default: | |
2034 | break; | |
2035 | } | |
2036 | ||
4562236b HW |
2037 | return status; |
2038 | } | |
2039 | ||
d0778ebf | 2040 | static void disable_link(struct dc_link *link, enum signal_type signal) |
4562236b HW |
2041 | { |
2042 | /* | |
2043 | * TODO: implement call for dp_set_hw_test_pattern | |
2044 | * it is needed for compliance testing | |
2045 | */ | |
2046 | ||
2047 | /* here we need to specify that encoder output settings | |
2048 | * need to be calculated as for the set mode, | |
2049 | * it will lead to querying dynamic link capabilities | |
2050 | * which should be done before enable output */ | |
2051 | ||
2052 | if (dc_is_dp_signal(signal)) { | |
2053 | /* SST DP, eDP */ | |
2054 | if (dc_is_dp_sst_signal(signal)) | |
2055 | dp_disable_link_phy(link, signal); | |
2056 | else | |
2057 | dp_disable_link_phy_mst(link, signal); | |
2058 | } else | |
069d418f | 2059 | link->link_enc->funcs->disable_output(link->link_enc, signal); |
4562236b HW |
2060 | } |
2061 | ||
73da927b | 2062 | static bool dp_active_dongle_validate_timing( |
6bffebc9 | 2063 | const struct dc_crtc_timing *timing, |
f110892e | 2064 | const struct dpcd_caps *dpcd_caps) |
6bffebc9 EY |
2065 | { |
2066 | unsigned int required_pix_clk = timing->pix_clk_khz; | |
f110892e HW |
2067 | const struct dc_dongle_caps *dongle_caps = &dpcd_caps->dongle_caps; |
2068 | ||
2069 | switch (dpcd_caps->dongle_type) { | |
2070 | case DISPLAY_DONGLE_DP_VGA_CONVERTER: | |
2071 | case DISPLAY_DONGLE_DP_DVI_CONVERTER: | |
2072 | case DISPLAY_DONGLE_DP_DVI_DONGLE: | |
2073 | if (timing->pixel_encoding == PIXEL_ENCODING_RGB) | |
2074 | return true; | |
2075 | else | |
2076 | return false; | |
2077 | default: | |
2078 | break; | |
2079 | } | |
6bffebc9 EY |
2080 | |
2081 | if (dongle_caps->dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER || | |
2082 | dongle_caps->extendedCapValid == false) | |
2083 | return true; | |
2084 | ||
2085 | /* Check Pixel Encoding */ | |
2086 | switch (timing->pixel_encoding) { | |
2087 | case PIXEL_ENCODING_RGB: | |
2088 | case PIXEL_ENCODING_YCBCR444: | |
2089 | break; | |
2090 | case PIXEL_ENCODING_YCBCR422: | |
2091 | if (!dongle_caps->is_dp_hdmi_ycbcr422_pass_through) | |
2092 | return false; | |
2093 | break; | |
2094 | case PIXEL_ENCODING_YCBCR420: | |
2095 | if (!dongle_caps->is_dp_hdmi_ycbcr420_pass_through) | |
2096 | return false; | |
2097 | break; | |
2098 | default: | |
2099 | /* Invalid Pixel Encoding*/ | |
2100 | return false; | |
2101 | } | |
2102 | ||
2103 | ||
2104 | /* Check Color Depth and Pixel Clock */ | |
2105 | if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) | |
2106 | required_pix_clk /= 2; | |
73da927b EY |
2107 | else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) |
2108 | required_pix_clk = required_pix_clk * 2 / 3; | |
6bffebc9 EY |
2109 | |
2110 | switch (timing->display_color_depth) { | |
2111 | case COLOR_DEPTH_666: | |
2112 | case COLOR_DEPTH_888: | |
2113 | /*888 and 666 should always be supported*/ | |
2114 | break; | |
2115 | case COLOR_DEPTH_101010: | |
2116 | if (dongle_caps->dp_hdmi_max_bpc < 10) | |
2117 | return false; | |
2118 | required_pix_clk = required_pix_clk * 10 / 8; | |
2119 | break; | |
2120 | case COLOR_DEPTH_121212: | |
2121 | if (dongle_caps->dp_hdmi_max_bpc < 12) | |
2122 | return false; | |
2123 | required_pix_clk = required_pix_clk * 12 / 8; | |
2124 | break; | |
2125 | ||
2126 | case COLOR_DEPTH_141414: | |
2127 | case COLOR_DEPTH_161616: | |
2128 | default: | |
2129 | /* These color depths are currently not supported */ | |
2130 | return false; | |
2131 | } | |
2132 | ||
2133 | if (required_pix_clk > dongle_caps->dp_hdmi_max_pixel_clk) | |
2134 | return false; | |
2135 | ||
2136 | return true; | |
2137 | } | |
2138 | ||
4562236b | 2139 | enum dc_status dc_link_validate_mode_timing( |
0971c40e | 2140 | const struct dc_stream_state *stream, |
d0778ebf | 2141 | struct dc_link *link, |
4562236b HW |
2142 | const struct dc_crtc_timing *timing) |
2143 | { | |
ceb3dbb4 | 2144 | uint32_t max_pix_clk = stream->link->dongle_max_pix_clk; |
f110892e | 2145 | struct dpcd_caps *dpcd_caps = &link->dpcd_caps; |
4562236b HW |
2146 | |
2147 | /* A hack to avoid failing any modes for EDID override feature on | |
2148 | * topology change such as lower quality cable for DP or different dongle | |
2149 | */ | |
d0778ebf | 2150 | if (link->remote_sinks[0]) |
4562236b HW |
2151 | return DC_OK; |
2152 | ||
6bffebc9 | 2153 | /* Passive Dongle */ |
4562236b | 2154 | if (0 != max_pix_clk && timing->pix_clk_khz > max_pix_clk) |
6bffebc9 EY |
2155 | return DC_EXCEED_DONGLE_CAP; |
2156 | ||
2157 | /* Active Dongle*/ | |
f110892e | 2158 | if (!dp_active_dongle_validate_timing(timing, dpcd_caps)) |
6bffebc9 | 2159 | return DC_EXCEED_DONGLE_CAP; |
4562236b HW |
2160 | |
2161 | switch (stream->signal) { | |
2162 | case SIGNAL_TYPE_EDP: | |
2163 | case SIGNAL_TYPE_DISPLAY_PORT: | |
2164 | if (!dp_validate_mode_timing( | |
2165 | link, | |
2166 | timing)) | |
2167 | return DC_NO_DP_LINK_BANDWIDTH; | |
2168 | break; | |
2169 | ||
2170 | default: | |
2171 | break; | |
2172 | } | |
2173 | ||
2174 | return DC_OK; | |
2175 | } | |
2176 | ||
620a0d27 DF |
2177 | int dc_link_get_backlight_level(const struct dc_link *link) |
2178 | { | |
2179 | struct abm *abm = link->ctx->dc->res_pool->abm; | |
2180 | ||
262485a5 | 2181 | if (abm == NULL || abm->funcs->get_current_backlight == NULL) |
620a0d27 DF |
2182 | return DC_ERROR_UNEXPECTED; |
2183 | ||
262485a5 | 2184 | return (int) abm->funcs->get_current_backlight(abm); |
620a0d27 | 2185 | } |
4562236b | 2186 | |
262485a5 AK |
2187 | bool dc_link_set_backlight_level(const struct dc_link *link, |
2188 | uint32_t backlight_pwm_u16_16, | |
923fe495 | 2189 | uint32_t frame_ramp) |
4562236b | 2190 | { |
fb3466a4 | 2191 | struct dc *core_dc = link->ctx->dc; |
6728b30c | 2192 | struct abm *abm = core_dc->res_pool->abm; |
23bfb331 | 2193 | struct dmcu *dmcu = core_dc->res_pool->dmcu; |
4562236b | 2194 | unsigned int controller_id = 0; |
23bfb331 | 2195 | bool use_smooth_brightness = true; |
4562236b | 2196 | int i; |
5d4b05dd | 2197 | DC_LOGGER_INIT(link->ctx->logger); |
4562236b | 2198 | |
23bfb331 AK |
2199 | if ((dmcu == NULL) || |
2200 | (abm == NULL) || | |
262485a5 | 2201 | (abm->funcs->set_backlight_level_pwm == NULL)) |
6728b30c | 2202 | return false; |
4562236b | 2203 | |
23bfb331 AK |
2204 | use_smooth_brightness = dmcu->funcs->is_dmcu_initialized(dmcu); |
2205 | ||
262485a5 AK |
2206 | DC_LOG_BACKLIGHT("New Backlight level: %d (0x%X)\n", |
2207 | backlight_pwm_u16_16, backlight_pwm_u16_16); | |
4562236b | 2208 | |
d0778ebf | 2209 | if (dc_is_embedded_signal(link->connector_signal)) { |
6ccda157 DF |
2210 | for (i = 0; i < MAX_PIPES; i++) { |
2211 | if (core_dc->current_state->res_ctx.pipe_ctx[i].stream) { | |
608ac7bb | 2212 | if (core_dc->current_state->res_ctx. |
ceb3dbb4 | 2213 | pipe_ctx[i].stream->link |
6ccda157 | 2214 | == link) |
6728b30c AK |
2215 | /* DMCU -1 for all controller id values, |
2216 | * therefore +1 here | |
2217 | */ | |
2218 | controller_id = | |
608ac7bb | 2219 | core_dc->current_state-> |
6b670fa9 | 2220 | res_ctx.pipe_ctx[i].stream_res.tg->inst + |
6728b30c AK |
2221 | 1; |
2222 | } | |
4562236b | 2223 | } |
262485a5 | 2224 | abm->funcs->set_backlight_level_pwm( |
6728b30c | 2225 | abm, |
262485a5 | 2226 | backlight_pwm_u16_16, |
6728b30c | 2227 | frame_ramp, |
23bfb331 AK |
2228 | controller_id, |
2229 | use_smooth_brightness); | |
4562236b | 2230 | } |
4562236b HW |
2231 | |
2232 | return true; | |
2233 | } | |
2234 | ||
ab892598 RL |
2235 | bool dc_link_set_abm_disable(const struct dc_link *link) |
2236 | { | |
2237 | struct dc *core_dc = link->ctx->dc; | |
2238 | struct abm *abm = core_dc->res_pool->abm; | |
2239 | ||
262485a5 | 2240 | if ((abm == NULL) || (abm->funcs->set_backlight_level_pwm == NULL)) |
ab892598 RL |
2241 | return false; |
2242 | ||
2243 | abm->funcs->set_abm_immediate_disable(abm); | |
2244 | ||
2245 | return true; | |
2246 | } | |
2247 | ||
c7299705 | 2248 | bool dc_link_set_psr_enable(const struct dc_link *link, bool enable, bool wait) |
4562236b | 2249 | { |
fb3466a4 | 2250 | struct dc *core_dc = link->ctx->dc; |
3548f073 AZ |
2251 | struct dmcu *dmcu = core_dc->res_pool->dmcu; |
2252 | ||
4b594b8d | 2253 | if ((dmcu != NULL && dmcu->funcs->is_dmcu_initialized(dmcu)) && link->psr_enabled) |
c7299705 | 2254 | dmcu->funcs->set_psr_enable(dmcu, enable, wait); |
4562236b | 2255 | |
4562236b HW |
2256 | return true; |
2257 | } | |
2258 | ||
d0778ebf | 2259 | const struct dc_link_status *dc_link_get_status(const struct dc_link *link) |
4562236b | 2260 | { |
4562236b HW |
2261 | return &link->link_status; |
2262 | } | |
2263 | ||
d0778ebf | 2264 | void core_link_resume(struct dc_link *link) |
4562236b | 2265 | { |
d0778ebf | 2266 | if (link->connector_signal != SIGNAL_TYPE_VIRTUAL) |
b0c4e977 | 2267 | program_hpd_filter(link); |
4562236b HW |
2268 | } |
2269 | ||
0971c40e | 2270 | static struct fixed31_32 get_pbn_per_slot(struct dc_stream_state *stream) |
4562236b HW |
2271 | { |
2272 | struct dc_link_settings *link_settings = | |
ceb3dbb4 | 2273 | &stream->link->cur_link_settings; |
4562236b HW |
2274 | uint32_t link_rate_in_mbps = |
2275 | link_settings->link_rate * LINK_RATE_REF_FREQ_IN_MHZ; | |
eb0e5154 | 2276 | struct fixed31_32 mbps = dc_fixpt_from_int( |
4562236b HW |
2277 | link_rate_in_mbps * link_settings->lane_count); |
2278 | ||
eb0e5154 | 2279 | return dc_fixpt_div_int(mbps, 54); |
4562236b HW |
2280 | } |
2281 | ||
2282 | static int get_color_depth(enum dc_color_depth color_depth) | |
2283 | { | |
2284 | switch (color_depth) { | |
2285 | case COLOR_DEPTH_666: return 6; | |
2286 | case COLOR_DEPTH_888: return 8; | |
2287 | case COLOR_DEPTH_101010: return 10; | |
2288 | case COLOR_DEPTH_121212: return 12; | |
2289 | case COLOR_DEPTH_141414: return 14; | |
2290 | case COLOR_DEPTH_161616: return 16; | |
2291 | default: return 0; | |
2292 | } | |
2293 | } | |
2294 | ||
2295 | static struct fixed31_32 get_pbn_from_timing(struct pipe_ctx *pipe_ctx) | |
2296 | { | |
2297 | uint32_t bpc; | |
2298 | uint64_t kbps; | |
2299 | struct fixed31_32 peak_kbps; | |
2300 | uint32_t numerator; | |
2301 | uint32_t denominator; | |
2302 | ||
10688217 HW |
2303 | bpc = get_color_depth(pipe_ctx->stream_res.pix_clk_params.color_depth); |
2304 | kbps = pipe_ctx->stream_res.pix_clk_params.requested_pix_clk * bpc * 3; | |
4562236b HW |
2305 | |
2306 | /* | |
2307 | * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006 | |
2308 | * The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on | |
2309 | * common multiplier to render an integer PBN for all link rate/lane | |
2310 | * counts combinations | |
2311 | * calculate | |
2312 | * peak_kbps *= (1006/1000) | |
2313 | * peak_kbps *= (64/54) | |
2314 | * peak_kbps *= 8 convert to bytes | |
2315 | */ | |
2316 | ||
2317 | numerator = 64 * PEAK_FACTOR_X1000; | |
2318 | denominator = 54 * 8 * 1000 * 1000; | |
2319 | kbps *= numerator; | |
eb0e5154 | 2320 | peak_kbps = dc_fixpt_from_fraction(kbps, denominator); |
4562236b HW |
2321 | |
2322 | return peak_kbps; | |
2323 | } | |
2324 | ||
2325 | static void update_mst_stream_alloc_table( | |
d0778ebf | 2326 | struct dc_link *link, |
4562236b HW |
2327 | struct stream_encoder *stream_enc, |
2328 | const struct dp_mst_stream_allocation_table *proposed_table) | |
2329 | { | |
2330 | struct link_mst_stream_allocation work_table[MAX_CONTROLLER_NUM] = { | |
2331 | { 0 } }; | |
2332 | struct link_mst_stream_allocation *dc_alloc; | |
2333 | ||
2334 | int i; | |
2335 | int j; | |
2336 | ||
2337 | /* if DRM proposed_table has more than one new payload */ | |
2338 | ASSERT(proposed_table->stream_count - | |
2339 | link->mst_stream_alloc_table.stream_count < 2); | |
2340 | ||
d0778ebf | 2341 | /* copy proposed_table to link, add stream encoder */ |
4562236b HW |
2342 | for (i = 0; i < proposed_table->stream_count; i++) { |
2343 | ||
2344 | for (j = 0; j < link->mst_stream_alloc_table.stream_count; j++) { | |
2345 | dc_alloc = | |
2346 | &link->mst_stream_alloc_table.stream_allocations[j]; | |
2347 | ||
2348 | if (dc_alloc->vcp_id == | |
2349 | proposed_table->stream_allocations[i].vcp_id) { | |
2350 | ||
2351 | work_table[i] = *dc_alloc; | |
2352 | break; /* exit j loop */ | |
2353 | } | |
2354 | } | |
2355 | ||
2356 | /* new vcp_id */ | |
2357 | if (j == link->mst_stream_alloc_table.stream_count) { | |
2358 | work_table[i].vcp_id = | |
2359 | proposed_table->stream_allocations[i].vcp_id; | |
2360 | work_table[i].slot_count = | |
2361 | proposed_table->stream_allocations[i].slot_count; | |
2362 | work_table[i].stream_enc = stream_enc; | |
2363 | } | |
2364 | } | |
2365 | ||
2366 | /* update link->mst_stream_alloc_table with work_table */ | |
2367 | link->mst_stream_alloc_table.stream_count = | |
2368 | proposed_table->stream_count; | |
2369 | for (i = 0; i < MAX_CONTROLLER_NUM; i++) | |
2370 | link->mst_stream_alloc_table.stream_allocations[i] = | |
2371 | work_table[i]; | |
2372 | } | |
2373 | ||
2374 | /* convert link_mst_stream_alloc_table to dm dp_mst_stream_alloc_table | |
2375 | * because stream_encoder is not exposed to dm | |
2376 | */ | |
2377 | static enum dc_status allocate_mst_payload(struct pipe_ctx *pipe_ctx) | |
2378 | { | |
0971c40e | 2379 | struct dc_stream_state *stream = pipe_ctx->stream; |
ceb3dbb4 | 2380 | struct dc_link *link = stream->link; |
4562236b | 2381 | struct link_encoder *link_encoder = link->link_enc; |
8e9c4c8c | 2382 | struct stream_encoder *stream_encoder = pipe_ctx->stream_res.stream_enc; |
4562236b HW |
2383 | struct dp_mst_stream_allocation_table proposed_table = {0}; |
2384 | struct fixed31_32 avg_time_slots_per_mtp; | |
2385 | struct fixed31_32 pbn; | |
2386 | struct fixed31_32 pbn_per_slot; | |
2387 | uint8_t i; | |
5d4b05dd | 2388 | DC_LOGGER_INIT(link->ctx->logger); |
4562236b HW |
2389 | |
2390 | /* enable_link_dp_mst already check link->enabled_stream_count | |
2391 | * and stream is in link->stream[]. This is called during set mode, | |
2392 | * stream_enc is available. | |
2393 | */ | |
2394 | ||
2395 | /* get calculate VC payload for stream: stream_alloc */ | |
2396 | if (dm_helpers_dp_mst_write_payload_allocation_table( | |
2397 | stream->ctx, | |
4fa086b9 | 2398 | stream, |
4562236b HW |
2399 | &proposed_table, |
2400 | true)) { | |
2401 | update_mst_stream_alloc_table( | |
8e9c4c8c | 2402 | link, pipe_ctx->stream_res.stream_enc, &proposed_table); |
4562236b HW |
2403 | } |
2404 | else | |
1296423b | 2405 | DC_LOG_WARNING("Failed to update" |
4562236b HW |
2406 | "MST allocation table for" |
2407 | "pipe idx:%d\n", | |
2408 | pipe_ctx->pipe_idx); | |
2409 | ||
1296423b | 2410 | DC_LOG_MST("%s " |
4562236b HW |
2411 | "stream_count: %d: \n ", |
2412 | __func__, | |
2413 | link->mst_stream_alloc_table.stream_count); | |
2414 | ||
2415 | for (i = 0; i < MAX_CONTROLLER_NUM; i++) { | |
3032deb5 | 2416 | DC_LOG_MST("stream_enc[%d]: %p " |
4562236b HW |
2417 | "stream[%d].vcp_id: %d " |
2418 | "stream[%d].slot_count: %d\n", | |
2419 | i, | |
3032deb5 | 2420 | (void *) link->mst_stream_alloc_table.stream_allocations[i].stream_enc, |
4562236b HW |
2421 | i, |
2422 | link->mst_stream_alloc_table.stream_allocations[i].vcp_id, | |
2423 | i, | |
2424 | link->mst_stream_alloc_table.stream_allocations[i].slot_count); | |
2425 | } | |
2426 | ||
2427 | ASSERT(proposed_table.stream_count > 0); | |
2428 | ||
2429 | /* program DP source TX for payload */ | |
2430 | link_encoder->funcs->update_mst_stream_allocation_table( | |
2431 | link_encoder, | |
2432 | &link->mst_stream_alloc_table); | |
2433 | ||
2434 | /* send down message */ | |
2435 | dm_helpers_dp_mst_poll_for_allocation_change_trigger( | |
2436 | stream->ctx, | |
4fa086b9 | 2437 | stream); |
4562236b HW |
2438 | |
2439 | dm_helpers_dp_mst_send_payload_allocation( | |
2440 | stream->ctx, | |
4fa086b9 | 2441 | stream, |
4562236b HW |
2442 | true); |
2443 | ||
2444 | /* slot X.Y for only current stream */ | |
2445 | pbn_per_slot = get_pbn_per_slot(stream); | |
2446 | pbn = get_pbn_from_timing(pipe_ctx); | |
eb0e5154 | 2447 | avg_time_slots_per_mtp = dc_fixpt_div(pbn, pbn_per_slot); |
4562236b HW |
2448 | |
2449 | stream_encoder->funcs->set_mst_bandwidth( | |
2450 | stream_encoder, | |
2451 | avg_time_slots_per_mtp); | |
2452 | ||
2453 | return DC_OK; | |
2454 | ||
2455 | } | |
2456 | ||
2457 | static enum dc_status deallocate_mst_payload(struct pipe_ctx *pipe_ctx) | |
2458 | { | |
0971c40e | 2459 | struct dc_stream_state *stream = pipe_ctx->stream; |
ceb3dbb4 | 2460 | struct dc_link *link = stream->link; |
4562236b | 2461 | struct link_encoder *link_encoder = link->link_enc; |
8e9c4c8c | 2462 | struct stream_encoder *stream_encoder = pipe_ctx->stream_res.stream_enc; |
4562236b | 2463 | struct dp_mst_stream_allocation_table proposed_table = {0}; |
eb0e5154 | 2464 | struct fixed31_32 avg_time_slots_per_mtp = dc_fixpt_from_int(0); |
4562236b | 2465 | uint8_t i; |
d0778ebf | 2466 | bool mst_mode = (link->type == dc_connection_mst_branch); |
5d4b05dd | 2467 | DC_LOGGER_INIT(link->ctx->logger); |
4562236b HW |
2468 | |
2469 | /* deallocate_mst_payload is called before disable link. When mode or | |
2470 | * disable/enable monitor, new stream is created which is not in link | |
2471 | * stream[] yet. For this, payload is not allocated yet, so de-alloc | |
2472 | * should not done. For new mode set, map_resources will get engine | |
2473 | * for new stream, so stream_enc->id should be validated until here. | |
2474 | */ | |
2475 | ||
2476 | /* slot X.Y */ | |
2477 | stream_encoder->funcs->set_mst_bandwidth( | |
2478 | stream_encoder, | |
2479 | avg_time_slots_per_mtp); | |
2480 | ||
2481 | /* TODO: which component is responsible for remove payload table? */ | |
2482 | if (mst_mode) { | |
2483 | if (dm_helpers_dp_mst_write_payload_allocation_table( | |
2484 | stream->ctx, | |
4fa086b9 | 2485 | stream, |
4562236b HW |
2486 | &proposed_table, |
2487 | false)) { | |
2488 | ||
2489 | update_mst_stream_alloc_table( | |
8e9c4c8c | 2490 | link, pipe_ctx->stream_res.stream_enc, &proposed_table); |
4562236b HW |
2491 | } |
2492 | else { | |
1296423b | 2493 | DC_LOG_WARNING("Failed to update" |
4562236b HW |
2494 | "MST allocation table for" |
2495 | "pipe idx:%d\n", | |
2496 | pipe_ctx->pipe_idx); | |
2497 | } | |
2498 | } | |
2499 | ||
1296423b | 2500 | DC_LOG_MST("%s" |
4562236b HW |
2501 | "stream_count: %d: ", |
2502 | __func__, | |
2503 | link->mst_stream_alloc_table.stream_count); | |
2504 | ||
2505 | for (i = 0; i < MAX_CONTROLLER_NUM; i++) { | |
3032deb5 | 2506 | DC_LOG_MST("stream_enc[%d]: %p " |
4562236b HW |
2507 | "stream[%d].vcp_id: %d " |
2508 | "stream[%d].slot_count: %d\n", | |
2509 | i, | |
3032deb5 | 2510 | (void *) link->mst_stream_alloc_table.stream_allocations[i].stream_enc, |
4562236b HW |
2511 | i, |
2512 | link->mst_stream_alloc_table.stream_allocations[i].vcp_id, | |
2513 | i, | |
2514 | link->mst_stream_alloc_table.stream_allocations[i].slot_count); | |
2515 | } | |
2516 | ||
2517 | link_encoder->funcs->update_mst_stream_allocation_table( | |
2518 | link_encoder, | |
2519 | &link->mst_stream_alloc_table); | |
2520 | ||
2521 | if (mst_mode) { | |
2522 | dm_helpers_dp_mst_poll_for_allocation_change_trigger( | |
2523 | stream->ctx, | |
4fa086b9 | 2524 | stream); |
4562236b HW |
2525 | |
2526 | dm_helpers_dp_mst_send_payload_allocation( | |
2527 | stream->ctx, | |
4fa086b9 | 2528 | stream, |
4562236b HW |
2529 | false); |
2530 | } | |
2531 | ||
2532 | return DC_OK; | |
2533 | } | |
2534 | ||
ab8db3e1 AG |
2535 | void core_link_enable_stream( |
2536 | struct dc_state *state, | |
2537 | struct pipe_ctx *pipe_ctx) | |
4562236b | 2538 | { |
fb3466a4 | 2539 | struct dc *core_dc = pipe_ctx->stream->ctx->dc; |
1e7e86c4 | 2540 | struct dc_stream_state *stream = pipe_ctx->stream; |
4cac1e6d | 2541 | enum dc_status status; |
5d4b05dd | 2542 | DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); |
4cac1e6d | 2543 | |
1e7e86c4 | 2544 | if (pipe_ctx->stream->signal != SIGNAL_TYPE_VIRTUAL) { |
ceb3dbb4 JL |
2545 | stream->link->link_enc->funcs->setup( |
2546 | stream->link->link_enc, | |
1e7e86c4 ST |
2547 | pipe_ctx->stream->signal); |
2548 | pipe_ctx->stream_res.stream_enc->funcs->setup_stereo_sync( | |
2549 | pipe_ctx->stream_res.stream_enc, | |
2550 | pipe_ctx->stream_res.tg->inst, | |
2551 | stream->timing.timing_3d_format != TIMING_3D_FORMAT_NONE); | |
2552 | } | |
2553 | ||
2554 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) | |
2555 | pipe_ctx->stream_res.stream_enc->funcs->dp_set_stream_attribute( | |
2556 | pipe_ctx->stream_res.stream_enc, | |
2557 | &stream->timing, | |
2558 | stream->output_color_space); | |
2559 | ||
2560 | if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) | |
2561 | pipe_ctx->stream_res.stream_enc->funcs->hdmi_set_stream_attribute( | |
2562 | pipe_ctx->stream_res.stream_enc, | |
2563 | &stream->timing, | |
2564 | stream->phy_pix_clk, | |
2565 | pipe_ctx->stream_res.audio != NULL); | |
2566 | ||
2567 | if (dc_is_dvi_signal(pipe_ctx->stream->signal)) | |
2568 | pipe_ctx->stream_res.stream_enc->funcs->dvi_set_stream_attribute( | |
2569 | pipe_ctx->stream_res.stream_enc, | |
2570 | &stream->timing, | |
2571 | (pipe_ctx->stream->signal == SIGNAL_TYPE_DVI_DUAL_LINK) ? | |
2572 | true : false); | |
2573 | ||
11c3ee48 AD |
2574 | if (dc_is_lvds_signal(pipe_ctx->stream->signal)) |
2575 | pipe_ctx->stream_res.stream_enc->funcs->lvds_set_stream_attribute( | |
2576 | pipe_ctx->stream_res.stream_enc, | |
2577 | &stream->timing); | |
2578 | ||
aa9c4abe NC |
2579 | if (!IS_FPGA_MAXIMUS_DC(core_dc->ctx->dce_environment)) { |
2580 | resource_build_info_frame(pipe_ctx); | |
2581 | core_dc->hwss.update_info_frame(pipe_ctx); | |
2582 | ||
2583 | /* eDP lit up by bios already, no need to enable again. */ | |
2584 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_EDP && | |
2585 | pipe_ctx->stream->apply_edp_fast_boot_optimization) { | |
2586 | pipe_ctx->stream->apply_edp_fast_boot_optimization = false; | |
2587 | pipe_ctx->stream->dpms_off = false; | |
2588 | return; | |
2589 | } | |
4cac1e6d | 2590 | |
aa9c4abe NC |
2591 | if (pipe_ctx->stream->dpms_off) |
2592 | return; | |
f0362823 | 2593 | |
aa9c4abe | 2594 | status = enable_link(state, pipe_ctx); |
c0ba5ec7 | 2595 | |
aa9c4abe | 2596 | if (status != DC_OK) { |
1296423b | 2597 | DC_LOG_WARNING("enabling link %u failed: %d\n", |
ceb3dbb4 | 2598 | pipe_ctx->stream->link->link_index, |
c0ba5ec7 KC |
2599 | status); |
2600 | ||
2601 | /* Abort stream enable *unless* the failure was due to | |
2602 | * DP link training - some DP monitors will recover and | |
00f713c6 EY |
2603 | * show the stream anyway. But MST displays can't proceed |
2604 | * without link training. | |
c0ba5ec7 | 2605 | */ |
00f713c6 EY |
2606 | if (status != DC_FAIL_DP_LINK_TRAINING || |
2607 | pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { | |
c0ba5ec7 KC |
2608 | BREAK_TO_DEBUGGER(); |
2609 | return; | |
2610 | } | |
aa9c4abe | 2611 | } |
4562236b | 2612 | |
aa9c4abe | 2613 | core_dc->hwss.enable_audio_stream(pipe_ctx); |
1a05873f | 2614 | |
aa9c4abe NC |
2615 | /* turn off otg test pattern if enable */ |
2616 | if (pipe_ctx->stream_res.tg->funcs->set_test_pattern) | |
2617 | pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg, | |
2618 | CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, | |
2619 | COLOR_DEPTH_UNDEFINED); | |
71021265 | 2620 | |
aa9c4abe | 2621 | core_dc->hwss.enable_stream(pipe_ctx); |
4562236b | 2622 | |
aa9c4abe NC |
2623 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) |
2624 | allocate_mst_payload(pipe_ctx); | |
2625 | ||
2626 | core_dc->hwss.unblank_stream(pipe_ctx, | |
ceb3dbb4 | 2627 | &pipe_ctx->stream->link->cur_link_settings); |
aa9c4abe | 2628 | |
14fee4ca JA |
2629 | if (dc_is_dp_signal(pipe_ctx->stream->signal)) |
2630 | enable_stream_features(pipe_ctx); | |
aa9c4abe | 2631 | } |
d050f8ed | 2632 | |
4562236b HW |
2633 | } |
2634 | ||
4176664b | 2635 | void core_link_disable_stream(struct pipe_ctx *pipe_ctx, int option) |
4562236b | 2636 | { |
fb3466a4 | 2637 | struct dc *core_dc = pipe_ctx->stream->ctx->dc; |
4562236b | 2638 | |
8c9d90ee JFZ |
2639 | core_dc->hwss.blank_stream(pipe_ctx); |
2640 | ||
4562236b HW |
2641 | if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) |
2642 | deallocate_mst_payload(pipe_ctx); | |
2643 | ||
4176664b | 2644 | core_dc->hwss.disable_stream(pipe_ctx, option); |
4562236b | 2645 | |
ceb3dbb4 | 2646 | disable_link(pipe_ctx->stream->link, pipe_ctx->stream->signal); |
4562236b HW |
2647 | } |
2648 | ||
15e17335 CL |
2649 | void core_link_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) |
2650 | { | |
fb3466a4 | 2651 | struct dc *core_dc = pipe_ctx->stream->ctx->dc; |
15e17335 CL |
2652 | |
2653 | if (pipe_ctx->stream->signal != SIGNAL_TYPE_HDMI_TYPE_A) | |
2654 | return; | |
2655 | ||
2656 | core_dc->hwss.set_avmute(pipe_ctx, enable); | |
2657 | } | |
2658 | ||
a27f1996 YAS |
2659 | /** |
2660 | ***************************************************************************** | |
2661 | * Function: dc_link_enable_hpd_filter | |
2662 | * | |
2663 | * @brief | |
2664 | * If enable is true, programs HPD filter on associated HPD line using | |
2665 | * delay_on_disconnect/delay_on_connect values dependent on | |
2666 | * link->connector_signal | |
2667 | * | |
2668 | * If enable is false, programs HPD filter on associated HPD line with no | |
2669 | * delays on connect or disconnect | |
2670 | * | |
2671 | * @param [in] link: pointer to the dc link | |
2672 | * @param [in] enable: boolean specifying whether to enable hbd | |
2673 | ***************************************************************************** | |
2674 | */ | |
11fffe45 | 2675 | void dc_link_enable_hpd_filter(struct dc_link *link, bool enable) |
9a6995ce JB |
2676 | { |
2677 | struct gpio *hpd; | |
2678 | ||
11fffe45 JB |
2679 | if (enable) { |
2680 | link->is_hpd_filter_disabled = false; | |
2681 | program_hpd_filter(link); | |
2682 | } else { | |
9a6995ce JB |
2683 | link->is_hpd_filter_disabled = true; |
2684 | /* Obtain HPD handle */ | |
2685 | hpd = get_hpd_gpio(link->ctx->dc_bios, link->link_id, link->ctx->gpio_service); | |
2686 | ||
2687 | if (!hpd) | |
2688 | return; | |
2689 | ||
2690 | /* Setup HPD filtering */ | |
2691 | if (dal_gpio_open(hpd, GPIO_MODE_INTERRUPT) == GPIO_RESULT_OK) { | |
2692 | struct gpio_hpd_config config; | |
2693 | ||
2694 | config.delay_on_connect = 0; | |
2695 | config.delay_on_disconnect = 0; | |
2696 | ||
2697 | dal_irq_setup_hpd_filter(hpd, &config); | |
2698 | ||
2699 | dal_gpio_close(hpd); | |
2700 | } else { | |
2701 | ASSERT_CRITICAL(false); | |
2702 | } | |
2703 | /* Release HPD handle */ | |
2704 | dal_gpio_destroy_irq(&hpd); | |
2705 | } | |
2706 | } | |
2707 |