aboutsummaryrefslogtreecommitdiffstats
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.c257
1 files changed, 199 insertions, 58 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index a5178845065b..1ec06ef6d161 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -64,61 +64,85 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
/* dapm power sequences - make this per codec in the future */
static int dapm_up_seq[] = {
- [snd_soc_dapm_pre] = 0,
- [snd_soc_dapm_regulator_supply] = 1,
- [snd_soc_dapm_pinctrl] = 1,
- [snd_soc_dapm_clock_supply] = 1,
- [snd_soc_dapm_supply] = 2,
- [snd_soc_dapm_micbias] = 3,
- [snd_soc_dapm_dai_link] = 2,
- [snd_soc_dapm_dai_in] = 4,
- [snd_soc_dapm_dai_out] = 4,
- [snd_soc_dapm_aif_in] = 4,
- [snd_soc_dapm_aif_out] = 4,
- [snd_soc_dapm_mic] = 5,
- [snd_soc_dapm_mux] = 6,
- [snd_soc_dapm_demux] = 6,
- [snd_soc_dapm_dac] = 7,
- [snd_soc_dapm_switch] = 8,
- [snd_soc_dapm_mixer] = 8,
- [snd_soc_dapm_mixer_named_ctl] = 8,
- [snd_soc_dapm_pga] = 9,
- [snd_soc_dapm_adc] = 10,
- [snd_soc_dapm_out_drv] = 11,
- [snd_soc_dapm_hp] = 11,
- [snd_soc_dapm_spk] = 11,
- [snd_soc_dapm_line] = 11,
- [snd_soc_dapm_kcontrol] = 12,
- [snd_soc_dapm_post] = 13,
+ [snd_soc_dapm_pre] = 1,
+ [snd_soc_dapm_regulator_supply] = 2,
+ [snd_soc_dapm_pinctrl] = 2,
+ [snd_soc_dapm_clock_supply] = 2,
+ [snd_soc_dapm_supply] = 3,
+ [snd_soc_dapm_micbias] = 4,
+ [snd_soc_dapm_vmid] = 4,
+ [snd_soc_dapm_dai_link] = 3,
+ [snd_soc_dapm_dai_in] = 5,
+ [snd_soc_dapm_dai_out] = 5,
+ [snd_soc_dapm_aif_in] = 5,
+ [snd_soc_dapm_aif_out] = 5,
+ [snd_soc_dapm_mic] = 6,
+ [snd_soc_dapm_siggen] = 6,
+ [snd_soc_dapm_input] = 6,
+ [snd_soc_dapm_output] = 6,
+ [snd_soc_dapm_mux] = 7,
+ [snd_soc_dapm_demux] = 7,
+ [snd_soc_dapm_dac] = 8,
+ [snd_soc_dapm_switch] = 9,
+ [snd_soc_dapm_mixer] = 9,
+ [snd_soc_dapm_mixer_named_ctl] = 9,
+ [snd_soc_dapm_pga] = 10,
+ [snd_soc_dapm_buffer] = 10,
+ [snd_soc_dapm_scheduler] = 10,
+ [snd_soc_dapm_effect] = 10,
+ [snd_soc_dapm_src] = 10,
+ [snd_soc_dapm_asrc] = 10,
+ [snd_soc_dapm_encoder] = 10,
+ [snd_soc_dapm_decoder] = 10,
+ [snd_soc_dapm_adc] = 11,
+ [snd_soc_dapm_out_drv] = 12,
+ [snd_soc_dapm_hp] = 12,
+ [snd_soc_dapm_spk] = 12,
+ [snd_soc_dapm_line] = 12,
+ [snd_soc_dapm_sink] = 12,
+ [snd_soc_dapm_kcontrol] = 13,
+ [snd_soc_dapm_post] = 14,
};
static int dapm_down_seq[] = {
- [snd_soc_dapm_pre] = 0,
- [snd_soc_dapm_kcontrol] = 1,
- [snd_soc_dapm_adc] = 2,
- [snd_soc_dapm_hp] = 3,
- [snd_soc_dapm_spk] = 3,
- [snd_soc_dapm_line] = 3,
- [snd_soc_dapm_out_drv] = 3,
- [snd_soc_dapm_pga] = 4,
- [snd_soc_dapm_switch] = 5,
- [snd_soc_dapm_mixer_named_ctl] = 5,
- [snd_soc_dapm_mixer] = 5,
- [snd_soc_dapm_dac] = 6,
- [snd_soc_dapm_mic] = 7,
- [snd_soc_dapm_micbias] = 8,
- [snd_soc_dapm_mux] = 9,
- [snd_soc_dapm_demux] = 9,
- [snd_soc_dapm_aif_in] = 10,
- [snd_soc_dapm_aif_out] = 10,
- [snd_soc_dapm_dai_in] = 10,
- [snd_soc_dapm_dai_out] = 10,
- [snd_soc_dapm_dai_link] = 11,
- [snd_soc_dapm_supply] = 12,
- [snd_soc_dapm_clock_supply] = 13,
- [snd_soc_dapm_pinctrl] = 13,
- [snd_soc_dapm_regulator_supply] = 13,
- [snd_soc_dapm_post] = 14,
+ [snd_soc_dapm_pre] = 1,
+ [snd_soc_dapm_kcontrol] = 2,
+ [snd_soc_dapm_adc] = 3,
+ [snd_soc_dapm_hp] = 4,
+ [snd_soc_dapm_spk] = 4,
+ [snd_soc_dapm_line] = 4,
+ [snd_soc_dapm_out_drv] = 4,
+ [snd_soc_dapm_sink] = 4,
+ [snd_soc_dapm_pga] = 5,
+ [snd_soc_dapm_buffer] = 5,
+ [snd_soc_dapm_scheduler] = 5,
+ [snd_soc_dapm_effect] = 5,
+ [snd_soc_dapm_src] = 5,
+ [snd_soc_dapm_asrc] = 5,
+ [snd_soc_dapm_encoder] = 5,
+ [snd_soc_dapm_decoder] = 5,
+ [snd_soc_dapm_switch] = 6,
+ [snd_soc_dapm_mixer_named_ctl] = 6,
+ [snd_soc_dapm_mixer] = 6,
+ [snd_soc_dapm_dac] = 7,
+ [snd_soc_dapm_mic] = 8,
+ [snd_soc_dapm_siggen] = 8,
+ [snd_soc_dapm_input] = 8,
+ [snd_soc_dapm_output] = 8,
+ [snd_soc_dapm_micbias] = 9,
+ [snd_soc_dapm_vmid] = 9,
+ [snd_soc_dapm_mux] = 10,
+ [snd_soc_dapm_demux] = 10,
+ [snd_soc_dapm_aif_in] = 11,
+ [snd_soc_dapm_aif_out] = 11,
+ [snd_soc_dapm_dai_in] = 11,
+ [snd_soc_dapm_dai_out] = 11,
+ [snd_soc_dapm_dai_link] = 12,
+ [snd_soc_dapm_supply] = 13,
+ [snd_soc_dapm_clock_supply] = 14,
+ [snd_soc_dapm_pinctrl] = 14,
+ [snd_soc_dapm_regulator_supply] = 14,
+ [snd_soc_dapm_post] = 15,
};
static void dapm_assert_locked(struct snd_soc_dapm_context *dapm)
@@ -133,6 +157,7 @@ static void pop_wait(u32 pop_time)
schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));
}
+__printf(3, 4)
static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...)
{
va_list args;
@@ -295,7 +320,24 @@ EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);
static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
const struct snd_soc_dapm_widget *_widget)
{
- return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
+ struct snd_soc_dapm_widget *w;
+
+ w = kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
+ if (!w)
+ return NULL;
+
+ /*
+ * w->name is duplicated in caller, but w->sname isn't.
+ * Duplicate it here if defined
+ */
+ if (_widget->sname) {
+ w->sname = kstrdup_const(_widget->sname, GFP_KERNEL);
+ if (!w->sname) {
+ kfree(w);
+ return NULL;
+ }
+ }
+ return w;
}
struct dapm_kcontrol_data {
@@ -1386,11 +1428,17 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
{
int *sort;
+ BUILD_BUG_ON(ARRAY_SIZE(dapm_up_seq) != SND_SOC_DAPM_TYPE_COUNT);
+ BUILD_BUG_ON(ARRAY_SIZE(dapm_down_seq) != SND_SOC_DAPM_TYPE_COUNT);
+
if (power_up)
sort = dapm_up_seq;
else
sort = dapm_down_seq;
+ WARN_ONCE(sort[a->id] == 0, "offset a->id %d not initialized\n", a->id);
+ WARN_ONCE(sort[b->id] == 0, "offset b->id %d not initialized\n", b->id);
+
if (sort[a->id] != sort[b->id])
return sort[a->id] - sort[b->id];
if (a->subseq != b->subseq) {
@@ -2019,19 +2067,19 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
out = is_connected_output_ep(w, NULL, NULL);
}
- ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",
+ ret = scnprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",
w->name, w->power ? "On" : "Off",
w->force ? " (forced)" : "", in, out);
if (w->reg >= 0)
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
" - R%d(0x%x) mask 0x%x",
w->reg, w->reg, w->mask << w->shift);
- ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
if (w->sname)
- ret += snprintf(buf + ret, PAGE_SIZE - ret, " stream %s %s\n",
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret, " stream %s %s\n",
w->sname,
w->active ? "active" : "inactive");
@@ -2044,7 +2092,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
if (!p->connect)
continue;
- ret += snprintf(buf + ret, PAGE_SIZE - ret,
+ ret += scnprintf(buf + ret, PAGE_SIZE - ret,
" %s \"%s\" \"%s\"\n",
(rdir == SND_SOC_DAPM_DIR_IN) ? "in" : "out",
p->name ? p->name : "static",
@@ -2412,6 +2460,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
kfree(w->kcontrols);
kfree_const(w->name);
+ kfree_const(w->sname);
kfree(w);
}
@@ -2525,6 +2574,81 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
+static int dapm_update_dai_chan(struct snd_soc_dapm_path *p,
+ struct snd_soc_dapm_widget *w,
+ int channels)
+{
+ switch (w->id) {
+ case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_aif_in:
+ break;
+ default:
+ return 0;
+ }
+
+ dev_dbg(w->dapm->dev, "%s DAI route %s -> %s\n",
+ w->channel < channels ? "Connecting" : "Disconnecting",
+ p->source->name, p->sink->name);
+
+ if (w->channel < channels)
+ soc_dapm_connect_path(p, true, "dai update");
+ else
+ soc_dapm_connect_path(p, false, "dai update");
+
+ return 0;
+}
+
+static int dapm_update_dai_unlocked(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int dir = substream->stream;
+ int channels = params_channels(params);
+ struct snd_soc_dapm_path *p;
+ struct snd_soc_dapm_widget *w;
+ int ret;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ w = dai->playback_widget;
+ else
+ w = dai->capture_widget;
+
+ if (!w)
+ return 0;
+
+ dev_dbg(dai->dev, "Update DAI routes for %s %s\n", dai->name,
+ dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture");
+
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ ret = dapm_update_dai_chan(p, p->sink, channels);
+ if (ret < 0)
+ return ret;
+ }
+
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ ret = dapm_update_dai_chan(p, p->source, channels);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int ret;
+
+ mutex_lock_nested(&rtd->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ ret = dapm_update_dai_unlocked(substream, params, dai);
+ mutex_unlock(&rtd->card->dapm_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_update_dai);
+
/*
* dapm_update_widget_flags() - Re-compute widget sink and source flags
* @w: The widget for which to update the flags
@@ -2741,6 +2865,8 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
char prefixed_sink[80];
char prefixed_source[80];
const char *prefix;
+ unsigned int sink_ref = 0;
+ unsigned int source_ref = 0;
int ret;
prefix = soc_dapm_prefix(dapm);
@@ -2774,6 +2900,11 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
if (wsource)
break;
}
+ sink_ref++;
+ if (sink_ref > 1)
+ dev_warn(dapm->dev,
+ "ASoC: sink widget %s overwritten\n",
+ w->name);
continue;
}
if (!wsource && !(strcmp(w->name, source))) {
@@ -2783,6 +2914,11 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
if (wsink)
break;
}
+ source_ref++;
+ if (source_ref > 1)
+ dev_warn(dapm->dev,
+ "ASoC: source widget %s overwritten\n",
+ w->name);
}
}
/* use widget from another DAPM context if not found from this */
@@ -3469,6 +3605,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
else
w->name = kstrdup_const(widget->name, GFP_KERNEL);
if (w->name == NULL) {
+ kfree_const(w->sname);
kfree(w);
return ERR_PTR(-ENOMEM);
}
@@ -3689,6 +3826,8 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
ret = soc_dai_hw_params(&substream, params, source);
if (ret < 0)
goto out;
+
+ dapm_update_dai_unlocked(&substream, params, source);
}
substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
@@ -3709,6 +3848,8 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
ret = soc_dai_hw_params(&substream, params, sink);
if (ret < 0)
goto out;
+
+ dapm_update_dai_unlocked(&substream, params, sink);
}
break;