xref: /openbmc/linux/sound/soc/sof/intel/hda-dai-ops.c (revision 3d40aed8)
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 void hda_codec_dai_set_stream(struct snd_sof_dev *sdev,
179 				     struct snd_pcm_substream *substream,
180 				     struct hdac_stream *hstream)
181 {
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);
184 
185 	/* set the hdac_stream in the codec dai */
186 	snd_soc_dai_set_stream(codec_dai, hstream, substream->stream);
187 }
188 
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)
192 {
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;
197 
198 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
199 		link_bps = codec_dai->driver->playback.sig_bits;
200 	else
201 		link_bps = codec_dai->driver->capture.sig_bits;
202 
203 	format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
204 						 params_format(params), link_bps, 0);
205 
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));
208 
209 	return format_val;
210 }
211 
212 static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev,
213 					   struct snd_pcm_substream *substream)
214 {
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);
218 
219 	return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
220 }
221 
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)
224 {
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;
230 	int ret = 0;
231 
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;
236 
237 	mutex_lock(&ipc4_data->pipeline_state_mutex);
238 
239 	switch (cmd) {
240 	case SNDRV_PCM_TRIGGER_START:
241 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
242 		break;
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);
248 		if (ret < 0)
249 			goto out;
250 
251 		pipeline->state = SOF_IPC4_PIPE_PAUSED;
252 		break;
253 	default:
254 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
255 		ret = -EINVAL;
256 	}
257 out:
258 	mutex_unlock(&ipc4_data->pipeline_state_mutex);
259 	return ret;
260 }
261 
262 static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
263 		       struct snd_pcm_substream *substream, int cmd)
264 {
265 	struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
266 
267 	switch (cmd) {
268 	case SNDRV_PCM_TRIGGER_START:
269 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
270 		snd_hdac_ext_stream_start(hext_stream);
271 		break;
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);
276 		break;
277 	default:
278 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
279 		return -EINVAL;
280 	}
281 
282 	return 0;
283 }
284 
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)
287 {
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;
293 	int ret = 0;
294 
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;
299 
300 	mutex_lock(&ipc4_data->pipeline_state_mutex);
301 
302 	switch (cmd) {
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);
307 			if (ret < 0)
308 				goto out;
309 			pipeline->state = SOF_IPC4_PIPE_PAUSED;
310 		}
311 
312 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
313 						  SOF_IPC4_PIPE_RUNNING);
314 		if (ret < 0)
315 			goto out;
316 		pipeline->state = SOF_IPC4_PIPE_RUNNING;
317 		swidget->spipe->started_count++;
318 		break;
319 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
320 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
321 						  SOF_IPC4_PIPE_RUNNING);
322 		if (ret < 0)
323 			goto out;
324 		pipeline->state = SOF_IPC4_PIPE_RUNNING;
325 		break;
326 	case SNDRV_PCM_TRIGGER_SUSPEND:
327 	case SNDRV_PCM_TRIGGER_STOP:
328 		/*
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
331 		 */
332 		swidget->spipe->started_count = 0;
333 		break;
334 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
335 		break;
336 	default:
337 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
338 		ret = -EINVAL;
339 		break;
340 	}
341 out:
342 	mutex_unlock(&ipc4_data->pipeline_state_mutex);
343 	return ret;
344 }
345 
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,
358 };
359 
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,
370 };
371 
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)
374 {
375 	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
376 
377 	switch (cmd) {
378 	case SNDRV_PCM_TRIGGER_SUSPEND:
379 	case SNDRV_PCM_TRIGGER_STOP:
380 	{
381 		struct snd_sof_dai_config_data data = { 0 };
382 
383 		data.dai_data = DMA_CHAN_INVALID;
384 		return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
385 	}
386 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
387 		return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
388 	default:
389 		break;
390 	}
391 
392 	return 0;
393 }
394 
395 static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
396 	.get_hext_stream = hda_get_hext_stream,
397 	.assign_hext_stream = hda_assign_hext_stream,
398 	.release_hext_stream = hda_release_hext_stream,
399 	.setup_hext_stream = hda_setup_hext_stream,
400 	.reset_hext_stream = hda_reset_hext_stream,
401 	.trigger = hda_trigger,
402 	.post_trigger = hda_ipc3_post_trigger,
403 	.codec_dai_set_stream = hda_codec_dai_set_stream,
404 	.calc_stream_format = hda_calc_stream_format,
405 	.get_hlink = hda_get_hlink,
406 };
407 
408 static struct hdac_ext_stream *
409 hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
410 			    struct snd_pcm_substream *substream)
411 {
412 	struct hdac_stream *hstream = substream->runtime->private_data;
413 
414 	return stream_to_hdac_ext_stream(hstream);
415 }
416 
417 static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
418 					  struct hdac_ext_stream *hext_stream,
419 					  unsigned int format_val)
420 {
421 	/*
422 	 * Save the format_val which was adjusted by the maxbps of the codec.
423 	 * This information is not available on the FE side since there we are
424 	 * using dummy_codec.
425 	 */
426 	hext_stream->hstream.format_val = format_val;
427 }
428 
429 static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
430 	.get_hext_stream = hda_dspless_get_hext_stream,
431 	.setup_hext_stream = hda_dspless_setup_hext_stream,
432 	.codec_dai_set_stream = hda_codec_dai_set_stream,
433 	.calc_stream_format = hda_calc_stream_format,
434 	.get_hlink = hda_get_hlink,
435 };
436 
437 #endif
438 
439 const struct hda_dai_widget_dma_ops *
440 hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
441 {
442 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
443 	struct snd_sof_dai *sdai;
444 
445 	if (sdev->dspless_mode_selected)
446 		return &hda_dspless_dma_ops;
447 
448 	sdai = swidget->private;
449 
450 	switch (sdev->pdata->ipc_type) {
451 	case SOF_IPC:
452 	{
453 		struct sof_dai_private_data *private = sdai->private;
454 
455 		if (private->dai_config->type == SOF_DAI_INTEL_HDA)
456 			return &hda_ipc3_dma_ops;
457 		break;
458 	}
459 	case SOF_INTEL_IPC4:
460 	{
461 		struct sof_ipc4_copier *ipc4_copier = sdai->private;
462 
463 		if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) {
464 			struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
465 			struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
466 
467 			if (pipeline->use_chain_dma)
468 				return &hda_ipc4_chain_dma_ops;
469 
470 			return &hda_ipc4_dma_ops;
471 		}
472 		break;
473 	}
474 	default:
475 		break;
476 	}
477 #endif
478 	return NULL;
479 }
480