Skip to content

Commit

Permalink
drm/radeon: fix dp link rate selection (v2)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
alexdeucher committed Dec 21, 2015
1 parent 41869c1 commit 092c96a
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 77 deletions.
108 changes: 36 additions & 72 deletions drivers/gpu/drm/radeon/atombios_dp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -491,17 +445,22 @@ 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;
dig_connector = radeon_connector->con_priv;

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;
}
}
}

Expand All @@ -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)))
Expand All @@ -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)))
Expand Down
12 changes: 9 additions & 3 deletions drivers/gpu/drm/radeon/radeon_dp_mst.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
6 changes: 4 additions & 2 deletions drivers/gpu/drm/radeon/radeon_mode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 092c96a

Please sign in to comment.