From f227b88f0fce5f9b82aa934f8829a741c2e06d82 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 16 Jan 2014 16:02:10 +0100 Subject: ASoC: core: Add signed register volume control logic Some codecs use signed volume control representation with non standard register sizes, e.g. 6 or 7 bit signed integers. This patch adds generic signed register volume control logic to soc-core. Instead of a fixed width signed register control, this implementation uses a 'min' value and the signed bit location to translate it to an absolute volume. Using the 'sign_bit' we can calculate a correct mask for the register values and translate it back into signed integers of standard size. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/sound') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9a001472b96a..a25f3ccb3fde 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1067,6 +1067,7 @@ struct soc_mixer_control { int min, max, platform_max; int reg, rreg; unsigned int shift, rshift; + unsigned int sign_bit; unsigned int invert:1; unsigned int autodisable:1; }; -- cgit v1.2.3 From cd21b123346c6a2f033d8c3bd2bf240198b5712a Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 16 Jan 2014 16:02:11 +0100 Subject: ASoC: core: Add SOC_DOUBLE_R_S_TLV Add a macro for signed mixer with two registers and tlv array. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- include/sound/soc.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include/sound') diff --git a/include/sound/soc.h b/include/sound/soc.h index a25f3ccb3fde..c4be7ab89399 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -45,6 +45,11 @@ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \ .max = xmax, .platform_max = xmax, .invert = xinvert}) +#define SOC_DOUBLE_R_S_VALUE(xlreg, xrreg, xshift, xmin, xmax, xsign_bit, xinvert) \ + ((unsigned long)&(struct soc_mixer_control) \ + {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \ + .max = xmax, .min = xmin, .platform_max = xmax, .sign_bit = xsign_bit, \ + .invert = xinvert}) #define SOC_DOUBLE_R_RANGE_VALUE(xlreg, xrreg, xshift, xmin, xmax, xinvert) \ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \ @@ -152,6 +157,15 @@ {.reg = xreg, .rreg = xrreg, \ .shift = xshift, .rshift = xshift, \ .max = xmax, .min = xmin} } +#define SOC_DOUBLE_R_S_TLV(xname, reg_left, reg_right, xshift, xmin, xmax, xsign_bit, xinvert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \ + .private_value = SOC_DOUBLE_R_S_VALUE(reg_left, reg_right, xshift, \ + xmin, xmax, xsign_bit, xinvert) } #define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ -- cgit v1.2.3 From 113911006442a36c2b4669faf1699d9042ef80ab Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 18 Feb 2014 15:22:14 +0000 Subject: ASoC: dapm: Add locking to snd_soc_dapm_xxxx_pin functions The snd_soc_dapm_xxxx_pin all require the dapm_mutex to be held when they are called as they edit the dirty list, however very few of the callers do so. This patch adds unlocked versions of all the functions replacing the existing implementations with one that holds the lock internally. We also fix up the places where the lock was actually held on the caller side. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/extcon/extcon-arizona.c | 12 ---- drivers/input/misc/arizona-haptics.c | 16 ----- include/sound/soc-dapm.h | 8 +++ sound/soc/soc-dapm.c | 133 ++++++++++++++++++++++++++++++++--- 4 files changed, 131 insertions(+), 38 deletions(-) (limited to 'include/sound') diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index c20602f601ee..98a14f6143a7 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -222,27 +222,19 @@ static void arizona_extcon_pulse_micbias(struct arizona_extcon_info *info) struct snd_soc_dapm_context *dapm = arizona->dapm; int ret; - mutex_lock(&dapm->card->dapm_mutex); - ret = snd_soc_dapm_force_enable_pin(dapm, widget); if (ret != 0) dev_warn(arizona->dev, "Failed to enable %s: %d\n", widget, ret); - mutex_unlock(&dapm->card->dapm_mutex); - snd_soc_dapm_sync(dapm); if (!arizona->pdata.micd_force_micbias) { - mutex_lock(&dapm->card->dapm_mutex); - ret = snd_soc_dapm_disable_pin(arizona->dapm, widget); if (ret != 0) dev_warn(arizona->dev, "Failed to disable %s: %d\n", widget, ret); - mutex_unlock(&dapm->card->dapm_mutex); - snd_soc_dapm_sync(dapm); } } @@ -304,16 +296,12 @@ static void arizona_stop_mic(struct arizona_extcon_info *info) ARIZONA_MICD_ENA, 0, &change); - mutex_lock(&dapm->card->dapm_mutex); - ret = snd_soc_dapm_disable_pin(dapm, widget); if (ret != 0) dev_warn(arizona->dev, "Failed to disable %s: %d\n", widget, ret); - mutex_unlock(&dapm->card->dapm_mutex); - snd_soc_dapm_sync(dapm); if (info->micd_reva) { diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c index e7e12a5f5c2d..ef2e281b0a43 100644 --- a/drivers/input/misc/arizona-haptics.c +++ b/drivers/input/misc/arizona-haptics.c @@ -37,7 +37,6 @@ static void arizona_haptics_work(struct work_struct *work) struct arizona_haptics, work); struct arizona *arizona = haptics->arizona; - struct mutex *dapm_mutex = &arizona->dapm->card->dapm_mutex; int ret; if (!haptics->arizona->dapm) { @@ -67,18 +66,13 @@ static void arizona_haptics_work(struct work_struct *work) return; } - mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS"); if (ret != 0) { dev_err(arizona->dev, "Failed to start HAPTICS: %d\n", ret); - mutex_unlock(dapm_mutex); return; } - mutex_unlock(dapm_mutex); - ret = snd_soc_dapm_sync(arizona->dapm); if (ret != 0) { dev_err(arizona->dev, "Failed to sync DAPM: %d\n", @@ -87,18 +81,13 @@ static void arizona_haptics_work(struct work_struct *work) } } else { /* This disable sequence will be a noop if already enabled */ - mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS"); if (ret != 0) { dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n", ret); - mutex_unlock(dapm_mutex); return; } - mutex_unlock(dapm_mutex); - ret = snd_soc_dapm_sync(arizona->dapm); if (ret != 0) { dev_err(arizona->dev, "Failed to sync DAPM: %d\n", @@ -152,16 +141,11 @@ static int arizona_haptics_play(struct input_dev *input, void *data, static void arizona_haptics_close(struct input_dev *input) { struct arizona_haptics *haptics = input_get_drvdata(input); - struct mutex *dapm_mutex = &haptics->arizona->dapm->card->dapm_mutex; cancel_work_sync(&haptics->work); - mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - if (haptics->arizona->dapm) snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS"); - - mutex_unlock(dapm_mutex); } static int arizona_haptics_probe(struct platform_device *pdev) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 68d92e36facd..6e89ef6c11c1 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -449,14 +449,22 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, /* dapm audio pin control and status */ int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin); int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin); int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin); int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); +int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin); int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin); void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index fd39cd2827d7..b9dc6acbba8c 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3210,15 +3210,11 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); const char *pin = (const char *)kcontrol->private_value; - mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - if (ucontrol->value.integer.value[0]) snd_soc_dapm_enable_pin(&card->dapm, pin); else snd_soc_dapm_disable_pin(&card->dapm, pin); - mutex_unlock(&card->dapm_mutex); - snd_soc_dapm_sync(&card->dapm); return 0; } @@ -3766,6 +3762,26 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, mutex_unlock(&card->dapm_mutex); } +/** + * snd_soc_dapm_enable_pin_unlocked - enable pin. + * @dapm: DAPM context + * @pin: pin name + * + * Enables input/output pin and its parents or children widgets iff there is + * a valid audio route and active audio stream. + * + * Requires external locking. + * + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. + */ +int snd_soc_dapm_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin) +{ + return snd_soc_dapm_set_pin(dapm, pin, 1); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin_unlocked); + /** * snd_soc_dapm_enable_pin - enable pin. * @dapm: DAPM context @@ -3773,17 +3789,26 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, * * Enables input/output pin and its parents or children widgets iff there is * a valid audio route and active audio stream. + * * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to * do any widget power switching. */ int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin) { - return snd_soc_dapm_set_pin(dapm, pin, 1); + int ret; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_set_pin(dapm, pin, 1); + + mutex_unlock(&dapm->card->dapm_mutex); + + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); /** - * snd_soc_dapm_force_enable_pin - force a pin to be enabled + * snd_soc_dapm_force_enable_pin_unlocked - force a pin to be enabled * @dapm: DAPM context * @pin: pin name * @@ -3791,11 +3816,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); * intended for use with microphone bias supplies used in microphone * jack detection. * + * Requires external locking. + * * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to * do any widget power switching. */ -int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, - const char *pin) +int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin) { struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); @@ -3811,24 +3838,102 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, return 0; } +EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin_unlocked); + +/** + * snd_soc_dapm_force_enable_pin - force a pin to be enabled + * @dapm: DAPM context + * @pin: pin name + * + * Enables input/output pin regardless of any other state. This is + * intended for use with microphone bias supplies used in microphone + * jack detection. + * + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. + */ +int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, + const char *pin) +{ + int ret; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_force_enable_pin_unlocked(dapm, pin); + + mutex_unlock(&dapm->card->dapm_mutex); + + return ret; +} EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin); +/** + * snd_soc_dapm_disable_pin_unlocked - disable pin. + * @dapm: DAPM context + * @pin: pin name + * + * Disables input/output pin and its parents or children widgets. + * + * Requires external locking. + * + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. + */ +int snd_soc_dapm_disable_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin) +{ + return snd_soc_dapm_set_pin(dapm, pin, 0); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin_unlocked); + /** * snd_soc_dapm_disable_pin - disable pin. * @dapm: DAPM context * @pin: pin name * * Disables input/output pin and its parents or children widgets. + * * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to * do any widget power switching. */ int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, const char *pin) { - return snd_soc_dapm_set_pin(dapm, pin, 0); + int ret; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_set_pin(dapm, pin, 0); + + mutex_unlock(&dapm->card->dapm_mutex); + + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); +/** + * snd_soc_dapm_nc_pin_unlocked - permanently disable pin. + * @dapm: DAPM context + * @pin: pin name + * + * Marks the specified pin as being not connected, disabling it along + * any parent or child widgets. At present this is identical to + * snd_soc_dapm_disable_pin() but in future it will be extended to do + * additional things such as disabling controls which only affect + * paths through the pin. + * + * Requires external locking. + * + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. + */ +int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, + const char *pin) +{ + return snd_soc_dapm_set_pin(dapm, pin, 0); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin_unlocked); + /** * snd_soc_dapm_nc_pin - permanently disable pin. * @dapm: DAPM context @@ -3845,7 +3950,15 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); */ int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin) { - return snd_soc_dapm_set_pin(dapm, pin, 0); + int ret; + + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_set_pin(dapm, pin, 0); + + mutex_unlock(&dapm->card->dapm_mutex); + + return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); -- cgit v1.2.3 From f6d5e586b416746664c01ab5d4b19ed5e2d1cbaa Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 18 Feb 2014 15:22:13 +0000 Subject: ASoC: dapm: Add helpers to lock/unlock DAPM mutex Acquiring the DAPM mutex is necessary before using several DAPM functions and dereference is quite ugly. This patch provides a helper function to simplify this. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/sound/soc.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include/sound') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9a001472b96a..1e12b66da2cc 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1188,4 +1188,15 @@ extern struct dentry *snd_soc_debugfs_root; extern const struct dev_pm_ops snd_soc_pm_ops; +/* Helper functions */ +static inline void snd_soc_dapm_mutex_lock(struct snd_soc_dapm_context *dapm) +{ + mutex_lock(&dapm->card->dapm_mutex); +} + +static inline void snd_soc_dapm_mutex_unlock(struct snd_soc_dapm_context *dapm) +{ + mutex_unlock(&dapm->card->dapm_mutex); +} + #endif -- cgit v1.2.3 From 3eb29dfb3d3bd4b600370007b96c3c675fb97aa7 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 18 Feb 2014 15:22:15 +0000 Subject: ASoC: dapm: Add unlocked version of snd_soc_dapm_sync We will often call sync after several functions that require the DAPM mutex to be held. Rather than release and immediately relock the mutex provide an unlocked function for this situation. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 + sound/soc/soc-dapm.c | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'include/sound') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 6e89ef6c11c1..3b991766a8f2 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -461,6 +461,7 @@ int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm); +int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin); int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 48a4eeb05192..4200f96a1483 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2342,18 +2342,18 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, } /** - * snd_soc_dapm_sync - scan and power dapm paths + * snd_soc_dapm_sync_unlocked - scan and power dapm paths * @dapm: DAPM context * * Walks all dapm audio paths and powers widgets according to their * stream or path usage. * + * Requires external locking. + * * Returns 0 for success. */ -int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) +int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm) { - int ret; - /* * Suppress early reports (eg, jacks syncing their state) to avoid * silly DAPM runs during card startup. @@ -2361,8 +2361,25 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) if (!dapm->card || !dapm->card->instantiated) return 0; + return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_unlocked); + +/** + * snd_soc_dapm_sync - scan and power dapm paths + * @dapm: DAPM context + * + * Walks all dapm audio paths and powers widgets according to their + * stream or path usage. + * + * Returns 0 for success. + */ +int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) +{ + int ret; + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); + ret = snd_soc_dapm_sync_unlocked(dapm); mutex_unlock(&dapm->card->dapm_mutex); return ret; } -- cgit v1.2.3