]> Git Repo - linux.git/blob - sound/soc/sof/intel/hda-dai-ops.c
Merge tag 'amd-drm-next-6.5-2023-06-09' of https://gitlab.freedesktop.org/agd5f/linux...
[linux.git] / sound / soc / sof / intel / hda-dai-ops.c
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2 //
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.
5 //
6 // Copyright(c) 2022 Intel Corporation. All rights reserved.
7
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"
16 #include "hda.h"
17
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)
20 /*
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.
24  */
25 static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
26                           int dir, int stream_tag)
27 {
28         struct snd_pcm_substream *fe_substream;
29         struct hdac_stream *fe_hstream;
30         struct snd_soc_dpcm *dpcm;
31
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)
36                         return true;
37         }
38
39         return false;
40 }
41
42 static struct hdac_ext_stream *
43 hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream)
44 {
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;
51
52         int stream_dir = substream->stream;
53
54         if (!bus->ppcap) {
55                 dev_err(bus->dev, "stream type not supported\n");
56                 return NULL;
57         }
58
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)
64                         continue;
65
66                 hda_stream = hstream_to_sof_hda_stream(hext_stream);
67                 sdev = hda_stream->sdev;
68                 chip = get_chip_info(sdev->pdata);
69
70                 /* check if link is available */
71                 if (!hext_stream->link_locked) {
72                         /*
73                          * choose the first available link for platforms that do not have the
74                          * PROCEN_FMT_QUIRK set.
75                          */
76                         if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
77                                 res = hext_stream;
78                                 break;
79                         }
80
81                         if (hstream->opened) {
82                                 /*
83                                  * check if the stream tag matches the stream
84                                  * tag of one of the connected FEs
85                                  */
86                                 if (hda_check_fes(rtd, stream_dir,
87                                                   hstream->stream_tag)) {
88                                         res = hext_stream;
89                                         break;
90                                 }
91                         } else {
92                                 res = hext_stream;
93
94                                 /*
95                                  * This must be a hostless stream.
96                                  * So reserve the host DMA channel.
97                                  */
98                                 hda_stream->host_reserved = 1;
99                                 break;
100                         }
101                 }
102         }
103
104         if (res) {
105                 /* Make sure that host and link DMA is decoupled. */
106                 snd_hdac_ext_stream_decouple_locked(bus, res, true);
107
108                 res->link_locked = 1;
109                 res->link_substream = substream;
110         }
111         spin_unlock_irq(&bus->reg_lock);
112
113         return res;
114 }
115
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)
119 {
120         return snd_soc_dai_get_dma_data(cpu_dai, substream);
121 }
122
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)
126 {
127         struct hdac_ext_stream *hext_stream;
128
129         hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
130         if (!hext_stream)
131                 return NULL;
132
133         snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
134
135         return hext_stream;
136 }
137
138 static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
139                                     struct snd_pcm_substream *substream)
140 {
141         struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
142
143         snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
144         snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
145 }
146
147 static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
148                                   unsigned int format_val)
149 {
150         snd_hdac_ext_stream_setup(hext_stream, format_val);
151 }
152
153 static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
154 {
155         snd_hdac_ext_stream_reset(hext_stream);
156 }
157
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)
160 {
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;
165         int ret;
166
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;
171
172         switch (cmd) {
173         case SNDRV_PCM_TRIGGER_START:
174         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
175                 break;
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);
181                 if (ret < 0)
182                         return ret;
183
184                 pipeline->state = SOF_IPC4_PIPE_PAUSED;
185                 break;
186         default:
187                 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
188                 return -EINVAL;
189         }
190
191         return 0;
192 }
193
194 static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
195                        struct snd_pcm_substream *substream, int cmd)
196 {
197         struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
198
199         switch (cmd) {
200         case SNDRV_PCM_TRIGGER_START:
201         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
202                 snd_hdac_ext_stream_start(hext_stream);
203                 break;
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);
208                 break;
209         default:
210                 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
211                 return -EINVAL;
212         }
213
214         return 0;
215 }
216
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)
219 {
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;
224         int ret;
225
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;
230
231         switch (cmd) {
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);
237                         if (ret < 0)
238                                 return ret;
239                         pipeline->state = SOF_IPC4_PIPE_PAUSED;
240                 }
241
242                 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
243                                                   SOF_IPC4_PIPE_RUNNING);
244                 if (ret < 0)
245                         return ret;
246                 pipeline->state = SOF_IPC4_PIPE_RUNNING;
247                 break;
248         case SNDRV_PCM_TRIGGER_SUSPEND:
249         case SNDRV_PCM_TRIGGER_STOP:
250         {
251                 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
252                                                   SOF_IPC4_PIPE_RESET);
253                 if (ret < 0)
254                         return ret;
255
256                 pipeline->state = SOF_IPC4_PIPE_RESET;
257                 break;
258         }
259         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
260                 break;
261         default:
262                 dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
263                 return -EINVAL;
264         }
265
266         return 0;
267 }
268
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
278 };
279
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,
287 };
288
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)
291 {
292         struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
293
294         switch (cmd) {
295         case SNDRV_PCM_TRIGGER_SUSPEND:
296         case SNDRV_PCM_TRIGGER_STOP:
297         {
298                 struct snd_sof_dai_config_data data = { 0 };
299
300                 data.dai_data = DMA_CHAN_INVALID;
301                 return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
302         }
303         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
304                 return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
305         default:
306                 break;
307         }
308
309         return 0;
310 }
311
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,
320 };
321
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)
325 {
326         struct hdac_stream *hstream = substream->runtime->private_data;
327
328         return stream_to_hdac_ext_stream(hstream);
329 }
330
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)
334 {
335         /*
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
338          * using dummy_codec.
339          */
340         hext_stream->hstream.format_val = format_val;
341 }
342
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,
346 };
347
348 #endif
349
350 const struct hda_dai_widget_dma_ops *
351 hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
352 {
353 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
354         struct snd_sof_dai *sdai;
355
356         if (sdev->dspless_mode_selected)
357                 return &hda_dspless_dma_ops;
358
359         sdai = swidget->private;
360
361         switch (sdev->pdata->ipc_type) {
362         case SOF_IPC:
363         {
364                 struct sof_dai_private_data *private = sdai->private;
365
366                 if (private->dai_config->type == SOF_DAI_INTEL_HDA)
367                         return &hda_ipc3_dma_ops;
368                 break;
369         }
370         case SOF_INTEL_IPC4:
371         {
372                 struct sof_ipc4_copier *ipc4_copier = sdai->private;
373
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;
377
378                         if (pipeline->use_chain_dma)
379                                 return &hda_ipc4_chain_dma_ops;
380
381                         return &hda_ipc4_dma_ops;
382                 }
383                 break;
384         }
385         default:
386                 break;
387         }
388 #endif
389         return NULL;
390 }
This page took 0.058686 seconds and 4 git commands to generate.