diff options
Diffstat (limited to 'sound/soc/codecs/sta32x.c')
-rw-r--r-- | sound/soc/codecs/sta32x.c | 50 |
1 files changed, 49 insertions, 1 deletions
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 97091e3b9a0b..3b0deafd766b 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
28 | #include <linux/regulator/consumer.h> | 28 | #include <linux/regulator/consumer.h> |
29 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <linux/workqueue.h> | ||
30 | #include <sound/core.h> | 31 | #include <sound/core.h> |
31 | #include <sound/pcm.h> | 32 | #include <sound/pcm.h> |
32 | #include <sound/pcm_params.h> | 33 | #include <sound/pcm_params.h> |
@@ -80,6 +81,8 @@ struct sta32x_priv { | |||
80 | unsigned int format; | 81 | unsigned int format; |
81 | 82 | ||
82 | u32 coef_shadow[STA32X_COEF_COUNT]; | 83 | u32 coef_shadow[STA32X_COEF_COUNT]; |
84 | struct delayed_work watchdog_work; | ||
85 | int shutdown; | ||
83 | }; | 86 | }; |
84 | 87 | ||
85 | static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12700, 50, 1); | 88 | static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12700, 50, 1); |
@@ -304,6 +307,46 @@ int sta32x_cache_sync(struct snd_soc_codec *codec) | |||
304 | return rc; | 307 | return rc; |
305 | } | 308 | } |
306 | 309 | ||
310 | /* work around ESD issue where sta32x resets and loses all configuration */ | ||
311 | static void sta32x_watchdog(struct work_struct *work) | ||
312 | { | ||
313 | struct sta32x_priv *sta32x = container_of(work, struct sta32x_priv, | ||
314 | watchdog_work.work); | ||
315 | struct snd_soc_codec *codec = sta32x->codec; | ||
316 | unsigned int confa, confa_cached; | ||
317 | |||
318 | /* check if sta32x has reset itself */ | ||
319 | confa_cached = snd_soc_read(codec, STA32X_CONFA); | ||
320 | codec->cache_bypass = 1; | ||
321 | confa = snd_soc_read(codec, STA32X_CONFA); | ||
322 | codec->cache_bypass = 0; | ||
323 | if (confa != confa_cached) { | ||
324 | codec->cache_sync = 1; | ||
325 | sta32x_cache_sync(codec); | ||
326 | } | ||
327 | |||
328 | if (!sta32x->shutdown) | ||
329 | schedule_delayed_work(&sta32x->watchdog_work, | ||
330 | round_jiffies_relative(HZ)); | ||
331 | } | ||
332 | |||
333 | static void sta32x_watchdog_start(struct sta32x_priv *sta32x) | ||
334 | { | ||
335 | if (sta32x->pdata->needs_esd_watchdog) { | ||
336 | sta32x->shutdown = 0; | ||
337 | schedule_delayed_work(&sta32x->watchdog_work, | ||
338 | round_jiffies_relative(HZ)); | ||
339 | } | ||
340 | } | ||
341 | |||
342 | static void sta32x_watchdog_stop(struct sta32x_priv *sta32x) | ||
343 | { | ||
344 | if (sta32x->pdata->needs_esd_watchdog) { | ||
345 | sta32x->shutdown = 1; | ||
346 | cancel_delayed_work_sync(&sta32x->watchdog_work); | ||
347 | } | ||
348 | } | ||
349 | |||
307 | #define SINGLE_COEF(xname, index) \ | 350 | #define SINGLE_COEF(xname, index) \ |
308 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | 351 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ |
309 | .info = sta32x_coefficient_info, \ | 352 | .info = sta32x_coefficient_info, \ |
@@ -714,6 +757,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec, | |||
714 | } | 757 | } |
715 | 758 | ||
716 | sta32x_cache_sync(codec); | 759 | sta32x_cache_sync(codec); |
760 | sta32x_watchdog_start(sta32x); | ||
717 | } | 761 | } |
718 | 762 | ||
719 | /* Power up to mute */ | 763 | /* Power up to mute */ |
@@ -730,7 +774,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec, | |||
730 | STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, | 774 | STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, |
731 | STA32X_CONFF_PWDN); | 775 | STA32X_CONFF_PWDN); |
732 | msleep(300); | 776 | msleep(300); |
733 | 777 | sta32x_watchdog_stop(sta32x); | |
734 | regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), | 778 | regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), |
735 | sta32x->supplies); | 779 | sta32x->supplies); |
736 | break; | 780 | break; |
@@ -863,6 +907,9 @@ static int sta32x_probe(struct snd_soc_codec *codec) | |||
863 | sta32x->coef_shadow[60] = 0x400000; | 907 | sta32x->coef_shadow[60] = 0x400000; |
864 | sta32x->coef_shadow[61] = 0x400000; | 908 | sta32x->coef_shadow[61] = 0x400000; |
865 | 909 | ||
910 | if (sta32x->pdata->needs_esd_watchdog) | ||
911 | INIT_DELAYED_WORK(&sta32x->watchdog_work, sta32x_watchdog); | ||
912 | |||
866 | sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 913 | sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
867 | /* Bias level configuration will have done an extra enable */ | 914 | /* Bias level configuration will have done an extra enable */ |
868 | regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); | 915 | regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); |
@@ -879,6 +926,7 @@ static int sta32x_remove(struct snd_soc_codec *codec) | |||
879 | { | 926 | { |
880 | struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); | 927 | struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); |
881 | 928 | ||
929 | sta32x_watchdog_stop(sta32x); | ||
882 | sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF); | 930 | sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF); |
883 | regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); | 931 | regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); |
884 | regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); | 932 | regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); |