@ -27,6 +27,7 @@
# include <linux/platform_device.h>
# include <linux/regulator/consumer.h>
# include <linux/slab.h>
# include <linux/workqueue.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
@ -80,6 +81,8 @@ struct sta32x_priv {
unsigned int format ;
u32 coef_shadow [ STA32X_COEF_COUNT ] ;
struct delayed_work watchdog_work ;
int shutdown ;
} ;
static const DECLARE_TLV_DB_SCALE ( mvol_tlv , - 12700 , 50 , 1 ) ;
@ -304,6 +307,46 @@ int sta32x_cache_sync(struct snd_soc_codec *codec)
return rc ;
}
/* work around ESD issue where sta32x resets and loses all configuration */
static void sta32x_watchdog ( struct work_struct * work )
{
struct sta32x_priv * sta32x = container_of ( work , struct sta32x_priv ,
watchdog_work . work ) ;
struct snd_soc_codec * codec = sta32x - > codec ;
unsigned int confa , confa_cached ;
/* check if sta32x has reset itself */
confa_cached = snd_soc_read ( codec , STA32X_CONFA ) ;
codec - > cache_bypass = 1 ;
confa = snd_soc_read ( codec , STA32X_CONFA ) ;
codec - > cache_bypass = 0 ;
if ( confa ! = confa_cached ) {
codec - > cache_sync = 1 ;
sta32x_cache_sync ( codec ) ;
}
if ( ! sta32x - > shutdown )
schedule_delayed_work ( & sta32x - > watchdog_work ,
round_jiffies_relative ( HZ ) ) ;
}
static void sta32x_watchdog_start ( struct sta32x_priv * sta32x )
{
if ( sta32x - > pdata - > needs_esd_watchdog ) {
sta32x - > shutdown = 0 ;
schedule_delayed_work ( & sta32x - > watchdog_work ,
round_jiffies_relative ( HZ ) ) ;
}
}
static void sta32x_watchdog_stop ( struct sta32x_priv * sta32x )
{
if ( sta32x - > pdata - > needs_esd_watchdog ) {
sta32x - > shutdown = 1 ;
cancel_delayed_work_sync ( & sta32x - > watchdog_work ) ;
}
}
# define SINGLE_COEF(xname, index) \
{ . iface = SNDRV_CTL_ELEM_IFACE_MIXER , . name = xname , \
. info = sta32x_coefficient_info , \
@ -714,6 +757,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
}
sta32x_cache_sync ( codec ) ;
sta32x_watchdog_start ( sta32x ) ;
}
/* Power up to mute */
@ -730,7 +774,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
STA32X_CONFF_PWDN | STA32X_CONFF_EAPD ,
STA32X_CONFF_PWDN ) ;
msleep ( 300 ) ;
sta32x_watchdog_stop ( sta32x ) ;
regulator_bulk_disable ( ARRAY_SIZE ( sta32x - > supplies ) ,
sta32x - > supplies ) ;
break ;
@ -863,6 +907,9 @@ static int sta32x_probe(struct snd_soc_codec *codec)
sta32x - > coef_shadow [ 60 ] = 0x400000 ;
sta32x - > coef_shadow [ 61 ] = 0x400000 ;
if ( sta32x - > pdata - > needs_esd_watchdog )
INIT_DELAYED_WORK ( & sta32x - > watchdog_work , sta32x_watchdog ) ;
sta32x_set_bias_level ( codec , SND_SOC_BIAS_STANDBY ) ;
/* Bias level configuration will have done an extra enable */
regulator_bulk_disable ( ARRAY_SIZE ( sta32x - > supplies ) , sta32x - > supplies ) ;
@ -879,6 +926,7 @@ static int sta32x_remove(struct snd_soc_codec *codec)
{
struct sta32x_priv * sta32x = snd_soc_codec_get_drvdata ( codec ) ;
sta32x_watchdog_stop ( sta32x ) ;
sta32x_set_bias_level ( codec , SND_SOC_BIAS_OFF ) ;
regulator_bulk_disable ( ARRAY_SIZE ( sta32x - > supplies ) , sta32x - > supplies ) ;
regulator_bulk_free ( ARRAY_SIZE ( sta32x - > supplies ) , sta32x - > supplies ) ;