ipc4-pcm.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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/sof/ipc4/header.h>
  10. #include "sof-audio.h"
  11. #include "sof-priv.h"
  12. #include "ipc4-priv.h"
  13. #include "ipc4-topology.h"
  14. int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
  15. {
  16. struct sof_ipc4_msg msg = {{ 0 }};
  17. u32 primary;
  18. dev_dbg(sdev->dev, "ipc4 set pipeline %d state %d", id, state);
  19. primary = state;
  20. primary |= SOF_IPC4_GLB_PIPE_STATE_ID(id);
  21. primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE);
  22. primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
  23. primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
  24. msg.primary = primary;
  25. return sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
  26. }
  27. EXPORT_SYMBOL(sof_ipc4_set_pipeline_state);
  28. static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
  29. struct snd_pcm_substream *substream, int state)
  30. {
  31. struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
  32. struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  33. struct snd_sof_widget *pipeline_widget;
  34. struct snd_soc_dapm_widget_list *list;
  35. struct snd_soc_dapm_widget *widget;
  36. struct sof_ipc4_pipeline *pipeline;
  37. struct snd_sof_widget *swidget;
  38. struct snd_sof_pcm *spcm;
  39. int ret = 0;
  40. int num_widgets;
  41. spcm = snd_sof_find_spcm_dai(component, rtd);
  42. if (!spcm)
  43. return -EINVAL;
  44. list = spcm->stream[substream->stream].list;
  45. for_each_dapm_widgets(list, num_widgets, widget) {
  46. swidget = widget->dobj.private;
  47. if (!swidget)
  48. continue;
  49. /*
  50. * set pipeline state for both FE and BE pipelines for RUNNING state.
  51. * For PAUSE/RESET, set the pipeline state only for the FE pipeline.
  52. */
  53. switch (state) {
  54. case SOF_IPC4_PIPE_PAUSED:
  55. case SOF_IPC4_PIPE_RESET:
  56. if (!WIDGET_IS_AIF(swidget->id))
  57. continue;
  58. break;
  59. default:
  60. break;
  61. }
  62. /* find pipeline widget for the pipeline that this widget belongs to */
  63. pipeline_widget = swidget->pipe_widget;
  64. pipeline = (struct sof_ipc4_pipeline *)pipeline_widget->private;
  65. if (pipeline->state == state)
  66. continue;
  67. /* first set the pipeline to PAUSED state */
  68. if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
  69. ret = sof_ipc4_set_pipeline_state(sdev, swidget->pipeline_id,
  70. SOF_IPC4_PIPE_PAUSED);
  71. if (ret < 0) {
  72. dev_err(sdev->dev, "failed to pause pipeline %d\n",
  73. swidget->pipeline_id);
  74. return ret;
  75. }
  76. }
  77. pipeline->state = SOF_IPC4_PIPE_PAUSED;
  78. if (pipeline->state == state)
  79. continue;
  80. /* then set the final state */
  81. ret = sof_ipc4_set_pipeline_state(sdev, swidget->pipeline_id, state);
  82. if (ret < 0) {
  83. dev_err(sdev->dev, "failed to set state %d for pipeline %d\n",
  84. state, swidget->pipeline_id);
  85. break;
  86. }
  87. pipeline->state = state;
  88. }
  89. return ret;
  90. }
  91. static int sof_ipc4_pcm_trigger(struct snd_soc_component *component,
  92. struct snd_pcm_substream *substream, int cmd)
  93. {
  94. int state;
  95. /* determine the pipeline state */
  96. switch (cmd) {
  97. case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  98. state = SOF_IPC4_PIPE_PAUSED;
  99. break;
  100. case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  101. case SNDRV_PCM_TRIGGER_RESUME:
  102. case SNDRV_PCM_TRIGGER_START:
  103. state = SOF_IPC4_PIPE_RUNNING;
  104. break;
  105. case SNDRV_PCM_TRIGGER_SUSPEND:
  106. case SNDRV_PCM_TRIGGER_STOP:
  107. state = SOF_IPC4_PIPE_PAUSED;
  108. break;
  109. default:
  110. dev_err(component->dev, "%s: unhandled trigger cmd %d\n", __func__, cmd);
  111. return -EINVAL;
  112. }
  113. /* set the pipeline state */
  114. return sof_ipc4_trigger_pipelines(component, substream, state);
  115. }
  116. static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component,
  117. struct snd_pcm_substream *substream)
  118. {
  119. return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET);
  120. }
  121. static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
  122. struct snd_pcm_hw_params *params)
  123. {
  124. struct snd_sof_dai_link *slink;
  125. struct snd_sof_dai *dai;
  126. bool dai_link_found = false;
  127. int i;
  128. list_for_each_entry(slink, &sdev->dai_link_list, list) {
  129. if (!strcmp(slink->link->name, link_name)) {
  130. dai_link_found = true;
  131. break;
  132. }
  133. }
  134. if (!dai_link_found)
  135. return;
  136. for (i = 0; i < slink->num_hw_configs; i++) {
  137. struct snd_soc_tplg_hw_config *hw_config = &slink->hw_configs[i];
  138. if (params_rate(params) == le32_to_cpu(hw_config->fsync_rate)) {
  139. /* set current config for all DAI's with matching name */
  140. list_for_each_entry(dai, &sdev->dai_list, list)
  141. if (!strcmp(slink->link->name, dai->name))
  142. dai->current_config = le32_to_cpu(hw_config->id);
  143. break;
  144. }
  145. }
  146. }
  147. static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
  148. struct snd_pcm_hw_params *params)
  149. {
  150. struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
  151. struct snd_sof_dai *dai = snd_sof_find_dai(component, rtd->dai_link->name);
  152. struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
  153. struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
  154. struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
  155. struct sof_ipc4_copier *ipc4_copier;
  156. struct snd_soc_dpcm *dpcm;
  157. if (!dai) {
  158. dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
  159. rtd->dai_link->name);
  160. return -EINVAL;
  161. }
  162. ipc4_copier = dai->private;
  163. if (!ipc4_copier) {
  164. dev_err(component->dev, "%s: No private data found for DAI %s\n",
  165. __func__, rtd->dai_link->name);
  166. return -EINVAL;
  167. }
  168. /* always set BE format to 32-bits for both playback and capture */
  169. snd_mask_none(fmt);
  170. snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
  171. rate->min = ipc4_copier->available_fmt.base_config->audio_fmt.sampling_frequency;
  172. rate->max = rate->min;
  173. /*
  174. * Set trigger order for capture to SND_SOC_DPCM_TRIGGER_PRE. This is required
  175. * to ensure that the BE DAI pipeline gets stopped/suspended before the FE DAI
  176. * pipeline gets triggered and the pipeline widgets are freed.
  177. */
  178. for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) {
  179. struct snd_soc_pcm_runtime *fe = dpcm->fe;
  180. fe->dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;
  181. }
  182. switch (ipc4_copier->dai_type) {
  183. case SOF_DAI_INTEL_SSP:
  184. ipc4_ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params);
  185. break;
  186. default:
  187. break;
  188. }
  189. return 0;
  190. }
  191. const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
  192. .trigger = sof_ipc4_pcm_trigger,
  193. .hw_free = sof_ipc4_pcm_hw_free,
  194. .dai_link_fixup = sof_ipc4_pcm_dai_link_fixup,
  195. };