From: Sean Anderson <sean.anderson@linux.dev>
To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>,
Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
Maxime Ripard <mripard@kernel.org>,
Thomas Zimmermann <tzimmermann@suse.de>,
dri-devel@lists.freedesktop.org
Cc: linux-kernel@vger.kernel.org, David Airlie <airlied@gmail.com>,
Hyun Kwon <hyun.kwon@xilinx.com>, Simona Vetter <simona@ffwll.ch>,
Michal Simek <michal.simek@amd.com>,
linux-arm-kernel@lists.infradead.org,
Sean Anderson <sean.anderson@linux.dev>
Subject: [PATCH 3/3] drm: zynqmp_dp: Retrain link after HPD if necessary
Date: Fri, 24 Oct 2025 15:17:07 -0400 [thread overview]
Message-ID: <20251024191707.2310589-4-sean.anderson@linux.dev> (raw)
In-Reply-To: <20251024191707.2310589-1-sean.anderson@linux.dev>
The section 5.1.4 of the v1.2 DisplayPort standard says
> The Source device shall respond to Hot Plug event/Hot Re-plug event by
> first reading DPCD Link/Sink Device Status registers at DPCD 00200h
> through 00205h.... If the link is unstable or lost, the Source device
> then reads the DPCD Receiver Capabilities registers at DPCD 00000h
> through 0000Fh to determine the appropriate information needed to
> train the link. The Source device shall then initiate link training.
However, zynqmp_dp_hpd_work_func does not check the link status. This
may prevent the sink from detecting the source if, for example, the user
disconnects the cable and then reconnects it. I encountered this problem
when testing a mini DP connector (although I had no problem when using a
full-size connector with the existing driver).
Follow the spec by checking the link status after a HPD event and
retraining if necessary.
Fixes: d76271d22694 ("drm: xlnx: DRM/KMS driver for Xilinx ZynqMP DisplayPort Subsystem")
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---
drivers/gpu/drm/xlnx/zynqmp_dp.c | 37 ++++++++++++++++++++------------
1 file changed, 23 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index caf2e0ce3644..a90bc0e406f6 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -1677,6 +1677,24 @@ static int zynqmp_dp_bridge_atomic_check(struct drm_bridge *bridge,
return 0;
}
+static bool zynqmp_hpd_needs_retain(struct zynqmp_dp *dp)
+{
+ u8 status[DP_LINK_STATUS_SIZE + 2];
+ int err;
+
+ err = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, status,
+ DP_LINK_STATUS_SIZE + 2);
+ if (err < 0) {
+ dev_dbg_ratelimited(dp->dev,
+ "could not read sink status: %d\n", err);
+ return false;
+ }
+
+ return status[4] & DP_LINK_STATUS_UPDATED ||
+ !drm_dp_clock_recovery_ok(&status[2], dp->mode.lane_cnt) ||
+ !drm_dp_channel_eq_ok(&status[2], dp->mode.lane_cnt);
+}
+
static enum drm_connector_status __zynqmp_dp_bridge_detect(struct zynqmp_dp *dp)
{
struct zynqmp_dp_link_config *link_config = &dp->link_config;
@@ -1698,6 +1716,9 @@ static enum drm_connector_status __zynqmp_dp_bridge_detect(struct zynqmp_dp *dp)
if (state & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_HPD) {
WRITE_ONCE(dp->status, connector_status_connected);
+ if (!zynqmp_hpd_needs_retain(dp))
+ return connector_status_connected;
+
ret = drm_dp_dpcd_read(&dp->aux, 0x0, dp->dpcd,
sizeof(dp->dpcd));
if (ret < 0) {
@@ -2335,25 +2356,13 @@ static void zynqmp_dp_hpd_irq_work_func(struct work_struct *work)
{
struct zynqmp_dp *dp = container_of(work, struct zynqmp_dp,
hpd_irq_work);
- u8 status[DP_LINK_STATUS_SIZE + 2];
- int err;
guard(mutex)(&dp->lock);
if (dp->ignore_hpd)
return;
- err = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, status,
- DP_LINK_STATUS_SIZE + 2);
- if (err < 0) {
- dev_dbg_ratelimited(dp->dev,
- "could not read sink status: %d\n", err);
- } else {
- if (status[4] & DP_LINK_STATUS_UPDATED ||
- !drm_dp_clock_recovery_ok(&status[2], dp->mode.lane_cnt) ||
- !drm_dp_channel_eq_ok(&status[2], dp->mode.lane_cnt)) {
- zynqmp_dp_train_loop(dp);
- }
- }
+ if (zynqmp_hpd_needs_retain(dp))
+ zynqmp_dp_train_loop(dp);
}
static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data)
--
2.35.1.1320.gc452695387.dirty
prev parent reply other threads:[~2025-10-24 19:22 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-24 19:17 [PATCH 0/3] drm: zynqmp_dp: Retrain link after HPD if necessary Sean Anderson
2025-10-24 19:17 ` [PATCH 1/3] drm: zynqmp_dp: Update connector state before AUX transfers Sean Anderson
2025-10-24 19:17 ` [PATCH 2/3] drm: zynqmp_dp: Use smp_load/store for status Sean Anderson
2025-10-24 19:17 ` Sean Anderson [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251024191707.2310589-4-sean.anderson@linux.dev \
--to=sean.anderson@linux.dev \
--cc=airlied@gmail.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=hyun.kwon@xilinx.com \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maarten.lankhorst@linux.intel.com \
--cc=michal.simek@amd.com \
--cc=mripard@kernel.org \
--cc=simona@ffwll.ch \
--cc=tomi.valkeinen@ideasonboard.com \
--cc=tzimmermann@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).