Path: blob/master/drivers/gpu/drm/display/drm_hdmi_audio_helper.c
26494 views
// SPDX-License-Identifier: MIT1/*2* Copyright (c) 2024 Linaro Ltd3*/45#include <linux/export.h>6#include <linux/mutex.h>7#include <linux/of_graph.h>8#include <linux/platform_device.h>910#include <drm/drm_connector.h>11#include <drm/drm_device.h>12#include <drm/display/drm_hdmi_audio_helper.h>1314#include <sound/hdmi-codec.h>1516static int drm_connector_hdmi_audio_startup(struct device *dev, void *data)17{18struct drm_connector *connector = data;19const struct drm_connector_hdmi_audio_funcs *funcs =20connector->hdmi_audio.funcs;2122if (funcs->startup)23return funcs->startup(connector);2425return 0;26}2728static int drm_connector_hdmi_audio_prepare(struct device *dev, void *data,29struct hdmi_codec_daifmt *fmt,30struct hdmi_codec_params *hparms)31{32struct drm_connector *connector = data;33const struct drm_connector_hdmi_audio_funcs *funcs =34connector->hdmi_audio.funcs;3536return funcs->prepare(connector, fmt, hparms);37}3839static void drm_connector_hdmi_audio_shutdown(struct device *dev, void *data)40{41struct drm_connector *connector = data;42const struct drm_connector_hdmi_audio_funcs *funcs =43connector->hdmi_audio.funcs;4445return funcs->shutdown(connector);46}4748static int drm_connector_hdmi_audio_mute_stream(struct device *dev, void *data,49bool enable, int direction)50{51struct drm_connector *connector = data;52const struct drm_connector_hdmi_audio_funcs *funcs =53connector->hdmi_audio.funcs;5455if (funcs->mute_stream)56return funcs->mute_stream(connector, enable, direction);5758return -ENOTSUPP;59}6061static int drm_connector_hdmi_audio_get_dai_id(struct snd_soc_component *comment,62struct device_node *endpoint,63void *data)64{65struct drm_connector *connector = data;66struct of_endpoint of_ep;67int ret;6869if (connector->hdmi_audio.dai_port < 0)70return -ENOTSUPP;7172ret = of_graph_parse_endpoint(endpoint, &of_ep);73if (ret < 0)74return ret;7576if (of_ep.port == connector->hdmi_audio.dai_port)77return 0;7879return -EINVAL;80}8182static int drm_connector_hdmi_audio_get_eld(struct device *dev, void *data,83uint8_t *buf, size_t len)84{85struct drm_connector *connector = data;8687mutex_lock(&connector->eld_mutex);88memcpy(buf, connector->eld, min(sizeof(connector->eld), len));89mutex_unlock(&connector->eld_mutex);9091return 0;92}9394static int drm_connector_hdmi_audio_hook_plugged_cb(struct device *dev,95void *data,96hdmi_codec_plugged_cb fn,97struct device *codec_dev)98{99struct drm_connector *connector = data;100101mutex_lock(&connector->hdmi_audio.lock);102103connector->hdmi_audio.plugged_cb = fn;104connector->hdmi_audio.plugged_cb_dev = codec_dev;105106if (fn)107fn(codec_dev, connector->hdmi_audio.last_state);108109mutex_unlock(&connector->hdmi_audio.lock);110111return 0;112}113114void drm_connector_hdmi_audio_plugged_notify(struct drm_connector *connector,115bool plugged)116{117mutex_lock(&connector->hdmi_audio.lock);118119connector->hdmi_audio.last_state = plugged;120121if (connector->hdmi_audio.plugged_cb &&122connector->hdmi_audio.plugged_cb_dev)123connector->hdmi_audio.plugged_cb(connector->hdmi_audio.plugged_cb_dev,124connector->hdmi_audio.last_state);125126mutex_unlock(&connector->hdmi_audio.lock);127}128EXPORT_SYMBOL(drm_connector_hdmi_audio_plugged_notify);129130static const struct hdmi_codec_ops drm_connector_hdmi_audio_ops = {131.audio_startup = drm_connector_hdmi_audio_startup,132.prepare = drm_connector_hdmi_audio_prepare,133.audio_shutdown = drm_connector_hdmi_audio_shutdown,134.mute_stream = drm_connector_hdmi_audio_mute_stream,135.get_eld = drm_connector_hdmi_audio_get_eld,136.get_dai_id = drm_connector_hdmi_audio_get_dai_id,137.hook_plugged_cb = drm_connector_hdmi_audio_hook_plugged_cb,138};139140/**141* drm_connector_hdmi_audio_init - Initialize HDMI Codec device for the DRM connector142* @connector: A pointer to the connector to allocate codec for143* @hdmi_codec_dev: device to be used as a parent for the HDMI Codec144* @funcs: callbacks for this HDMI Codec145* @max_i2s_playback_channels: maximum number of playback I2S channels146* @i2s_formats: set of I2S formats (use 0 for a bus-specific set)147* @spdif_playback: set if HDMI codec has S/PDIF playback port148* @dai_port: sound DAI port, -1 if it is not enabled149*150* Create a HDMI codec device to be used with the specified connector.151*152* Returns:153* Zero on success, error code on failure.154*/155int drm_connector_hdmi_audio_init(struct drm_connector *connector,156struct device *hdmi_codec_dev,157const struct drm_connector_hdmi_audio_funcs *funcs,158unsigned int max_i2s_playback_channels,159u64 i2s_formats,160bool spdif_playback,161int dai_port)162{163struct hdmi_codec_pdata codec_pdata = {164.ops = &drm_connector_hdmi_audio_ops,165.max_i2s_channels = max_i2s_playback_channels,166.i2s = !!max_i2s_playback_channels,167.i2s_formats = i2s_formats,168.spdif = spdif_playback,169.no_i2s_capture = true,170.no_spdif_capture = true,171.data = connector,172};173struct platform_device *pdev;174175if (!funcs ||176!funcs->prepare ||177!funcs->shutdown)178return -EINVAL;179180connector->hdmi_audio.funcs = funcs;181connector->hdmi_audio.dai_port = dai_port;182183pdev = platform_device_register_data(hdmi_codec_dev,184HDMI_CODEC_DRV_NAME,185PLATFORM_DEVID_AUTO,186&codec_pdata, sizeof(codec_pdata));187if (IS_ERR(pdev))188return PTR_ERR(pdev);189190connector->hdmi_audio.codec_pdev = pdev;191192return 0;193}194EXPORT_SYMBOL(drm_connector_hdmi_audio_init);195196197