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