mirror of
https://github.com/torvalds/linux.git
synced 2026-10-26 20:46:02 +01:00
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. 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> Acked-by: Mark Brown <broonie@kernel.org> Acked-by: Jarkko Nikula <jarkko.nikula@bitmer.com> Tested-by: Jarkko Nikula <jarkko.nikula@bitmer.com> Link: https://patch.msgid.link/20260819171935.233946-1-ivo.g.dimitrov.75@gmail.com Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
This commit is contained in:
parent
20207f7838
commit
699e5975e2
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -425,6 +425,15 @@ static void hdmi5_bridge_disable(struct drm_bridge *bridge,
|
|||
mutex_unlock(&hdmi->lock);
|
||||
}
|
||||
|
||||
static void hdmi5_bridge_hpd_notify(struct drm_bridge *bridge,
|
||||
struct drm_connector *connector,
|
||||
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 +484,7 @@ static const struct drm_bridge_funcs hdmi5_bridge_funcs = {
|
|||
.atomic_create_state = drm_atomic_helper_bridge_create_state,
|
||||
.atomic_enable = hdmi5_bridge_enable,
|
||||
.atomic_disable = hdmi5_bridge_disable,
|
||||
.hpd_notify = hdmi5_bridge_hpd_notify,
|
||||
.edid_read = hdmi5_bridge_edid_read,
|
||||
};
|
||||
|
||||
|
|
@ -601,12 +611,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 +670,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 +757,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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user