drm/radeon: fix dp link rate selection (v2)
authorAlex Deucher <alexander.deucher@amd.com>
Thu, 17 Dec 2015 15:23:34 +0000 (10:23 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 21 Dec 2015 21:39:02 +0000 (16:39 -0500)
Need to properly handle the max link rate in the dpcd.
This prevents some cases where 5.4 Ghz is selected when
it shouldn't be.

v2: simplify logic, add array bounds check

Reviewed-by: Tom St Denis <tom.stdenis@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/radeon/radeon_dp_mst.c
drivers/gpu/drm/radeon/radeon_mode.h

index bd73b4069069b900b01e4858bc38bb42dfc3ac10..44ee72e04df9e953bafe64cdfdadf2f01c1f9bce 100644 (file)
@@ -302,77 +302,31 @@ static int convert_bpc_to_bpp(int bpc)
                return bpc * 3;
 }
 
-/* get the max pix clock supported by the link rate and lane num */
-static int dp_get_max_dp_pix_clock(int link_rate,
-                                  int lane_num,
-                                  int bpp)
-{
-       return (link_rate * lane_num * 8) / bpp;
-}
-
 /***** radeon specific DP functions *****/
 
-int radeon_dp_get_max_link_rate(struct drm_connector *connector,
-                               const u8 dpcd[DP_DPCD_SIZE])
-{
-       int max_link_rate;
-
-       if (radeon_connector_is_dp12_capable(connector))
-               max_link_rate = min(drm_dp_max_link_rate(dpcd), 540000);
-       else
-               max_link_rate = min(drm_dp_max_link_rate(dpcd), 270000);
-
-       return max_link_rate;
-}
-
-/* First get the min lane# when low rate is used according to pixel clock
- * (prefer low rate), second check max lane# supported by DP panel,
- * if the max lane# < low rate lane# then use max lane# instead.
- */
-static int radeon_dp_get_dp_lane_number(struct drm_connector *connector,
-                                       const u8 dpcd[DP_DPCD_SIZE],
-                                       int pix_clock)
-{
-       int bpp = convert_bpc_to_bpp(radeon_get_monitor_bpc(connector));
-       int max_link_rate = radeon_dp_get_max_link_rate(connector, dpcd);
-       int max_lane_num = drm_dp_max_lane_count(dpcd);
-       int lane_num;
-       int max_dp_pix_clock;
-
-       for (lane_num = 1; lane_num < max_lane_num; lane_num <<= 1) {
-               max_dp_pix_clock = dp_get_max_dp_pix_clock(max_link_rate, lane_num, bpp);
-               if (pix_clock <= max_dp_pix_clock)
-                       break;
-       }
-
-       return lane_num;
-}
-
-static int radeon_dp_get_dp_link_clock(struct drm_connector *connector,
-                                      const u8 dpcd[DP_DPCD_SIZE],
-                                      int pix_clock)
+int radeon_dp_get_dp_link_config(struct drm_connector *connector,
+                                const u8 dpcd[DP_DPCD_SIZE],
+                                unsigned pix_clock,
+                                unsigned *dp_lanes, unsigned *dp_rate)
 {
        int bpp = convert_bpc_to_bpp(radeon_get_monitor_bpc(connector));
-       int lane_num, max_pix_clock;
-
-       if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
-           ENCODER_OBJECT_ID_NUTMEG)
-               return 270000;
-
-       lane_num = radeon_dp_get_dp_lane_number(connector, dpcd, pix_clock);
-       max_pix_clock = dp_get_max_dp_pix_clock(162000, lane_num, bpp);
-       if (pix_clock <= max_pix_clock)
-               return 162000;
-       max_pix_clock = dp_get_max_dp_pix_clock(270000, lane_num, bpp);
-       if (pix_clock <= max_pix_clock)
-               return 270000;
-       if (radeon_connector_is_dp12_capable(connector)) {
-               max_pix_clock = dp_get_max_dp_pix_clock(540000, lane_num, bpp);
-               if (pix_clock <= max_pix_clock)
-                       return 540000;
+       static const unsigned link_rates[3] = { 162000, 270000, 540000 };
+       unsigned max_link_rate = drm_dp_max_link_rate(dpcd);
+       unsigned max_lane_num = drm_dp_max_lane_count(dpcd);
+       unsigned lane_num, i, max_pix_clock;
+
+       for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
+               for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
+                       max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
+                       if (max_pix_clock >= pix_clock) {
+                               *dp_lanes = lane_num;
+                               *dp_rate = link_rates[i];
+                               return 0;
+                       }
+               }
        }
 
