[PATCH v4 4/4] drm/msm/dp: Introduce link training per-segment for LTTPRs
Aleksandrs Vinarskis
alex.vinarskis at gmail.com
Mon May 5 14:32:01 UTC 2025
On Mon, 5 May 2025 at 00:06, Aleksandrs Vinarskis
<alex.vinarskis at gmail.com> wrote:
>
> On Sun, 4 May 2025 at 05:02, Abhinav Kumar <quic_abhinavk at quicinc.com> wrote:
> >
> > Hi Alex
> >
> > Thanks for the response.
> >
> > My updates below. I also had one question for Abel below.
> >
> > Thanks
> >
> > Abhinav
> >
> > On 5/1/2025 8:56 AM, Aleksandrs Vinarskis wrote:
> > > On Thu, 1 May 2025 at 04:11, Abhinav Kumar <quic_abhinavk at quicinc.com> wrote:
> > >>
> > >>
> > >>
> > >> On 4/29/2025 5:09 PM, Aleksandrs Vinarskis wrote:
> > >>> DisplayPort requires per-segment link training when LTTPR are switched
> > >>> to non-transparent mode, starting with LTTPR closest to the source.
> > >>> Only when each segment is trained individually, source can link train
> > >>> to sink.
> > >>>
> > >>> Implement per-segment link traning when LTTPR(s) are detected, to
> > >>> support external docking stations. On higher level, changes are:
> > >>>
> > >>> * Pass phy being trained down to all required helpers
> > >>> * Run CR, EQ link training per phy
> > >>> * Set voltage swing, pre-emphasis levels per phy
> > >>>
> > >>> This ensures successful link training both when connected directly to
> > >>> the monitor (single LTTPR onboard most X1E laptops) and via the docking
> > >>> station (at least two LTTPRs).
> > >>>
> > >>> Fixes: 72d0af4accd9 ("drm/msm/dp: Add support for LTTPR handling")
> > >>>
> > >>
> > >> Thanks for the patch to improve and add support for link training in
> > >> non-transparent mode.
> > >>
> > >> Some questions below as the DP 2.1a spec documentation is not very clear
> > >> about segmented link training as you noted in the cover letter, so I am
> > >> also only reviewing i915 as reference here.
> > >>
> > >>
> > >>> Tested-by: Johan Hovold <johan+linaro at kernel.org>
> > >>> Tested-by: Rob Clark <robdclark at gmail.com>
> > >>> Tested-by: Stefan Schmidt <stefan.schmidt at linaro.org>
> > >>> Signed-off-by: Aleksandrs Vinarskis <alex.vinarskis at gmail.com>
> > >>> Reviewed-by: Abel Vesa <abel.vesa at linaro.org>
> > >>> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov at oss.qualcomm.com>
> > >>> ---
> > >>> drivers/gpu/drm/msm/dp/dp_ctrl.c | 126 ++++++++++++++++++++++---------
> > >>> 1 file changed, 89 insertions(+), 37 deletions(-)
> > >>>
> > >>> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > >>> index d8633a596f8d..35b28c2fcd64 100644
> > >>> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > >>> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> > >>> @@ -1034,10 +1034,12 @@ static int msm_dp_ctrl_set_vx_px(struct msm_dp_ctrl_private *ctrl,
> > >>> return 0;
> > >>> }
> > >>>
> > >>> -static int msm_dp_ctrl_update_vx_px(struct msm_dp_ctrl_private *ctrl)
> > >>> +static int msm_dp_ctrl_update_phy_vx_px(struct msm_dp_ctrl_private *ctrl,
> > >>> + enum drm_dp_phy dp_phy)
> > >>> {
> > >>> struct msm_dp_link *link = ctrl->link;
> > >>> - int ret = 0, lane, lane_cnt;
> > >>> + int lane, lane_cnt, reg;
> > >>> + int ret = 0;
> > >>> u8 buf[4];
> > >>> u32 max_level_reached = 0;
> > >>> u32 voltage_swing_level = link->phy_params.v_level;
> > >>> @@ -1075,8 +1077,13 @@ static int msm_dp_ctrl_update_vx_px(struct msm_dp_ctrl_private *ctrl)
> > >>>
> > >>> drm_dbg_dp(ctrl->drm_dev, "sink: p|v=0x%x\n",
> > >>> voltage_swing_level | pre_emphasis_level);
> > >>> - ret = drm_dp_dpcd_write(ctrl->aux, DP_TRAINING_LANE0_SET,
> > >>> - buf, lane_cnt);
> > >>> +
> > >>> + if (dp_phy == DP_PHY_DPRX)
> > >>> + reg = DP_TRAINING_LANE0_SET;
> > >>> + else
> > >>> + reg = DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy);
> > >>> +
> > >>> + ret = drm_dp_dpcd_write(ctrl->aux, reg, buf, lane_cnt);
> > >>
> > >> For the max voltage and swing levels, it seems like we need to use the
> > >> source (DPTX) or the DPRX immediately upstream of the RX we are trying
> > >> to train. i915 achieves it with below:
> > >>
> > >> /*
> > >> * Get voltage_max from the DPTX_PHY (source or LTTPR) upstream
> > >> from
> > >> * the DPRX_PHY we train.
> > >> */
> > >> if (intel_dp_phy_is_downstream_of_source(intel_dp, dp_phy))
> > >> voltage_max = intel_dp->voltage_max(intel_dp, crtc_state);
> > >> else
> > >> voltage_max = intel_dp_lttpr_voltage_max(intel_dp,
> > >> dp_phy + 1);
> > >>
> >
> > Before I update on the below set of questions from Alex, let me clarify
> > one point from Abel.
> >
> > Hi Abel
> >
> > Apologies to ask this late, but as per the earlier discussions we had
> > internally, I thought we wanted to set the LTTPR to transparent mode to
> > avoid the issues. The per-segment link training becomes a requirement if
> > we use non-transparent mode iiuc.
> >
> > In the description of the PHY_REPEATER_MODE DPCD register, it states
> > like below:
> >
> > "A DPTX operating with 8b/10b Link Layer (MAIN_LINK_CHANNEL_CODING_SET
> > register (DPCD Address 00108h) is programmed to 01h) may configure LTTPRs
> > to either Transparent (default) or Non-transparent mode.
> > A DPTX that establishes the DP link with 128b/132b channel coding shall
> > write
> > 02h to the MAIN_LINK_CHANNEL_CODING_SET register and configure LTTPRs
> > to Non-transparent mode."
> >
> > As per the msm dp code, we are using 8b/10b encoding, like below
> >
> > static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl,
> > int *training_step)
> > {
> > int ret = 0;
> > const u8 *dpcd = ctrl->panel->dpcd;
> > u8 encoding[] = { 0, DP_SET_ANSI_8B10B };
> >
> > So can you pls elaborate why we set the PHY_REPEATER_MODE to
> > non-transparent mode because drm_dp_lttpr_init() will set the LTTPR to
> > non-transparent mode.
> >
> > The second part of the section is what was described in the commit text
> > of the 72d0af4accd9 ("drm/msm/dp: Add support for LTTPR handling") but
> >
> > "Before performing link training with LTTPR(s), the DPTX may place the
> > LTTPR(s) in
> > Non-transparent mode by first writing 55h to the PHY_REPEATER_MODE
> > register, and then
> > writing AAh. This operation does not need to be performed on subsequent
> > link training actions
> > unless a downstream device unplug event is detected."
> >
> > So just wanted to understand this better that was there any requirement
> > to put it to non-transparent mode other than the section of the spec
> > highlighted above? Because above lines are only suggesting that if we
> > want to put the LTTPR to non-transparent mode, how to do it but not to
> > always do it. Please let me know your comments.
> >
> > I shall also check internally on this to close this.
> >
> >
> > Hi Alex
> >
> > >>
> > >> But I do not see (unless I missed) how this patch takes care of this
> > >> requirement.
> > >>
> > >> Same holds true for preemph too
> > >
> > > Thanks for you review,
> > >
> > > This is a very good point. You are right, in the present state it does
> > > not. Intel's driver is verifying whether LTTPRs supports
> > > DP_TRAIN_LEVEL_3 or only DP_TRAIN_LEVEL_2, while my current change
> > > follows msm-dp's default which was recently set to DP_TRAIN_LEVEL_3
> > > [1]. I came to conclusion that in particular case it was not required
> > > to verify that LTTPR indeed supports training level 3, but do not
> > > remember the details as its been a few months... should've document it
> > > :)
> > >
> >
> > > As I recall, from one of the DP specs onward (has to be 1.4a then,
> > > since LTTPR was initially introduced in DP 1.3, but register for phy
> > > capabilities only added in 1.4a [2]) it mandates training level 3
> > > support for LTTPRs, so the assumption would've be correct in that
> > > case. Is this something you could verify from the official
> > > documentation? Unfortunately I do not have sources to back this
> > > statement, so it may be incorrect...
> > >
> >
> > I went through DP spec 1.4(a), DP 2.0 and DP 2.1(a). This is what is
> > mentioned below:
> >
> >
> > "LTTPR shall support all required voltage swing and pre-emphasis
> > combinations defined
> > in Table 3-2. The LTTPR shall reflect its support of optional Voltage
> > Swing Level 3
> > and Pre-emphasis Level 3 in the VOLTAGE_SWING_LEVEL_3_SUPPORTED and
> > VOLTAGE_SWING_LEVEL_3_SUPPORTED bits, respectively, in the
> > TRANSMITTER_CAPABILITY_PHY_REPEATERx register(s) (e.g., DPCD
> > Address F0021h for LTTPR1, bits 0 and 1, respectively)."
> >
> > From this paragraph, it means that LTTPR support for levels 0/1/2 can
> > be assumed and level 3 is optional. Whether or not level 3 is supported
> > comes from the TRANSMITTER_CAPABILITY_PHY_REPEATERx register(s).
> >
> > This aligns with i915 implementation.
> >
> >
> > Now, right after this, there is another paragraph in the spec:
> >
> > "If the DPTX sets the voltage swing or pre-emphasis to a level that the
> > LTTPR does not support,
> > the LTTPR shall set its transmitter levels as close as possible to those
> > requested by the DPTX.
> > Although the LTTPR’s level choosing is implementation-specific, the
> > levels chosen shall
> > comply with Section 3.5.4."
>
> Hi Abhinav,
>
> Could you please provide the exact section number and DP spec version
> for this paragraph? For reference in the commit message, see below.
>
> >
> > This tells us that even if we try to do a level3 and the LTTPR does not
> > support it, it will use the one closest to this.
> >
> > So overall, even though i915's implementation is the accurate one, the
> > DP spec does mention that the LTTPR can adjust. I just hope all LTTPRs
> > can adjust to this. Hopefully this clarifies the requirements spec-wise.
>
> Thanks for this clarification, this is extremely useful. A bit sad
> that DP spec is only available to VESA members.
> So my assumption was indeed incorrect. This also explains why eg.
> AMD's driver works, nice.
>
> >
> > Hence I am okay with this change as such as multiple folks including us
> > have given a Tested-by but I would like this to be documented in the
> > commit text so that full context is preserved. The only concern I have
> > is I hope that the level to which the LTTPR adjusts will be correct as
> > that again is "implementation specific".
>
> I started implementing i915's approach meanwhile, to see the
> difference in behaviour. POC fixup for patch 3,4 of this series can be
> found in [1]. Discovered something interesting:
> * Dell WD19TB docking station's LTTPR reports support of training level 3
> * PS8833 retimer in Asus Zenbook A14 reports support of training level 3
> * PS8830 retimer in Dell XPS 9345 claims to _not_ report support
> training level 3. This is the case on two different machines with BIOS
> 1.9.0 (PS8830 payload version 5.3.0.14) and BIOS 2.5.0 (PS8830 payload
> version 9.3.0.01).
>
> This leads to interesting test results:
> * Asus Zenbook A14 (PS8833, supports train level 3) with direct
> monitor connection via Type-C works, both in current version of msm-dp
> (aka AMD's approach) and with additional patches I linked above (aka
> i915's approach)
> * Dell XPS 9345 (PS8830, claims to not support train level 3) with
> Dell WD19TB (supports train level 3) works, both in current version of
> msm-dp and with additional patches I linked above. In this
> combination, PS8830->WD19TB segment training succeeds with vs=2, pe=0
> already.
> * Dell XPS 9345 (PS8830, claims to not support train level 3) with
> direct monitor connection via Type-C works with current version of
> msm-dp, but does _not_ work with additional patches I linked above.
> For PS8830->Monitor segment training, after reaching vs=2,pe=0 and
> being stopped from going higher (due to PS8830 claiming it cannot do
> train level 3), link training fails. With current msm-dp state
> however, the same PS8830->Monitor segment training succeeds with
> vs=2,pe=1. This is contrary to retimer reporting it does not support
> train level 3 - it in fact does, and in case with 1m long Type-C to DP
> cable it only works with train level 3. Bug in P8830's LTTPR
> implementation? :)
>
> Combining both patches linked above as well as debug patch to force
> max train level to 3 like it was before [2], here are detailed logs:
> Asus Zenbook A14, BIOS version "UX3407QA.305":
> ```
> phy #1: params reset #
> training DPRX (phy1/PS8833)
> phy #1: max_v_level=3, max_p_level=3 # DPTX source
> (X1E) supports train level 3
> phy #1: forcing max_v_level=3, max_p_level=3
> phy #1: v_level=0, p_level=0 #
> passes with vs=0,ps=0
> phy #1: max_v_level=3, max_p_level=3
> phy #0: params reset
> # training DPRX (phy0/Monitor)
> phy #0: max_v_level=3, max_p_level=3 # DPTX source
> (phy1/PS8833) supports train level 3
> phy #0: forcing max_v_level=3, max_p_level=3
> phy #0: v_level=0, p_level=0
> phy #0: max_v_level=3, max_p_level=3
> phy #X: v_level=2, p_level=0
> phy #0: v_level=2, p_level=0
> phy #0: max_v_level=3, max_p_level=3
> phy #X: v_level=2, p_level=1
> phy #0: v_level=2, p_level=1 #
> training passes with vs=2,ps=1
> phy #0: max_v_level=3, max_p_level=3
> ```
>
> Dell XPS 9345, BIOS version "2.5.0", PS8830 payload version "9.3.0.01":
> ```
> phy #1: params reset #
> training DPRX (phy1/PS8830)
> phy #1: max_v_level=3, max_p_level=3 # DPTX source
> (X1E) supports train level 3
> phy #1: forcing max_v_level=3, max_p_level=3
> phy #1: v_level=0, p_level=0 #
> passes with vs=0,ps=0
> phy #1: max_v_level=3, max_p_level=3
> phy #0: params reset #
> training DPRX (phy0/Monitor)
> phy #0: max_v_level=2, max_p_level=2 # DPTX source
> (phy1/PS8830) claims to not support train level 3
> phy #0: forcing max_v_level=3, max_p_level=3 # Ignore
> advertised levels, force to max=3, otherwise training fails
> phy #0: v_level=0, p_level=0
> phy #0: max_v_level=3, max_p_level=3
> phy #X: v_level=2, p_level=0
> phy #0: v_level=2, p_level=0
> phy #0: max_v_level=3, max_p_level=3
> phy #X: v_level=2, p_level=1
> phy #0: v_level=2, p_level=1 #
> training passes with vs=2,ps=1 (aka train level 3)
> phy #0: max_v_level=3, max_p_level=3
> ```
>
> While, as you correctly mentioned, i915's implementation would be a
> more accurate one, and I can respin to v5 with [1] applied to patches
> 3,4 of this series respectively, it appears that at least on some X1E
> based devices with PS8830 that would break DP output support at least
> in some cases. The fact that the same device with the same monitor
> works on Windows suggests that Windows driver also uses AMD's approach
> of just assuming LTTPR can do train level 3, without verifying it, and
> letting LTTPR figure the rest. I have asked other community members to
> cross-check these findings on another X1E platform with PS8830
> retimers. With this in mind, I am very glad to hear that you are okay
> with this change as such, as it now appears that a more accurate
> implementation would've caused additional issues.
Cross-confirmed on X1E DevKit with PS8830, and HP Omnibook X14 with
PS8830 - in both cases PS8830 reports max train level as 2 instead of
3. In the case of DevKit, training of phy0 (monitor) was already
passing with vs=2,pe=0 (source phy1/ps8830). In case of HP Omnibook
with some dongles attached, in the worst case training of phy0
(monitor) passed with vs=3,pe=0 (source phy1/ps8830), thus confirming
that PS8830 indeed supports training level 3, but reports otherwise in
its capabilities.
Alex
>
> >
> > I would still like to hear from Abel though about whether setting to
> > non-transparent mode was needed in the first place.
>
> Fwiw, without Abel's initial change DP output didn't work on X1E
> platform at all, neither direct monitor connection nor docking
> station. Not sure if that is because PS883x found in most X1E/X1P
> laptops do not work in transparent mode at all (even though they
> should've), or laptop's firmware would leave it in some weird state,
> and perhaps re-enabling transparent mode would've also fixed it.
>
> Lets wait for Abel's answer and the rest of this conversation to be
> resolved, and as I see it the next step would be for me to respin to
> v5 current change as is, in order to update the commit message of 4th
> patch to reflect the new findings and reference DP spec and section,
> as per the first comment of this reply.
>
> Thanks for your help,
> Alex
>
> [1] https://github.com/alexVinarskis/linux-x1e80100-dell-tributo/tree/msm/dp-lttpr-segment-lt
> [2] https://github.com/alexVinarskis/linux-x1e80100-dell-tributo/tree/msm/dp-lttpr-segment-lt-debug
>
>
> >
> > Thanks
> >
> > Abhinav
> > > Now reviewing it again, my reasoning may to be wrong, as source
> > > supporting training level 3 and DP 1.4a does not necessarily imply
> > > that external LTTPR does, nor that external LTTPR is DP 1.4a
> > > compliant.
> > >
> > > fwiw, after quickly inspecting AMD's driver it seems it also assumes
> > > DP_TRAIN_LEVEL_3 support for LTTPR and does not explicitly verify it.
> > > Similarly to proposed msm solution, iteration over phys [3] calls
> > > `perform_8b_10b_clock_recovery_sequence` [4] which is generic for both
> > > DPRX and LTTPR(s). This eventually calls `dp_is_max_vs_reached` [5] to
> > > check against hardcoded value of 3 [6]. Generally, it appears no other
> > > driver use `
> > > drm_dp_lttpr_voltage_swing_level_3_supported` or
> > > `drm_dp_lttpr_pre_emphasis_level_3_supported` helpers introduced by
> > > Intel, nor directly use register 0xf0021.
> > >
> > > Alternatively, if we cannot verify that LTTPR is expected to always
> > > support DP_TRAIN_LEVEL_3, I change this patch to match Intel's example
> > > of retrieving max vs and pe per phy. As it appears to be a bit time
> > > sensitive, can have it done and re-tested on all available hardware by
> > > Monday. Please let me know your thoughts.
> > >
> > > Thanks,
> > > Alex
> > >
> > > [1] https://lore.kernel.org/all/20240203-dp-swing-3-v1-1-6545e1706196@linaro.org/
> > > [2] https://patchwork.freedesktop.org/patch/329863/
> > > [3] https://github.com/torvalds/linux/blob/v6.15-rc4/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c#L396-L430
> > > [4] https://github.com/torvalds/linux/blob/v6.15-rc4/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training_8b_10b.c#L176-L294
> > > [5] https://github.com/torvalds/linux/blob/v6.15-rc4/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c#L462-L475
> > > [6] https://github.com/torvalds/linux/blob/v6.15-rc4/drivers/gpu/drm/amd/display/dc/dc_dp_types.h#L80
> > >
> > >>
> > >> if (intel_dp_phy_is_downstream_of_source(intel_dp, dp_phy))
> > >> preemph_max = intel_dp->preemph_max(intel_dp);
> > >> else
> > >> preemph_max = intel_dp_lttpr_preemph_max(intel_dp,
> > >> dp_phy + 1);
> > >>
> > >> drm_WARN_ON_ONCE(display->drm,
> > >> preemph_max != DP_TRAIN_PRE_EMPH_LEVEL_2 &&
> > >> preemph_max != DP_TRAIN_PRE_EMPH_LEVEL_3);
> > >>
> > >>
> > >>> if (ret == lane_cnt)
> > >>> ret = 0;
> > >>>
> > >>> @@ -1084,9 +1091,10 @@ static int msm_dp_ctrl_update_vx_px(struct msm_dp_ctrl_private *ctrl)
> > >>> }
> > >>>
> > >>> static bool msm_dp_ctrl_train_pattern_set(struct msm_dp_ctrl_private *ctrl,
> > >>> - u8 pattern)
> > >>> + u8 pattern, enum drm_dp_phy dp_phy)
> > >>> {
> > >>> u8 buf;
> > >>> + int reg;
> > >>> int ret = 0;
> > >>>
> > >>> drm_dbg_dp(ctrl->drm_dev, "sink: pattern=%x\n", pattern);
> > >>> @@ -1096,7 +1104,12 @@ static bool msm_dp_ctrl_train_pattern_set(struct msm_dp_ctrl_private *ctrl,
> > >>> if (pattern && pattern != DP_TRAINING_PATTERN_4)
> > >>> buf |= DP_LINK_SCRAMBLING_DISABLE;
> > >>>
> > >>> - ret = drm_dp_dpcd_writeb(ctrl->aux, DP_TRAINING_PATTERN_SET, buf);
> > >>> + if (dp_phy == DP_PHY_DPRX)
> > >>> + reg = DP_TRAINING_PATTERN_SET;
> > >>> + else
> > >>> + reg = DP_TRAINING_PATTERN_SET_PHY_REPEATER(dp_phy);
> > >>> +
> > >>> + ret = drm_dp_dpcd_writeb(ctrl->aux, reg, buf);
> > >>> return ret == 1;
> > >>> }
> > >>>
> > >>> @@ -1115,12 +1128,16 @@ static int msm_dp_ctrl_read_link_status(struct msm_dp_ctrl_private *ctrl,
> > >>> }
> > >>>
> > >>> static int msm_dp_ctrl_link_train_1(struct msm_dp_ctrl_private *ctrl,
> > >>> - int *training_step)
> > >>> + int *training_step, enum drm_dp_phy dp_phy)
> > >>> {
> > >>> + int delay_us;
> > >>> int tries, old_v_level, ret = 0;
> > >>> u8 link_status[DP_LINK_STATUS_SIZE];
> > >>> int const maximum_retries = 4;
> > >>>
> > >>> + delay_us = drm_dp_read_clock_recovery_delay(ctrl->aux,
> > >>> + ctrl->panel->dpcd, dp_phy, false);
> > >>> +
> > >>> msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
> > >>>
> > >>> *training_step = DP_TRAINING_1;
> > >>> @@ -1129,18 +1146,19 @@ static int msm_dp_ctrl_link_train_1(struct msm_dp_ctrl_private *ctrl,
> > >>> if (ret)
> > >>> return ret;
> > >>> msm_dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_1 |
> > >>> - DP_LINK_SCRAMBLING_DISABLE);
> > >>> + DP_LINK_SCRAMBLING_DISABLE, dp_phy);
> > >>>
> > >>> - ret = msm_dp_ctrl_update_vx_px(ctrl);
> > >>> + msm_dp_link_reset_phy_params_vx_px(ctrl->link);
> > >>> + ret = msm_dp_ctrl_update_phy_vx_px(ctrl, dp_phy);
> > >>> if (ret)
> > >>> return ret;
> > >>>
> > >>> tries = 0;
> > >>> old_v_level = ctrl->link->phy_params.v_level;
> > >>> for (tries = 0; tries < maximum_retries; tries++) {
> > >>> - drm_dp_link_train_clock_recovery_delay(ctrl->aux, ctrl->panel->dpcd);
> > >>> + fsleep(delay_us);
> > >>>
> > >>> - ret = msm_dp_ctrl_read_link_status(ctrl, link_status);
> > >>> + ret = drm_dp_dpcd_read_phy_link_status(ctrl->aux, dp_phy, link_status);
> > >>> if (ret)
> > >>> return ret;
> > >>>
> > >>> @@ -1161,7 +1179,7 @@ static int msm_dp_ctrl_link_train_1(struct msm_dp_ctrl_private *ctrl,
> > >>> }
> > >>>
> > >>> msm_dp_link_adjust_levels(ctrl->link, link_status);
> > >>> - ret = msm_dp_ctrl_update_vx_px(ctrl);
> > >>> + ret = msm_dp_ctrl_update_phy_vx_px(ctrl, dp_phy);
> > >>> if (ret)
> > >>> return ret;
> > >>> }
> > >>> @@ -1213,21 +1231,31 @@ static int msm_dp_ctrl_link_lane_down_shift(struct msm_dp_ctrl_private *ctrl)
> > >>> return 0;
> > >>> }
> > >>>
> > >>> -static void msm_dp_ctrl_clear_training_pattern(struct msm_dp_ctrl_private *ctrl)
> > >>> +static void msm_dp_ctrl_clear_training_pattern(struct msm_dp_ctrl_private *ctrl,
> > >>> + enum drm_dp_phy dp_phy)
> > >>> {
> > >>> - msm_dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_DISABLE);
> > >>> - drm_dp_link_train_channel_eq_delay(ctrl->aux, ctrl->panel->dpcd);
> > >>> + int delay_us;
> > >>> +
> > >>> + msm_dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_DISABLE, dp_phy);
> > >>> +
> > >>> + delay_us = drm_dp_read_channel_eq_delay(ctrl->aux,
> > >>> + ctrl->panel->dpcd, dp_phy, false);
> > >>> + fsleep(delay_us);
> > >>> }
> > >>>
> > >>> static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl,
> > >>> - int *training_step)
> > >>> + int *training_step, enum drm_dp_phy dp_phy)
> > >>> {
> > >>> + int delay_us;
> > >>> int tries = 0, ret = 0;
> > >>> u8 pattern;
> > >>> u32 state_ctrl_bit;
> > >>> int const maximum_retries = 5;
> > >>> u8 link_status[DP_LINK_STATUS_SIZE];
> > >>>
> > >>> + delay_us = drm_dp_read_channel_eq_delay(ctrl->aux,
> > >>> + ctrl->panel->dpcd, dp_phy, false);
> > >>> +
> > >>> msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
> > >>>
> > >>> *training_step = DP_TRAINING_2;
> > >>> @@ -1247,12 +1275,12 @@ static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl,
> > >>> if (ret)
> > >>> return ret;
> > >>>
> > >>> - msm_dp_ctrl_train_pattern_set(ctrl, pattern);
> > >>> + msm_dp_ctrl_train_pattern_set(ctrl, pattern, dp_phy);
> > >>>
> > >>> for (tries = 0; tries <= maximum_retries; tries++) {
> > >>> - drm_dp_link_train_channel_eq_delay(ctrl->aux, ctrl->panel->dpcd);
> > >>> + fsleep(delay_us);
> > >>>
> > >>> - ret = msm_dp_ctrl_read_link_status(ctrl, link_status);
> > >>> + ret = drm_dp_dpcd_read_phy_link_status(ctrl->aux, dp_phy, link_status);
> > >>> if (ret)
> > >>> return ret;
> > >>>
> > >>> @@ -1262,7 +1290,7 @@ static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl,
> > >>> }
> > >>>
> > >>> msm_dp_link_adjust_levels(ctrl->link, link_status);
> > >>> - ret = msm_dp_ctrl_update_vx_px(ctrl);
> > >>> + ret = msm_dp_ctrl_update_phy_vx_px(ctrl, dp_phy);
> > >>> if (ret)
> > >>> return ret;
> > >>>
> > >>> @@ -1271,9 +1299,32 @@ static int msm_dp_ctrl_link_train_2(struct msm_dp_ctrl_private *ctrl,
> > >>> return -ETIMEDOUT;
> > >>> }
> > >>>
> > >>> +static int msm_dp_ctrl_link_train_1_2(struct msm_dp_ctrl_private *ctrl,
> > >>> + int *training_step, enum drm_dp_phy dp_phy)
> > >>> +{
> > >>> + int ret;
> > >>> +
> > >>> + ret = msm_dp_ctrl_link_train_1(ctrl, training_step, dp_phy);
> > >>> + if (ret) {
> > >>> + DRM_ERROR("link training #1 on phy %d failed. ret=%d\n", dp_phy, ret);
> > >>> + return ret;
> > >>> + }
> > >>> + drm_dbg_dp(ctrl->drm_dev, "link training #1 on phy %d successful\n", dp_phy);
> > >>> +
> > >>> + ret = msm_dp_ctrl_link_train_2(ctrl, training_step, dp_phy);
> > >>> + if (ret) {
> > >>> + DRM_ERROR("link training #2 on phy %d failed. ret=%d\n", dp_phy, ret);
> > >>> + return ret;
> > >>> + }
> > >>> + drm_dbg_dp(ctrl->drm_dev, "link training #2 on phy %d successful\n", dp_phy);
> > >>> +
> > >>> + return 0;
> > >>> +}
> > >>> +
> > >>> static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl,
> > >>> int *training_step)
> > >>> {
> > >>> + int i;
> > >>> int ret = 0;
> > >>> const u8 *dpcd = ctrl->panel->dpcd;
> > >>> u8 encoding[] = { 0, DP_SET_ANSI_8B10B };
> > >>> @@ -1286,8 +1337,6 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl,
> > >>> link_info.rate = ctrl->link->link_params.rate;
> > >>> link_info.capabilities = DP_LINK_CAP_ENHANCED_FRAMING;
> > >>>
> > >>> - msm_dp_link_reset_phy_params_vx_px(ctrl->link);
> > >>> -
> > >>> msm_dp_aux_link_configure(ctrl->aux, &link_info);
> > >>>
> > >>> if (drm_dp_max_downspread(dpcd))
> > >>> @@ -1302,24 +1351,27 @@ static int msm_dp_ctrl_link_train(struct msm_dp_ctrl_private *ctrl,
> > >>> &assr, 1);
> > >>> }
> > >>>
> > >>> - ret = msm_dp_ctrl_link_train_1(ctrl, training_step);
> > >>> + for (i = ctrl->link->lttpr_count - 1; i >= 0; i--) {
> > >>> + enum drm_dp_phy dp_phy = DP_PHY_LTTPR(i);
> > >>> +
> > >>> + ret = msm_dp_ctrl_link_train_1_2(ctrl, training_step, dp_phy);
> > >>> + msm_dp_ctrl_clear_training_pattern(ctrl, dp_phy);
> > >>> +
> > >>> + if (ret)
> > >>> + break;
> > >>> + }
> > >>> +
> > >>> if (ret) {
> > >>> - DRM_ERROR("link training #1 failed. ret=%d\n", ret);
> > >>> + DRM_ERROR("link training of LTTPR(s) failed. ret=%d\n", ret);
> > >>> goto end;
> > >>> }
> > >>>
> > >>> - /* print success info as this is a result of user initiated action */
> > >>> - drm_dbg_dp(ctrl->drm_dev, "link training #1 successful\n");
> > >>> -
> > >>> - ret = msm_dp_ctrl_link_train_2(ctrl, training_step);
> > >>> + ret = msm_dp_ctrl_link_train_1_2(ctrl, training_step, DP_PHY_DPRX);
> > >>> if (ret) {
> > >>> - DRM_ERROR("link training #2 failed. ret=%d\n", ret);
> > >>> + DRM_ERROR("link training on sink failed. ret=%d\n", ret);
> > >>> goto end;
> > >>> }
> > >>>
> > >>> - /* print success info as this is a result of user initiated action */
> > >>> - drm_dbg_dp(ctrl->drm_dev, "link training #2 successful\n");
> > >>> -
> > >>> end:
> > >>> msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
> > >>>
> > >>> @@ -1636,7 +1688,7 @@ static int msm_dp_ctrl_link_maintenance(struct msm_dp_ctrl_private *ctrl)
> > >>> if (ret)
> > >>> goto end;
> > >>>
> > >>> - msm_dp_ctrl_clear_training_pattern(ctrl);
> > >>> + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX);
> > >>>
> > >>> msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
> > >>>
> > >>> @@ -1660,7 +1712,7 @@ static bool msm_dp_ctrl_send_phy_test_pattern(struct msm_dp_ctrl_private *ctrl)
> > >>> return false;
> > >>> }
> > >>> msm_dp_catalog_ctrl_send_phy_pattern(ctrl->catalog, pattern_requested);
> > >>> - msm_dp_ctrl_update_vx_px(ctrl);
> > >>> + msm_dp_ctrl_update_phy_vx_px(ctrl, DP_PHY_DPRX);
> > >>> msm_dp_link_send_test_response(ctrl->link);
> > >>>
> > >>> pattern_sent = msm_dp_catalog_ctrl_read_phy_pattern(ctrl->catalog);
> > >>> @@ -1902,7 +1954,7 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
> > >>> }
> > >>>
> > >>> /* stop link training before start re training */
> > >>> - msm_dp_ctrl_clear_training_pattern(ctrl);
> > >>> + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX);
> > >>> }
> > >>>
> > >>> rc = msm_dp_ctrl_reinitialize_mainlink(ctrl);
> > >>> @@ -1926,7 +1978,7 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl)
> > >>> * link training failed
> > >>> * end txing train pattern here
> > >>> */
> > >>> - msm_dp_ctrl_clear_training_pattern(ctrl);
> > >>> + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX);
> > >>>
> > >>> msm_dp_ctrl_deinitialize_mainlink(ctrl);
> > >>> rc = -ECONNRESET;
> > >>> @@ -1997,7 +2049,7 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, bool force_link_train
> > >>> msm_dp_ctrl_link_retrain(ctrl);
> > >>>
> > >>> /* stop txing train pattern to end link training */
> > >>> - msm_dp_ctrl_clear_training_pattern(ctrl);
> > >>> + msm_dp_ctrl_clear_training_pattern(ctrl, DP_PHY_DPRX);
> > >>>
> > >>> /*
> > >>> * Set up transfer unit values and set controller state to send
> > >>
> >
More information about the dri-devel
mailing list