From nobody Mon Sep 28 16:22:27 2026 Received: from mail-wr1-f44.google.com (mail-wr1-f44.google.com [209.85.221.44]) (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 797D3480DF5 for ; Wed, 19 Aug 2026 17:20:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787160017; cv=none; b=BiOaFnYuLYbkliy6QErKqTpwQaElhRq6JhE2QeUZGDscZ0u2epxlD+LovHPPH42Tp2qU/sSPyA1h7rnif+pCP+fJVjbA1E0tEVK0fWjeeUyny7aGxcRExUPXf/MijzOTP3OaewshIRgKRH/meDfdb9HLtsR5ExtySKlOd5W1y3I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787160017; c=relaxed/simple; bh=KiS7s2YWKfO0/RJfTe+FQs+3VjzN90YoB9vay3+yqCo=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=evypjS6t5TlhH1THvdTsvj95wIloTmojPoLwTr2mloRmOMOMZxrpFvRoPxPjYySCD2Cc9ZgaWr6ZbjIv0UXrzxjYZKrP8vp3KxAdAFeM+Ay8Bh81LROlaNTMLSh5XS/eGFTRTrPKxxSAHl9zBnBY6ZCELRWYJauTa208aMdHlzw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=f2ctcBRj; arc=none smtp.client-ip=209.85.221.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="f2ctcBRj" Received: by mail-wr1-f44.google.com with SMTP id ffacd0b85a97d-47fe377a217so770400f8f.1 for ; Wed, 19 Aug 2026 10:20:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787160009; x=1787764809; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=8aiWTi8iCRitLJdmIbO9vTChppeuaJGSr+c2w6PERcA=; b=f2ctcBRjzXWh7GJ5JzZqTU8O+hlHta2XbKV71VYthCJhyhNvojLSV28MmEX9zaVzRe K3ReCFUBzq6ScyL5HYS4gxt+F+EzjbgpvIxqpXQz1CN3vgOO7yC3EmY9vHAgD5rNCDLw yIyrf2clK7E1OtYBnXrotq3brpvrxyTwr96Ju0M2B3iCmLfgSimlf605lyEI4cR/khGt z+iU9wi/BvhBCzZsOfP8EZ1oQ4nwCcp7Z7PRlYyvcd7qKFuaUBdbDyIjwO4QAixHqMqS P8k1oOmGr13e7rrnuZ+Ig6T2rxgXULBq2qO9wXXWLBODzIh5nQA9I2RQ1ToKpisBYy+P /C8w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787160009; x=1787764809; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=8aiWTi8iCRitLJdmIbO9vTChppeuaJGSr+c2w6PERcA=; b=DUfzhrvU7kFOIYHXDM8xpG0FbD0fumVR1+DTojG4JGro385a6Ai5ps09UPN5RickCl J3O8gFu+TpF7NdtUHVarlTMH68O+wKuVYIC/XRUOvu2kVO95incouO3MeCqboYW2KD3q F68cszQxEHolhvqINcNZd1W4epiSBmvKrpqiHJqnd8lle5xMqRZGTtVymchjQnoUrGVh osrN3ztNDUh9YLC5jzxIzzt7WHWArR7hCLiAChgfYdzcQ2yV9f0nrYuYVr17y+oKI3Ho AJapBskWWD9EFDp1EEYO4+EsNfuAI7X7zK4j98w+Xu0HlBqItZzrbSDwsHJLskCC+VoU uiYw== X-Forwarded-Encrypted: i=1; AHgh+RqRMvvPMJt2JLAwTTrPOkIhMP0PSD/jHkkd/5YenZOarxw43gqAB3oiCyXG0wBtSc7vaTyrA9/XNcxQpz8=@vger.kernel.org X-Gm-Message-State: AFuF++m6gV0HQlVutOSHL/31suiu5aqzaxZG9JIHuho0qj4/A90yjcUE PlIgyqBUDvt8Jkg9uNt0uqp+5lOCGtpULG7EllsCtjkeXaH5GxFQ43Hm X-Gm-Gg: AR+sD11XH3zJGfmVdLTnNzCB9H+3sYLaWiP+3XvB/cQXeZ/wKTDIe8vByIW61maJpOd 0eD/2z+UIGM6z9elPz915pv56ncxZiYkckVqAGYgTMZZCSD90ZawH38tFf5HMiE7Mn1UXah6CQH wN5TgpyC+VDbdvthfWvp+CeUpXaIN3PYIoLKmt0NIBeICSi8672m2VtUSU34CW7BbH/ygdlu6pQ Bv9h9j3TcNBSzBv9xZQ3pb2GNaKsocxzUvPjU0manFAXumzzE7nyGmWxMkKZwfX+uI/LDMdCw2V eDyk7sjQ3enryzGmS1S3C0HzcQq8qyfVIl8Mzhil7r5eS3NLW1WSC1vScGI71QmdM5ijI1B7tb5 7q95L36jtosIg5MkkOMPh2ajsc9F6C2G52YV04z7tGP3ppjG5NBLhVi0YjekZqOWBEgVmlgAovD A6RvtpphHDKQbq9KxQAr3GHQve06N7autajCOd8+x7Ld0WFjVUqlG/2BKwUmVY+CrYi92StbKCf zlzJltHbA== X-Received: by 2002:a05:6000:3c5:b0:481:4fe2:ce55 with SMTP id ffacd0b85a97d-482b1ffd595mr11674656f8f.21.1787160009175; Wed, 19 Aug 2026 10:20:09 -0700 (PDT) Received: from localhost.localdomain ([95.43.220.235]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-482b1450e54sm7638119f8f.11.2026.08.19.10.20.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Aug 2026 10:20:08 -0700 (PDT) From: Ivaylo Dimitrov To: Tomi Valkeinen , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Jaroslav Kysela , Takashi Iwai , Jarkko Nikula , Sen Wang , Liam Girdwood , Mark Brown , Jakub Kicinski , Alex Deucher Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-sound@vger.kernel.org, linux-omap@vger.kernel.org, Ivaylo Dimitrov Subject: [PATCH v3] drm/omap: Report HDMI hotplug events to the ASoC HDMI codec Date: Wed, 19 Aug 2026 20:19:35 +0300 Message-Id: <20260819171935.233946-1-ivo.g.dimitrov.75@gmail.com> X-Mailer: git-send-email 2.25.1 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 Content-Type: text/plain; charset="utf-8" The OMAP HDMI audio driver currently has no way of reporting HDMI cable hotplug events to ASoC. As a result, user space cannot detect HDMI audio availability through the standard jack mechanism and audio policy managers cannot automatically route audio to or from the HDMI output. Add an optional HPD callback to `omap_hdmi_audio_pdata` and invoke it from the DRM HDMI bridge whenever the connector status changes. The HDMI audio driver registers an ASoC jack and reports `SND_JACK_AVOUT` state changes in response to these notifications. Also fix the disconnect path by taking a runtime PM reference before updating the CEC physical address. Signed-off-by: Ivaylo Dimitrov Acked-by: Mark Brown --- drivers/gpu/drm/omapdrm/dss/hdmi.h | 6 +++ drivers/gpu/drm/omapdrm/dss/hdmi4.c | 29 ++++++++++--- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 37 +++++++++++++--- drivers/gpu/drm/omapdrm/dss/hdmi_common.c | 14 ++++++ include/sound/omap-hdmi-audio.h | 1 + sound/soc/ti/omap-hdmi.c | 53 +++++++++++++++++++++-- 6 files changed, 125 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi.h b/drivers/gpu/drm/omapdrm/d= ss/hdmi.h index c4a4e07f0b99..a79d4590611f 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi.h +++ b/drivers/gpu/drm/omapdrm/dss/hdmi.h @@ -15,6 +15,7 @@ #include #include #include +#include =20 #include "omapdss.h" #include "dss.h" @@ -368,6 +369,8 @@ struct omap_hdmi { struct drm_bridge bridge; =20 struct platform_device *audio_pdev; + struct mutex audio_lock; + void (*audio_abort_cb)(struct device *dev); int wp_idlemode; =20 @@ -380,6 +383,9 @@ struct omap_hdmi { bool display_enabled; }; =20 +void hdmi_audio_hpd_notify(struct omap_hdmi *hdmi, + enum drm_connector_status status); + #define drm_bridge_to_hdmi(b) container_of(b, struct omap_hdmi, bridge) =20 #endif diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/= dss/hdmi4.c index e306247ed8a0..e0e077397b77 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -433,8 +433,15 @@ static void hdmi4_bridge_hpd_notify(struct drm_bridge = *bridge, { struct omap_hdmi *hdmi =3D drm_bridge_to_hdmi(bridge); =20 - if (status =3D=3D connector_status_disconnected) + hdmi_audio_hpd_notify(hdmi, status); + + if (status =3D=3D connector_status_disconnected) { + if (hdmi_runtime_get(hdmi)) + return; + hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); + hdmi_runtime_put(hdmi); + } } =20 static const struct drm_edid *hdmi4_bridge_edid_read(struct drm_bridge *br= idge, @@ -626,12 +633,17 @@ static int hdmi_audio_register(struct omap_hdmi *hdmi) .ops =3D &hdmi_audio_ops, }; =20 + guard(mutex)(&hdmi->audio_lock); hdmi->audio_pdev =3D platform_device_register_data( &hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO, &pdata, sizeof(pdata)); =20 - if (IS_ERR(hdmi->audio_pdev)) - return PTR_ERR(hdmi->audio_pdev); + if (IS_ERR(hdmi->audio_pdev)) { + int err =3D PTR_ERR(hdmi->audio_pdev); + + hdmi->audio_pdev =3D NULL; + return err; + } =20 return 0; } @@ -688,8 +700,14 @@ static void hdmi4_unbind(struct device *dev, struct de= vice *master, void *data) =20 dss_debugfs_remove_file(hdmi->debugfs); =20 - if (hdmi->audio_pdev) - platform_device_unregister(hdmi->audio_pdev); + scoped_guard(mutex, &hdmi->audio_lock) { + if (hdmi->audio_pdev) { + struct platform_device *pdev =3D hdmi->audio_pdev; + + hdmi->audio_pdev =3D NULL; + platform_device_unregister(pdev); + } + } =20 hdmi4_cec_uninit(&hdmi->core); hdmi_pll_uninit(&hdmi->pll); @@ -770,6 +788,7 @@ static int hdmi4_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, hdmi); =20 mutex_init(&hdmi->lock); + mutex_init(&hdmi->audio_lock); spin_lock_init(&hdmi->audio_playing_lock); =20 r =3D hdmi4_probe_of(hdmi); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/= dss/hdmi5.c index ab2d4eb6787f..3828eb3be6f9 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -425,6 +425,15 @@ static void hdmi5_bridge_disable(struct drm_bridge *br= idge, mutex_unlock(&hdmi->lock); } =20 +static void hdmi5_bridge_hpd_notify(struct drm_bridge *bridge, + struct drm_connector *connector, + enum drm_connector_status status) +{ + struct omap_hdmi *hdmi =3D drm_bridge_to_hdmi(bridge); + + hdmi_audio_hpd_notify(hdmi, status); +} + static const struct drm_edid *hdmi5_bridge_edid_read(struct drm_bridge *br= idge, struct drm_connector *connector) { @@ -475,6 +484,7 @@ static const struct drm_bridge_funcs hdmi5_bridge_funcs= =3D { .atomic_reset =3D drm_atomic_helper_bridge_reset, .atomic_enable =3D hdmi5_bridge_enable, .atomic_disable =3D hdmi5_bridge_disable, + .hpd_notify =3D hdmi5_bridge_hpd_notify, .edid_read =3D hdmi5_bridge_edid_read, }; =20 @@ -601,12 +611,18 @@ static int hdmi_audio_register(struct omap_hdmi *hdmi) .ops =3D &hdmi_audio_ops, }; =20 - hdmi->audio_pdev =3D platform_device_register_data( - &hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO, - &pdata, sizeof(pdata)); + scoped_guard(mutex, &hdmi->audio_lock) { + hdmi->audio_pdev =3D platform_device_register_data( + &hdmi->pdev->dev, "omap-hdmi-audio", + PLATFORM_DEVID_AUTO, &pdata, sizeof(pdata)); =20 - if (IS_ERR(hdmi->audio_pdev)) - return PTR_ERR(hdmi->audio_pdev); + if (IS_ERR(hdmi->audio_pdev)) { + int err =3D PTR_ERR(hdmi->audio_pdev); + + hdmi->audio_pdev =3D NULL; + return err; + } + } =20 hdmi_runtime_get(hdmi); hdmi->wp_idlemode =3D @@ -654,8 +670,14 @@ static void hdmi5_unbind(struct device *dev, struct de= vice *master, void *data) =20 dss_debugfs_remove_file(hdmi->debugfs); =20 - if (hdmi->audio_pdev) - platform_device_unregister(hdmi->audio_pdev); + scoped_guard(mutex, &hdmi->audio_lock) { + if (hdmi->audio_pdev) { + struct platform_device *pdev =3D hdmi->audio_pdev; + + hdmi->audio_pdev =3D NULL; + platform_device_unregister(pdev); + } + } =20 hdmi_pll_uninit(&hdmi->pll); } @@ -735,6 +757,7 @@ static int hdmi5_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, hdmi); =20 mutex_init(&hdmi->lock); + mutex_init(&hdmi->audio_lock); spin_lock_init(&hdmi->audio_playing_lock); =20 r =3D hdmi5_probe_of(hdmi); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_common.c b/drivers/gpu/drm/om= apdrm/dss/hdmi_common.c index 3ecde23ac604..e9adc5775913 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_common.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_common.c @@ -147,3 +147,17 @@ int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n= , u32 *cts) =20 return 0; } + +void hdmi_audio_hpd_notify(struct omap_hdmi *hdmi, + enum drm_connector_status status) +{ + guard(mutex)(&hdmi->audio_lock); + + if (hdmi->audio_pdev) { + struct device *dev =3D &hdmi->audio_pdev->dev; + struct omap_hdmi_audio_pdata *ha =3D dev_get_platdata(dev); + + if (ha->audio_hpd) + ha->audio_hpd(dev, status =3D=3D connector_status_connected); + } +} diff --git a/include/sound/omap-hdmi-audio.h b/include/sound/omap-hdmi-audi= o.h index e5f82044a404..8dca92360ee0 100644 --- a/include/sound/omap-hdmi-audio.h +++ b/include/sound/omap-hdmi-audio.h @@ -34,6 +34,7 @@ struct omap_hdmi_audio_pdata { phys_addr_t audio_dma_addr; =20 const struct omap_hdmi_audio_ops *ops; + void (*audio_hpd)(struct device *dev, bool connected); }; =20 #endif /* __OMAP_HDMI_AUDIO_H__ */ diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c index e60f5b483fc5..90ce12a81246 100644 --- a/sound/soc/ti/omap-hdmi.c +++ b/sound/soc/ti/omap-hdmi.c @@ -17,6 +17,7 @@ #include #include #include +#include #include =20 #include "sdma-pcm.h" @@ -35,6 +36,8 @@ struct hdmi_audio_data { =20 struct mutex current_stream_lock; struct snd_pcm_substream *current_stream; + struct snd_soc_jack jack; + atomic_t jack_state; }; =20 static @@ -262,6 +265,34 @@ static void hdmi_dai_shutdown(struct snd_pcm_substream= *substream, ad->current_stream =3D NULL; } =20 +static void hdmi_audio_hpd(struct device *dev, bool connected) +{ + struct hdmi_audio_data *ad =3D dev_get_drvdata(dev); + + if (atomic_xchg(&ad->jack_state, connected) =3D=3D connected) + return; + + snd_soc_jack_report(&ad->jack, + connected ? SND_JACK_AVOUT : 0, SND_JACK_AVOUT); + dev_dbg(dev, "HDMI %s\n", connected ? "CONNECTED" : "DISCONNECTED"); +} + +static int hdmi_audio_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card =3D rtd->card; + struct hdmi_audio_data *ad =3D snd_soc_card_get_drvdata(rtd->card); + int ret; + + ret =3D snd_soc_card_jack_new( + card, "HDMI", SND_JACK_AVOUT, &ad->jack); + if (ret < 0) { + dev_err(card->dev, "Cannot create HDMI jack: %i\n", ret); + return ret; + } + + return 0; +} + static const struct snd_soc_dai_ops hdmi_dai_ops =3D { .startup =3D hdmi_dai_startup, .hw_params =3D hdmi_dai_hw_params, @@ -371,28 +402,44 @@ static int omap_hdmi_audio_probe(struct platform_devi= ce *pdev) card->dai_link->stream_name =3D card->name; card->dai_link->cpus->dai_name =3D dev_name(ad->dssdev); card->dai_link->platforms->name =3D dev_name(ad->dssdev); + card->dai_link->init =3D hdmi_audio_dai_init; card->num_links =3D 1; card->dev =3D dev; =20 + atomic_set(&ad->jack_state, -1); + ad->card =3D card; + + snd_soc_card_set_drvdata(card, ad); ret =3D devm_snd_soc_register_card(dev, card); if (ret) { dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); return ret; } =20 - ad->card =3D card; - snd_soc_card_set_drvdata(card, ad); - dev_set_drvdata(dev, ad); =20 + ha->audio_hpd =3D hdmi_audio_hpd; + return 0; } =20 +static void omap_hdmi_audio_remove(struct platform_device *pdev) +{ + struct omap_hdmi_audio_pdata *ha =3D pdev->dev.platform_data; + + /* + * hdmi4_unbind() holds audio lock across platform_device_unregister(), + * so audio_hpd() cannot run concurrently with this callback. + */ + ha->audio_hpd =3D NULL; +} + static struct platform_driver hdmi_audio_driver =3D { .driver =3D { .name =3D DRV_NAME, }, .probe =3D omap_hdmi_audio_probe, + .remove =3D omap_hdmi_audio_remove, }; =20 module_platform_driver(hdmi_audio_driver); --=20 2.25.1