Commit 092c96a8 authored by Alex Deucher's avatar Alex Deucher

drm/radeon: fix dp link rate selection (v2)

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: default avatarTom St Denis <tom.stdenis@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 41869c1c
...@@ -302,77 +302,31 @@ static int convert_bpc_to_bpp(int bpc) ...@@ -302,77 +302,31 @@ static int convert_bpc_to_bpp(int bpc)
return bpc * 3; 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 *****/ /***** radeon specific DP functions *****/
int radeon_dp_get_max_link_rate(struct drm_connector *connector, int radeon_dp_get_dp_link_config(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], const u8 dpcd[DP_DPCD_SIZE],
int pix_clock) unsigned pix_clock,
unsigned *dp_lanes, unsigned *dp_rate)
{ {
int bpp = convert_bpc_to_bpp(radeon_get_monitor_bpc(connector)); int bpp = convert_bpc_to_bpp(radeon_get_monitor_bpc(connector));
int max_link_rate = radeon_dp_get_max_link_rate(connector, dpcd); static const unsigned link_rates[3] = { 162000, 270000, 540000 };
int max_lane_num = drm_dp_max_lane_count(dpcd); unsigned max_link_rate = drm_dp_max_link_rate(dpcd);
int lane_num; unsigned max_lane_num = drm_dp_max_lane_count(dpcd);
int max_dp_pix_clock; unsigned lane_num, i, max_pix_clock;
for (lane_num = 1; lane_num < max_lane_num; lane_num <<= 1) { 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); for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
if (pix_clock <= max_dp_pix_clock) max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
break; if (max_pix_clock >= pix_clock) {
*dp_lanes = lane_num;
*dp_rate = link_rates[i];
return 0;
}
}
} }
return lane_num; return -EINVAL;
}
static int radeon_dp_get_dp_link_clock(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 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;
}
return radeon_dp_get_max_link_rate(connector, dpcd);
} }
static u8 radeon_dp_encoder_service(struct radeon_device *rdev, static u8 radeon_dp_encoder_service(struct radeon_device *rdev,
...@@ -491,6 +445,7 @@ void radeon_dp_set_link_config(struct drm_connector *connector, ...@@ -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 *radeon_connector = to_radeon_connector(connector);
struct radeon_connector_atom_dig *dig_connector; struct radeon_connector_atom_dig *dig_connector;
int ret;
if (!radeon_connector->con_priv) if (!radeon_connector->con_priv)
return; return;
...@@ -498,10 +453,14 @@ void radeon_dp_set_link_config(struct drm_connector *connector, ...@@ -498,10 +453,14 @@ void radeon_dp_set_link_config(struct drm_connector *connector,
if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
(dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) { (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
dig_connector->dp_clock = ret = radeon_dp_get_dp_link_config(connector, dig_connector->dpcd,
radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock); mode->clock,
dig_connector->dp_lane_count = &dig_connector->dp_lane_count,
radeon_dp_get_dp_lane_number(connector, dig_connector->dpcd, mode->clock); &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, ...@@ -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 *radeon_connector = to_radeon_connector(connector);
struct radeon_connector_atom_dig *dig_connector; struct radeon_connector_atom_dig *dig_connector;
int dp_clock; unsigned dp_clock, dp_lanes;
int ret;
if ((mode->clock > 340000) && if ((mode->clock > 340000) &&
(!radeon_connector_is_dp12_capable(connector))) (!radeon_connector_is_dp12_capable(connector)))
...@@ -520,8 +480,12 @@ int radeon_dp_mode_valid_helper(struct drm_connector *connector, ...@@ -520,8 +480,12 @@ int radeon_dp_mode_valid_helper(struct drm_connector *connector,
return MODE_CLOCK_HIGH; return MODE_CLOCK_HIGH;
dig_connector = radeon_connector->con_priv; dig_connector = radeon_connector->con_priv;
dp_clock = ret = radeon_dp_get_dp_link_config(connector, dig_connector->dpcd,
radeon_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock); mode->clock,
&dp_lanes,
&dp_clock);
if (ret)
return MODE_CLOCK_HIGH;
if ((dp_clock == 540000) && if ((dp_clock == 540000) &&
(!radeon_connector_is_dp12_capable(connector))) (!radeon_connector_is_dp12_capable(connector)))
......
...@@ -525,11 +525,17 @@ static bool radeon_mst_mode_fixup(struct drm_encoder *encoder, ...@@ -525,11 +525,17 @@ static bool radeon_mst_mode_fixup(struct drm_encoder *encoder,
drm_mode_set_crtcinfo(adjusted_mode, 0); drm_mode_set_crtcinfo(adjusted_mode, 0);
{ {
struct radeon_connector_atom_dig *dig_connector; struct radeon_connector_atom_dig *dig_connector;
int ret;
dig_connector = mst_enc->connector->con_priv; dig_connector = mst_enc->connector->con_priv;
dig_connector->dp_lane_count = drm_dp_max_lane_count(dig_connector->dpcd); ret = radeon_dp_get_dp_link_config(&mst_enc->connector->base,
dig_connector->dp_clock = radeon_dp_get_max_link_rate(&mst_enc->connector->base, dig_connector->dpcd, adjusted_mode->clock,
dig_connector->dpcd); &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, DRM_DEBUG_KMS("dig clock %p %d %d\n", dig_connector,
dig_connector->dp_lane_count, dig_connector->dp_clock); dig_connector->dp_lane_count, dig_connector->dp_clock);
} }
......
...@@ -756,8 +756,10 @@ extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector); ...@@ -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 bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector);
extern int radeon_dp_get_panel_mode(struct drm_encoder *encoder, extern int radeon_dp_get_panel_mode(struct drm_encoder *encoder,
struct drm_connector *connector); struct drm_connector *connector);
int radeon_dp_get_max_link_rate(struct drm_connector *connector, extern int radeon_dp_get_dp_link_config(struct drm_connector *connector,
const u8 *dpcd); const u8 *dpcd,
unsigned pix_clock,
unsigned *dp_lanes, unsigned *dp_rate);
extern void radeon_dp_set_rx_power_state(struct drm_connector *connector, extern void radeon_dp_set_rx_power_state(struct drm_connector *connector,
u8 power_state); u8 power_state);
extern void radeon_dp_aux_init(struct radeon_connector *radeon_connector); extern void radeon_dp_aux_init(struct radeon_connector *radeon_connector);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment