From: Thierry Reding treding@nvidia.com
Hi,
this set of patches is based on work that I had done some 2-3 years ago to enable DP support on Pixel C. I never really got that to work, but I recently got my hands on newer hardware with DP connectivity, so I went to revive these patches.
I think this takes into account all review comments I was able to find and adds a bit of kerneldoc here and there. I've got a couple of patches on top of this that implement DP support (and make use of more of these helpers in the existing eDP support) for Tegra.
Thierry
Thierry Reding (17): drm/dp: Add missing kerneldoc for struct drm_dp_link drm/dp: Add drm_dp_link_reset() implementation drm/dp: Track link capabilities alongside settings drm/dp: Turn link capabilities into booleans drm/dp: Probe link using existing parsing helpers drm/dp: Read fast training capability from link drm/dp: Read TPS3 capability from sink drm/dp: Read channel coding capability from sink drm/dp: Read alternate scrambler reset capability from sink drm/dp: Read eDP version from DPCD drm/dp: Read AUX read interval from DPCD drm/dp: Do not busy-loop during link training drm/dp: Use drm_dp_aux_rd_interval() drm/dp: Add helper to get post-cursor adjustments drm/dp: Set channel coding on link configuration drm/dp: Enable alternate scrambler reset when supported drm/dp: Add drm_dp_link_choose() helper
drivers/gpu/drm/bridge/tc358767.c | 20 ++-- drivers/gpu/drm/drm_dp_helper.c | 211 +++++++++++++++++++++++++++++---- drivers/gpu/drm/msm/edp/edp_ctrl.c | 12 +- drivers/gpu/drm/rockchip/cdn-dp-core.c | 8 +- drivers/gpu/drm/rockchip/cdn-dp-reg.c | 13 +- drivers/gpu/drm/tegra/dpaux.c | 8 +- drivers/gpu/drm/tegra/sor.c | 32 ++--- include/drm/drm_dp_helper.h | 95 ++++++++++++++- 8 files changed, 328 insertions(+), 71 deletions(-)
From: Thierry Reding treding@nvidia.com
The drm_dp_link structure tracks capabilities on the DP link. Add some kerneldoc to explain what each of its fields means.
Signed-off-by: Thierry Reding treding@nvidia.com --- include/drm/drm_dp_helper.h | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index da58a428c8d7..6ced0bc6feb9 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1099,6 +1099,13 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, */ #define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0)
+/** + * struct drm_dp_link - DP link capabilities + * @revision: DP specification revision supported on the link + * @rate: maximum clock rate supported on the link + * @num_lanes: maximum number of lanes supported on the link + * @capabilities: bitmask of capabilities supported on the link + */ struct drm_dp_link { unsigned char revision; unsigned int rate;
From: Thierry Reding treding@nvidia.com
Subsequent patches will add non-volatile fields to struct drm_dp_link, so introduce a function to zero out only the volatile fields.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index adf79be42c1e..1b8a471ec1b7 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -315,6 +315,17 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, } EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
+static void drm_dp_link_reset(struct drm_dp_link *link) +{ + if (!link) + return; + + link->revision = 0; + link->rate = 0; + link->num_lanes = 0; + link->capabilities = 0; +} + /** * drm_dp_link_probe() - probe a DisplayPort link for capabilities * @aux: DisplayPort AUX channel @@ -331,7 +342,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) u8 values[3]; int err;
- memset(link, 0, sizeof(*link)); + drm_dp_link_reset(link);
err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values)); if (err < 0)
From: Thierry Reding treding@nvidia.com
Store capabilities in max_* fields and add separate fields for the currently selected settings.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/bridge/tc358767.c | 14 +++++++------- drivers/gpu/drm/drm_dp_helper.c | 16 +++++++++++----- drivers/gpu/drm/msm/edp/edp_ctrl.c | 8 ++++---- drivers/gpu/drm/rockchip/cdn-dp-core.c | 8 ++++---- drivers/gpu/drm/rockchip/cdn-dp-reg.c | 13 ++++++------- drivers/gpu/drm/tegra/dpaux.c | 8 ++++---- drivers/gpu/drm/tegra/sor.c | 28 ++++++++++++++-------------- include/drm/drm_dp_helper.h | 15 ++++++++++----- 8 files changed, 60 insertions(+), 50 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 08ab7d6aea65..55037f999579 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -391,7 +391,7 @@ static u32 tc_srcctrl(struct tc_data *tc) reg |= DP0_SRCCTRL_EN810B; if (tc->link.spread) reg |= DP0_SRCCTRL_SSCG; /* Spread Spectrum Enable */ - if (tc->link.base.num_lanes == 2) + if (tc->link.base.lanes == 2) reg |= DP0_SRCCTRL_LANES_2; /* Two Main Channel Lanes */ if (tc->link.base.rate != 162000) reg |= DP0_SRCCTRL_BW27; /* 2.7 Gbps link */ @@ -610,9 +610,9 @@ static int tc_get_display_props(struct tc_data *tc) tc->link.base.rate = 270000; }
- if (tc->link.base.num_lanes > 2) { + if (tc->link.base.lanes > 2) { dev_dbg(tc->dev, "Falling to 2 lanes\n"); - tc->link.base.num_lanes = 2; + tc->link.base.lanes = 2; }
ret = drm_dp_dpcd_readb(&tc->aux, DP_MAX_DOWNSPREAD, tmp); @@ -634,7 +634,7 @@ static int tc_get_display_props(struct tc_data *tc) dev_dbg(tc->dev, "DPCD rev: %d.%d, rate: %s, lanes: %d, framing: %s\n", tc->link.base.revision >> 4, tc->link.base.revision & 0x0f, (tc->link.base.rate == 162000) ? "1.62Gbps" : "2.7Gbps", - tc->link.base.num_lanes, + tc->link.base.lanes, (tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) ? "enhanced" : "non-enhanced"); dev_dbg(tc->dev, "ANSI 8B/10B: %d\n", tc->link.coding8b10b); @@ -788,13 +788,13 @@ static int tc_link_training(struct tc_data *tc, int pattern) LT_INTERLANE_ALIGN_DONE | LT_CHANNEL0_EQ_BITS; /* in case of two lanes */ - if ((tc->link.base.num_lanes == 2) && + if ((tc->link.base.lanes == 2) && (value == (LT_CHANNEL1_EQ_BITS | LT_INTERLANE_ALIGN_DONE | LT_CHANNEL0_EQ_BITS))) break; /* in case of one line */ - if ((tc->link.base.num_lanes == 1) && + if ((tc->link.base.lanes == 1) && (value == (LT_INTERLANE_ALIGN_DONE | LT_CHANNEL0_EQ_BITS))) break; @@ -966,7 +966,7 @@ static int tc_main_link_setup(struct tc_data *tc) if (ret < 0) goto err_dpcd_read; } while ((--timeout) && - !(drm_dp_channel_eq_ok(tmp + 2, tc->link.base.num_lanes))); + !(drm_dp_channel_eq_ok(tmp + 2, tc->link.base.lanes)));
if (timeout == 0) { /* Read DPCD 0x200-0x201 */ diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 1b8a471ec1b7..f401377a199d 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -321,9 +321,12 @@ static void drm_dp_link_reset(struct drm_dp_link *link) return;
link->revision = 0; - link->rate = 0; - link->num_lanes = 0; + link->max_rate = 0; + link->max_lanes = 0; link->capabilities = 0; + + link->rate = 0; + link->lanes = 0; }
/** @@ -349,12 +352,15 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) return err;
link->revision = values[0]; - link->rate = drm_dp_bw_code_to_link_rate(values[1]); - link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK; + link->max_rate = drm_dp_bw_code_to_link_rate(values[1]); + link->max_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
if (values[2] & DP_ENHANCED_FRAME_CAP) link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
+ link->rate = link->max_rate; + link->lanes = link->max_lanes; + return 0; } EXPORT_SYMBOL(drm_dp_link_probe); @@ -441,7 +447,7 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) int err;
values[0] = drm_dp_link_rate_to_bw_code(link->rate); - values[1] = link->num_lanes; + values[1] = link->lanes;
if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; diff --git a/drivers/gpu/drm/msm/edp/edp_ctrl.c b/drivers/gpu/drm/msm/edp/edp_ctrl.c index 7c72264101ff..71192a0656e3 100644 --- a/drivers/gpu/drm/msm/edp/edp_ctrl.c +++ b/drivers/gpu/drm/msm/edp/edp_ctrl.c @@ -411,7 +411,7 @@ static void edp_fill_link_cfg(struct edp_ctrl *ctrl) u32 prate; u32 lrate; u32 bpp; - u8 max_lane = ctrl->dp_link.num_lanes; + u8 max_lane = ctrl->dp_link.max_lanes; u8 lane;
prate = ctrl->pixel_rate; @@ -421,7 +421,7 @@ static void edp_fill_link_cfg(struct edp_ctrl *ctrl) * By default, use the maximum link rate and minimum lane count, * so that we can do rate down shift during link training. */ - ctrl->link_rate = drm_dp_link_rate_to_bw_code(ctrl->dp_link.rate); + ctrl->link_rate = drm_dp_link_rate_to_bw_code(ctrl->dp_link.max_rate);
prate *= bpp; prate /= 8; /* in kByte */ @@ -709,7 +709,7 @@ static int edp_link_rate_down_shift(struct edp_ctrl *ctrl)
rate = ctrl->link_rate; lane = ctrl->lane_cnt; - max_lane = ctrl->dp_link.num_lanes; + max_lane = ctrl->dp_link.max_lanes;
bpp = ctrl->color_depth * 3; prate = ctrl->pixel_rate; @@ -767,7 +767,7 @@ static int edp_do_link_train(struct edp_ctrl *ctrl) * Set the current link rate and lane cnt to panel. They may have been * adjusted and the values are different from them in DPCD CAP */ - dp_link.num_lanes = ctrl->lane_cnt; + dp_link.lanes = ctrl->lane_cnt; dp_link.rate = drm_dp_bw_code_to_link_rate(ctrl->link_rate); dp_link.capabilities = ctrl->dp_link.capabilities; if (drm_dp_link_configure(ctrl->drm_aux, &dp_link) < 0) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index ec999d9f15f6..00298acf804b 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -494,7 +494,7 @@ static int cdn_dp_disable(struct cdn_dp_device *dp) cdn_dp_clk_disable(dp); dp->active = false; dp->link.rate = 0; - dp->link.num_lanes = 0; + dp->link.lanes = 0; if (!dp->connected) { kfree(dp->edid); dp->edid = NULL; @@ -586,7 +586,7 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) struct cdn_dp_port *port = cdn_dp_connected_port(dp); u8 sink_lanes = drm_dp_max_lane_count(dp->dpcd);
- if (!port || !dp->link.rate || !dp->link.num_lanes) + if (!port || !dp->link.rate || !dp->link.lanes) return false;
if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status, @@ -969,7 +969,7 @@ static void cdn_dp_pd_event_work(struct work_struct *work) /* Enabled and connected with a sink, re-train if requested */ } else if (!cdn_dp_check_link_status(dp)) { unsigned int rate = dp->link.rate; - unsigned int lanes = dp->link.num_lanes; + unsigned int lanes = dp->link.lanes; struct drm_display_mode *mode = &dp->mode;
DRM_DEV_INFO(dp->dev, "Connected with sink. Re-train link\n"); @@ -982,7 +982,7 @@ static void cdn_dp_pd_event_work(struct work_struct *work)
/* If training result is changed, update the video config */ if (mode->clock && - (rate != dp->link.rate || lanes != dp->link.num_lanes)) { + (rate != dp->link.rate || lanes != dp->link.lanes)) { ret = cdn_dp_config_video(dp); if (ret) { dp->connected = false; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index eb3042c6d1b2..14421be48c90 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -544,7 +544,7 @@ static int cdn_dp_get_training_status(struct cdn_dp_device *dp) goto err_get_training_status;
dp->link.rate = status[0]; - dp->link.num_lanes = status[1]; + dp->link.lanes = status[1];
err_get_training_status: if (ret) @@ -569,7 +569,7 @@ int cdn_dp_train_link(struct cdn_dp_device *dp) }
DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->link.rate, - dp->link.num_lanes); + dp->link.lanes); return ret; }
@@ -667,14 +667,13 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) do { tu_size_reg += 2; symbol = tu_size_reg * mode->clock * bit_per_pix; - do_div(symbol, dp->link.num_lanes * link_rate * 8); + do_div(symbol, dp->link.lanes * link_rate * 8); rem = do_div(symbol, 1000); if (tu_size_reg > 64) { ret = -EINVAL; DRM_DEV_ERROR(dp->dev, "tu error, clk:%d, lanes:%d, rate:%d\n", - mode->clock, dp->link.num_lanes, - link_rate); + mode->clock, dp->link.lanes, link_rate); goto err_config_video; } } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || @@ -688,7 +687,7 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
/* set the FIFO Buffer size */ val = div_u64(mode->clock * (symbol + 1), 1000) + link_rate; - val /= (dp->link.num_lanes * link_rate); + val /= (dp->link.lanes * link_rate); val = div_u64(8 * (symbol + 1), bit_per_pix) - val; val += 2; ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val); @@ -846,7 +845,7 @@ static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp, u32 val;
if (audio->channels == 2) { - if (dp->link.num_lanes == 1) + if (dp->link.lanes == 1) sub_pckt_num = 2; else sub_pckt_num = 4; diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index d84e81ff36ad..844fb946fb2e 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -788,14 +788,14 @@ int drm_dp_aux_train(struct drm_dp_aux *aux, struct drm_dp_link *link, if (tp == DP_TRAINING_PATTERN_DISABLE) return 0;
- for (i = 0; i < link->num_lanes; i++) + for (i = 0; i < link->lanes; i++) values[i] = DP_TRAIN_MAX_PRE_EMPHASIS_REACHED | DP_TRAIN_PRE_EMPH_LEVEL_0 | DP_TRAIN_MAX_SWING_REACHED | DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
err = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, values, - link->num_lanes); + link->lanes); if (err < 0) return err;
@@ -807,13 +807,13 @@ int drm_dp_aux_train(struct drm_dp_aux *aux, struct drm_dp_link *link,
switch (tp) { case DP_TRAINING_PATTERN_1: - if (!drm_dp_clock_recovery_ok(status, link->num_lanes)) + if (!drm_dp_clock_recovery_ok(status, link->lanes)) return -EAGAIN;
break;
case DP_TRAINING_PATTERN_2: - if (!drm_dp_channel_eq_ok(status, link->num_lanes)) + if (!drm_dp_channel_eq_ok(status, link->lanes)) return -EAGAIN;
break; diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 8c03ca870a09..e3c3f5add411 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -585,7 +585,7 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, if (err < 0) return err;
- for (i = 0, value = 0; i < link->num_lanes; i++) { + for (i = 0, value = 0; i < link->lanes; i++) { unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | SOR_DP_TPG_SCRAMBLER_NONE | SOR_DP_TPG_PATTERN_TRAIN1; @@ -606,7 +606,7 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, value |= SOR_DP_SPARE_MACRO_SOR_CLK; tegra_sor_writel(sor, value, SOR_DP_SPARE0);
- for (i = 0, value = 0; i < link->num_lanes; i++) { + for (i = 0, value = 0; i < link->lanes; i++) { unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | SOR_DP_TPG_SCRAMBLER_NONE | SOR_DP_TPG_PATTERN_TRAIN2; @@ -621,7 +621,7 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, if (err < 0) return err;
- for (i = 0, value = 0; i < link->num_lanes; i++) { + for (i = 0, value = 0; i < link->lanes; i++) { unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | SOR_DP_TPG_SCRAMBLER_GALIOS | SOR_DP_TPG_PATTERN_NONE; @@ -848,11 +848,11 @@ static int tegra_sor_compute_config(struct tegra_sor *sor, u32 num_syms_per_line; unsigned int i;
- if (!link_rate || !link->num_lanes || !pclk || !config->bits_per_pixel) + if (!link_rate || !link->lanes || !pclk || !config->bits_per_pixel) return -EINVAL;
- output = link_rate * 8 * link->num_lanes; input = pclk * config->bits_per_pixel; + output = link_rate * 8 * link->lanes;
if (input >= output) return -ERANGE; @@ -895,7 +895,7 @@ static int tegra_sor_compute_config(struct tegra_sor *sor, watermark = div_u64(watermark + params.error, f); config->watermark = watermark + (config->bits_per_pixel / 8) + 2; num_syms_per_line = (mode->hdisplay * config->bits_per_pixel) * - (link->num_lanes * 8); + (link->lanes * 8);
if (config->watermark > 30) { config->watermark = 30; @@ -915,12 +915,12 @@ static int tegra_sor_compute_config(struct tegra_sor *sor, if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) config->hblank_symbols -= 3;
- config->hblank_symbols -= 12 / link->num_lanes; + config->hblank_symbols -= 12 / link->lanes;
/* compute the number of symbols per vertical blanking interval */ num = (mode->hdisplay - 25) * link_rate; config->vblank_symbols = div_u64(num, pclk); - config->vblank_symbols -= 36 / link->num_lanes + 4; + config->vblank_symbols -= 36 / link->lanes + 4;
dev_dbg(sor->dev, "blank symbols: H:%u V:%u\n", config->hblank_symbols, config->vblank_symbols); @@ -2004,17 +2004,17 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) /* power DP lanes */ value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
- if (link.num_lanes <= 2) + if (link.lanes <= 2) value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2); else value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2;
- if (link.num_lanes <= 1) + if (link.lanes <= 1) value &= ~SOR_DP_PADCTL_PD_TXD_1; else value |= SOR_DP_PADCTL_PD_TXD_1;
- if (link.num_lanes == 0) + if (link.lanes == 0) value &= ~SOR_DP_PADCTL_PD_TXD_0; else value |= SOR_DP_PADCTL_PD_TXD_0; @@ -2023,7 +2023,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
value = tegra_sor_readl(sor, SOR_DP_LINKCTL0); value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; - value |= SOR_DP_LINKCTL_LANE_COUNT(link.num_lanes); + value |= SOR_DP_LINKCTL_LANE_COUNT(link.lanes); tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
/* start lane sequencer */ @@ -2080,7 +2080,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) dev_err(sor->dev, "failed to configure eDP link: %d\n", err);
rate = drm_dp_link_rate_to_bw_code(link.rate); - lanes = link.num_lanes; + lanes = link.lanes;
value = tegra_sor_readl(sor, SOR_CLK_CNTRL); value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; @@ -2098,7 +2098,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder)
/* disable training pattern generator */
- for (i = 0; i < link.num_lanes; i++) { + for (i = 0; i < link.lanes; i++) { unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | SOR_DP_TPG_SCRAMBLER_GALIOS | SOR_DP_TPG_PATTERN_NONE; diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 6ced0bc6feb9..216a388f467f 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1100,17 +1100,22 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, #define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0)
/** - * struct drm_dp_link - DP link capabilities + * struct drm_dp_link - DP link capabilities and configuration * @revision: DP specification revision supported on the link - * @rate: maximum clock rate supported on the link - * @num_lanes: maximum number of lanes supported on the link + * @max_rate: maximum clock rate supported on the link + * @max_lanes: maximum number of lanes supported on the link * @capabilities: bitmask of capabilities supported on the link + * @rate: currently configured link rate + * @lanes: currently configured number of lanes */ struct drm_dp_link { unsigned char revision; - unsigned int rate; - unsigned int num_lanes; + unsigned int max_rate; + unsigned int max_lanes; unsigned long capabilities; + + unsigned int rate; + unsigned int lanes; };
int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);
From: Thierry Reding treding@nvidia.com
Rather than storing capabilities as flags in an integer, use a separate boolean per capability. This simplifies the code that checks for these capabilities.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/bridge/tc358767.c | 6 +++--- drivers/gpu/drm/drm_dp_helper.c | 19 ++++++++++++++++--- drivers/gpu/drm/msm/edp/edp_ctrl.c | 4 ++-- drivers/gpu/drm/tegra/sor.c | 4 ++-- include/drm/drm_dp_helper.h | 17 ++++++++++++++--- 5 files changed, 37 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 55037f999579..ad278a79feb3 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -635,8 +635,8 @@ static int tc_get_display_props(struct tc_data *tc) tc->link.base.revision >> 4, tc->link.base.revision & 0x0f, (tc->link.base.rate == 162000) ? "1.62Gbps" : "2.7Gbps", tc->link.base.lanes, - (tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) ? - "enhanced" : "non-enhanced"); + (tc->link.base.caps.enhanced_framing) ? "enhanced" : + "non-enhanced"); dev_dbg(tc->dev, "ANSI 8B/10B: %d\n", tc->link.coding8b10b); dev_dbg(tc->dev, "Display ASSR: %d, TC358767 ASSR: %d\n", tc->link.assr, tc->assr); @@ -1015,7 +1015,7 @@ static int tc_main_link_stream(struct tc_data *tc, int state)
if (state) { value = VID_MN_GEN | DP_EN; - if (tc->link.base.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) + if (tc->link.base.caps.enhanced_framing) value |= EF_EN; tc_write(DP0CTL, value); /* diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index f401377a199d..6fbce4554029 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -315,6 +315,18 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, } EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
+static void drm_dp_link_caps_reset(struct drm_dp_link_caps *caps) +{ + caps->enhanced_framing = false; +} + +void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, + const struct drm_dp_link_caps *src) +{ + dest->enhanced_framing = src->enhanced_framing; +} +EXPORT_SYMBOL(drm_dp_link_caps_copy); + static void drm_dp_link_reset(struct drm_dp_link *link) { if (!link) @@ -323,7 +335,8 @@ static void drm_dp_link_reset(struct drm_dp_link *link) link->revision = 0; link->max_rate = 0; link->max_lanes = 0; - link->capabilities = 0; + + drm_dp_link_caps_reset(&link->caps);
link->rate = 0; link->lanes = 0; @@ -356,7 +369,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->max_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
if (values[2] & DP_ENHANCED_FRAME_CAP) - link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING; + link->caps.enhanced_framing = true;
link->rate = link->max_rate; link->lanes = link->max_lanes; @@ -449,7 +462,7 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) values[0] = drm_dp_link_rate_to_bw_code(link->rate); values[1] = link->lanes;
- if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) + if (link->caps.enhanced_framing) values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values)); diff --git a/drivers/gpu/drm/msm/edp/edp_ctrl.c b/drivers/gpu/drm/msm/edp/edp_ctrl.c index 71192a0656e3..489fa81bb508 100644 --- a/drivers/gpu/drm/msm/edp/edp_ctrl.c +++ b/drivers/gpu/drm/msm/edp/edp_ctrl.c @@ -447,7 +447,7 @@ static void edp_config_ctrl(struct edp_ctrl *ctrl)
data = EDP_CONFIGURATION_CTRL_LANES(ctrl->lane_cnt - 1);
- if (ctrl->dp_link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) + if (ctrl->dp_link.caps.enhanced_framing) data |= EDP_CONFIGURATION_CTRL_ENHANCED_FRAMING;
depth = EDP_6BIT; @@ -769,7 +769,7 @@ static int edp_do_link_train(struct edp_ctrl *ctrl) */ dp_link.lanes = ctrl->lane_cnt; dp_link.rate = drm_dp_bw_code_to_link_rate(ctrl->link_rate); - dp_link.capabilities = ctrl->dp_link.capabilities; + drm_dp_link_caps_copy(&dp_link.caps, &ctrl->dp_link.caps); if (drm_dp_link_configure(ctrl->drm_aux, &dp_link) < 0) return EDP_TRAIN_FAIL;
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index e3c3f5add411..f24c012f1b41 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -912,7 +912,7 @@ static int tegra_sor_compute_config(struct tegra_sor *sor, num = ((mode->htotal - mode->hdisplay) - 7) * link_rate; config->hblank_symbols = div_u64(num, pclk);
- if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) + if (link->caps.enhanced_framing) config->hblank_symbols -= 3;
config->hblank_symbols -= 12 / link->lanes; @@ -2091,7 +2091,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; value |= SOR_DP_LINKCTL_LANE_COUNT(lanes);
- if (link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) + if (link.caps.enhanced_framing) value |= SOR_DP_LINKCTL_ENHANCED_FRAME;
tegra_sor_writel(sor, value, SOR_DP_LINKCTL0); diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 216a388f467f..e3429abb03f9 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1097,14 +1097,24 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, /* * DisplayPort link */ -#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0) + +/** + * struct drm_dp_link_caps - DP link capabilities + * @enhanced_framing: enhanced framing capability (mandatory as of DP 1.2) + */ +struct drm_dp_link_caps { + bool enhanced_framing; +}; + +void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, + const struct drm_dp_link_caps *src);
/** * struct drm_dp_link - DP link capabilities and configuration * @revision: DP specification revision supported on the link * @max_rate: maximum clock rate supported on the link * @max_lanes: maximum number of lanes supported on the link - * @capabilities: bitmask of capabilities supported on the link + * @caps: capabilities supported on the link (see &drm_dp_link_caps) * @rate: currently configured link rate * @lanes: currently configured number of lanes */ @@ -1112,7 +1122,8 @@ struct drm_dp_link { unsigned char revision; unsigned int max_rate; unsigned int max_lanes; - unsigned long capabilities; + + struct drm_dp_link_caps caps;
unsigned int rate; unsigned int lanes;
From: Thierry Reding treding@nvidia.com
Use existing parsing helpers to probe a DisplayPort link.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 29 ++++++++++++++++++++++------- include/drm/drm_dp_helper.h | 2 ++ 2 files changed, 24 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 6fbce4554029..982dd9febb1c 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -299,6 +299,22 @@ ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, } EXPORT_SYMBOL(drm_dp_dpcd_write);
+/** + * drm_dp_dpcd_read_link_caps() - read DPCD link capabilities + * @aux: DisplayPort AUX channel + * @caps: buffer to store the link capabilities in + * + * Returns: + * The number of bytes transferred on success or a negative error code on + * failure. + */ +int drm_dp_dpcd_read_link_caps(struct drm_dp_aux *aux, + u8 caps[DP_RECEIVER_CAP_SIZE]) +{ + return drm_dp_dpcd_read(aux, DP_DPCD_REV, caps, DP_RECEIVER_CAP_SIZE); +} +EXPORT_SYMBOL(drm_dp_dpcd_read_link_caps); + /** * drm_dp_dpcd_read_link_status() - read DPCD link status (bytes 0x202-0x207) * @aux: DisplayPort AUX channel @@ -355,21 +371,20 @@ static void drm_dp_link_reset(struct drm_dp_link *link) */ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) { - u8 values[3]; + u8 values[DP_RECEIVER_CAP_SIZE]; int err;
drm_dp_link_reset(link);
- err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values)); + err = drm_dp_dpcd_read_link_caps(aux, values); if (err < 0) return err;
- link->revision = values[0]; - link->max_rate = drm_dp_bw_code_to_link_rate(values[1]); - link->max_lanes = values[2] & DP_MAX_LANE_COUNT_MASK; + link->revision = values[DP_DPCD_REV]; + link->max_rate = drm_dp_max_link_rate(values); + link->max_lanes = drm_dp_max_lane_count(values);
- if (values[2] & DP_ENHANCED_FRAME_CAP) - link->caps.enhanced_framing = true; + link->caps.enhanced_framing = drm_dp_enhanced_frame_cap(values);
link->rate = link->max_rate; link->lanes = link->max_lanes; diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index e3429abb03f9..85ff67958875 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1091,6 +1091,8 @@ static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux, return drm_dp_dpcd_write(aux, offset, &value, 1); }
+int drm_dp_dpcd_read_link_caps(struct drm_dp_aux *aux, + u8 caps[DP_RECEIVER_CAP_SIZE]); int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, u8 status[DP_LINK_STATUS_SIZE]);
From: Thierry Reding treding@nvidia.com
While probing the DisplayPort link, query the fast training capability. If supported, drivers can use the fast link training sequence instead of the more involved full link training sequence.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 3 +++ include/drm/drm_dp_helper.h | 9 +++++++++ 2 files changed, 12 insertions(+)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 982dd9febb1c..2d56aeda1b75 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -334,12 +334,14 @@ EXPORT_SYMBOL(drm_dp_dpcd_read_link_status); static void drm_dp_link_caps_reset(struct drm_dp_link_caps *caps) { caps->enhanced_framing = false; + caps->fast_training = false; }
void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, const struct drm_dp_link_caps *src) { dest->enhanced_framing = src->enhanced_framing; + dest->fast_training = src->fast_training; } EXPORT_SYMBOL(drm_dp_link_caps_copy);
@@ -385,6 +387,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->max_lanes = drm_dp_max_lane_count(values);
link->caps.enhanced_framing = drm_dp_enhanced_frame_cap(values); + link->caps.fast_training = drm_dp_fast_training_cap(values);
link->rate = link->max_rate; link->lanes = link->max_lanes; diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 85ff67958875..680d6719d463 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -976,6 +976,13 @@ drm_dp_is_branch(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) return dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT; }
+static inline bool +drm_dp_fast_training_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_DPCD_REV] >= 0x11 && + (dpcd[DP_MAX_DOWNSPREAD] & DP_NO_AUX_HANDSHAKE_LINK_TRAINING); +} + /* * DisplayPort AUX channel */ @@ -1103,9 +1110,11 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, /** * struct drm_dp_link_caps - DP link capabilities * @enhanced_framing: enhanced framing capability (mandatory as of DP 1.2) + * @fast_training: AUX CH handshake not required for link training */ struct drm_dp_link_caps { bool enhanced_framing; + bool fast_training; };
void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
From: Thierry Reding treding@nvidia.com
The TPS3 capability can be exposed by DP 1.2 and later sinks if they support the alternative training pattern for channel equalization.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 3 +++ include/drm/drm_dp_helper.h | 2 ++ 2 files changed, 5 insertions(+)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 2d56aeda1b75..664d814ab250 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -334,6 +334,7 @@ EXPORT_SYMBOL(drm_dp_dpcd_read_link_status); static void drm_dp_link_caps_reset(struct drm_dp_link_caps *caps) { caps->enhanced_framing = false; + caps->tps3_supported = false; caps->fast_training = false; }
@@ -341,6 +342,7 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, const struct drm_dp_link_caps *src) { dest->enhanced_framing = src->enhanced_framing; + dest->tps3_supported = src->tps3_supported; dest->fast_training = src->fast_training; } EXPORT_SYMBOL(drm_dp_link_caps_copy); @@ -387,6 +389,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->max_lanes = drm_dp_max_lane_count(values);
link->caps.enhanced_framing = drm_dp_enhanced_frame_cap(values); + link->caps.tps3_supported = drm_dp_tps3_supported(values); link->caps.fast_training = drm_dp_fast_training_cap(values);
link->rate = link->max_rate; diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 680d6719d463..81af618c6b4a 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1110,10 +1110,12 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, /** * struct drm_dp_link_caps - DP link capabilities * @enhanced_framing: enhanced framing capability (mandatory as of DP 1.2) + * @tps3_supported: training pattern sequence 3 supported for equalization * @fast_training: AUX CH handshake not required for link training */ struct drm_dp_link_caps { bool enhanced_framing; + bool tps3_supported; bool fast_training; };
From: Thierry Reding treding@nvidia.com
Parse from the sink capabilities whether or not it supports ANSI 8B/10B channel coding as specified in ANSI X3.230-1994, clause 11.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 3 +++ include/drm/drm_dp_helper.h | 9 +++++++++ 2 files changed, 12 insertions(+)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 664d814ab250..743343527bfa 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -336,6 +336,7 @@ static void drm_dp_link_caps_reset(struct drm_dp_link_caps *caps) caps->enhanced_framing = false; caps->tps3_supported = false; caps->fast_training = false; + caps->channel_coding = false; }
void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, @@ -344,6 +345,7 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, dest->enhanced_framing = src->enhanced_framing; dest->tps3_supported = src->tps3_supported; dest->fast_training = src->fast_training; + dest->channel_coding = src->channel_coding; } EXPORT_SYMBOL(drm_dp_link_caps_copy);
@@ -391,6 +393,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->caps.enhanced_framing = drm_dp_enhanced_frame_cap(values); link->caps.tps3_supported = drm_dp_tps3_supported(values); link->caps.fast_training = drm_dp_fast_training_cap(values); + link->caps.channel_coding = drm_dp_channel_coding_supported(values);
link->rate = link->max_rate; link->lanes = link->max_lanes; diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 81af618c6b4a..fe7aef2c0736 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -89,6 +89,7 @@ # define DP_DETAILED_CAP_INFO_AVAILABLE (1 << 4) /* DPI */
#define DP_MAIN_LINK_CHANNEL_CODING 0x006 +# define DP_CAP_ANSI_8B10B (1 << 0)
#define DP_DOWN_STREAM_PORT_COUNT 0x007 # define DP_PORT_COUNT_MASK 0x0f @@ -983,6 +984,12 @@ drm_dp_fast_training_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) (dpcd[DP_MAX_DOWNSPREAD] & DP_NO_AUX_HANDSHAKE_LINK_TRAINING); }
+static inline bool +drm_dp_channel_coding_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_MAIN_LINK_CHANNEL_CODING] & DP_CAP_ANSI_8B10B; +} + /* * DisplayPort AUX channel */ @@ -1112,11 +1119,13 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, * @enhanced_framing: enhanced framing capability (mandatory as of DP 1.2) * @tps3_supported: training pattern sequence 3 supported for equalization * @fast_training: AUX CH handshake not required for link training + * @channel_coding: ANSI 8B/10B channel coding capability */ struct drm_dp_link_caps { bool enhanced_framing; bool tps3_supported; bool fast_training; + bool channel_coding; };
void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
From: Thierry Reding treding@nvidia.com
Parse from the sink capabilities whether or not the eDP alternate scrambler reset value of 0xfffe is supported.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 5 +++++ include/drm/drm_dp_helper.h | 9 +++++++++ 2 files changed, 14 insertions(+)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 743343527bfa..23429a53452d 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -337,6 +337,7 @@ static void drm_dp_link_caps_reset(struct drm_dp_link_caps *caps) caps->tps3_supported = false; caps->fast_training = false; caps->channel_coding = false; + caps->alternate_scrambler_reset = false; }
void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, @@ -346,6 +347,7 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, dest->tps3_supported = src->tps3_supported; dest->fast_training = src->fast_training; dest->channel_coding = src->channel_coding; + dest->alternate_scrambler_reset = src->alternate_scrambler_reset; } EXPORT_SYMBOL(drm_dp_link_caps_copy);
@@ -395,6 +397,9 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->caps.fast_training = drm_dp_fast_training_cap(values); link->caps.channel_coding = drm_dp_channel_coding_supported(values);
+ if (drm_dp_alternate_scrambler_reset_cap(values)) + link->caps.alternate_scrambler_reset = true; + link->rate = link->max_rate; link->lanes = link->max_lanes;
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index fe7aef2c0736..c52b6cf96a81 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -990,6 +990,13 @@ drm_dp_channel_coding_supported(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) return dpcd[DP_MAIN_LINK_CHANNEL_CODING] & DP_CAP_ANSI_8B10B; }
+static inline bool +drm_dp_alternate_scrambler_reset_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_EDP_CONFIGURATION_CAP] & + DP_ALTERNATE_SCRAMBLER_RESET_CAP; +} + /* * DisplayPort AUX channel */ @@ -1120,12 +1127,14 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, * @tps3_supported: training pattern sequence 3 supported for equalization * @fast_training: AUX CH handshake not required for link training * @channel_coding: ANSI 8B/10B channel coding capability + * @alternate_scrambler_reset: eDP alternate scrambler reset capability */ struct drm_dp_link_caps { bool enhanced_framing; bool tps3_supported; bool fast_training; bool channel_coding; + bool alternate_scrambler_reset; };
void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
From: Thierry Reding treding@nvidia.com
If the sink supports eDP, read the eDP revision from it's DPCD.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 16 +++++++++++++++- include/drm/drm_dp_helper.h | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 23429a53452d..c78cc62f149f 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -361,6 +361,7 @@ static void drm_dp_link_reset(struct drm_dp_link *link) link->max_lanes = 0;
drm_dp_link_caps_reset(&link->caps); + link->edp = 0;
link->rate = 0; link->lanes = 0; @@ -397,9 +398,22 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->caps.fast_training = drm_dp_fast_training_cap(values); link->caps.channel_coding = drm_dp_channel_coding_supported(values);
- if (drm_dp_alternate_scrambler_reset_cap(values)) + if (drm_dp_alternate_scrambler_reset_cap(values)) { + static const u8 edp_revs[] = { 0x11, 0x12, 0x13, 0x14 }; + u8 value; + link->caps.alternate_scrambler_reset = true;
+ err = drm_dp_dpcd_readb(aux, DP_EDP_DPCD_REV, &value); + if (err < 0) + return err; + + if (value >= ARRAY_SIZE(edp_revs)) + DRM_ERROR("unsupported eDP version: %02x\n", value); + else + link->edp = edp_revs[value]; + } + link->rate = link->max_rate; link->lanes = link->max_lanes;
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index c52b6cf96a81..705c56ebe74d 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -1146,6 +1146,7 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, * @max_rate: maximum clock rate supported on the link * @max_lanes: maximum number of lanes supported on the link * @caps: capabilities supported on the link (see &drm_dp_link_caps) + * @edp: eDP revision (0x11: eDP 1.1, 0x12: eDP 1.2, ...) * @rate: currently configured link rate * @lanes: currently configured number of lanes */ @@ -1155,6 +1156,7 @@ struct drm_dp_link { unsigned int max_lanes;
struct drm_dp_link_caps caps; + unsigned char edp;
unsigned int rate; unsigned int lanes;
From: Thierry Reding treding@nvidia.com
Store the AUX read interval from DPCD, so that it can be used to wait for the durations given in the specification during link training.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 3 +++ include/drm/drm_dp_helper.h | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index c78cc62f149f..0d9d5360465c 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -361,6 +361,7 @@ static void drm_dp_link_reset(struct drm_dp_link *link) link->max_lanes = 0;
drm_dp_link_caps_reset(&link->caps); + link->aux_rd_interval = 0; link->edp = 0;
link->rate = 0; @@ -414,6 +415,8 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) link->edp = edp_revs[value]; }
+ link->aux_rd_interval = drm_dp_aux_rd_interval(values); + link->rate = link->max_rate; link->lanes = link->max_lanes;
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 705c56ebe74d..8213f1c53f9b 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -997,6 +997,22 @@ drm_dp_alternate_scrambler_reset_cap(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) DP_ALTERNATE_SCRAMBLER_RESET_CAP; }
+/** + * drm_dp_read_aux_interval() - read the AUX read interval from the DPCD + * @dpcd: receiver capacity buffer + * + * Reads the AUX read interval (in microseconds) from the DPCD. Note that the + * TRAINING_AUX_RD_INTERVAL stores the value in units of 4 milliseconds. + * + * Returns: + * The read AUX interval in microseconds. + */ +static inline unsigned int +drm_dp_aux_rd_interval(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + return dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4000; +} + /* * DisplayPort AUX channel */ @@ -1146,6 +1162,7 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest, * @max_rate: maximum clock rate supported on the link * @max_lanes: maximum number of lanes supported on the link * @caps: capabilities supported on the link (see &drm_dp_link_caps) + * @aux_rd_interval: AUX read interval to use for training (in microseconds) * @edp: eDP revision (0x11: eDP 1.1, 0x12: eDP 1.2, ...) * @rate: currently configured link rate * @lanes: currently configured number of lanes @@ -1156,6 +1173,7 @@ struct drm_dp_link { unsigned int max_lanes;
struct drm_dp_link_caps caps; + unsigned int aux_rd_interval; unsigned char edp;
unsigned int rate;
From: Thierry Reding treding@nvidia.com
Use microsecond sleeps for the clock recovery and channel equalization delays during link training. The duration of these delays can be from 100 us up to 16 ms. It is rude to busy-loop for that amount of time.
While at it, also convert to standard coding style by putting the opening braces in a function definition on a new line.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 0d9d5360465c..0d9e66ea9667 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -118,19 +118,29 @@ u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SI } EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis);
-void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { - if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) - udelay(100); +void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + unsigned int min; + + if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] != 0) + min = dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4000; else - mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); + min = 100; + + usleep_range(min, min * 2); } EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);
-void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { - if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0) - udelay(400); +void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + unsigned int min; + + if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] != 0) + min = dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4000; else - mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4); + min = 400; + + usleep_range(min, min * 2); } EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);
From: Thierry Reding treding@nvidia.com
Make use of the newly added drm_dp_aux_rd_interval() helper in existing DP link training helpers and add comments about minimum required delays mandated by the DP specification.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 0d9e66ea9667..dabac772dfc3 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -120,11 +120,13 @@ EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis);
void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { - unsigned int min; + unsigned int min = drm_dp_aux_rd_interval(dpcd);
- if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] != 0) - min = dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4000; - else + /* + * The DP specification mandates a delay of 100 us during clock + * recovery if the sink doesn't report an AUX read interval. + */ + if (min == 0) min = 100;
usleep_range(min, min * 2); @@ -133,11 +135,13 @@ EXPORT_SYMBOL(drm_dp_link_train_clock_recovery_delay);
void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { - unsigned int min; + unsigned int min = drm_dp_aux_rd_interval(dpcd);
- if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] != 0) - min = dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4000; - else + /* + * The DP specification mandates a delay of 400 us during clock + * recovery if the sink doesn't report an AUX read interval. + */ + if (min == 0) min = 400;
usleep_range(min, min * 2);
From: Thierry Reding treding@nvidia.com
If the transmitter supports pre-emphasis post cursor2 the sink will request adjustments in a similar way to how it requests adjustments to the voltage swing and pre-emphasis settings.
Add a helper to extract these adjustments on a per-lane basis from the DPCD link status.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 10 ++++++++++ include/drm/drm_dp_helper.h | 10 ++++++++++ 2 files changed, 20 insertions(+)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index dabac772dfc3..f1a7f18ab795 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -118,6 +118,16 @@ u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SI } EXPORT_SYMBOL(drm_dp_get_adjust_request_pre_emphasis);
+u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZE], + unsigned int lane) +{ + unsigned int offset = DP_ADJUST_REQUEST_POST_CURSOR2; + u8 value = dp_link_status(link_status, offset); + + return (value >> (lane << 1)) & 0x3; +} +EXPORT_SYMBOL(drm_dp_get_adjust_request_post_cursor); + void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) { unsigned int min = drm_dp_aux_rd_interval(dpcd); diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 8213f1c53f9b..4c7badcde945 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -512,6 +512,14 @@ # define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6
#define DP_ADJUST_REQUEST_POST_CURSOR2 0x20c +# define DP_ADJUST_POST_CURSOR2_LANE0_MASK 0x03 +# define DP_ADJUST_POST_CURSOR2_LANE0_SHIFT 0 +# define DP_ADJUST_POST_CURSOR2_LANE1_MASK 0x0c +# define DP_ADJUST_POST_CURSOR2_LANE1_SHIFT 2 +# define DP_ADJUST_POST_CURSOR2_LANE2_MASK 0x30 +# define DP_ADJUST_POST_CURSOR2_LANE2_SHIFT 4 +# define DP_ADJUST_POST_CURSOR2_LANE3_MASK 0xc0 +# define DP_ADJUST_POST_CURSOR2_LANE3_SHIFT 6
#define DP_TEST_REQUEST 0x218 # define DP_TEST_LINK_TRAINING (1 << 0) @@ -892,6 +900,8 @@ u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE], int lane); u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE], int lane); +u8 drm_dp_get_adjust_request_post_cursor(const u8 link_status[DP_LINK_STATUS_SIZE], + unsigned int lane);
#define DP_BRANCH_OUI_HEADER_SIZE 0xc #define DP_RECEIVER_CAP_SIZE 0xf
From: Thierry Reding treding@nvidia.com
Make use of ANSI 8B/10B channel coding if the DisplayPort sink supports it.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index f1a7f18ab795..828dc091b015 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -526,7 +526,7 @@ EXPORT_SYMBOL(drm_dp_link_power_down); */ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) { - u8 values[2]; + u8 values[2], value = 0; int err;
values[0] = drm_dp_link_rate_to_bw_code(link->rate); @@ -539,6 +539,13 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) if (err < 0) return err;
+ if (link->caps.channel_coding) + value = DP_SET_ANSI_8B10B; + + err = drm_dp_dpcd_writeb(aux, DP_MAIN_LINK_CHANNEL_CODING_SET, value); + if (err < 0) + return err; + return 0; } EXPORT_SYMBOL(drm_dp_link_configure);
From: Thierry Reding treding@nvidia.com
If the sink is eDP and supports the alternate scrambler reset, enable it.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 828dc091b015..c8b18c0161d7 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -546,6 +546,13 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) if (err < 0) return err;
+ if (link->caps.alternate_scrambler_reset) { + err = drm_dp_dpcd_writeb(aux, DP_EDP_CONFIGURATION_SET, + DP_ALTERNATE_SCRAMBLER_RESET_ENABLE); + if (err < 0) + return err; + } + return 0; } EXPORT_SYMBOL(drm_dp_link_configure);
From: Thierry Reding treding@nvidia.com
This helper chooses an appropriate configuration, according to the bitrate requirements of the video mode and the capabilities of the DisplayPort sink.
Signed-off-by: Thierry Reding treding@nvidia.com --- drivers/gpu/drm/drm_dp_helper.c | 55 +++++++++++++++++++++++++++++++++++++++++ include/drm/drm_dp_helper.h | 5 ++++ 2 files changed, 60 insertions(+)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index c8b18c0161d7..fb6ee3ebc37d 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -557,6 +557,61 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) } EXPORT_SYMBOL(drm_dp_link_configure);
+/** + * drm_dp_link_choose() - choose the lowest possible configuration for a mode + * @link: DRM DP link object + * @mode: DRM display mode + * @info: DRM display information + * + * According to the eDP specification, a source should select a configuration + * with the lowest number of lanes and the lowest possible link rate that can + * match the bitrate requirements of a video mode. However it must ensure not + * to exceed the capabilities of the sink. + * + * Returns: 0 on success or a negative error code on failure. + */ +int drm_dp_link_choose(struct drm_dp_link *link, + const struct drm_display_mode *mode, + const struct drm_display_info *info) +{ + /* available link symbol clock rates */ + static const unsigned int rates[3] = { 162000, 270000, 540000 }; + /* available number of lanes */ + static const unsigned int lanes[3] = { 1, 2, 4 }; + unsigned long requirement, capacity; + unsigned int rate = link->max_rate; + unsigned int i, j; + + /* bandwidth requirement */ + requirement = mode->clock * info->bpc * 3; + + for (i = 0; i < ARRAY_SIZE(lanes) && lanes[i] <= link->max_lanes; i++) { + for (j = 0; j < ARRAY_SIZE(rates) && rates[j] <= rate; j++) { + /* + * Capacity for this combination of lanes and rate, + * factoring in the ANSI 8B/10B encoding. + * + * Link rates in the DRM DP helpers are really link + * symbol frequencies, so a tenth of the actual rate + * of the link. + */ + capacity = lanes[i] * (rates[j] * 10) * 8 / 10; + + if (capacity >= requirement) { + DRM_DEBUG_KMS("using %u lanes at %u kHz (%lu/%lu kbps)\n", + lanes[i], rates[j], requirement, + capacity); + link->lanes = lanes[i]; + link->rate = rates[j]; + return 0; + } + } + } + + return -ERANGE; +} +EXPORT_SYMBOL(drm_dp_link_choose); + /** * drm_dp_downstream_max_clock() - extract branch device max * pixel rate for legacy VGA diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 4c7badcde945..39d134f9a954 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -27,6 +27,8 @@ #include <linux/i2c.h> #include <linux/delay.h>
+#include <drm/drm_crtc.h> + /* * Unless otherwise noted, all values are from the DP 1.1a spec. Note that * DP and DPCD versions are independent. Differences from 1.0 are not noted, @@ -1194,6 +1196,9 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link); int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link); int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link); int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link); +int drm_dp_link_choose(struct drm_dp_link *link, + const struct drm_display_mode *mode, + const struct drm_display_info *info); int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], const u8 port_cap[4]); int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
On Mon, 05 Feb 2018, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
This helper chooses an appropriate configuration, according to the bitrate requirements of the video mode and the capabilities of the DisplayPort sink.
Signed-off-by: Thierry Reding treding@nvidia.com
drivers/gpu/drm/drm_dp_helper.c | 55 +++++++++++++++++++++++++++++++++++++++++ include/drm/drm_dp_helper.h | 5 ++++ 2 files changed, 60 insertions(+)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index c8b18c0161d7..fb6ee3ebc37d 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -557,6 +557,61 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) } EXPORT_SYMBOL(drm_dp_link_configure);
+/**
- drm_dp_link_choose() - choose the lowest possible configuration for a mode
- @link: DRM DP link object
- @mode: DRM display mode
- @info: DRM display information
- According to the eDP specification, a source should select a configuration
- with the lowest number of lanes and the lowest possible link rate that can
- match the bitrate requirements of a video mode. However it must ensure not
- to exceed the capabilities of the sink.
Just a couple of notes here:
Recent eDP allows more rates than just the ones mentioned. So you'll actually have a number of source and sink rates, and you'll have to intersect them to find the common rates. We have this in i915.
Although the spec says use the "smallest" link parameters possible, we've found that many panels out in the wild only work at the maximum sink parameters. Presumably the sink max rate and width correspond to the native resolution, and not much testing happens using other parameters. :(
BR, Jani.
- Returns: 0 on success or a negative error code on failure.
- */
+int drm_dp_link_choose(struct drm_dp_link *link,
const struct drm_display_mode *mode,
const struct drm_display_info *info)
+{
- /* available link symbol clock rates */
- static const unsigned int rates[3] = { 162000, 270000, 540000 };
- /* available number of lanes */
- static const unsigned int lanes[3] = { 1, 2, 4 };
- unsigned long requirement, capacity;
- unsigned int rate = link->max_rate;
- unsigned int i, j;
- /* bandwidth requirement */
- requirement = mode->clock * info->bpc * 3;
- for (i = 0; i < ARRAY_SIZE(lanes) && lanes[i] <= link->max_lanes; i++) {
for (j = 0; j < ARRAY_SIZE(rates) && rates[j] <= rate; j++) {
/*
* Capacity for this combination of lanes and rate,
* factoring in the ANSI 8B/10B encoding.
*
* Link rates in the DRM DP helpers are really link
* symbol frequencies, so a tenth of the actual rate
* of the link.
*/
capacity = lanes[i] * (rates[j] * 10) * 8 / 10;
if (capacity >= requirement) {
DRM_DEBUG_KMS("using %u lanes at %u kHz (%lu/%lu kbps)\n",
lanes[i], rates[j], requirement,
capacity);
link->lanes = lanes[i];
link->rate = rates[j];
return 0;
}
}
- }
- return -ERANGE;
+} +EXPORT_SYMBOL(drm_dp_link_choose);
/**
- drm_dp_downstream_max_clock() - extract branch device max
pixel rate for legacy VGA
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 4c7badcde945..39d134f9a954 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -27,6 +27,8 @@ #include <linux/i2c.h> #include <linux/delay.h>
+#include <drm/drm_crtc.h>
/*
- Unless otherwise noted, all values are from the DP 1.1a spec. Note that
- DP and DPCD versions are independent. Differences from 1.0 are not noted,
@@ -1194,6 +1196,9 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link); int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link); int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link); int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link); +int drm_dp_link_choose(struct drm_dp_link *link,
const struct drm_display_mode *mode,
const struct drm_display_info *info);
int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], const u8 port_cap[4]); int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
On Wed, Feb 07, 2018 at 02:53:19PM +0200, Jani Nikula wrote:
On Mon, 05 Feb 2018, Thierry Reding thierry.reding@gmail.com wrote:
From: Thierry Reding treding@nvidia.com
This helper chooses an appropriate configuration, according to the bitrate requirements of the video mode and the capabilities of the DisplayPort sink.
Signed-off-by: Thierry Reding treding@nvidia.com
drivers/gpu/drm/drm_dp_helper.c | 55 +++++++++++++++++++++++++++++++++++++++++ include/drm/drm_dp_helper.h | 5 ++++ 2 files changed, 60 insertions(+)
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index c8b18c0161d7..fb6ee3ebc37d 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -557,6 +557,61 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) } EXPORT_SYMBOL(drm_dp_link_configure);
+/**
- drm_dp_link_choose() - choose the lowest possible configuration for a mode
- @link: DRM DP link object
- @mode: DRM display mode
- @info: DRM display information
- According to the eDP specification, a source should select a configuration
- with the lowest number of lanes and the lowest possible link rate that can
- match the bitrate requirements of a video mode. However it must ensure not
- to exceed the capabilities of the sink.
Just a couple of notes here:
Sorry, this got burried under too much email.
Recent eDP allows more rates than just the ones mentioned. So you'll actually have a number of source and sink rates, and you'll have to intersect them to find the common rates. We have this in i915.
I'm aware of this and I have a local patch to implement this. However I currently don't have an eDP setup where I can test it, so I didn't think it right to submit the patch.
Although the spec says use the "smallest" link parameters possible, we've found that many panels out in the wild only work at the maximum sink parameters. Presumably the sink max rate and width correspond to the native resolution, and not much testing happens using other parameters. :(
I suppose I could just drop this helper. Or perhaps add a note about the potential pitfalls. It works fine for my particular use-case, so I could move it into the Tegra driver.
How about the other patches? It's getting really late for v4.17, but I'd like to still get these in if possible so I can reduce my local patch count and merge DP support for Tegra186.
Thierry
dri-devel@lists.freedesktop.org