]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
drm/amd/display: optionally optimize edp link rate based on timing
authorJosip Pavic <Josip.Pavic@amd.com>
Wed, 6 Feb 2019 00:27:38 +0000 (19:27 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Tue, 5 Mar 2019 20:09:33 +0000 (15:09 -0500)
[Why]
eDP v1.4 allows panels to report link rates other than RBR/HBR/HBR2, that
may be more optimal for the panel's timing. Power can be saved by using
a link rate closer to the required bandwidth of the panel's timing.

[How]
Scan the table of reported link rates from the panel, and select the
minimum link rate that satisfies the bandwidth requirements of the panel's
timing. Include a flag to make the feature optional.

Signed-off-by: Josip Pavic <Josip.Pavic@amd.com>
Reviewed-by: Harry Wentland <Harry.Wentland@amd.com>
Acked-by: Anthony Koo <Anthony.Koo@amd.com>
Acked-by: Leo Li <sunpeng.li@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
drivers/gpu/drm/amd/display/dc/dc.h
drivers/gpu/drm/amd/display/dc/dc_dp_types.h

index 09d301216076371f1e0f1329e8dc121fb1af08ea..8ad79df56bf86e0804ae3ff809bd87ed4dba809f 100644 (file)
@@ -93,12 +93,10 @@ static void dpcd_set_link_settings(
        struct dc_link *link,
        const struct link_training_settings *lt_settings)
 {
-       uint8_t rate = (uint8_t)
-       (lt_settings->link_settings.link_rate);
+       uint8_t rate;
 
        union down_spread_ctrl downspread = { {0} };
        union lane_count_set lane_count_set = { {0} };
-       uint8_t link_set_buffer[2];
 
        downspread.raw = (uint8_t)
        (lt_settings->link_settings.link_spread);
@@ -111,29 +109,42 @@ static void dpcd_set_link_settings(
        lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED =
                link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED;
 
-       link_set_buffer[0] = rate;
-       link_set_buffer[1] = lane_count_set.raw;
-
-       core_link_write_dpcd(link, DP_LINK_BW_SET,
-       link_set_buffer, 2);
        core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL,
        &downspread.raw, sizeof(downspread));
 
+       core_link_write_dpcd(link, DP_LANE_COUNT_SET,
+       &lane_count_set.raw, 1);
+
        if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14 &&
-               (link->dpcd_caps.link_rate_set >= 1 &&
-               link->dpcd_caps.link_rate_set <= 8)) {
+                       lt_settings->link_settings.use_link_rate_set == true) {
+               rate = 0;
+               core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
                core_link_write_dpcd(link, DP_LINK_RATE_SET,
-               &link->dpcd_caps.link_rate_set, 1);
+                               &lt_settings->link_settings.link_rate_set, 1);
+       } else {
+               rate = (uint8_t) (lt_settings->link_settings.link_rate);
+               core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
        }
 
-       DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x\n %x spread = %x\n",
-               __func__,
-               DP_LINK_BW_SET,
-               lt_settings->link_settings.link_rate,
-               DP_LANE_COUNT_SET,
-               lt_settings->link_settings.lane_count,
-               DP_DOWNSPREAD_CTRL,
-               lt_settings->link_settings.link_spread);
+       if (rate) {
+               DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x\n %x spread = %x\n",
+                       __func__,
+                       DP_LINK_BW_SET,
+                       lt_settings->link_settings.link_rate,
+                       DP_LANE_COUNT_SET,
+                       lt_settings->link_settings.lane_count,
+                       DP_DOWNSPREAD_CTRL,
+                       lt_settings->link_settings.link_spread);
+       } else {
+               DC_LOG_HW_LINK_TRAINING("%s\n %x rate set = %x\n %x lane = %x\n %x spread = %x\n",
+                       __func__,
+                       DP_LINK_RATE_SET,
+                       lt_settings->link_settings.link_rate_set,
+                       DP_LANE_COUNT_SET,
+                       lt_settings->link_settings.lane_count,
+                       DP_DOWNSPREAD_CTRL,
+                       lt_settings->link_settings.link_spread);
+       }
 
 }
 
