xref: /linux/sound/soc/sof/intel/hda-dai-ops.c (revision 2f3092e77f98fcfc0d653846591401bfe2a5232e)
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_LINK)
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_ipc4_get_hext_stream(struct snd_sof_dev *sdev,
124 							struct snd_soc_dai *cpu_dai,
125 							struct snd_pcm_substream *substream)
126 {
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;
131 
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;
136 
137 	/* mark pipeline so that it can be skipped during FE trigger */
138 	pipeline->skip_during_fe_trigger = true;
139 
140 	return snd_soc_dai_get_dma_data(cpu_dai, substream);
141 }
142 
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)
146 {
147 	struct hdac_ext_stream *hext_stream;
148 
149 	hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
150 	if (!hext_stream)
151 		return NULL;
152 
153 	snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
154 
155 	return hext_stream;
156 }
157 
158 static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
159 				    struct snd_pcm_substream *substream)
160 {
161 	struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
162 
163 	snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
164 	snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
165 }
166 
167 static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
168 				  unsigned int format_val)
169 {
170 	snd_hdac_ext_stream_setup(hext_stream, format_val);
171 }
172 
173 static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
174 {
175 	snd_hdac_ext_stream_reset(hext_stream);
176 }
177 
178 static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
179 				struct snd_pcm_substream *substream, int cmd)
180 {
181 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
182 	struct snd_sof_widget *pipe_widget;
183 	struct sof_ipc4_pipeline *pipeline;
184 	struct snd_sof_widget *swidget;
185 	struct snd_soc_dapm_widget *w;
186 	int ret;
187 
188 	w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
189 	swidget = w->dobj.private;
190 	pipe_widget = swidget->spipe->pipe_widget;
191 	pipeline = pipe_widget->private;
192 
193 	mutex_lock(&ipc4_data->pipeline_state_mutex);
194 
195 	switch (cmd) {
196 	case SNDRV_PCM_TRIGGER_START:
197 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
198 		break;
199 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
200 	case SNDRV_PCM_TRIGGER_SUSPEND:
201 	case SNDRV_PCM_TRIGGER_STOP:
202 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
203 						  SOF_IPC4_PIPE_PAUSED);
204 		if (ret < 0)
205 			goto out;
206 
207 		pipeline->state = SOF_IPC4_PIPE_PAUSED;
208 		break;
209 	default:
210 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
211 		return -EINVAL;
212 	}
213 out:
214 	mutex_unlock(&ipc4_data->pipeline_state_mutex);
215 	return 0;
216 }
217 
218 static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
219 		       struct snd_pcm_substream *substream, int cmd)
220 {
221 	struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
222 
223 	switch (cmd) {
224 	case SNDRV_PCM_TRIGGER_START:
225 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
226 		snd_hdac_ext_stream_start(hext_stream);
227 		break;
228 	case SNDRV_PCM_TRIGGER_SUSPEND:
229 	case SNDRV_PCM_TRIGGER_STOP:
230 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
231 		snd_hdac_ext_stream_clear(hext_stream);
232 		break;
233 	default:
234 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
235 		return -EINVAL;
236 	}
237 
238 	return 0;
239 }
240 
241 static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
242 				 struct snd_pcm_substream *substream, int cmd)
243 {
244 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
245 	struct snd_sof_widget *pipe_widget;
246 	struct sof_ipc4_pipeline *pipeline;
247 	struct snd_sof_widget *swidget;
248 	struct snd_soc_dapm_widget *w;
249 	int ret = 0;
250 
251 	w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
252 	swidget = w->dobj.private;
253 	pipe_widget = swidget->spipe->pipe_widget;
254 	pipeline = pipe_widget->private;
255 
256 	mutex_lock(&ipc4_data->pipeline_state_mutex);
257 
258 	switch (cmd) {
259 	case SNDRV_PCM_TRIGGER_START:
260 		if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
261 			ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
262 							  SOF_IPC4_PIPE_PAUSED);
263 			if (ret < 0)
264 				goto out;
265 			pipeline->state = SOF_IPC4_PIPE_PAUSED;
266 		}
267 
268 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
269 						  SOF_IPC4_PIPE_RUNNING);
270 		if (ret < 0)
271 			goto out;
272 		pipeline->state = SOF_IPC4_PIPE_RUNNING;
273 		swidget->spipe->started_count++;
274 		break;
275 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
276 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
277 						  SOF_IPC4_PIPE_RUNNING);
278 		if (ret < 0)
279 			goto out;
280 		pipeline->state = SOF_IPC4_PIPE_RUNNING;
281 		break;
282 	case SNDRV_PCM_TRIGGER_SUSPEND:
283 	case SNDRV_PCM_TRIGGER_STOP:
284 		/*
285 		 * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have
286 		 * been stopped. So, clear the started_count so that the pipeline can be reset
287 		 */
288 		swidget->spipe->started_count = 0;
289 		break;
290 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
291 		break;
292 	default:
293 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
294 		ret = -EINVAL;
295 		break;
296 	}
297 out:
298 	mutex_unlock(&ipc4_data->pipeline_state_mutex);
299 	return ret;
300 }
301 
302 static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
303 	.get_hext_stream = hda_ipc4_get_hext_stream,
304 	.assign_hext_stream = hda_assign_hext_stream,
305 	.release_hext_stream = hda_release_hext_stream,
306 	.setup_hext_stream = hda_setup_hext_stream,
307 	.reset_hext_stream = hda_reset_hext_stream,
308 	.pre_trigger = hda_ipc4_pre_trigger,
309 	.trigger = hda_trigger,
310 	.post_trigger = hda_ipc4_post_trigger
311 };
312 
313 static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
314 	.get_hext_stream = hda_get_hext_stream,
315 	.assign_hext_stream = hda_assign_hext_stream,
316 	.release_hext_stream = hda_release_hext_stream,
317 	.setup_hext_stream = hda_setup_hext_stream,
318 	.reset_hext_stream = hda_reset_hext_stream,
319 	.trigger = hda_trigger,
320 };
321 
322 static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
323 				 struct snd_pcm_substream *substream, int cmd)
324 {
325 	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
326 
327 	switch (cmd) {
328 	case SNDRV_PCM_TRIGGER_SUSPEND:
329 	case SNDRV_PCM_TRIGGER_STOP:
330 	{
331 		struct snd_sof_dai_config_data data = { 0 };
332 
333 		data.dai_data = DMA_CHAN_INVALID;
334 		return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
335 	}
336 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
337 		return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
338 	default:
339 		break;
340 	}
341 
342 	return 0;
343 }
344 
345 static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
346 	.get_hext_stream = hda_get_hext_stream,
347 	.assign_hext_stream = hda_assign_hext_stream,
348 	.release_hext_stream = hda_release_hext_stream,
349 	.setup_hext_stream = hda_setup_hext_stream,
350 	.reset_hext_stream = hda_reset_hext_stream,
351 	.trigger = hda_trigger,
352 	.post_trigger = hda_ipc3_post_trigger,
353 };
354 
355 static struct hdac_ext_stream *
356 hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
357 			    struct snd_pcm_substream *substream)
358 {
359 	struct hdac_stream *hstream = substream->runtime->private_data;
360 
361 	return stream_to_hdac_ext_stream(hstream);
362 }
363 
364 static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
365 					  struct hdac_ext_stream *hext_stream,
366 					  unsigned int format_val)
367 {
368 	/*
369 	 * Save the format_val which was adjusted by the maxbps of the codec.
370 	 * This information is not available on the FE side since there we are
371 	 * using dummy_codec.
372 	 */
373 	hext_stream->hstream.format_val = format_val;
374 }
375 
376 static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
377 	.get_hext_stream = hda_dspless_get_hext_stream,
378 	.setup_hext_stream = hda_dspless_setup_hext_stream,
379 };
380 
381 #endif
382 
383 const struct hda_dai_widget_dma_ops *
384 hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
385 {
386 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
387 	struct snd_sof_dai *sdai;
388 
389 	if (sdev->dspless_mode_selected)
390 		return &hda_dspless_dma_ops;
391 
392 	sdai = swidget->private;
393 
394 	switch (sdev->pdata->ipc_type) {
395 	case SOF_IPC:
396 	{
397 		struct sof_dai_private_data *private = sdai->private;
398 
399 		if (private->dai_config->type == SOF_DAI_INTEL_HDA)
400 			return &hda_ipc3_dma_ops;
401 		break;
402 	}
403 	case SOF_INTEL_IPC4:
404 	{
405 		struct sof_ipc4_copier *ipc4_copier = sdai->private;
406 
407 		if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) {
408 			struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
409 			struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
410 
411 			if (pipeline->use_chain_dma)
412 				return &hda_ipc4_chain_dma_ops;
413 
414 			return &hda_ipc4_dma_ops;
415 		}
416 		break;
417 	}
418 	default:
419 		break;
420 	}
421 #endif
422 	return NULL;
423 }
424