-       return radeon_dp_get_max_link_rate(connector, dpcd);
+       return -EINVAL;
 }
 
 static u8 radeon_dp_encoder_service(struct radeon_device *rdev,
@@ -491,6 +445,7 @@ void radeon_dp_set_link_config(struct drm_connector *connector,
 {
        struct radeon_connector *radeon_connector = to_radeon_connector(connector);
        struct radeon_connector_atom_dig *dig_connector;
+       int ret;
 
        if (!radeon_connector->con_priv)
                return;
@@ -498,10 +453,14 @@ void radeon_dp_set_link_config(struct drm_connector *connector,
 
        if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
            (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
-               dig_connector->dp_clock =
-                       radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock);
-               dig_connector->dp_lane_count =
-                       radeon_dp_get_dp_lane_number(connector, dig_connector->dpcd, mode->clock);
+               ret = radeon_dp_get_dp_link_config(connector, dig_connector->dpcd,
+                                                  mode->clock,
+                                                  &dig_connector->dp_lane_count,
+                                                  &dig_connector->dp_clock);
+               if (ret) {
+                       dig_connector->dp_clock = 0;
+                       dig_connector->dp_lane_count = 0;
+               }
        }
 }
 
@@ -510,7 +469,8 @@ int radeon_dp_mode_valid_helper(struct drm_connector *connector,
 {
        struct radeon_connector *radeon_connector = to_radeon_connector(connector);
        struct radeon_connector_atom_dig *dig_connector;
-       int dp_clock;
+       unsigned dp_clock, dp_lanes;
+       int ret;
 
        if ((mode->clock > 340000) &&
            (!radeon_connector_is_dp12_capable(connector)))
@@ -520,8 +480,12 @@ int radeon_dp_mode_valid_helper(struct drm_connector *connector,
                return MODE_CLOCK_HIGH;
        dig_connector = radeon_connector->con_priv;
 
-       dp_clock =
-               radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock);
+       ret = radeon_dp_get_dp_link_config(connector, dig_connector->dpcd,
+                                          mode->clock,
+                                          &dp_lanes,
+                                          &dp_clock);
+       if (ret)
+               return MODE_CLOCK_HIGH;
 
        if ((dp_clock == 540000) &&
            (!radeon_connector_is_dp12_capable(connector)))
index 744f5c49c66463c56187dbc2130a77539a264130..b431c9c2b247b193b62292f77852c52a989050f1 100644 (file)
@@ -525,11 +525,17 @@ static bool radeon_mst_mode_fixup(struct drm_encoder *encoder,
        drm_mode_set_crtcinfo(adjusted_mode, 0);
        {
          struct radeon_connector_atom_dig *dig_connector;
+         int ret;
 
          dig_connector = mst_enc->connector->con_priv;
-         dig_connector->dp_lane_count = drm_dp_max_lane_count(dig_connector->dpcd);
-         dig_connector->dp_clock = radeon_dp_get_max_link_rate(&mst_enc->connector->base,
-                                                               dig_connector->dpcd);
+         ret = radeon_dp_get_dp_link_config(&mst_enc->connector->base,
+                                            dig_connector->dpcd, adjusted_mode->clock,
+                                            &dig_connector->dp_lane_count,
+                                            &dig_connector->dp_clock);
+         if (ret) {
+                 dig_connector->dp_lane_count = 0;
+                 dig_connector->dp_clock = 0;
+         }
          DRM_DEBUG_KMS("dig clock %p %d %d\n", dig_connector,
                        dig_connector->dp_lane_count, dig_connector->dp_clock);
        }
index d8babb80206319dfe02cffd879494969f8f2326f..2c833107852946e3343642881289259c29ee751e 100644 (file)
@@ -756,8 +756,10 @@ extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector);
 extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector);
 extern int radeon_dp_get_panel_mode(struct drm_encoder *encoder,
                                    struct drm_connector *connector);
-int radeon_dp_get_max_link_rate(struct drm_connector *connector,
-                               const u8 *dpcd);
+extern int radeon_dp_get_dp_link_config(struct drm_connector *connector,
+                                       const u8 *dpcd,
+                                       unsigned pix_clock,
+                                       unsigned *dp_lanes, unsigned *dp_rate);
 extern void radeon_dp_set_rx_power_state(struct drm_connector *connector,
                                         u8 power_state);
 extern void radeon_dp_aux_init(struct radeon_connector *radeon_connector);