@@ -952,6 +963,8 @@ enum link_training_result dc_link_dp_perform_link_training(
 
        lt_settings.link_settings.link_rate = link_setting->link_rate;
        lt_settings.link_settings.lane_count = link_setting->lane_count;
+       lt_settings.link_settings.use_link_rate_set = link_setting->use_link_rate_set;
+       lt_settings.link_settings.link_rate_set = link_setting->link_rate_set;
 
        /*@todo[vdevulap] move SS to LS, should not be handled by displaypath*/
 
@@ -1075,7 +1088,7 @@ static struct dc_link_settings get_max_link_cap(struct dc_link *link)
 {
        /* Set Default link settings */
        struct dc_link_settings max_link_cap = {LANE_COUNT_FOUR, LINK_RATE_HIGH,
-                       LINK_SPREAD_05_DOWNSPREAD_30KHZ};
+                       LINK_SPREAD_05_DOWNSPREAD_30KHZ, false, 0};
 
        /* Higher link settings based on feature supported */
        if (link->link_enc->features.flags.bits.IS_HBR2_CAPABLE)
@@ -1629,47 +1642,65 @@ bool dp_validate_mode_timing(
                return false;
 }
 
-void decide_link_settings(struct dc_stream_state *stream,
-       struct dc_link_settings *link_setting)
+static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_settings *link_setting, uint32_t req_bw)
 {
-
        struct dc_link_settings initial_link_setting = {
-               LANE_COUNT_ONE, LINK_RATE_LOW, LINK_SPREAD_DISABLED};
+               LANE_COUNT_ONE, LINK_RATE_LOW, LINK_SPREAD_DISABLED, false, 0};
        struct dc_link_settings current_link_setting =
                        initial_link_setting;
-       struct dc_link *link;
-       uint32_t req_bw;
        uint32_t link_bw;
 
-       req_bw = bandwidth_in_kbps_from_timing(&stream->timing);
-
-       link = stream->link;
-
-       /* if preferred is specified through AMDDP, use it, if it's enough
-        * to drive the mode
+       /* search for the minimum link setting that:
+        * 1. is supported according to the link training result
+        * 2. could support the b/w requested by the timing
         */
-       if (link->preferred_link_setting.lane_count !=
-                       LANE_COUNT_UNKNOWN &&
-                       link->preferred_link_setting.link_rate !=
-                                       LINK_RATE_UNKNOWN) {
-               *link_setting =  link->preferred_link_setting;
-               return;
-       }
+       while (current_link_setting.link_rate <=
+                       link->verified_link_cap.link_rate) {
+               link_bw = bandwidth_in_kbps_from_link_settings(
+                               &current_link_setting);
+               if (req_bw <= link_bw) {
+                       *link_setting = current_link_setting;
+                       return true;
+               }
 
-       /* MST doesn't perform link training for now
-        * TODO: add MST specific link training routine
-        */
-       if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
-               *link_setting = link->verified_link_cap;
-               return;
+               if (current_link_setting.lane_count <
+                               link->verified_link_cap.lane_count) {
+                       current_link_setting.lane_count =
+                                       increase_lane_count(
+                                                       current_link_setting.lane_count);
+               } else {
+                       current_link_setting.link_rate =
+                                       increase_link_rate(
+                                                       current_link_setting.link_rate);
+                       current_link_setting.lane_count =
+                                       initial_link_setting.lane_count;
+               }
        }
 
-       /* EDP use the link cap setting */
-       if (link->connector_signal == SIGNAL_TYPE_EDP) {
+       return false;
+}
+
+static bool decide_edp_link_settings(struct dc_link *link, struct dc_link_settings *link_setting, uint32_t req_bw)
+{
+       struct dc_link_settings initial_link_setting;
+       struct dc_link_settings current_link_setting;
+       uint32_t link_bw;
+
+       if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14 ||
+                       link->dpcd_caps.edp_supported_link_rates_count == 0 ||
+                       link->dc->config.optimize_edp_link_rate == false) {
                *link_setting = link->verified_link_cap;
-               return;
+               return true;
        }
 
+       memset(&initial_link_setting, 0, sizeof(initial_link_setting));
+       initial_link_setting.lane_count = LANE_COUNT_ONE;
+       initial_link_setting.link_rate = link->dpcd_caps.edp_supported_link_rates[0];
+       initial_link_setting.link_spread = LINK_SPREAD_DISABLED;
+       initial_link_setting.use_link_rate_set = true;
+       initial_link_setting.link_rate_set = 0;
+       current_link_setting = initial_link_setting;
+
        /* search for the minimum link setting that:
         * 1. is supported according to the link training result
         * 2. could support the b/w requested by the timing
@@ -1680,7 +1711,7 @@ void decide_link_settings(struct dc_stream_state *stream,
                                &current_link_setting);
                if (req_bw <= link_bw) {
                        *link_setting = current_link_setting;
-                       return;
+                       return true;
                }
 
                if (current_link_setting.lane_count <
@@ -1689,13 +1720,53 @@ void decide_link_settings(struct dc_stream_state *stream,
                                        increase_lane_count(
                                                        current_link_setting.lane_count);
                } else {
-                       current_link_setting.link_rate =
-                                       increase_link_rate(
-                                                       current_link_setting.link_rate);
-                       current_link_setting.lane_count =
-                                       initial_link_setting.lane_count;
+                       if (current_link_setting.link_rate_set < link->dpcd_caps.edp_supported_link_rates_count) {
+                               current_link_setting.link_rate_set++;
+                               current_link_setting.link_rate =
+                                       link->dpcd_caps.edp_supported_link_rates[current_link_setting.link_rate_set];
+                               current_link_setting.lane_count =
+                                                                       initial_link_setting.lane_count;
+                       } else
+                               break;
                }
        }
+       return false;
+}
+
+void decide_link_settings(struct dc_stream_state *stream,
+       struct dc_link_settings *link_setting)
+{
+       struct dc_link *link;
+       uint32_t req_bw;
+
+       req_bw = bandwidth_in_kbps_from_timing(&stream->timing);
+
+       link = stream->link;
+
+       /* if preferred is specified through AMDDP, use it, if it's enough
+        * to drive the mode
+        */
+       if (link->preferred_link_setting.lane_count !=
+                       LANE_COUNT_UNKNOWN &&
+                       link->preferred_link_setting.link_rate !=
+                                       LINK_RATE_UNKNOWN) {
+               *link_setting =  link->preferred_link_setting;
+               return;
+       }
+
+       /* MST doesn't perform link training for now
+        * TODO: add MST specific link training routine
+        */
+       if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
+               *link_setting = link->verified_link_cap;
+               return;
+       }
+
+       if (link->connector_signal == SIGNAL_TYPE_EDP) {
+               if (decide_edp_link_settings(link, link_setting, req_bw))
+                       return;
+       } else if (decide_dp_link_settings(link, link_setting, req_bw))
+               return;
 
        BREAK_TO_DEBUGGER();
        ASSERT(link->verified_link_cap.lane_count != LANE_COUNT_UNKNOWN);
