[alsa-devel] [PATCH 01/10] ASoC: dapm: Move snd_soc_dapm_update from dapm context to card
The update field of a DAPM context is only assigned while the card's dapm_mutex is locked, the field is also cleared again while the mutex is stil locked. So there will only ever be one DAPM context at a time with a non-NULL update field. So it is safe to move the update field from the DAPM context struct to the card struct. Doing so will allow further cleanups in this area.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/soc-dapm.h | 2 -- include/sound/soc.h | 1 + sound/soc/soc-dapm.c | 22 +++++++++++----------- 3 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e77c6f5..3397292 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -575,8 +575,6 @@ struct snd_soc_dapm_context { struct delayed_work delayed_work; unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
- struct snd_soc_dapm_update *update; - void (*seq_notifier)(struct snd_soc_dapm_context *, enum snd_soc_dapm_type, int);
diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7..b1e1f96 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1042,6 +1042,7 @@ struct snd_soc_card { /* Generic DAPM context for the card */ struct snd_soc_dapm_context dapm; struct snd_soc_dapm_stats dapm_stats; + struct snd_soc_dapm_update *update;
#ifdef CONFIG_DEBUG_FS struct dentry *debugfs_card_root; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 366daef..7449e27 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1425,7 +1425,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
static void dapm_widget_update(struct snd_soc_dapm_context *dapm) { - struct snd_soc_dapm_update *update = dapm->update; + struct snd_soc_dapm_update *update = dapm->card->update; struct snd_soc_dapm_widget_list *wlist; struct snd_soc_dapm_widget *w = NULL; unsigned int wi; @@ -1959,9 +1959,9 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, int ret;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm->update = update; + card->update = update; ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); - dapm->update = NULL; + card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2002,9 +2002,9 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, int ret;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm->update = update; + card->update = update; ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); - dapm->update = NULL; + card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2693,11 +2693,11 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, update.mask = mask; update.val = val;
- widget->dapm->update = &update; + card->update = &update;
soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect);
- widget->dapm->update = NULL; + card->update = NULL; }
mutex_unlock(&card->dapm_mutex); @@ -2775,11 +2775,11 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.reg = e->reg; update.mask = mask; update.val = val; - widget->dapm->update = &update; + card->update = &update;
soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e);
- widget->dapm->update = NULL; + card->update = NULL; }
mutex_unlock(&card->dapm_mutex); @@ -2928,11 +2928,11 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, update.reg = e->reg; update.mask = mask; update.val = val; - widget->dapm->update = &update; + card->update = &update;
soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e);
- widget->dapm->update = NULL; + card->update = NULL; }
mutex_unlock(&card->dapm_mutex);
DAPM operations are always performed on the card as a whole. Yet (primarily for historic reasons) dapm_power_widgets() takes a DAPM context as its parameter. The DAPM context is mainly used to look up a pointer to the card. The same is true for a couple of functions that are being called from dapm_power_widgets(). This patch changes the signature of dapm_power_widgets() and a couple of related functions to take a snd_soc_card instead of a snd_soc_dapm_context.
Some of the functions also use the DAPM's device to print error and debug messages. This can be a bit confusing though since this means the messages for all widgets, also those from other contexts, will be printed with that device. The patch updates those cases to use the device of the widget's DAPM context.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/soc-dapm.c | 100 +++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 51 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7449e27..5db8df2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1213,10 +1213,9 @@ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, list_add_tail(&new_widget->power_list, list); }
-static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm, +static void dapm_seq_check_event(struct snd_soc_card *card, struct snd_soc_dapm_widget *w, int event) { - struct snd_soc_card *card = dapm->card; const char *ev_name; int power, ret;
@@ -1254,22 +1253,21 @@ static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm, return;
if (w->event && (w->event_flags & event)) { - pop_dbg(dapm->dev, card->pop_time, "pop test : %s %s\n", + pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n", w->name, ev_name); trace_snd_soc_dapm_widget_event_start(w, event); ret = w->event(w, NULL, event); trace_snd_soc_dapm_widget_event_done(w, event); if (ret < 0) - dev_err(dapm->dev, "ASoC: %s: %s event failed: %d\n", + dev_err(w->dapm->dev, "ASoC: %s: %s event failed: %d\n", ev_name, w->name, ret); } }
/* Apply the coalesced changes from a DAPM sequence */ -static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, +static void dapm_seq_run_coalesced(struct snd_soc_card *card, struct list_head *pending) { - struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; int reg, power; unsigned int value = 0; @@ -1292,13 +1290,13 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, if (power) value |= cur_mask;
- pop_dbg(dapm->dev, card->pop_time, + pop_dbg(w->dapm->dev, card->pop_time, "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", w->name, reg, value, mask);
/* Check for events */ - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMU); - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMD); + dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMU); + dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMD); }
if (reg >= 0) { @@ -1308,7 +1306,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, w = list_first_entry(pending, struct snd_soc_dapm_widget, power_list);
- pop_dbg(dapm->dev, card->pop_time, + pop_dbg(w->dapm->dev, card->pop_time, "pop test : Applying 0x%x/0x%x to %x in %dms\n", value, mask, reg, card->pop_time); pop_wait(card->pop_time); @@ -1316,8 +1314,8 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, }
list_for_each_entry(w, pending, power_list) { - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMU); - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMD); + dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMU); + dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMD); } }
@@ -1329,8 +1327,8 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, * Currently anything that requires more than a single write is not * handled. */ -static void dapm_seq_run(struct snd_soc_dapm_context *dapm, - struct list_head *list, int event, bool power_up) +static void dapm_seq_run(struct snd_soc_card *card, + struct list_head *list, int event, bool power_up) { struct snd_soc_dapm_widget *w, *n; LIST_HEAD(pending); @@ -1353,7 +1351,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, if (sort[w->id] != cur_sort || w->reg != cur_reg || w->dapm != cur_dapm || w->subseq != cur_subseq) { if (!list_empty(&pending)) - dapm_seq_run_coalesced(cur_dapm, &pending); + dapm_seq_run_coalesced(card, &pending);
if (cur_dapm && cur_dapm->seq_notifier) { for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) @@ -1413,7 +1411,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, }
if (!list_empty(&pending)) - dapm_seq_run_coalesced(cur_dapm, &pending); + dapm_seq_run_coalesced(card, &pending);
if (cur_dapm && cur_dapm->seq_notifier) { for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) @@ -1423,9 +1421,9 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, } }
-static void dapm_widget_update(struct snd_soc_dapm_context *dapm) +static void dapm_widget_update(struct snd_soc_card *card) { - struct snd_soc_dapm_update *update = dapm->card->update; + struct snd_soc_dapm_update *update = card->update; struct snd_soc_dapm_widget_list *wlist; struct snd_soc_dapm_widget *w = NULL; unsigned int wi; @@ -1442,7 +1440,7 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) { ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); if (ret != 0) - dev_err(dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", + dev_err(w->dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", w->name, ret); } } @@ -1453,7 +1451,7 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) ret = soc_widget_update_bits_locked(w, update->reg, update->mask, update->val); if (ret < 0) - dev_err(dapm->dev, "ASoC: %s DAPM update failed: %d\n", + dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n", w->name, ret);
for (wi = 0; wi < wlist->num_widgets; wi++) { @@ -1462,7 +1460,7 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) { ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); if (ret != 0) - dev_err(dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", + dev_err(w->dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", w->name, ret); } } @@ -1627,9 +1625,8 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, * o Input pin to Output pin (bypass, sidetone) * o DAC to ADC (loopback). */ -static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) +static int dapm_power_widgets(struct snd_soc_card *card, int event) { - struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; struct snd_soc_dapm_context *d; LIST_HEAD(up_list); @@ -1711,29 +1708,29 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) trace_snd_soc_dapm_walk_done(card);
/* Run all the bias changes in parallel */ - list_for_each_entry(d, &dapm->card->dapm_list, list) + list_for_each_entry(d, &card->dapm_list, list) 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) { - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMD); + dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMD); }
list_for_each_entry(w, &up_list, power_list) { - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMU); + dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMU); }
/* Power down widgets first; try to avoid amplifying pops. */ - dapm_seq_run(dapm, &down_list, event, false); + dapm_seq_run(card, &down_list, event, false);
- dapm_widget_update(dapm); + dapm_widget_update(card);
/* Now power up. */ - dapm_seq_run(dapm, &up_list, event, true); + dapm_seq_run(card, &up_list, event, true);
/* Run all the bias changes in parallel */ - list_for_each_entry(d, &dapm->card->dapm_list, list) + list_for_each_entry(d, &card->dapm_list, list) async_schedule_domain(dapm_post_sequence_async, d, &async_domain); async_synchronize_full_domain(&async_domain); @@ -1744,7 +1741,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) d->stream_event(d, event); }
- pop_dbg(dapm->dev, card->pop_time, + pop_dbg(card->dev, card->pop_time, "DAPM sequencing finished, waiting %dms\n", card->pop_time); pop_wait(card->pop_time);
@@ -1917,14 +1914,14 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) #endif
/* test and update the power status of a mux widget */ -static int soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, +static int soc_dapm_mux_update_power(struct snd_soc_card *card, struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) { struct snd_soc_dapm_path *path; int found = 0;
/* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &dapm->card->paths, list) { + list_for_each_entry(path, &card->paths, list) { if (path->kcontrol != kcontrol) continue;
@@ -1946,7 +1943,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, }
if (found) - dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
return found; } @@ -1960,7 +1957,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); card->update = update; - ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); + ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) @@ -1970,14 +1967,14 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
/* test and update the power status of a mixer or switch widget */ -static int soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, +static int soc_dapm_mixer_update_power(struct snd_soc_card *card, struct snd_kcontrol *kcontrol, int connect) { struct snd_soc_dapm_path *path; int found = 0;
/* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &dapm->card->paths, list) { + list_for_each_entry(path, &card->paths, list) { if (path->kcontrol != kcontrol) continue;
@@ -1989,7 +1986,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, }
if (found) - dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
return found; } @@ -2003,7 +2000,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); card->update = update; - ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); + ret = soc_dapm_mixer_update_power(card, kcontrol, connect); card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) @@ -2180,7 +2177,7 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) return 0;
mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + ret = dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); mutex_unlock(&dapm->card->dapm_mutex); return ret; } @@ -2545,12 +2542,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); */ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) { + struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; unsigned int val;
- mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
- list_for_each_entry(w, &dapm->card->widgets, list) + list_for_each_entry(w, &card->widgets, list) { if (w->new) continue; @@ -2560,7 +2558,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) sizeof(struct snd_kcontrol *), GFP_KERNEL); if (!w->kcontrols) { - mutex_unlock(&dapm->card->dapm_mutex); + mutex_unlock(&card->dapm_mutex); return -ENOMEM; } } @@ -2601,8 +2599,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) dapm_debugfs_add_widget(w); }
- dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); - mutex_unlock(&dapm->card->dapm_mutex); + dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&card->dapm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); @@ -2695,7 +2693,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
card->update = &update;
- soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect); + soc_dapm_mixer_update_power(card, kcontrol, connect);
card->update = NULL; } @@ -2777,7 +2775,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.val = val; card->update = &update;
- soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); + soc_dapm_mux_update_power(card, kcontrol, mux, e);
card->update = NULL; } @@ -2832,7 +2830,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, change = widget->value != ucontrol->value.enumerated.item[0]; if (change) { widget->value = ucontrol->value.enumerated.item[0]; - soc_dapm_mux_update_power(widget->dapm, kcontrol, widget->value, e); + soc_dapm_mux_update_power(card, kcontrol, widget->value, e); }
mutex_unlock(&card->dapm_mutex); @@ -2930,7 +2928,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, update.val = val; card->update = &update;
- soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); + soc_dapm_mux_update_power(card, kcontrol, mux, e);
card->update = NULL; } @@ -3478,7 +3476,7 @@ static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, } }
- dapm_power_widgets(&rtd->card->dapm, event); + dapm_power_widgets(rtd->card, event); }
/** @@ -3747,7 +3745,7 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) if (dapm->bias_level == SND_SOC_BIAS_ON) snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE); - dapm_seq_run(dapm, &down_list, 0, false); + dapm_seq_run(card, &down_list, 0, false); if (dapm->bias_level == SND_SOC_BIAS_PREPARE) snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY);
We use the same 3 lines to get the CODEC for a kcontrol in a quite a few places. This patch puts them into a common helper function. Having this encapsulated in a helper function will also make it more easier to eventually change the data layout of the kcontrol's private data.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/soc-dapm.h | 2 ++ sound/soc/codecs/tlv320aic3x.c | 7 +++---- sound/soc/codecs/twl6040.c | 4 +--- sound/soc/codecs/wm8903.c | 4 +--- sound/soc/codecs/wm8994.c | 4 +--- sound/soc/codecs/wm8995.c | 5 +---- sound/soc/codecs/wm_hubs.c | 8 ++------ sound/soc/soc-dapm.c | 40 +++++++++++++++++++++++----------------- 8 files changed, 34 insertions(+), 40 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3397292..ebfae8f 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -427,6 +427,8 @@ void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list);
+struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol); + /* dapm widget types */ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index d3417c8..6e3f269 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -138,8 +138,7 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -165,14 +164,14 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, mask <<= shift; val <<= shift;
- change = snd_soc_test_bits(widget->codec, val, mask, reg); + change = snd_soc_test_bits(codec, val, mask, reg); if (change) { update.kcontrol = kcontrol; update.reg = reg; update.mask = mask; update.val = val;
- snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect, + snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect, &update); }
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index caf8784..3c79dbb 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -438,9 +438,7 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data) static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val;
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index fa24ced..eebcb1d 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -364,9 +364,7 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm, static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); u16 reg; int ret; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index a8d7b0c..d76b056 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -1438,9 +1438,7 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING, static int wm8994_put_class_w(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *w = wlist->widgets[0]; - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 90a65c4..da2899e6 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -549,12 +549,9 @@ static int check_clk_sys(struct snd_soc_dapm_widget *source, static int wm8995_put_class_w(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *w = wlist->widgets[0]; - struct snd_soc_codec *codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret;
- codec = w->codec; ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); wm8995_update_class_w(codec); return ret; diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 2d9e099..8b50e59 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -699,9 +699,7 @@ EXPORT_SYMBOL_GPL(wm_hubs_update_class_w); static int class_w_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret;
ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); @@ -721,9 +719,7 @@ static int class_w_put_volsw(struct snd_kcontrol *kcontrol, static int class_w_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret;
ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5db8df2..b18ac5b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -174,6 +174,17 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); }
+/** + * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol + * @kcontrol: The kcontrol + */ +struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + return wlist->widgets[0]->codec; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); + static void dapm_reset(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; @@ -2617,8 +2628,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2628,12 +2638,12 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, unsigned int invert = mc->invert;
if (snd_soc_volsw_is_stereo(mc)) - dev_warn(widget->dapm->dev, + dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name);
ucontrol->value.integer.value[0] = - (snd_soc_read(widget->codec, reg) >> shift) & mask; + (snd_soc_read(codec, reg) >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; @@ -2654,9 +2664,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; @@ -2670,7 +2678,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_update update;
if (snd_soc_volsw_is_stereo(mc)) - dev_warn(widget->dapm->dev, + dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name);
@@ -2684,7 +2692,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- change = snd_soc_test_bits(widget->codec, reg, mask, val); + change = snd_soc_test_bits(codec, reg, mask, val); if (change) { update.kcontrol = kcontrol; update.reg = reg; @@ -2715,12 +2723,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val;
- val = snd_soc_read(widget->codec, e->reg); + val = snd_soc_read(codec, e->reg); ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask; if (e->shift_l != e->shift_r) ucontrol->value.enumerated.item[1] = @@ -2765,7 +2772,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { widget->value = val;
@@ -2854,12 +2861,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int reg_val, val, mux;
- reg_val = snd_soc_read(widget->codec, e->reg); + reg_val = snd_soc_read(codec, e->reg); val = (reg_val >> e->shift_l) & e->mask; for (mux = 0; mux < e->max; mux++) { if (val == e->values[mux]) @@ -2918,7 +2924,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { widget->value = val;
In preparation for adding additional per control data wrap all access to the widget list in helper functions.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/soc-dapm.c | 119 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 41 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b18ac5b..da35b10 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -174,14 +174,72 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); }
+struct dapm_kcontrol_data { + struct snd_soc_dapm_widget_list wlist; +}; + +static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data; + + data = kzalloc(sizeof(*data) + sizeof(widget), GFP_KERNEL); + if (!data) { + dev_err(widget->dapm->dev, + "ASoC: can't allocate kcontrol data for %s\n", + widget->name); + return -ENOMEM; + } + + data->wlist.widgets[0] = widget; + data->wlist.num_widgets = 1; + + kcontrol->private_data = data; + + return 0; +} + +static void dapm_kcontrol_free(struct snd_kcontrol *kctl) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl); + kfree(data); +} + +static struct snd_soc_dapm_widget_list *dapm_kcontrol_get_wlist( + const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + return &data->wlist; +} + +static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_widget *widget) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + struct dapm_kcontrol_data *new_data; + unsigned int n = data->wlist.num_widgets + 1; + + new_data = krealloc(data, sizeof(*data) + sizeof(widget) * n, + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->wlist.widgets[n - 1] = widget; + data->wlist.num_widgets = n; + + kcontrol->private_data = data; + + return 0; +} + /** * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol * @kcontrol: The kcontrol */ struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - return wlist->widgets[0]->codec; + return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->codec; } EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec);
@@ -488,11 +546,6 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, return 0; }
-static void dapm_kcontrol_free(struct snd_kcontrol *kctl) -{ - kfree(kctl->private_data); -} - /* * Determine if a kcontrol is shared. If it is, look it up. If it isn't, * create it. Either way, add the widget into the control's widget list @@ -506,9 +559,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, size_t prefix_len; int shared; struct snd_kcontrol *kcontrol; - struct snd_soc_dapm_widget_list *wlist; - int wlistentries; - size_t wlistsize; bool wname_in_long_name, kcname_in_long_name; char *long_name; const char *name; @@ -527,25 +577,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci], &kcontrol);
- if (kcontrol) { - wlist = kcontrol->private_data; - wlistentries = wlist->num_widgets + 1; - } else { - wlist = NULL; - wlistentries = 1; - } - - wlistsize = sizeof(struct snd_soc_dapm_widget_list) + - wlistentries * sizeof(struct snd_soc_dapm_widget *); - wlist = krealloc(wlist, wlistsize, GFP_KERNEL); - if (wlist == NULL) { - dev_err(dapm->dev, "ASoC: can't allocate widget list for %s\n", - w->name); - return -ENOMEM; - } - wlist->num_widgets = wlistentries; - wlist->widgets[wlistentries - 1] = w; - if (!kcontrol) { if (shared) { wname_in_long_name = false; @@ -568,7 +599,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, kcname_in_long_name = false; break; default: - kfree(wlist); return -EINVAL; } } @@ -583,10 +613,8 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, long_name = kasprintf(GFP_KERNEL, "%s %s", w->name + prefix_len, w->kcontrol_news[kci].name); - if (long_name == NULL) { - kfree(wlist); + if (long_name == NULL) return -ENOMEM; - }
name = long_name; } else if (wname_in_long_name) { @@ -597,21 +625,30 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, name = w->kcontrol_news[kci].name; }
- kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], wlist, name, + kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], NULL, name, prefix); kcontrol->private_free = dapm_kcontrol_free; kfree(long_name); + + ret = dapm_kcontrol_data_alloc(w, kcontrol); + if (ret) { + snd_ctl_free_one(kcontrol); + return ret; + } + ret = snd_ctl_add(card, kcontrol); if (ret < 0) { dev_err(dapm->dev, "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", w->name, name, ret); - kfree(wlist); return ret; } + } else { + ret = dapm_kcontrol_add_widget(kcontrol, w); + if (ret) + return ret; }
- kcontrol->private_data = wlist; w->kcontrols[kci] = kcontrol; path->kcontrol = kcontrol;
@@ -1443,7 +1480,7 @@ static void dapm_widget_update(struct snd_soc_card *card) if (!update) return;
- wlist = snd_kcontrol_chip(update->kcontrol); + wlist = dapm_kcontrol_get_wlist(update->kcontrol);
for (wi = 0; wi < wlist->num_widgets; wi++) { w = wlist->widgets[wi]; @@ -2749,7 +2786,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; struct snd_soc_card *card = codec->card; @@ -2802,7 +2839,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0];
ucontrol->value.enumerated.item[0] = widget->value; @@ -2821,7 +2858,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; struct snd_soc_card *card = codec->card; @@ -2901,7 +2938,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double); int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; struct snd_soc_card *card = codec->card;
The 'value' field is really per control and not per widget. Currently it is only used for virtual MUXes, which only have one control per widget. So in that case there is not so much of a difference between whether it is stored per widget or per control. Moving the 'value' field from the widget to the control will allow us to use it also for cases where we have more than one control per widget. E.g. for mixers with multiple input controls.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/soc-dapm.h | 1 - sound/soc/soc-dapm.c | 53 +++++++++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 24 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ebfae8f..d7d26cc 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -523,7 +523,6 @@ struct snd_soc_dapm_widget { /* dapm control */ int reg; /* negative reg = no direct dapm */ unsigned char shift; /* bits to shift */ - unsigned int value; /* widget current value */ unsigned int mask; /* non-shifted mask */ unsigned int on_val; /* on state value */ unsigned int off_val; /* off state value */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index da35b10..bad6f6d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -175,6 +175,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( }
struct dapm_kcontrol_data { + unsigned int value; struct snd_soc_dapm_widget_list wlist; };
@@ -233,6 +234,26 @@ static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, return 0; }
+static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + return data->value; +} + +static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, + unsigned int value) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + if (data->value == value) + return false; + + data->value = value; + + return true; +} + /** * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol * @kcontrol: The kcontrol @@ -2786,9 +2807,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; @@ -2811,8 +2830,6 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { - widget->value = val; - update.kcontrol = kcontrol; update.reg = e->reg; update.mask = mask; @@ -2839,11 +2856,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - - ucontrol->value.enumerated.item[0] = widget->value; - + ucontrol->value.enumerated.item[0] = dapm_kcontrol_get_value(kcontrol); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); @@ -2858,10 +2871,9 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; + unsigned int value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; @@ -2871,11 +2883,10 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- change = widget->value != ucontrol->value.enumerated.item[0]; - if (change) { - widget->value = ucontrol->value.enumerated.item[0]; - soc_dapm_mux_update_power(card, kcontrol, widget->value, e); - } + value = ucontrol->value.enumerated.item[0]; + change = dapm_kcontrol_set_value(kcontrol, value); + if (change) + soc_dapm_mux_update_power(card, kcontrol, value, e);
mutex_unlock(&card->dapm_mutex); return change; @@ -2938,9 +2949,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double); int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; @@ -2963,8 +2972,6 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { - widget->value = val; - update.kcontrol = kcontrol; update.reg = e->reg; update.mask = mask;
Currently we store for each path which control (if any at all) is associated with that control. But we are only ever interested in the reverse relationship, i.e. we want to know all the paths a certain control is associated with. This is currently implemented by always iterating over all paths. This patch updates the code to keep a list for each control which contains all the paths that are associated with that control. This improves the run time of e.g. soc_dapm_mixer_update_power() and soc_dapm_mux_update_power() from O(n) (with n being the number of paths for the card) to O(1).
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/soc-dapm.h | 2 +- sound/soc/soc-dapm.c | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index d7d26cc..693c75b 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -490,7 +490,6 @@ struct snd_soc_dapm_path { /* source (input) and sink (output) widgets */ struct snd_soc_dapm_widget *source; struct snd_soc_dapm_widget *sink; - struct snd_kcontrol *kcontrol;
/* status */ u32 connect:1; /* source and sink widgets are connected */ @@ -503,6 +502,7 @@ struct snd_soc_dapm_path {
struct list_head list_source; struct list_head list_sink; + struct list_head list_kcontrol; struct list_head list; };
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index bad6f6d..b779d36 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -176,6 +176,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
struct dapm_kcontrol_data { unsigned int value; + struct list_head paths; struct snd_soc_dapm_widget_list wlist; };
@@ -194,6 +195,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
data->wlist.widgets[0] = widget; data->wlist.num_widgets = 1; + INIT_LIST_HEAD(&data->paths);
kcontrol->private_data = data;
@@ -234,6 +236,26 @@ static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, return 0; }
+static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_path *path) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + list_add_tail(&path->list_kcontrol, &data->paths); +} + +static struct list_head *dapm_kcontrol_get_path_list( + const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + return &data->paths; +} + +#define dapm_kcontrol_for_each_path(path, kcontrol) \ + list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \ + list_kcontrol) + static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); @@ -671,7 +693,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, }
w->kcontrols[kci] = kcontrol; - path->kcontrol = kcontrol; + dapm_kcontrol_add_path(kcontrol, path);
return 0; } @@ -691,7 +713,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) continue;
if (w->kcontrols[i]) { - path->kcontrol = w->kcontrols[i]; + dapm_kcontrol_add_path(w->kcontrols[i], path); continue; }
@@ -730,7 +752,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return ret;
list_for_each_entry(path, &w->sources, list_sink) - path->kcontrol = w->kcontrols[0]; + dapm_kcontrol_add_path(w->kcontrols[0], path);
return 0; } @@ -1990,10 +2012,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card, int found = 0;
/* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &card->paths, list) { - if (path->kcontrol != kcontrol) - continue; - + dapm_kcontrol_for_each_path(path, kcontrol) { if (!path->name || !e->texts[mux]) continue;
@@ -2043,11 +2062,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, int found = 0;
/* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &card->paths, list) { - if (path->kcontrol != kcontrol) - continue; - - /* found, now check type */ + dapm_kcontrol_for_each_path(path, kcontrol) { found = 1; path->connect = connect; dapm_mark_dirty(path->source, "mixer connection"); @@ -2152,6 +2167,7 @@ static void dapm_free_path(struct snd_soc_dapm_path *path) { list_del(&path->list_sink); list_del(&path->list_source); + list_del(&path->list_kcontrol); list_del(&path->list); kfree(path); }
Currently the DAPM code is limited to only setting or clearing a single bit in a register to power a widget up or down. This patch extends the DAPM code to be more flexible in that regard and allow widgets to use arbitrary values to be used to put a widget in either on or off state.
Since the snd_soc_dapm_widget struct already contains a on_val and off_val field no additional fields need to be added and in fact the invert field can even be removed. Also the generated code is slightly smaller.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/soc-dapm.h | 163 +++++++++++++++++++++++++++-------------------- sound/soc/soc-dapm.c | 34 ++++------ 2 files changed, 106 insertions(+), 91 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 693c75b..3575721 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -70,121 +70,144 @@ struct device; .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
+#define SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) \ + .reg = wreg, .mask = 1, .shift = wshift, \ + .on_val = winvert ? 0 : 1, .off_val = winvert ? 1 : 0 + /* path domain */ #define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\ wcontrols, wncontrols) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\ wcontrols, wncontrols) \ -{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_out_drv, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \ wcontrols, wncontrols)\ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \ wcontrols, wncontrols)\ -{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0} +{ .id = snd_soc_dapm_micbias, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = NULL, .num_kcontrols = 0} #define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} +{ .id = snd_soc_dapm_switch, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} +{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, \ + .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} +{ .id = snd_soc_dapm_virt_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = 1} +{ .id = snd_soc_dapm_value_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1}
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\ wcontrols) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} #define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \ wcontrols)\ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} #define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \ wcontrols)\ -{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = ARRAY_SIZE(wcontrols)} +{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
/* path domain with event - event handler must return 0 for success */ #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ +{ .id = snd_soc_dapm_out_drv, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \ wcontrols, wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, \ .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ +{ .id = snd_soc_dapm_switch, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ +{ .id = snd_soc_dapm_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ +{ .id = snd_soc_dapm_virt_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags}
/* additional sequencing control within an event type */ #define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \ wevent, wflags) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .event = wevent, .event_flags = wflags, \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .event = wevent, .event_flags = wflags, \ .subseq = wsubseq} #define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \ wflags) \ -{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .event = wevent, \ - .event_flags = wflags, .subseq = wsubseq} +{ .id = snd_soc_dapm_supply, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .event = wevent, .event_flags = wflags, .subseq = wsubseq}
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .event = wevent, .event_flags = wflags} #define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .event = wevent, .event_flags = wflags} #define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \ wcontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ + .event = wevent, .event_flags = wflags}
/* events that are pre and post DAPM */ #define SND_SOC_DAPM_PRE(wname, wevent) \ @@ -199,35 +222,36 @@ struct device; /* stream domain */ #define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert } + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \ wevent, wflags) \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags } #define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert } + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \ wevent, wflags) \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags } #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert} +{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) } #define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \ wevent, wflags) \ -{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert, \ +{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags} + #define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert} +{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \ wevent, wflags) \ -{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert, \ +{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \ { .id = snd_soc_dapm_clock_supply, .name = wname, \ @@ -241,14 +265,14 @@ struct device; .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} #define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \ -{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .event = wevent, \ - .event_flags = wflags} +{ .id = snd_soc_dapm_supply, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay, wflags) \ { .id = snd_soc_dapm_regulator_supply, .name = wname, \ .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ - .invert = wflags} + .on_val = wflags}
/* dapm kcontrol types */ @@ -527,7 +551,6 @@ struct snd_soc_dapm_widget { unsigned int on_val; /* on state value */ unsigned int off_val; /* off state value */ unsigned char power:1; /* block power status */ - unsigned char invert:1; /* invert the power bit */ unsigned char active:1; /* active stream on DAC, ADC's */ unsigned char connected:1; /* connected codec pin */ unsigned char new:1; /* cnew complete */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b779d36..59bcc66 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1122,7 +1122,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, int ret;
if (SND_SOC_DAPM_EVENT_ON(event)) { - if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, false); if (ret != 0) dev_warn(w->dapm->dev, @@ -1132,7 +1132,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
return regulator_enable(w->regulator); } else { - if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, true); if (ret != 0) dev_warn(w->dapm->dev, @@ -1360,26 +1360,21 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, struct list_head *pending) { struct snd_soc_dapm_widget *w; - int reg, power; + int reg; unsigned int value = 0; unsigned int mask = 0; - unsigned int cur_mask;
reg = list_first_entry(pending, struct snd_soc_dapm_widget, power_list)->reg;
list_for_each_entry(w, pending, power_list) { - cur_mask = 1 << w->shift; BUG_ON(reg != w->reg);
- if (w->invert) - power = !w->power; + mask |= w->mask << w->shift; + if (w->power) + value |= w->on_val << w->shift; else - power = w->power; - - mask |= cur_mask; - if (power) - value |= cur_mask; + value |= w->off_val << w->shift;
pop_dbg(w->dapm->dev, card->pop_time, "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", @@ -1867,8 +1862,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
if (w->reg >= 0) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " - R%d(0x%x) bit %d", - w->reg, w->reg, w->shift); + " - R%d(0x%x) mask 0x%x", + w->reg, w->reg, w->mask << w->shift);
ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
@@ -2669,12 +2664,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
/* Read the initial power state from the device */ if (w->reg >= 0) { - val = soc_widget_read(w, w->reg); - val &= 1 << w->shift; - if (w->invert) - val = !val; - - if (val) + val = soc_widget_read(w, w->reg) >> w->shift; + val &= w->mask; + if (val == w->on_val) w->power = 1; }
@@ -3093,7 +3085,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, return NULL; }
- if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, true); if (ret != 0) dev_warn(w->dapm->dev,
snd_soc_dapm_add_path() is similar to snd_soc_dapm_add_route() except that it expects the pointer to the source and sink widgets instead of their names. This allows us to simplify the case where we already have a pointer to widgets. (E.g. as we have in snd_soc_dapm_link_dai_widgets()). snd_soc_dapm_add_route() will be updated to just look up the widget and then use snd_soc_dapm_add_path() to handle everything else.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/soc-dapm.c | 151 +++++++++++++++++++++++++++------------------------ 1 file changed, 81 insertions(+), 70 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 59bcc66..b811a27 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2263,64 +2263,14 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
-static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_route *route) +static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, + const char *control, + int (*connected)(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink)) { struct snd_soc_dapm_path *path; - struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; - struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; - const char *sink; - const char *control = route->control; - const char *source; - char prefixed_sink[80]; - char prefixed_source[80]; - int ret = 0; - - if (dapm->codec && dapm->codec->name_prefix) { - snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", - dapm->codec->name_prefix, route->sink); - sink = prefixed_sink; - snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", - dapm->codec->name_prefix, route->source); - source = prefixed_source; - } else { - sink = route->sink; - source = route->source; - } - - /* - * find src and dest widgets over all widgets but favor a widget from - * current DAPM context - */ - list_for_each_entry(w, &dapm->card->widgets, list) { - if (!wsink && !(strcmp(w->name, sink))) { - wtsink = w; - if (w->dapm == dapm) - wsink = w; - continue; - } - if (!wsource && !(strcmp(w->name, source))) { - wtsource = w; - if (w->dapm == dapm) - wsource = w; - } - } - /* use widget from another DAPM context if not found from this */ - if (!wsink) - wsink = wtsink; - if (!wsource) - wsource = wtsource; - - if (wsource == NULL) { - dev_err(dapm->dev, "ASoC: no source widget found for %s\n", - route->source); - return -ENODEV; - } - if (wsink == NULL) { - dev_err(dapm->dev, "ASoC: no sink widget found for %s\n", - route->sink); - return -ENODEV; - } + int ret;
path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); if (!path) @@ -2328,7 +2278,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
path->source = wsource; path->sink = wsink; - path->connected = route->connected; + path->connected = connected; INIT_LIST_HEAD(&path->list); INIT_LIST_HEAD(&path->list_source); INIT_LIST_HEAD(&path->list_sink); @@ -2414,11 +2364,77 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, dapm_mark_dirty(wsink, "Route added");
return 0; +err: + kfree(path); + return ret; +}
+static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route) +{ + struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; + struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; + const char *sink; + const char *source; + char prefixed_sink[80]; + char prefixed_source[80]; + int ret; + + if (dapm->codec && dapm->codec->name_prefix) { + snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", + dapm->codec->name_prefix, route->sink); + sink = prefixed_sink; + snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", + dapm->codec->name_prefix, route->source); + source = prefixed_source; + } else { + sink = route->sink; + source = route->source; + } + + /* + * find src and dest widgets over all widgets but favor a widget from + * current DAPM context + */ + list_for_each_entry(w, &dapm->card->widgets, list) { + if (!wsink && !(strcmp(w->name, sink))) { + wtsink = w; + if (w->dapm == dapm) + wsink = w; + continue; + } + if (!wsource && !(strcmp(w->name, source))) { + wtsource = w; + if (w->dapm == dapm) + wsource = w; + } + } + /* use widget from another DAPM context if not found from this */ + if (!wsink) + wsink = wtsink; + if (!wsource) + wsource = wtsource; + + if (wsource == NULL) { + dev_err(dapm->dev, "ASoC: no source widget found for %s\n", + route->source); + return -ENODEV; + } + if (wsink == NULL) { + dev_err(dapm->dev, "ASoC: no sink widget found for %s\n", + route->sink); + return -ENODEV; + } + + ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control, + route->connected); + if (ret) + goto err; + + return 0; err: dev_warn(dapm->dev, "ASoC: no dapm match for %s --> %s --> %s\n", - source, control, sink); - kfree(path); + source, route->control, sink); return ret; }
@@ -3421,9 +3437,6 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) { struct snd_soc_dapm_widget *dai_w, *w; struct snd_soc_dai *dai; - struct snd_soc_dapm_route r; - - memset(&r, 0, sizeof(r));
/* For each DAI widget... */ list_for_each_entry(dai_w, &card->widgets, list) { @@ -3456,23 +3469,21 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) if (dai->driver->playback.stream_name && strstr(w->sname, dai->driver->playback.stream_name)) { - r.source = dai->playback_widget->name; - r.sink = w->name; dev_dbg(dai->dev, "%s -> %s\n", - r.source, r.sink); + dai->playback_widget->name, w->name);
- snd_soc_dapm_add_route(w->dapm, &r); + snd_soc_dapm_add_path(w->dapm, + dai->playback_widget, w, NULL, NULL); }
if (dai->driver->capture.stream_name && strstr(w->sname, dai->driver->capture.stream_name)) { - r.source = w->name; - r.sink = dai->capture_widget->name; dev_dbg(dai->dev, "%s -> %s\n", - r.source, r.sink); + w->name, dai->capture_widget->name);
- snd_soc_dapm_add_route(w->dapm, &r); + snd_soc_dapm_add_path(w->dapm, w, + dai->capture_widget, NULL, NULL); } } }
On Mon, Jul 29, 2013 at 05:14:02PM +0200, Lars-Peter Clausen wrote:
snd_soc_dapm_add_path() is similar to snd_soc_dapm_add_route() except that it expects the pointer to the source and sink widgets instead of their names. This allows us to simplify the case where we already have a pointer to widgets. (E.g. as we have in snd_soc_dapm_link_dai_widgets()). snd_soc_dapm_add_route() will be updated to just look up the widget and then use snd_soc_dapm_add_path() to handle everything else.
I've applied everything up to patch 9, this is all really good stuff. This one would've been a bit easier to review if the refactoring were split out from the changes to the users (who could've continued to use add_route() for the time being).
Patch 10 I want to have a bit more of a think about and probably try using it in something.
On 07/29/2013 07:45 PM, Mark Brown wrote:
On Mon, Jul 29, 2013 at 05:14:02PM +0200, Lars-Peter Clausen wrote:
snd_soc_dapm_add_path() is similar to snd_soc_dapm_add_route() except that it expects the pointer to the source and sink widgets instead of their names. This allows us to simplify the case where we already have a pointer to widgets. (E.g. as we have in snd_soc_dapm_link_dai_widgets()). snd_soc_dapm_add_route() will be updated to just look up the widget and then use snd_soc_dapm_add_path() to handle everything else.
I've applied everything up to patch 9, this is all really good stuff. This one would've been a bit easier to review if the refactoring were split out from the changes to the users (who could've continued to use add_route() for the time being).
Patch 10 I want to have a bit more of a think about and probably try using it in something.
I do have a driver that uses the auto-disable feature. The plan is to submit the driver soonish. I can resend patch 10 as part of the patch series that adds the driver if necessary.
- Lars
Wait with updating the widgets power field until the changes are actually written to the hardware in dapm_seq_run_coalesced(). This will allow us to query the current hardware state between calling dapm_power_one_widget() and actually writing the new power state to hardware.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- sound/soc/soc-dapm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b811a27..9abb3b2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -293,6 +293,7 @@ static void dapm_reset(struct snd_soc_card *card) memset(&card->dapm_stats, 0, sizeof(card->dapm_stats));
list_for_each_entry(w, &card->widgets, list) { + w->new_power = w->power; w->power_checked = false; w->inputs = -1; w->outputs = -1; @@ -1340,7 +1341,7 @@ static void dapm_seq_check_event(struct snd_soc_card *card, return; }
- if (w->power != power) + if (w->new_power != power) return;
if (w->event && (w->event_flags & event)) { @@ -1369,6 +1370,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
list_for_each_entry(w, pending, power_list) { BUG_ON(reg != w->reg); + w->power = w->new_power;
mask |= w->mask << w->shift; if (w->power) @@ -1676,8 +1678,6 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, dapm_seq_insert(w, up_list, true); else dapm_seq_insert(w, down_list, false); - - w->power = power; }
static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, @@ -1752,7 +1752,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) break; }
- if (w->power) { + if (w->new_power) { d = w->dapm;
/* Supplies and micbiases only bring the
Some devices have the problem that if a internal audio signal source is disabled the output of the source becomes undefined or goes to a undesired state (E.g. DAC output goes to ground instead of VMID). In this case it is necessary, in order to avoid unwanted clicks and pops, to disable any mixer input the signal feeds into or to active a mute control along the path to the output. Often it is still desirable to expose the same mixer input control to userspace, so cerain paths can sill be disabled manually. This means we can not use conventional DAPM to manage the mixer input control. This patch implements a method for letting DAPM overwrite the state of a userspace visible control. I.e. DAPM will disable the control if the path on which the control sits becomes inactive. Userspace will then only see a cached copy of the controls state. Once DAPM powers the path up again it will sync the userspace setting with the hardware and give control back to userspace.
To implement this a new widget type is introduced. One widget of this type will be created for each DAPM kcontrol which has the auto-disable feature enabled. For each path that is controlled by the kcontrol the widget will be connected to the source of that path. The new widget type behaves like a supply widget, which means it will power up if one of its sinks are powered up and will only power down if all of its sinks are powered down. In order to only have the mixer input enabled when the source signal is valid the new widget type will be disabled before all other widget types and only be enabled after all other widget types.
E.g. consider the following simplified example. A DAC is connected to a mixer and the mixer has a control to enable or disable the signal from the DAC.
+-------+ +-----+ | | | DAC |-----[Ctrl]-| Mixer | +-----+ : | | | : +-------+ | : +-------------+ | Ctrl widget | +-------------+
If the control has the auto-disable feature enabled we'll create a widget for the control. This widget is connected to the DAC as it is the source for the mixer input. If the DAC powers up the control widget powers up and if the DAC powers down the control widget is powered down. As long as the control widget is powered down the hardware input control is kept disabled and if it is enabled userspace can freely change the control's state.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/soc-dapm.h | 17 +++++++- include/sound/soc.h | 28 ++++++------ sound/soc/soc-dapm.c | 108 +++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 125 insertions(+), 28 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3575721..c728d28 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -280,14 +280,26 @@ struct device; { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } +#define SOC_DAPM_SINGLE_AUTODISABLE(xname, reg, shift, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 1) } #define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ .tlv.p = (tlv_array), \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } +#define SOC_DAPM_SINGLE_TLV_AUTODISABLE(xname, reg, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_DAPM_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ @@ -484,6 +496,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_dai_in, /* link to DAI structure */ snd_soc_dapm_dai_out, snd_soc_dapm_dai_link, /* link between two DAI structures */ + snd_soc_dapm_kcontrol, /* Auto-disabled kcontrol */ };
enum snd_soc_dapm_subclass { diff --git a/include/sound/soc.h b/include/sound/soc.h index b1e1f96..6201c6e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -30,13 +30,13 @@ /* * Convenience kcontrol builders */ -#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert) \ +#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert, xautodisable) \ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xreg, .rreg = xreg, .shift = shift_left, \ .rshift = shift_right, .max = xmax, .platform_max = xmax, \ - .invert = xinvert}) -#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \ - SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert) + .invert = xinvert, .autodisable = xautodisable}) +#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \ + SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable) #define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xreg, .max = xmax, .platform_max = xmax, .invert = xinvert}) @@ -52,7 +52,7 @@ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_SINGLE_RANGE(xname, xreg, xshift, xmin, xmax, xinvert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw_range, .get = snd_soc_get_volsw_range, \ @@ -68,7 +68,7 @@ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_SINGLE_SX_TLV(xname, xreg, xshift, xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ @@ -97,7 +97,7 @@ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \ - max, invert) } + max, invert, 0) } #define SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .info = snd_soc_info_volsw, \ @@ -119,7 +119,7 @@ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \ - max, invert) } + max, invert, 0) } #define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ @@ -190,14 +190,14 @@ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) } #define SOC_DOUBLE_EXT(xname, reg, shift_left, shift_right, max, invert,\ xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = \ - SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert) } + SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert, 0) } #define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -206,7 +206,7 @@ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) } #define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ @@ -216,7 +216,7 @@ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, \ - xmax, xinvert) } + xmax, xinvert, 0) } #define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ @@ -1088,7 +1088,9 @@ struct snd_soc_pcm_runtime { /* mixer control */ struct soc_mixer_control { int min, max, platform_max; - unsigned int reg, rreg, shift, rshift, invert; + unsigned int reg, rreg, shift, rshift; + unsigned int invert:1; + unsigned int autodisable:1; };
struct soc_bytes { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 9abb3b2..37c9a05 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -47,6 +47,15 @@
#define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++;
+static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, + const char *control, + int (*connected)(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink)); +static struct snd_soc_dapm_widget * +snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget); + /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, @@ -73,16 +82,18 @@ static int dapm_up_seq[] = { [snd_soc_dapm_hp] = 10, [snd_soc_dapm_spk] = 10, [snd_soc_dapm_line] = 10, - [snd_soc_dapm_post] = 11, + [snd_soc_dapm_kcontrol] = 11, + [snd_soc_dapm_post] = 12, };
static int dapm_down_seq[] = { [snd_soc_dapm_pre] = 0, - [snd_soc_dapm_adc] = 1, - [snd_soc_dapm_hp] = 2, - [snd_soc_dapm_spk] = 2, - [snd_soc_dapm_line] = 2, - [snd_soc_dapm_out_drv] = 2, + [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, @@ -176,6 +187,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
struct dapm_kcontrol_data { unsigned int value; + struct snd_soc_dapm_widget *widget; struct list_head paths; struct snd_soc_dapm_widget_list wlist; }; @@ -184,6 +196,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol) { struct dapm_kcontrol_data *data; + struct soc_mixer_control *mc;
data = kzalloc(sizeof(*data) + sizeof(widget), GFP_KERNEL); if (!data) { @@ -197,6 +210,39 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, data->wlist.num_widgets = 1; INIT_LIST_HEAD(&data->paths);
+ switch (widget->id) { + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + mc = (struct soc_mixer_control *)kcontrol->private_value; + + if (mc->autodisable) { + struct snd_soc_dapm_widget template; + + memset(&template, 0, sizeof(template)); + template.reg = mc->reg; + template.mask = (1 << fls(mc->max)) - 1; + template.shift = mc->shift; + if (mc->invert) + template.off_val = mc->max; + else + template.off_val = 0; + template.on_val = template.off_val; + template.id = snd_soc_dapm_kcontrol; + template.name = kcontrol->id.name; + + data->widget = snd_soc_dapm_new_control(widget->dapm, + &template); + if (!data->widget) { + kfree(data); + return -ENOMEM; + } + } + break; + default: + break; + } + kcontrol->private_data = data;
return 0; @@ -205,6 +251,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, static void dapm_kcontrol_free(struct snd_kcontrol *kctl) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl); + kfree(data->widget); kfree(data); }
@@ -242,6 +289,21 @@ static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol, struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
list_add_tail(&path->list_kcontrol, &data->paths); + + if (data->widget) { + snd_soc_dapm_add_path(data->widget->dapm, data->widget, + path->source, NULL, NULL); + } +} + +static bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + if (!data->widget) + return true; + + return data->widget->power; }
static struct list_head *dapm_kcontrol_get_path_list( @@ -271,6 +333,9 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, if (data->value == value) return false;
+ if (data->widget) + data->widget->on_val = value; + data->value = value;
return true; @@ -511,6 +576,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, case snd_soc_dapm_spk: case snd_soc_dapm_line: case snd_soc_dapm_dai_link: + case snd_soc_dapm_kcontrol: p->connect = 1; break; /* does affect routing - dynamically connected */ @@ -874,6 +940,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: return 0; default: break; @@ -969,6 +1036,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: return 0; default: break; @@ -1517,7 +1585,7 @@ static void dapm_widget_update(struct snd_soc_card *card) unsigned int wi; int ret;
- if (!update) + if (!update || !dapm_kcontrol_is_powered(update->kcontrol)) return;
wlist = dapm_kcontrol_get_wlist(update->kcontrol); @@ -1662,6 +1730,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: /* Supplies can't affect their outputs, only their inputs */ break; default: @@ -2329,6 +2398,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: case snd_soc_dapm_dai_link: + case snd_soc_dapm_kcontrol: list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); @@ -2711,6 +2781,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2718,17 +2789,24 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; + unsigned int val;
if (snd_soc_volsw_is_stereo(mc)) dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name);
- ucontrol->value.integer.value[0] = - (snd_soc_read(codec, reg) >> shift) & mask; + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + if (dapm_kcontrol_is_powered(kcontrol)) + val = (snd_soc_read(codec, reg) >> shift) & mask; + else + val = dapm_kcontrol_get_value(kcontrol); + mutex_unlock(&card->dapm_mutex); + if (invert) - ucontrol->value.integer.value[0] = - max - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[0] = max - val; + else + ucontrol->value.integer.value[0] = val;
return 0; } @@ -2769,11 +2847,14 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
if (invert) val = max - val; - mask = mask << shift; - val = val << shift;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+ dapm_kcontrol_set_value(kcontrol, val); + + mask = mask << shift; + val = val << shift; + change = snd_soc_test_bits(codec, reg, mask, val); if (change) { update.kcontrol = kcontrol; @@ -3173,6 +3254,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: w->power_check = dapm_supply_check_power; break; default:
On Mon, Jul 29, 2013 at 05:14:04PM +0200, Lars-Peter Clausen wrote:
- ucontrol->value.integer.value[0] =
(snd_soc_read(codec, reg) >> shift) & mask;
- mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- if (dapm_kcontrol_is_powered(kcontrol))
val = (snd_soc_read(codec, reg) >> shift) & mask;
- else
val = dapm_kcontrol_get_value(kcontrol);
- mutex_unlock(&card->dapm_mutex);
My first thought looking at this is that I would expect this to be encapsulated in kcntrol_get_value(), though at the minute it's actually only returning the virtual value which makes sense for the existing use.
Equally well I'd expect the value to always be a functioning cache of the real value so I think what I'm really saying here is that I don't think we should really be checking if the control is powered at all. We do need the I/O path but the power isn't the reason for it, the fact that we have the value stashed locally is.
Another thing that's bothering me here is that this only works for mono controls but many of the uses are stereo mutes and/or volumes. We'd need to add back the support for those.
On 08/01/2013 12:48 PM, Mark Brown wrote:
On Mon, Jul 29, 2013 at 05:14:04PM +0200, Lars-Peter Clausen wrote:
- ucontrol->value.integer.value[0] =
(snd_soc_read(codec, reg) >> shift) & mask;
- mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- if (dapm_kcontrol_is_powered(kcontrol))
val = (snd_soc_read(codec, reg) >> shift) & mask;
- else
val = dapm_kcontrol_get_value(kcontrol);
- mutex_unlock(&card->dapm_mutex);
My first thought looking at this is that I would expect this to be encapsulated in kcntrol_get_value(), though at the minute it's actually only returning the virtual value which makes sense for the existing use.
Equally well I'd expect the value to always be a functioning cache of the real value so I think what I'm really saying here is that I don't think we should really be checking if the control is powered at all. We do need the I/O path but the power isn't the reason for it, the fact that we have the value stashed locally is.
There might be some corner cases where the driver directly modifies the register value outside of put_volsw() which would break. But otherwise I agree.
Another thing that's bothering me here is that this only works for mono controls but many of the uses are stereo mutes and/or volumes. We'd need to add back the support for those.
But that's a general limitation for DAPM controls. Right now snd_soc_dapm_put_volsw() only supports mono controls. If we have a left and a right mixer we usually have one mono control for each side. But I of course it would be nice to have support for stereo controls here. I guess with the shared control infrastructure we actually have most of what we need in place. The only thing missing is a way to express which channel of the control controls which path. E.g. an extra field per path or per mixer widget selecting the index.
- Lars
On Thu, Aug 01, 2013 at 09:24:11PM +0200, Lars-Peter Clausen wrote:
On 08/01/2013 12:48 PM, Mark Brown wrote:
Equally well I'd expect the value to always be a functioning cache of the real value so I think what I'm really saying here is that I don't think we should really be checking if the control is powered at all. We do need the I/O path but the power isn't the reason for it, the fact that we have the value stashed locally is.
There might be some corner cases where the driver directly modifies the register value outside of put_volsw() which would break. But otherwise I agree.
Yeah, I'd argue that anything doing that is probably broken though.
Another thing that's bothering me here is that this only works for mono controls but many of the uses are stereo mutes and/or volumes. We'd need to add back the support for those.
But that's a general limitation for DAPM controls. Right now snd_soc_dapm_put_volsw() only supports mono controls. If we have a left and a right mixer we usually have one mono control for each side. But I of course it would be nice to have support for stereo controls here. I guess with the shared control infrastructure we actually have most of what we need in place. The only thing missing is a way to express which channel of the control controls which path. E.g. an extra field per path or per mixer widget selecting the index.
Yes, or we could just say as a first pass that as soon as any one of the paths gets enabled the whole control gets enabled which should cover a large propoprtion of use cases (headphones or stereo speakers of which only one might be connected).
On Mon, Jul 29, 2013 at 05:14:04PM +0200, Lars-Peter Clausen wrote:
Some devices have the problem that if a internal audio signal source is disabled the output of the source becomes undefined or goes to a undesired state (E.g.
I was about to apply this but it looks like it needs refreshing after the bug fixes - can you check and resend please?
participants (2)
-
Lars-Peter Clausen
-
Mark Brown