Commit b436e8db authored by Alex Deucher's avatar Alex Deucher Committed by Tim Gardner

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

BugLink: http://bugs.launchpad.net/bugs/1546572

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>
(cherry picked from commit 41869c1c)
Signed-off-by: default avatarAlberto Milone <alberto.milone@canonical.com>
Signed-off-by: default avatarTim Gardner <tim.gardner@canonical.com>
parent e9586991
...@@ -243,7 +243,7 @@ static void amdgpu_atombios_dp_get_adjust_train(const u8 link_status[DP_LINK_STA ...@@ -243,7 +243,7 @@ static void amdgpu_atombios_dp_get_adjust_train(const u8 link_status[DP_LINK_STA
/* convert bits per color to bits per pixel */ /* convert bits per color to bits per pixel */
/* get bpc from the EDID */ /* get bpc from the EDID */
static int amdgpu_atombios_dp_convert_bpc_to_bpp(int bpc) static unsigned amdgpu_atombios_dp_convert_bpc_to_bpp(int bpc)
{ {
if (bpc == 0) if (bpc == 0)
return 24; return 24;
...@@ -251,64 +251,32 @@ static int amdgpu_atombios_dp_convert_bpc_to_bpp(int bpc) ...@@ -251,64 +251,32 @@ static int amdgpu_atombios_dp_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 amdgpu_atombios_dp_get_max_dp_pix_clock(int link_rate,
int lane_num,
int bpp)
{
return (link_rate * lane_num * 8) / bpp;
}
/***** amdgpu specific DP functions *****/ /***** amdgpu specific DP functions *****/
/* First get the min lane# when low rate is used according to pixel clock static int amdgpu_atombios_dp_get_dp_link_config(struct drm_connector *connector,
* (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 amdgpu_atombios_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 = amdgpu_atombios_dp_convert_bpc_to_bpp(amdgpu_connector_get_monitor_bpc(connector)); unsigned bpp =
int max_link_rate = drm_dp_max_link_rate(dpcd); amdgpu_atombios_dp_convert_bpc_to_bpp(amdgpu_connector_get_monitor_bpc(connector));
int max_lane_num = drm_dp_max_lane_count(dpcd); static const unsigned link_rates[3] = { 162000, 270000, 540000 };
int lane_num; unsigned max_link_rate = drm_dp_max_link_rate(dpcd);
int max_dp_pix_clock; 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) {
max_dp_pix_clock = amdgpu_atombios_dp_get_max_dp_pix_clock(max_link_rate, lane_num, bpp); for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
if (pix_clock <= max_dp_pix_clock) for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
break; 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 lane_num;
}
static int amdgpu_atombios_dp_get_dp_link_clock(struct drm_connector *connector,
const u8 dpcd[DP_DPCD_SIZE],
int pix_clock)
{
int bpp = amdgpu_atombios_dp_convert_bpc_to_bpp(amdgpu_connector_get_monitor_bpc(connector));
int lane_num, max_pix_clock;
if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) ==
ENCODER_OBJECT_ID_NUTMEG)
return 270000;
lane_num = amdgpu_atombios_dp_get_dp_lane_number(connector, dpcd, pix_clock);
max_pix_clock = amdgpu_atombios_dp_get_max_dp_pix_clock(162000, lane_num, bpp);
if (pix_clock <= max_pix_clock)
return 162000;
max_pix_clock = amdgpu_atombios_dp_get_max_dp_pix_clock(270000, lane_num, bpp);
if (pix_clock <= max_pix_clock)
return 270000;
if (amdgpu_connector_is_dp12_capable(connector)) {
max_pix_clock = amdgpu_atombios_dp_get_max_dp_pix_clock(540000, lane_num, bpp);
if (pix_clock <= max_pix_clock)
return 540000;
} }
return drm_dp_max_link_rate(dpcd); return -EINVAL;
} }
static u8 amdgpu_atombios_dp_encoder_service(struct amdgpu_device *adev, static u8 amdgpu_atombios_dp_encoder_service(struct amdgpu_device *adev,
...@@ -422,6 +390,7 @@ void amdgpu_atombios_dp_set_link_config(struct drm_connector *connector, ...@@ -422,6 +390,7 @@ void amdgpu_atombios_dp_set_link_config(struct drm_connector *connector,
{ {
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
struct amdgpu_connector_atom_dig *dig_connector; struct amdgpu_connector_atom_dig *dig_connector;
int ret;
if (!amdgpu_connector->con_priv) if (!amdgpu_connector->con_priv)
return; return;
...@@ -429,10 +398,14 @@ void amdgpu_atombios_dp_set_link_config(struct drm_connector *connector, ...@@ -429,10 +398,14 @@ void amdgpu_atombios_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 = amdgpu_atombios_dp_get_dp_link_config(connector, dig_connector->dpcd,
amdgpu_atombios_dp_get_dp_link_clock(connector, dig_connector->dpcd, mode->clock); mode->clock,
dig_connector->dp_lane_count = &dig_connector->dp_lane_count,
amdgpu_atombios_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;
}
} }
} }
...@@ -441,14 +414,17 @@ int amdgpu_atombios_dp_mode_valid_helper(struct drm_connector *connector, ...@@ -441,14 +414,17 @@ int amdgpu_atombios_dp_mode_valid_helper(struct drm_connector *connector,
{ {
struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
struct amdgpu_connector_atom_dig *dig_connector; struct amdgpu_connector_atom_dig *dig_connector;
int dp_clock; unsigned dp_lanes, dp_clock;
int ret;
if (!amdgpu_connector->con_priv) if (!amdgpu_connector->con_priv)
return MODE_CLOCK_HIGH; return MODE_CLOCK_HIGH;
dig_connector = amdgpu_connector->con_priv; dig_connector = amdgpu_connector->con_priv;
dp_clock = ret = amdgpu_atombios_dp_get_dp_link_config(connector, dig_connector->dpcd,
amdgpu_atombios_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) &&
(!amdgpu_connector_is_dp12_capable(connector))) (!amdgpu_connector_is_dp12_capable(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