@@ -2536,31 +2607,31 @@ enum dc_link_rate linkRateInKHzToLinkRateMultiplier(uint32_t link_rate_in_khz)
 
 void detect_edp_sink_caps(struct dc_link *link)
 {
-       uint8_t supported_link_rates[16] = {0};
+       uint8_t supported_link_rates[16];
        uint32_t entry;
        uint32_t link_rate_in_khz;
        enum dc_link_rate link_rate = LINK_RATE_UNKNOWN;
 
        retrieve_link_cap(link);
+       link->dpcd_caps.edp_supported_link_rates_count = 0;
+       memset(supported_link_rates, 0, sizeof(supported_link_rates));
 
-       if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14) {
+       if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14 &&
+                       link->dc->config.optimize_edp_link_rate) {
                // Read DPCD 00010h - 0001Fh 16 bytes at one shot
                core_link_read_dpcd(link, DP_SUPPORTED_LINK_RATES,
                                                        supported_link_rates, sizeof(supported_link_rates));
 
-               link->dpcd_caps.link_rate_set = 0;
                for (entry = 0; entry < 16; entry += 2) {
                        // DPCD register reports per-lane link rate = 16-bit link rate capability
-                       // value X 200 kHz. Need multipler to find link rate in kHz.
+                       // value X 200 kHz. Need multiplier to find link rate in kHz.
                        link_rate_in_khz = (supported_link_rates[entry+1] * 0x100 +
                                                                                supported_link_rates[entry]) * 200;
 
                        if (link_rate_in_khz != 0) {
                                link_rate = linkRateInKHzToLinkRateMultiplier(link_rate_in_khz);
-                               if (link->reported_link_cap.link_rate < link_rate) {
-                                       link->reported_link_cap.link_rate = link_rate;
-                                       link->dpcd_caps.link_rate_set = entry;
-                               }
+                               link->dpcd_caps.edp_supported_link_rates[link->dpcd_caps.edp_supported_link_rates_count] = link_rate;
+                               link->dpcd_caps.edp_supported_link_rates_count++;
                        }
                }
        }
index a4d3da849a725d61f37a9d90c647f8471b39980f..e98e19ca9c1c1fd7395474aa56a04a01cea3ce47 100644 (file)
@@ -164,6 +164,7 @@ struct dc_config {
        bool gpu_vm_support;
        bool disable_disp_pll_sharing;
        bool fbc_support;
+       bool optimize_edp_link_rate;
 };
 
 enum visual_confirm {
@@ -648,6 +649,10 @@ struct dpcd_caps {
        union max_lane_count max_ln_count;
        union max_down_spread max_down_spread;
 
+       /* valid only for eDP v1.4 or higher*/
+       uint8_t edp_supported_link_rates_count;
+       enum dc_link_rate edp_supported_link_rates[8];
+
        /* dongle type (DP converter, CV smart dongle) */
        enum display_dongle_type dongle_type;
        /* Dongle's downstream count. */
@@ -665,7 +670,6 @@ struct dpcd_caps {
        int8_t branch_dev_name[6];
        int8_t branch_hw_revision;
        int8_t branch_fw_revision[2];
-       uint8_t link_rate_set;
 
        bool allow_invalid_MSA_timing_param;
        bool panel_mode_edp;
index d4eab33c453b99071df316f2202f3ab469799733..ec403cd8b834bfc584c420f262adbfa9e5fa56bf 100644 (file)
@@ -94,6 +94,8 @@ struct dc_link_settings {
        enum dc_lane_count lane_count;
        enum dc_link_rate link_rate;
        enum dc_link_spread link_spread;
+       bool use_link_rate_set;
+       uint8_t link_rate_set;
 };
 
 struct dc_lane_settings {