Skip to content

Commit

Permalink
vc4: Add jack detection to HDMI audio driver
Browse files Browse the repository at this point in the history
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 <[email protected]>
cillian64 authored and rajeshkumarwr committed Jul 9, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent d6c79ef commit 4df9e89
Showing 2 changed files with 86 additions and 2 deletions.
61 changes: 59 additions & 2 deletions drivers/gpu/drm/vc4/vc4_hdmi.c
Original file line number Diff line number Diff line change
@@ -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(struct vc4_hdmi *vc4_hdmi,
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(struct vc4_hdmi *vc4_hdmi,
* 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 device *dev, void *data,
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(void *ptr)
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 vc4_hdmi *vc4_hdmi)
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 *dev, struct device *master, void *data)
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 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
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)
27 changes: 27 additions & 0 deletions drivers/gpu/drm/vc4/vc4_hdmi.h
Original file line number Diff line number Diff line change
@@ -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) \

0 comments on commit 4df9e89

Please sign in to comment.