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.
9 #include <sound/pcm_params.h>
10 #include <sound/sof/ipc4/header.h>
11 #include "sof-audio.h"
13 #include "ipc4-priv.h"
14 #include "ipc4-topology.h"
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)
19 struct sof_ipc4_msg msg = {{ 0 }};
20 u32 primary, ipc_size;
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);
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;
32 /* trigger multiple pipelines with a single IPC */
33 msg.extension = SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI;
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;
40 return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0);
43 int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
45 struct sof_ipc4_msg msg = {{ 0 }};
48 dev_dbg(sdev->dev, "ipc4 set pipeline %d state %d", id, 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);
56 msg.primary = primary;
58 return sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
60 EXPORT_SYMBOL(sof_ipc4_set_pipeline_state);
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)
67 struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
68 struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
70 if (pipeline->skip_during_fe_trigger)
74 case SOF_IPC4_PIPE_RUNNING:
76 * Trigger pipeline if all PCMs containing it are paused or if it is RUNNING
79 if (spipe->started_count == spipe->paused_count)
80 trigger_list->pipeline_ids[trigger_list->count++] =
81 pipe_widget->instance_id;
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;
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;
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)
105 struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
106 struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
109 if (pipeline->skip_during_fe_trigger)
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;
121 case SOF_IPC4_PIPE_PAUSED:
123 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
125 * increment paused_count if the PAUSED is the final state during
128 spipe->paused_count++;
130 case SNDRV_PCM_TRIGGER_STOP:
131 case SNDRV_PCM_TRIGGER_SUSPEND:
133 * decrement started_count if PAUSED is the final state during the
136 spipe->started_count--;
142 case SOF_IPC4_PIPE_RUNNING:
144 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
145 /* decrement paused_count for RELEASE */
146 spipe->paused_count--;
148 case SNDRV_PCM_TRIGGER_START:
149 case SNDRV_PCM_TRIGGER_RESUME:
150 /* increment started_count for START/RESUME */
151 spipe->started_count++;
163 * The picture below represents the pipeline state machine wrt PCM actions corresponding to the
164 * triggers and ioctls
175 * +----------------+ +------v-------+ +-------------+
176 * | | START | | HW_FREE | |
177 * | RUNNING <-------------+ PAUSED +--------------> + RESET |
179 * +------+---------+ RELEASE +---------+----+ +-------------+
185 * +---------------------------------+
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.
194 static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
195 struct snd_pcm_substream *substream, int state, int cmd)
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;
207 dev_dbg(sdev->dev, "trigger cmd: %d state: %d\n", cmd, state);
209 spcm = snd_sof_find_spcm_dai(component, rtd);
213 pipeline_list = &spcm->stream[substream->stream].pipeline_list;
215 /* nothing to trigger if the list is empty */
216 if (!pipeline_list->pipelines || !pipeline_list->count)
219 /* allocate memory for the pipeline data */
220 trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count),
225 mutex_lock(&ipc4_data->pipeline_state_mutex);
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.
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);
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);
247 /* return if all pipelines are in the requested state already */
248 if (!trigger_list->count) {
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;
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.
261 ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list);
263 dev_err(sdev->dev, "failed to pause all pipelines\n");
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,
274 /* return if this is the final state */
275 if (state == SOF_IPC4_PIPE_PAUSED)
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);
281 dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state);
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);
292 mutex_unlock(&ipc4_data->pipeline_state_mutex);
297 static int sof_ipc4_pcm_trigger(struct snd_soc_component *component,
298 struct snd_pcm_substream *substream, int cmd)
302 /* determine the pipeline state */
304 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
305 state = SOF_IPC4_PIPE_PAUSED;
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;
312 case SNDRV_PCM_TRIGGER_SUSPEND:
313 case SNDRV_PCM_TRIGGER_STOP:
314 state = SOF_IPC4_PIPE_PAUSED;
317 dev_err(component->dev, "%s: unhandled trigger cmd %d\n", __func__, cmd);
321 /* set the pipeline state */
322 return sof_ipc4_trigger_pipelines(component, substream, state, cmd);
325 static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component,
326 struct snd_pcm_substream *substream)
328 /* command is not relevant with RESET, so just pass 0 */
329 return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0);
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)
335 struct snd_sof_dai_link *slink;
336 struct snd_sof_dai *dai;
337 bool dai_link_found = false;
340 list_for_each_entry(slink, &sdev->dai_link_list, list) {
341 if (!strcmp(slink->link->name, link_name)) {
342 dai_link_found = true;
350 for (i = 0; i < slink->num_hw_configs; i++) {
351 struct snd_soc_tplg_hw_config *hw_config = &slink->hw_configs[i];
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);
363 static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
364 struct snd_pcm_hw_params *params)
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;
374 dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
375 rtd->dai_link->name);
379 ipc4_copier = dai->private;
381 dev_err(component->dev, "%s: No private data found for DAI %s\n",
382 __func__, rtd->dai_link->name);
386 /* always set BE format to 32-bits for both playback and capture */
388 snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
390 rate->min = ipc4_copier->available_fmt.base_config->audio_fmt.sampling_frequency;
391 rate->max = rate->min;
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);
404 static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
406 struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
409 for_each_pcm_streams(stream) {
410 pipeline_list = &spcm->stream[stream].pipeline_list;
411 kfree(pipeline_list->pipelines);
412 pipeline_list->pipelines = NULL;
416 static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
418 struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
419 struct sof_ipc4_fw_data *ipc4_data = sdev->private;
422 for_each_pcm_streams(stream) {
423 pipeline_list = &spcm->stream[stream].pipeline_list;
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);
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,