1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright (c) 2024 Linaro Ltd 4 */ 5 6 #include <linux/mutex.h> 7 #include <linux/of_graph.h> 8 #include <linux/platform_device.h> 9 10 #include <drm/drm_connector.h> 11 #include <drm/drm_device.h> 12 #include <drm/display/drm_hdmi_audio_helper.h> 13 14 #include <sound/hdmi-codec.h> 15 16 static int drm_connector_hdmi_audio_startup(struct device *dev, void *data) 17 { 18 struct drm_connector *connector = data; 19 const struct drm_connector_hdmi_audio_funcs *funcs = 20 connector->hdmi_audio.funcs; 21 22 if (funcs->startup) 23 return funcs->startup(connector); 24 25 return 0; 26 } 27 28 static int drm_connector_hdmi_audio_prepare(struct device *dev, void *data, 29 struct hdmi_codec_daifmt *fmt, 30 struct hdmi_codec_params *hparms) 31 { 32 struct drm_connector *connector = data; 33 const struct drm_connector_hdmi_audio_funcs *funcs = 34 connector->hdmi_audio.funcs; 35 36 return funcs->prepare(connector, fmt, hparms); 37 } 38 39 static void drm_connector_hdmi_audio_shutdown(struct device *dev, void *data) 40 { 41 struct drm_connector *connector = data; 42 const struct drm_connector_hdmi_audio_funcs *funcs = 43 connector->hdmi_audio.funcs; 44 45 return funcs->shutdown(connector); 46 } 47 48 static int drm_connector_hdmi_audio_mute_stream(struct device *dev, void *data, 49 bool enable, int direction) 50 { 51 struct drm_connector *connector = data; 52 const struct drm_connector_hdmi_audio_funcs *funcs = 53 connector->hdmi_audio.funcs; 54 55 if (funcs->mute_stream) 56 return funcs->mute_stream(connector, enable, direction); 57 58 return -ENOTSUPP; 59 } 60 61 static int drm_connector_hdmi_audio_get_dai_id(struct snd_soc_component *comment, 62 struct device_node *endpoint, 63 void *data) 64 { 65 struct drm_connector *connector = data; 66 struct of_endpoint of_ep; 67 int ret; 68 69 if (connector->hdmi_audio.dai_port < 0) 70 return -ENOTSUPP; 71 72 ret = of_graph_parse_endpoint(endpoint, &of_ep); 73 if (ret < 0) 74 return ret; 75 76 if (of_ep.port == connector->hdmi_audio.dai_port) 77 return 0; 78 79 return -EINVAL; 80 } 81 82 static int drm_connector_hdmi_audio_get_eld(struct device *dev, void *data, 83 uint8_t *buf, size_t len) 84 { 85 struct drm_connector *connector = data; 86 87 mutex_lock(&connector->eld_mutex); 88 memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); 89 mutex_unlock(&connector->eld_mutex); 90 91 return 0; 92 } 93 94 static int drm_connector_hdmi_audio_hook_plugged_cb(struct device *dev, 95 void *data, 96 hdmi_codec_plugged_cb fn, 97 struct device *codec_dev) 98 { 99 struct drm_connector *connector = data; 100 101 mutex_lock(&connector->hdmi_audio.lock); 102 103 connector->hdmi_audio.plugged_cb = fn; 104 connector->hdmi_audio.plugged_cb_dev = codec_dev; 105 106 fn(codec_dev, connector->hdmi_audio.last_state); 107 108 mutex_unlock(&connector->hdmi_audio.lock); 109 110 return 0; 111 } 112 113 void drm_connector_hdmi_audio_plugged_notify(struct drm_connector *connector, 114 bool plugged) 115 { 116 mutex_lock(&connector->hdmi_audio.lock); 117 118 connector->hdmi_audio.last_state = plugged; 119 120 if (connector->hdmi_audio.plugged_cb && 121 connector->hdmi_audio.plugged_cb_dev) 122 connector->hdmi_audio.plugged_cb(connector->hdmi_audio.plugged_cb_dev, 123 connector->hdmi_audio.last_state); 124 125 mutex_unlock(&connector->hdmi_audio.lock); 126 } 127 EXPORT_SYMBOL(drm_connector_hdmi_audio_plugged_notify); 128 129 static const struct hdmi_codec_ops drm_connector_hdmi_audio_ops = { 130 .audio_startup = drm_connector_hdmi_audio_startup, 131 .prepare = drm_connector_hdmi_audio_prepare, 132 .audio_shutdown = drm_connector_hdmi_audio_shutdown, 133 .mute_stream = drm_connector_hdmi_audio_mute_stream, 134 .get_eld = drm_connector_hdmi_audio_get_eld, 135 .get_dai_id = drm_connector_hdmi_audio_get_dai_id, 136 .hook_plugged_cb = drm_connector_hdmi_audio_hook_plugged_cb, 137 }; 138 139 /** 140 * drm_connector_hdmi_audio_init - Initialize HDMI Codec device for the DRM connector 141 * @connector: A pointer to the connector to allocate codec for 142 * @hdmi_codec_dev: device to be used as a parent for the HDMI Codec 143 * @funcs: callbacks for this HDMI Codec 144 * @max_i2s_playback_channels: maximum number of playback I2S channels 145 * @spdif_playback: set if HDMI codec has S/PDIF playback port 146 * @dai_port: sound DAI port, -1 if it is not enabled 147 * 148 * Create a HDMI codec device to be used with the specified connector. 149 * 150 * Returns: 151 * Zero on success, error code on failure. 152 */ 153 int drm_connector_hdmi_audio_init(struct drm_connector *connector, 154 struct device *hdmi_codec_dev, 155 const struct drm_connector_hdmi_audio_funcs *funcs, 156 unsigned int max_i2s_playback_channels, 157 bool spdif_playback, 158 int dai_port) 159 { 160 struct hdmi_codec_pdata codec_pdata = { 161 .ops = &drm_connector_hdmi_audio_ops, 162 .max_i2s_channels = max_i2s_playback_channels, 163 .i2s = !!max_i2s_playback_channels, 164 .spdif = spdif_playback, 165 .no_i2s_capture = true, 166 .no_spdif_capture = true, 167 .data = connector, 168 }; 169 struct platform_device *pdev; 170 171 if (!funcs || 172 !funcs->prepare || 173 !funcs->shutdown) 174 return -EINVAL; 175 176 connector->hdmi_audio.funcs = funcs; 177 connector->hdmi_audio.dai_port = dai_port; 178 179 pdev = platform_device_register_data(hdmi_codec_dev, 180 HDMI_CODEC_DRV_NAME, 181 PLATFORM_DEVID_AUTO, 182 &codec_pdata, sizeof(codec_pdata)); 183 if (IS_ERR(pdev)) 184 return PTR_ERR(pdev); 185 186 connector->hdmi_audio.codec_pdev = pdev; 187 188 return 0; 189 } 190 EXPORT_SYMBOL(drm_connector_hdmi_audio_init); 191