xref: /openbmc/linux/sound/soc/sof/pcm.c (revision 57e960f0020ec46db277426762ba5ffe77e03e3c)
1868bd00fSLiam Girdwood // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2868bd00fSLiam Girdwood //
3868bd00fSLiam Girdwood // This file is provided under a dual BSD/GPLv2 license.  When using or
4868bd00fSLiam Girdwood // redistributing this file, you may do so under either license.
5868bd00fSLiam Girdwood //
6868bd00fSLiam Girdwood // Copyright(c) 2018 Intel Corporation. All rights reserved.
7868bd00fSLiam Girdwood //
8868bd00fSLiam Girdwood // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
9868bd00fSLiam Girdwood //
10868bd00fSLiam Girdwood // PCM Layer, interface between ALSA and IPC.
11868bd00fSLiam Girdwood //
12868bd00fSLiam Girdwood 
13868bd00fSLiam Girdwood #include <linux/pm_runtime.h>
14868bd00fSLiam Girdwood #include <sound/pcm_params.h>
15868bd00fSLiam Girdwood #include <sound/sof.h>
16868bd00fSLiam Girdwood #include "sof-priv.h"
17ee1e79b7SRanjani Sridharan #include "sof-audio.h"
18868bd00fSLiam Girdwood #include "ops.h"
19868bd00fSLiam Girdwood 
20868bd00fSLiam Girdwood /* Create DMA buffer page table for DSP */
211c91d77eSKuninori Morimoto static int create_page_table(struct snd_soc_component *component,
221c91d77eSKuninori Morimoto 			     struct snd_pcm_substream *substream,
23868bd00fSLiam Girdwood 			     unsigned char *dma_area, size_t size)
24868bd00fSLiam Girdwood {
25868bd00fSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
26868bd00fSLiam Girdwood 	struct snd_sof_pcm *spcm;
27868bd00fSLiam Girdwood 	struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
28868bd00fSLiam Girdwood 	int stream = substream->stream;
29868bd00fSLiam Girdwood 
30ee1e79b7SRanjani Sridharan 	spcm = snd_sof_find_spcm_dai(component, rtd);
31868bd00fSLiam Girdwood 	if (!spcm)
32868bd00fSLiam Girdwood 		return -EINVAL;
33868bd00fSLiam Girdwood 
34ee1e79b7SRanjani Sridharan 	return snd_sof_create_page_table(component->dev, dmab,
35868bd00fSLiam Girdwood 		spcm->stream[stream].page_table.area, size);
36868bd00fSLiam Girdwood }
37868bd00fSLiam Girdwood 
38868bd00fSLiam Girdwood static int sof_pcm_dsp_params(struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream,
39868bd00fSLiam Girdwood 			      const struct sof_ipc_pcm_params_reply *reply)
40868bd00fSLiam Girdwood {
41ee1e79b7SRanjani Sridharan 	struct snd_soc_component *scomp = spcm->scomp;
42ee1e79b7SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
43ee1e79b7SRanjani Sridharan 
44868bd00fSLiam Girdwood 	/* validate offset */
45868bd00fSLiam Girdwood 	int ret = snd_sof_ipc_pcm_params(sdev, substream, reply);
46868bd00fSLiam Girdwood 
47868bd00fSLiam Girdwood 	if (ret < 0)
48ee1e79b7SRanjani Sridharan 		dev_err(scomp->dev, "error: got wrong reply for PCM %d\n",
49868bd00fSLiam Girdwood 			spcm->pcm.pcm_id);
50868bd00fSLiam Girdwood 
51868bd00fSLiam Girdwood 	return ret;
52868bd00fSLiam Girdwood }
53868bd00fSLiam Girdwood 
54e2803e61SKeyon Jie /*
55e2803e61SKeyon Jie  * sof pcm period elapse work
56e2803e61SKeyon Jie  */
57e2803e61SKeyon Jie static void sof_pcm_period_elapsed_work(struct work_struct *work)
58e2803e61SKeyon Jie {
59e2803e61SKeyon Jie 	struct snd_sof_pcm_stream *sps =
60e2803e61SKeyon Jie 		container_of(work, struct snd_sof_pcm_stream,
61e2803e61SKeyon Jie 			     period_elapsed_work);
62e2803e61SKeyon Jie 
63e2803e61SKeyon Jie 	snd_pcm_period_elapsed(sps->substream);
64e2803e61SKeyon Jie }
65e2803e61SKeyon Jie 
66e2803e61SKeyon Jie /*
67e2803e61SKeyon Jie  * sof pcm period elapse, this could be called at irq thread context.
68e2803e61SKeyon Jie  */
69e2803e61SKeyon Jie void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream)
70e2803e61SKeyon Jie {
71e2803e61SKeyon Jie 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
72e2803e61SKeyon Jie 	struct snd_soc_component *component =
73ee1e79b7SRanjani Sridharan 		snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
74e2803e61SKeyon Jie 	struct snd_sof_pcm *spcm;
75e2803e61SKeyon Jie 
76ee1e79b7SRanjani Sridharan 	spcm = snd_sof_find_spcm_dai(component, rtd);
77e2803e61SKeyon Jie 	if (!spcm) {
78ee1e79b7SRanjani Sridharan 		dev_err(component->dev,
79e2803e61SKeyon Jie 			"error: period elapsed for unknown stream!\n");
80e2803e61SKeyon Jie 		return;
81e2803e61SKeyon Jie 	}
82e2803e61SKeyon Jie 
83e2803e61SKeyon Jie 	/*
84e2803e61SKeyon Jie 	 * snd_pcm_period_elapsed() can be called in interrupt context
85e2803e61SKeyon Jie 	 * before IRQ_HANDLED is returned. Inside snd_pcm_period_elapsed(),
86e2803e61SKeyon Jie 	 * when the PCM is done draining or xrun happened, a STOP IPC will
87e2803e61SKeyon Jie 	 * then be sent and this IPC will hit IPC timeout.
88e2803e61SKeyon Jie 	 * To avoid sending IPC before the previous IPC is handled, we
89e2803e61SKeyon Jie 	 * schedule delayed work here to call the snd_pcm_period_elapsed().
90e2803e61SKeyon Jie 	 */
91e2803e61SKeyon Jie 	schedule_work(&spcm->stream[substream->stream].period_elapsed_work);
92e2803e61SKeyon Jie }
93e2803e61SKeyon Jie EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
94e2803e61SKeyon Jie 
95868bd00fSLiam Girdwood /* this may get called several times by oss emulation */
961c91d77eSKuninori Morimoto static int sof_pcm_hw_params(struct snd_soc_component *component,
971c91d77eSKuninori Morimoto 			     struct snd_pcm_substream *substream,
98868bd00fSLiam Girdwood 			     struct snd_pcm_hw_params *params)
99868bd00fSLiam Girdwood {
100868bd00fSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
101868bd00fSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
102868bd00fSLiam Girdwood 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
103868bd00fSLiam Girdwood 	struct snd_sof_pcm *spcm;
104868bd00fSLiam Girdwood 	struct sof_ipc_pcm_params pcm;
105868bd00fSLiam Girdwood 	struct sof_ipc_pcm_params_reply ipc_params_reply;
106868bd00fSLiam Girdwood 	int ret;
107868bd00fSLiam Girdwood 
108868bd00fSLiam Girdwood 	/* nothing to do for BE */
109868bd00fSLiam Girdwood 	if (rtd->dai_link->no_pcm)
110868bd00fSLiam Girdwood 		return 0;
111868bd00fSLiam Girdwood 
112ee1e79b7SRanjani Sridharan 	spcm = snd_sof_find_spcm_dai(component, rtd);
113868bd00fSLiam Girdwood 	if (!spcm)
114868bd00fSLiam Girdwood 		return -EINVAL;
115868bd00fSLiam Girdwood 
116ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n",
117868bd00fSLiam Girdwood 		spcm->pcm.pcm_id, substream->stream);
118868bd00fSLiam Girdwood 
119868bd00fSLiam Girdwood 	memset(&pcm, 0, sizeof(pcm));
120868bd00fSLiam Girdwood 
121*57e960f0STakashi Iwai 	/* create compressed page table for audio firmware */
122*57e960f0STakashi Iwai 	if (runtime->buffer_changed) {
1231c91d77eSKuninori Morimoto 		ret = create_page_table(component, substream, runtime->dma_area,
124868bd00fSLiam Girdwood 					runtime->dma_bytes);
125868bd00fSLiam Girdwood 		if (ret < 0)
126868bd00fSLiam Girdwood 			return ret;
127868bd00fSLiam Girdwood 	}
128868bd00fSLiam Girdwood 
129868bd00fSLiam Girdwood 	/* number of pages should be rounded up */
130868bd00fSLiam Girdwood 	pcm.params.buffer.pages = PFN_UP(runtime->dma_bytes);
131868bd00fSLiam Girdwood 
132868bd00fSLiam Girdwood 	/* set IPC PCM parameters */
133868bd00fSLiam Girdwood 	pcm.hdr.size = sizeof(pcm);
134868bd00fSLiam Girdwood 	pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS;
135868bd00fSLiam Girdwood 	pcm.comp_id = spcm->stream[substream->stream].comp_id;
136868bd00fSLiam Girdwood 	pcm.params.hdr.size = sizeof(pcm.params);
137868bd00fSLiam Girdwood 	pcm.params.buffer.phy_addr =
138868bd00fSLiam Girdwood 		spcm->stream[substream->stream].page_table.addr;
139868bd00fSLiam Girdwood 	pcm.params.buffer.size = runtime->dma_bytes;
140868bd00fSLiam Girdwood 	pcm.params.direction = substream->stream;
141868bd00fSLiam Girdwood 	pcm.params.sample_valid_bytes = params_width(params) >> 3;
142868bd00fSLiam Girdwood 	pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
143868bd00fSLiam Girdwood 	pcm.params.rate = params_rate(params);
144868bd00fSLiam Girdwood 	pcm.params.channels = params_channels(params);
145868bd00fSLiam Girdwood 	pcm.params.host_period_bytes = params_period_bytes(params);
146868bd00fSLiam Girdwood 
147868bd00fSLiam Girdwood 	/* container size */
148868bd00fSLiam Girdwood 	ret = snd_pcm_format_physical_width(params_format(params));
149868bd00fSLiam Girdwood 	if (ret < 0)
150868bd00fSLiam Girdwood 		return ret;
151868bd00fSLiam Girdwood 	pcm.params.sample_container_bytes = ret >> 3;
152868bd00fSLiam Girdwood 
153868bd00fSLiam Girdwood 	/* format */
154868bd00fSLiam Girdwood 	switch (params_format(params)) {
155868bd00fSLiam Girdwood 	case SNDRV_PCM_FORMAT_S16:
156868bd00fSLiam Girdwood 		pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE;
157868bd00fSLiam Girdwood 		break;
158868bd00fSLiam Girdwood 	case SNDRV_PCM_FORMAT_S24:
159868bd00fSLiam Girdwood 		pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE;
160868bd00fSLiam Girdwood 		break;
161868bd00fSLiam Girdwood 	case SNDRV_PCM_FORMAT_S32:
162868bd00fSLiam Girdwood 		pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE;
163868bd00fSLiam Girdwood 		break;
164868bd00fSLiam Girdwood 	case SNDRV_PCM_FORMAT_FLOAT:
165868bd00fSLiam Girdwood 		pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT;
166868bd00fSLiam Girdwood 		break;
167868bd00fSLiam Girdwood 	default:
168868bd00fSLiam Girdwood 		return -EINVAL;
169868bd00fSLiam Girdwood 	}
170868bd00fSLiam Girdwood 
171868bd00fSLiam Girdwood 	/* firmware already configured host stream */
172868bd00fSLiam Girdwood 	ret = snd_sof_pcm_platform_hw_params(sdev,
173868bd00fSLiam Girdwood 					     substream,
174868bd00fSLiam Girdwood 					     params,
175868bd00fSLiam Girdwood 					     &pcm.params);
176868bd00fSLiam Girdwood 	if (ret < 0) {
177ee1e79b7SRanjani Sridharan 		dev_err(component->dev, "error: platform hw params failed\n");
178868bd00fSLiam Girdwood 		return ret;
179868bd00fSLiam Girdwood 	}
180868bd00fSLiam Girdwood 
181ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag);
182868bd00fSLiam Girdwood 
183868bd00fSLiam Girdwood 	/* send IPC to the DSP */
184868bd00fSLiam Girdwood 	ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
185868bd00fSLiam Girdwood 				 &ipc_params_reply, sizeof(ipc_params_reply));
186868bd00fSLiam Girdwood 	if (ret < 0) {
187ee1e79b7SRanjani Sridharan 		dev_err(component->dev, "error: hw params ipc failed for stream %d\n",
188868bd00fSLiam Girdwood 			pcm.params.stream_tag);
189868bd00fSLiam Girdwood 		return ret;
190868bd00fSLiam Girdwood 	}
191868bd00fSLiam Girdwood 
192868bd00fSLiam Girdwood 	ret = sof_pcm_dsp_params(spcm, substream, &ipc_params_reply);
193868bd00fSLiam Girdwood 	if (ret < 0)
194868bd00fSLiam Girdwood 		return ret;
195868bd00fSLiam Girdwood 
19604c80277SKai Vehmanen 	spcm->prepared[substream->stream] = true;
19704c80277SKai Vehmanen 
198868bd00fSLiam Girdwood 	/* save pcm hw_params */
199868bd00fSLiam Girdwood 	memcpy(&spcm->params[substream->stream], params, sizeof(*params));
200868bd00fSLiam Girdwood 
201868bd00fSLiam Girdwood 	return ret;
202868bd00fSLiam Girdwood }
203868bd00fSLiam Girdwood 
204a49b6871SKai Vehmanen static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream,
205a49b6871SKai Vehmanen 				struct snd_sof_dev *sdev,
206a49b6871SKai Vehmanen 				struct snd_sof_pcm *spcm)
207a49b6871SKai Vehmanen {
208a49b6871SKai Vehmanen 	struct sof_ipc_stream stream;
209a49b6871SKai Vehmanen 	struct sof_ipc_reply reply;
210a49b6871SKai Vehmanen 	int ret;
211a49b6871SKai Vehmanen 
212a49b6871SKai Vehmanen 	stream.hdr.size = sizeof(stream);
213a49b6871SKai Vehmanen 	stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE;
214a49b6871SKai Vehmanen 	stream.comp_id = spcm->stream[substream->stream].comp_id;
215a49b6871SKai Vehmanen 
216a49b6871SKai Vehmanen 	/* send IPC to the DSP */
217a49b6871SKai Vehmanen 	ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
218a49b6871SKai Vehmanen 				 sizeof(stream), &reply, sizeof(reply));
219a49b6871SKai Vehmanen 	if (!ret)
220a49b6871SKai Vehmanen 		spcm->prepared[substream->stream] = false;
221a49b6871SKai Vehmanen 
222a49b6871SKai Vehmanen 	return ret;
223a49b6871SKai Vehmanen }
224a49b6871SKai Vehmanen 
2251c91d77eSKuninori Morimoto static int sof_pcm_hw_free(struct snd_soc_component *component,
2261c91d77eSKuninori Morimoto 			   struct snd_pcm_substream *substream)
227868bd00fSLiam Girdwood {
228868bd00fSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
229868bd00fSLiam Girdwood 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
230868bd00fSLiam Girdwood 	struct snd_sof_pcm *spcm;
231e66e52c5SKai Vehmanen 	int ret, err = 0;
232868bd00fSLiam Girdwood 
233868bd00fSLiam Girdwood 	/* nothing to do for BE */
234868bd00fSLiam Girdwood 	if (rtd->dai_link->no_pcm)
235868bd00fSLiam Girdwood 		return 0;
236868bd00fSLiam Girdwood 
237ee1e79b7SRanjani Sridharan 	spcm = snd_sof_find_spcm_dai(component, rtd);
238868bd00fSLiam Girdwood 	if (!spcm)
239868bd00fSLiam Girdwood 		return -EINVAL;
240868bd00fSLiam Girdwood 
241ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev, "pcm: free stream %d dir %d\n",
242ee1e79b7SRanjani Sridharan 		spcm->pcm.pcm_id, substream->stream);
243868bd00fSLiam Girdwood 
244e66e52c5SKai Vehmanen 	if (spcm->prepared[substream->stream]) {
245a49b6871SKai Vehmanen 		ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
246e66e52c5SKai Vehmanen 		if (ret < 0)
247e66e52c5SKai Vehmanen 			err = ret;
248e66e52c5SKai Vehmanen 	}
249868bd00fSLiam Girdwood 
250e2803e61SKeyon Jie 	cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
251e2803e61SKeyon Jie 
25293146bc2SRanjani Sridharan 	ret = snd_sof_pcm_platform_hw_free(sdev, substream);
253e66e52c5SKai Vehmanen 	if (ret < 0) {
254ee1e79b7SRanjani Sridharan 		dev_err(component->dev, "error: platform hw free failed\n");
255e66e52c5SKai Vehmanen 		err = ret;
256e66e52c5SKai Vehmanen 	}
25793146bc2SRanjani Sridharan 
258e66e52c5SKai Vehmanen 	return err;
259868bd00fSLiam Girdwood }
260868bd00fSLiam Girdwood 
2611c91d77eSKuninori Morimoto static int sof_pcm_prepare(struct snd_soc_component *component,
2621c91d77eSKuninori Morimoto 			   struct snd_pcm_substream *substream)
263868bd00fSLiam Girdwood {
264868bd00fSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
265868bd00fSLiam Girdwood 	struct snd_sof_pcm *spcm;
266868bd00fSLiam Girdwood 	int ret;
267868bd00fSLiam Girdwood 
268868bd00fSLiam Girdwood 	/* nothing to do for BE */
269868bd00fSLiam Girdwood 	if (rtd->dai_link->no_pcm)
270868bd00fSLiam Girdwood 		return 0;
271868bd00fSLiam Girdwood 
272ee1e79b7SRanjani Sridharan 	spcm = snd_sof_find_spcm_dai(component, rtd);
273868bd00fSLiam Girdwood 	if (!spcm)
274868bd00fSLiam Girdwood 		return -EINVAL;
275868bd00fSLiam Girdwood 
27604c80277SKai Vehmanen 	if (spcm->prepared[substream->stream])
277868bd00fSLiam Girdwood 		return 0;
278868bd00fSLiam Girdwood 
279ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev, "pcm: prepare stream %d dir %d\n",
280ee1e79b7SRanjani Sridharan 		spcm->pcm.pcm_id, substream->stream);
281868bd00fSLiam Girdwood 
282868bd00fSLiam Girdwood 	/* set hw_params */
2831c91d77eSKuninori Morimoto 	ret = sof_pcm_hw_params(component,
2841c91d77eSKuninori Morimoto 				substream, &spcm->params[substream->stream]);
285868bd00fSLiam Girdwood 	if (ret < 0) {
286ee1e79b7SRanjani Sridharan 		dev_err(component->dev,
287ee1e79b7SRanjani Sridharan 			"error: set pcm hw_params after resume\n");
288868bd00fSLiam Girdwood 		return ret;
289868bd00fSLiam Girdwood 	}
290868bd00fSLiam Girdwood 
291868bd00fSLiam Girdwood 	return 0;
292868bd00fSLiam Girdwood }
293868bd00fSLiam Girdwood 
294868bd00fSLiam Girdwood /*
295868bd00fSLiam Girdwood  * FE dai link trigger actions are always executed in non-atomic context because
296868bd00fSLiam Girdwood  * they involve IPC's.
297868bd00fSLiam Girdwood  */
2981c91d77eSKuninori Morimoto static int sof_pcm_trigger(struct snd_soc_component *component,
2991c91d77eSKuninori Morimoto 			   struct snd_pcm_substream *substream, int cmd)
300868bd00fSLiam Girdwood {
301868bd00fSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
302868bd00fSLiam Girdwood 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
303868bd00fSLiam Girdwood 	struct snd_sof_pcm *spcm;
304868bd00fSLiam Girdwood 	struct sof_ipc_stream stream;
305868bd00fSLiam Girdwood 	struct sof_ipc_reply reply;
30604c80277SKai Vehmanen 	bool reset_hw_params = false;
3070a1b0834SPan Xiuli 	bool ipc_first = false;
308868bd00fSLiam Girdwood 	int ret;
309868bd00fSLiam Girdwood 
310868bd00fSLiam Girdwood 	/* nothing to do for BE */
311868bd00fSLiam Girdwood 	if (rtd->dai_link->no_pcm)
312868bd00fSLiam Girdwood 		return 0;
313868bd00fSLiam Girdwood 
314ee1e79b7SRanjani Sridharan 	spcm = snd_sof_find_spcm_dai(component, rtd);
315868bd00fSLiam Girdwood 	if (!spcm)
316868bd00fSLiam Girdwood 		return -EINVAL;
317868bd00fSLiam Girdwood 
318ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n",
319868bd00fSLiam Girdwood 		spcm->pcm.pcm_id, substream->stream, cmd);
320868bd00fSLiam Girdwood 
321868bd00fSLiam Girdwood 	stream.hdr.size = sizeof(stream);
322868bd00fSLiam Girdwood 	stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG;
323868bd00fSLiam Girdwood 	stream.comp_id = spcm->stream[substream->stream].comp_id;
324868bd00fSLiam Girdwood 
325868bd00fSLiam Girdwood 	switch (cmd) {
326868bd00fSLiam Girdwood 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
327868bd00fSLiam Girdwood 		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE;
3280a1b0834SPan Xiuli 		ipc_first = true;
329868bd00fSLiam Girdwood 		break;
330868bd00fSLiam Girdwood 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
331868bd00fSLiam Girdwood 		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE;
332868bd00fSLiam Girdwood 		break;
333868bd00fSLiam Girdwood 	case SNDRV_PCM_TRIGGER_RESUME:
334ac8c046fSKeyon Jie 		if (spcm->stream[substream->stream].suspend_ignored) {
335ac8c046fSKeyon Jie 			/*
336ac8c046fSKeyon Jie 			 * this case will be triggered when INFO_RESUME is
337ac8c046fSKeyon Jie 			 * supported, no need to resume streams that remained
338ac8c046fSKeyon Jie 			 * enabled in D0ix.
339ac8c046fSKeyon Jie 			 */
340ac8c046fSKeyon Jie 			spcm->stream[substream->stream].suspend_ignored = false;
341ac8c046fSKeyon Jie 			return 0;
342ac8c046fSKeyon Jie 		}
343ac8c046fSKeyon Jie 
344868bd00fSLiam Girdwood 		/* set up hw_params */
3451c91d77eSKuninori Morimoto 		ret = sof_pcm_prepare(component, substream);
346868bd00fSLiam Girdwood 		if (ret < 0) {
347ee1e79b7SRanjani Sridharan 			dev_err(component->dev,
348868bd00fSLiam Girdwood 				"error: failed to set up hw_params upon resume\n");
349868bd00fSLiam Girdwood 			return ret;
350868bd00fSLiam Girdwood 		}
351868bd00fSLiam Girdwood 
352868bd00fSLiam Girdwood 		/* fallthrough */
353868bd00fSLiam Girdwood 	case SNDRV_PCM_TRIGGER_START:
354ac8c046fSKeyon Jie 		if (spcm->stream[substream->stream].suspend_ignored) {
355ac8c046fSKeyon Jie 			/*
356ac8c046fSKeyon Jie 			 * This case will be triggered when INFO_RESUME is
357ac8c046fSKeyon Jie 			 * not supported, no need to re-start streams that
358ac8c046fSKeyon Jie 			 * remained enabled in D0ix.
359ac8c046fSKeyon Jie 			 */
360ac8c046fSKeyon Jie 			spcm->stream[substream->stream].suspend_ignored = false;
361ac8c046fSKeyon Jie 			return 0;
362ac8c046fSKeyon Jie 		}
363868bd00fSLiam Girdwood 		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START;
364868bd00fSLiam Girdwood 		break;
365868bd00fSLiam Girdwood 	case SNDRV_PCM_TRIGGER_SUSPEND:
366ac8c046fSKeyon Jie 		if (sdev->s0_suspend &&
367ac8c046fSKeyon Jie 		    spcm->stream[substream->stream].d0i3_compatible) {
368ac8c046fSKeyon Jie 			/*
369ac8c046fSKeyon Jie 			 * trap the event, not sending trigger stop to
370ac8c046fSKeyon Jie 			 * prevent the FW pipelines from being stopped,
371ac8c046fSKeyon Jie 			 * and mark the flag to ignore the upcoming DAPM
372ac8c046fSKeyon Jie 			 * PM events.
373ac8c046fSKeyon Jie 			 */
374ac8c046fSKeyon Jie 			spcm->stream[substream->stream].suspend_ignored = true;
375ac8c046fSKeyon Jie 			return 0;
376ac8c046fSKeyon Jie 		}
377ac8c046fSKeyon Jie 		/* fallthrough */
378868bd00fSLiam Girdwood 	case SNDRV_PCM_TRIGGER_STOP:
379868bd00fSLiam Girdwood 		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
3800a1b0834SPan Xiuli 		ipc_first = true;
38104c80277SKai Vehmanen 		reset_hw_params = true;
382868bd00fSLiam Girdwood 		break;
383868bd00fSLiam Girdwood 	default:
384ee1e79b7SRanjani Sridharan 		dev_err(component->dev, "error: unhandled trigger cmd %d\n",
385ee1e79b7SRanjani Sridharan 			cmd);
386868bd00fSLiam Girdwood 		return -EINVAL;
387868bd00fSLiam Girdwood 	}
388868bd00fSLiam Girdwood 
3890a1b0834SPan Xiuli 	/*
3900a1b0834SPan Xiuli 	 * DMA and IPC sequence is different for start and stop. Need to send
3910a1b0834SPan Xiuli 	 * STOP IPC before stop DMA
3920a1b0834SPan Xiuli 	 */
3930a1b0834SPan Xiuli 	if (!ipc_first)
394868bd00fSLiam Girdwood 		snd_sof_pcm_platform_trigger(sdev, substream, cmd);
395868bd00fSLiam Girdwood 
396868bd00fSLiam Girdwood 	/* send IPC to the DSP */
397868bd00fSLiam Girdwood 	ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
398868bd00fSLiam Girdwood 				 sizeof(stream), &reply, sizeof(reply));
399868bd00fSLiam Girdwood 
4000a1b0834SPan Xiuli 	/* need to STOP DMA even if STOP IPC failed */
4010a1b0834SPan Xiuli 	if (ipc_first)
4020a1b0834SPan Xiuli 		snd_sof_pcm_platform_trigger(sdev, substream, cmd);
4030a1b0834SPan Xiuli 
4040a1b0834SPan Xiuli 	/* free PCM if reset_hw_params is set and the STOP IPC is successful */
405a49b6871SKai Vehmanen 	if (!ret && reset_hw_params)
406a49b6871SKai Vehmanen 		ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
407a49b6871SKai Vehmanen 
408868bd00fSLiam Girdwood 	return ret;
409868bd00fSLiam Girdwood }
410868bd00fSLiam Girdwood 
4111c91d77eSKuninori Morimoto static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component,
4121c91d77eSKuninori Morimoto 					 struct snd_pcm_substream *substream)
413868bd00fSLiam Girdwood {
414868bd00fSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
415868bd00fSLiam Girdwood 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
416868bd00fSLiam Girdwood 	struct snd_sof_pcm *spcm;
417868bd00fSLiam Girdwood 	snd_pcm_uframes_t host, dai;
418868bd00fSLiam Girdwood 
419868bd00fSLiam Girdwood 	/* nothing to do for BE */
420868bd00fSLiam Girdwood 	if (rtd->dai_link->no_pcm)
421868bd00fSLiam Girdwood 		return 0;
422868bd00fSLiam Girdwood 
423868bd00fSLiam Girdwood 	/* use dsp ops pointer callback directly if set */
424868bd00fSLiam Girdwood 	if (sof_ops(sdev)->pcm_pointer)
425868bd00fSLiam Girdwood 		return sof_ops(sdev)->pcm_pointer(sdev, substream);
426868bd00fSLiam Girdwood 
427ee1e79b7SRanjani Sridharan 	spcm = snd_sof_find_spcm_dai(component, rtd);
428868bd00fSLiam Girdwood 	if (!spcm)
429868bd00fSLiam Girdwood 		return -EINVAL;
430868bd00fSLiam Girdwood 
431868bd00fSLiam Girdwood 	/* read position from DSP */
432868bd00fSLiam Girdwood 	host = bytes_to_frames(substream->runtime,
433868bd00fSLiam Girdwood 			       spcm->stream[substream->stream].posn.host_posn);
434868bd00fSLiam Girdwood 	dai = bytes_to_frames(substream->runtime,
435868bd00fSLiam Girdwood 			      spcm->stream[substream->stream].posn.dai_posn);
436868bd00fSLiam Girdwood 
437ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev,
438ee1e79b7SRanjani Sridharan 		"PCM: stream %d dir %d DMA position %lu DAI position %lu\n",
439868bd00fSLiam Girdwood 		spcm->pcm.pcm_id, substream->stream, host, dai);
440868bd00fSLiam Girdwood 
441868bd00fSLiam Girdwood 	return host;
442868bd00fSLiam Girdwood }
443868bd00fSLiam Girdwood 
4441c91d77eSKuninori Morimoto static int sof_pcm_open(struct snd_soc_component *component,
4451c91d77eSKuninori Morimoto 			struct snd_pcm_substream *substream)
446868bd00fSLiam Girdwood {
447868bd00fSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
448868bd00fSLiam Girdwood 	struct snd_pcm_runtime *runtime = substream->runtime;
449868bd00fSLiam Girdwood 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
45027e322faSPierre-Louis Bossart 	const struct snd_sof_dsp_ops *ops = sof_ops(sdev);
451868bd00fSLiam Girdwood 	struct snd_sof_pcm *spcm;
452868bd00fSLiam Girdwood 	struct snd_soc_tplg_stream_caps *caps;
453868bd00fSLiam Girdwood 	int ret;
454868bd00fSLiam Girdwood 
455868bd00fSLiam Girdwood 	/* nothing to do for BE */
456868bd00fSLiam Girdwood 	if (rtd->dai_link->no_pcm)
457868bd00fSLiam Girdwood 		return 0;
458868bd00fSLiam Girdwood 
459ee1e79b7SRanjani Sridharan 	spcm = snd_sof_find_spcm_dai(component, rtd);
460868bd00fSLiam Girdwood 	if (!spcm)
461868bd00fSLiam Girdwood 		return -EINVAL;
462868bd00fSLiam Girdwood 
463ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev, "pcm: open stream %d dir %d\n",
464ee1e79b7SRanjani Sridharan 		spcm->pcm.pcm_id, substream->stream);
465868bd00fSLiam Girdwood 
466fab4edf4SPierre-Louis Bossart 	INIT_WORK(&spcm->stream[substream->stream].period_elapsed_work,
467fab4edf4SPierre-Louis Bossart 		  sof_pcm_period_elapsed_work);
468868bd00fSLiam Girdwood 
469868bd00fSLiam Girdwood 	caps = &spcm->pcm.caps[substream->stream];
470868bd00fSLiam Girdwood 
471868bd00fSLiam Girdwood 	/* set any runtime constraints based on topology */
472868bd00fSLiam Girdwood 	snd_pcm_hw_constraint_step(substream->runtime, 0,
473868bd00fSLiam Girdwood 				   SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
474868bd00fSLiam Girdwood 				   le32_to_cpu(caps->period_size_min));
475868bd00fSLiam Girdwood 	snd_pcm_hw_constraint_step(substream->runtime, 0,
476868bd00fSLiam Girdwood 				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
477868bd00fSLiam Girdwood 				   le32_to_cpu(caps->period_size_min));
478868bd00fSLiam Girdwood 
479868bd00fSLiam Girdwood 	/* set runtime config */
48027e322faSPierre-Louis Bossart 	runtime->hw.info = ops->hw_info; /* platform-specific */
48127e322faSPierre-Louis Bossart 
482868bd00fSLiam Girdwood 	runtime->hw.formats = le64_to_cpu(caps->formats);
483868bd00fSLiam Girdwood 	runtime->hw.period_bytes_min = le32_to_cpu(caps->period_size_min);
484868bd00fSLiam Girdwood 	runtime->hw.period_bytes_max = le32_to_cpu(caps->period_size_max);
485868bd00fSLiam Girdwood 	runtime->hw.periods_min = le32_to_cpu(caps->periods_min);
486868bd00fSLiam Girdwood 	runtime->hw.periods_max = le32_to_cpu(caps->periods_max);
487868bd00fSLiam Girdwood 
488868bd00fSLiam Girdwood 	/*
489868bd00fSLiam Girdwood 	 * caps->buffer_size_min is not used since the
490868bd00fSLiam Girdwood 	 * snd_pcm_hardware structure only defines buffer_bytes_max
491868bd00fSLiam Girdwood 	 */
492868bd00fSLiam Girdwood 	runtime->hw.buffer_bytes_max = le32_to_cpu(caps->buffer_size_max);
493868bd00fSLiam Girdwood 
494ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev, "period min %zd max %zd bytes\n",
495868bd00fSLiam Girdwood 		runtime->hw.period_bytes_min,
496868bd00fSLiam Girdwood 		runtime->hw.period_bytes_max);
497ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev, "period count %d max %d\n",
498868bd00fSLiam Girdwood 		runtime->hw.periods_min,
499868bd00fSLiam Girdwood 		runtime->hw.periods_max);
500ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev, "buffer max %zd bytes\n",
501868bd00fSLiam Girdwood 		runtime->hw.buffer_bytes_max);
502868bd00fSLiam Girdwood 
503868bd00fSLiam Girdwood 	/* set wait time - TODO: come from topology */
504868bd00fSLiam Girdwood 	substream->wait_time = 500;
505868bd00fSLiam Girdwood 
506868bd00fSLiam Girdwood 	spcm->stream[substream->stream].posn.host_posn = 0;
507868bd00fSLiam Girdwood 	spcm->stream[substream->stream].posn.dai_posn = 0;
508868bd00fSLiam Girdwood 	spcm->stream[substream->stream].substream = substream;
50904c80277SKai Vehmanen 	spcm->prepared[substream->stream] = false;
510868bd00fSLiam Girdwood 
511868bd00fSLiam Girdwood 	ret = snd_sof_pcm_platform_open(sdev, substream);
51214a2212dSRanjani Sridharan 	if (ret < 0)
513ee1e79b7SRanjani Sridharan 		dev_err(component->dev, "error: pcm open failed %d\n", ret);
514868bd00fSLiam Girdwood 
515868bd00fSLiam Girdwood 	return ret;
516868bd00fSLiam Girdwood }
517868bd00fSLiam Girdwood 
5181c91d77eSKuninori Morimoto static int sof_pcm_close(struct snd_soc_component *component,
5191c91d77eSKuninori Morimoto 			 struct snd_pcm_substream *substream)
520868bd00fSLiam Girdwood {
521868bd00fSLiam Girdwood 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
522868bd00fSLiam Girdwood 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
523868bd00fSLiam Girdwood 	struct snd_sof_pcm *spcm;
524868bd00fSLiam Girdwood 	int err;
525868bd00fSLiam Girdwood 
526868bd00fSLiam Girdwood 	/* nothing to do for BE */
527868bd00fSLiam Girdwood 	if (rtd->dai_link->no_pcm)
528868bd00fSLiam Girdwood 		return 0;
529868bd00fSLiam Girdwood 
530ee1e79b7SRanjani Sridharan 	spcm = snd_sof_find_spcm_dai(component, rtd);
531868bd00fSLiam Girdwood 	if (!spcm)
532868bd00fSLiam Girdwood 		return -EINVAL;
533868bd00fSLiam Girdwood 
534ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev, "pcm: close stream %d dir %d\n",
535ee1e79b7SRanjani Sridharan 		spcm->pcm.pcm_id, substream->stream);
536868bd00fSLiam Girdwood 
537868bd00fSLiam Girdwood 	err = snd_sof_pcm_platform_close(sdev, substream);
538868bd00fSLiam Girdwood 	if (err < 0) {
539ee1e79b7SRanjani Sridharan 		dev_err(component->dev, "error: pcm close failed %d\n",
540868bd00fSLiam Girdwood 			err);
541868bd00fSLiam Girdwood 		/*
542868bd00fSLiam Girdwood 		 * keep going, no point in preventing the close
543868bd00fSLiam Girdwood 		 * from happening
544868bd00fSLiam Girdwood 		 */
545868bd00fSLiam Girdwood 	}
546868bd00fSLiam Girdwood 
547868bd00fSLiam Girdwood 	return 0;
548868bd00fSLiam Girdwood }
549868bd00fSLiam Girdwood 
550868bd00fSLiam Girdwood /*
551868bd00fSLiam Girdwood  * Pre-allocate playback/capture audio buffer pages.
552868bd00fSLiam Girdwood  * no need to explicitly release memory preallocated by sof_pcm_new in pcm_free
553868bd00fSLiam Girdwood  * snd_pcm_lib_preallocate_free_for_all() is called by the core.
554868bd00fSLiam Girdwood  */
5551c91d77eSKuninori Morimoto static int sof_pcm_new(struct snd_soc_component *component,
5561c91d77eSKuninori Morimoto 		       struct snd_soc_pcm_runtime *rtd)
557868bd00fSLiam Girdwood {
558868bd00fSLiam Girdwood 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
559868bd00fSLiam Girdwood 	struct snd_sof_pcm *spcm;
560868bd00fSLiam Girdwood 	struct snd_pcm *pcm = rtd->pcm;
561868bd00fSLiam Girdwood 	struct snd_soc_tplg_stream_caps *caps;
562868bd00fSLiam Girdwood 	int stream = SNDRV_PCM_STREAM_PLAYBACK;
563868bd00fSLiam Girdwood 
564868bd00fSLiam Girdwood 	/* find SOF PCM for this RTD */
565ee1e79b7SRanjani Sridharan 	spcm = snd_sof_find_spcm_dai(component, rtd);
566868bd00fSLiam Girdwood 	if (!spcm) {
567ee1e79b7SRanjani Sridharan 		dev_warn(component->dev, "warn: can't find PCM with DAI ID %d\n",
568868bd00fSLiam Girdwood 			 rtd->dai_link->id);
569868bd00fSLiam Girdwood 		return 0;
570868bd00fSLiam Girdwood 	}
571868bd00fSLiam Girdwood 
572ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev, "creating new PCM %s\n", spcm->pcm.pcm_name);
573868bd00fSLiam Girdwood 
574868bd00fSLiam Girdwood 	/* do we need to pre-allocate playback audio buffer pages */
575868bd00fSLiam Girdwood 	if (!spcm->pcm.playback)
576868bd00fSLiam Girdwood 		goto capture;
577868bd00fSLiam Girdwood 
578868bd00fSLiam Girdwood 	caps = &spcm->pcm.caps[stream];
579868bd00fSLiam Girdwood 
580868bd00fSLiam Girdwood 	/* pre-allocate playback audio buffer pages */
581ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev,
582ee1e79b7SRanjani Sridharan 		"spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n",
583868bd00fSLiam Girdwood 		caps->name, caps->buffer_size_min, caps->buffer_size_max);
584868bd00fSLiam Girdwood 
585*57e960f0STakashi Iwai 	snd_pcm_set_managed_buffer(pcm->streams[stream].substream,
586868bd00fSLiam Girdwood 				   SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
587868bd00fSLiam Girdwood 				   le32_to_cpu(caps->buffer_size_min),
588868bd00fSLiam Girdwood 				   le32_to_cpu(caps->buffer_size_max));
589868bd00fSLiam Girdwood capture:
590868bd00fSLiam Girdwood 	stream = SNDRV_PCM_STREAM_CAPTURE;
591868bd00fSLiam Girdwood 
592868bd00fSLiam Girdwood 	/* do we need to pre-allocate capture audio buffer pages */
593868bd00fSLiam Girdwood 	if (!spcm->pcm.capture)
594868bd00fSLiam Girdwood 		return 0;
595868bd00fSLiam Girdwood 
596868bd00fSLiam Girdwood 	caps = &spcm->pcm.caps[stream];
597868bd00fSLiam Girdwood 
598868bd00fSLiam Girdwood 	/* pre-allocate capture audio buffer pages */
599ee1e79b7SRanjani Sridharan 	dev_dbg(component->dev,
600ee1e79b7SRanjani Sridharan 		"spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n",
601868bd00fSLiam Girdwood 		caps->name, caps->buffer_size_min, caps->buffer_size_max);
602868bd00fSLiam Girdwood 
603*57e960f0STakashi Iwai 	snd_pcm_set_managed_buffer(pcm->streams[stream].substream,
604868bd00fSLiam Girdwood 				   SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
605868bd00fSLiam Girdwood 				   le32_to_cpu(caps->buffer_size_min),
606868bd00fSLiam Girdwood 				   le32_to_cpu(caps->buffer_size_max));
607868bd00fSLiam Girdwood 
608868bd00fSLiam Girdwood 	return 0;
609868bd00fSLiam Girdwood }
610868bd00fSLiam Girdwood 
611868bd00fSLiam Girdwood /* fixup the BE DAI link to match any values from topology */
612868bd00fSLiam Girdwood static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
613868bd00fSLiam Girdwood 				  struct snd_pcm_hw_params *params)
614868bd00fSLiam Girdwood {
615868bd00fSLiam Girdwood 	struct snd_interval *rate = hw_param_interval(params,
616868bd00fSLiam Girdwood 			SNDRV_PCM_HW_PARAM_RATE);
617868bd00fSLiam Girdwood 	struct snd_interval *channels = hw_param_interval(params,
618868bd00fSLiam Girdwood 						SNDRV_PCM_HW_PARAM_CHANNELS);
619868bd00fSLiam Girdwood 	struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
620868bd00fSLiam Girdwood 	struct snd_soc_component *component =
621ee1e79b7SRanjani Sridharan 		snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
622868bd00fSLiam Girdwood 	struct snd_sof_dai *dai =
623ee1e79b7SRanjani Sridharan 		snd_sof_find_dai(component, (char *)rtd->dai_link->name);
624868bd00fSLiam Girdwood 
625868bd00fSLiam Girdwood 	/* no topology exists for this BE, try a common configuration */
626868bd00fSLiam Girdwood 	if (!dai) {
627ee1e79b7SRanjani Sridharan 		dev_warn(component->dev,
628ee1e79b7SRanjani Sridharan 			 "warning: no topology found for BE DAI %s config\n",
629868bd00fSLiam Girdwood 			 rtd->dai_link->name);
630868bd00fSLiam Girdwood 
631868bd00fSLiam Girdwood 		/*  set 48k, stereo, 16bits by default */
632868bd00fSLiam Girdwood 		rate->min = 48000;
633868bd00fSLiam Girdwood 		rate->max = 48000;
634868bd00fSLiam Girdwood 
635868bd00fSLiam Girdwood 		channels->min = 2;
636868bd00fSLiam Girdwood 		channels->max = 2;
637868bd00fSLiam Girdwood 
638868bd00fSLiam Girdwood 		snd_mask_none(fmt);
639868bd00fSLiam Girdwood 		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
640868bd00fSLiam Girdwood 
641868bd00fSLiam Girdwood 		return 0;
642868bd00fSLiam Girdwood 	}
643868bd00fSLiam Girdwood 
644868bd00fSLiam Girdwood 	/* read format from topology */
645868bd00fSLiam Girdwood 	snd_mask_none(fmt);
646868bd00fSLiam Girdwood 
647868bd00fSLiam Girdwood 	switch (dai->comp_dai.config.frame_fmt) {
648868bd00fSLiam Girdwood 	case SOF_IPC_FRAME_S16_LE:
649868bd00fSLiam Girdwood 		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
650868bd00fSLiam Girdwood 		break;
651868bd00fSLiam Girdwood 	case SOF_IPC_FRAME_S24_4LE:
652868bd00fSLiam Girdwood 		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
653868bd00fSLiam Girdwood 		break;
654868bd00fSLiam Girdwood 	case SOF_IPC_FRAME_S32_LE:
655868bd00fSLiam Girdwood 		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
656868bd00fSLiam Girdwood 		break;
657868bd00fSLiam Girdwood 	default:
658ee1e79b7SRanjani Sridharan 		dev_err(component->dev, "error: No available DAI format!\n");
659868bd00fSLiam Girdwood 		return -EINVAL;
660868bd00fSLiam Girdwood 	}
661868bd00fSLiam Girdwood 
662868bd00fSLiam Girdwood 	/* read rate and channels from topology */
663868bd00fSLiam Girdwood 	switch (dai->dai_config->type) {
664868bd00fSLiam Girdwood 	case SOF_DAI_INTEL_SSP:
665868bd00fSLiam Girdwood 		rate->min = dai->dai_config->ssp.fsync_rate;
666868bd00fSLiam Girdwood 		rate->max = dai->dai_config->ssp.fsync_rate;
667868bd00fSLiam Girdwood 		channels->min = dai->dai_config->ssp.tdm_slots;
668868bd00fSLiam Girdwood 		channels->max = dai->dai_config->ssp.tdm_slots;
669868bd00fSLiam Girdwood 
670ee1e79b7SRanjani Sridharan 		dev_dbg(component->dev,
671868bd00fSLiam Girdwood 			"rate_min: %d rate_max: %d\n", rate->min, rate->max);
672ee1e79b7SRanjani Sridharan 		dev_dbg(component->dev,
673868bd00fSLiam Girdwood 			"channels_min: %d channels_max: %d\n",
674868bd00fSLiam Girdwood 			channels->min, channels->max);
675868bd00fSLiam Girdwood 
676868bd00fSLiam Girdwood 		break;
677868bd00fSLiam Girdwood 	case SOF_DAI_INTEL_DMIC:
678868bd00fSLiam Girdwood 		/* DMIC only supports 16 or 32 bit formats */
679868bd00fSLiam Girdwood 		if (dai->comp_dai.config.frame_fmt == SOF_IPC_FRAME_S24_4LE) {
680ee1e79b7SRanjani Sridharan 			dev_err(component->dev,
681868bd00fSLiam Girdwood 				"error: invalid fmt %d for DAI type %d\n",
682868bd00fSLiam Girdwood 				dai->comp_dai.config.frame_fmt,
683868bd00fSLiam Girdwood 				dai->dai_config->type);
684868bd00fSLiam Girdwood 		}
685868bd00fSLiam Girdwood 		break;
686868bd00fSLiam Girdwood 	case SOF_DAI_INTEL_HDA:
687868bd00fSLiam Girdwood 		/* do nothing for HDA dai_link */
688868bd00fSLiam Girdwood 		break;
6896e3360cdSPierre-Louis Bossart 	case SOF_DAI_INTEL_ALH:
6906e3360cdSPierre-Louis Bossart 		/* do nothing for ALH dai_link */
6916e3360cdSPierre-Louis Bossart 		break;
692a4eff5f8SDaniel Baluta 	case SOF_DAI_IMX_ESAI:
693a4eff5f8SDaniel Baluta 		channels->min = dai->dai_config->esai.tdm_slots;
694a4eff5f8SDaniel Baluta 		channels->max = dai->dai_config->esai.tdm_slots;
695a4eff5f8SDaniel Baluta 
696ee1e79b7SRanjani Sridharan 		dev_dbg(component->dev,
697a4eff5f8SDaniel Baluta 			"channels_min: %d channels_max: %d\n",
698a4eff5f8SDaniel Baluta 			channels->min, channels->max);
699a4eff5f8SDaniel Baluta 		break;
700868bd00fSLiam Girdwood 	default:
701ee1e79b7SRanjani Sridharan 		dev_err(component->dev, "error: invalid DAI type %d\n",
702868bd00fSLiam Girdwood 			dai->dai_config->type);
703868bd00fSLiam Girdwood 		break;
704868bd00fSLiam Girdwood 	}
705868bd00fSLiam Girdwood 
706868bd00fSLiam Girdwood 	return 0;
707868bd00fSLiam Girdwood }
708868bd00fSLiam Girdwood 
709868bd00fSLiam Girdwood static int sof_pcm_probe(struct snd_soc_component *component)
710868bd00fSLiam Girdwood {
711868bd00fSLiam Girdwood 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
712868bd00fSLiam Girdwood 	struct snd_sof_pdata *plat_data = sdev->pdata;
713868bd00fSLiam Girdwood 	const char *tplg_filename;
714868bd00fSLiam Girdwood 	int ret;
715868bd00fSLiam Girdwood 
716868bd00fSLiam Girdwood 	/* load the default topology */
717868bd00fSLiam Girdwood 	sdev->component = component;
718868bd00fSLiam Girdwood 
719868bd00fSLiam Girdwood 	tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
720868bd00fSLiam Girdwood 				       "%s/%s",
721868bd00fSLiam Girdwood 				       plat_data->tplg_filename_prefix,
722868bd00fSLiam Girdwood 				       plat_data->tplg_filename);
723868bd00fSLiam Girdwood 	if (!tplg_filename)
724868bd00fSLiam Girdwood 		return -ENOMEM;
725868bd00fSLiam Girdwood 
726ee1e79b7SRanjani Sridharan 	ret = snd_sof_load_topology(component, tplg_filename);
727868bd00fSLiam Girdwood 	if (ret < 0) {
728ee1e79b7SRanjani Sridharan 		dev_err(component->dev, "error: failed to load DSP topology %d\n",
729868bd00fSLiam Girdwood 			ret);
730868bd00fSLiam Girdwood 		return ret;
731868bd00fSLiam Girdwood 	}
732868bd00fSLiam Girdwood 
733868bd00fSLiam Girdwood 	return ret;
734868bd00fSLiam Girdwood }
735868bd00fSLiam Girdwood 
736868bd00fSLiam Girdwood static void sof_pcm_remove(struct snd_soc_component *component)
737868bd00fSLiam Girdwood {
738868bd00fSLiam Girdwood 	/* remove topology */
739868bd00fSLiam Girdwood 	snd_soc_tplg_component_remove(component, SND_SOC_TPLG_INDEX_ALL);
740868bd00fSLiam Girdwood }
741868bd00fSLiam Girdwood 
742868bd00fSLiam Girdwood void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
743868bd00fSLiam Girdwood {
744868bd00fSLiam Girdwood 	struct snd_soc_component_driver *pd = &sdev->plat_drv;
745868bd00fSLiam Girdwood 	struct snd_sof_pdata *plat_data = sdev->pdata;
746868bd00fSLiam Girdwood 	const char *drv_name;
747868bd00fSLiam Girdwood 
748868bd00fSLiam Girdwood 	drv_name = plat_data->machine->drv_name;
749868bd00fSLiam Girdwood 
750868bd00fSLiam Girdwood 	pd->name = "sof-audio-component";
751868bd00fSLiam Girdwood 	pd->probe = sof_pcm_probe;
752868bd00fSLiam Girdwood 	pd->remove = sof_pcm_remove;
7531c91d77eSKuninori Morimoto 	pd->open = sof_pcm_open;
7541c91d77eSKuninori Morimoto 	pd->close = sof_pcm_close;
7551c91d77eSKuninori Morimoto 	pd->hw_params = sof_pcm_hw_params;
7561c91d77eSKuninori Morimoto 	pd->prepare = sof_pcm_prepare;
7571c91d77eSKuninori Morimoto 	pd->hw_free = sof_pcm_hw_free;
7581c91d77eSKuninori Morimoto 	pd->trigger = sof_pcm_trigger;
7591c91d77eSKuninori Morimoto 	pd->pointer = sof_pcm_pointer;
7601c91d77eSKuninori Morimoto 
761868bd00fSLiam Girdwood #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
762868bd00fSLiam Girdwood 	pd->compr_ops = &sof_compressed_ops;
763868bd00fSLiam Girdwood #endif
7641c91d77eSKuninori Morimoto 	pd->pcm_construct = sof_pcm_new;
765868bd00fSLiam Girdwood 	pd->ignore_machine = drv_name;
766868bd00fSLiam Girdwood 	pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
767868bd00fSLiam Girdwood 	pd->be_pcm_base = SOF_BE_PCM_BASE;
768868bd00fSLiam Girdwood 	pd->use_dai_pcm_id = true;
769868bd00fSLiam Girdwood 	pd->topology_name_prefix = "sof";
770868bd00fSLiam Girdwood 
771868bd00fSLiam Girdwood 	 /* increment module refcount when a pcm is opened */
772868bd00fSLiam Girdwood 	pd->module_get_upon_open = 1;
773868bd00fSLiam Girdwood }
774