|
@@ -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)
|
|
|
{
|