* [PATCH v1 0/2] drm/msm/dp: Introduce link training per-segment for LTTPRs @ 2025-03-10 21:05 Aleksandrs Vinarskis 2025-03-10 21:05 ` [PATCH v1 1/2] drm/msm/dp: Fix support of LTTPR handling Aleksandrs Vinarskis 2025-03-10 21:05 ` [PATCH v1 2/2] drm/msm/dp: Introduce link training per-segment for LTTPRs Aleksandrs Vinarskis 0 siblings, 2 replies; 6+ messages in thread From: Aleksandrs Vinarskis @ 2025-03-10 21:05 UTC (permalink / raw) To: Dmitry Baryshkov, linux-arm-msm, dri-devel, freedreno, linux-kernel Cc: Rob Clark, Abhinav Kumar, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, laurentiu.tudor1, abel.vesa, johan, Aleksandrs Vinarskis Recently added Initial LTTPR support in msm/dp has configured LTTPR(s) to non-transparent mode to enable video output on X1E-based devices that come with LTTPR on the motherboards. However, video would not work if additional LTTPR(s) are present between sink and source, which is the case for USB Type-C docks (eg. Dell WD19TB/WD22TB4), and at least some universal Thunderbolt/USB Type-C monitors (eg. Dell U2725QE). First, take into account LTTPR capabilities when computing max link rate, number of lanes. Take into account previous discussion on the lists - exit early if reading DPCD caps failed. This also fixes "*ERROR* panel edid read failed" on some monitors which seems to be caused by msm_dp_panel_read_sink_caps running before LTTPR(s) are initialized. Finally, implement link training per-segment. Pass lttpr_count to all required helpers. This seems to also partially improve UI hanging when changing external display's link parameters (resolution, framerate): * Prior to this series, via direct USB Type-C to display connection, attempt to change resolution or framerate hangs the UI, setting does not stick. Some back and forth replugging finally sets desired parameters. * With this series, via direct USB Type-C to display connection, changing parameters works most of the time, without UI freezing. Via docking station/multiple LTTPRs the setting works when increasing bandwith (eg. change framerate from 60hz to 100hz), but in all other cases the setting again does not stick. These appear to be mainlink initialization related, as in all cases LT passes successfully. Test matrix: * Dell XPS 9345 * Left USB Type-C, Right USB Type-C * Direct monitor connection, Dell WD19TB, Dell WD22TB4 * Dell AW3423DWF, Samsung LS24A600, dual Samsung LS24A600 (one monitor per USB Type-C connector) * Dell XPS 9345 * Left USB Type-C, Right USB Type-C * Direct monitor connection * Dell U2725QE (universal Thunderbolt/DP Alt mode, probes with LTTPR when in DP Alt mode) In both cases, "Thunderbot Support"/"USB4 PCIE Tunneling" was disabled in UEFI to force universal Thunderbolt/DP Alt mode devices to work in DP Alt mode. In both cases laptops had HBR3 patches applied [1], resulting in maximum successful link with 3440x1440@100hz and 4k@60hz respectively. When using WD22TB4/U2725QE, USB Type-C pin assigment D got enabled, and USB3.0 devices were working in parallel to video ouput. Known issues: * As mentioned above, mainlink parameters changing is not stable, but appears to be unrelated to this series, and works better than before. * In a very particular combination of Dell XPS 9345 + Dell AW3423DWF at max resolution, right USB Type-C connector does not always work: following successful link training, mainlink is not starting. When switching monitor to PIP/PBP mode, the issue is gone. I am unable to reproduce this issue in any other combination of devices/modes, so perhaps its an edge case of either the monitor (Dell AW3423DWF) or the docking station (Dell WD19TB/WD22TB4) specifically. Due to lack of access to official DisplayPort specfication, changes were primarily inspired by/reverse engineered from Intel's i915 driver. [1] https://lore.kernel.org/all/20250226231436.16138-2-alex.vinarskis@gmail.com/ Aleksandrs Vinarskis (2): drm/msm/dp: Fix support of LTTPR handling drm/msm/dp: Introduce link training per-segment for LTTPRs drivers/gpu/drm/msm/dp/dp_ctrl.c | 136 +++++++++++++++++++--------- drivers/gpu/drm/msm/dp/dp_ctrl.h | 2 +- drivers/gpu/drm/msm/dp/dp_display.c | 33 +++++-- drivers/gpu/drm/msm/dp/dp_panel.c | 30 ++++-- drivers/gpu/drm/msm/dp/dp_panel.h | 2 + 5 files changed, 141 insertions(+), 62 deletions(-) -- 2.45.2 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v1 1/2] drm/msm/dp: Fix support of LTTPR handling 2025-03-10 21:05 [PATCH v1 0/2] drm/msm/dp: Introduce link training per-segment for LTTPRs Aleksandrs Vinarskis @ 2025-03-10 21:05 ` Aleksandrs Vinarskis 2025-03-11 7:22 ` Abel Vesa 2025-03-10 21:05 ` [PATCH v1 2/2] drm/msm/dp: Introduce link training per-segment for LTTPRs Aleksandrs Vinarskis 1 sibling, 1 reply; 6+ messages in thread From: Aleksandrs Vinarskis @ 2025-03-10 21:05 UTC (permalink / raw) To: Dmitry Baryshkov, linux-arm-msm, dri-devel, freedreno, linux-kernel Cc: Rob Clark, Abhinav Kumar, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, laurentiu.tudor1, abel.vesa, johan, Aleksandrs Vinarskis Take into account LTTPR capabilities when selecting maximum allowed link rate, number of data lines. Initialize LTTPR before msm_dp_panel_read_sink_caps, as a) Link params computation need to take into account LTTPR's caps b) It appears DPTX shall (re)read DPRX caps after LTTPR detection Return lttpr_count to prepare for per-segment link training. Signed-off-by: Aleksandrs Vinarskis <alex.vinarskis@gmail.com> --- drivers/gpu/drm/msm/dp/dp_display.c | 31 +++++++++++++++++++---------- drivers/gpu/drm/msm/dp/dp_panel.c | 30 +++++++++++++++++++--------- drivers/gpu/drm/msm/dp/dp_panel.h | 2 ++ 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index bbc47d86ae9e..2edbc6adfde5 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -108,6 +108,8 @@ struct msm_dp_display_private { struct msm_dp_event event_list[DP_EVENT_Q_MAX]; spinlock_t event_lock; + u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE]; + bool wide_bus_supported; struct msm_dp_audio *audio; @@ -367,17 +369,21 @@ static int msm_dp_display_send_hpd_notification(struct msm_dp_display_private *d return 0; } -static void msm_dp_display_lttpr_init(struct msm_dp_display_private *dp) +static int msm_dp_display_lttpr_init(struct msm_dp_display_private *dp, u8 *dpcd) { - u8 lttpr_caps[DP_LTTPR_COMMON_CAP_SIZE]; - int rc; + int rc, lttpr_count; - if (drm_dp_read_lttpr_common_caps(dp->aux, dp->panel->dpcd, lttpr_caps)) - return; + if (drm_dp_read_lttpr_common_caps(dp->aux, dpcd, dp->lttpr_common_caps)) + return 0; - rc = drm_dp_lttpr_init(dp->aux, drm_dp_lttpr_count(lttpr_caps)); - if (rc) - DRM_ERROR("failed to set LTTPRs transparency mode, rc=%d\n", rc); + lttpr_count = drm_dp_lttpr_count(dp->lttpr_common_caps); + rc = drm_dp_lttpr_init(dp->aux, lttpr_count); + if (rc) { + DRM_ERROR("fialed to set LTTPRs transparency mode, rc=%d\n", rc); + return 0; + } + + return lttpr_count; } static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) @@ -385,12 +391,17 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) struct drm_connector *connector = dp->msm_dp_display.connector; const struct drm_display_info *info = &connector->display_info; int rc = 0; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; - rc = msm_dp_panel_read_sink_caps(dp->panel, connector); + rc = drm_dp_read_dpcd_caps(dp->aux, dpcd); if (rc) goto end; - msm_dp_display_lttpr_init(dp); + msm_dp_display_lttpr_init(dp, dpcd); + + rc = msm_dp_panel_read_sink_caps(dp->panel, dp->lttpr_common_caps, connector); + if (rc) + goto end; msm_dp_link_process_request(dp->link); diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index 92415bf8aa16..f41b4cf7002e 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -45,9 +45,12 @@ static void msm_dp_panel_read_psr_cap(struct msm_dp_panel_private *panel) } } -static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel) +static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel, + const u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE]) { int rc; + int max_sink_lanes, max_source_lanes, max_lttpr_lanes; + int max_sink_rate, max_source_rate, max_lttpr_rate; struct msm_dp_panel_private *panel; struct msm_dp_link_info *link_info; u8 *dpcd, major, minor; @@ -64,16 +67,24 @@ static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel) major = (link_info->revision >> 4) & 0x0f; minor = link_info->revision & 0x0f; - link_info->rate = drm_dp_max_link_rate(dpcd); - link_info->num_lanes = drm_dp_max_lane_count(dpcd); + max_source_lanes = msm_dp_panel->max_dp_lanes; + max_source_rate = msm_dp_panel->max_dp_link_rate; - /* Limit data lanes from data-lanes of endpoint property of dtsi */ - if (link_info->num_lanes > msm_dp_panel->max_dp_lanes) - link_info->num_lanes = msm_dp_panel->max_dp_lanes; + max_sink_lanes = drm_dp_max_lane_count(dpcd); + max_sink_rate = drm_dp_max_link_rate(dpcd); + + max_lttpr_lanes = drm_dp_lttpr_max_lane_count(lttpr_common_caps); + max_lttpr_rate = drm_dp_lttpr_max_link_rate(lttpr_common_caps); + if (max_lttpr_lanes) + max_sink_lanes = min(max_sink_lanes, max_lttpr_lanes); + if (max_lttpr_rate) + max_sink_rate = min(max_sink_rate, max_lttpr_rate); + + /* Limit data lanes from data-lanes of endpoint property of dtsi */ + link_info->num_lanes = min(max_sink_lanes, max_source_lanes); /* Limit link rate from link-frequencies of endpoint property of dtsi */ - if (link_info->rate > msm_dp_panel->max_dp_link_rate) - link_info->rate = msm_dp_panel->max_dp_link_rate; + link_info->rate = min(max_sink_rate, max_source_rate); drm_dbg_dp(panel->drm_dev, "version: %d.%d\n", major, minor); drm_dbg_dp(panel->drm_dev, "link_rate=%d\n", link_info->rate); @@ -109,6 +120,7 @@ static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel, } int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, + const u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE], struct drm_connector *connector) { int rc, bw_code; @@ -125,7 +137,7 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, drm_dbg_dp(panel->drm_dev, "max_lanes=%d max_link_rate=%d\n", msm_dp_panel->max_dp_lanes, msm_dp_panel->max_dp_link_rate); - rc = msm_dp_panel_read_dpcd(msm_dp_panel); + rc = msm_dp_panel_read_dpcd(msm_dp_panel, lttpr_common_caps); if (rc) { DRM_ERROR("read dpcd failed %d\n", rc); return rc; diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h index 4906f4f09f24..d89e17a9add5 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.h +++ b/drivers/gpu/drm/msm/dp/dp_panel.h @@ -7,6 +7,7 @@ #define _DP_PANEL_H_ #include <drm/msm_drm.h> +#include <drm/display/drm_dp_helper.h> #include "dp_aux.h" #include "dp_link.h" @@ -49,6 +50,7 @@ int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel); int msm_dp_panel_deinit(struct msm_dp_panel *msm_dp_panel); int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel); int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, + const u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE], struct drm_connector *connector); u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, u32 mode_max_bpp, u32 mode_pclk_khz); -- 2.45.2 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v1 1/2] drm/msm/dp: Fix support of LTTPR handling 2025-03-10 21:05 ` [PATCH v1 1/2] drm/msm/dp: Fix support of LTTPR handling Aleksandrs Vinarskis @ 2025-03-11 7:22 ` Abel Vesa 0 siblings, 0 replies; 6+ messages in thread From: Abel Vesa @ 2025-03-11 7:22 UTC (permalink / raw) To: Aleksandrs Vinarskis Cc: Dmitry Baryshkov, linux-arm-msm, dri-devel, freedreno, linux-kernel, Rob Clark, Abhinav Kumar, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, laurentiu.tudor1, johan On 25-03-10 22:05:51, Aleksandrs Vinarskis wrote: > Take into account LTTPR capabilities when selecting maximum allowed > link rate, number of data lines. Initialize LTTPR before > msm_dp_panel_read_sink_caps, as > a) Link params computation need to take into account LTTPR's caps > b) It appears DPTX shall (re)read DPRX caps after LTTPR detection > > Return lttpr_count to prepare for per-segment link training. > > Signed-off-by: Aleksandrs Vinarskis <alex.vinarskis@gmail.com> > --- > drivers/gpu/drm/msm/dp/dp_display.c | 31 +++++++++++++++++++---------- > drivers/gpu/drm/msm/dp/dp_panel.c | 30 +++++++++++++++++++--------- > drivers/gpu/drm/msm/dp/dp_panel.h | 2 ++ > 3 files changed, 44 insertions(+), 19 deletions(-) > > diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c > index bbc47d86ae9e..2edbc6adfde5 100644 > --- a/drivers/gpu/drm/msm/dp/dp_display.c > +++ b/drivers/gpu/drm/msm/dp/dp_display.c > @@ -108,6 +108,8 @@ struct msm_dp_display_private { > struct msm_dp_event event_list[DP_EVENT_Q_MAX]; > spinlock_t event_lock; > > + u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE]; > + > bool wide_bus_supported; > > struct msm_dp_audio *audio; > @@ -367,17 +369,21 @@ static int msm_dp_display_send_hpd_notification(struct msm_dp_display_private *d > return 0; > } > > -static void msm_dp_display_lttpr_init(struct msm_dp_display_private *dp) > +static int msm_dp_display_lttpr_init(struct msm_dp_display_private *dp, u8 *dpcd) > { > - u8 lttpr_caps[DP_LTTPR_COMMON_CAP_SIZE]; > - int rc; > + int rc, lttpr_count; > > - if (drm_dp_read_lttpr_common_caps(dp->aux, dp->panel->dpcd, lttpr_caps)) > - return; > + if (drm_dp_read_lttpr_common_caps(dp->aux, dpcd, dp->lttpr_common_caps)) > + return 0; > > - rc = drm_dp_lttpr_init(dp->aux, drm_dp_lttpr_count(lttpr_caps)); > - if (rc) > - DRM_ERROR("failed to set LTTPRs transparency mode, rc=%d\n", rc); > + lttpr_count = drm_dp_lttpr_count(dp->lttpr_common_caps); > + rc = drm_dp_lttpr_init(dp->aux, lttpr_count); > + if (rc) { > + DRM_ERROR("fialed to set LTTPRs transparency mode, rc=%d\n", rc); Nitpick: failed With that fixed, LGTM: Reviewed-by: Abel Vesa <abel.vesa@linaro.org> > + return 0; > + } > + > + return lttpr_count; > } > > static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) > @@ -385,12 +391,17 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) > struct drm_connector *connector = dp->msm_dp_display.connector; > const struct drm_display_info *info = &connector->display_info; > int rc = 0; > + u8 dpcd[DP_RECEIVER_CAP_SIZE]; > > - rc = msm_dp_panel_read_sink_caps(dp->panel, connector); > + rc = drm_dp_read_dpcd_caps(dp->aux, dpcd); > if (rc) > goto end; > > - msm_dp_display_lttpr_init(dp); > + msm_dp_display_lttpr_init(dp, dpcd); > + > + rc = msm_dp_panel_read_sink_caps(dp->panel, dp->lttpr_common_caps, connector); > + if (rc) > + goto end; > > msm_dp_link_process_request(dp->link); > > diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c > index 92415bf8aa16..f41b4cf7002e 100644 > --- a/drivers/gpu/drm/msm/dp/dp_panel.c > +++ b/drivers/gpu/drm/msm/dp/dp_panel.c > @@ -45,9 +45,12 @@ static void msm_dp_panel_read_psr_cap(struct msm_dp_panel_private *panel) > } > } > > -static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel) > +static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel, > + const u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE]) > { > int rc; > + int max_sink_lanes, max_source_lanes, max_lttpr_lanes; > + int max_sink_rate, max_source_rate, max_lttpr_rate; > struct msm_dp_panel_private *panel; > struct msm_dp_link_info *link_info; > u8 *dpcd, major, minor; > @@ -64,16 +67,24 @@ static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel) > major = (link_info->revision >> 4) & 0x0f; > minor = link_info->revision & 0x0f; > > - link_info->rate = drm_dp_max_link_rate(dpcd); > - link_info->num_lanes = drm_dp_max_lane_count(dpcd); > + max_source_lanes = msm_dp_panel->max_dp_lanes; > + max_source_rate = msm_dp_panel->max_dp_link_rate; > > - /* Limit data lanes from data-lanes of endpoint property of dtsi */ > - if (link_info->num_lanes > msm_dp_panel->max_dp_lanes) > - link_info->num_lanes = msm_dp_panel->max_dp_lanes; > + max_sink_lanes = drm_dp_max_lane_count(dpcd); > + max_sink_rate = drm_dp_max_link_rate(dpcd); > + > + max_lttpr_lanes = drm_dp_lttpr_max_lane_count(lttpr_common_caps); > + max_lttpr_rate = drm_dp_lttpr_max_link_rate(lttpr_common_caps); > > + if (max_lttpr_lanes) > + max_sink_lanes = min(max_sink_lanes, max_lttpr_lanes); > + if (max_lttpr_rate) > + max_sink_rate = min(max_sink_rate, max_lttpr_rate); > + > + /* Limit data lanes from data-lanes of endpoint property of dtsi */ > + link_info->num_lanes = min(max_sink_lanes, max_source_lanes); > /* Limit link rate from link-frequencies of endpoint property of dtsi */ > - if (link_info->rate > msm_dp_panel->max_dp_link_rate) > - link_info->rate = msm_dp_panel->max_dp_link_rate; > + link_info->rate = min(max_sink_rate, max_source_rate); > > drm_dbg_dp(panel->drm_dev, "version: %d.%d\n", major, minor); > drm_dbg_dp(panel->drm_dev, "link_rate=%d\n", link_info->rate); > @@ -109,6 +120,7 @@ static u32 msm_dp_panel_get_supported_bpp(struct msm_dp_panel *msm_dp_panel, > } > > int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, > + const u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE], > struct drm_connector *connector) > { > int rc, bw_code; > @@ -125,7 +137,7 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, > drm_dbg_dp(panel->drm_dev, "max_lanes=%d max_link_rate=%d\n", > msm_dp_panel->max_dp_lanes, msm_dp_panel->max_dp_link_rate); > > - rc = msm_dp_panel_read_dpcd(msm_dp_panel); > + rc = msm_dp_panel_read_dpcd(msm_dp_panel, lttpr_common_caps); > if (rc) { > DRM_ERROR("read dpcd failed %d\n", rc); > return rc; > diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h > index 4906f4f09f24..d89e17a9add5 100644 > --- a/drivers/gpu/drm/msm/dp/dp_panel.h > +++ b/drivers/gpu/drm/msm/dp/dp_panel.h > @@ -7,6 +7,7 @@ > #define _DP_PANEL_H_ > > #include <drm/msm_drm.h> > +#include <drm/display/drm_dp_helper.h> > > #include "dp_aux.h" > #include "dp_link.h" > @@ -49,6 +50,7 @@ int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel); > int msm_dp_panel_deinit(struct msm_dp_panel *msm_dp_panel); > int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel); > int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, > + const u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE], > struct drm_connector *connector); > u32 msm_dp_panel_get_mode_bpp(struct msm_dp_panel *msm_dp_panel, u32 mode_max_bpp, > u32 mode_pclk_khz); > -- > 2.45.2 > ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v1 2/2] drm/msm/dp: Introduce link training per-segment for LTTPRs 2025-03-10 21:05 [PATCH v1 0/2] drm/msm/dp: Introduce link training per-segment for LTTPRs Aleksandrs Vinarskis 2025-03-10 21:05 ` [PATCH v1 1/2] drm/msm/dp: Fix support of LTTPR handling Aleksandrs Vinarskis @ 2025-03-10 21:05 ` Aleksandrs Vinarskis 2025-03-11 8:20 ` Abel Vesa 2025-03-11 8:28 ` Johan Hovold 1 sibling, 2 replies; 6+ messages in thread From: Aleksandrs Vinarskis @ 2025-03-10 21:05 UTC (permalink / raw) To: Dmitry Baryshkov, linux-arm-msm, dri-devel, freedreno, linux-kernel Cc: Rob Clark, Abhinav Kumar, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, laurentiu.tudor1, abel.vesa, johan, Aleksandrs Vinarskis 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). This does not address/resolve underlying mainlink initialization issues. Signed-off-by: Aleksandrs Vinarskis <alex.vinarskis@gmail.com> --- drivers/gpu/drm/msm/dp/dp_ctrl.c | 136 +++++++++++++++++++--------- drivers/gpu/drm/msm/dp/dp_ctrl.h | 2 +- drivers/gpu/drm/msm/dp/dp_display.c | 4 +- 3 files changed, 98 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index d8633a596f8d..20f07ef46ef3 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -79,6 +79,8 @@ struct msm_dp_ctrl_private { struct msm_dp_link *link; struct msm_dp_catalog *catalog; + int *lttpr_count; + struct phy *phy; unsigned int num_core_clks; @@ -1034,7 +1036,8 @@ 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; @@ -1075,8 +1078,12 @@ 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); + + int reg = dp_phy == DP_PHY_DPRX ? + DP_TRAINING_LANE0_SET : + DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy); + + ret = drm_dp_dpcd_write(ctrl->aux, reg, buf, lane_cnt); if (ret == lane_cnt) ret = 0; @@ -1084,10 +1091,13 @@ 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 ret = 0; + int reg = dp_phy == DP_PHY_DPRX ? + DP_TRAINING_PATTERN_SET : + DP_TRAINING_PATTERN_SET_PHY_REPEATER(dp_phy); drm_dbg_dp(ctrl->drm_dev, "sink: pattern=%x\n", pattern); @@ -1096,7 +1106,7 @@ 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); + ret = drm_dp_dpcd_writeb(ctrl->aux, reg, buf); return ret == 1; } @@ -1115,12 +1125,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 +1143,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 +1176,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 +1228,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 +1272,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 +1287,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,10 +1296,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 ret = 0; + int ret = 0, i; const u8 *dpcd = ctrl->panel->dpcd; u8 encoding[] = { 0, DP_SET_ANSI_8B10B }; u8 assr; @@ -1286,8 +1333,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,23 +1347,29 @@ 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->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"); + drm_dbg_dp(ctrl->drm_dev, "link training on sink successful\n"); end: msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0); @@ -1636,7 +1687,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 +1711,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 +1953,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 +1977,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 +2048,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 @@ -2207,7 +2258,7 @@ static int msm_dp_ctrl_clk_init(struct msm_dp_ctrl *msm_dp_ctrl) struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link, struct msm_dp_panel *panel, struct drm_dp_aux *aux, - struct msm_dp_catalog *catalog, + struct msm_dp_catalog *catalog, int *lttpr_count, struct phy *phy) { struct msm_dp_ctrl_private *ctrl; @@ -2242,12 +2293,13 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link init_completion(&ctrl->video_comp); /* in parameters */ - ctrl->panel = panel; - ctrl->aux = aux; - ctrl->link = link; - ctrl->catalog = catalog; - ctrl->dev = dev; - ctrl->phy = phy; + ctrl->panel = panel; + ctrl->aux = aux; + ctrl->link = link; + ctrl->catalog = catalog; + ctrl->dev = dev; + ctrl->phy = phy; + ctrl->lttpr_count = lttpr_count; ret = msm_dp_ctrl_clk_init(&ctrl->msm_dp_ctrl); if (ret) { diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h index b7abfedbf574..3fb45b138b31 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h @@ -27,7 +27,7 @@ irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl); void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl); struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link, struct msm_dp_panel *panel, struct drm_dp_aux *aux, - struct msm_dp_catalog *catalog, + struct msm_dp_catalog *catalog, int *lttpr_count, struct phy *phy); void msm_dp_ctrl_reset_irq_ctrl(struct msm_dp_ctrl *msm_dp_ctrl, bool enable); diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 2edbc6adfde5..5fcc5951797b 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -108,6 +108,7 @@ struct msm_dp_display_private { struct msm_dp_event event_list[DP_EVENT_Q_MAX]; spinlock_t event_lock; + int lttpr_count; u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE]; bool wide_bus_supported; @@ -397,7 +398,7 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) if (rc) goto end; - msm_dp_display_lttpr_init(dp, dpcd); + dp->lttpr_count = msm_dp_display_lttpr_init(dp, dpcd); rc = msm_dp_panel_read_sink_caps(dp->panel, dp->lttpr_common_caps, connector); if (rc) @@ -798,6 +799,7 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp) dp->ctrl = msm_dp_ctrl_get(dev, dp->link, dp->panel, dp->aux, dp->catalog, + &dp->lttpr_count, phy); if (IS_ERR(dp->ctrl)) { rc = PTR_ERR(dp->ctrl); -- 2.45.2 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v1 2/2] drm/msm/dp: Introduce link training per-segment for LTTPRs 2025-03-10 21:05 ` [PATCH v1 2/2] drm/msm/dp: Introduce link training per-segment for LTTPRs Aleksandrs Vinarskis @ 2025-03-11 8:20 ` Abel Vesa 2025-03-11 8:28 ` Johan Hovold 1 sibling, 0 replies; 6+ messages in thread From: Abel Vesa @ 2025-03-11 8:20 UTC (permalink / raw) To: Aleksandrs Vinarskis Cc: Dmitry Baryshkov, linux-arm-msm, dri-devel, freedreno, linux-kernel, Rob Clark, Abhinav Kumar, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, laurentiu.tudor1, johan On 25-03-10 22:05:52, 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). This does not address/resolve underlying > mainlink initialization issues. > > Signed-off-by: Aleksandrs Vinarskis <alex.vinarskis@gmail.com> Reviewed-by: Abel Vesa <abel.vesa@linaro.org> > --- > drivers/gpu/drm/msm/dp/dp_ctrl.c | 136 +++++++++++++++++++--------- > drivers/gpu/drm/msm/dp/dp_ctrl.h | 2 +- > drivers/gpu/drm/msm/dp/dp_display.c | 4 +- > 3 files changed, 98 insertions(+), 44 deletions(-) > > diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c > index d8633a596f8d..20f07ef46ef3 100644 > --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c > +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c > @@ -79,6 +79,8 @@ struct msm_dp_ctrl_private { > struct msm_dp_link *link; > struct msm_dp_catalog *catalog; > > + int *lttpr_count; > + > struct phy *phy; > > unsigned int num_core_clks; > @@ -1034,7 +1036,8 @@ 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; > @@ -1075,8 +1078,12 @@ 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); > + > + int reg = dp_phy == DP_PHY_DPRX ? > + DP_TRAINING_LANE0_SET : > + DP_TRAINING_LANE0_SET_PHY_REPEATER(dp_phy); > + > + ret = drm_dp_dpcd_write(ctrl->aux, reg, buf, lane_cnt); > if (ret == lane_cnt) > ret = 0; > > @@ -1084,10 +1091,13 @@ 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 ret = 0; > + int reg = dp_phy == DP_PHY_DPRX ? > + DP_TRAINING_PATTERN_SET : > + DP_TRAINING_PATTERN_SET_PHY_REPEATER(dp_phy); > > drm_dbg_dp(ctrl->drm_dev, "sink: pattern=%x\n", pattern); > > @@ -1096,7 +1106,7 @@ 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); > + ret = drm_dp_dpcd_writeb(ctrl->aux, reg, buf); > return ret == 1; > } > > @@ -1115,12 +1125,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 +1143,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 +1176,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 +1228,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 +1272,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 +1287,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,10 +1296,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 ret = 0; > + int ret = 0, i; > const u8 *dpcd = ctrl->panel->dpcd; > u8 encoding[] = { 0, DP_SET_ANSI_8B10B }; > u8 assr; > @@ -1286,8 +1333,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,23 +1347,29 @@ 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->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"); > + drm_dbg_dp(ctrl->drm_dev, "link training on sink successful\n"); > > end: > msm_dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0); > @@ -1636,7 +1687,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 +1711,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 +1953,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 +1977,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 +2048,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 > @@ -2207,7 +2258,7 @@ static int msm_dp_ctrl_clk_init(struct msm_dp_ctrl *msm_dp_ctrl) > > struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link, > struct msm_dp_panel *panel, struct drm_dp_aux *aux, > - struct msm_dp_catalog *catalog, > + struct msm_dp_catalog *catalog, int *lttpr_count, > struct phy *phy) > { > struct msm_dp_ctrl_private *ctrl; > @@ -2242,12 +2293,13 @@ struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link > init_completion(&ctrl->video_comp); > > /* in parameters */ > - ctrl->panel = panel; > - ctrl->aux = aux; > - ctrl->link = link; > - ctrl->catalog = catalog; > - ctrl->dev = dev; > - ctrl->phy = phy; > + ctrl->panel = panel; > + ctrl->aux = aux; > + ctrl->link = link; > + ctrl->catalog = catalog; > + ctrl->dev = dev; > + ctrl->phy = phy; > + ctrl->lttpr_count = lttpr_count; > > ret = msm_dp_ctrl_clk_init(&ctrl->msm_dp_ctrl); > if (ret) { > diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h > index b7abfedbf574..3fb45b138b31 100644 > --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h > +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h > @@ -27,7 +27,7 @@ irqreturn_t msm_dp_ctrl_isr(struct msm_dp_ctrl *msm_dp_ctrl); > void msm_dp_ctrl_handle_sink_request(struct msm_dp_ctrl *msm_dp_ctrl); > struct msm_dp_ctrl *msm_dp_ctrl_get(struct device *dev, struct msm_dp_link *link, > struct msm_dp_panel *panel, struct drm_dp_aux *aux, > - struct msm_dp_catalog *catalog, > + struct msm_dp_catalog *catalog, int *lttpr_count, > struct phy *phy); > > void msm_dp_ctrl_reset_irq_ctrl(struct msm_dp_ctrl *msm_dp_ctrl, bool enable); > diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c > index 2edbc6adfde5..5fcc5951797b 100644 > --- a/drivers/gpu/drm/msm/dp/dp_display.c > +++ b/drivers/gpu/drm/msm/dp/dp_display.c > @@ -108,6 +108,7 @@ struct msm_dp_display_private { > struct msm_dp_event event_list[DP_EVENT_Q_MAX]; > spinlock_t event_lock; > > + int lttpr_count; > u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE]; > > bool wide_bus_supported; > @@ -397,7 +398,7 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp) > if (rc) > goto end; > > - msm_dp_display_lttpr_init(dp, dpcd); > + dp->lttpr_count = msm_dp_display_lttpr_init(dp, dpcd); > > rc = msm_dp_panel_read_sink_caps(dp->panel, dp->lttpr_common_caps, connector); > if (rc) > @@ -798,6 +799,7 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp) > > dp->ctrl = msm_dp_ctrl_get(dev, dp->link, dp->panel, dp->aux, > dp->catalog, > + &dp->lttpr_count, > phy); > if (IS_ERR(dp->ctrl)) { > rc = PTR_ERR(dp->ctrl); > -- > 2.45.2 > ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v1 2/2] drm/msm/dp: Introduce link training per-segment for LTTPRs 2025-03-10 21:05 ` [PATCH v1 2/2] drm/msm/dp: Introduce link training per-segment for LTTPRs Aleksandrs Vinarskis 2025-03-11 8:20 ` Abel Vesa @ 2025-03-11 8:28 ` Johan Hovold 1 sibling, 0 replies; 6+ messages in thread From: Johan Hovold @ 2025-03-11 8:28 UTC (permalink / raw) To: Aleksandrs Vinarskis Cc: Dmitry Baryshkov, linux-arm-msm, dri-devel, freedreno, linux-kernel, Rob Clark, Abhinav Kumar, Sean Paul, Marijn Suijten, David Airlie, Simona Vetter, laurentiu.tudor1, abel.vesa Hi Aleksandrs, Just a drive-by comment. On Mon, Mar 10, 2025 at 10:05:52PM +0100, Aleksandrs Vinarskis wrote: > @@ -1084,10 +1091,13 @@ 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 ret = 0; > + int reg = dp_phy == DP_PHY_DPRX ? > + DP_TRAINING_PATTERN_SET : > + DP_TRAINING_PATTERN_SET_PHY_REPEATER(dp_phy); This is hardly readable; avoid using the ternary operator and split declaration from non-trivial initialisation. Johan ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-03-11 8:28 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-03-10 21:05 [PATCH v1 0/2] drm/msm/dp: Introduce link training per-segment for LTTPRs Aleksandrs Vinarskis 2025-03-10 21:05 ` [PATCH v1 1/2] drm/msm/dp: Fix support of LTTPR handling Aleksandrs Vinarskis 2025-03-11 7:22 ` Abel Vesa 2025-03-10 21:05 ` [PATCH v1 2/2] drm/msm/dp: Introduce link training per-segment for LTTPRs Aleksandrs Vinarskis 2025-03-11 8:20 ` Abel Vesa 2025-03-11 8:28 ` Johan Hovold
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox