[PATCH v6 03/10] drm/connector: implement generic HDMI codec helpers
Maxime Ripard
mripard at kernel.org
Mon Dec 16 09:04:41 PST 2024
Hi,
On Fri, Dec 06, 2024 at 12:15:57PM +0200, Dmitry Baryshkov wrote:
> Several DRM drivers implement HDMI codec support (despite its name it
> applies to both HDMI and DisplayPort drivers). Implement generic
> framework to be used by these drivers. This removes a requirement to
> implement get_eld() callback and provides default implementation for
> codec's plug handling.
>
> The framework is integrated with the DRM HDMI Connector framework, but
> can be used by DisplayPort drivers.
>
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov at linaro.org>
> ---
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/drm_connector.c | 5 +
> drivers/gpu/drm/drm_connector_hdmi_codec.c | 185 +++++++++++++++++++++++++++++
> include/drm/drm_connector.h | 80 +++++++++++++
> 4 files changed, 271 insertions(+)
>
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 1677c1f335fbb0c6114bdb4cc0b12eb407d84564..afdd9268ca23ac7602e73bbe45f3f9cd090a3afd 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -42,6 +42,7 @@ drm-y := \
> drm_cache.o \
> drm_color_mgmt.o \
> drm_connector.o \
> + drm_connector_hdmi_codec.o \
> drm_crtc.o \
> drm_displayid.o \
> drm_drv.o \
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index bbdaaf7022b62d84594a29f1b60144920903a99a..4abfbded962bf45b793a2bd5b1b5c4d9f478a1f7 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -33,6 +33,7 @@
> #include <drm/drm_sysfs.h>
> #include <drm/drm_utils.h>
>
> +#include <linux/platform_device.h>
> #include <linux/property.h>
> #include <linux/uaccess.h>
>
> @@ -280,6 +281,7 @@ static int __drm_connector_init(struct drm_device *dev,
> mutex_init(&connector->eld_mutex);
> mutex_init(&connector->edid_override_mutex);
> mutex_init(&connector->hdmi.infoframes.lock);
> + mutex_init(&connector->hdmi_codec.lock);
> connector->edid_blob_ptr = NULL;
> connector->epoch_counter = 0;
> connector->tile_blob_ptr = NULL;
> @@ -632,6 +634,8 @@ void drm_connector_cleanup(struct drm_connector *connector)
> DRM_CONNECTOR_REGISTERED))
> drm_connector_unregister(connector);
>
> + platform_device_unregister(connector->hdmi_codec.codec_pdev);
> +
> if (connector->privacy_screen) {
> drm_privacy_screen_put(connector->privacy_screen);
> connector->privacy_screen = NULL;
> @@ -670,6 +674,7 @@ void drm_connector_cleanup(struct drm_connector *connector)
> connector->funcs->atomic_destroy_state(connector,
> connector->state);
>
> + mutex_destroy(&connector->hdmi_codec.lock);
> mutex_destroy(&connector->hdmi.infoframes.lock);
> mutex_destroy(&connector->mutex);
>
> diff --git a/drivers/gpu/drm/drm_connector_hdmi_codec.c b/drivers/gpu/drm/drm_connector_hdmi_codec.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..1934fb53b4d128434559970c9fea548bbc4bedda
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_connector_hdmi_codec.c
> @@ -0,0 +1,185 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright (c) 2024 Linaro Ltd
> + */
> +
> +#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 <sound/hdmi-codec.h>
> +
> +static int drm_connector_hdmi_codec_audio_startup(struct device *dev, void *data)
> +{
> + struct drm_connector *connector = data;
> + const struct drm_connector_hdmi_codec_funcs *funcs =
> + connector->hdmi.funcs->codec_funcs;
> +
> + if (funcs->audio_startup)
> + return funcs->audio_startup(connector);
> +
> + return 0;
> +}
> +
> +static int drm_connector_hdmi_codec_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_codec_funcs *funcs =
> + connector->hdmi.funcs->codec_funcs;
> +
> + return funcs->prepare(connector, fmt, hparms);
> +}
> +
> +static void drm_connector_hdmi_codec_audio_shutdown(struct device *dev, void *data)
> +{
> + struct drm_connector *connector = data;
> + const struct drm_connector_hdmi_codec_funcs *funcs =
> + connector->hdmi.funcs->codec_funcs;
> +
> + return funcs->audio_shutdown(connector);
> +}
> +
> +static int drm_connector_hdmi_codec_mute_stream(struct device *dev, void *data,
> + bool enable, int direction)
> +{
> + struct drm_connector *connector = data;
> + const struct drm_connector_hdmi_codec_funcs *funcs =
> + connector->hdmi.funcs->codec_funcs;
> +
> + if (funcs->mute_stream)
> + return funcs->mute_stream(connector, enable, direction);
> +
> + return -ENOTSUPP;
> +}
> +
> +static int drm_connector_hdmi_codec_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_codec.dai_port < 0)
> + return -ENOTSUPP;
> +
> + ret = of_graph_parse_endpoint(endpoint, &of_ep);
> + if (ret < 0)
> + return ret;
> +
> + if (of_ep.port == connector->hdmi_codec.dai_port)
> + return 0;
> +
> + return -EINVAL;
> +}
> +
> +static int drm_connector_hdmi_codec_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_codec_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_codec.lock);
> +
> + connector->hdmi_codec.plugged_cb = fn;
> + connector->hdmi_codec.plugged_cb_dev = codec_dev;
> +
> + fn(codec_dev, connector->hdmi_codec.last_state);
> +
> + mutex_unlock(&connector->hdmi_codec.lock);
> +
> + return 0;
> +}
> +
> +void drm_connector_hdmi_codec_plugged_notify(struct drm_connector *connector,
> + bool plugged)
> +{
> + mutex_lock(&connector->hdmi_codec.lock);
> +
> + connector->hdmi_codec.last_state = plugged;
> +
> + if (connector->hdmi_codec.plugged_cb &&
> + connector->hdmi_codec.plugged_cb_dev)
> + connector->hdmi_codec.plugged_cb(connector->hdmi_codec.plugged_cb_dev,
> + connector->hdmi_codec.last_state);
> +
> + mutex_unlock(&connector->hdmi_codec.lock);
> +}
> +EXPORT_SYMBOL(drm_connector_hdmi_codec_plugged_notify);
> +
> +static const struct hdmi_codec_ops drm_connector_hdmi_codec_ops = {
> + .audio_startup = drm_connector_hdmi_codec_audio_startup,
> + .prepare = drm_connector_hdmi_codec_prepare,
> + .audio_shutdown = drm_connector_hdmi_codec_audio_shutdown,
> + .mute_stream = drm_connector_hdmi_codec_mute_stream,
> + .get_eld = drm_connector_hdmi_codec_get_eld,
> + .get_dai_id = drm_connector_hdmi_codec_get_dai_id,
> + .hook_plugged_cb = drm_connector_hdmi_codec_hook_plugged_cb,
> +};
> +
> +/**
> + * drm_connector_hdmi_codec_init - Initialize HDMI Codec device for the DRM connector
I think it'd be better to call it drm_connector_hdmi_audio_init over
drm_connector_hdmi_codec_init (feature over implementation).
> + * @connector: A pointer to the connector to allocate codec for
> + * @hdmi_codec_dev: device to be used as a parent for the HDMI Codec
> + * @max_i2s_playback_channels: maximum number of playback I2S channels
> + * @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_codec_init(struct drm_connector *connector,
> + struct device *hdmi_codec_dev,
> + unsigned int max_i2s_playback_channels,
> + bool spdif_playback,
> + int dai_port)
> +{
> + struct hdmi_codec_pdata codec_pdata = {
> + .ops = &drm_connector_hdmi_codec_ops,
> + .max_i2s_channels = max_i2s_playback_channels,
> + .i2s = !!max_i2s_playback_channels,
> + .spdif = spdif_playback,
> + .no_i2s_capture = true,
> + .no_spdif_capture = true,
> + .data = connector,
> + };
> + struct platform_device *pdev;
> +
> + connector->hdmi_codec.dai_port = dai_port;
> +
> + if (!connector->hdmi.funcs->codec_funcs->prepare ||
> + !connector->hdmi.funcs->codec_funcs->audio_shutdown)
> + return -EINVAL;
> +
> + 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_codec.codec_pdev = pdev;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_connector_hdmi_codec_init);
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 1e2b25e204cb523d61d30f5409faa059bf2b86eb..1d113c0ceec7ce8196a412d7c00a1737175c6fbe 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -46,6 +46,8 @@ struct drm_property_blob;
> struct drm_printer;
> struct drm_privacy_screen;
> struct edid;
> +struct hdmi_codec_daifmt;
> +struct hdmi_codec_params;
> struct i2c_adapter;
>
> enum drm_connector_force {
> @@ -1141,6 +1143,52 @@ struct drm_connector_state {
> struct drm_connector_hdmi_state hdmi;
> };
>
> +struct drm_connector_hdmi_codec_funcs {
> + /**
> + * @audio_startup:
> + *
> + * Called when ASoC starts an audio stream setup. The
> + * @hdmi_audio_startup is optional.
> + *
> + * Returns:
> + * 0 on success, a negative error code otherwise
> + */
> + int (*audio_startup)(struct drm_connector *connector);
> +
> + /**
> + * @prepare:
> + * Configures HDMI-encoder for audio stream. Can be called
> + * multiple times for each setup. Mandatory.
> + *
> + * Returns:
> + * 0 on success, a negative error code otherwise
> + */
> + int (*prepare)(struct drm_connector *connector,
> + struct hdmi_codec_daifmt *fmt,
> + struct hdmi_codec_params *hparms);
Missing newline
> + /**
> + * @audio_shutdown:
> + *
> + * Shut down the audio stream. Mandatory.
> + *
> + * Returns:
> + * 0 on success, a negative error code otherwise
> + */
> + void (*audio_shutdown)(struct drm_connector *connector);
And thus we can probably just call that one shutdown?
Maxime
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 273 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20241216/69913deb/attachment.sig>
More information about the linux-arm-kernel
mailing list