Virtual switches are similar to virtual MUX controls. It does not have a representation in the actual hardware but allows to control the DAPM routing by enabling or disabling a input path into the mixer. This is useful if we want to present a way to the user to for example mute a path despite the absence of a separate mute control in hardware.
More specific this will be used by the ADAU1361 driver where the hardware has a mute control for each of the DAC output paths, but we have to make sure that the path is muted whenever the DAC is disabled. So the mute switches needs to be controlled by DAPM so it can disable them before disabling the DAC. On the other hand we still want to be to mute individual paths. This will be accomplished by using virtual switch controls.
Signed-off-by: Lars-Peter Clausen lars@metafoo.de --- include/sound/soc-dapm.h | 10 +++++++ sound/soc/soc-dapm.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e1ef63d..c3d0371 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -257,6 +257,12 @@ struct device; .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) } +#define SOC_DAPM_SINGLE_VIRT(xname, shift, max) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw_virt, \ + .put = snd_soc_dapm_put_volsw_virt, \ + .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, shift, max, 0) } #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, \ @@ -344,6 +350,10 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_put_volsw_virt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_get_volsw_virt(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 1e36bc8..c3a0f43 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -347,7 +347,10 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert;
- val = soc_widget_read(w, reg); + if (reg >= 0) + val = soc_widget_read(w, reg); + else + val = 0; val = (val >> shift) & mask; if (invert) val = max - val; @@ -2697,6 +2700,79 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
/** + * snd_soc_dapm_get_volsw_virt - virtual dapm mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a virtual dapm mixer control. + * + * Returns 0 for success. + */ +int snd_soc_dapm_get_volsw_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 *widget = wlist->widgets[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int shift = mc->shift; + unsigned int mask = (1 << fls(mc->max)) - 1; + + ucontrol->value.integer.value[0] = (widget->value >> shift) & mask; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw_virt); + +/** + * snd_soc_dapm_put_volsw_virt - virtual dapm mixer set callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to set the value of a virutal dapm mixer control. + * + * Returns 0 for success. + */ +int snd_soc_dapm_put_volsw_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 *widget = wlist->widgets[0]; + struct snd_soc_card *card = widget->codec->card; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int shift = mc->shift; + unsigned int val, connect; + int wi; + + val = ucontrol->value.integer.value[0] & mask; + + mask = mask << shift; + val = val << shift; + + if (val) + connect = 1; + else + connect = 0; + + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + if (val != (widget->value & mask)) { + widget->value &= ~mask; + widget->value |= val; + for (wi = 0; wi < wlist->num_widgets; wi++) { + widget = wlist->widgets[wi]; + soc_dapm_mixer_update_power(widget, kcontrol, connect); + } + } + + mutex_unlock(&card->dapm_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw_virt); + +/** * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback * @kcontrol: mixer control * @ucontrol: control element information