projects/linux·blob master · refs

338 lines · 9.3 KiB

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
41static 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
83static const struct drm_audio_component_ops amdgpu_dm_audio_component_ops = {
84 .get_eld = amdgpu_dm_audio_component_get_eld,
85};
86
87STATIC_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}
100EXPORT_IF_KUNIT(amdgpu_dm_audio_component_bind);
101
102STATIC_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}
112EXPORT_IF_KUNIT(amdgpu_dm_audio_component_unbind);
113
114static 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
119STATIC_IFN_KUNIT
120void 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}
138EXPORT_IF_KUNIT(amdgpu_dm_audio_init_pins);
139
140int 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}
164EXPORT_IF_KUNIT(amdgpu_dm_audio_init);
165
166void 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}
183EXPORT_IF_KUNIT(amdgpu_dm_audio_fini);
184
185void 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}
196EXPORT_IF_KUNIT(amdgpu_dm_audio_eld_notify);
197
198void 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}
242EXPORT_IF_KUNIT(amdgpu_dm_fill_audio_info);
243
244void 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
275notify:
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}
324EXPORT_IF_KUNIT(amdgpu_dm_commit_audio);
325
326#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
327int amdgpu_dm_audio_get_param(void)
328{
329 return amdgpu_audio;
330}
331EXPORT_IF_KUNIT(amdgpu_dm_audio_get_param);
332
333void amdgpu_dm_audio_set_param(int val)
334{
335 amdgpu_audio = val;
336}
337EXPORT_IF_KUNIT(amdgpu_dm_audio_set_param);
338#endif