Currently when updating a control that is shared between multiple widgets the
whole power-up/power-down sequence is being run once for each widget. The
control register is updated during the first run, which means the CODEC internal
routing is also updated for all widgets during this first run. The input and
output paths for each widgets are only updated though during the respective run
for that widget. This leads to a slight inconsistency between the CODEC's
internal state and ASoC's state, which causes non optimal behavior in regard to
click and pop avoidance.
E.g. consider the following setup where two MUXs share the same control.
+------+
A1 ------| |
| MUX1 |----- C1
B1 ------| |
+------+
|
control ---+
|
+------+
A2 ------| |
| MUX2 |----- C2
B2 ------| |
+------+
If the control is updated to switch the MUXs from input A to input B with the
current code the power-up/power-down sequence will look like this:
Run soc_dapm_mux_update_power for MUX1
Power-down A1
Update MUXing
Power-up B1
Run soc_dapm_mux_update_power for MUX2
Power-down A2
(Update MUXing)
Power-up B2
Note that the second 'Update Muxing' is a no-op, since the register was already
updated.
While the preferred order for avoiding pops and clicks should be:
Run soc_dapm_mux_update_power for control
Power-down A1
Power-down A2
Update MUXing
Power-up B1
Power-up B2
This patch changes the behavior to the later by running the updates for all
widgets that the control is attached to at the same time.
The new code is also a bit simpler since callers of
soc_dapm_{mux,muxer}_update_power don't have to loop over each widget anymore
and neither do we need to keep track for which of the kcontrol's widgets the
current update is.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Mark Brown <broonie@linaro.org>
void snd_soc_dapm_shutdown(struct snd_soc_card *card);
/* external DAPM widget events */
void snd_soc_dapm_shutdown(struct snd_soc_card *card);
/* external DAPM widget events */
-int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
struct snd_kcontrol *kcontrol, int connect);
struct snd_kcontrol *kcontrol, int connect);
-int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
- struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e);
+int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
+ struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e);
/* dapm sys fs - used by the core */
int snd_soc_dapm_sys_add(struct device *dev);
/* dapm sys fs - used by the core */
int snd_soc_dapm_sys_add(struct device *dev);
};
struct snd_soc_dapm_update {
};
struct snd_soc_dapm_update {
- struct snd_soc_dapm_widget *widget;
struct snd_kcontrol *kcontrol;
int reg;
int mask;
struct snd_kcontrol *kcontrol;
int reg;
int mask;
static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
{
struct snd_soc_dapm_update *update = dapm->update;
static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
{
struct snd_soc_dapm_update *update = dapm->update;
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget_list *wlist;
+ struct snd_soc_dapm_widget *w = NULL;
+ unsigned int wi;
int ret;
if (!update)
return;
int ret;
if (!update)
return;
+ wlist = snd_kcontrol_chip(update->kcontrol);
- 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",
- w->name, ret);
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ w = wlist->widgets[wi];
+
+ 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",
+ w->name, ret);
+ }
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",
w->name, ret);
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",
w->name, ret);
- 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",
- w->name, ret);
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ w = wlist->widgets[wi];
+
+ 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",
+ w->name, ret);
+ }
#endif
/* test and update the power status of a mux widget */
#endif
/* test and update the power status of a mux widget */
-static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
+static int soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
int found = 0;
struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
int found = 0;
- if (widget->id != snd_soc_dapm_mux &&
- widget->id != snd_soc_dapm_virt_mux &&
- widget->id != snd_soc_dapm_value_mux)
- return -ENODEV;
-
/* find dapm widget path assoc with kcontrol */
/* find dapm widget path assoc with kcontrol */
- list_for_each_entry(path, &widget->dapm->card->paths, list) {
+ list_for_each_entry(path, &dapm->card->paths, list) {
if (path->kcontrol != kcontrol)
continue;
if (path->kcontrol != kcontrol)
continue;
"mux disconnection");
path->connect = 0; /* old connection must be powered down */
}
"mux disconnection");
path->connect = 0; /* old connection must be powered down */
}
+ dapm_mark_dirty(path->sink, "mux change");
- if (found) {
- dapm_mark_dirty(widget, "mux change");
- dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
- }
+ if (found)
+ dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
-int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
- struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
+int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
+ struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
- struct snd_soc_card *card = widget->dapm->card;
+ struct snd_soc_card *card = dapm->card;
int ret;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
int ret;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+ ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e);
mutex_unlock(&card->dapm_mutex);
if (ret > 0)
soc_dpcm_runtime_update(card);
mutex_unlock(&card->dapm_mutex);
if (ret > 0)
soc_dpcm_runtime_update(card);
EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
/* test and update the power status of a mixer or switch widget */
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_widget *widget,
+static int soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
struct snd_kcontrol *kcontrol, int connect)
{
struct snd_soc_dapm_path *path;
int found = 0;
struct snd_kcontrol *kcontrol, int connect)
{
struct snd_soc_dapm_path *path;
int found = 0;
- if (widget->id != snd_soc_dapm_mixer &&
- widget->id != snd_soc_dapm_mixer_named_ctl &&
- widget->id != snd_soc_dapm_switch)
- return -ENODEV;
-
/* find dapm widget path assoc with kcontrol */
/* find dapm widget path assoc with kcontrol */
- list_for_each_entry(path, &widget->dapm->card->paths, list) {
+ list_for_each_entry(path, &dapm->card->paths, list) {
if (path->kcontrol != kcontrol)
continue;
if (path->kcontrol != kcontrol)
continue;
found = 1;
path->connect = connect;
dapm_mark_dirty(path->source, "mixer connection");
found = 1;
path->connect = connect;
dapm_mark_dirty(path->source, "mixer connection");
+ dapm_mark_dirty(path->sink, "mixer update");
- if (found) {
- dapm_mark_dirty(widget, "mixer update");
- dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
- }
+ if (found)
+ dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
-int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
- struct snd_kcontrol *kcontrol, int connect)
+int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
+ struct snd_kcontrol *kcontrol, int connect)
- struct snd_soc_card *card = widget->dapm->card;
+ struct snd_soc_card *card = dapm->card;
int ret;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
int ret;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- ret = soc_dapm_mixer_update_power(widget, kcontrol, connect);
+ ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect);
mutex_unlock(&card->dapm_mutex);
if (ret > 0)
soc_dpcm_runtime_update(card);
mutex_unlock(&card->dapm_mutex);
if (ret > 0)
soc_dpcm_runtime_update(card);
unsigned int val;
int connect, change;
struct snd_soc_dapm_update update;
unsigned int val;
int connect, change;
struct snd_soc_dapm_update update;
if (snd_soc_volsw_is_stereo(mc))
dev_warn(widget->dapm->dev,
if (snd_soc_volsw_is_stereo(mc))
dev_warn(widget->dapm->dev,
change = snd_soc_test_bits(widget->codec, reg, mask, val);
if (change) {
change = snd_soc_test_bits(widget->codec, reg, mask, val);
if (change) {
- for (wi = 0; wi < wlist->num_widgets; wi++) {
- widget = wlist->widgets[wi];
-
- widget->value = val;
+ update.kcontrol = kcontrol;
+ update.reg = reg;
+ update.mask = mask;
+ update.val = val;
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ widget->dapm->update = &update;
- soc_dapm_mixer_update_power(widget, kcontrol, connect);
+ soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect);
- widget->dapm->update = NULL;
- }
+ widget->dapm->update = NULL;
}
mutex_unlock(&card->dapm_mutex);
}
mutex_unlock(&card->dapm_mutex);
unsigned int val, mux, change;
unsigned int mask;
struct snd_soc_dapm_update update;
unsigned int val, mux, change;
unsigned int mask;
struct snd_soc_dapm_update update;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
if (change) {
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
if (change) {
- for (wi = 0; wi < wlist->num_widgets; wi++) {
- widget = wlist->widgets[wi];
+ update.kcontrol = kcontrol;
+ update.reg = e->reg;
+ update.mask = mask;
+ update.val = val;
+ widget->dapm->update = &update;
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = e->reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e);
- soc_dapm_mux_update_power(widget, kcontrol, mux, e);
-
- widget->dapm->update = NULL;
- }
+ widget->dapm->update = NULL;
}
mutex_unlock(&card->dapm_mutex);
}
mutex_unlock(&card->dapm_mutex);
struct soc_enum *e =
(struct soc_enum *)kcontrol->private_value;
int change;
struct soc_enum *e =
(struct soc_enum *)kcontrol->private_value;
int change;
if (ucontrol->value.enumerated.item[0] >= e->max)
return -EINVAL;
if (ucontrol->value.enumerated.item[0] >= e->max)
return -EINVAL;
change = widget->value != ucontrol->value.enumerated.item[0];
if (change) {
change = widget->value != ucontrol->value.enumerated.item[0];
if (change) {
- for (wi = 0; wi < wlist->num_widgets; wi++) {
- widget = wlist->widgets[wi];
-
- widget->value = ucontrol->value.enumerated.item[0];
-
- soc_dapm_mux_update_power(widget, kcontrol, widget->value, e);
- }
+ widget->value = ucontrol->value.enumerated.item[0];
+ soc_dapm_mux_update_power(widget->dapm, kcontrol, widget->value, e);
}
mutex_unlock(&card->dapm_mutex);
}
mutex_unlock(&card->dapm_mutex);
unsigned int val, mux, change;
unsigned int mask;
struct snd_soc_dapm_update update;
unsigned int val, mux, change;
unsigned int mask;
struct snd_soc_dapm_update update;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL;
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
if (change) {
change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
if (change) {
- for (wi = 0; wi < wlist->num_widgets; wi++) {
- widget = wlist->widgets[wi];
-
- widget->value = val;
- update.kcontrol = kcontrol;
- update.widget = widget;
- update.reg = e->reg;
- update.mask = mask;
- update.val = val;
- widget->dapm->update = &update;
+ update.kcontrol = kcontrol;
+ update.reg = e->reg;
+ update.mask = mask;
+ update.val = val;
+ widget->dapm->update = &update;
- soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+ soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e);
- widget->dapm->update = NULL;
- }
+ widget->dapm->update = NULL;
}
mutex_unlock(&card->dapm_mutex);
}
mutex_unlock(&card->dapm_mutex);