* [PATCH v2] drm/omap: Report HDMI hotplug events to the ASoC HDMI codec
@ 2026-08-19 15:27 Ivaylo Dimitrov
2026-08-19 15:39 ` sashiko-bot
2026-08-19 16:02 ` Mark Brown
0 siblings, 2 replies; 4+ messages in thread
From: Ivaylo Dimitrov @ 2026-08-19 15:27 UTC (permalink / raw)
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, linux-kernel, linux-sound, linux-omap, Ivaylo Dimitrov
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 <ivo.g.dimitrov.75@gmail.com>
---
drivers/gpu/drm/omapdrm/dss/hdmi.h | 6 +++
drivers/gpu/drm/omapdrm/dss/hdmi4.c | 29 ++++++++++---
drivers/gpu/drm/omapdrm/dss/hdmi5.c | 36 ++++++++++++---
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, 124 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi.h b/drivers/gpu/drm/omapdrm/dss/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 <sound/omap-hdmi-audio.h>
#include <media/cec.h>
#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
#include "omapdss.h"
#include "dss.h"
@@ -368,6 +369,8 @@ struct omap_hdmi {
struct drm_bridge bridge;
struct platform_device *audio_pdev;
+ struct mutex audio_lock;
+
void (*audio_abort_cb)(struct device *dev);
int wp_idlemode;
@@ -380,6 +383,9 @@ struct omap_hdmi {
bool display_enabled;
};
+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)
#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 = drm_bridge_to_hdmi(bridge);
- if (status == connector_status_disconnected)
+ hdmi_audio_hpd_notify(hdmi, status);
+
+ if (status == connector_status_disconnected) {
+ if (hdmi_runtime_get(hdmi))
+ return;
+
hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID);
+ hdmi_runtime_put(hdmi);
+ }
}
static const struct drm_edid *hdmi4_bridge_edid_read(struct drm_bridge *bridge,
@@ -626,12 +633,17 @@ static int hdmi_audio_register(struct omap_hdmi *hdmi)
.ops = &hdmi_audio_ops,
};
+ guard(mutex)(&hdmi->audio_lock);
hdmi->audio_pdev = platform_device_register_data(
&hdmi->pdev->dev, "omap-hdmi-audio", PLATFORM_DEVID_AUTO,
&pdata, sizeof(pdata));
- if (IS_ERR(hdmi->audio_pdev))
- return PTR_ERR(hdmi->audio_pdev);
+ if (IS_ERR(hdmi->audio_pdev)) {
+ int err = PTR_ERR(hdmi->audio_pdev);
+
+ hdmi->audio_pdev = NULL;
+ return err;
+ }
return 0;
}
@@ -688,8 +700,14 @@ static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
dss_debugfs_remove_file(hdmi->debugfs);
- if (hdmi->audio_pdev)
- platform_device_unregister(hdmi->audio_pdev);
+ scoped_guard(mutex, &hdmi->audio_lock) {
+ if (hdmi->audio_pdev) {
+ struct platform_device *pdev = hdmi->audio_pdev;
+
+ hdmi->audio_pdev = NULL;
+ platform_device_unregister(pdev);
+ }
+ }
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);
mutex_init(&hdmi->lock);
+ mutex_init(&hdmi->audio_lock);
spin_lock_init(&hdmi->audio_playing_lock);
r = hdmi4_probe_of(hdmi);
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
index ab2d4eb6787f..76aedc1c9ba1 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
@@ -425,6 +425,14 @@ static void hdmi5_bridge_disable(struct drm_bridge *bridge,
mutex_unlock(&hdmi->lock);
}
+static void hdmi5_bridge_hpd_notify(struct drm_bridge *bridge,
+ enum drm_connector_status status)
+{
+ struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
+
+ hdmi_audio_hpd_notify(hdmi, status);
+}
+
static const struct drm_edid *hdmi5_bridge_edid_read(struct drm_bridge *bridge,
struct drm_connector *connector)
{
@@ -475,6 +483,7 @@ static const struct drm_bridge_funcs hdmi5_bridge_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_enable = hdmi5_bridge_enable,
.atomic_disable = hdmi5_bridge_disable,
+ .hpd_notify = hdmi5_bridge_hpd_notify,
.edid_read = hdmi5_bridge_edid_read,
};
@@ -601,12 +610,18 @@ static int hdmi_audio_register(struct omap_hdmi *hdmi)
.ops = &hdmi_audio_ops,
};
- hdmi->audio_pdev = 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 = platform_device_register_data(
+ &hdmi->pdev->dev, "omap-hdmi-audio",
+ PLATFORM_DEVID_AUTO, &pdata, sizeof(pdata));
- if (IS_ERR(hdmi->audio_pdev))
- return PTR_ERR(hdmi->audio_pdev);
+ if (IS_ERR(hdmi->audio_pdev)) {
+ int err = PTR_ERR(hdmi->audio_pdev);
+
+ hdmi->audio_pdev = NULL;
+ return err;
+ }
+ }
hdmi_runtime_get(hdmi);
hdmi->wp_idlemode =
@@ -654,8 +669,14 @@ static void hdmi5_unbind(struct device *dev, struct device *master, void *data)
dss_debugfs_remove_file(hdmi->debugfs);
- if (hdmi->audio_pdev)
- platform_device_unregister(hdmi->audio_pdev);
+ scoped_guard(mutex, &hdmi->audio_lock) {
+ if (hdmi->audio_pdev) {
+ struct platform_device *pdev = hdmi->audio_pdev;
+
+ hdmi->audio_pdev = NULL;
+ platform_device_unregister(pdev);
+ }
+ }
hdmi_pll_uninit(&hdmi->pll);
}
@@ -735,6 +756,7 @@ static int hdmi5_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, hdmi);
mutex_init(&hdmi->lock);
+ mutex_init(&hdmi->audio_lock);
spin_lock_init(&hdmi->audio_playing_lock);
r = hdmi5_probe_of(hdmi);
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_common.c b/drivers/gpu/drm/omapdrm/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)
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 = &hdmi->audio_pdev->dev;
+ struct omap_hdmi_audio_pdata *ha = dev_get_platdata(dev);
+
+ if (ha->audio_hpd)
+ ha->audio_hpd(dev, status == connector_status_connected);
+ }
+}
diff --git a/include/sound/omap-hdmi-audio.h b/include/sound/omap-hdmi-audio.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;
const struct omap_hdmi_audio_ops *ops;
+ void (*audio_hpd)(struct device *dev, bool connected);
};
#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 <sound/dmaengine_pcm.h>
#include <uapi/sound/asound.h>
#include <sound/asoundef.h>
+#include <sound/jack.h>
#include <sound/omap-hdmi-audio.h>
#include "sdma-pcm.h"
@@ -35,6 +36,8 @@ struct hdmi_audio_data {
struct mutex current_stream_lock;
struct snd_pcm_substream *current_stream;
+ struct snd_soc_jack jack;
+ atomic_t jack_state;
};
static
@@ -262,6 +265,34 @@ static void hdmi_dai_shutdown(struct snd_pcm_substream *substream,
ad->current_stream = NULL;
}
+static void hdmi_audio_hpd(struct device *dev, bool connected)
+{
+ struct hdmi_audio_data *ad = dev_get_drvdata(dev);
+
+ if (atomic_xchg(&ad->jack_state, connected) == 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 = rtd->card;
+ struct hdmi_audio_data *ad = snd_soc_card_get_drvdata(rtd->card);
+ int ret;
+
+ ret = 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 = {
.startup = hdmi_dai_startup,
.hw_params = hdmi_dai_hw_params,
@@ -371,28 +402,44 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
card->dai_link->stream_name = card->name;
card->dai_link->cpus->dai_name = dev_name(ad->dssdev);
card->dai_link->platforms->name = dev_name(ad->dssdev);
+ card->dai_link->init = hdmi_audio_dai_init;
card->num_links = 1;
card->dev = dev;
+ atomic_set(&ad->jack_state, -1);
+ ad->card = card;
+
+ snd_soc_card_set_drvdata(card, ad);
ret = devm_snd_soc_register_card(dev, card);
if (ret) {
dev_err(dev, "snd_soc_register_card failed (%d)\n", ret);
return ret;
}
- ad->card = card;
- snd_soc_card_set_drvdata(card, ad);
-
dev_set_drvdata(dev, ad);
+ ha->audio_hpd = hdmi_audio_hpd;
+
return 0;
}
+static void omap_hdmi_audio_remove(struct platform_device *pdev)
+{
+ struct omap_hdmi_audio_pdata *ha = 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 = NULL;
+}
+
static struct platform_driver hdmi_audio_driver = {
.driver = {
.name = DRV_NAME,
},
.probe = omap_hdmi_audio_probe,
+ .remove = omap_hdmi_audio_remove,
};
module_platform_driver(hdmi_audio_driver);
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2] drm/omap: Report HDMI hotplug events to the ASoC HDMI codec
2026-08-19 15:27 [PATCH v2] drm/omap: Report HDMI hotplug events to the ASoC HDMI codec Ivaylo Dimitrov
@ 2026-08-19 15:39 ` sashiko-bot
2026-08-19 16:02 ` Mark Brown
1 sibling, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-08-19 15:39 UTC (permalink / raw)
To: Ivaylo Dimitrov; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Use-After-Free during HDMI hotplug events due to vulnerable sysfs unbind path.
- [High] Function signature mismatch in `hdmi5_bridge_hpd_notify` causes a CFI kernel panic and breaks state reporting.
- [Medium] Missing initial jack state synchronization causes HDMI audio to remain disconnected at boot.
--
--- Patch [1]: [PATCH v2] drm/omap: Report HDMI hotplug events to the ASoC HDMI codec ---
commit 79155722ff98d49bdbf6c4e880ed9ec3b85d6fd0
Author: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
drm/omap: Report HDMI hotplug events to the ASoC HDMI codec
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.
> diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
> index ab2d4eb6787f2..76aedc1c9ba1f 100644
> --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c
> +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c
> @@ -425,6 +425,14 @@ static void hdmi5_bridge_disable(struct drm_bridge *bridge,
> mutex_unlock(&hdmi->lock);
> }
>
> +static void hdmi5_bridge_hpd_notify(struct drm_bridge *bridge,
> + enum drm_connector_status status)
> +{
[Severity: High]
Will this cause a Control-Flow Integrity (CFI) kernel panic?
The DRM core expects the hpd_notify callback to have three arguments:
void (*hpd_notify)(struct drm_bridge *bridge, struct drm_connector *connector,
enum drm_connector_status status)
hdmi5_bridge_hpd_notify() is defined with only two arguments. With CFI
enabled, calling this with a mismatched signature triggers a panic. Even
without CFI, the calling convention causes the callee to read the
connector pointer as the status enum.
> + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
> +
> + hdmi_audio_hpd_notify(hdmi, status);
> +}
> +
[ ... ]
> diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c
> index e60f5b483fc57..90ce12a812468 100644
> --- a/sound/soc/ti/omap-hdmi.c
> +++ b/sound/soc/ti/omap-hdmi.c
> @@ -262,6 +265,34 @@ static void hdmi_dai_shutdown(struct snd_pcm_substream *substream,
[ ... ]
> +static int hdmi_audio_dai_init(struct snd_soc_pcm_runtime *rtd)
> +{
> + struct snd_soc_card *card = rtd->card;
> + struct hdmi_audio_data *ad = snd_soc_card_get_drvdata(rtd->card);
> + int ret;
> +
> + ret = snd_soc_card_jack_new(
> + card, "HDMI", SND_JACK_AVOUT, &ad->jack);
[Severity: Medium]
Is there a way to query the initial connection state of the jack here?
If the system boots with the HDMI cable already connected, the ASoC jack
is initialized to a disconnected state (0) here. Because the DRM hardware
interrupt only fires on state edges, hdmi_audio_hpd_notify() is never
called at boot, leaving the jack reporting as disconnected until the user
unplugs and replugs the cable.
> + if (ret < 0) {
> + dev_err(card->dev, "Cannot create HDMI jack: %i\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
[ ... ]
> @@ -371,28 +402,44 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
[ ... ]
> +static void omap_hdmi_audio_remove(struct platform_device *pdev)
> +{
> + struct omap_hdmi_audio_pdata *ha = 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 = NULL;
> +}
> +
> static struct platform_driver hdmi_audio_driver = {
> .driver = {
> .name = DRV_NAME,
> },
> .probe = omap_hdmi_audio_probe,
> + .remove = omap_hdmi_audio_remove,
[Severity: High]
Could a user space sysfs unbind trigger a use-after-free here?
If a user unbinds the omap-hdmi-audio driver via sysfs,
omap_hdmi_audio_remove() locklessly clears ha->audio_hpd.
Concurrently, if a physical hotplug event occurs:
CPU1
hdmi_audio_hpd_notify()
if (ha->audio_hpd)
...gets preempted here...
CPU2
omap_hdmi_audio_remove()
ha->audio_hpd = NULL;
...driver unbinds and frees 'ad' via devres...
CPU1
...resumes...
ha->audio_hpd(dev, ...) -> calls hdmi_audio_hpd()
dereferences freed 'ad'.
Should this driver struct set .suppress_bind_attrs = true to prevent
uncoordinated sysfs lifecycle events?
> };
>
> module_platform_driver(hdmi_audio_driver);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260819152755.231754-1-ivo.g.dimitrov.75@gmail.com?part=1
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] drm/omap: Report HDMI hotplug events to the ASoC HDMI codec
2026-08-19 15:27 [PATCH v2] drm/omap: Report HDMI hotplug events to the ASoC HDMI codec Ivaylo Dimitrov
2026-08-19 15:39 ` sashiko-bot
@ 2026-08-19 16:02 ` Mark Brown
2026-08-19 17:15 ` Ivaylo Dimitrov
1 sibling, 1 reply; 4+ messages in thread
From: Mark Brown @ 2026-08-19 16:02 UTC (permalink / raw)
To: Ivaylo Dimitrov
Cc: Tomi Valkeinen, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jaroslav Kysela,
Takashi Iwai, Jarkko Nikula, Sen Wang, Liam Girdwood,
Jakub Kicinski, Alex Deucher, dri-devel, linux-kernel,
linux-sound, linux-omap
[-- Attachment #1: Type: text/plain, Size: 397 bytes --]
On Wed, Aug 19, 2026 at 06:27:55PM +0300, Ivaylo Dimitrov wrote:
> 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.
Acked-by: Mark Brown <broonie@kernel.org>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2] drm/omap: Report HDMI hotplug events to the ASoC HDMI codec
2026-08-19 16:02 ` Mark Brown
@ 2026-08-19 17:15 ` Ivaylo Dimitrov
0 siblings, 0 replies; 4+ messages in thread
From: Ivaylo Dimitrov @ 2026-08-19 17:15 UTC (permalink / raw)
To: Mark Brown
Cc: Tomi Valkeinen, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jaroslav Kysela,
Takashi Iwai, Jarkko Nikula, Sen Wang, Liam Girdwood,
Jakub Kicinski, Alex Deucher, dri-devel, linux-kernel,
linux-sound, linux-omap
On 19.08.26 г. 19:02 ч., Mark Brown wrote:
> On Wed, Aug 19, 2026 at 06:27:55PM +0300, Ivaylo Dimitrov wrote:
>> 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.
>
> Acked-by: Mark Brown <broonie@kernel.org>
Ugh, somehow managed to send the patch with wrong
hdmi5_bridge_hpd_notify() signature, sashiko noticed that, will send v3.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-19 17:15 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-19 15:27 [PATCH v2] drm/omap: Report HDMI hotplug events to the ASoC HDMI codec Ivaylo Dimitrov
2026-08-19 15:39 ` sashiko-bot
2026-08-19 16:02 ` Mark Brown
2026-08-19 17:15 ` Ivaylo Dimitrov
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.