xref: /openbmc/linux/sound/soc/sof/ipc4-pcm.c (revision c765ceda78f0bd9df1217f9beaefea58ecf3865c)
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 
9 #include <sound/pcm_params.h>
10 #include <sound/sof/ipc4/header.h>
11 #include "sof-audio.h"
12 #include "sof-priv.h"
13 #include "ipc4-priv.h"
14 #include "ipc4-topology.h"
15 
16 static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state,
17 					     struct ipc4_pipeline_set_state_data *trigger_list)
18 {
19 	struct sof_ipc4_msg msg = {{ 0 }};
20 	u32 primary, ipc_size;
21 
22 	/* trigger a single pipeline */
23 	if (trigger_list->count == 1)
24 		return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_ids[0], state);
25 
26 	primary = state;
27 	primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE);
28 	primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
29 	primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
30 	msg.primary = primary;
31 
32 	/* trigger multiple pipelines with a single IPC */
33 	msg.extension = SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI;
34 
35 	/* ipc_size includes the count and the pipeline IDs for the number of pipelines */
36 	ipc_size = sizeof(u32) * (trigger_list->count + 1);
37 	msg.data_size = ipc_size;
38 	msg.data_ptr = trigger_list;
39 
40 	return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0);
41 }
42 
43 int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
44 {
45 	struct sof_ipc4_msg msg = {{ 0 }};
46 	u32 primary;
47 
48 	dev_dbg(sdev->dev, "ipc4 set pipeline %d state %d", id, state);
49 
50 	primary = state;
51 	primary |= SOF_IPC4_GLB_PIPE_STATE_ID(id);
52 	primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE);
53 	primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
54 	primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
55 
56 	msg.primary = primary;
57 
58 	return sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
59 }
60 EXPORT_SYMBOL(sof_ipc4_set_pipeline_state);
61 
62 static void
63 sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state,
64 				      struct snd_sof_pipeline *spipe,
65 				      struct ipc4_pipeline_set_state_data *trigger_list)
66 {
67 	struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
68 	struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
69 
70 	if (pipeline->skip_during_fe_trigger)
71 		return;
72 
73 	switch (state) {
74 	case SOF_IPC4_PIPE_RUNNING:
75 		/*
76 		 * Trigger pipeline if all PCMs containing it are paused or if it is RUNNING
77 		 * for the first time
78 		 */
79 		if (spipe->started_count == spipe->paused_count)
80 			trigger_list->pipeline_ids[trigger_list->count++] =
81 				pipe_widget->instance_id;
82 		break;
83 	case SOF_IPC4_PIPE_RESET:
84 		/* RESET if the pipeline is neither running nor paused */
85 		if (!spipe->started_count && !spipe->paused_count)
86 			trigger_list->pipeline_ids[trigger_list->count++] =
87 				pipe_widget->instance_id;
88 		break;
89 	case SOF_IPC4_PIPE_PAUSED:
90 		/* Pause the pipeline only when its started_count is 1 more than paused_count */
91 		if (spipe->paused_count == (spipe->started_count - 1))
92 			trigger_list->pipeline_ids[trigger_list->count++] =
93 				pipe_widget->instance_id;
94 		break;
95 	default:
96 		break;
97 	}
98 }
99 
100 static void
101 sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
102 			       struct snd_sof_pipeline *spipe,
103 			       struct ipc4_pipeline_set_state_data *trigger_list)
104 {
105 	struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
106 	struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
107 	int i;
108 
109 	if (pipeline->skip_during_fe_trigger)
110 		return;
111 
112 	/* set state for pipeline if it was just triggered */
113 	for (i = 0; i < trigger_list->count; i++) {
114 		if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) {
115 			pipeline->state = state;
116 			break;
117 		}
118 	}
119 
120 	switch (state) {
121 	case SOF_IPC4_PIPE_PAUSED:
122 		switch (cmd) {
123 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
124 			/*
125 			 * increment paused_count if the PAUSED is the final state during
126 			 * the PAUSE trigger
127 			 */
128 			spipe->paused_count++;
129 			break;
130 		case SNDRV_PCM_TRIGGER_STOP:
131 		case SNDRV_PCM_TRIGGER_SUSPEND:
132 			/*
133 			 * decrement started_count if PAUSED is the final state during the
134 			 * STOP trigger
135 			 */
136 			spipe->started_count--;
137 			break;
138 		default:
139 			break;
140 		}
141 		break;
142 	case SOF_IPC4_PIPE_RUNNING:
143 		switch (cmd) {
144 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
145 			/* decrement paused_count for RELEASE */
146 			spipe->paused_count--;
147 			break;
148 		case SNDRV_PCM_TRIGGER_START:
149 		case SNDRV_PCM_TRIGGER_RESUME:
150 			/* increment started_count for START/RESUME */
151 			spipe->started_count++;
152 			break;
153 		default:
154 			break;
155 		}
156 		break;
157 	default:
158 		break;
159 	}
160 }
161 
162 /*
163  * The picture below represents the pipeline state machine wrt PCM actions corresponding to the
164  * triggers and ioctls
165  *				+---------------+
166  *				|               |
167  *				|    INIT       |
168  *				|               |
169  *				+-------+-------+
170  *					|
171  *					|
172  *					| START
173  *					|
174  *					|
175  * +----------------+		   +------v-------+		  +-------------+
176  * |                |   START     |              |   HW_FREE	  |             |
177  * |   RUNNING      <-------------+  PAUSED      +--------------> +   RESET     |
178  * |                |   PAUSE     |              |		  |             |
179  * +------+---------+   RELEASE   +---------+----+		  +-------------+
180  *	  |				     ^
181  *	  |				     |
182  *	  |				     |
183  *	  |				     |
184  *	  |		PAUSE		     |
185  *	  +---------------------------------+
186  *			STOP/SUSPEND
187  *
188  * Note that during system suspend, the suspend trigger is followed by a hw_free in
189  * sof_pcm_trigger(). So, the final state during suspend would be RESET.
190  * Also, since the SOF driver doesn't support full resume, streams would be restarted with the
191  * prepare ioctl before the START trigger.
192  */
193 
194 static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
195 				      struct snd_pcm_substream *substream, int state, int cmd)
196 {
197 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
198 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
199 	struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
200 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
201 	struct ipc4_pipeline_set_state_data *trigger_list;
202 	struct snd_sof_pipeline *spipe;
203 	struct snd_sof_pcm *spcm;
204 	int ret;
205 	int i;
206 
207 	dev_dbg(sdev->dev, "trigger cmd: %d state: %d\n", cmd, state);
208 
209 	spcm = snd_sof_find_spcm_dai(component, rtd);
210 	if (!spcm)
211 		return -EINVAL;
212 
213 	pipeline_list = &spcm->stream[substream->stream].pipeline_list;
214 
215 	/* nothing to trigger if the list is empty */
216 	if (!pipeline_list->pipelines || !pipeline_list->count)
217 		return 0;
218 
219 	/* allocate memory for the pipeline data */
220 	trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count),
221 			       GFP_KERNEL);
222 	if (!trigger_list)
223 		return -ENOMEM;
224 
225 	mutex_lock(&ipc4_data->pipeline_state_mutex);
226 
227 	/*
228 	 * IPC4 requires pipelines to be triggered in order starting at the sink and
229 	 * walking all the way to the source. So traverse the pipeline_list in the order
230 	 * sink->source when starting PCM's and in the reverse order to pause/stop PCM's.
231 	 * Skip the pipelines that have their skip_during_fe_trigger flag set. If there is a fork
232 	 * in the pipeline, the order of triggering between the left/right paths will be
233 	 * indeterministic. But the sink->source trigger order sink->source would still be
234 	 * guaranteed for each fork independently.
235 	 */
236 	if (state == SOF_IPC4_PIPE_RUNNING || state == SOF_IPC4_PIPE_RESET)
237 		for (i = pipeline_list->count - 1; i >= 0; i--) {
238 			spipe = pipeline_list->pipelines[i];
239 			sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list);
240 		}
241 	else
242 		for (i = 0; i < pipeline_list->count; i++) {
243 			spipe = pipeline_list->pipelines[i];
244 			sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list);
245 		}
246 
247 	/* return if all pipelines are in the requested state already */
248 	if (!trigger_list->count) {
249 		ret = 0;
250 		goto free;
251 	}
252 
253 	/* no need to pause before reset or before pause release */
254 	if (state == SOF_IPC4_PIPE_RESET || cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
255 		goto skip_pause_transition;
256 
257 	/*
258 	 * set paused state for pipelines if the final state is PAUSED or when the pipeline
259 	 * is set to RUNNING for the first time after the PCM is started.
260 	 */
261 	ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list);
262 	if (ret < 0) {
263 		dev_err(sdev->dev, "failed to pause all pipelines\n");
264 		goto free;
265 	}
266 
267 	/* update PAUSED state for all pipelines just triggered */
268 	for (i = 0; i < pipeline_list->count ; i++) {
269 		spipe = pipeline_list->pipelines[i];
270 		sof_ipc4_update_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, cmd, spipe,
271 					       trigger_list);
272 	}
273 
274 	/* return if this is the final state */
275 	if (state == SOF_IPC4_PIPE_PAUSED)
276 		goto free;
277 skip_pause_transition:
278 	/* else set the RUNNING/RESET state in the DSP */
279 	ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list);
280 	if (ret < 0) {
281 		dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state);
282 		goto free;
283 	}
284 
285 	/* update RUNNING/RESET state for all pipelines that were just triggered */
286 	for (i = 0; i < pipeline_list->count; i++) {
287 		spipe = pipeline_list->pipelines[i];
288 		sof_ipc4_update_pipeline_state(sdev, state, cmd, spipe, trigger_list);
289 	}
290 
291 free:
292 	mutex_unlock(&ipc4_data->pipeline_state_mutex);
293 	kfree(trigger_list);
294 	return ret;
295 }
296 
297 static int sof_ipc4_pcm_trigger(struct snd_soc_component *component,
298 				struct snd_pcm_substream *substream, int cmd)
299 {
300 	int state;
301 
302 	/* determine the pipeline state */
303 	switch (cmd) {
304 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
305 		state = SOF_IPC4_PIPE_PAUSED;
306 		break;
307 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
308 	case SNDRV_PCM_TRIGGER_RESUME:
309 	case SNDRV_PCM_TRIGGER_START:
310 		state = SOF_IPC4_PIPE_RUNNING;
311 		break;
312 	case SNDRV_PCM_TRIGGER_SUSPEND:
313 	case SNDRV_PCM_TRIGGER_STOP:
314 		state = SOF_IPC4_PIPE_PAUSED;
315 		break;
316 	default:
317 		dev_err(component->dev, "%s: unhandled trigger cmd %d\n", __func__, cmd);
318 		return -EINVAL;
319 	}
320 
321 	/* set the pipeline state */
322 	return sof_ipc4_trigger_pipelines(component, substream, state, cmd);
323 }
324 
325 static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component,
326 				struct snd_pcm_substream *substream)
327 {
328 	/* command is not relevant with RESET, so just pass 0 */
329 	return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0);
330 }
331 
332 static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
333 						 struct snd_pcm_hw_params *params)
334 {
335 	struct snd_sof_dai_link *slink;
336 	struct snd_sof_dai *dai;
337 	bool dai_link_found = false;
338 	int i;
339 
340 	list_for_each_entry(slink, &sdev->dai_link_list, list) {
341 		if (!strcmp(slink->link->name, link_name)) {
342 			dai_link_found = true;
343 			break;
344 		}
345 	}
346 
347 	if (!dai_link_found)
348 		return;
349 
350 	for (i = 0; i < slink->num_hw_configs; i++) {
351 		struct snd_soc_tplg_hw_config *hw_config = &slink->hw_configs[i];
352 
353 		if (params_rate(params) == le32_to_cpu(hw_config->fsync_rate)) {
354 			/* set current config for all DAI's with matching name */
355 			list_for_each_entry(dai, &sdev->dai_list, list)
356 				if (!strcmp(slink->link->name, dai->name))
357 					dai->current_config = le32_to_cpu(hw_config->id);
358 			break;
359 		}
360 	}
361 }
362 
363 static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
364 				       struct snd_pcm_hw_params *params)
365 {
366 	struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
367 	struct snd_sof_dai *dai = snd_sof_find_dai(component, rtd->dai_link->name);
368 	struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
369 	struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
370 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
371 	struct sof_ipc4_copier *ipc4_copier;
372 
373 	if (!dai) {
374 		dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
375 			rtd->dai_link->name);
376 		return -EINVAL;
377 	}
378 
379 	ipc4_copier = dai->private;
380 	if (!ipc4_copier) {
381 		dev_err(component->dev, "%s: No private data found for DAI %s\n",
382 			__func__, rtd->dai_link->name);
383 		return -EINVAL;
384 	}
385 
386 	/* always set BE format to 32-bits for both playback and capture */
387 	snd_mask_none(fmt);
388 	snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
389 
390 	rate->min = ipc4_copier->available_fmt.base_config->audio_fmt.sampling_frequency;
391 	rate->max = rate->min;
392 
393 	switch (ipc4_copier->dai_type) {
394 	case SOF_DAI_INTEL_SSP:
395 		ipc4_ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params);
396 		break;
397 	default:
398 		break;
399 	}
400 
401 	return 0;
402 }
403 
404 static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
405 {
406 	struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
407 	int stream;
408 
409 	for_each_pcm_streams(stream) {
410 		pipeline_list = &spcm->stream[stream].pipeline_list;
411 		kfree(pipeline_list->pipelines);
412 		pipeline_list->pipelines = NULL;
413 	}
414 }
415 
416 static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
417 {
418 	struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
419 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
420 	int stream;
421 
422 	for_each_pcm_streams(stream) {
423 		pipeline_list = &spcm->stream[stream].pipeline_list;
424 
425 		/* allocate memory for max number of pipeline IDs */
426 		pipeline_list->pipelines = kcalloc(ipc4_data->max_num_pipelines,
427 						   sizeof(struct snd_sof_widget *), GFP_KERNEL);
428 		if (!pipeline_list->pipelines) {
429 			sof_ipc4_pcm_free(sdev, spcm);
430 			return -ENOMEM;
431 		}
432 	}
433 
434 	return 0;
435 }
436 
437 const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
438 	.trigger = sof_ipc4_pcm_trigger,
439 	.hw_free = sof_ipc4_pcm_hw_free,
440 	.dai_link_fixup = sof_ipc4_pcm_dai_link_fixup,
441 	.pcm_setup = sof_ipc4_pcm_setup,
442 	.pcm_free = sof_ipc4_pcm_free,
443 };
444