1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
3 // This file is provided under a dual BSD/GPLv2 license. When using or
4 // redistributing this file, you may do so under either license.
6 // Copyright(c) 2022 Intel Corporation. All rights reserved.
8 #include <sound/pcm_params.h>
9 #include <sound/hdaudio_ext.h>
10 #include <sound/sof/ipc4/header.h>
11 #include <uapi/sound/sof/header.h>
12 #include "../ipc4-priv.h"
13 #include "../ipc4-topology.h"
14 #include "../sof-priv.h"
15 #include "../sof-audio.h"
18 /* These ops are only applicable for the HDA DAI's in their current form */
19 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
21 * This function checks if the host dma channel corresponding
22 * to the link DMA stream_tag argument is assigned to one
23 * of the FEs connected to the BE DAI.
25 static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
26 int dir, int stream_tag)
28 struct snd_pcm_substream *fe_substream;
29 struct hdac_stream *fe_hstream;
30 struct snd_soc_dpcm *dpcm;
32 for_each_dpcm_fe(rtd, dir, dpcm) {
33 fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
34 fe_hstream = fe_substream->runtime->private_data;
35 if (fe_hstream->stream_tag == stream_tag)
42 static struct hdac_ext_stream *
43 hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream)
45 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
46 struct sof_intel_hda_stream *hda_stream;
47 const struct sof_intel_dsp_desc *chip;
48 struct snd_sof_dev *sdev;
49 struct hdac_ext_stream *res = NULL;
50 struct hdac_stream *hstream = NULL;
52 int stream_dir = substream->stream;
55 dev_err(bus->dev, "stream type not supported\n");
59 spin_lock_irq(&bus->reg_lock);
60 list_for_each_entry(hstream, &bus->stream_list, list) {
61 struct hdac_ext_stream *hext_stream =
62 stream_to_hdac_ext_stream(hstream);
63 if (hstream->direction != substream->stream)
66 hda_stream = hstream_to_sof_hda_stream(hext_stream);
67 sdev = hda_stream->sdev;
68 chip = get_chip_info(sdev->pdata);
70 /* check if link is available */
71 if (!hext_stream->link_locked) {
73 * choose the first available link for platforms that do not have the
74 * PROCEN_FMT_QUIRK set.
76 if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
81 if (hstream->opened) {
83 * check if the stream tag matches the stream
84 * tag of one of the connected FEs
86 if (hda_check_fes(rtd, stream_dir,
87 hstream->stream_tag)) {
95 * This must be a hostless stream.
96 * So reserve the host DMA channel.
98 hda_stream->host_reserved = 1;
105 /* Make sure that host and link DMA is decoupled. */
106 snd_hdac_ext_stream_decouple_locked(bus, res, true);
108 res->link_locked = 1;
109 res->link_substream = substream;
111 spin_unlock_irq(&bus->reg_lock);
116 static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev,
117 struct snd_soc_dai *cpu_dai,
118 struct snd_pcm_substream *substream)
120 return snd_soc_dai_get_dma_data(cpu_dai, substream);
123 static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev,
124 struct snd_soc_dai *cpu_dai,
125 struct snd_pcm_substream *substream)
127 struct snd_sof_widget *pipe_widget;
128 struct sof_ipc4_pipeline *pipeline;
129 struct snd_sof_widget *swidget;
130 struct snd_soc_dapm_widget *w;
132 w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
133 swidget = w->dobj.private;
134 pipe_widget = swidget->spipe->pipe_widget;
135 pipeline = pipe_widget->private;
137 /* mark pipeline so that it can be skipped during FE trigger */
138 pipeline->skip_during_fe_trigger = true;
140 return snd_soc_dai_get_dma_data(cpu_dai, substream);
143 static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
144 struct snd_soc_dai *cpu_dai,
145 struct snd_pcm_substream *substream)
147 struct hdac_ext_stream *hext_stream;
149 hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
153 snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
158 static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
159 struct snd_pcm_substream *substream)
161 struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
163 snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
164 snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
167 static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
168 unsigned int format_val)
170 snd_hdac_ext_stream_setup(hext_stream, format_val);
173 static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
175 snd_hdac_ext_stream_reset(hext_stream);
178 static void hda_codec_dai_set_stream(struct snd_sof_dev *sdev,
179 struct snd_pcm_substream *substream,
180 struct hdac_stream *hstream)
182 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
183 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
185 /* set the hdac_stream in the codec dai */
186 snd_soc_dai_set_stream(codec_dai, hstream, substream->stream);
189 static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev,
190 struct snd_pcm_substream *substream,
191 struct snd_pcm_hw_params *params)
193 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
194 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
195 unsigned int link_bps;
196 unsigned int format_val;
198 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
199 link_bps = codec_dai->driver->playback.sig_bits;
201 link_bps = codec_dai->driver->capture.sig_bits;
203 format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
204 params_format(params), link_bps, 0);
206 dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
207 params_rate(params), params_channels(params), params_format(params));
212 static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev,
213 struct snd_pcm_substream *substream)
215 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
216 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
217 struct hdac_bus *bus = sof_to_bus(sdev);
219 return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
222 static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
223 struct snd_pcm_substream *substream, int cmd)
225 struct sof_ipc4_fw_data *ipc4_data = sdev->private;
226 struct snd_sof_widget *pipe_widget;
227 struct sof_ipc4_pipeline *pipeline;
228 struct snd_sof_widget *swidget;
229 struct snd_soc_dapm_widget *w;
232 w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
233 swidget = w->dobj.private;
234 pipe_widget = swidget->spipe->pipe_widget;
235 pipeline = pipe_widget->private;
237 mutex_lock(&ipc4_data->pipeline_state_mutex);
240 case SNDRV_PCM_TRIGGER_START:
241 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
243 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
244 case SNDRV_PCM_TRIGGER_SUSPEND:
245 case SNDRV_PCM_TRIGGER_STOP:
246 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
247 SOF_IPC4_PIPE_PAUSED);
251 pipeline->state = SOF_IPC4_PIPE_PAUSED;
254 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
258 mutex_unlock(&ipc4_data->pipeline_state_mutex);
262 static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
263 struct snd_pcm_substream *substream, int cmd)
265 struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
268 case SNDRV_PCM_TRIGGER_START:
269 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
270 snd_hdac_ext_stream_start(hext_stream);
272 case SNDRV_PCM_TRIGGER_SUSPEND:
273 case SNDRV_PCM_TRIGGER_STOP:
274 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
275 snd_hdac_ext_stream_clear(hext_stream);
278 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
285 static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
286 struct snd_pcm_substream *substream, int cmd)
288 struct sof_ipc4_fw_data *ipc4_data = sdev->private;
289 struct snd_sof_widget *pipe_widget;
290 struct sof_ipc4_pipeline *pipeline;
291 struct snd_sof_widget *swidget;
292 struct snd_soc_dapm_widget *w;
295 w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
296 swidget = w->dobj.private;
297 pipe_widget = swidget->spipe->pipe_widget;
298 pipeline = pipe_widget->private;
300 mutex_lock(&ipc4_data->pipeline_state_mutex);
303 case SNDRV_PCM_TRIGGER_START:
304 if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
305 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
306 SOF_IPC4_PIPE_PAUSED);
309 pipeline->state = SOF_IPC4_PIPE_PAUSED;
312 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
313 SOF_IPC4_PIPE_RUNNING);
316 pipeline->state = SOF_IPC4_PIPE_RUNNING;
317 swidget->spipe->started_count++;
319 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
320 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
321 SOF_IPC4_PIPE_RUNNING);
324 pipeline->state = SOF_IPC4_PIPE_RUNNING;
326 case SNDRV_PCM_TRIGGER_SUSPEND:
327 case SNDRV_PCM_TRIGGER_STOP:
329 * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have
330 * been stopped. So, clear the started_count so that the pipeline can be reset
332 swidget->spipe->started_count = 0;
334 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
337 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
342 mutex_unlock(&ipc4_data->pipeline_state_mutex);
346 static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
347 .get_hext_stream = hda_ipc4_get_hext_stream,
348 .assign_hext_stream = hda_assign_hext_stream,
349 .release_hext_stream = hda_release_hext_stream,
350 .setup_hext_stream = hda_setup_hext_stream,
351 .reset_hext_stream = hda_reset_hext_stream,
352 .pre_trigger = hda_ipc4_pre_trigger,
353 .trigger = hda_trigger,
354 .post_trigger = hda_ipc4_post_trigger,
355 .codec_dai_set_stream = hda_codec_dai_set_stream,
356 .calc_stream_format = hda_calc_stream_format,
357 .get_hlink = hda_get_hlink,
360 static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
361 .get_hext_stream = hda_get_hext_stream,
362 .assign_hext_stream = hda_assign_hext_stream,
363 .release_hext_stream = hda_release_hext_stream,
364 .setup_hext_stream = hda_setup_hext_stream,
365 .reset_hext_stream = hda_reset_hext_stream,
366 .trigger = hda_trigger,
367 .codec_dai_set_stream = hda_codec_dai_set_stream,
368 .calc_stream_format = hda_calc_stream_format,
369 .get_hlink = hda_get_hlink,
372 static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
373 struct snd_pcm_substream *substream, int cmd)
375 struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
376 struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
379 case SNDRV_PCM_TRIGGER_SUSPEND:
380 case SNDRV_PCM_TRIGGER_STOP:
382 struct snd_sof_dai_config_data data = { 0 };
385 data.dai_data = DMA_CHAN_INVALID;
386 ret = hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
390 if (cmd == SNDRV_PCM_TRIGGER_STOP)
391 return hda_link_dma_cleanup(substream, hext_stream, cpu_dai);
395 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
396 return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
404 static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
405 .get_hext_stream = hda_get_hext_stream,
406 .assign_hext_stream = hda_assign_hext_stream,
407 .release_hext_stream = hda_release_hext_stream,
408 .setup_hext_stream = hda_setup_hext_stream,
409 .reset_hext_stream = hda_reset_hext_stream,
410 .trigger = hda_trigger,
411 .post_trigger = hda_ipc3_post_trigger,
412 .codec_dai_set_stream = hda_codec_dai_set_stream,
413 .calc_stream_format = hda_calc_stream_format,
414 .get_hlink = hda_get_hlink,
417 static struct hdac_ext_stream *
418 hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
419 struct snd_pcm_substream *substream)
421 struct hdac_stream *hstream = substream->runtime->private_data;
423 return stream_to_hdac_ext_stream(hstream);
426 static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
427 struct hdac_ext_stream *hext_stream,
428 unsigned int format_val)
431 * Save the format_val which was adjusted by the maxbps of the codec.
432 * This information is not available on the FE side since there we are
435 hext_stream->hstream.format_val = format_val;
438 static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
439 .get_hext_stream = hda_dspless_get_hext_stream,
440 .setup_hext_stream = hda_dspless_setup_hext_stream,
441 .codec_dai_set_stream = hda_codec_dai_set_stream,
442 .calc_stream_format = hda_calc_stream_format,
443 .get_hlink = hda_get_hlink,
448 const struct hda_dai_widget_dma_ops *
449 hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
451 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
452 struct snd_sof_dai *sdai;
454 if (sdev->dspless_mode_selected)
455 return &hda_dspless_dma_ops;
457 sdai = swidget->private;
459 switch (sdev->pdata->ipc_type) {
462 struct sof_dai_private_data *private = sdai->private;
464 if (private->dai_config->type == SOF_DAI_INTEL_HDA)
465 return &hda_ipc3_dma_ops;
470 struct sof_ipc4_copier *ipc4_copier = sdai->private;
472 if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) {
473 struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
474 struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
476 if (pipeline->use_chain_dma)
477 return &hda_ipc4_chain_dma_ops;
479 return &hda_ipc4_dma_ops;