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.c102
1 files changed, 76 insertions, 26 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index b9dc6acbba8c..5c01ac1cfc0a 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -115,6 +115,12 @@ static int dapm_down_seq[] = {
[snd_soc_dapm_post] = 14,
};
+static void dapm_assert_locked(struct snd_soc_dapm_context *dapm)
+{
+ if (dapm->card && dapm->card->instantiated)
+ lockdep_assert_held(&dapm->card->dapm_mutex);
+}
+
static void pop_wait(u32 pop_time)
{
if (pop_time)
@@ -146,15 +152,16 @@ static bool dapm_dirty_widget(struct snd_soc_dapm_widget *w)
return !list_empty(&w->dirty);
}
-void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
+static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
{
+ dapm_assert_locked(w->dapm);
+
if (!dapm_dirty_widget(w)) {
dev_vdbg(w->dapm->dev, "Marking %s dirty due to %s\n",
w->name, reason);
list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty);
}
}
-EXPORT_SYMBOL_GPL(dapm_mark_dirty);
void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm)
{
@@ -361,6 +368,8 @@ static void dapm_reset(struct snd_soc_card *card)
{
struct snd_soc_dapm_widget *w;
+ lockdep_assert_held(&card->dapm_mutex);
+
memset(&card->dapm_stats, 0, sizeof(card->dapm_stats));
list_for_each_entry(w, &card->widgets, list) {
@@ -386,7 +395,8 @@ static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg,
return -1;
}
-static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val)
+static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg,
+ unsigned int val)
{
if (w->codec)
return snd_soc_write(w->codec, reg, val);
@@ -506,7 +516,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl: {
- int val;
+ unsigned int val;
struct soc_mixer_control *mc = (struct soc_mixer_control *)
w->kcontrol_news[i].private_value;
int reg = mc->reg;
@@ -530,7 +540,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_mux: {
struct soc_enum *e = (struct soc_enum *)
w->kcontrol_news[i].private_value;
- int val, item;
+ unsigned int val, item;
soc_widget_read(w, e->reg, &val);
item = (val >> e->shift_l) & e->mask;
@@ -559,7 +569,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_value_mux: {
struct soc_enum *e = (struct soc_enum *)
w->kcontrol_news[i].private_value;
- int val, item;
+ unsigned int val, item;
soc_widget_read(w, e->reg, &val);
val = (val >> e->shift_l) & e->mask;
@@ -1823,6 +1833,8 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
ASYNC_DOMAIN_EXCLUSIVE(async_domain);
enum snd_soc_bias_level bias;
+ lockdep_assert_held(&card->dapm_mutex);
+
trace_snd_soc_dapm_start(card);
list_for_each_entry(d, &card->dapm_list, list) {
@@ -1897,10 +1909,14 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
trace_snd_soc_dapm_walk_done(card);
- /* Run all the bias changes in parallel */
- list_for_each_entry(d, &card->dapm_list, list)
- async_schedule_domain(dapm_pre_sequence_async, d,
- &async_domain);
+ /* Run card bias changes at first */
+ dapm_pre_sequence_async(&card->dapm, 0);
+ /* Run other bias changes in parallel */
+ list_for_each_entry(d, &card->dapm_list, list) {
+ if (d != &card->dapm)
+ async_schedule_domain(dapm_pre_sequence_async, d,
+ &async_domain);
+ }
async_synchronize_full_domain(&async_domain);
list_for_each_entry(w, &down_list, power_list) {
@@ -1920,10 +1936,14 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
dapm_seq_run(card, &up_list, event, true);
/* Run all the bias changes in parallel */
- list_for_each_entry(d, &card->dapm_list, list)
- async_schedule_domain(dapm_post_sequence_async, d,
- &async_domain);
+ list_for_each_entry(d, &card->dapm_list, list) {
+ if (d != &card->dapm)
+ async_schedule_domain(dapm_post_sequence_async, d,
+ &async_domain);
+ }
async_synchronize_full_domain(&async_domain);
+ /* Run card bias changes at last */
+ dapm_post_sequence_async(&card->dapm, 0);
/* do we need to notify any clients that DAPM event is complete */
list_for_each_entry(d, &card->dapm_list, list) {
@@ -2110,6 +2130,8 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card,
struct snd_soc_dapm_path *path;
int found = 0;
+ lockdep_assert_held(&card->dapm_mutex);
+
/* find dapm widget path assoc with kcontrol */
dapm_kcontrol_for_each_path(path, kcontrol) {
if (!path->name || !e->texts[mux])
@@ -2160,6 +2182,8 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
struct snd_soc_dapm_path *path;
int found = 0;
+ lockdep_assert_held(&card->dapm_mutex);
+
/* find dapm widget path assoc with kcontrol */
dapm_kcontrol_for_each_path(path, kcontrol) {
found = 1;
@@ -2325,6 +2349,8 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
{
struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true);
+ dapm_assert_locked(dapm);
+
if (!w) {
dev_err(dapm->dev, "ASoC: DAPM unknown pin %s\n", pin);
return -EINVAL;
@@ -2341,18 +2367,18 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
}
/**
- * snd_soc_dapm_sync - scan and power dapm paths
+ * snd_soc_dapm_sync_unlocked - scan and power dapm paths
* @dapm: DAPM context
*
* Walks all dapm audio paths and powers widgets according to their
* stream or path usage.
*
+ * Requires external locking.
+ *
* Returns 0 for success.
*/
-int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
+int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm)
{
- int ret;
-
/*
* Suppress early reports (eg, jacks syncing their state) to avoid
* silly DAPM runs during card startup.
@@ -2360,8 +2386,25 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
if (!dapm->card || !dapm->card->instantiated)
return 0;
+ return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_unlocked);
+
+/**
+ * snd_soc_dapm_sync - scan and power dapm paths
+ * @dapm: DAPM context
+ *
+ * Walks all dapm audio paths and powers widgets according to their
+ * stream or path usage.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
+{
+ int ret;
+
mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- ret = dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP);
+ ret = snd_soc_dapm_sync_unlocked(dapm);
mutex_unlock(&dapm->card->dapm_mutex);
return ret;
}
@@ -4098,7 +4141,7 @@ void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
-static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
+static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm)
{
struct snd_soc_card *card = dapm->card;
struct snd_soc_dapm_widget *w;
@@ -4138,14 +4181,21 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
*/
void snd_soc_dapm_shutdown(struct snd_soc_card *card)
{
- struct snd_soc_codec *codec;
-
- list_for_each_entry(codec, &card->codec_dev_list, card_list) {
- soc_dapm_shutdown_codec(&codec->dapm);
- if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY)
- snd_soc_dapm_set_bias_level(&codec->dapm,
- SND_SOC_BIAS_OFF);
+ struct snd_soc_dapm_context *dapm;
+
+ list_for_each_entry(dapm, &card->dapm_list, list) {
+ if (dapm != &card->dapm) {
+ soc_dapm_shutdown_dapm(dapm);
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
+ snd_soc_dapm_set_bias_level(dapm,
+ SND_SOC_BIAS_OFF);
+ }
}
+
+ soc_dapm_shutdown_dapm(&card->dapm);
+ if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY)
+ snd_soc_dapm_set_bias_level(&card->dapm,
+ SND_SOC_BIAS_OFF);
}
/* Module information */