diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2013-08-05 11:27:31 +0200 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2013-08-05 15:50:27 +0100 |
commit | 57295073b6acfdfaf9319d3caf92a5c433fdf109 (patch) | |
tree | 0ded984b3452b7ee0fc5a2ead00ad84a95337255 /include/sound | |
parent | 2c75bdf3fd935119cf8681ac0df2b4a5edd5167d (diff) | |
download | linux-57295073b6acfdfaf9319d3caf92a5c433fdf109.tar.gz linux-57295073b6acfdfaf9319d3caf92a5c433fdf109.tar.bz2 linux-57295073b6acfdfaf9319d3caf92a5c433fdf109.zip |
ASoC: dapm: Implement mixer input auto-disable
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>
Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'include/sound')
-rw-r--r-- | include/sound/soc-dapm.h | 17 | ||||
-rw-r--r-- | include/sound/soc.h | 28 |
2 files changed, 30 insertions, 15 deletions
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3575721a955d..c728d28ae9a5 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 b1e1f967ae1e..6201c6ede8ba 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 { |