// SPDX-License-Identifier: MIT
/*
* Copyright ( c ) 2024 Linaro Ltd
*/
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <drm/drm_connector.h>
#include <drm/drm_device.h>
#include <drm/display/drm_hdmi_audio_helper.h>
#include <sound/hdmi-codec.h>
static int drm_connector_hdmi_audio_startup(struct device *dev, void *data)
{
struct drm_connector *connector = data;
const struct drm_connector_hdmi_audio_funcs *funcs =
connector->hdmi_audio.funcs;
if (funcs->startup)
return funcs->startup(connector);
return 0 ;
}
static int drm_connector_hdmi_audio_prepare(struct device *dev, void *data,
struct hdmi_codec_daifmt *fmt,
struct hdmi_codec_params *hparms)
{
struct drm_connector *connector = data;
const struct drm_connector_hdmi_audio_funcs *funcs =
connector->hdmi_audio.funcs;
return funcs->prepare(connector, fmt, hparms);
}
static void drm_connector_hdmi_audio_shutdown(struct device *dev, void *data)
{
struct drm_connector *connector = data;
const struct drm_connector_hdmi_audio_funcs *funcs =
connector->hdmi_audio.funcs;
return funcs->shutdown(connector);
}
static int drm_connector_hdmi_audio_mute_stream(struct device *dev, void *data,
bool enable, int direction)
{
struct drm_connector *connector = data;
const struct drm_connector_hdmi_audio_funcs *funcs =
connector->hdmi_audio.funcs;
if (funcs->mute_stream)
return funcs->mute_stream(connector, enable, direction);
return -ENOTSUPP;
}
static int drm_connector_hdmi_audio_get_dai_id(struct snd_soc_component *comment,
struct device_node *endpoint,
void *data)
{
struct drm_connector *connector = data;
struct of_endpoint of_ep;
int ret;
if (connector->hdmi_audio.dai_port < 0 )
return -ENOTSUPP;
ret = of_graph_parse_endpoint(endpoint, &of_ep);
if (ret < 0 )
return ret;
if (of_ep.port == connector->hdmi_audio.dai_port)
return 0 ;
return -EINVAL;
}
static int drm_connector_hdmi_audio_get_eld(struct device *dev, void *data,
uint8_t *buf, size_t len)
{
struct drm_connector *connector = data;
mutex_lock(&connector->eld_mutex);
memcpy(buf, connector->eld, min(sizeof (connector->eld), len));
mutex_unlock(&connector->eld_mutex);
return 0 ;
}
static int drm_connector_hdmi_audio_hook_plugged_cb(struct device *dev,
void *data,
hdmi_codec_plugged_cb fn,
struct device *codec_dev)
{
struct drm_connector *connector = data;
mutex_lock(&connector->hdmi_audio.lock);
connector->hdmi_audio.plugged_cb = fn;
connector->hdmi_audio.plugged_cb_dev = codec_dev;
if (fn)
fn(codec_dev, connector->hdmi_audio.last_state);
mutex_unlock(&connector->hdmi_audio.lock);
return 0 ;
}
void drm_connector_hdmi_audio_plugged_notify(struct drm_connector *connector,
bool plugged)
{
mutex_lock(&connector->hdmi_audio.lock);
connector->hdmi_audio.last_state = plugged;
if (connector->hdmi_audio.plugged_cb &&
connector->hdmi_audio.plugged_cb_dev)
connector->hdmi_audio.plugged_cb(connector->hdmi_audio.plugged_cb_dev,
connector->hdmi_audio.last_state);
mutex_unlock(&connector->hdmi_audio.lock);
}
EXPORT_SYMBOL(drm_connector_hdmi_audio_plugged_notify);
static const struct hdmi_codec_ops drm_connector_hdmi_audio_ops = {
.audio_startup = drm_connector_hdmi_audio_startup,
.prepare = drm_connector_hdmi_audio_prepare,
.audio_shutdown = drm_connector_hdmi_audio_shutdown,
.mute_stream = drm_connector_hdmi_audio_mute_stream,
.get_eld = drm_connector_hdmi_audio_get_eld,
.get_dai_id = drm_connector_hdmi_audio_get_dai_id,
.hook_plugged_cb = drm_connector_hdmi_audio_hook_plugged_cb,
};
/**
* drm_connector_hdmi_audio_init - Initialize HDMI Codec device for the DRM connector
* @ connector : A pointer to the connector to allocate codec for
* @ hdmi_codec_dev : device to be used as a parent for the HDMI Codec
* @ funcs : callbacks for this HDMI Codec
* @ max_i2s_playback_channels : maximum number of playback I2S channels
* @ i2s_formats : set of I2S formats ( use 0 for a bus - specific set )
* @ spdif_playback : set if HDMI codec has S / PDIF playback port
* @ dai_port : sound DAI port , - 1 if it is not enabled
*
* Create a HDMI codec device to be used with the specified connector .
*
* Returns :
* Zero on success , error code on failure .
*/
int drm_connector_hdmi_audio_init(struct drm_connector *connector,
struct device *hdmi_codec_dev,
const struct drm_connector_hdmi_audio_funcs *funcs,
unsigned int max_i2s_playback_channels,
u64 i2s_formats,
bool spdif_playback,
int dai_port)
{
struct hdmi_codec_pdata codec_pdata = {
.ops = &drm_connector_hdmi_audio_ops,
.max_i2s_channels = max_i2s_playback_channels,
.i2s = !!max_i2s_playback_channels,
.i2s_formats = i2s_formats,
.spdif = spdif_playback,
.no_i2s_capture = true ,
.no_spdif_capture = true ,
.data = connector,
};
struct platform_device *pdev;
if (!funcs ||
!funcs->prepare ||
!funcs->shutdown)
return -EINVAL;
connector->hdmi_audio.funcs = funcs;
connector->hdmi_audio.dai_port = dai_port;
pdev = platform_device_register_data(hdmi_codec_dev,
HDMI_CODEC_DRV_NAME,
PLATFORM_DEVID_AUTO,
&codec_pdata, sizeof (codec_pdata));
if (IS_ERR(pdev))
return PTR_ERR(pdev);
connector->hdmi_audio.codec_pdev = pdev;
return 0 ;
}
EXPORT_SYMBOL(drm_connector_hdmi_audio_init);
Messung V0.5 in Prozent C=99 H=100 G=99
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-10-02)
¤
*© Formatika GbR, Deutschland