* [PATCH v2 1/2] drm/bridge: it6505: Add audio support
2026-07-20 15:42 [PATCH v2 0/2] drm/bridge: it6505: DP audio support + shared-DAI hw_params fix Daniel Golle
@ 2026-07-20 15:43 ` Daniel Golle
2026-07-20 15:44 ` [PATCH v2 2/2] drm/bridge: it6505: Don't reject audio hw_params without an encoder Daniel Golle
1 sibling, 0 replies; 3+ messages in thread
From: Daniel Golle @ 2026-07-20 15:43 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Matthias Brugger, AngeloGioacchino Del Regno, Hermes Wu,
Allen Chen, dri-devel, linux-kernel, linux-arm-kernel,
linux-mediatek
From: Jiaxin Yu <jiaxin.yu@mediatek.com>
Add audio support for it6505 by bridging to the hdmi-codec, registering
an "hdmi-audio-codec" platform device from probe. The it6505's audio
setup/shutdown helpers were merged earlier as unused code; wire them up
via hdmi_codec_ops so the DAI actually appears, which unblocks the
mt8186-mt6366 sound card that references it6505 as the I2S3 codec.
Some DP-to-HDMI dongles get into a bad state if InfoFrame is sent
without audio data, so it6505's audio is only enabled once the stream
is unmuted.
Signed-off-by: Jiaxin Yu <jiaxin.yu@mediatek.com>
Link: https://lore.kernel.org/all/20230730180803.22570-4-jiaxin.yu@mediatek.com/
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
djg: respin of Jiaxin Yu's v3, rebased onto current mainline. Changes
from v3: hdmi_codec_ops lost .trigger, so drive enable/disable from
.mute_stream; drop the now-redundant __maybe_unused; use it6505->dev
instead of &client->dev, addressing AngeloGioacchino Del Regno's v3
review. Also fix issues in v3: initialise delayed_audio before
registering the codec device; keep and unregister the platform_device
and cancel delayed_audio on remove to avoid a leak and a
use-after-free; cancel the delayed work on audio shutdown; and
actually disable audio (not just cancel the pending enable) when the
stream is muted.
Note: it6505_enable_audio()/it6505_disable_audio() can still run
concurrently from the delayed work, the .mute_stream / .audio_shutdown
callbacks and the audio-FIFO-error IRQ; as in v3 these are left
unserialised -- input welcome on whether a lock is warranted.
v2:
* store the hdmi-codec platform_device and unregister it on i2c
remove, fixing a resource leak and use-after-free on unbind
* initialise the delayed audio work before registering the codec
device instead of after
* synchronously cancel the delayed audio work on audio shutdown and
on driver remove (cancel_delayed_work_sync)
* rework the mute path to cancel pending work synchronously and
disable audio immediately when muting, removing a race with the
delayed enable work
drivers/gpu/drm/bridge/ite-it6505.c | 101 +++++++++++++++++++++++-----
1 file changed, 85 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
index 8ecb43611dba..d84441926e8e 100644
--- a/drivers/gpu/drm/bridge/ite-it6505.c
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -476,6 +476,7 @@ struct it6505 {
bool enable_enhanced_frame;
hdmi_codec_plugged_cb plugged_cb;
struct device *codec_dev;
+ struct platform_device *audio_pdev;
struct delayed_work delayed_audio;
struct it6505_audio_data audio;
struct dentry *debugfs;
@@ -2320,19 +2321,12 @@ static void it6505_stop_link_train(struct it6505 *it6505)
static void it6505_link_train_ok(struct it6505 *it6505)
{
- struct device *dev = it6505->dev;
-
it6505->link_state = LINK_OK;
/* disalbe mute enable avi info frame */
it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, 0x00);
it6505_set_bits(it6505, REG_INFOFRAME_CTRL,
EN_VID_CTRL_PKT, EN_VID_CTRL_PKT);
- if (it6505_audio_input(it6505)) {
- DRM_DEV_DEBUG_DRIVER(dev, "Enable audio!");
- it6505_enable_audio(it6505);
- }
-
if (it6505->hdcp_desired)
it6505_start_hdcp(it6505);
}
@@ -2960,7 +2954,7 @@ static void it6505_remove_notifier_module(struct it6505 *it6505)
}
}
-static void __maybe_unused it6505_delayed_audio(struct work_struct *work)
+static void it6505_delayed_audio(struct work_struct *work)
{
struct it6505 *it6505 = container_of(work, struct it6505,
delayed_audio.work);
@@ -2974,9 +2968,9 @@ static void __maybe_unused it6505_delayed_audio(struct work_struct *work)
it6505_enable_audio(it6505);
}
-static int __maybe_unused it6505_audio_setup_hw_params(struct it6505 *it6505,
- struct hdmi_codec_params
- *params)
+static int it6505_audio_setup_hw_params(struct it6505 *it6505,
+ struct hdmi_codec_params
+ *params)
{
struct device *dev = it6505->dev;
int i = 0;
@@ -3031,18 +3025,52 @@ static int __maybe_unused it6505_audio_setup_hw_params(struct it6505 *it6505,
return 0;
}
-static void __maybe_unused it6505_audio_shutdown(struct device *dev, void *data)
+static void it6505_audio_shutdown(struct device *dev, void *data)
{
struct it6505 *it6505 = dev_get_drvdata(dev);
+ cancel_delayed_work_sync(&it6505->delayed_audio);
if (it6505->powered)
it6505_disable_audio(it6505);
}
-static int __maybe_unused it6505_audio_hook_plugged_cb(struct device *dev,
- void *data,
- hdmi_codec_plugged_cb fn,
- struct device *codec_dev)
+static int it6505_audio_hw_params(struct device *dev, void *data,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
+{
+ struct it6505 *it6505 = dev_get_drvdata(dev);
+
+ return it6505_audio_setup_hw_params(it6505, params);
+}
+
+static int it6505_audio_mute(struct device *dev, void *data,
+ bool enable, int direction)
+{
+ struct it6505 *it6505 = dev_get_drvdata(dev);
+
+ DRM_DEV_DEBUG_DRIVER(dev, "mute: %d", enable);
+
+ /*
+ * Some DP-to-HDMI dongles get into a bad state if the InfoFrame is
+ * sent without audio data, so only enable it6505's audio once the
+ * stream is unmuted (i.e. actually playing).
+ */
+ if (enable) {
+ cancel_delayed_work_sync(&it6505->delayed_audio);
+ if (it6505->powered)
+ it6505_disable_audio(it6505);
+ } else {
+ queue_delayed_work(system_wq, &it6505->delayed_audio,
+ msecs_to_jiffies(180));
+ }
+
+ return 0;
+}
+
+static int it6505_audio_hook_plugged_cb(struct device *dev,
+ void *data,
+ hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
{
struct it6505 *it6505 = data;
@@ -3053,6 +3081,39 @@ static int __maybe_unused it6505_audio_hook_plugged_cb(struct device *dev,
return 0;
}
+static const struct hdmi_codec_ops it6505_audio_codec_ops = {
+ .hw_params = it6505_audio_hw_params,
+ .mute_stream = it6505_audio_mute,
+ .audio_shutdown = it6505_audio_shutdown,
+ .hook_plugged_cb = it6505_audio_hook_plugged_cb,
+};
+
+static int it6505_register_audio_driver(struct device *dev)
+{
+ struct it6505 *it6505 = dev_get_drvdata(dev);
+ struct hdmi_codec_pdata codec_data = {
+ .ops = &it6505_audio_codec_ops,
+ .max_i2s_channels = 8,
+ .i2s = 1,
+ .no_capture_mute = 1,
+ .data = it6505,
+ };
+ struct platform_device *pdev;
+
+ INIT_DELAYED_WORK(&it6505->delayed_audio, it6505_delayed_audio);
+
+ pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
+ PLATFORM_DEVID_AUTO, &codec_data,
+ sizeof(codec_data));
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ it6505->audio_pdev = pdev;
+ DRM_DEV_DEBUG_DRIVER(dev, "bound to %s", HDMI_CODEC_DRV_NAME);
+
+ return 0;
+}
+
static inline struct it6505 *bridge_to_it6505(struct drm_bridge *bridge)
{
return container_of(bridge, struct it6505, bridge);
@@ -3612,6 +3673,12 @@ static int it6505_i2c_probe(struct i2c_client *client)
return err;
}
+ err = it6505_register_audio_driver(dev);
+ if (err < 0) {
+ dev_err(dev, "Failed to register audio driver: %d", err);
+ return err;
+ }
+
INIT_WORK(&it6505->link_works, it6505_link_training_work);
INIT_WORK(&it6505->hdcp_wait_ksv_list, it6505_hdcp_wait_ksv_list);
INIT_DELAYED_WORK(&it6505->hdcp_work, it6505_hdcp_work);
@@ -3644,6 +3711,8 @@ static void it6505_i2c_remove(struct i2c_client *client)
{
struct it6505 *it6505 = i2c_get_clientdata(client);
+ platform_device_unregister(it6505->audio_pdev);
+ cancel_delayed_work_sync(&it6505->delayed_audio);
drm_bridge_remove(&it6505->bridge);
drm_dp_aux_unregister(&it6505->aux);
it6505_debugfs_remove(it6505);
--
2.55.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH v2 2/2] drm/bridge: it6505: Don't reject audio hw_params without an encoder
2026-07-20 15:42 [PATCH v2 0/2] drm/bridge: it6505: DP audio support + shared-DAI hw_params fix Daniel Golle
2026-07-20 15:43 ` [PATCH v2 1/2] drm/bridge: it6505: Add audio support Daniel Golle
@ 2026-07-20 15:44 ` Daniel Golle
1 sibling, 0 replies; 3+ messages in thread
From: Daniel Golle @ 2026-07-20 15:44 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Matthias Brugger, AngeloGioacchino Del Regno, Hermes Wu,
Allen Chen, dri-devel, linux-kernel, linux-arm-kernel,
linux-mediatek
it6505_audio_setup_hw_params() returns -ENODEV when the bridge is not
attached to a DRM encoder. Now that it6505 registers an hdmi-audio-codec,
this callback runs whenever a stream is configured on the I2S DAI it is
wired to, including when that DAI is shared with another codec. On
mt8186-corsola the speaker amplifier (rt1019) and it6505 share I2S3, so
when the it6505 DP output has no display attached (bridge.encoder is
NULL) the -ENODEV propagates up through dpcm_be_dai_hw_params() and tears
down the whole backend, breaking speaker playback.
The check is harmful in its own right: the rest of the function only
caches the stream parameters (channel count, rate, word length) in
software, none of which needs an encoder. Returning early leaves
it6505->audio.channel_count at 0, which would then index
audio_info_ca[-1] in it6505_enable_audio_infoframe() should a display be
hotplugged while a stream is running, and makes such a hotplug play with
stale parameters.
Drop the encoder check so the parameters are always cached; the actual
audio output is already gated by it6505->powered.
Fixes: b5c84a9edcd4 ("drm/bridge: add it6505 driver")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v2:
* drop the encoder check entirely instead of returning 0 early, so
the stream parameters are always cached even while no display is
attached; rewrite the commit message to explain why the check is
harmful
drivers/gpu/drm/bridge/ite-it6505.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
index d84441926e8e..e20edbbb7c13 100644
--- a/drivers/gpu/drm/bridge/ite-it6505.c
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -2979,9 +2979,6 @@ static int it6505_audio_setup_hw_params(struct it6505 *it6505,
params->sample_rate, params->sample_width,
params->cea.channels);
- if (!it6505->bridge.encoder)
- return -ENODEV;
-
if (params->cea.channels <= 1 || params->cea.channels > 8) {
DRM_DEV_DEBUG_DRIVER(dev, "channel number: %d not support",
it6505->audio.channel_count);
--
2.55.0
^ permalink raw reply related [flat|nested] 3+ messages in thread