summaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-dapm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r--sound/soc/soc-dapm.c155
1 files changed, 152 insertions, 3 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index bda29ccf88f4..96a10dc4c005 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -52,6 +52,7 @@ static int dapm_up_seq[] = {
[snd_soc_dapm_supply] = 1,
[snd_soc_dapm_regulator_supply] = 1,
[snd_soc_dapm_micbias] = 2,
+ [snd_soc_dapm_dai_link] = 2,
[snd_soc_dapm_dai] = 3,
[snd_soc_dapm_aif_in] = 3,
[snd_soc_dapm_aif_out] = 3,
@@ -88,9 +89,10 @@ static int dapm_down_seq[] = {
[snd_soc_dapm_aif_in] = 10,
[snd_soc_dapm_aif_out] = 10,
[snd_soc_dapm_dai] = 10,
- [snd_soc_dapm_regulator_supply] = 11,
- [snd_soc_dapm_supply] = 11,
- [snd_soc_dapm_post] = 12,
+ [snd_soc_dapm_dai_link] = 11,
+ [snd_soc_dapm_regulator_supply] = 12,
+ [snd_soc_dapm_supply] = 12,
+ [snd_soc_dapm_post] = 13,
};
static void pop_wait(u32 pop_time)
@@ -394,6 +396,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_mic:
case snd_soc_dapm_spk:
case snd_soc_dapm_line:
+ case snd_soc_dapm_dai_link:
p->connect = 1;
break;
/* does affect routing - dynamically connected */
@@ -2079,6 +2082,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai:
+ case snd_soc_dapm_dai_link:
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
@@ -2807,6 +2811,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
+ case snd_soc_dapm_dai_link:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_supply:
@@ -2871,6 +2876,150 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
+static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_dapm_path *source_p, *sink_p;
+ struct snd_soc_dai *source, *sink;
+ const struct snd_soc_pcm_stream *config = w->params;
+ struct snd_pcm_substream substream;
+ struct snd_pcm_hw_params params;
+ u64 fmt;
+ int ret;
+
+ BUG_ON(!config);
+ BUG_ON(list_empty(&w->sources) || list_empty(&w->sinks));
+
+ /* We only support a single source and sink, pick the first */
+ source_p = list_first_entry(&w->sources, struct snd_soc_dapm_path,
+ list_sink);
+ sink_p = list_first_entry(&w->sinks, struct snd_soc_dapm_path,
+ list_source);
+
+ BUG_ON(!source_p || !sink_p);
+ BUG_ON(!sink_p->source || !source_p->sink);
+ BUG_ON(!source_p->source || !sink_p->sink);
+
+ source = source_p->source->priv;
+ sink = sink_p->sink->priv;
+
+ /* Be a little careful as we don't want to overflow the mask array */
+ if (config->formats) {
+ fmt = ffs(config->formats) - 1;
+ } else {
+ dev_warn(w->dapm->dev, "Invalid format %lx specified\n",
+ config->formats);
+ fmt = 0;
+ }
+
+ /* Currently very limited parameter selection */
+ memset(&params, 0, sizeof(params));
+ snd_mask_set(hw_param_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT), fmt);
+
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE)->min =
+ config->rate_min;
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE)->max =
+ config->rate_max;
+
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS)->min
+ = config->channels_min;
+ hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS)->max
+ = config->channels_max;
+
+ memset(&substream, 0, sizeof(substream));
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (source->driver->ops && source->driver->ops->hw_params) {
+ substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+ ret = source->driver->ops->hw_params(&substream,
+ &params, source);
+ if (ret != 0) {
+ dev_err(source->dev,
+ "hw_params() failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (sink->driver->ops && sink->driver->ops->hw_params) {
+ substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+ ret = sink->driver->ops->hw_params(&substream, &params,
+ sink);
+ if (ret != 0) {
+ dev_err(sink->dev,
+ "hw_params() failed: %d\n", ret);
+ return ret;
+ }
+ }
+ break;
+
+ case SND_SOC_DAPM_POST_PMU:
+ ret = snd_soc_dai_digital_mute(sink, 0);
+ if (ret != 0 && ret != -ENOTSUPP)
+ dev_warn(sink->dev, "Failed to unmute: %d\n", ret);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = snd_soc_dai_digital_mute(sink, 1);
+ if (ret != 0 && ret != -ENOTSUPP)
+ dev_warn(sink->dev, "Failed to mute: %d\n", ret);
+ break;
+
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
+ const struct snd_soc_pcm_stream *params,
+ struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_route routes[2];
+ struct snd_soc_dapm_widget template;
+ struct snd_soc_dapm_widget *w;
+ size_t len;
+ char *link_name;
+
+ len = strlen(source->name) + strlen(sink->name) + 2;
+ link_name = devm_kzalloc(card->dev, len, GFP_KERNEL);
+ if (!link_name)
+ return -ENOMEM;
+ snprintf(link_name, len, "%s-%s", source->name, sink->name);
+
+ memset(&template, 0, sizeof(template));
+ template.reg = SND_SOC_NOPM;
+ template.id = snd_soc_dapm_dai_link;
+ template.name = link_name;
+ template.event = snd_soc_dai_link_event;
+ template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD;
+
+ dev_dbg(card->dev, "adding %s widget\n", link_name);
+
+ w = snd_soc_dapm_new_control(&card->dapm, &template);
+ if (!w) {
+ dev_err(card->dev, "Failed to create %s widget\n",
+ link_name);
+ return -ENOMEM;
+ }
+
+ w->params = params;
+
+ memset(&routes, 0, sizeof(routes));
+
+ routes[0].source = source->name;
+ routes[0].sink = link_name;
+ routes[1].source = link_name;
+ routes[1].sink = sink->name;
+
+ return snd_soc_dapm_add_routes(&card->dapm, routes,
+ ARRAY_SIZE(routes));
+}
+
int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
struct snd_soc_dai *dai)
{