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