aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-08-12 22:57:18 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-11-29 15:22:00 -0500
commitb00adf76a6fa492c39f8225fc42debc01bbbdc1d (patch)
treeab0f53480c9a0a4dc57a61ce15bade43f5748fd7 /sound
parent500fa30ed5795a1d8e8539d0cd81f73b34f831a3 (diff)
ASoC: Enhance default WM8958 microphone detection
Actively manage the detection rate for microphones with WM8958, providing improved power consumption and maximising the benefit from the hardware debounce. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/wm8994.c120
-rw-r--r--sound/soc/codecs/wm8994.h2
2 files changed, 111 insertions, 11 deletions
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 207bccd156f1..027bf683efce 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -53,6 +53,56 @@ static int wm8994_retune_mobile_base[] = {
53 WM8994_AIF2_EQ_GAINS_1, 53 WM8994_AIF2_EQ_GAINS_1,
54}; 54};
55 55
56static void wm8958_default_micdet(u16 status, void *data);
57
58static const struct {
59 int sysclk;
60 bool idle;
61 int start;
62 int rate;
63} wm8958_micd_rates[] = {
64 { 32768, true, 1, 4 },
65 { 32768, false, 1, 1 },
66 { 44100 * 256, true, 7, 6 },
67 { 44100 * 256, false, 7, 6 },
68};
69
70static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
71{
72 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
73 int best, i, sysclk, val;
74 bool idle;
75
76 if (wm8994->jack_cb != wm8958_default_micdet)
77 return;
78
79 idle = !wm8994->jack_mic;
80
81 sysclk = snd_soc_read(codec, WM8994_CLOCKING_1);
82 if (sysclk & WM8994_SYSCLK_SRC)
83 sysclk = wm8994->aifclk[1];
84 else
85 sysclk = wm8994->aifclk[0];
86
87 best = 0;
88 for (i = 0; i < ARRAY_SIZE(wm8958_micd_rates); i++) {
89 if (wm8958_micd_rates[i].idle != idle)
90 continue;
91 if (abs(wm8958_micd_rates[i].sysclk - sysclk) <
92 abs(wm8958_micd_rates[best].sysclk - sysclk))
93 best = i;
94 else if (wm8958_micd_rates[best].idle != idle)
95 best = i;
96 }
97
98 val = wm8958_micd_rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT
99 | wm8958_micd_rates[best].rate << WM8958_MICD_RATE_SHIFT;
100
101 snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
102 WM8958_MICD_BIAS_STARTTIME_MASK |
103 WM8958_MICD_RATE_MASK, val);
104}
105
56static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) 106static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
57{ 107{
58 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); 108 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
@@ -221,8 +271,10 @@ static int configure_clock(struct snd_soc_codec *codec)
221 */ 271 */
222 272
223 /* If they're equal it doesn't matter which is used */ 273 /* If they're equal it doesn't matter which is used */
224 if (wm8994->aifclk[0] == wm8994->aifclk[1]) 274 if (wm8994->aifclk[0] == wm8994->aifclk[1]) {
275 wm8958_micd_set_rate(codec);
225 return 0; 276 return 0;
277 }
226 278
227 if (wm8994->aifclk[0] < wm8994->aifclk[1]) 279 if (wm8994->aifclk[0] < wm8994->aifclk[1])
228 new = WM8994_SYSCLK_SRC; 280 new = WM8994_SYSCLK_SRC;
@@ -236,6 +288,8 @@ static int configure_clock(struct snd_soc_codec *codec)
236 288
237 snd_soc_dapm_sync(&codec->dapm); 289 snd_soc_dapm_sync(&codec->dapm);
238 290
291 wm8958_micd_set_rate(codec);
292
239 return 0; 293 return 0;
240} 294}
241 295
@@ -2987,21 +3041,56 @@ static void wm8958_default_micdet(u16 status, void *data)
2987{ 3041{
2988 struct snd_soc_codec *codec = data; 3042 struct snd_soc_codec *codec = data;
2989 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); 3043 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
2990 int report = 0;
2991 3044
2992 /* If nothing present then clear our statuses */ 3045 /* If nothing present then clear our statuses */
2993 if (!(status & WM8958_MICD_STS)) 3046 if (!(status & WM8958_MICD_STS)) {
2994 goto done; 3047 dev_dbg(codec->dev, "Detected open circuit\n");
3048 wm8994->jack_mic = false;
3049 wm8994->detecting = true;
3050
3051 wm8958_micd_set_rate(codec);
2995 3052
2996 report = SND_JACK_MICROPHONE; 3053 snd_soc_jack_report(wm8994->micdet[0].jack, 0,
3054 SND_JACK_BTN_0 | SND_JACK_HEADSET);
3055
3056 return;
3057 }
2997 3058
2998 /* Everything else is buttons; just assign slots */ 3059 /* If the measurement is showing a high impedence we've got a
2999 if (status & 0x1c) 3060 * microphone.
3000 report |= SND_JACK_BTN_0; 3061 */
3062 if (wm8994->detecting && (status & 0x600)) {
3063 dev_dbg(codec->dev, "Detected microphone\n");
3064
3065 wm8994->detecting = false;
3066 wm8994->jack_mic = true;
3067
3068 wm8958_micd_set_rate(codec);
3069
3070 snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET,
3071 SND_JACK_HEADSET);
3072 }
3001 3073
3002done: 3074
3003 snd_soc_jack_report(wm8994->micdet[0].jack, report, 3075 if (wm8994->detecting && status & 0x4) {
3004 SND_JACK_BTN_0 | SND_JACK_MICROPHONE); 3076 dev_dbg(codec->dev, "Detected headphone\n");
3077 wm8994->detecting = false;
3078
3079 wm8958_micd_set_rate(codec);
3080
3081 snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE,
3082 SND_JACK_HEADSET);
3083 }
3084
3085 /* Report short circuit as a button */
3086 if (wm8994->jack_mic) {
3087 if (status & 0x4)
3088 snd_soc_jack_report(wm8994->micdet[0].jack,
3089 SND_JACK_BTN_0, SND_JACK_BTN_0);
3090 else
3091 snd_soc_jack_report(wm8994->micdet[0].jack,
3092 0, SND_JACK_BTN_0);
3093 }
3005} 3094}
3006 3095
3007/** 3096/**
@@ -3047,6 +3136,15 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
3047 wm8994->jack_cb = cb; 3136 wm8994->jack_cb = cb;
3048 wm8994->jack_cb_data = cb_data; 3137 wm8994->jack_cb_data = cb_data;
3049 3138
3139 wm8994->detecting = true;
3140 wm8994->jack_mic = false;
3141
3142 wm8958_micd_set_rate(codec);
3143
3144 /* Detect microphones and short circuits */
3145 snd_soc_update_bits(codec, WM8958_MIC_DETECT_2,
3146 WM8958_MICD_LVL_SEL_MASK, 0x41);
3147
3050 snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, 3148 snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
3051 WM8958_MICD_ENA, WM8958_MICD_ENA); 3149 WM8958_MICD_ENA, WM8958_MICD_ENA);
3052 } else { 3150 } else {
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index f4f1355efc82..1087425cbac0 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -126,6 +126,8 @@ struct wm8994_priv {
126 struct soc_enum enh_eq_enum; 126 struct soc_enum enh_eq_enum;
127 127
128 struct wm8994_micdet micdet[2]; 128 struct wm8994_micdet micdet[2];
129 bool detecting;
130 bool jack_mic;
129 131
130 wm8958_micdet_cb jack_cb; 132 wm8958_micdet_cb jack_cb;
131 void *jack_cb_data; 133 void *jack_cb_data;