1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright 2026 Advanced Micro Devices, Inc. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 * OTHER DEALINGS IN THE SOFTWARE. 22 * 23 * Authors: AMD 24 */ 25 26 #include "amdgpu.h" 27 #include "amdgpu_dm.h" 28 #include "amdgpu_dm_audio.h" 29 #include "dm_helpers.h" 30 #include "dc.h" 31 32 #include <linux/component.h> 33 #include <drm/drm_atomic.h> 34 #include <drm/drm_audio_component.h> 35 #include <drm/drm_connector.h> 36 #include <drm/drm_edid.h> 37 #include <drm/drm_eld.h> 38 39 #include "dc/inc/core_types.h" 40 41 static int amdgpu_dm_audio_component_get_eld(struct device *kdev, int port, 42 int pipe, bool *enabled, 43 unsigned char *buf, int max_bytes) 44 { 45 struct drm_device *dev = dev_get_drvdata(kdev); 46 struct amdgpu_device *adev = drm_to_adev(dev); 47 struct drm_connector *connector; 48 struct drm_connector_list_iter conn_iter; 49 struct amdgpu_dm_connector *aconnector; 50 int ret = 0; 51 52 *enabled = false; 53 54 mutex_lock(&adev->dm.audio_lock); 55 56 drm_connector_list_iter_begin(dev, &conn_iter); 57 drm_for_each_connector_iter(connector, &conn_iter) { 58 59 if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) 60 continue; 61 62 aconnector = to_amdgpu_dm_connector(connector); 63 if (aconnector->audio_inst != port) 64 continue; 65 66 *enabled = true; 67 mutex_lock(&connector->eld_mutex); 68 ret = drm_eld_size(connector->eld); 69 memcpy(buf, connector->eld, min(max_bytes, ret)); 70 mutex_unlock(&connector->eld_mutex); 71 72 break; 73 } 74 drm_connector_list_iter_end(&conn_iter); 75 76 mutex_unlock(&adev->dm.audio_lock); 77 78 drm_dbg_kms(adev_to_drm(adev), "Get ELD : idx=%d ret=%d en=%d\n", port, ret, *enabled); 79 80 return ret; 81 } 82 83 static const struct drm_audio_component_ops amdgpu_dm_audio_component_ops = { 84 .get_eld = amdgpu_dm_audio_component_get_eld, 85 }; 86 87 STATIC_IFN_KUNIT int amdgpu_dm_audio_component_bind(struct device *kdev, 88 struct device *hda_kdev, void *data) 89 { 90 struct drm_device *dev = dev_get_drvdata(kdev); 91 struct amdgpu_device *adev = drm_to_adev(dev); 92 struct drm_audio_component *acomp = data; 93 94 acomp->ops = &amdgpu_dm_audio_component_ops; 95 acomp->dev = kdev; 96 adev->dm.audio_component = acomp; 97 98 return 0; 99 } 100 EXPORT_IF_KUNIT(amdgpu_dm_audio_component_bind); 101 102 STATIC_IFN_KUNIT void amdgpu_dm_audio_component_unbind(struct device *kdev, 103 struct device *hda_kdev, void *data) 104 { 105 struct amdgpu_device *adev = drm_to_adev(dev_get_drvdata(kdev)); 106 struct drm_audio_component *acomp = data; 107 108 acomp->ops = NULL; 109 acomp->dev = NULL; 110 adev->dm.audio_component = NULL; 111 } 112 EXPORT_IF_KUNIT(amdgpu_dm_audio_component_unbind); 113 114 static const struct component_ops amdgpu_dm_audio_component_bind_ops = { 115 .bind = amdgpu_dm_audio_component_bind, 116 .unbind = amdgpu_dm_audio_component_unbind, 117 }; 118 119 STATIC_IFN_KUNIT 120 void amdgpu_dm_audio_init_pins(struct amdgpu_device *adev, int audio_count, 121 const unsigned int *inst_array) 122 { 123 int i; 124 125 adev->mode_info.audio.num_pins = audio_count; 126 127 for (i = 0; i < audio_count; i++) { 128 adev->mode_info.audio.pin[i].channels = -1; 129 adev->mode_info.audio.pin[i].rate = -1; 130 adev->mode_info.audio.pin[i].bits_per_sample = -1; 131 adev->mode_info.audio.pin[i].status_bits = 0; 132 adev->mode_info.audio.pin[i].category_code = 0; 133 adev->mode_info.audio.pin[i].connected = false; 134 adev->mode_info.audio.pin[i].id = inst_array[i]; 135 adev->mode_info.audio.pin[i].offset = 0; 136 } 137 } 138 EXPORT_IF_KUNIT(amdgpu_dm_audio_init_pins); 139 140 int amdgpu_dm_audio_init(struct amdgpu_device *adev) 141 { 142 unsigned int inst_array[MAX_AUDIOS]; 143 int audio_count; 144 int i, ret; 145 146 if (!amdgpu_audio) 147 return 0; 148 149 adev->mode_info.audio.enabled = true; 150 151 audio_count = adev->dm.dc->res_pool->audio_count; 152 for (i = 0; i < audio_count; i++) 153 inst_array[i] = adev->dm.dc->res_pool->audios[i]->inst; 154 amdgpu_dm_audio_init_pins(adev, audio_count, inst_array); 155 156 ret = component_add(adev->dev, &amdgpu_dm_audio_component_bind_ops); 157 if (ret < 0) 158 return ret; 159 160 adev->dm.audio_registered = true; 161 162 return 0; 163 } 164 EXPORT_IF_KUNIT(amdgpu_dm_audio_init); 165 166 void amdgpu_dm_audio_fini(struct amdgpu_device *adev) 167 { 168 if (!amdgpu_audio) 169 return; 170 171 if (!adev->mode_info.audio.enabled) 172 return; 173 174 if (adev->dm.audio_registered) { 175 component_del(adev->dev, &amdgpu_dm_audio_component_bind_ops); 176 adev->dm.audio_registered = false; 177 } 178 179 /* TODO: Disable audio? */ 180 181 adev->mode_info.audio.enabled = false; 182 } 183 EXPORT_IF_KUNIT(amdgpu_dm_audio_fini); 184 185 void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin) 186 { 187 struct drm_audio_component *acomp = adev->dm.audio_component; 188 189 if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) { 190 drm_dbg_kms(adev_to_drm(adev), "Notify ELD: %d\n", pin); 191 192 acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, 193 pin, -1); 194 } 195 } 196 EXPORT_IF_KUNIT(amdgpu_dm_audio_eld_notify); 197 198 void amdgpu_dm_fill_audio_info(struct audio_info *audio_info, 199 const struct drm_connector *drm_connector, 200 const struct dc_sink *dc_sink) 201 { 202 int i = 0; 203 int cea_revision = 0; 204 const struct dc_edid_caps *edid_caps = &dc_sink->edid_caps; 205 206 audio_info->manufacture_id = edid_caps->manufacturer_id; 207 audio_info->product_id = edid_caps->product_id; 208 209 cea_revision = drm_connector->display_info.cea_rev; 210 211 strscpy(audio_info->display_name, 212 edid_caps->display_name, 213 AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS); 214 215 if (cea_revision >= 3) { 216 audio_info->mode_count = edid_caps->audio_mode_count; 217 218 for (i = 0; i < audio_info->mode_count; ++i) { 219 audio_info->modes[i].format_code = 220 (enum audio_format_code) 221 (edid_caps->audio_modes[i].format_code); 222 audio_info->modes[i].channel_count = 223 edid_caps->audio_modes[i].channel_count; 224 audio_info->modes[i].sample_rates.all = 225 edid_caps->audio_modes[i].sample_rate; 226 audio_info->modes[i].sample_size = 227 edid_caps->audio_modes[i].sample_size; 228 } 229 } 230 231 audio_info->flags.all = edid_caps->speaker_flags; 232 233 /* TODO: We only check for the progressive mode, check for interlace mode too */ 234 if (drm_connector->latency_present[0]) { 235 audio_info->video_latency = drm_connector->video_latency[0]; 236 audio_info->audio_latency = drm_connector->audio_latency[0]; 237 } 238 239 /* TODO: For DP, video and audio latency should be calculated from DPCD caps */ 240 241 } 242 EXPORT_IF_KUNIT(amdgpu_dm_fill_audio_info); 243 244 void amdgpu_dm_commit_audio(struct drm_device *dev, 245 struct drm_atomic_commit *state) 246 { 247 struct amdgpu_device *adev = drm_to_adev(dev); 248 struct amdgpu_dm_connector *aconnector; 249 struct drm_connector *connector; 250 struct drm_connector_state *old_con_state, *new_con_state; 251 struct drm_crtc_state *new_crtc_state; 252 struct dm_crtc_state *new_dm_crtc_state; 253 const struct dc_stream_status *status; 254 int i, inst; 255 256 /* Notify device removals. */ 257 for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { 258 if (old_con_state->crtc != new_con_state->crtc) { 259 /* CRTC changes require notification. */ 260 goto notify; 261 } 262 263 if (!new_con_state->crtc) 264 continue; 265 266 new_crtc_state = drm_atomic_get_new_crtc_state( 267 state, new_con_state->crtc); 268 269 if (!new_crtc_state) 270 continue; 271 272 if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) 273 continue; 274 275 notify: 276 if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) 277 continue; 278 279 aconnector = to_amdgpu_dm_connector(connector); 280 281 mutex_lock(&adev->dm.audio_lock); 282 inst = aconnector->audio_inst; 283 aconnector->audio_inst = -1; 284 mutex_unlock(&adev->dm.audio_lock); 285 286 amdgpu_dm_audio_eld_notify(adev, inst); 287 } 288 289 /* Notify audio device additions. */ 290 for_each_new_connector_in_state(state, connector, new_con_state, i) { 291 if (!new_con_state->crtc) 292 continue; 293 294 new_crtc_state = drm_atomic_get_new_crtc_state( 295 state, new_con_state->crtc); 296 297 if (!new_crtc_state) 298 continue; 299 300 if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) 301 continue; 302 303 new_dm_crtc_state = to_dm_crtc_state(new_crtc_state); 304 if (!new_dm_crtc_state->stream) 305 continue; 306 307 status = dc_stream_get_status(new_dm_crtc_state->stream); 308 if (!status) 309 continue; 310 311 if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) 312 continue; 313 314 aconnector = to_amdgpu_dm_connector(connector); 315 316 mutex_lock(&adev->dm.audio_lock); 317 inst = status->audio_inst; 318 aconnector->audio_inst = inst; 319 mutex_unlock(&adev->dm.audio_lock); 320 321 amdgpu_dm_audio_eld_notify(adev, inst); 322 } 323 } 324 EXPORT_IF_KUNIT(amdgpu_dm_commit_audio); 325 326 #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) 327 int amdgpu_dm_audio_get_param(void) 328 { 329 return amdgpu_audio; 330 } 331 EXPORT_IF_KUNIT(amdgpu_dm_audio_get_param); 332 333 void amdgpu_dm_audio_set_param(int val) 334 { 335 amdgpu_audio = val; 336 } 337 EXPORT_IF_KUNIT(amdgpu_dm_audio_set_param); 338 #endif 339