2020-05-28 10:47:46 +09:00
// SPDX-License-Identifier: GPL-2.0
//
// soc-card.c
//
// Copyright (C) 2019 Renesas Electronics Corp.
// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
//
ASoC: soc-card: Fix missing locking in snd_soc_card_get_kcontrol()
snd_soc_card_get_kcontrol() must be holding a read lock on
card->controls_rwsem while walking the controls list.
Compare with snd_ctl_find_numid().
The existing function is renamed snd_soc_card_get_kcontrol_locked()
so that it can be called from contexts that are already holding
card->controls_rwsem (for example, control get/put functions).
There are few direct or indirect callers of
snd_soc_card_get_kcontrol(), and most are safe. Three require
changes, which have been included in this patch:
codecs/cs35l45.c:
cs35l45_activate_ctl() is called from a control put() function so
is changed to call snd_soc_card_get_kcontrol_locked().
codecs/cs35l56.c:
cs35l56_sync_asp1_mixer_widgets_with_firmware() is called from
control get()/put() functions so is changed to call
snd_soc_card_get_kcontrol_locked().
fsl/fsl_xcvr.c:
fsl_xcvr_activate_ctl() is called from three places, one of which
already holds card->controls_rwsem:
1. fsl_xcvr_mode_put(), a control put function, which will
already be holding card->controls_rwsem.
2. fsl_xcvr_startup(), a DAI startup function.
3. fsl_xcvr_shutdown(), a DAI shutdown function.
To fix this, fsl_xcvr_activate_ctl() has been changed to call
snd_soc_card_get_kcontrol_locked() so that it is safe to call
directly from fsl_xcvr_mode_put().
The fsl_xcvr_startup() and fsl_xcvr_shutdown() functions have been
changed to take a read lock on card->controls_rsem() around calls
to fsl_xcvr_activate_ctl(). While this is not very elegant, it
keeps the change small, to avoid this patch creating a large
collateral churn in fsl/fsl_xcvr.c.
Analysis of other callers of snd_soc_card_get_kcontrol() is that
they do not need any changes, they are not holding card->controls_rwsem
when they call snd_soc_card_get_kcontrol().
Direct callers of snd_soc_card_get_kcontrol():
fsl/fsl_spdif.c: fsl_spdif_dai_probe() - DAI probe function
fsl/fsl_micfil.c: voice_detected_fn() - IRQ handler
Indirect callers via soc_component_notify_control():
codecs/cs42l43: cs42l43_mic_shutter() - IRQ handler
codecs/cs42l43: cs42l43_spk_shutter() - IRQ handler
codecs/ak4118.c: ak4118_irq_handler() - IRQ handler
codecs/wm_adsp.c: wm_adsp_write_ctl() - not currently used
Indirect callers via snd_soc_limit_volume():
qcom/sc8280xp.c: sc8280xp_snd_init() - DAIlink init function
ti/rx51.c: rx51_aic34_init() - DAI init function
I don't have hardware to test the fsl/*, qcom/sc828xp.c, ti/rx51.c
and ak4118.c changes.
Backport note:
The fsl/, qcom/, cs35l45, cs35l56 and cs42l43 callers were added
since the Fixes commit so won't all be present on older kernels.
Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Fixes: 209c6cdfd283 ("ASoC: soc-card: move snd_soc_card_get_kcontrol() to soc-card")
Link: https://lore.kernel.org/r/20240221123710.690224-1-rf@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2024-02-21 12:37:10 +00:00
# include <linux/lockdep.h>
# include <linux/rwsem.h>
2020-05-28 10:47:46 +09:00
# include <sound/soc.h>
2020-05-28 10:48:03 +09:00
# include <sound/jack.h>
2020-05-28 10:47:46 +09:00
# define soc_card_ret(dai, ret) _soc_card_ret(dai, __func__, ret)
static inline int _soc_card_ret ( struct snd_soc_card * card ,
const char * func , int ret )
{
switch ( ret ) {
case - EPROBE_DEFER :
case - ENOTSUPP :
case 0 :
break ;
default :
dev_err ( card - > dev ,
" ASoC: error at %s on %s: %d \n " ,
func , card - > name , ret ) ;
}
return ret ;
}
2020-05-28 10:47:56 +09:00
ASoC: soc-card: Fix missing locking in snd_soc_card_get_kcontrol()
snd_soc_card_get_kcontrol() must be holding a read lock on
card->controls_rwsem while walking the controls list.
Compare with snd_ctl_find_numid().
The existing function is renamed snd_soc_card_get_kcontrol_locked()
so that it can be called from contexts that are already holding
card->controls_rwsem (for example, control get/put functions).
There are few direct or indirect callers of
snd_soc_card_get_kcontrol(), and most are safe. Three require
changes, which have been included in this patch:
codecs/cs35l45.c:
cs35l45_activate_ctl() is called from a control put() function so
is changed to call snd_soc_card_get_kcontrol_locked().
codecs/cs35l56.c:
cs35l56_sync_asp1_mixer_widgets_with_firmware() is called from
control get()/put() functions so is changed to call
snd_soc_card_get_kcontrol_locked().
fsl/fsl_xcvr.c:
fsl_xcvr_activate_ctl() is called from three places, one of which
already holds card->controls_rwsem:
1. fsl_xcvr_mode_put(), a control put function, which will
already be holding card->controls_rwsem.
2. fsl_xcvr_startup(), a DAI startup function.
3. fsl_xcvr_shutdown(), a DAI shutdown function.
To fix this, fsl_xcvr_activate_ctl() has been changed to call
snd_soc_card_get_kcontrol_locked() so that it is safe to call
directly from fsl_xcvr_mode_put().
The fsl_xcvr_startup() and fsl_xcvr_shutdown() functions have been
changed to take a read lock on card->controls_rsem() around calls
to fsl_xcvr_activate_ctl(). While this is not very elegant, it
keeps the change small, to avoid this patch creating a large
collateral churn in fsl/fsl_xcvr.c.
Analysis of other callers of snd_soc_card_get_kcontrol() is that
they do not need any changes, they are not holding card->controls_rwsem
when they call snd_soc_card_get_kcontrol().
Direct callers of snd_soc_card_get_kcontrol():
fsl/fsl_spdif.c: fsl_spdif_dai_probe() - DAI probe function
fsl/fsl_micfil.c: voice_detected_fn() - IRQ handler
Indirect callers via soc_component_notify_control():
codecs/cs42l43: cs42l43_mic_shutter() - IRQ handler
codecs/cs42l43: cs42l43_spk_shutter() - IRQ handler
codecs/ak4118.c: ak4118_irq_handler() - IRQ handler
codecs/wm_adsp.c: wm_adsp_write_ctl() - not currently used
Indirect callers via snd_soc_limit_volume():
qcom/sc8280xp.c: sc8280xp_snd_init() - DAIlink init function
ti/rx51.c: rx51_aic34_init() - DAI init function
I don't have hardware to test the fsl/*, qcom/sc828xp.c, ti/rx51.c
and ak4118.c changes.
Backport note:
The fsl/, qcom/, cs35l45, cs35l56 and cs42l43 callers were added
since the Fixes commit so won't all be present on older kernels.
Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Fixes: 209c6cdfd283 ("ASoC: soc-card: move snd_soc_card_get_kcontrol() to soc-card")
Link: https://lore.kernel.org/r/20240221123710.690224-1-rf@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2024-02-21 12:37:10 +00:00
struct snd_kcontrol * snd_soc_card_get_kcontrol_locked ( struct snd_soc_card * soc_card ,
const char * name )
2020-05-28 10:47:56 +09:00
{
struct snd_card * card = soc_card - > snd_card ;
struct snd_kcontrol * kctl ;
ASoC: soc-card: Fix missing locking in snd_soc_card_get_kcontrol()
snd_soc_card_get_kcontrol() must be holding a read lock on
card->controls_rwsem while walking the controls list.
Compare with snd_ctl_find_numid().
The existing function is renamed snd_soc_card_get_kcontrol_locked()
so that it can be called from contexts that are already holding
card->controls_rwsem (for example, control get/put functions).
There are few direct or indirect callers of
snd_soc_card_get_kcontrol(), and most are safe. Three require
changes, which have been included in this patch:
codecs/cs35l45.c:
cs35l45_activate_ctl() is called from a control put() function so
is changed to call snd_soc_card_get_kcontrol_locked().
codecs/cs35l56.c:
cs35l56_sync_asp1_mixer_widgets_with_firmware() is called from
control get()/put() functions so is changed to call
snd_soc_card_get_kcontrol_locked().
fsl/fsl_xcvr.c:
fsl_xcvr_activate_ctl() is called from three places, one of which
already holds card->controls_rwsem:
1. fsl_xcvr_mode_put(), a control put function, which will
already be holding card->controls_rwsem.
2. fsl_xcvr_startup(), a DAI startup function.
3. fsl_xcvr_shutdown(), a DAI shutdown function.
To fix this, fsl_xcvr_activate_ctl() has been changed to call
snd_soc_card_get_kcontrol_locked() so that it is safe to call
directly from fsl_xcvr_mode_put().
The fsl_xcvr_startup() and fsl_xcvr_shutdown() functions have been
changed to take a read lock on card->controls_rsem() around calls
to fsl_xcvr_activate_ctl(). While this is not very elegant, it
keeps the change small, to avoid this patch creating a large
collateral churn in fsl/fsl_xcvr.c.
Analysis of other callers of snd_soc_card_get_kcontrol() is that
they do not need any changes, they are not holding card->controls_rwsem
when they call snd_soc_card_get_kcontrol().
Direct callers of snd_soc_card_get_kcontrol():
fsl/fsl_spdif.c: fsl_spdif_dai_probe() - DAI probe function
fsl/fsl_micfil.c: voice_detected_fn() - IRQ handler
Indirect callers via soc_component_notify_control():
codecs/cs42l43: cs42l43_mic_shutter() - IRQ handler
codecs/cs42l43: cs42l43_spk_shutter() - IRQ handler
codecs/ak4118.c: ak4118_irq_handler() - IRQ handler
codecs/wm_adsp.c: wm_adsp_write_ctl() - not currently used
Indirect callers via snd_soc_limit_volume():
qcom/sc8280xp.c: sc8280xp_snd_init() - DAIlink init function
ti/rx51.c: rx51_aic34_init() - DAI init function
I don't have hardware to test the fsl/*, qcom/sc828xp.c, ti/rx51.c
and ak4118.c changes.
Backport note:
The fsl/, qcom/, cs35l45, cs35l56 and cs42l43 callers were added
since the Fixes commit so won't all be present on older kernels.
Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Fixes: 209c6cdfd283 ("ASoC: soc-card: move snd_soc_card_get_kcontrol() to soc-card")
Link: https://lore.kernel.org/r/20240221123710.690224-1-rf@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2024-02-21 12:37:10 +00:00
/* must be held read or write */
lockdep_assert_held ( & card - > controls_rwsem ) ;
2020-05-28 10:47:56 +09:00
if ( unlikely ( ! name ) )
return NULL ;
list_for_each_entry ( kctl , & card - > controls , list )
if ( ! strncmp ( kctl - > id . name , name , sizeof ( kctl - > id . name ) ) )
return kctl ;
return NULL ;
}
ASoC: soc-card: Fix missing locking in snd_soc_card_get_kcontrol()
snd_soc_card_get_kcontrol() must be holding a read lock on
card->controls_rwsem while walking the controls list.
Compare with snd_ctl_find_numid().
The existing function is renamed snd_soc_card_get_kcontrol_locked()
so that it can be called from contexts that are already holding
card->controls_rwsem (for example, control get/put functions).
There are few direct or indirect callers of
snd_soc_card_get_kcontrol(), and most are safe. Three require
changes, which have been included in this patch:
codecs/cs35l45.c:
cs35l45_activate_ctl() is called from a control put() function so
is changed to call snd_soc_card_get_kcontrol_locked().
codecs/cs35l56.c:
cs35l56_sync_asp1_mixer_widgets_with_firmware() is called from
control get()/put() functions so is changed to call
snd_soc_card_get_kcontrol_locked().
fsl/fsl_xcvr.c:
fsl_xcvr_activate_ctl() is called from three places, one of which
already holds card->controls_rwsem:
1. fsl_xcvr_mode_put(), a control put function, which will
already be holding card->controls_rwsem.
2. fsl_xcvr_startup(), a DAI startup function.
3. fsl_xcvr_shutdown(), a DAI shutdown function.
To fix this, fsl_xcvr_activate_ctl() has been changed to call
snd_soc_card_get_kcontrol_locked() so that it is safe to call
directly from fsl_xcvr_mode_put().
The fsl_xcvr_startup() and fsl_xcvr_shutdown() functions have been
changed to take a read lock on card->controls_rsem() around calls
to fsl_xcvr_activate_ctl(). While this is not very elegant, it
keeps the change small, to avoid this patch creating a large
collateral churn in fsl/fsl_xcvr.c.
Analysis of other callers of snd_soc_card_get_kcontrol() is that
they do not need any changes, they are not holding card->controls_rwsem
when they call snd_soc_card_get_kcontrol().
Direct callers of snd_soc_card_get_kcontrol():
fsl/fsl_spdif.c: fsl_spdif_dai_probe() - DAI probe function
fsl/fsl_micfil.c: voice_detected_fn() - IRQ handler
Indirect callers via soc_component_notify_control():
codecs/cs42l43: cs42l43_mic_shutter() - IRQ handler
codecs/cs42l43: cs42l43_spk_shutter() - IRQ handler
codecs/ak4118.c: ak4118_irq_handler() - IRQ handler
codecs/wm_adsp.c: wm_adsp_write_ctl() - not currently used
Indirect callers via snd_soc_limit_volume():
qcom/sc8280xp.c: sc8280xp_snd_init() - DAIlink init function
ti/rx51.c: rx51_aic34_init() - DAI init function
I don't have hardware to test the fsl/*, qcom/sc828xp.c, ti/rx51.c
and ak4118.c changes.
Backport note:
The fsl/, qcom/, cs35l45, cs35l56 and cs42l43 callers were added
since the Fixes commit so won't all be present on older kernels.
Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Fixes: 209c6cdfd283 ("ASoC: soc-card: move snd_soc_card_get_kcontrol() to soc-card")
Link: https://lore.kernel.org/r/20240221123710.690224-1-rf@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2024-02-21 12:37:10 +00:00
EXPORT_SYMBOL_GPL ( snd_soc_card_get_kcontrol_locked ) ;
struct snd_kcontrol * snd_soc_card_get_kcontrol ( struct snd_soc_card * soc_card ,
const char * name )
{
struct snd_card * card = soc_card - > snd_card ;
struct snd_kcontrol * kctl ;
down_read ( & card - > controls_rwsem ) ;
kctl = snd_soc_card_get_kcontrol_locked ( soc_card , name ) ;
up_read ( & card - > controls_rwsem ) ;
return kctl ;
}
2020-05-28 10:47:56 +09:00
EXPORT_SYMBOL_GPL ( snd_soc_card_get_kcontrol ) ;
2020-05-28 10:48:03 +09:00
2022-04-08 13:11:14 +09:00
static int jack_new ( struct snd_soc_card * card , const char * id , int type ,
struct snd_soc_jack * jack , bool initial_kctl )
{
mutex_init ( & jack - > mutex ) ;
jack - > card = card ;
INIT_LIST_HEAD ( & jack - > pins ) ;
INIT_LIST_HEAD ( & jack - > jack_zones ) ;
BLOCKING_INIT_NOTIFIER_HEAD ( & jack - > notifier ) ;
return snd_jack_new ( card - > snd_card , id , type , & jack - > jack , initial_kctl , false ) ;
}
2020-05-28 10:48:03 +09:00
/**
2022-04-08 13:11:14 +09:00
* snd_soc_card_jack_new - Create a new jack without pins
* @ card : ASoC card
* @ id : an identifying string for this jack
* @ type : a bitmask of enum snd_jack_type values that can be detected by
* this jack
* @ jack : structure to use for the jack
*
* Creates a new jack object without pins . If adding pins later ,
* snd_soc_card_jack_new_pins ( ) should be used instead with 0 as num_pins
* argument .
*
* Returns zero if successful , or a negative error code on failure .
* On success jack will be initialised .
*/
int snd_soc_card_jack_new ( struct snd_soc_card * card , const char * id , int type ,
struct snd_soc_jack * jack )
{
return soc_card_ret ( card , jack_new ( card , id , type , jack , true ) ) ;
}
EXPORT_SYMBOL_GPL ( snd_soc_card_jack_new ) ;
/**
* snd_soc_card_jack_new_pins - Create a new jack with pins
2020-05-28 10:48:03 +09:00
* @ card : ASoC card
* @ id : an identifying string for this jack
* @ type : a bitmask of enum snd_jack_type values that can be detected by
* this jack
* @ jack : structure to use for the jack
* @ pins : Array of jack pins to be added to the jack or NULL
* @ num_pins : Number of elements in the @ pins array
*
2022-04-08 13:11:14 +09:00
* Creates a new jack object with pins . If not adding pins ,
* snd_soc_card_jack_new ( ) should be used instead .
2020-05-28 10:48:03 +09:00
*
* Returns zero if successful , or a negative error code on failure .
* On success jack will be initialised .
*/
2022-04-08 13:11:14 +09:00
int snd_soc_card_jack_new_pins ( struct snd_soc_card * card , const char * id ,
int type , struct snd_soc_jack * jack ,
struct snd_soc_jack_pin * pins ,
unsigned int num_pins )
2020-05-28 10:48:03 +09:00
{
int ret ;
2022-04-08 13:11:14 +09:00
ret = jack_new ( card , id , type , jack , false ) ;
2020-05-28 10:48:03 +09:00
if ( ret )
goto end ;
if ( num_pins )
ret = snd_soc_jack_add_pins ( jack , num_pins , pins ) ;
end :
return soc_card_ret ( card , ret ) ;
}
2022-04-08 13:11:14 +09:00
EXPORT_SYMBOL_GPL ( snd_soc_card_jack_new_pins ) ;
2020-05-28 10:48:39 +09:00
int snd_soc_card_suspend_pre ( struct snd_soc_card * card )
{
int ret = 0 ;
if ( card - > suspend_pre )
ret = card - > suspend_pre ( card ) ;
return soc_card_ret ( card , ret ) ;
}
2020-05-28 10:48:48 +09:00
int snd_soc_card_suspend_post ( struct snd_soc_card * card )
{
int ret = 0 ;
if ( card - > suspend_post )
ret = card - > suspend_post ( card ) ;
return soc_card_ret ( card , ret ) ;
}
2020-05-28 10:48:55 +09:00
int snd_soc_card_resume_pre ( struct snd_soc_card * card )
{
int ret = 0 ;
if ( card - > resume_pre )
ret = card - > resume_pre ( card ) ;
return soc_card_ret ( card , ret ) ;
}
2020-05-28 10:49:02 +09:00
int snd_soc_card_resume_post ( struct snd_soc_card * card )
{
int ret = 0 ;
if ( card - > resume_post )
ret = card - > resume_post ( card ) ;
return soc_card_ret ( card , ret ) ;
}
2020-05-28 10:49:11 +09:00
int snd_soc_card_probe ( struct snd_soc_card * card )
{
if ( card - > probe ) {
int ret = card - > probe ( card ) ;
if ( ret < 0 )
return soc_card_ret ( card , ret ) ;
/*
* It has " card->probe " and " card->late_probe " callbacks .
* So , set " probed " flag here , because it needs to care
* about " late_probe " .
*
* see
* snd_soc_bind_card ( )
* snd_soc_card_late_probe ( )
*/
card - > probed = 1 ;
}
return 0 ;
}
2020-05-28 10:49:20 +09:00
int snd_soc_card_late_probe ( struct snd_soc_card * card )
{
if ( card - > late_probe ) {
int ret = card - > late_probe ( card ) ;
if ( ret < 0 )
return soc_card_ret ( card , ret ) ;
}
/*
* It has " card->probe " and " card->late_probe " callbacks ,
* and " late_probe " callback is called after " probe " .
* This means , we can set " card->probed " flag afer " late_probe "
* for all cases .
*
* see
* snd_soc_bind_card ( )
* snd_soc_card_probe ( )
*/
card - > probed = 1 ;
return 0 ;
}
2020-05-28 10:49:26 +09:00
2022-06-06 21:19:09 +02:00
void snd_soc_card_fixup_controls ( struct snd_soc_card * card )
{
if ( card - > fixup_controls )
card - > fixup_controls ( card ) ;
}
2020-05-28 10:49:26 +09:00
int snd_soc_card_remove ( struct snd_soc_card * card )
{
int ret = 0 ;
if ( card - > probed & &
card - > remove )
ret = card - > remove ( card ) ;
card - > probed = 0 ;
return soc_card_ret ( card , ret ) ;
}
2020-05-28 10:49:35 +09:00
int snd_soc_card_set_bias_level ( struct snd_soc_card * card ,
struct snd_soc_dapm_context * dapm ,
enum snd_soc_bias_level level )
{
int ret = 0 ;
if ( card & & card - > set_bias_level )
ret = card - > set_bias_level ( card , dapm , level ) ;
return soc_card_ret ( card , ret ) ;
}
2020-05-28 10:50:35 +09:00
int snd_soc_card_set_bias_level_post ( struct snd_soc_card * card ,
struct snd_soc_dapm_context * dapm ,
enum snd_soc_bias_level level )
{
int ret = 0 ;
if ( card & & card - > set_bias_level_post )
ret = card - > set_bias_level_post ( card , dapm , level ) ;
return soc_card_ret ( card , ret ) ;
}
2020-05-28 10:50:41 +09:00
int snd_soc_card_add_dai_link ( struct snd_soc_card * card ,
struct snd_soc_dai_link * dai_link )
{
int ret = 0 ;
if ( card - > add_dai_link )
ret = card - > add_dai_link ( card , dai_link ) ;
return soc_card_ret ( card , ret ) ;
}
EXPORT_SYMBOL_GPL ( snd_soc_card_add_dai_link ) ;
2020-05-28 10:50:46 +09:00
void snd_soc_card_remove_dai_link ( struct snd_soc_card * card ,
struct snd_soc_dai_link * dai_link )
{
if ( card - > remove_dai_link )
card - > remove_dai_link ( card , dai_link ) ;
}
EXPORT_SYMBOL_GPL ( snd_soc_card_remove_dai_link ) ;