From 051392bfdc6dc54563ed9909cc1164e8d734af43 Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 28 Feb 2024 10:55:39 +0000 Subject: [PATCH 0930/1085] vc4: Add jack detection to HDMI audio driver Add ALSA jack detection to the vc4-hdmi audio driver so userspace knows when to add/remove HDMI audio devices. Signed-off-by: David Turner --- drivers/gpu/drm/vc4/vc4_hdmi.c | 61 ++++++++++++++++++++++++++++++++-- drivers/gpu/drm/vc4/vc4_hdmi.h | 27 +++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -421,7 +422,7 @@ static void vc4_hdmi_handle_hotplug(stru enum drm_connector_status status) { struct drm_connector *connector = &vc4_hdmi->connector; - struct edid *edid; + struct edid *edid = NULL; int ret; /* @@ -439,12 +440,25 @@ static void vc4_hdmi_handle_hotplug(stru * the lock for now. */ + if (status != connector_status_disconnected) + edid = drm_get_edid(connector, vc4_hdmi->ddc); + + /* + * Report plugged/unplugged events to ALSA jack detection. Do this + * *after* EDID probing, otherwise userspace might try to bring up + * audio before it's ready. + */ + mutex_lock(&vc4_hdmi->update_plugged_status_lock); + if (vc4_hdmi->plugged_cb && vc4_hdmi->codec_dev) + vc4_hdmi->plugged_cb(vc4_hdmi->codec_dev, + status != connector_status_disconnected); + mutex_unlock(&vc4_hdmi->update_plugged_status_lock); + if (status == connector_status_disconnected) { cec_phys_addr_invalidate(vc4_hdmi->cec_adap); return; } - edid = drm_get_edid(connector, vc4_hdmi->ddc); if (!edid) return; @@ -2691,8 +2705,23 @@ static int vc4_hdmi_audio_get_eld(struct return 0; } +static int vc4_hdmi_audio_hook_plugged_cb(struct device *dev, void *data, + hdmi_codec_plugged_cb fn, + struct device *codec_dev) +{ + struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); + + mutex_lock(&vc4_hdmi->update_plugged_status_lock); + vc4_hdmi->plugged_cb = fn; + vc4_hdmi->codec_dev = codec_dev; + mutex_unlock(&vc4_hdmi->update_plugged_status_lock); + + return 0; +} + static const struct hdmi_codec_ops vc4_hdmi_codec_ops = { .get_eld = vc4_hdmi_audio_get_eld, + .hook_plugged_cb = vc4_hdmi_audio_hook_plugged_cb, .prepare = vc4_hdmi_audio_prepare, .audio_shutdown = vc4_hdmi_audio_shutdown, .audio_startup = vc4_hdmi_audio_startup, @@ -2712,6 +2741,22 @@ static void vc4_hdmi_audio_codec_release vc4_hdmi->audio.codec_pdev = NULL; } +static int vc4_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct vc4_hdmi *vc4_hdmi = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; + int ret; + + ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT, + &vc4_hdmi->hdmi_jack); + if (ret) { + dev_err(rtd->dev, "HDMI Jack creation failed: %d\n", ret); + return ret; + } + + return snd_soc_component_set_jack(component, &vc4_hdmi->hdmi_jack, NULL); +} + static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) { const struct vc4_hdmi_register *mai_data = @@ -2841,6 +2886,8 @@ static int vc4_hdmi_audio_init(struct vc dai_link->codecs->name = dev_name(&codec_pdev->dev); dai_link->platforms->name = dev_name(dev); + dai_link->init = vc4_hdmi_codec_init; + card->dai_link = dai_link; card->num_links = 1; card->name = vc4_hdmi->variant->card_name; @@ -3701,6 +3748,8 @@ static int vc4_hdmi_bind(struct device * if (ret) return ret; + mutex_init(&vc4_hdmi->update_plugged_status_lock); + spin_lock_init(&vc4_hdmi->hw_lock); INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq); @@ -3813,8 +3862,16 @@ err_put_runtime_pm: return ret; } +static void vc4_hdmi_unbind(struct device *dev, struct device *master, void *data) +{ + struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); + + mutex_destroy(&vc4_hdmi->update_plugged_status_lock); +} + static const struct component_ops vc4_hdmi_ops = { .bind = vc4_hdmi_bind, + .unbind = vc4_hdmi_unbind, }; static int vc4_hdmi_dev_probe(struct platform_device *pdev) --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -2,8 +2,10 @@ #define _VC4_HDMI_H_ #include +#include #include #include +#include #include #include "vc4_drv.h" @@ -228,6 +230,31 @@ struct vc4_hdmi { * for use outside of KMS hooks. Protected by @mutex. */ enum vc4_hdmi_output_format output_format; + + /** + * @plugged_cb: Callback provided by hdmi-codec to indicate that an + * HDMI hotplug occurred and jack state should be updated. Protected by + * @update_plugged_status_lock. + */ + hdmi_codec_plugged_cb plugged_cb; + + /** + * @plugged_cb: Context for plugged_cb. Protected by + * @update_plugged_status_lock. + */ + struct device *codec_dev; + + /** + * @update_plugged_status_lock: Prevents a race condition where an HDMI + * hotplug might occur between @plugged_cb and @codec_dev being set. + */ + struct mutex update_plugged_status_lock; + + /** + * @hdmi_jack: Represents the connection state of the HDMI plug, for + * ALSA jack detection. + */ + struct snd_soc_jack hdmi_jack; }; #define connector_to_vc4_hdmi(_connector) \