-
Notifications
You must be signed in to change notification settings - Fork 139
Introducing virtual FE #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5017eda
0452dd4
188a191
b829c2b
bd1132f
f00e120
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -855,6 +855,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w, | |
| kcname_in_long_name = true; | ||
| } else { | ||
| switch (w->id) { | ||
| case snd_soc_dapm_siggen: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as discussed last week, I wonder if we should also add a snd_soc_dapm_sig_sink (the dual of a generator). This would be very useful for testing e.g. by letting us set-up all sort of DMA channels with a self-test checking.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I will add it to my next update. |
||
| case snd_soc_dapm_switch: | ||
| case snd_soc_dapm_mixer: | ||
| case snd_soc_dapm_pga: | ||
|
|
@@ -1246,7 +1247,8 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, | |
| custom_stop_condition); | ||
|
|
||
| /* Drop starting point */ | ||
| list_del(widgets.next); | ||
| if (!list_is_singular(&widgets)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if you have more than one widget between widget and BE, it's valid and could be constructed.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part is a bit unclear to me. The problem I was facing was that the snd_soc_dapm_dai_get_connected_widgets() returned a list with only the BE DAI in the case of the tone pipeline and dropping the starting point resulted in an empty list of paths to process. the dai argument passed to this method was the cpu_dai->playback_widget() which is the BE dai in my case. And this was the only way I could get it to be enabled. I could use some help here to figure out the right way to do it.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, what happens if you have tone followed by a volume control?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do have a tone followed by volume in my topology. But the snd_soc_dapm_dai_get_connected_widgets() is called starting with the dai->playback_widget as the starting point. And this is the BE DAI.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should also state in a comment what we are doing here and why. |
||
| list_del(widgets.next); | ||
|
|
||
| ret = dapm_widget_list_create(list, &widgets); | ||
| if (ret) | ||
|
|
@@ -3076,6 +3078,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) | |
| case snd_soc_dapm_demux: | ||
| dapm_new_mux(w); | ||
| break; | ||
| case snd_soc_dapm_siggen: | ||
| case snd_soc_dapm_pga: | ||
| case snd_soc_dapm_out_drv: | ||
| dapm_new_pga(w); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1582,7 +1582,6 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, | |
|
|
||
| /* Create any new FE <--> BE connections */ | ||
| for (i = 0; i < list->num_widgets; i++) { | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. best to keep this line as is. |
||
| switch (list->widgets[i]->id) { | ||
| case snd_soc_dapm_dai_in: | ||
| if (stream != SNDRV_PCM_STREAM_PLAYBACK) | ||
|
|
@@ -2759,6 +2758,8 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card) | |
| mutex_unlock(&card->mutex); | ||
| return 0; | ||
| } | ||
| EXPORT_SYMBOL_GPL(soc_dpcm_runtime_update); | ||
|
|
||
| int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) | ||
| { | ||
| struct snd_soc_dpcm *dpcm; | ||
|
|
@@ -2789,6 +2790,109 @@ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) | |
| return 0; | ||
| } | ||
|
|
||
| /* | ||
| * create a virtual FE DAI link which has no pcm device registered. | ||
| * Virtual FE DAI links are used in hostless pipelines | ||
| * to enable the codecs when the pipeline is triggered | ||
| */ | ||
| int soc_dpcm_vfe_new(struct snd_soc_card *card, int index, | ||
| const char *link_name, const char *cpu_dai_name, | ||
| const char *platform_name, int stream_dir) | ||
| { | ||
| struct snd_soc_dai_link *link; | ||
|
|
||
| link = kzalloc(sizeof(*link), GFP_KERNEL); | ||
| if (!link) | ||
| return -ENOMEM; | ||
|
|
||
| dev_dbg(card->dev, "ASoC: adding new virtual FE DAI link %s\n", | ||
| link_name); | ||
|
|
||
| /* define FE DAI link */ | ||
| link->name = link_name; | ||
| link->cpu_dai_name = cpu_dai_name; | ||
| link->platform_name = platform_name; | ||
| link->codec_name = "snd-soc-dummy"; | ||
| link->codec_dai_name = "snd-soc-dummy-dai"; | ||
|
|
||
| /* | ||
| * Enabling both dynamic and no_pcm flags indicates the link is virtual | ||
| * i.e. it is a FE dai link but without a registered pcm device | ||
| */ | ||
| link->dynamic = 1; | ||
| link->no_pcm = 1; | ||
|
|
||
| /* enable playback */ | ||
| if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) | ||
| link->dpcm_playback = 1; | ||
|
|
||
| /* enable capture */ | ||
| if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) | ||
| link->dpcm_capture = 1; | ||
|
|
||
| link->dobj.index = index; | ||
| link->dobj.type = SND_SOC_DOBJ_DAI_LINK; | ||
|
|
||
| /* add link to card dai link list */ | ||
| snd_soc_add_dai_link(card, link); | ||
|
|
||
| return 0; | ||
| } | ||
| EXPORT_SYMBOL_GPL(soc_dpcm_vfe_new); | ||
|
|
||
| /* free virtual FE DAI link */ | ||
| int soc_dpcm_vfe_free(struct snd_soc_card *card, const char *link_name) | ||
| { | ||
| struct snd_soc_rtdcom_list *rtdcom1, *rtdcom2; | ||
| struct snd_soc_pcm_runtime *rtd, *temp; | ||
| struct snd_pcm_str *pstr; | ||
| int stream_dir; | ||
|
|
||
| list_for_each_entry_safe(rtd, temp, &card->rtd_list, list) { | ||
|
|
||
| /* delete requested FE link */ | ||
| if (!strcmp(rtd->dai_link->name, link_name)) { | ||
|
|
||
| if (rtd->dai_link->dpcm_playback) | ||
| stream_dir = SNDRV_PCM_STREAM_PLAYBACK; | ||
| else | ||
| stream_dir = SNDRV_PCM_STREAM_CAPTURE; | ||
|
|
||
| /* disconnect FE from BE */ | ||
| dpcm_be_disconnect(rtd, stream_dir); | ||
|
|
||
| /* free pcm runtime */ | ||
| kfree(rtd->dpcm[stream_dir].runtime); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question: all these frees don't reflect what is done for the _new method, could we rely on a helper to avoid tracking all these actions? It looks like either copy-paste or something extracted from somewhere else.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actually not a copy-paste at all. I traced all the steps from the point of adding the link to the time when the sound card is registered and freed all the data that is allocated. Here're the steps I traced:
So, in the vfe_free() method, I undid all of this. But I wonder if I can avoid all of this manually in the free method. Let me dig into these steps a bit more. |
||
|
|
||
| pstr = &rtd->pcm->streams[stream_dir]; | ||
|
|
||
| /* free pcm substream amd pcm */ | ||
| kfree(pstr->substream); | ||
|
|
||
| /* free pcm */ | ||
| kfree(rtd->pcm); | ||
|
|
||
| /* free codec dais and component list */ | ||
| kfree(rtd->codec_dais); | ||
|
|
||
| for_each_rtdcom_safe(rtd, rtdcom1, rtdcom2) | ||
| kfree(rtdcom1); | ||
|
|
||
| /* remove dai_link from card */ | ||
| snd_soc_remove_dai_link(card, rtd->dai_link); | ||
|
|
||
| /* free link */ | ||
| kfree(rtd->dai_link); | ||
|
|
||
| /* free runtime */ | ||
| kfree(rtd); | ||
| } | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
| EXPORT_SYMBOL_GPL(soc_dpcm_vfe_free); | ||
|
|
||
| static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) | ||
| { | ||
| struct snd_soc_pcm_runtime *fe = fe_substream->private_data; | ||
|
|
@@ -2996,6 +3100,55 @@ static int soc_rtdcom_mmap(struct snd_pcm_substream *substream, | |
| return -EINVAL; | ||
| } | ||
|
|
||
| /* | ||
| * for virtual FE dai links, there is no need | ||
| * to register PCM device. So only allocate memory for | ||
| * the dummy pcm device and substreams to be used for | ||
| * codec startup | ||
| */ | ||
| static int setup_virtual_fe(struct snd_soc_pcm_runtime *rtd, | ||
| struct snd_pcm *pcm) | ||
| { | ||
| struct snd_pcm_substream *substream; | ||
| struct snd_pcm_str *pstr; | ||
| int stream_dir; | ||
|
|
||
| /* create pcm */ | ||
| pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); | ||
| if (!pcm) | ||
| return -ENOMEM; | ||
|
|
||
| /* set up playback/capture substream */ | ||
| if (rtd->dai_link->dpcm_playback) | ||
| stream_dir = SNDRV_PCM_STREAM_PLAYBACK; | ||
| else | ||
| stream_dir = SNDRV_PCM_STREAM_CAPTURE; | ||
|
|
||
| pstr = &pcm->streams[stream_dir]; | ||
|
|
||
| /* allocate memory for substream */ | ||
| substream = kzalloc(sizeof(*substream), GFP_KERNEL); | ||
| if (!substream) | ||
| return -ENOMEM; | ||
|
|
||
| /* define substream */ | ||
| substream->pcm = pcm; | ||
| substream->pstr = pstr; | ||
| substream->number = 0; | ||
| substream->stream = stream_dir; | ||
| sprintf(substream->name, "subdevice #%i", 0); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the name exposed by /sysff or /proc ? if so, do we have any naming collisions when we register 2 or more VFE ?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm I did not check if it is exposed. Let me try to make it unique |
||
| substream->buffer_bytes_max = UINT_MAX; | ||
|
|
||
| /* set pcm substream */ | ||
| pstr->substream = substream; | ||
|
|
||
| pcm->nonatomic = rtd->dai_link->nonatomic; | ||
| rtd->pcm = pcm; | ||
| pcm->private_data = rtd; | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| /* create a new pcm */ | ||
| int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) | ||
| { | ||
|
|
@@ -3043,6 +3196,13 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) | |
| ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, | ||
| playback, capture, &pcm); | ||
| } else { | ||
|
|
||
| /* set up virtual FE dai link */ | ||
| if (rtd->dai_link->dynamic && rtd->dai_link->no_pcm) { | ||
| setup_virtual_fe(rtd, pcm); | ||
| goto out; | ||
| } | ||
|
|
||
| if (rtd->dai_link->dynamic) | ||
| snprintf(new_name, sizeof(new_name), "%s (*)", | ||
| rtd->dai_link->stream_name); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what 'active' runtime means. I would think it becomes 'active' when the switch is On, but maybe it's a different concept you are referring to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, what I mean by active here is when the kcontrol is switched on and the dpcm_runtime_update() is called, it checks to see if playback or capture is active on the FE link. So I set the FE playback/capture active status by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, but I wonder if this has negative implications related to suspend/resume? If there is nothing going on it's a bit odd to force a runtime to be active.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, thats a good point.
The problem is the runtime_update() method is oblivious to where it is being called from. It just looks for an active link to process the update.
Maybe I can avoid this by activating the FE in the kcontrol IO handler just before calling runtime_update.