Commit c2e1ea32 authored by Douglas Anderson's avatar Douglas Anderson Committed by Neil Armstrong

drm/bridge: ti-sn65dsi86: Train at faster rates if slower ones fail

If we fail training at a lower DP link rate let's now keep trying
until we run out of rates to try.  Basically the algorithm here is to
start at the link rate that is the theoretical minimum and then slowly
bump up until we run out of rates or hit the max rate of the sink.  We
query the sink using a DPCD read.

This is, in fact, important in practice.  Specifically at least one
panel hooked up to the bridge (AUO B116XAK01) had a theoretical min
rate more than 1.62 GHz (if run at 24 bpp) and fails to train at the
next rate (2.16 GHz).  It would train at 2.7 GHz, though.
Signed-off-by: default avatarDouglas Anderson <dianders@chromium.org>
Tested-by: default avatarRob Clark <robdclark@gmail.com>
Reviewed-by: default avatarRob Clark <robdclark@gmail.com>
Reviewed-by: default avatarBjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: default avatarNeil Armstrong <narmstrong@baylibre.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191218143416.v3.8.I251add713bc5c97225200894ab110ea9183434fd@changeid
parent 3438ea3d
...@@ -454,7 +454,7 @@ static const unsigned int ti_sn_bridge_dp_rate_lut[] = { ...@@ -454,7 +454,7 @@ static const unsigned int ti_sn_bridge_dp_rate_lut[] = {
0, 1620, 2160, 2430, 2700, 3240, 4320, 5400 0, 1620, 2160, 2430, 2700, 3240, 4320, 5400
}; };
static void ti_sn_bridge_set_dp_rate(struct ti_sn_bridge *pdata) static int ti_sn_bridge_calc_min_dp_rate_idx(struct ti_sn_bridge *pdata)
{ {
unsigned int bit_rate_khz, dp_rate_mhz; unsigned int bit_rate_khz, dp_rate_mhz;
unsigned int i; unsigned int i;
...@@ -472,8 +472,42 @@ static void ti_sn_bridge_set_dp_rate(struct ti_sn_bridge *pdata) ...@@ -472,8 +472,42 @@ static void ti_sn_bridge_set_dp_rate(struct ti_sn_bridge *pdata)
if (ti_sn_bridge_dp_rate_lut[i] > dp_rate_mhz) if (ti_sn_bridge_dp_rate_lut[i] > dp_rate_mhz)
break; break;
regmap_update_bits(pdata->regmap, SN_DATARATE_CONFIG_REG, return i;
DP_DATARATE_MASK, DP_DATARATE(i)); }
static int ti_sn_bridge_get_max_dp_rate_idx(struct ti_sn_bridge *pdata)
{
u8 data;
int ret;
ret = drm_dp_dpcd_readb(&pdata->aux, DP_MAX_LINK_RATE, &data);
if (ret != 1) {
DRM_DEV_ERROR(pdata->dev,
"Can't read max rate (%d); assuming 5.4 GHz\n",
ret);
return ARRAY_SIZE(ti_sn_bridge_dp_rate_lut) - 1;
}
/*
* Return an index into ti_sn_bridge_dp_rate_lut. Just hardcode
* these indicies since it's not like the register spec is ever going
* to change and a loop would just be more complicated. Apparently
* the DP sink can only return these few rates as supported even
* though the bridge allows some rates in between.
*/
switch (data) {
case DP_LINK_BW_1_62:
return 1;
case DP_LINK_BW_2_7:
return 4;
case DP_LINK_BW_5_4:
return 7;
}
DRM_DEV_ERROR(pdata->dev,
"Unexpected max data rate (%#x); assuming 5.4 GHz\n",
(int)data);
return ARRAY_SIZE(ti_sn_bridge_dp_rate_lut) - 1;
} }
static void ti_sn_bridge_set_video_timings(struct ti_sn_bridge *pdata) static void ti_sn_bridge_set_video_timings(struct ti_sn_bridge *pdata)
...@@ -530,13 +564,15 @@ static unsigned int ti_sn_get_max_lanes(struct ti_sn_bridge *pdata) ...@@ -530,13 +564,15 @@ static unsigned int ti_sn_get_max_lanes(struct ti_sn_bridge *pdata)
return data & DP_LANE_COUNT_MASK; return data & DP_LANE_COUNT_MASK;
} }
static int ti_sn_link_training(struct ti_sn_bridge *pdata) static int ti_sn_link_training(struct ti_sn_bridge *pdata, int dp_rate_idx,
const char **last_err_str)
{ {
unsigned int val; unsigned int val;
int ret; int ret;
/* set dp clk frequency value */ /* set dp clk frequency value */
ti_sn_bridge_set_dp_rate(pdata); regmap_update_bits(pdata->regmap, SN_DATARATE_CONFIG_REG,
DP_DATARATE_MASK, DP_DATARATE(dp_rate_idx));
/* enable DP PLL */ /* enable DP PLL */
regmap_write(pdata->regmap, SN_PLL_ENABLE_REG, 1); regmap_write(pdata->regmap, SN_PLL_ENABLE_REG, 1);
...@@ -545,7 +581,7 @@ static int ti_sn_link_training(struct ti_sn_bridge *pdata) ...@@ -545,7 +581,7 @@ static int ti_sn_link_training(struct ti_sn_bridge *pdata)
val & DPPLL_SRC_DP_PLL_LOCK, 1000, val & DPPLL_SRC_DP_PLL_LOCK, 1000,
50 * 1000); 50 * 1000);
if (ret) { if (ret) {
DRM_ERROR("DP_PLL_LOCK polling failed (%d)\n", ret); *last_err_str = "DP_PLL_LOCK polling failed";
goto exit; goto exit;
} }
...@@ -556,9 +592,9 @@ static int ti_sn_link_training(struct ti_sn_bridge *pdata) ...@@ -556,9 +592,9 @@ static int ti_sn_link_training(struct ti_sn_bridge *pdata)
val == ML_TX_NORMAL_MODE, 1000, val == ML_TX_NORMAL_MODE, 1000,
500 * 1000); 500 * 1000);
if (ret) { if (ret) {
DRM_ERROR("Training complete polling failed (%d)\n", ret); *last_err_str = "Training complete polling failed";
} else if (val == ML_TX_MAIN_LINK_OFF) { } else if (val == ML_TX_MAIN_LINK_OFF) {
DRM_ERROR("Link training failed, link is off\n"); *last_err_str = "Link training failed, link is off";
ret = -EIO; ret = -EIO;
} }
...@@ -573,8 +609,11 @@ static int ti_sn_link_training(struct ti_sn_bridge *pdata) ...@@ -573,8 +609,11 @@ static int ti_sn_link_training(struct ti_sn_bridge *pdata)
static void ti_sn_bridge_enable(struct drm_bridge *bridge) static void ti_sn_bridge_enable(struct drm_bridge *bridge)
{ {
struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge);
const char *last_err_str = "No supported DP rate";
int dp_rate_idx;
int max_dp_rate_idx;
unsigned int val; unsigned int val;
int ret; int ret = -EINVAL;
/* /*
* Run with the maximum number of lanes that the DP sink supports. * Run with the maximum number of lanes that the DP sink supports.
...@@ -616,9 +655,19 @@ static void ti_sn_bridge_enable(struct drm_bridge *bridge) ...@@ -616,9 +655,19 @@ static void ti_sn_bridge_enable(struct drm_bridge *bridge)
regmap_update_bits(pdata->regmap, SN_SSC_CONFIG_REG, DP_NUM_LANES_MASK, regmap_update_bits(pdata->regmap, SN_SSC_CONFIG_REG, DP_NUM_LANES_MASK,
val); val);
ret = ti_sn_link_training(pdata); /* Train until we run out of rates */
if (ret) max_dp_rate_idx = ti_sn_bridge_get_max_dp_rate_idx(pdata);
for (dp_rate_idx = ti_sn_bridge_calc_min_dp_rate_idx(pdata);
dp_rate_idx <= max_dp_rate_idx;
dp_rate_idx++) {
ret = ti_sn_link_training(pdata, dp_rate_idx, &last_err_str);
if (!ret)
break;
}
if (ret) {
DRM_DEV_ERROR(pdata->dev, "%s (%d)\n", last_err_str, ret);
return; return;
}
/* config video parameters */ /* config video parameters */
ti_sn_bridge_set_video_timings(pdata); ti_sn_bridge_set_video_timings(pdata);
......
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