From patchwork Sat May 16 18:38:28 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonas Karlman X-Patchwork-Id: 2269 Return-Path: X-Original-To: noreply@patchwork.local Delivered-To: noreply@patchwork.local Received: from sto.lore.kernel.org (sto.lore.kernel.org [172.232.135.74]) by mxe881.netcup.net (Postfix) with ESMTPS id 60A3B1C0085 for ; Sat, 16 May 2026 20:45:20 +0200 (CEST) Authentication-Results: mxe881; dkim=pass header.d=kwiboo.se; spf=pass (sender IP is 172.232.135.74) smtp.mailfrom=linux-sunxi+bounces-23418-noreply=patchwork.local@lists.linux.dev smtp.helo=sto.lore.kernel.org Received-SPF: pass (mxe881: domain of lists.linux.dev designates 172.232.135.74 as permitted sender) client-ip=172.232.135.74; envelope-from=linux-sunxi+bounces-23418-noreply=patchwork.local@lists.linux.dev; helo=sto.lore.kernel.org; Received: from smtp.subspace.kernel.org (conduit.subspace.kernel.org [100.90.174.1]) by sto.lore.kernel.org (Postfix) with ESMTP id A538B300E29A for ; Sat, 16 May 2026 18:43:35 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 82A1D33E35F; Sat, 16 May 2026 18:43:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kwiboo.se header.i=@kwiboo.se header.b="GN8zJOvx" X-Original-To: linux-sunxi@lists.linux.dev Received: from smtp.forwardemail.net (smtp.forwardemail.net [149.28.215.223]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 393B0261B9C for ; Sat, 16 May 2026 18:43:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=149.28.215.223 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778957012; cv=none; b=KJ6kccn3sjQYTJGxkIhSNB8ii2LwUfEET3PxBbtD/VGWQGn2F/iMI2Cj3nh0K5iJgvjvrCA53JbRinYW/kBKgj83wWxQz3+QgtK7X1lqm0r58eQq6oVVOFOwhjlvXXcgqHoMrde9ZQQ28y/7oWlHQf5Mc0t5y13Szpcq1L0pGFE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778957012; c=relaxed/simple; bh=i63JkEbRMHKEUCTZIsli8P0LuwF/+zbjrtt4s5fzAkE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pfb3noJmxrjTWUuetVYDsG7hRKePeEtiAodB/i8jViXVI4I+aqnTXxX9m8A8uitw/SMOedUtm+myQStr7fzzFNKajHZ0bhiBXFDMQetgtlwWD/iuMqGMzgkbPb09jzRslOfoPvx/KHXuVxcOxZe2F2+u5bcOW4gtFK8bVCNReT4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=kwiboo.se; spf=pass smtp.mailfrom=fe-bounces.kwiboo.se; dkim=pass (2048-bit key) header.d=kwiboo.se header.i=@kwiboo.se header.b=GN8zJOvx; arc=none smtp.client-ip=149.28.215.223 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=kwiboo.se Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=fe-bounces.kwiboo.se DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kwiboo.se; h=Content-Transfer-Encoding: MIME-Version: References: In-Reply-To: Message-ID: Date: Subject: Cc: To: From; q=dns/txt; s=fe-e1b5cab7be; t=1778957008; bh=/Cp+BncUKHITD5VnLJnG/fIqdmqcksXBGq8aKvyLg6E=; b=GN8zJOvx0PRLA7bZtwojQB/hv1Uml5Yv8/sYxQ2BLdTTx64KceTayR2b1QIp4JhgnsZLN6xeQ qH3P2U/NqPxCExuGGdnF/5CkufNBHECkFpcCAMVlC25dY6Vg40EGOIdoI/orkgYt/c1T6Kw+rcA zplTDYqvxaS7qX0VqYXnH53pVqCXTw3YSv+fxCFMYsBTTlC2eukhUkpo1OwyRWgIh3vDQbk/iVX cPJib21hAR1HYwEAac7jBYlrRIlq4aZyw/TPyVfjKuA+xJhk5OFk16crb2YHa4d/Zj4NZ0E9FCn wfyi4hjcQ5dqBII9s6BvjvYRHoJZAMC5VeZiXQKkKAqQ== X-Forward-Email-ID: 6a08ba0179c7625f20eeeef9 X-Forward-Email-Sender: rfc822; jonas@kwiboo.se, smtp.forwardemail.net, 149.28.215.223 X-Forward-Email-Version: 2.8.12 X-Forward-Email-Website: https://forwardemail.net X-Complaints-To: abuse@forwardemail.net X-Report-Abuse: abuse@forwardemail.net X-Report-Abuse-To: abuse@forwardemail.net From: Jonas Karlman To: Andrzej Hajda , Neil Armstrong , Robert Foss , Heiko Stuebner , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Luca Ceresoli , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter Cc: Liu Ying , Sandy Huang , Andy Yan , Chen-Yu Tsai , Christian Hewitt , Diederik de Haas , Nicolas Frattaroli , Dmitry Baryshkov , dri-devel@lists.freedesktop.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-amlogic@lists.infradead.org, linux-sunxi@lists.linux.dev, imx@lists.linux.dev, linux-kernel@vger.kernel.org Subject: [PATCH v6 18/22] drm: bridge: dw_hdmi: Use delayed_work to debounce hotplug event Date: Sat, 16 May 2026 18:38:28 +0000 Message-ID: <20260516183838.2024991-19-jonas@kwiboo.se> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260516183838.2024991-1-jonas@kwiboo.se> References: <20260516183838.2024991-1-jonas@kwiboo.se> Precedence: bulk X-Mailing-List: linux-sunxi@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Rspamd-Server: rspamd-worker-8404 X-Spamd-Result: default: False [-0.66 / 15.00]; BAYES_HAM(-5.50)[100.00%]; RBL_SENDERSCORE(2.00)[172.232.135.74:from]; SUSPICIOUS_RECIPS(1.50)[]; MID_CONTAINS_FROM(1.00)[]; R_MISSING_CHARSET(0.50)[]; MAILLIST(-0.15)[generic]; MIME_GOOD(-0.10)[text/plain]; BAD_REP_POLICIES(0.10)[]; HAS_LIST_UNSUB(-0.01)[]; RCPT_COUNT_TWELVE(0.00)[28]; PRECEDENCE_BULK(0.00)[]; TAGGED_RCPT(0.00)[]; FUZZY_BLOCKED(0.00)[rspamd.com]; DBL_BLOCKED_OPENRESOLVER(0.00)[kwiboo.se:email,kwiboo.se:dkim,sto.lore.kernel.org:rdns,sto.lore.kernel.org:helo,hpd_work.work:url]; FROM_HAS_DN(0.00)[]; FREEMAIL_CC(0.00)[nxp.com,rock-chips.com,kernel.org,gmail.com,cknow-tech.com,collabora.com,oss.qualcomm.com,lists.freedesktop.org,lists.infradead.org,lists.linux.dev,vger.kernel.org]; R_DKIM_ALLOW(0.00)[kwiboo.se:s=fe-e1b5cab7be]; FROM_NEQ_ENVFROM(0.00)[jonas@kwiboo.se,linux-sunxi@lists.linux.dev]; DMARC_POLICY_ALLOW(0.00)[kwiboo.se,quarantine]; FREEMAIL_TO(0.00)[intel.com,linaro.org,kernel.org,sntech.de,ideasonboard.com,kwiboo.se,gmail.com,bootlin.com,linux.intel.com,suse.de,ffwll.ch]; DKIM_TRACE(0.00)[kwiboo.se:+]; R_SPF_ALLOW(0.00)[+ip4:172.232.135.74:c]; RCVD_COUNT_THREE(0.00)[4]; FORGED_SENDER_MAILLIST(0.00)[]; FORGED_RECIPIENTS_MAILLIST(0.00)[]; RCVD_TLS_LAST(0.00)[]; ARC_ALLOW(0.00)[subspace.kernel.org:s=arc-20240116:i=1]; MIME_TRACE(0.00)[0:+]; TAGGED_FROM(0.00)[bounces-23418-noreply=patchwork.local]; ASN(0.00)[asn:63949, ipnet:172.232.128.0/19, country:SG]; TO_DN_SOME(0.00)[] X-Rspamd-Queue-Id: 60A3B1C0085 X-MORS-Enabled: yes X-MORS-DOMAIN: patchwork.local X-MORS-HOSTING: hosting172546 X-MORS-USER: hosting172546 X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= HDMI Specification Version 1.4b chapter 8.5 mentions: An HDMI Sink shall not assert high voltage level on its Hot Plug Detect pin when the E-EDID is not available for reading. A Source may use a high voltage level Hot Plug Detect signal to initiate the reading of E-EDID data. An HDMI Sink shall indicate any change to the contents of the E-EDID by driving a low voltage level pulse on the Hot Plug Detect pin. This pulse shall be at least 100 msec. Use a delayed work to debounce reacting on HPD events to improve handling of a HPD low voltage level pulse when a sink changes the EDID. The delayed work is only enabled between enable_hpd()/hpd_enable() and disable_hpd()/hpd_disable() calls from core, i.e. enabled after attach/bind/resume and disabled before detach/unbind/suspend. The 1100 msec hotplug debounce timeout was arbitrarily picked to match other drivers using same const, and testing using a Raspberry Pi Monitor seem to use a 200-300 msec pulse when going from standby to power on state. Signed-off-by: Jonas Karlman --- v6: Change back to disable_delayed_work_sync() in hpd disable ops, Ensure HPD interrupt is masked and IRQ handler is disabled early in dw_hdmi_remove() to prevent any irq re-arming of delayed work, Drop use of suspend helper v5: Change to none-sync disable_delayed_work() in hpd disable ops, Change to cancel_delayed_work_sync() in remove, Add cancel_delayed_work_sync() to new suspend helper v4: Disable/mask delayed_work until enable_hpd()/hpd_enable(), Read connector status directly from HW regs in hpd_work v3: New patch --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 76 +++++++++++++++++++++-- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 6cc7b2a860bd..300dac4a6c35 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -50,6 +50,8 @@ #define HDMI14_MAX_TMDSCLK 340000000 +#define HOTPLUG_DEBOUNCE_MS 1100 + static const u16 csc_coeff_default[3][4] = { { 0x2000, 0x0000, 0x0000, 0x0000 }, { 0x0000, 0x2000, 0x0000, 0x0000 }, @@ -185,6 +187,7 @@ struct dw_hdmi { hdmi_codec_plugged_cb plugged_cb; struct device *codec_dev; enum drm_connector_status last_connector_result; + struct delayed_work hpd_work; }; const struct dw_hdmi_plat_data *dw_hdmi_to_plat_data(struct dw_hdmi *hdmi) @@ -2517,6 +2520,20 @@ static void dw_hdmi_connector_force(struct drm_connector *connector) dw_hdmi_connector_status_update(hdmi, connector, connector->status); } +static void dw_hdmi_connector_enable_hpd(struct drm_connector *connector) +{ + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, connector); + + enable_delayed_work(&hdmi->hpd_work); +} + +static void dw_hdmi_connector_disable_hpd(struct drm_connector *connector) +{ + struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, connector); + + disable_delayed_work_sync(&hdmi->hpd_work); +} + static void dw_hdmi_connector_destroy(struct drm_connector *connector) { struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi, connector); @@ -2538,6 +2555,8 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = { static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = { .get_modes = dw_hdmi_connector_get_modes, .atomic_check = dw_hdmi_connector_atomic_check, + .enable_hpd = dw_hdmi_connector_enable_hpd, + .disable_hpd = dw_hdmi_connector_disable_hpd, }; static int dw_hdmi_connector_create(struct dw_hdmi *hdmi) @@ -2968,6 +2987,20 @@ static const struct drm_edid *dw_hdmi_bridge_edid_read(struct drm_bridge *bridge return dw_hdmi_edid_read(hdmi, connector); } +static void dw_hdmi_bridge_hpd_enable(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + enable_delayed_work(&hdmi->hpd_work); +} + +static void dw_hdmi_bridge_hpd_disable(struct drm_bridge *bridge) +{ + struct dw_hdmi *hdmi = bridge->driver_private; + + disable_delayed_work_sync(&hdmi->hpd_work); +} + static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, @@ -2981,6 +3014,8 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = { .mode_valid = dw_hdmi_bridge_mode_valid, .detect = dw_hdmi_bridge_detect, .edid_read = dw_hdmi_bridge_edid_read, + .hpd_enable = dw_hdmi_bridge_hpd_enable, + .hpd_disable = dw_hdmi_bridge_hpd_disable, }; /* ----------------------------------------------------------------------------- @@ -3101,8 +3136,8 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) status == connector_status_connected ? "plugin" : "plugout"); - if (hdmi->bridge.dev) - drm_helper_hpd_irq_event(hdmi->bridge.dev); + mod_delayed_work(system_percpu_wq, &hdmi->hpd_work, + msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); } hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); @@ -3112,6 +3147,29 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static void dw_hdmi_hpd_work(struct work_struct *work) +{ + struct dw_hdmi *hdmi = container_of(work, struct dw_hdmi, hpd_work.work); + struct drm_device *dev = hdmi->bridge.dev; + + if (WARN_ON(!dev)) + return; + + /* + * Notify the DRM core of the HPD event using drm_helper_hpd_irq_event() + * instead of drm_bridge_hpd_notify(). This will cause the DRM function + * check_connector_changed() to be called, which in turn calls the + * connector detect()/force() funcs to detect any connection status or + * epoch changes. The bridge connector detect() func also ensures that + * any hpd_notify() funcs are called for all bridges in the chain. + * + * drm_bridge_hpd_notify() shares a mutex with drm_bridge_hpd_disable(), + * and can result in a deadlock due to the disable_delayed_work_sync() + * call to wait on work to complete in dw_hdmi_bridge_hpd_disable(). + */ + drm_helper_hpd_irq_event(dev); +} + static const struct dw_hdmi_phy_data dw_hdmi_phys[] = { { .type = DW_HDMI_PHY_DWC_HDMI_TX_PHY, @@ -3396,6 +3454,9 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, goto err_res; } + INIT_DELAYED_WORK(&hdmi->hpd_work, dw_hdmi_hpd_work); + disable_delayed_work(&hdmi->hpd_work); + ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq, dw_hdmi_irq, IRQF_SHARED, dev_name(dev), hdmi); @@ -3531,6 +3592,14 @@ EXPORT_SYMBOL_GPL(dw_hdmi_probe); void dw_hdmi_remove(struct dw_hdmi *hdmi) { + struct platform_device *pdev = to_platform_device(hdmi->dev); + int irq = platform_get_irq(pdev, 0); + + /* Disable cable hot plug irq. */ + hdmi_writeb(hdmi, ~0, HDMI_PHY_MASK0); + devm_free_irq(hdmi->dev, irq, hdmi); + cancel_delayed_work_sync(&hdmi->hpd_work); + drm_bridge_remove(&hdmi->bridge); if (hdmi->audio && !IS_ERR(hdmi->audio)) @@ -3538,9 +3607,6 @@ void dw_hdmi_remove(struct dw_hdmi *hdmi) if (!IS_ERR(hdmi->cec)) platform_device_unregister(hdmi->cec); - /* Disable all interrupts */ - hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); - if (hdmi->i2c) i2c_del_adapter(&hdmi->i2c->adap); else