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 if (fn) 107 fn(codec_dev, connector->hdmi_audio.last_state); 108 109 mutex_unlock(&connector->hdmi_audio.lock); 110 111 return 0; 112 } 113 114 void drm_connector_hdmi_audio_plugged_notify(struct drm_connector *connector, 115 bool plugged) 116 { 117 mutex_lock(&connector->hdmi_audio.lock); 118 119 connector->hdmi_audio.last_state = plugged; 120 121 if (connector->hdmi_audio.plugged_cb && 122 connector->hdmi_audio.plugged_cb_dev) 123 connector->hdmi_audio.plugged_cb(connector->hdmi_audio.plugged_cb_dev, 124 connector->hdmi_audio.last_state); 125 126 mutex_unlock(&connector->hdmi_audio.lock); 127 } 128 EXPORT_SYMBOL(drm_connector_hdmi_audio_plugged_notify); 129 130 static 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 }; 139 140 /** 141 * drm_connector_hdmi_audio_init - Initialize HDMI Codec device for the DRM connector 142 * @connector: A pointer to the connector to allocate codec for 143 * @hdmi_codec_dev: device to be used as a parent for the HDMI Codec 144 * @funcs: callbacks for this HDMI Codec 145 * @max_i2s_playback_channels: maximum number of playback I2S channels 146 * @spdif_playback: set if HDMI codec has S/PDIF playback port 147 * @dai_port: sound DAI port, -1 if it is not enabled 148 * 149 * Create a HDMI codec device to be used with the specified connector. 150 * 151 * Returns: 152 * Zero on success, error code on failure. 153 */ 154 int drm_connector_hdmi_audio_init(struct drm_connector *connector, 155 struct device *hdmi_codec_dev, 156 const struct drm_connector_hdmi_audio_funcs *funcs, 157 unsigned int max_i2s_playback_channels, 158 bool spdif_playback, 159 int dai_port) 160 { 161 struct hdmi_codec_pdata codec_pdata = { 162 .ops = &drm_connector_hdmi_audio_ops, 163 .max_i2s_channels = max_i2s_playback_channels, 164 .i2s = !!max_i2s_playback_channels, 165 .spdif = spdif_playback, 166 .no_i2s_capture = true, 167 .no_spdif_capture = true, 168 .data = connector, 169 }; 170 struct platform_device *pdev; 171 172 if (!funcs || 173 !funcs->prepare || 174 !funcs->shutdown) 175 return -EINVAL; 176 177 connector->hdmi_audio.funcs = funcs; 178 connector->hdmi_audio.dai_port = dai_port; 179 180 pdev = platform_device_register_data(hdmi_codec_dev, 181 HDMI_CODEC_DRV_NAME, 182 PLATFORM_DEVID_AUTO, 183 &codec_pdata, sizeof(codec_pdata)); 184 if (IS_ERR(pdev)) 185 return PTR_ERR(pdev); 186 187 connector->hdmi_audio.codec_pdev = pdev; 188 189 return 0; 190 } 191 EXPORT_SYMBOL(drm_connector_hdmi_audio_init); 192