@@ -4014,6 +4014,108 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
40144014 return ret ;
40154015}
40164016
4017+ static int
4018+ snd_soc_dai_link_loopback_event_pre_pmu (struct snd_soc_dapm_widget * w ,
4019+ struct snd_pcm_substream * substream )
4020+ {
4021+ struct snd_soc_dapm_path * path ;
4022+ struct snd_soc_dai * source , * sink ;
4023+ struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd (substream );
4024+ int ret = 0 ;
4025+
4026+ rtd -> dpcm [SNDRV_PCM_STREAM_PLAYBACK ].state = SND_SOC_DPCM_STATE_START ;
4027+ rtd -> dpcm [SNDRV_PCM_STREAM_CAPTURE ].state = SND_SOC_DPCM_STATE_START ;
4028+
4029+ substream -> stream = SNDRV_PCM_STREAM_CAPTURE ;
4030+ snd_soc_dapm_widget_for_each_source_path (w , path ) {
4031+ source = path -> source -> priv ;
4032+ snd_soc_dai_activate (source , substream -> stream );
4033+ }
4034+
4035+ substream -> stream = SNDRV_PCM_STREAM_PLAYBACK ;
4036+ snd_soc_dapm_widget_for_each_sink_path (w , path ) {
4037+ sink = path -> sink -> priv ;
4038+ snd_soc_dai_activate (sink , substream -> stream );
4039+ }
4040+
4041+ return ret ;
4042+ }
4043+
4044+ static int snd_soc_dai_link_loopback_event (struct snd_soc_dapm_widget * w ,
4045+ struct snd_kcontrol * kcontrol , int event )
4046+ {
4047+ struct snd_soc_dapm_path * path ;
4048+ struct snd_soc_dai * source , * sink ;
4049+ struct snd_pcm_substream * substream = w -> priv ;
4050+ int ret = 0 , saved_stream = substream -> stream ;
4051+ struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd (substream );
4052+
4053+ if (WARN_ON (list_empty (& w -> edges [SND_SOC_DAPM_DIR_OUT ]) ||
4054+ list_empty (& w -> edges [SND_SOC_DAPM_DIR_IN ])))
4055+ return - EINVAL ;
4056+
4057+ switch (event ) {
4058+ case SND_SOC_DAPM_PRE_PMU :
4059+ ret = snd_soc_dai_link_loopback_event_pre_pmu (w , substream );
4060+ if (ret < 0 )
4061+ goto out ;
4062+
4063+ break ;
4064+
4065+ case SND_SOC_DAPM_POST_PMU :
4066+ snd_soc_dapm_widget_for_each_sink_path (w , path ) {
4067+ sink = path -> sink -> priv ;
4068+
4069+ ret = snd_soc_dai_digital_mute (sink , 0 ,
4070+ SNDRV_PCM_STREAM_PLAYBACK );
4071+ if (ret != 0 && ret != - EOPNOTSUPP )
4072+ dev_warn (sink -> dev ,
4073+ "ASoC: Failed to unmute: %d\n" , ret );
4074+ ret = 0 ;
4075+ }
4076+ break ;
4077+
4078+ case SND_SOC_DAPM_PRE_PMD :
4079+ snd_soc_dapm_widget_for_each_sink_path (w , path ) {
4080+ sink = path -> sink -> priv ;
4081+
4082+ ret = snd_soc_dai_digital_mute (sink , 1 ,
4083+ SNDRV_PCM_STREAM_PLAYBACK );
4084+ if (ret != 0 && ret != - EOPNOTSUPP )
4085+ dev_warn (sink -> dev ,
4086+ "ASoC: Failed to mute: %d\n" , ret );
4087+ ret = 0 ;
4088+ }
4089+
4090+ substream -> stream = SNDRV_PCM_STREAM_CAPTURE ;
4091+ snd_soc_dapm_widget_for_each_source_path (w , path ) {
4092+ source = path -> source -> priv ;
4093+ snd_soc_dai_deactivate (source , substream -> stream );
4094+ }
4095+
4096+ substream -> stream = SNDRV_PCM_STREAM_PLAYBACK ;
4097+ snd_soc_dapm_widget_for_each_sink_path (w , path ) {
4098+ sink = path -> sink -> priv ;
4099+ snd_soc_dai_deactivate (sink , substream -> stream );
4100+ }
4101+ break ;
4102+
4103+ case SND_SOC_DAPM_POST_PMD :
4104+ rtd -> dpcm [SNDRV_PCM_STREAM_PLAYBACK ].state = SND_SOC_DPCM_STATE_CLOSE ;
4105+ rtd -> dpcm [SNDRV_PCM_STREAM_CAPTURE ].state = SND_SOC_DPCM_STATE_CLOSE ;
4106+ break ;
4107+
4108+ default :
4109+ WARN (1 , "Unknown event %d\n" , event );
4110+ ret = - EINVAL ;
4111+ }
4112+
4113+ out :
4114+ /* Restore the substream direction */
4115+ substream -> stream = saved_stream ;
4116+ return ret ;
4117+ }
4118+
40174119static int snd_soc_dapm_dai_link_get (struct snd_kcontrol * kcontrol ,
40184120 struct snd_ctl_elem_value * ucontrol )
40194121{
@@ -4132,6 +4234,47 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
41324234 return NULL ;
41334235}
41344236
4237+ static struct snd_soc_dapm_widget *
4238+ snd_soc_dapm_new_loopback_dai (struct snd_soc_card * card , struct snd_pcm_substream * substream ,
4239+ char * id )
4240+ {
4241+ struct snd_soc_pcm_runtime * rtd = asoc_substream_to_rtd (substream );
4242+ struct snd_soc_dapm_widget template ;
4243+ struct snd_soc_dapm_widget * w ;
4244+ char * link_name ;
4245+ int ret ;
4246+
4247+ link_name = devm_kasprintf (card -> dev , GFP_KERNEL , "%s-%s" ,
4248+ rtd -> dai_link -> name , id );
4249+ if (!link_name )
4250+ return ERR_PTR (- ENOMEM );
4251+
4252+ memset (& template , 0 , sizeof (template ));
4253+ template .reg = SND_SOC_NOPM ;
4254+ template .id = snd_soc_dapm_dai_link ;
4255+ template .name = link_name ;
4256+ template .event = snd_soc_dai_link_loopback_event ;
4257+ template .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
4258+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD ;
4259+ template .kcontrol_news = NULL ;
4260+
4261+ w = snd_soc_dapm_new_control_unlocked (& card -> dapm , & template );
4262+ if (IS_ERR (w )) {
4263+ ret = PTR_ERR (w );
4264+ dev_err (rtd -> dev , "ASoC: Failed to create %s widget: %d\n" ,
4265+ link_name , ret );
4266+ goto outfree_link_name ;
4267+ }
4268+
4269+ w -> priv = substream ;
4270+
4271+ return w ;
4272+
4273+ outfree_link_name :
4274+ devm_kfree (card -> dev , link_name );
4275+ return ERR_PTR (ret );
4276+ }
4277+
41354278static struct snd_soc_dapm_widget *
41364279snd_soc_dapm_new_dai (struct snd_soc_card * card ,
41374280 struct snd_pcm_substream * substream ,
@@ -4337,14 +4480,30 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card,
43374480 struct snd_pcm_substream * substream ;
43384481 struct snd_pcm_str * streams = rtd -> pcm -> streams ;
43394482
4340- if (dai_link -> params ) {
4483+ if (dai_link -> params || ( rtd -> dai_link -> dynamic && rtd -> dai_link -> dpcm_loopback ) ) {
43414484 playback_cpu = cpu_dai -> capture_widget ;
43424485 capture_cpu = cpu_dai -> playback_widget ;
43434486 } else {
43444487 playback_cpu = cpu_dai -> playback_widget ;
43454488 capture_cpu = cpu_dai -> capture_widget ;
43464489 }
43474490
4491+ if (rtd -> dai_link -> dynamic && rtd -> dai_link -> dpcm_loopback ) {
4492+ if (!rtd -> playback_widget && !rtd -> capture_widget ) {
4493+ streams [SNDRV_PCM_STREAM_PLAYBACK ].substream -> private_data = rtd ;
4494+ streams [SNDRV_PCM_STREAM_CAPTURE ].substream -> private_data = rtd ;
4495+ substream = streams [SNDRV_PCM_STREAM_PLAYBACK ].substream ;
4496+ dai = snd_soc_dapm_new_loopback_dai (card , substream , "hostless" );
4497+ if (IS_ERR (dai ))
4498+ return ;
4499+ rtd -> playback_widget = dai ;
4500+ rtd -> capture_widget = dai ;
4501+ }
4502+ dapm_connect_dai_routes (& card -> dapm , cpu_dai , playback_cpu ,
4503+ dai ,
4504+ cpu_dai , capture_cpu );
4505+ return ;
4506+ }
43484507 /* connect BE DAI playback if widgets are valid */
43494508 codec = codec_dai -> playback_widget ;
43504509
@@ -4431,13 +4590,19 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
44314590 * dynamic FE links have no fixed DAI mapping.
44324591 * CODEC<->CODEC links have no direct connection.
44334592 */
4434- if (rtd -> dai_link -> dynamic )
4593+ if (rtd -> dai_link -> dynamic && ! rtd -> dai_link -> dpcm_loopback )
44354594 continue ;
44364595
44374596 if (rtd -> num_cpus == 1 ) {
4438- for_each_rtd_codec_dais (rtd , i , codec_dai )
4439- dapm_connect_dai_pair (card , rtd , codec_dai ,
4597+ /* connect FE to FE */
4598+ if (rtd -> dai_link -> dynamic && rtd -> dai_link -> dpcm_loopback ) {
4599+ dapm_connect_dai_pair (card , rtd , asoc_rtd_to_cpu (rtd , 0 ),
44404600 asoc_rtd_to_cpu (rtd , 0 ));
4601+ } else {
4602+ for_each_rtd_codec_dais (rtd , i , codec_dai )
4603+ dapm_connect_dai_pair (card , rtd , codec_dai ,
4604+ asoc_rtd_to_cpu (rtd , 0 ));
4605+ }
44414606 } else if (rtd -> num_codecs == rtd -> num_cpus ) {
44424607 for_each_rtd_codec_dais (rtd , i , codec_dai )
44434608 dapm_connect_dai_pair (card , rtd , codec_dai ,
0 commit comments