openwrt/target/linux/bcm27xx/patches-6.6/950-0930-vc4-Add-jack-detection-to-HDMI-audio-driver.patch

188 lines
5.5 KiB
Diff
Raw Normal View History

From 051392bfdc6dc54563ed9909cc1164e8d734af43 Mon Sep 17 00:00:00 2001
From: David Turner <david.turner@raspberrypi.com>
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 <david.turner@raspberrypi.com>
---
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 <linux/reset.h>
#include <sound/dmaengine_pcm.h>
#include <sound/hdmi-codec.h>
+#include <sound/jack.h>
#include <sound/pcm_drm_eld.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -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 <drm/drm_connector.h>
+#include <linux/mutex.h>
#include <media/cec.h>
#include <sound/dmaengine_pcm.h>
+#include <sound/hdmi-codec.h>
#include <sound/soc.h>
#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) \