diff options
Diffstat (limited to 'sound/soc/soc-dapm.c')
-rw-r--r-- | sound/soc/soc-dapm.c | 155 |
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(¶ms, 0, sizeof(params)); + snd_mask_set(hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT), fmt); + + hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_RATE)->min = + config->rate_min; + hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_RATE)->max = + config->rate_max; + + hw_param_interval(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS)->min + = config->channels_min; + hw_param_interval(¶ms, 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, + ¶ms, 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, ¶ms, + 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) { |