From nobody Sun Feb 8 20:32:37 2026 Received: from out-184.mta1.migadu.com (out-184.mta1.migadu.com [95.215.58.184]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C22AC5A0E7 for ; Fri, 15 Mar 2024 23:09:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544170; cv=none; b=LGvJ+faRbSWV4fRaS+P2Eh0o5wUSIg4AODCmX3l6CF0HPgx6mIOHOn9NBHDW2QrPtldW3ghLOcKLg2rS4ahB6Xv4xw5gNz4paRhbSzxJ9lwMIsVQLseOEOElW4+vLHtuj36KXqfL2kwMmUjNhT6mvGT6xj4wQWspneldhXse2vc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544170; c=relaxed/simple; bh=Xz/0F5R/GLzLFGe5pxtwwRD+d9yZLove/zXLT4RqBL4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=TdPLzxnVAqcjeGImEehbWRZTACBibkmUo7VfOYww07dPpEkbLpwM1g0ssqHrJR9uNl1wmk/ZYahNuUe5HA7P7fC9pq/6za23kjtyseMMkeTUZTDHv53o6xt5m9TWwuTiI04dB8B+a4I9PMoO+2sxBOoUAmEfCvXNxSG6yApTSmU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=TAY2rBh7; arc=none smtp.client-ip=95.215.58.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="TAY2rBh7" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1710544166; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=mAv6BgPrVkJLTM1YeEBSiN+uJDM8AJZB5ind4wcr5C4=; b=TAY2rBh7RljPyU0M6WV4VflZz7f7ZAEpV5Yg+zeSFWnHVouccYUGUQYx9qmSi7wNzo2tg9 HMsDJCqDm+5eyPLlFeBhprxgJZ+mdbTbGGC8EvWYHseTs3ws3b3HKXc1g5GdbfMRo7Fff5 RIlR2ffhFmFQZHFqR3jnpkTdkeQVG70= From: Sean Anderson To: Laurent Pinchart , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , dri-devel@lists.freedesktop.org Cc: David Airlie , linux-kernel@vger.kernel.org, Michal Simek , linux-arm-kernel@lists.infradead.org, Daniel Vetter , Sean Anderson Subject: [PATCH 1/6] drm: zynqmp_dp: Downgrade log level for aux retries message Date: Fri, 15 Mar 2024 19:09:11 -0400 Message-Id: <20240315230916.1759060-2-sean.anderson@linux.dev> In-Reply-To: <20240315230916.1759060-1-sean.anderson@linux.dev> References: <20240315230916.1759060-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Enable this message for verbose debugging only as it is otherwise printed after every AUX message, quickly filling the log buffer. Signed-off-by: Sean Anderson Reviewed-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp= _dp.c index a0606fab0e22..98a32e6a0459 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1006,7 +1006,7 @@ zynqmp_dp_aux_transfer(struct drm_dp_aux *aux, struct= drm_dp_aux_msg *msg) msg->buffer, msg->size, &msg->reply); if (!ret) { - dev_dbg(dp->dev, "aux %d retries\n", i); + dev_vdbg(dp->dev, "aux %d retries\n", i); return msg->size; } =20 --=20 2.35.1.1320.gc452695387.dirty From nobody Sun Feb 8 20:32:37 2026 Received: from out-183.mta1.migadu.com (out-183.mta1.migadu.com [95.215.58.183]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E88AC5A0FC for ; Fri, 15 Mar 2024 23:09:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.183 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544174; cv=none; b=O9m7aLL5KrRrF3e6BcTb+5dbyZDIyiIhhTq4ragArVp1/Qfqg4tc3wqRaL6f3fU1GKG4L1Ax+BaweavOCeMwPHrDwpsICgsmaBmLdaCjVBMTpvo+x0nyQp+KfPC3urRWCGN9QXcF4EbOzuyj4hGf716KynqwMLcd2QDdJtCQMEk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544174; c=relaxed/simple; bh=wJuj2LnV7wTCORzUWD0e3tl+Zokc0cUCwwvLx8oI67U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=aXUxG8GKMSJRGTTFUKd5gTJlhrZcCKNcep5spKzqB8W3PypKo+yENVMWkFgwGQMZ7taHO7t5lNvhWpbouz1riAtKInS71GS6q6Za87P1kyCfnKKtEPbNJariIGLZPkietlsqD4Sk9mrUVBDj2Ji69OKmw7sUEu5YTS6DDLh8/RU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=KsbOkTNZ; arc=none smtp.client-ip=95.215.58.183 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="KsbOkTNZ" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1710544167; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=R2Qeb73E854SLNNqpfgzZuZinTfxWG3SchDvt92uTCI=; b=KsbOkTNZiQeegFXVQDpqlUx8jkW8Z7Ir6x2ewKOI/CqJl/7WHHXmvqtOD63M5XhdZ1wOIl DZgfnKLuJS9QqhFmPbDKc6x874/8ClrrkRYiYl3uor0Hsyq+9vuNWNA8mq/V+LNmLZUgdh yPixMKOSoUVqdH8WGoTZ/mY3F1Gx1MQ= From: Sean Anderson To: Laurent Pinchart , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , dri-devel@lists.freedesktop.org Cc: David Airlie , linux-kernel@vger.kernel.org, Michal Simek , linux-arm-kernel@lists.infradead.org, Daniel Vetter , Sean Anderson Subject: [PATCH 2/6] drm: zynqmp_dp: Adjust training values per-lane Date: Fri, 15 Mar 2024 19:09:12 -0400 Message-Id: <20240315230916.1759060-3-sean.anderson@linux.dev> In-Reply-To: <20240315230916.1759060-1-sean.anderson@linux.dev> References: <20240315230916.1759060-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" The feedback we get from the DPRX is per-lane. Make changes using this information, instead of picking the maximum values from all lanes. This results in more-consistent training on marginal links. Signed-off-by: Sean Anderson --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp= _dp.c index 98a32e6a0459..8635b5673386 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -605,28 +605,21 @@ static void zynqmp_dp_adjust_train(struct zynqmp_dp *= dp, u8 link_status[DP_LINK_STATUS_SIZE]) { u8 *train_set =3D dp->train_set; - u8 voltage =3D 0, preemphasis =3D 0; u8 i; =20 for (i =3D 0; i < dp->mode.lane_cnt; i++) { - u8 v =3D drm_dp_get_adjust_request_voltage(link_status, i); - u8 p =3D drm_dp_get_adjust_request_pre_emphasis(link_status, i); + u8 voltage =3D drm_dp_get_adjust_request_voltage(link_status, i); + u8 preemphasis =3D + drm_dp_get_adjust_request_pre_emphasis(link_status, i); =20 - if (v > voltage) - voltage =3D v; + if (voltage >=3D DP_TRAIN_VOLTAGE_SWING_LEVEL_3) + voltage |=3D DP_TRAIN_MAX_SWING_REACHED; =20 - if (p > preemphasis) - preemphasis =3D p; - } + if (preemphasis >=3D DP_TRAIN_PRE_EMPH_LEVEL_2) + preemphasis |=3D DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; =20 - if (voltage >=3D DP_TRAIN_VOLTAGE_SWING_LEVEL_3) - voltage |=3D DP_TRAIN_MAX_SWING_REACHED; - - if (preemphasis >=3D DP_TRAIN_PRE_EMPH_LEVEL_2) - preemphasis |=3D DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; - - for (i =3D 0; i < dp->mode.lane_cnt; i++) train_set[i] =3D voltage | preemphasis; + } } =20 /** --=20 2.35.1.1320.gc452695387.dirty From nobody Sun Feb 8 20:32:37 2026 Received: from out-183.mta1.migadu.com (out-183.mta1.migadu.com [95.215.58.183]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A94D05A4D5 for ; Fri, 15 Mar 2024 23:09:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.183 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544175; cv=none; b=lWhtRdd6mc2j7abnoVPyiGauF8rIACMWsmWaj8kL7xj+7lhDfWQQ2Ksz0qCSA5K8ecPg8IWGrbAixP7VQ3jI28YefpCM4NLbX0u0tfGFCrOrlrEtjasH9J52u0FG2r+l3Kr/y7GU9TuqCMLwn1r5LCtUJjapi9hQmmSfkK6V74Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544175; c=relaxed/simple; bh=W1v5F0BWjUcXEzaN4Uw2sMp+nMFJwXxu31hI5lhNfK4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=koYfrssRRLXbxP3/qpV50cmNSk9ysCxR3/D3XM5WTWzIFPpTY6dUBiSnp9Q8/fbfDArt+jmQEbn7SmwG1cd21Zmq+Lw31wfylUqEVRue7lzC7115VYOihV2jbhjwGy4lJ+cYgVBSd82ddGyHCpf2zwo80Irltc95MsKv8nSBsh4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=sCTuaiSC; arc=none smtp.client-ip=95.215.58.183 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="sCTuaiSC" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1710544169; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=x3+PPHKFdpXChJ2kv+csEq7dgWK3ohjxhbsPdG1wSL8=; b=sCTuaiSCAibV4U3YanvC1CP7RQ4P/1K1uVl4TF+QK3DoU//lK/I48pIMz7ptYb/yNP48IV 1EGWc9lPRUdMfu7SNC87Rl5ZkIedou7jEXCMxqE3IHEIhcF6SexqcRCgcOwl/I0QqPYYuF FnBDtEn2cPx1zIM/RQVpgv64myIUmRo= From: Sean Anderson To: Laurent Pinchart , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , dri-devel@lists.freedesktop.org Cc: David Airlie , linux-kernel@vger.kernel.org, Michal Simek , linux-arm-kernel@lists.infradead.org, Daniel Vetter , Sean Anderson Subject: [PATCH 3/6] drm: zynqmp_dp: Add locking Date: Fri, 15 Mar 2024 19:09:13 -0400 Message-Id: <20240315230916.1759060-4-sean.anderson@linux.dev> In-Reply-To: <20240315230916.1759060-1-sean.anderson@linux.dev> References: <20240315230916.1759060-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add some locking, since none is provided by the drm subsystem. This will prevent the IRQ/workers/bridge API calls from stepping on each other's toes. Signed-off-by: Sean Anderson --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 59 +++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp= _dp.c index 8635b5673386..d2dee58e7bf2 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -279,6 +279,7 @@ struct zynqmp_dp_config { * @dpsub: Display subsystem * @iomem: device I/O memory for register access * @reset: reset controller + * @lock: Mutex protecting this struct and register access (but not AUX) * @irq: irq * @bridge: DRM bridge for the DP encoder * @next_bridge: The downstream bridge @@ -299,6 +300,7 @@ struct zynqmp_dp { struct zynqmp_dpsub *dpsub; void __iomem *iomem; struct reset_control *reset; + struct mutex lock; int irq; =20 struct drm_bridge bridge; @@ -308,7 +310,7 @@ struct zynqmp_dp { struct drm_dp_aux aux; struct phy *phy[ZYNQMP_DP_MAX_LANES]; u8 num_lanes; - struct delayed_work hpd_work; + struct delayed_work hpd_work, hpd_irq_work; enum drm_connector_status status; bool enabled; =20 @@ -1371,8 +1373,10 @@ zynqmp_dp_bridge_mode_valid(struct drm_bridge *bridg= e, } =20 /* Check with link rate and lane count */ + mutex_lock(&dp->lock); rate =3D zynqmp_dp_max_rate(dp->link_config.max_rate, dp->link_config.max_lanes, dp->config.bpp); + mutex_unlock(&dp->lock); if (mode->clock > rate) { dev_dbg(dp->dev, "filtered mode %s for high pixel rate\n", mode->name); @@ -1399,6 +1403,7 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm= _bridge *bridge, =20 pm_runtime_get_sync(dp->dev); =20 + mutex_lock(&dp->lock); zynqmp_dp_disp_enable(dp, old_bridge_state); =20 /* @@ -1459,6 +1464,7 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm= _bridge *bridge, zynqmp_dp_write(dp, ZYNQMP_DP_SOFTWARE_RESET, ZYNQMP_DP_SOFTWARE_RESET_ALL); zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 1); + mutex_unlock(&dp->lock); } =20 static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge, @@ -1466,6 +1472,7 @@ static void zynqmp_dp_bridge_atomic_disable(struct dr= m_bridge *bridge, { struct zynqmp_dp *dp =3D bridge_to_dp(bridge); =20 + mutex_lock(&dp->lock); dp->enabled =3D false; cancel_delayed_work(&dp->hpd_work); zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 0); @@ -1476,6 +1483,7 @@ static void zynqmp_dp_bridge_atomic_disable(struct dr= m_bridge *bridge, zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0); =20 zynqmp_dp_disp_disable(dp, old_bridge_state); + mutex_unlock(&dp->lock); =20 pm_runtime_put_sync(dp->dev); } @@ -1518,6 +1526,8 @@ static enum drm_connector_status zynqmp_dp_bridge_det= ect(struct drm_bridge *brid u32 state, i; int ret; =20 + mutex_lock(&dp->lock); + /* * This is from heuristic. It takes some delay (ex, 100 ~ 500 msec) to * get the HPD signal with some monitors. @@ -1545,11 +1555,13 @@ static enum drm_connector_status zynqmp_dp_bridge_d= etect(struct drm_bridge *brid dp->num_lanes); =20 dp->status =3D connector_status_connected; + mutex_unlock(&dp->lock); return connector_status_connected; } =20 disconnected: dp->status =3D connector_status_disconnected; + mutex_unlock(&dp->lock); return connector_status_disconnected; } =20 @@ -1611,6 +1623,29 @@ static void zynqmp_dp_hpd_work_func(struct work_stru= ct *work) drm_bridge_hpd_notify(&dp->bridge, status); } =20 +static void zynqmp_dp_hpd_irq_work_func(struct work_struct *work) +{ + struct zynqmp_dp *dp =3D container_of(work, struct zynqmp_dp, + hpd_irq_work.work); + u8 status[DP_LINK_STATUS_SIZE + 2]; + int err; + + mutex_lock(&dp->lock); + err =3D 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); + } + } + mutex_unlock(&dp->lock); +} + static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data) { struct zynqmp_dp *dp =3D (struct zynqmp_dp *)data; @@ -1635,23 +1670,9 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, vo= id *data) if (status & ZYNQMP_DP_INT_HPD_EVENT) schedule_delayed_work(&dp->hpd_work, 0); =20 - if (status & ZYNQMP_DP_INT_HPD_IRQ) { - int ret; - u8 status[DP_LINK_STATUS_SIZE + 2]; + if (status & ZYNQMP_DP_INT_HPD_IRQ) + schedule_delayed_work(&dp->hpd_irq_work, 0); =20 - ret =3D drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, status, - DP_LINK_STATUS_SIZE + 2); - if (ret < 0) - goto handled; - - 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); - } - } - -handled: return IRQ_HANDLED; } =20 @@ -1674,8 +1695,10 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) dp->dev =3D &pdev->dev; dp->dpsub =3D dpsub; dp->status =3D connector_status_disconnected; + mutex_init(&dp->lock); =20 INIT_DELAYED_WORK(&dp->hpd_work, zynqmp_dp_hpd_work_func); + INIT_DELAYED_WORK(&dp->hpd_irq_work, zynqmp_dp_hpd_irq_work_func); =20 /* Acquire all resources (IOMEM, IRQ and PHYs). */ res =3D platform_get_resource_byname(pdev, IORESOURCE_MEM, "dp"); @@ -1775,6 +1798,7 @@ void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub) zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, ZYNQMP_DP_INT_ALL); disable_irq(dp->irq); =20 + cancel_delayed_work_sync(&dp->hpd_irq_work); cancel_delayed_work_sync(&dp->hpd_work); =20 zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 0); @@ -1782,4 +1806,5 @@ void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub) =20 zynqmp_dp_phy_exit(dp); zynqmp_dp_reset(dp, true); + mutex_destroy(&dp->lock); } --=20 2.35.1.1320.gc452695387.dirty From nobody Sun Feb 8 20:32:37 2026 Received: from out-178.mta1.migadu.com (out-178.mta1.migadu.com [95.215.58.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5BC955B1F1 for ; Fri, 15 Mar 2024 23:09:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544175; cv=none; b=rziQL9lWnnAYhrlaK6I+npXxdEcIyf7I+AD6+CSrt5dJfzPl5FYsp8zL6sYpgWVKPIOInF55FfX3o1qBbLRuBrb0A3kUbkstHFgk+xGaVsN2KohdXX9H+BHHASdmp9laJZFRuZ41B6dxJIa2KhJdrpQ3yEJwIkLUhypB26m7vio= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544175; c=relaxed/simple; bh=CxjxZmkPwitdXK8b0r1uES32cbnbup5zS4MFPkmsP4Y=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=afM5LRx+hggKPSuGX3Fhvu4Xd6OHSY9T/05yFjOQwFM7Wt/0Wy86CgjxgdO7Ojpb/CPTwqXdrQuNja5BXi1+XWRYYh9FFC8ti3HrmOaY4HC2c74vXWOplGXPf5LXLVh/jsn8cokzE7B1q3at98f5psRjNbimbqOvW8AT24dZvIA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=U7Rkcebb; arc=none smtp.client-ip=95.215.58.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="U7Rkcebb" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1710544171; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=GuGHTETxhmXOcV9deAeLZwjxRa5UHxis9t8GwbgUvBg=; b=U7RkcebbeeF0JxQ3NK0tDUhd5nFg5+xGDimuRl5C8+9syvJe/Dao5gZlVOFqPGA8foblDq DjWXA6u3aTWi95egf3KpX+h/u/wVLv62F8FQarmCTHAbAk3Ck64Qid+tq/cxM1wg8zvgmA zFKMnVoJqvLFUoV7uacLm1rSVEqL/Rc= From: Sean Anderson To: Laurent Pinchart , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , dri-devel@lists.freedesktop.org Cc: David Airlie , linux-kernel@vger.kernel.org, Michal Simek , linux-arm-kernel@lists.infradead.org, Daniel Vetter , Sean Anderson Subject: [PATCH 4/6] drm: zynqmp_dp: Split off several helper functions Date: Fri, 15 Mar 2024 19:09:14 -0400 Message-Id: <20240315230916.1759060-5-sean.anderson@linux.dev> In-Reply-To: <20240315230916.1759060-1-sean.anderson@linux.dev> References: <20240315230916.1759060-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" In preparation for supporting compliance testing, split off several helper functions. No functional change intended. Signed-off-by: Sean Anderson Reviewed-by: Laurent Pinchart --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 49 +++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp= _dp.c index d2dee58e7bf2..24043847dab4 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -627,6 +627,7 @@ static void zynqmp_dp_adjust_train(struct zynqmp_dp *dp, /** * zynqmp_dp_update_vs_emph - Update the training values * @dp: DisplayPort IP core structure + * @train_set: A set of training values * * Update the training values based on the request from sink. The mapped v= alues * are predefined, and values(vs, pe, pc) are from the device manual. @@ -634,12 +635,12 @@ static void zynqmp_dp_adjust_train(struct zynqmp_dp *= dp, * Return: 0 if vs and emph are updated successfully, or the error code re= turned * by drm_dp_dpcd_write(). */ -static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp) +static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp, u8 *train_set) { unsigned int i; int ret; =20 - ret =3D drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->train_set, + ret =3D drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->mode.lane_cnt); if (ret < 0) return ret; @@ -647,7 +648,7 @@ static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *d= p) for (i =3D 0; i < dp->mode.lane_cnt; i++) { u32 reg =3D ZYNQMP_DP_SUB_TX_PHY_PRECURSOR_LANE_0 + i * 4; union phy_configure_opts opts =3D { 0 }; - u8 train =3D dp->train_set[i]; + u8 train =3D train_set[i]; =20 opts.dp.voltage[0] =3D (train & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT; @@ -691,7 +692,7 @@ static int zynqmp_dp_link_train_cr(struct zynqmp_dp *dp) * So, This loop should exit before 512 iterations */ for (max_tries =3D 0; max_tries < 512; max_tries++) { - ret =3D zynqmp_dp_update_vs_emph(dp); + ret =3D zynqmp_dp_update_vs_emph(dp, dp->train_set); if (ret) return ret; =20 @@ -756,7 +757,7 @@ static int zynqmp_dp_link_train_ce(struct zynqmp_dp *dp) return ret; =20 for (tries =3D 0; tries < DP_MAX_TRAINING_TRIES; tries++) { - ret =3D zynqmp_dp_update_vs_emph(dp); + ret =3D zynqmp_dp_update_vs_emph(dp, dp->train_set); if (ret) return ret; =20 @@ -779,28 +780,28 @@ static int zynqmp_dp_link_train_ce(struct zynqmp_dp *= dp) } =20 /** - * zynqmp_dp_train - Train the link - * @dp: DisplayPort IP core structure + * zynqmp_dp_setup() - Set up major link parameters + * @bw_code: The link bandwidth as a multiple of 270 MHz + * @lane_cnt: The number of lanes to use + * @enhanced: Use enhanced framing + * @downspread: Enable spread-spectrum clocking * - * Return: 0 if all trains are done successfully, or corresponding error c= ode. + * Return: 0 on success, or -errno on failure */ -static int zynqmp_dp_train(struct zynqmp_dp *dp) +static int zynqmp_dp_setup(struct zynqmp_dp *dp, u8 bw_code, u8 lane_cnt, + bool enhanced, bool downspread) { u32 reg; - u8 bw_code =3D dp->mode.bw_code; - u8 lane_cnt =3D dp->mode.lane_cnt; u8 aux_lane_cnt =3D lane_cnt; - bool enhanced; int ret; =20 zynqmp_dp_write(dp, ZYNQMP_DP_LANE_COUNT_SET, lane_cnt); - enhanced =3D drm_dp_enhanced_frame_cap(dp->dpcd); if (enhanced) { zynqmp_dp_write(dp, ZYNQMP_DP_ENHANCED_FRAME_EN, 1); aux_lane_cnt |=3D DP_LANE_COUNT_ENHANCED_FRAME_EN; } =20 - if (dp->dpcd[3] & 0x1) { + if (downspread) { zynqmp_dp_write(dp, ZYNQMP_DP_DOWNSPREAD_CTL, 1); drm_dp_dpcd_writeb(&dp->aux, DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5); @@ -843,8 +844,24 @@ static int zynqmp_dp_train(struct zynqmp_dp *dp) } =20 zynqmp_dp_write(dp, ZYNQMP_DP_PHY_CLOCK_SELECT, reg); - ret =3D zynqmp_dp_phy_ready(dp); - if (ret < 0) + return zynqmp_dp_phy_ready(dp); +} + + +/** + * zynqmp_dp_train - Train the link + * @dp: DisplayPort IP core structure + * + * Return: 0 if all trains are done successfully, or corresponding error c= ode. + */ +static int zynqmp_dp_train(struct zynqmp_dp *dp) +{ + int ret; + + ret =3D zynqmp_dp_setup(dp, dp->mode.bw_code, dp->mode.lane_cnt, + drm_dp_enhanced_frame_cap(dp->dpcd), + dp->dpcd[3] & 0x1); + if (ret) return ret; =20 zynqmp_dp_write(dp, ZYNQMP_DP_SCRAMBLING_DISABLE, 1); --=20 2.35.1.1320.gc452695387.dirty From nobody Sun Feb 8 20:32:37 2026 Received: from out-174.mta1.migadu.com (out-174.mta1.migadu.com [95.215.58.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AEE425B5C0 for ; Fri, 15 Mar 2024 23:09:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544178; cv=none; b=JyOwy08hNXpe8/HmlqJXI92+1Bl6nEQ2KZy2chDxf9HP4/ra7+YCwz4FqAKxkoCtCMEsYpRUF01j7mneTIWu/iFtRUycHuSo//IDaVDOjFTR2OL3QvMwMnsneGL4a1CYcj4vH2A7fOlVFZnxh7pG9jQ82AXzbBwtGHSb5hI3zAY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544178; c=relaxed/simple; bh=h4cojU8kwWfz/UTtLKse1V3hNWYPjNuN24lw+Cpjhno=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=fIJEyuRI5+RuG4IA8fElcFUOoqRCT8rkFi/4QXqu5K+1kFeNpNqkAPbxUWljxjJIwm+CXnK4ysDbOZGwUefTC4olS+Aq8Uxw7pAzoys7t3XQgtHJkqdwyKtvqWpOgLn6aIODMJqI+jW8iq9FAw5IYRbA4L4pDge0Kpn8itVwCzI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=tTOCd92z; arc=none smtp.client-ip=95.215.58.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="tTOCd92z" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1710544174; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2iCejvqA5IbzASiXQE6xrf0mIzF3OuUDUKlOJcBerFY=; b=tTOCd92zqEXX69LmQS9XAiZrEQdbzzyrOmbVeN3kSGIjYq/h/7Vz38DXpQE72l7TTNH8cC 2+SgkUQBW8h2oQ2tM+N9gEo393sCF4tto1OlQOcdFOrUagGkmLIm9UF45kodIDTnJy3nsU 7UoG7DkoiKNQpGH0z4W00x/+zHpRBY4= From: Sean Anderson To: Laurent Pinchart , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , dri-devel@lists.freedesktop.org Cc: David Airlie , linux-kernel@vger.kernel.org, Michal Simek , linux-arm-kernel@lists.infradead.org, Daniel Vetter , Sean Anderson Subject: [PATCH 5/6] drm: zynqmp_dp: Optionally ignore DPCD errors Date: Fri, 15 Mar 2024 19:09:15 -0400 Message-Id: <20240315230916.1759060-6-sean.anderson@linux.dev> In-Reply-To: <20240315230916.1759060-1-sean.anderson@linux.dev> References: <20240315230916.1759060-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" When testing, it's convenient to be able to ignore DPCD errors if there is test equipment which can't emulate a DPRX connected to the output. Add some (currently-unused) options to ignore these errors and just reconfigure our internal registers as we usually would. Signed-off-by: Sean Anderson --- 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 24043847dab4..040f7b88ee51 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -628,6 +628,7 @@ static void zynqmp_dp_adjust_train(struct zynqmp_dp *dp, * zynqmp_dp_update_vs_emph - Update the training values * @dp: DisplayPort IP core structure * @train_set: A set of training values + * @ignore_dpcd: Ignore DPCD errors * * Update the training values based on the request from sink. The mapped v= alues * are predefined, and values(vs, pe, pc) are from the device manual. @@ -635,15 +636,19 @@ static void zynqmp_dp_adjust_train(struct zynqmp_dp *= dp, * Return: 0 if vs and emph are updated successfully, or the error code re= turned * by drm_dp_dpcd_write(). */ -static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp, u8 *train_set) +static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp, u8 *train_set, + bool ignore_dpcd) { unsigned int i; int ret; =20 ret =3D drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->mode.lane_cnt); - if (ret < 0) - return ret; + if (ret < 0) { + if (!ignore_dpcd) + return ret; + dev_warn(dp->dev, "failed to update vs/emph\n"); + } =20 for (i =3D 0; i < dp->mode.lane_cnt; i++) { u32 reg =3D ZYNQMP_DP_SUB_TX_PHY_PRECURSOR_LANE_0 + i * 4; @@ -692,7 +697,7 @@ static int zynqmp_dp_link_train_cr(struct zynqmp_dp *dp) * So, This loop should exit before 512 iterations */ for (max_tries =3D 0; max_tries < 512; max_tries++) { - ret =3D zynqmp_dp_update_vs_emph(dp, dp->train_set); + ret =3D zynqmp_dp_update_vs_emph(dp, dp->train_set, false); if (ret) return ret; =20 @@ -757,7 +762,7 @@ static int zynqmp_dp_link_train_ce(struct zynqmp_dp *dp) return ret; =20 for (tries =3D 0; tries < DP_MAX_TRAINING_TRIES; tries++) { - ret =3D zynqmp_dp_update_vs_emph(dp, dp->train_set); + ret =3D zynqmp_dp_update_vs_emph(dp, dp->train_set, false); if (ret) return ret; =20 @@ -785,11 +790,12 @@ static int zynqmp_dp_link_train_ce(struct zynqmp_dp *= dp) * @lane_cnt: The number of lanes to use * @enhanced: Use enhanced framing * @downspread: Enable spread-spectrum clocking + * @ignore_dpcd: Ignore DPCD errors; useful for testing * * Return: 0 on success, or -errno on failure */ static int zynqmp_dp_setup(struct zynqmp_dp *dp, u8 bw_code, u8 lane_cnt, - bool enhanced, bool downspread) + bool enhanced, bool downspread, bool ignore_dpcd) { u32 reg; u8 aux_lane_cnt =3D lane_cnt; @@ -812,21 +818,24 @@ static int zynqmp_dp_setup(struct zynqmp_dp *dp, u8 b= w_code, u8 lane_cnt, =20 ret =3D drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET, aux_lane_cnt); if (ret < 0) { - dev_err(dp->dev, "failed to set lane count\n"); - return ret; + dev_warn(dp->dev, "failed to set lane count\n"); + if (!ignore_dpcd) + return ret; } =20 ret =3D drm_dp_dpcd_writeb(&dp->aux, DP_MAIN_LINK_CHANNEL_CODING_SET, DP_SET_ANSI_8B10B); if (ret < 0) { - dev_err(dp->dev, "failed to set ANSI 8B/10B encoding\n"); - return ret; + dev_warn(dp->dev, "failed to set ANSI 8B/10B encoding\n"); + if (!ignore_dpcd) + return ret; } =20 ret =3D drm_dp_dpcd_writeb(&dp->aux, DP_LINK_BW_SET, bw_code); if (ret < 0) { - dev_err(dp->dev, "failed to set DP bandwidth\n"); - return ret; + dev_warn(dp->dev, "failed to set DP bandwidth\n"); + if (!ignore_dpcd) + return ret; } =20 zynqmp_dp_write(dp, ZYNQMP_DP_LINK_BW_SET, bw_code); @@ -860,7 +869,7 @@ static int zynqmp_dp_train(struct zynqmp_dp *dp) =20 ret =3D zynqmp_dp_setup(dp, dp->mode.bw_code, dp->mode.lane_cnt, drm_dp_enhanced_frame_cap(dp->dpcd), - dp->dpcd[3] & 0x1); + dp->dpcd[3] & 0x1, false); if (ret) return ret; =20 @@ -877,7 +886,7 @@ static int zynqmp_dp_train(struct zynqmp_dp *dp) ret =3D drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); if (ret < 0) { - dev_err(dp->dev, "failed to disable training pattern\n"); + dev_warn(dp->dev, "failed to disable training pattern\n"); return ret; } zynqmp_dp_write(dp, ZYNQMP_DP_TRAINING_PATTERN_SET, --=20 2.35.1.1320.gc452695387.dirty From nobody Sun Feb 8 20:32:37 2026 Received: from out-184.mta1.migadu.com (out-184.mta1.migadu.com [95.215.58.184]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7D8B65B68D for ; Fri, 15 Mar 2024 23:09:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544180; cv=none; b=JkNiBgoULya9jhiTy2+Ge8CyVOFuLc0uazndnNlCl5hG8MPQ3J83kMymtrUvv+MFO1V1L4Q0IhdJODriqL1wMVTJpd6gxWY0HaImPAn11/AXH/6kO9qVK10jMAH9BySwOVFyKOPzOTc2vaHX0g64CZWIFlOSPcwdVWTScwt473s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1710544180; c=relaxed/simple; bh=gX2ZXaZHkPCE4Lbo82dnWxWzD26K6YRAFDZGIBwWj90=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=EdXlihcmQ7C/mWwQGD3Lod97UyA59bMKpV4vrlOYIyU8YtBfpbv6id9lE/qpcj7zHZKJTRu7xxmXc0lAvN1SVdiOSbgj26GfrmlIBnSqP2iHN8VQqId2V/DAKxF7FrQ++Uk6Ji1MuSGhifQvLH4/d44Yyyc6GdheXl64sWa1D2M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=BxTXyEDr; arc=none smtp.client-ip=95.215.58.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="BxTXyEDr" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1710544175; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=uDXfOekMkWxHj0tq2/kO43ElNfcMHuVmA6YRLV++uCM=; b=BxTXyEDrHCg/zgdtWnpqPFZs/Ai1j7w7L8cMPTnW0O+9WZz3SzGJrp++nBRMV7N8syzUAd aVMmu47GM8FxaoK0QtZCXUBvN9Wlm4OUAhQ3jH6isV62Ys6+yRsWl4W9whXSVKlscfHT5j SiYhf8rJXON+LxhvaErRM7MQHuRaSgw= From: Sean Anderson To: Laurent Pinchart , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , dri-devel@lists.freedesktop.org Cc: David Airlie , linux-kernel@vger.kernel.org, Michal Simek , linux-arm-kernel@lists.infradead.org, Daniel Vetter , Sean Anderson Subject: [PATCH 6/6] drm: zynqmp_dp: Add debugfs interface for compliance testing Date: Fri, 15 Mar 2024 19:09:16 -0400 Message-Id: <20240315230916.1759060-7-sean.anderson@linux.dev> In-Reply-To: <20240315230916.1759060-1-sean.anderson@linux.dev> References: <20240315230916.1759060-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add a debugfs interface for exercising the various test modes supported by the DisplayPort controller. This allows performing compliance testing, or performing signal integrity measurements on a failing link. At the moment, we do not support sink-driven link quality testing, although such support would be fairly easy to add. Signed-off-by: Sean Anderson --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 591 ++++++++++++++++++++++++++++++- 1 file changed, 590 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp= _dp.c index 040f7b88ee51..57032186e1ca 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -18,7 +18,9 @@ #include #include =20 +#include #include +#include #include #include #include @@ -50,6 +52,7 @@ MODULE_PARM_DESC(power_on_delay_ms, "DP power on delay in= msec (default: 4)"); #define ZYNQMP_DP_LANE_COUNT_SET 0x4 #define ZYNQMP_DP_ENHANCED_FRAME_EN 0x8 #define ZYNQMP_DP_TRAINING_PATTERN_SET 0xc +#define ZYNQMP_DP_LINK_QUAL_PATTERN_SET 0x10 #define ZYNQMP_DP_SCRAMBLING_DISABLE 0x14 #define ZYNQMP_DP_DOWNSPREAD_CTL 0x18 #define ZYNQMP_DP_SOFTWARE_RESET 0x1c @@ -63,6 +66,9 @@ MODULE_PARM_DESC(power_on_delay_ms, "DP power on delay in= msec (default: 4)"); ZYNQMP_DP_SOFTWARE_RESET_STREAM3 | \ ZYNQMP_DP_SOFTWARE_RESET_STREAM4 | \ ZYNQMP_DP_SOFTWARE_RESET_AUX) +#define ZYNQMP_DP_COMP_PATTERN_80BIT_1 0x20 +#define ZYNQMP_DP_COMP_PATTERN_80BIT_2 0x24 +#define ZYNQMP_DP_COMP_PATTERN_80BIT_3 0x28 =20 /* Core enable registers */ #define ZYNQMP_DP_TRANSMITTER_ENABLE 0x80 @@ -206,6 +212,7 @@ MODULE_PARM_DESC(power_on_delay_ms, "DP power on delay = in msec (default: 4)"); #define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_2 BIT(2) #define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_3 BIT(3) #define ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL 0xf +#define ZYNQMP_DP_TRANSMIT_PRBS7 0x230 #define ZYNQMP_DP_PHY_PRECURSOR_LANE_0 0x23c #define ZYNQMP_DP_PHY_PRECURSOR_LANE_1 0x240 #define ZYNQMP_DP_PHY_PRECURSOR_LANE_2 0x244 @@ -273,6 +280,69 @@ struct zynqmp_dp_config { u8 bpp; }; =20 +/** + * enum test_pattern - Test patterns for test testing + * TEST_VIDEO: Use regular video input + * TEST_SYMBOL_ERROR: Symbol error measurement pattern + * TEST_PRBS7: Output of the PRBS7 (x^7 + x^6 + 1) polynomial + * TEST_80BIT_CUSTOM: A custom 80-bit pattern + * TEST_CP2520: HBR2 compliance eye pattern + * TEST_TPS1: Link training symbol pattern TPS1 (/D10.2/) + * TEST_TPS2: Link training symbol pattern TPS2 + * TEST_TPS3: Link training symbol pattern TPS3 (for HBR2) + */ +enum test_pattern { + TEST_VIDEO, + TEST_TPS1, + TEST_TPS2, + TEST_TPS3, + TEST_SYMBOL_ERROR, + TEST_PRBS7, + TEST_80BIT_CUSTOM, + TEST_CP2520, +}; + +static const char *const test_pattern_str[] =3D { + [TEST_VIDEO] =3D "video", + [TEST_TPS1] =3D "tps1", + [TEST_TPS2] =3D "tps2", + [TEST_TPS3] =3D "tps3", + [TEST_SYMBOL_ERROR] =3D "symbol-error", + [TEST_PRBS7] =3D "prbs7", + [TEST_80BIT_CUSTOM] =3D "80bit-custom", + [TEST_CP2520] =3D "cp2520", +}; + +/** + * struct zynqmp_dp_test - Configuration for test mode + * @pattern: The test pattern + * @enhanced: Use enhanced framing + * @downspread: Use SSC + * @active: Whether test mode is active + * @custom: Custom pattern for %TEST_80BIT_CUSTOM + * @train_set: Voltage/preemphasis settings + * @bw_code: Bandwidth code for the link + * @link_cnt: Number of lanes + */ +struct zynqmp_dp_test { + enum test_pattern pattern; + bool enhanced, downspread, active; + u8 custom[10]; + u8 train_set[ZYNQMP_DP_MAX_LANES]; + u8 bw_code; + u8 link_cnt; +}; + +/** + * struct zynqmp_dp_train_set_priv - Private data for train_set debugfs fi= les + * @dp: DisplayPort IP core structure + * @lane: The lane for this file + */ +struct zynqmp_dp_train_set_priv { + struct zynqmp_dp *dp; + int lane; +}; + /** * struct zynqmp_dp - Xilinx DisplayPort core * @dev: device structure @@ -283,6 +353,7 @@ struct zynqmp_dp_config { * @irq: irq * @bridge: DRM bridge for the DP encoder * @next_bridge: The downstream bridge + * @test: Configuration for test mode * @config: IP core configuration from DTS * @aux: aux channel * @phy: PHY handles for DP lanes @@ -294,6 +365,7 @@ struct zynqmp_dp_config { * @link_config: common link configuration between IP core and sink device * @mode: current mode between IP core and sink device * @train_set: set of training data + * @debugfs_train_set: Debugfs private data for @train_set */ struct zynqmp_dp { struct device *dev; @@ -306,6 +378,7 @@ struct zynqmp_dp { struct drm_bridge bridge; struct drm_bridge *next_bridge; =20 + struct zynqmp_dp_test test; struct zynqmp_dp_config config; struct drm_dp_aux aux; struct phy *phy[ZYNQMP_DP_MAX_LANES]; @@ -318,6 +391,7 @@ struct zynqmp_dp { struct zynqmp_dp_link_config link_config; struct zynqmp_dp_mode mode; u8 train_set[ZYNQMP_DP_MAX_LANES]; + struct zynqmp_dp_train_set_priv debugfs_train_set[ZYNQMP_DP_MAX_LANES]; }; =20 static inline struct zynqmp_dp *bridge_to_dp(struct drm_bridge *bridge) @@ -1599,6 +1673,510 @@ static struct edid *zynqmp_dp_bridge_get_edid(struc= t drm_bridge *bridge, return drm_get_edid(connector, &dp->aux.ddc); } =20 +/* -----------------------------------------------------------------------= ------ + * debugfs + */ + +/** + * zynqmp_dp_set_test_pattern() - Configure the link for a test pattern + * @dp: DisplayPort IP core structure + */ +static void zynqmp_dp_set_test_pattern(struct zynqmp_dp *dp, + enum test_pattern pattern, + u8 *const custom) +{ + bool scramble =3D false; + u32 train_pattern =3D 0; + u32 link_pattern =3D 0; + u8 dpcd_train =3D 0; + u8 dpcd_link =3D 0; + int err; + + switch (pattern) { + case TEST_TPS1: + train_pattern =3D 1; + break; + case TEST_TPS2: + train_pattern =3D 2; + break; + case TEST_TPS3: + train_pattern =3D 3; + break; + case TEST_SYMBOL_ERROR: + scramble =3D true; + link_pattern =3D DP_PHY_TEST_PATTERN_ERROR_COUNT; + break; + case TEST_PRBS7: + /* We use a dedicated register to enable PRBS7 */ + dpcd_link =3D DP_LINK_QUAL_PATTERN_ERROR_RATE; + break; + case TEST_80BIT_CUSTOM: { + const u8 *p =3D custom; + + link_pattern =3D DP_LINK_QUAL_PATTERN_80BIT_CUSTOM; + + zynqmp_dp_write(dp, ZYNQMP_DP_COMP_PATTERN_80BIT_1, + (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); + zynqmp_dp_write(dp, ZYNQMP_DP_COMP_PATTERN_80BIT_2, + (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]); + zynqmp_dp_write(dp, ZYNQMP_DP_COMP_PATTERN_80BIT_3, + (p[9] << 8) | p[8]); + break; + } + case TEST_CP2520: + link_pattern =3D DP_LINK_QUAL_PATTERN_CP2520_PAT_1; + break; + default: + WARN_ON_ONCE(1); + fallthrough; + case TEST_VIDEO: + scramble =3D true; + } + + zynqmp_dp_write(dp, ZYNQMP_DP_SCRAMBLING_DISABLE, !scramble); + zynqmp_dp_write(dp, ZYNQMP_DP_TRAINING_PATTERN_SET, train_pattern); + zynqmp_dp_write(dp, ZYNQMP_DP_LINK_QUAL_PATTERN_SET, link_pattern); + zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMIT_PRBS7, pattern =3D=3D TEST_PRBS7); + + dpcd_link =3D dpcd_link ?: link_pattern; + dpcd_train =3D train_pattern; + if (!scramble) + dpcd_train |=3D DP_LINK_SCRAMBLING_DISABLE; + + if (dp->dpcd[DP_DPCD_REV] < 0x12) { + if (pattern =3D=3D TEST_CP2520) + dev_warn(dp->dev, + "can't set sink link quality pattern to CP2520 for DPCD < r1.2; error = counters will be invalid\n"); + else + dpcd_train |=3D FIELD_PREP(DP_LINK_QUAL_PATTERN_11_MASK, + dpcd_link); + } else { + u8 dpcd_link_lane[ZYNQMP_DP_MAX_LANES]; + + memset(dpcd_link_lane, dpcd_link, ZYNQMP_DP_MAX_LANES); + err =3D drm_dp_dpcd_write(&dp->aux, DP_LINK_QUAL_LANE0_SET, + dpcd_link_lane, ZYNQMP_DP_MAX_LANES); + if (err < 0) + dev_err(dp->dev, "failed to set quality pattern\n"); + } + + err =3D drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, dpcd_train); + if (err < 0) + dev_err(dp->dev, "failed to set training pattern\n"); +} + +static int zynqmp_dp_test_setup(struct zynqmp_dp *dp) +{ + return zynqmp_dp_setup(dp, dp->test.bw_code, dp->test.link_cnt, + dp->test.enhanced, dp->test.downspread, true); +} + +static ssize_t zynqmp_dp_pattern_read(struct file *file, char __user *user= _buf, + size_t count, loff_t *ppos) +{ + struct dentry *dentry =3D file->f_path.dentry; + struct zynqmp_dp *dp =3D file->private_data; + char buf[16]; + ssize_t ret; + + ret =3D debugfs_file_get(dentry); + if (unlikely(ret)) + return ret; + + mutex_lock(&dp->lock); + ret =3D snprintf(buf, sizeof(buf), "%s\n", + test_pattern_str[dp->test.pattern]); + mutex_unlock(&dp->lock); + + debugfs_file_put(dentry); + return simple_read_from_buffer(user_buf, count, ppos, buf, ret); +} + +static ssize_t zynqmp_dp_pattern_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + + struct dentry *dentry =3D file->f_path.dentry; + struct zynqmp_dp *dp =3D file->private_data; + char buf[16]; + ssize_t ret; + int pattern; + + ret =3D debugfs_file_get(dentry); + if (unlikely(ret)) + return ret; + + ret =3D simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, + count); + if (ret < 0) + goto out; + buf[ret] =3D '\0'; + + pattern =3D sysfs_match_string(test_pattern_str, buf); + if (pattern < 0) { + ret =3D -EINVAL; + goto out; + } + + mutex_lock(&dp->lock); + dp->test.pattern =3D pattern; + if (dp->test.active) + zynqmp_dp_set_test_pattern(dp, dp->test.pattern, + dp->test.custom); + mutex_unlock(&dp->lock); + +out: + debugfs_file_put(dentry); + return ret; +} + +static const struct file_operations fops_zynqmp_dp_pattern =3D { + .read =3D zynqmp_dp_pattern_read, + .write =3D zynqmp_dp_pattern_write, + .open =3D simple_open, + .llseek =3D noop_llseek, +}; + +static int zynqmp_dp_enhanced_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp =3D data; + + mutex_lock(&dp->lock); + *val =3D dp->test.enhanced; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_enhanced_set(void *data, u64 val) +{ + struct zynqmp_dp *dp =3D data; + int ret =3D 0; + + mutex_lock(&dp->lock); + dp->test.enhanced =3D val; + if (dp->test.active) + ret =3D zynqmp_dp_test_setup(dp); + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_enhanced, zynqmp_dp_enhanced_get, + zynqmp_dp_enhanced_set, "%llu\n"); + +static int zynqmp_dp_downspread_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp =3D data; + + mutex_lock(&dp->lock); + *val =3D dp->test.downspread; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_downspread_set(void *data, u64 val) +{ + struct zynqmp_dp *dp =3D data; + int ret =3D 0; + + mutex_lock(&dp->lock); + dp->test.downspread =3D val; + if (dp->test.active) + ret =3D zynqmp_dp_test_setup(dp); + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_downspread, zynqmp_dp_downspread_g= et, + zynqmp_dp_downspread_set, "%llu\n"); + +static int zynqmp_dp_active_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp =3D data; + + mutex_lock(&dp->lock); + *val =3D dp->test.active; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_active_set(void *data, u64 val) +{ + struct zynqmp_dp *dp =3D data; + int ret =3D 0; + + mutex_lock(&dp->lock); + if (val) { + if (val < 2) { + ret =3D zynqmp_dp_test_setup(dp); + if (ret) + goto out; + } + + zynqmp_dp_set_test_pattern(dp, dp->test.pattern, + dp->test.custom); + zynqmp_dp_update_vs_emph(dp, dp->test.train_set, true); + dp->test.active =3D true; + } else { + dp->test.active =3D false; + zynqmp_dp_set_test_pattern(dp, TEST_VIDEO, NULL); + zynqmp_dp_train_loop(dp); + } +out: + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_active, zynqmp_dp_active_get, + zynqmp_dp_active_set, "%llu\n"); + +static ssize_t zynqmp_dp_custom_read(struct file *file, char __user *user_= buf, + size_t count, loff_t *ppos) +{ + struct dentry *dentry =3D file->f_path.dentry; + struct zynqmp_dp *dp =3D file->private_data; + ssize_t ret; + + ret =3D debugfs_file_get(dentry); + if (unlikely(ret)) + return ret; + + mutex_lock(&dp->lock); + ret =3D simple_read_from_buffer(user_buf, count, ppos, &dp->test.custom, + sizeof(dp->test.custom)); + mutex_unlock(&dp->lock); + + debugfs_file_put(dentry); + return ret; +} + +static ssize_t zynqmp_dp_custom_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + + struct dentry *dentry =3D file->f_path.dentry; + struct zynqmp_dp *dp =3D file->private_data; + ssize_t ret; + char buf[sizeof(dp->test.custom)]; + + ret =3D debugfs_file_get(dentry); + if (unlikely(ret)) + return ret; + + ret =3D simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count); + if (ret < 0) + goto out; + + mutex_lock(&dp->lock); + memcpy(dp->test.custom, buf, ret); + if (dp->test.active) + zynqmp_dp_set_test_pattern(dp, dp->test.pattern, + dp->test.custom); + mutex_unlock(&dp->lock); + +out: + debugfs_file_put(dentry); + return ret; +} + +static const struct file_operations fops_zynqmp_dp_custom =3D { + .read =3D zynqmp_dp_custom_read, + .write =3D zynqmp_dp_custom_write, + .open =3D simple_open, + .llseek =3D noop_llseek, +}; + +static int zynqmp_dp_swing_get(void *data, u64 *val) +{ + struct zynqmp_dp_train_set_priv *priv =3D data; + struct zynqmp_dp *dp =3D priv->dp; + + mutex_lock(&dp->lock); + *val =3D dp->test.train_set[priv->lane] & DP_TRAIN_VOLTAGE_SWING_MASK; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_swing_set(void *data, u64 val) +{ + struct zynqmp_dp_train_set_priv *priv =3D data; + struct zynqmp_dp *dp =3D priv->dp; + u8 *train_set =3D &dp->test.train_set[priv->lane]; + int ret =3D 0; + + if (val > 3) + return -EINVAL; + + mutex_lock(&dp->lock); + *train_set &=3D ~(DP_TRAIN_MAX_SWING_REACHED | + DP_TRAIN_VOLTAGE_SWING_MASK); + *train_set |=3D val; + if (val =3D=3D 3) + *train_set |=3D DP_TRAIN_MAX_SWING_REACHED; + + if (dp->test.active) + zynqmp_dp_update_vs_emph(dp, dp->test.train_set, true); + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_swing, zynqmp_dp_swing_get, + zynqmp_dp_swing_set, "%llu\n"); + +static int zynqmp_dp_preemphasis_get(void *data, u64 *val) +{ + struct zynqmp_dp_train_set_priv *priv =3D data; + struct zynqmp_dp *dp =3D priv->dp; + + mutex_lock(&dp->lock); + *val =3D FIELD_GET(DP_TRAIN_PRE_EMPHASIS_MASK, + dp->test.train_set[priv->lane]); + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_preemphasis_set(void *data, u64 val) +{ + struct zynqmp_dp_train_set_priv *priv =3D data; + struct zynqmp_dp *dp =3D priv->dp; + u8 *train_set =3D &dp->test.train_set[priv->lane]; + int ret =3D 0; + + if (val > 2) + return -EINVAL; + + mutex_lock(&dp->lock); + *train_set &=3D ~(DP_TRAIN_MAX_PRE_EMPHASIS_REACHED | + DP_TRAIN_PRE_EMPHASIS_MASK); + *train_set |=3D val; + if (val =3D=3D 2) + *train_set |=3D DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + if (dp->test.active) + zynqmp_dp_update_vs_emph(dp, dp->test.train_set, true); + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_preemphasis, zynqmp_dp_preemphasis= _get, + zynqmp_dp_preemphasis_set, "%llu\n"); + +static int zynqmp_dp_lanes_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp =3D data; + + mutex_lock(&dp->lock); + *val =3D dp->test.link_cnt; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_lanes_set(void *data, u64 val) +{ + struct zynqmp_dp *dp =3D data; + int ret =3D 0; + + if (val > ZYNQMP_DP_MAX_LANES) + return -EINVAL; + + mutex_lock(&dp->lock); + if (val > dp->num_lanes) { + ret =3D -EINVAL; + } else { + dp->test.link_cnt =3D val; + if (dp->test.active) + ret =3D zynqmp_dp_test_setup(dp); + } + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_lanes, zynqmp_dp_lanes_get, + zynqmp_dp_lanes_set, "%llu\n"); + +static int zynqmp_dp_rate_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp =3D data; + + mutex_lock(&dp->lock); + *val =3D drm_dp_bw_code_to_link_rate(dp->test.bw_code) * 10000; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_rate_set(void *data, u64 val) +{ + struct zynqmp_dp *dp =3D data; + u8 bw_code =3D drm_dp_link_rate_to_bw_code(val / 10000); + int link_rate =3D drm_dp_bw_code_to_link_rate(bw_code); + int ret =3D 0; + + if (val / 10000 !=3D link_rate) + return -EINVAL; + + if (bw_code !=3D DP_LINK_BW_1_62 && bw_code !=3D DP_LINK_BW_2_7 && + bw_code !=3D DP_LINK_BW_5_4) + return -EINVAL; + + mutex_lock(&dp->lock); + dp->test.bw_code =3D bw_code; + if (dp->test.active) + ret =3D zynqmp_dp_test_setup(dp); + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_rate, zynqmp_dp_rate_get, + zynqmp_dp_rate_set, "%llu\n"); + +static void zynqmp_dp_bridge_debugfs_init(struct drm_bridge *bridge, + struct dentry *root) +{ + struct zynqmp_dp *dp =3D bridge_to_dp(bridge); + struct dentry *test; + int i; + + dp->test.bw_code =3D DP_LINK_BW_5_4; + dp->test.link_cnt =3D dp->num_lanes; + + test =3D debugfs_create_dir("test", root); +#define CREATE_FILE(name) \ + debugfs_create_file(#name, 0600, test, dp, &fops_zynqmp_dp_##name) + CREATE_FILE(pattern); + CREATE_FILE(enhanced); + CREATE_FILE(downspread); + CREATE_FILE(active); + CREATE_FILE(custom); + CREATE_FILE(rate); + CREATE_FILE(lanes); + + for (i =3D 0; i < dp->num_lanes; i++) { + static const char fmt[] =3D "lane%d_preemphasis"; + char name[sizeof(fmt)]; + + dp->debugfs_train_set[i].dp =3D dp; + dp->debugfs_train_set[i].lane =3D i; + + sprintf(name, fmt, i); + debugfs_create_file(name, 0600, test, + &dp->debugfs_train_set[i], + &fops_zynqmp_dp_preemphasis); + + sprintf(name, "lane%d_swing", i); + debugfs_create_file(name, 0600, test, + &dp->debugfs_train_set[i], + &fops_zynqmp_dp_swing); + } +} + static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs =3D { .attach =3D zynqmp_dp_bridge_attach, .detach =3D zynqmp_dp_bridge_detach, @@ -1611,6 +2189,7 @@ static const struct drm_bridge_funcs zynqmp_dp_bridge= _funcs =3D { .atomic_check =3D zynqmp_dp_bridge_atomic_check, .detect =3D zynqmp_dp_bridge_detect, .get_edid =3D zynqmp_dp_bridge_get_edid, + .debugfs_init =3D zynqmp_dp_bridge_debugfs_init, }; =20 /* -----------------------------------------------------------------------= ------ @@ -1645,6 +2224,9 @@ static void zynqmp_dp_hpd_work_func(struct work_struc= t *work) hpd_work.work); enum drm_connector_status status; =20 + if (dp->test.active) + return; + status =3D zynqmp_dp_bridge_detect(&dp->bridge); drm_bridge_hpd_notify(&dp->bridge, status); } @@ -1666,7 +2248,14 @@ static void zynqmp_dp_hpd_irq_work_func(struct work_= struct *work) 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 (dp->test.active) { + dev_dbg(dp->dev, "Ignoring HPD IRQ in test mode\n"); + } else { + dev_dbg(dp->dev, + "Retraining due to HPD IRQ (status is [%*ph])\n", + (int)sizeof(status), status); + zynqmp_dp_train_loop(dp); + } } } mutex_unlock(&dp->lock); --=20 2.35.1.1320.gc452695387.dirty