~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/sof/ipc3-pcm.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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) 2021 Intel Corporation
  7 //
  8 //
  9 
 10 #include <sound/pcm_params.h>
 11 #include "ipc3-priv.h"
 12 #include "ops.h"
 13 #include "sof-priv.h"
 14 #include "sof-audio.h"
 15 
 16 static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component,
 17                                 struct snd_pcm_substream *substream)
 18 {
 19         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
 20         struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 21         struct sof_ipc_stream stream;
 22         struct snd_sof_pcm *spcm;
 23 
 24         spcm = snd_sof_find_spcm_dai(component, rtd);
 25         if (!spcm)
 26                 return -EINVAL;
 27 
 28         if (!spcm->prepared[substream->stream])
 29                 return 0;
 30 
 31         stream.hdr.size = sizeof(stream);
 32         stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE;
 33         stream.comp_id = spcm->stream[substream->stream].comp_id;
 34 
 35         /* send IPC to the DSP */
 36         return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
 37 }
 38 
 39 static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
 40                                   struct snd_pcm_substream *substream,
 41                                   struct snd_pcm_hw_params *params,
 42                                   struct snd_sof_platform_stream_params *platform_params)
 43 {
 44         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
 45         struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
 46         struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
 47         struct snd_pcm_runtime *runtime = substream->runtime;
 48         struct sof_ipc_pcm_params_reply ipc_params_reply;
 49         struct sof_ipc_pcm_params pcm;
 50         struct snd_sof_pcm *spcm;
 51         int ret;
 52 
 53         spcm = snd_sof_find_spcm_dai(component, rtd);
 54         if (!spcm)
 55                 return -EINVAL;
 56 
 57         memset(&pcm, 0, sizeof(pcm));
 58 
 59         /* number of pages should be rounded up */
 60         pcm.params.buffer.pages = PFN_UP(runtime->dma_bytes);
 61 
 62         /* set IPC PCM parameters */
 63         pcm.hdr.size = sizeof(pcm);
 64         pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS;
 65         pcm.comp_id = spcm->stream[substream->stream].comp_id;
 66         pcm.params.hdr.size = sizeof(pcm.params);
 67         pcm.params.buffer.phy_addr = spcm->stream[substream->stream].page_table.addr;
 68         pcm.params.buffer.size = runtime->dma_bytes;
 69         pcm.params.direction = substream->stream;
 70         pcm.params.sample_valid_bytes = params_width(params) >> 3;
 71         pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
 72         pcm.params.rate = params_rate(params);
 73         pcm.params.channels = params_channels(params);
 74         pcm.params.host_period_bytes = params_period_bytes(params);
 75 
 76         /* container size */
 77         ret = snd_pcm_format_physical_width(params_format(params));
 78         if (ret < 0)
 79                 return ret;
 80         pcm.params.sample_container_bytes = ret >> 3;
 81 
 82         /* format */
 83         switch (params_format(params)) {
 84         case SNDRV_PCM_FORMAT_S16:
 85                 pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE;
 86                 break;
 87         case SNDRV_PCM_FORMAT_S24:
 88                 pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE;
 89                 break;
 90         case SNDRV_PCM_FORMAT_S32:
 91                 pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE;
 92                 break;
 93         case SNDRV_PCM_FORMAT_FLOAT:
 94                 pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT;
 95                 break;
 96         default:
 97                 return -EINVAL;
 98         }
 99 
100         /* Update the IPC message with information from the platform */
101         pcm.params.stream_tag = platform_params->stream_tag;
102 
103         if (platform_params->use_phy_address)
104                 pcm.params.buffer.phy_addr = platform_params->phy_addr;
105 
106         if (platform_params->no_ipc_position) {
107                 /* For older ABIs set host_period_bytes to zero to inform
108                  * FW we don't want position updates. Newer versions use
109                  * no_stream_position for this purpose.
110                  */
111                 if (v->abi_version < SOF_ABI_VER(3, 10, 0))
112                         pcm.params.host_period_bytes = 0;
113                 else
114                         pcm.params.no_stream_position = 1;
115         }
116 
117         if (platform_params->cont_update_posn)
118                 pcm.params.cont_update_posn = 1;
119 
120         dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag);
121 
122         /* send hw_params IPC to the DSP */
123         ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
124                                  &ipc_params_reply, sizeof(ipc_params_reply));
125         if (ret < 0) {
126                 dev_err(component->dev, "HW params ipc failed for stream %d\n",
127                         pcm.params.stream_tag);
128                 return ret;
129         }
130 
131         ret = snd_sof_set_stream_data_offset(sdev, &spcm->stream[substream->stream],
132                                              ipc_params_reply.posn_offset);
133         if (ret < 0) {
134                 dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n",
135                         __func__, spcm->pcm.pcm_id);
136                 return ret;
137         }
138 
139         return ret;
140 }
141 
142 static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
143                                 struct snd_pcm_substream *substream, int cmd)
144 {
145         struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
146         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
147         struct sof_ipc_stream stream;
148         struct snd_sof_pcm *spcm;
149 
150         spcm = snd_sof_find_spcm_dai(component, rtd);
151         if (!spcm)
152                 return -EINVAL;
153 
154         stream.hdr.size = sizeof(stream);
155         stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG;
156         stream.comp_id = spcm->stream[substream->stream].comp_id;
157 
158         switch (cmd) {
159         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
160                 stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE;
161                 break;
162         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
163                 stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE;
164                 break;
165         case SNDRV_PCM_TRIGGER_START:
166                 stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START;
167                 break;
168         case SNDRV_PCM_TRIGGER_SUSPEND:
169                 fallthrough;
170         case SNDRV_PCM_TRIGGER_STOP:
171                 stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
172                 break;
173         default:
174                 dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd);
175                 return -EINVAL;
176         }
177 
178         /* send IPC to the DSP */
179         return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));
180 }
181 
182 static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
183                                             struct snd_pcm_hw_params *params)
184 {
185         struct sof_ipc_dai_config *config;
186         struct snd_sof_dai *dai;
187         int i;
188 
189         /*
190          * Search for all matching DAIs as we can have both playback and capture DAI
191          * associated with the same link.
192          */
193         list_for_each_entry(dai, &sdev->dai_list, list) {
194                 if (!dai->name || strcmp(link_name, dai->name))
195                         continue;
196                 for (i = 0; i < dai->number_configs; i++) {
197                         struct sof_dai_private_data *private = dai->private;
198 
199                         config = &private->dai_config[i];
200                         if (config->ssp.fsync_rate == params_rate(params)) {
201                                 dev_dbg(sdev->dev, "DAI config %d matches pcm hw params\n", i);
202                                 dai->current_config = i;
203                                 break;
204                         }
205                 }
206         }
207 }
208 
209 static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
210                                        struct snd_pcm_hw_params *params)
211 {
212         struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
213         struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
214         struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name);
215         struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
216         struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
217         struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
218         struct sof_dai_private_data *private;
219         struct snd_soc_dpcm *dpcm;
220 
221         if (!dai) {
222                 dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
223                         rtd->dai_link->name);
224                 return -EINVAL;
225         }
226 
227         private = dai->private;
228         if (!private) {
229                 dev_err(component->dev, "%s: No private data found for DAI %s\n", __func__,
230                         rtd->dai_link->name);
231                 return -EINVAL;
232         }
233 
234         /* read format from topology */
235         snd_mask_none(fmt);
236 
237         switch (private->comp_dai->config.frame_fmt) {
238         case SOF_IPC_FRAME_S16_LE:
239                 snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
240                 break;
241         case SOF_IPC_FRAME_S24_4LE:
242                 snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
243                 break;
244         case SOF_IPC_FRAME_S32_LE:
245                 snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
246                 break;
247         default:
248                 dev_err(component->dev, "No available DAI format!\n");
249                 return -EINVAL;
250         }
251 
252         /* read rate and channels from topology */
253         switch (private->dai_config->type) {
254         case SOF_DAI_INTEL_SSP:
255                 /* search for config to pcm params match, if not found use default */
256                 ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params);
257 
258                 rate->min = private->dai_config[dai->current_config].ssp.fsync_rate;
259                 rate->max = private->dai_config[dai->current_config].ssp.fsync_rate;
260                 channels->min = private->dai_config[dai->current_config].ssp.tdm_slots;
261                 channels->max = private->dai_config[dai->current_config].ssp.tdm_slots;
262 
263                 dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
264                 dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
265                         channels->min, channels->max);
266 
267                 break;
268         case SOF_DAI_INTEL_DMIC:
269                 /* DMIC only supports 16 or 32 bit formats */
270                 if (private->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) {
271                         dev_err(component->dev, "Invalid fmt %d for DAI type %d\n",
272                                 private->comp_dai->config.frame_fmt,
273                                 private->dai_config->type);
274                 }
275                 break;
276         case SOF_DAI_INTEL_HDA:
277                 /*
278                  * HDAudio does not follow the default trigger
279                  * sequence due to firmware implementation
280                  */
281                 for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) {
282                         struct snd_soc_pcm_runtime *fe = dpcm->fe;
283 
284                         fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
285                                 SND_SOC_DPCM_TRIGGER_POST;
286                 }
287                 break;
288         case SOF_DAI_INTEL_ALH:
289                 /*
290                  * Dai could run with different channel count compared with
291                  * front end, so get dai channel count from topology
292                  */
293                 channels->min = private->dai_config->alh.channels;
294                 channels->max = private->dai_config->alh.channels;
295                 break;
296         case SOF_DAI_IMX_ESAI:
297                 rate->min = private->dai_config->esai.fsync_rate;
298                 rate->max = private->dai_config->esai.fsync_rate;
299                 channels->min = private->dai_config->esai.tdm_slots;
300                 channels->max = private->dai_config->esai.tdm_slots;
301 
302                 dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
303                 dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
304                         channels->min, channels->max);
305                 break;
306         case SOF_DAI_MEDIATEK_AFE:
307                 rate->min = private->dai_config->afe.rate;
308                 rate->max = private->dai_config->afe.rate;
309                 channels->min = private->dai_config->afe.channels;
310                 channels->max = private->dai_config->afe.channels;
311 
312                 snd_mask_none(fmt);
313 
314                 switch (private->dai_config->afe.format) {
315                 case SOF_IPC_FRAME_S16_LE:
316                         snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
317                         break;
318                 case SOF_IPC_FRAME_S24_4LE:
319                         snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
320                         break;
321                 case SOF_IPC_FRAME_S32_LE:
322                         snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
323                         break;
324                 default:
325                         dev_err(component->dev, "Not available format!\n");
326                         return -EINVAL;
327                 }
328 
329                 dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
330                 dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
331                         channels->min, channels->max);
332                 break;
333         case SOF_DAI_IMX_SAI:
334                 rate->min = private->dai_config->sai.fsync_rate;
335                 rate->max = private->dai_config->sai.fsync_rate;
336                 channels->min = private->dai_config->sai.tdm_slots;
337                 channels->max = private->dai_config->sai.tdm_slots;
338 
339                 dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
340                 dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
341                         channels->min, channels->max);
342                 break;
343         case SOF_DAI_AMD_BT:
344                 rate->min = private->dai_config->acpbt.fsync_rate;
345                 rate->max = private->dai_config->acpbt.fsync_rate;
346                 channels->min = private->dai_config->acpbt.tdm_slots;
347                 channels->max = private->dai_config->acpbt.tdm_slots;
348 
349                 dev_dbg(component->dev,
350                         "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max);
351                 dev_dbg(component->dev, "AMD_BT channels_min: %d channels_max: %d\n",
352                         channels->min, channels->max);
353                 break;
354         case SOF_DAI_AMD_SP:
355         case SOF_DAI_AMD_SP_VIRTUAL:
356                 rate->min = private->dai_config->acpsp.fsync_rate;
357                 rate->max = private->dai_config->acpsp.fsync_rate;
358                 channels->min = private->dai_config->acpsp.tdm_slots;
359                 channels->max = private->dai_config->acpsp.tdm_slots;
360 
361                 dev_dbg(component->dev,
362                         "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max);
363                 dev_dbg(component->dev, "AMD_SP channels_min: %d channels_max: %d\n",
364                         channels->min, channels->max);
365                 break;
366         case SOF_DAI_AMD_HS:
367         case SOF_DAI_AMD_HS_VIRTUAL:
368                 rate->min = private->dai_config->acphs.fsync_rate;
369                 rate->max = private->dai_config->acphs.fsync_rate;
370                 channels->min = private->dai_config->acphs.tdm_slots;
371                 channels->max = private->dai_config->acphs.tdm_slots;
372 
373                 dev_dbg(component->dev,
374                         "AMD_HS channel_max: %d rate_max: %d\n", channels->max, rate->max);
375                 break;
376         case SOF_DAI_AMD_DMIC:
377                 rate->min = private->dai_config->acpdmic.pdm_rate;
378                 rate->max = private->dai_config->acpdmic.pdm_rate;
379                 channels->min = private->dai_config->acpdmic.pdm_ch;
380                 channels->max = private->dai_config->acpdmic.pdm_ch;
381 
382                 dev_dbg(component->dev,
383                         "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max);
384                 dev_dbg(component->dev, "AMD_DMIC channels_min: %d channels_max: %d\n",
385                         channels->min, channels->max);
386                 break;
387         case SOF_DAI_IMX_MICFIL:
388                 rate->min = private->dai_config->micfil.pdm_rate;
389                 rate->max = private->dai_config->micfil.pdm_rate;
390                 channels->min = private->dai_config->micfil.pdm_ch;
391                 channels->max = private->dai_config->micfil.pdm_ch;
392 
393                 dev_dbg(component->dev,
394                         "MICFIL PDM rate_min: %d rate_max: %d\n", rate->min, rate->max);
395                 dev_dbg(component->dev, "MICFIL PDM channels_min: %d channels_max: %d\n",
396                         channels->min, channels->max);
397                 break;
398         case SOF_DAI_AMD_SDW:
399                 /* change the default trigger sequence as per HW implementation */
400                 for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) {
401                         struct snd_soc_pcm_runtime *fe = dpcm->fe;
402 
403                         fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
404                                         SND_SOC_DPCM_TRIGGER_POST;
405                 }
406 
407                 for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) {
408                         struct snd_soc_pcm_runtime *fe = dpcm->fe;
409 
410                         fe->dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] =
411                                         SND_SOC_DPCM_TRIGGER_POST;
412                 }
413                 rate->min = private->dai_config->acp_sdw.rate;
414                 rate->max = private->dai_config->acp_sdw.rate;
415                 channels->min = private->dai_config->acp_sdw.channels;
416                 channels->max = private->dai_config->acp_sdw.channels;
417 
418                 dev_dbg(component->dev,
419                         "AMD_SDW rate_min: %d rate_max: %d\n", rate->min, rate->max);
420                 dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n",
421                         channels->min, channels->max);
422                 break;
423         default:
424                 dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type);
425                 break;
426         }
427 
428         return 0;
429 }
430 
431 const struct sof_ipc_pcm_ops ipc3_pcm_ops = {
432         .hw_params = sof_ipc3_pcm_hw_params,
433         .hw_free = sof_ipc3_pcm_hw_free,
434         .trigger = sof_ipc3_pcm_trigger,
435         .dai_link_fixup = sof_ipc3_pcm_dai_link_fixup,
436         .reset_hw_params_during_stop = true,
437         .d0i3_supported_in_s0ix = true,
438 };
439 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php