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