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_AUDIO_CODEC)
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_assign_hext_stream(struct snd_sof_dev *sdev,
124 struct snd_soc_dai *cpu_dai,
125 struct snd_pcm_substream *substream)
127 struct hdac_ext_stream *hext_stream;
129 hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
133 snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
138 static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
139 struct snd_pcm_substream *substream)
141 struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
143 snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
144 snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
147 static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
148 unsigned int format_val)
150 snd_hdac_ext_stream_setup(hext_stream, format_val);
153 static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
155 snd_hdac_ext_stream_reset(hext_stream);
158 static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
159 struct snd_pcm_substream *substream, int cmd)
161 struct snd_sof_widget *pipe_widget;
162 struct sof_ipc4_pipeline *pipeline;
163 struct snd_sof_widget *swidget;
164 struct snd_soc_dapm_widget *w;
167 w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
168 swidget = w->dobj.private;
169 pipe_widget = swidget->spipe->pipe_widget;
170 pipeline = pipe_widget->private;
173 case SNDRV_PCM_TRIGGER_START:
174 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
176 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
177 case SNDRV_PCM_TRIGGER_SUSPEND:
178 case SNDRV_PCM_TRIGGER_STOP:
179 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
180 SOF_IPC4_PIPE_PAUSED);
184 pipeline->state = SOF_IPC4_PIPE_PAUSED;
187 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
194 static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
195 struct snd_pcm_substream *substream, int cmd)
197 struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
200 case SNDRV_PCM_TRIGGER_START:
201 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
202 snd_hdac_ext_stream_start(hext_stream);
204 case SNDRV_PCM_TRIGGER_SUSPEND:
205 case SNDRV_PCM_TRIGGER_STOP:
206 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
207 snd_hdac_ext_stream_clear(hext_stream);
210 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
217 static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
218 struct snd_pcm_substream *substream, int cmd)
220 struct snd_sof_widget *pipe_widget;
221 struct sof_ipc4_pipeline *pipeline;
222 struct snd_sof_widget *swidget;
223 struct snd_soc_dapm_widget *w;
226 w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
227 swidget = w->dobj.private;
228 pipe_widget = swidget->spipe->pipe_widget;
229 pipeline = pipe_widget->private;
232 case SNDRV_PCM_TRIGGER_START:
233 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
234 if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
235 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
236 SOF_IPC4_PIPE_PAUSED);
239 pipeline->state = SOF_IPC4_PIPE_PAUSED;
242 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
243 SOF_IPC4_PIPE_RUNNING);
246 pipeline->state = SOF_IPC4_PIPE_RUNNING;
248 case SNDRV_PCM_TRIGGER_SUSPEND:
249 case SNDRV_PCM_TRIGGER_STOP:
251 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
252 SOF_IPC4_PIPE_RESET);
256 pipeline->state = SOF_IPC4_PIPE_RESET;
259 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
262 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
269 static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
270 .get_hext_stream = hda_get_hext_stream,
271 .assign_hext_stream = hda_assign_hext_stream,
272 .release_hext_stream = hda_release_hext_stream,
273 .setup_hext_stream = hda_setup_hext_stream,
274 .reset_hext_stream = hda_reset_hext_stream,
275 .pre_trigger = hda_ipc4_pre_trigger,
276 .trigger = hda_trigger,
277 .post_trigger = hda_ipc4_post_trigger
280 static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
281 .get_hext_stream = hda_get_hext_stream,
282 .assign_hext_stream = hda_assign_hext_stream,
283 .release_hext_stream = hda_release_hext_stream,
284 .setup_hext_stream = hda_setup_hext_stream,
285 .reset_hext_stream = hda_reset_hext_stream,
286 .trigger = hda_trigger,
289 static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
290 struct snd_pcm_substream *substream, int cmd)
292 struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
295 case SNDRV_PCM_TRIGGER_SUSPEND:
296 case SNDRV_PCM_TRIGGER_STOP:
298 struct snd_sof_dai_config_data data = { 0 };
300 data.dai_data = DMA_CHAN_INVALID;
301 return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
303 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
304 return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
312 static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
313 .get_hext_stream = hda_get_hext_stream,
314 .assign_hext_stream = hda_assign_hext_stream,
315 .release_hext_stream = hda_release_hext_stream,
316 .setup_hext_stream = hda_setup_hext_stream,
317 .reset_hext_stream = hda_reset_hext_stream,
318 .trigger = hda_trigger,
319 .post_trigger = hda_ipc3_post_trigger,
322 static struct hdac_ext_stream *
323 hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
324 struct snd_pcm_substream *substream)
326 struct hdac_stream *hstream = substream->runtime->private_data;
328 return stream_to_hdac_ext_stream(hstream);
331 static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
332 struct hdac_ext_stream *hext_stream,
333 unsigned int format_val)
336 * Save the format_val which was adjusted by the maxbps of the codec.
337 * This information is not available on the FE side since there we are
340 hext_stream->hstream.format_val = format_val;
343 static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
344 .get_hext_stream = hda_dspless_get_hext_stream,
345 .setup_hext_stream = hda_dspless_setup_hext_stream,
350 const struct hda_dai_widget_dma_ops *
351 hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
353 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
354 struct snd_sof_dai *sdai;
356 if (sdev->dspless_mode_selected)
357 return &hda_dspless_dma_ops;
359 sdai = swidget->private;
361 switch (sdev->pdata->ipc_type) {
364 struct sof_dai_private_data *private = sdai->private;
366 if (private->dai_config->type == SOF_DAI_INTEL_HDA)
367 return &hda_ipc3_dma_ops;
372 struct sof_ipc4_copier *ipc4_copier = sdai->private;
374 if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) {
375 struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
376 struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
378 if (pipeline->use_chain_dma)
379 return &hda_ipc4_chain_dma_ops;
381 return &hda_ipc4_dma_ops;