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