diff options
-rw-r--r-- | drivers/gpu/drm/amd/display/dc/core/dc_link.c | 95 |
1 files changed, 77 insertions, 18 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c index ea5d5ffd5522..2fa521812d23 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c | |||
@@ -469,6 +469,13 @@ static void link_disconnect_sink(struct dc_link *link) | |||
469 | link->dpcd_sink_count = 0; | 469 | link->dpcd_sink_count = 0; |
470 | } | 470 | } |
471 | 471 | ||
472 | static void link_disconnect_remap(struct dc_sink *prev_sink, struct dc_link *link) | ||
473 | { | ||
474 | dc_sink_release(link->local_sink); | ||
475 | link->local_sink = prev_sink; | ||
476 | } | ||
477 | |||
478 | |||
472 | static bool detect_dp( | 479 | static bool detect_dp( |
473 | struct dc_link *link, | 480 | struct dc_link *link, |
474 | struct display_sink_capability *sink_caps, | 481 | struct display_sink_capability *sink_caps, |
@@ -551,6 +558,17 @@ static bool detect_dp( | |||
551 | return true; | 558 | return true; |
552 | } | 559 | } |
553 | 560 | ||
561 | static bool is_same_edid(struct dc_edid *old_edid, struct dc_edid *new_edid) | ||
562 | { | ||
563 | if (old_edid->length != new_edid->length) | ||
564 | return false; | ||
565 | |||
566 | if (new_edid->length == 0) | ||
567 | return false; | ||
568 | |||
569 | return (memcmp(old_edid->raw_edid, new_edid->raw_edid, new_edid->length) == 0); | ||
570 | } | ||
571 | |||
554 | bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) | 572 | bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) |
555 | { | 573 | { |
556 | struct dc_sink_init_data sink_init_data = { 0 }; | 574 | struct dc_sink_init_data sink_init_data = { 0 }; |
@@ -558,9 +576,13 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) | |||
558 | uint8_t i; | 576 | uint8_t i; |
559 | bool converter_disable_audio = false; | 577 | bool converter_disable_audio = false; |
560 | struct audio_support *aud_support = &link->dc->res_pool->audio_support; | 578 | struct audio_support *aud_support = &link->dc->res_pool->audio_support; |
579 | bool same_edid = false; | ||
561 | enum dc_edid_status edid_status; | 580 | enum dc_edid_status edid_status; |
562 | struct dc_context *dc_ctx = link->ctx; | 581 | struct dc_context *dc_ctx = link->ctx; |
563 | struct dc_sink *sink = NULL; | 582 | struct dc_sink *sink = NULL; |
583 | struct dc_sink *prev_sink = NULL; | ||
584 | struct dpcd_caps prev_dpcd_caps; | ||
585 | bool same_dpcd = true; | ||
564 | enum dc_connection_type new_connection_type = dc_connection_none; | 586 | enum dc_connection_type new_connection_type = dc_connection_none; |
565 | DC_LOGGER_INIT(link->ctx->logger); | 587 | DC_LOGGER_INIT(link->ctx->logger); |
566 | if (link->connector_signal == SIGNAL_TYPE_VIRTUAL) | 588 | if (link->connector_signal == SIGNAL_TYPE_VIRTUAL) |
@@ -575,6 +597,11 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) | |||
575 | link->local_sink) | 597 | link->local_sink) |
576 | return true; | 598 | return true; |
577 | 599 | ||
600 | prev_sink = link->local_sink; | ||
601 | if (prev_sink != NULL) { | ||
602 | dc_sink_retain(prev_sink); | ||
603 | memcpy(&prev_dpcd_caps, &link->dpcd_caps, sizeof(struct dpcd_caps)); | ||
604 | } | ||
578 | link_disconnect_sink(link); | 605 | link_disconnect_sink(link); |
579 | 606 | ||
580 | if (new_connection_type != dc_connection_none) { | 607 | if (new_connection_type != dc_connection_none) { |
@@ -616,14 +643,25 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) | |||
616 | link, | 643 | link, |
617 | &sink_caps, | 644 | &sink_caps, |
618 | &converter_disable_audio, | 645 | &converter_disable_audio, |
619 | aud_support, reason)) | 646 | aud_support, reason)) { |
647 | if (prev_sink != NULL) | ||
648 | dc_sink_release(prev_sink); | ||
620 | return false; | 649 | return false; |
650 | } | ||
621 | 651 | ||
652 | // Check if dpcp block is the same | ||
653 | if (prev_sink != NULL) { | ||
654 | if (memcmp(&link->dpcd_caps, &prev_dpcd_caps, sizeof(struct dpcd_caps))) | ||
655 | same_dpcd = false; | ||
656 | } | ||
622 | /* Active dongle downstream unplug */ | 657 | /* Active dongle downstream unplug */ |
623 | if (link->type == dc_connection_active_dongle | 658 | if (link->type == dc_connection_active_dongle |
624 | && link->dpcd_caps.sink_count. | 659 | && link->dpcd_caps.sink_count. |
625 | bits.SINK_COUNT == 0) | 660 | bits.SINK_COUNT == 0) { |
661 | if (prev_sink != NULL) | ||
662 | dc_sink_release(prev_sink); | ||
626 | return true; | 663 | return true; |
664 | } | ||
627 | 665 | ||
628 | if (link->type == dc_connection_mst_branch) { | 666 | if (link->type == dc_connection_mst_branch) { |
629 | LINK_INFO("link=%d, mst branch is now Connected\n", | 667 | LINK_INFO("link=%d, mst branch is now Connected\n", |
@@ -634,6 +672,8 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) | |||
634 | * pbn_per_slot value leading to exception on dc_fixpt_div() | 672 | * pbn_per_slot value leading to exception on dc_fixpt_div() |
635 | */ | 673 | */ |
636 | link->verified_link_cap = link->reported_link_cap; | 674 | link->verified_link_cap = link->reported_link_cap; |
675 | if (prev_sink != NULL) | ||
676 | dc_sink_release(prev_sink); | ||
637 | return false; | 677 | return false; |
638 | } | 678 | } |
639 | 679 | ||
@@ -643,6 +683,8 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) | |||
643 | default: | 683 | default: |
644 | DC_ERROR("Invalid connector type! signal:%d\n", | 684 | DC_ERROR("Invalid connector type! signal:%d\n", |
645 | link->connector_signal); | 685 | link->connector_signal); |
686 | if (prev_sink != NULL) | ||
687 | dc_sink_release(prev_sink); | ||
646 | return false; | 688 | return false; |
647 | } /* switch() */ | 689 | } /* switch() */ |
648 | 690 | ||
@@ -665,6 +707,8 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) | |||
665 | sink = dc_sink_create(&sink_init_data); | 707 | sink = dc_sink_create(&sink_init_data); |
666 | if (!sink) { | 708 | if (!sink) { |
667 | DC_ERROR("Failed to create sink!\n"); | 709 | DC_ERROR("Failed to create sink!\n"); |
710 | if (prev_sink != NULL) | ||
711 | dc_sink_release(prev_sink); | ||
668 | return false; | 712 | return false; |
669 | } | 713 | } |
670 | 714 | ||
@@ -688,22 +732,33 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) | |||
688 | break; | 732 | break; |
689 | } | 733 | } |
690 | 734 | ||
691 | if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && | 735 | // Check if edid is the same |
692 | sink_caps.transaction_type == | 736 | if ((prev_sink != NULL) && ((edid_status == EDID_THE_SAME) || (edid_status == EDID_OK))) |
693 | DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { | 737 | same_edid = is_same_edid(&prev_sink->dc_edid, &sink->dc_edid); |
694 | /* | ||
695 | * TODO debug why Dell 2413 doesn't like | ||
696 | * two link trainings | ||
697 | */ | ||
698 | 738 | ||
699 | /* deal with non-mst cases */ | 739 | // If both edid and dpcd are the same, then discard new sink and revert back to original sink |
700 | dp_hbr_verify_link_cap(link, &link->reported_link_cap); | 740 | if ((same_edid) && (same_dpcd)) { |
701 | } | 741 | link_disconnect_remap(prev_sink, link); |
742 | sink = prev_sink; | ||
743 | prev_sink = NULL; | ||
744 | } else { | ||
745 | if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT && | ||
746 | sink_caps.transaction_type == | ||
747 | DDC_TRANSACTION_TYPE_I2C_OVER_AUX) { | ||
748 | /* | ||
749 | * TODO debug why Dell 2413 doesn't like | ||
750 | * two link trainings | ||
751 | */ | ||
752 | |||
753 | /* deal with non-mst cases */ | ||
754 | dp_hbr_verify_link_cap(link, &link->reported_link_cap); | ||
755 | } | ||
702 | 756 | ||
703 | /* HDMI-DVI Dongle */ | 757 | /* HDMI-DVI Dongle */ |
704 | if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A && | 758 | if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A && |
705 | !sink->edid_caps.edid_hdmi) | 759 | !sink->edid_caps.edid_hdmi) |
706 | sink->sink_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; | 760 | sink->sink_signal = SIGNAL_TYPE_DVI_SINGLE_LINK; |
761 | } | ||
707 | 762 | ||
708 | /* Connectivity log: detection */ | 763 | /* Connectivity log: detection */ |
709 | for (i = 0; i < sink->dc_edid.length / EDID_BLOCK_SIZE; i++) { | 764 | for (i = 0; i < sink->dc_edid.length / EDID_BLOCK_SIZE; i++) { |
@@ -762,10 +817,14 @@ bool dc_link_detect(struct dc_link *link, enum dc_detect_reason reason) | |||
762 | sink_caps.signal = SIGNAL_TYPE_NONE; | 817 | sink_caps.signal = SIGNAL_TYPE_NONE; |
763 | } | 818 | } |
764 | 819 | ||
765 | LINK_INFO("link=%d, dc_sink_in=%p is now %s\n", | 820 | LINK_INFO("link=%d, dc_sink_in=%p is now %s prev_sink=%p dpcd same=%d edid same=%d\n", |
766 | link->link_index, sink, | 821 | link->link_index, sink, |
767 | (sink_caps.signal == SIGNAL_TYPE_NONE ? | 822 | (sink_caps.signal == SIGNAL_TYPE_NONE ? |
768 | "Disconnected":"Connected")); | 823 | "Disconnected":"Connected"), prev_sink, |
824 | same_dpcd, same_edid); | ||
825 | |||
826 | if (prev_sink != NULL) | ||
827 | dc_sink_release(prev_sink); | ||
769 | 828 | ||
770 | return true; | 829 | return true; |
771 | } | 830 | } |