[alsa-devel] [PATCH] ASoC: Enhance default WM8958 microphone detection

Mark Brown broonie at opensource.wolfsonmicro.com
Tue Nov 29 21:21:35 CET 2011


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 at opensource.wolfsonmicro.com>
---
 sound/soc/codecs/wm8994.c |  120 ++++++++++++++++++++++++++++++++++++++++----
 sound/soc/codecs/wm8994.h |    2 +
 2 files changed, 111 insertions(+), 11 deletions(-)

diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 207bccd..027bf68 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -53,6 +53,56 @@ static int wm8994_retune_mobile_base[] = {
 	WM8994_AIF2_EQ_GAINS_1,
 };
 
+static void wm8958_default_micdet(u16 status, void *data);
+
+static const struct {
+	int sysclk;
+	bool idle;
+	int start;
+	int rate;
+} wm8958_micd_rates[] = {
+	{ 32768,       true,  1, 4 },
+	{ 32768,       false, 1, 1 },
+	{ 44100 * 256, true,  7, 6 },
+	{ 44100 * 256, false, 7, 6 },
+};
+
+static void wm8958_micd_set_rate(struct snd_soc_codec *codec)
+{
+	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+	int best, i, sysclk, val;
+	bool idle;
+
+	if (wm8994->jack_cb != wm8958_default_micdet)
+		return;
+
+	idle = !wm8994->jack_mic;
+
+	sysclk = snd_soc_read(codec, WM8994_CLOCKING_1);
+	if (sysclk & WM8994_SYSCLK_SRC)
+		sysclk = wm8994->aifclk[1];
+	else
+		sysclk = wm8994->aifclk[0];
+
+	best = 0;
+	for (i = 0; i < ARRAY_SIZE(wm8958_micd_rates); i++) {
+		if (wm8958_micd_rates[i].idle != idle)
+			continue;
+		if (abs(wm8958_micd_rates[i].sysclk - sysclk) <
+		    abs(wm8958_micd_rates[best].sysclk - sysclk))
+			best = i;
+		else if (wm8958_micd_rates[best].idle != idle)
+			best = i;
+	}
+
+	val = wm8958_micd_rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT
+		| wm8958_micd_rates[best].rate << WM8958_MICD_RATE_SHIFT;
+
+	snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
+			    WM8958_MICD_BIAS_STARTTIME_MASK |
+			    WM8958_MICD_RATE_MASK, val);
+}
+
 static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
 {
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
@@ -221,8 +271,10 @@ static int configure_clock(struct snd_soc_codec *codec)
 	 */
 
 	/* If they're equal it doesn't matter which is used */
-	if (wm8994->aifclk[0] == wm8994->aifclk[1])
+	if (wm8994->aifclk[0] == wm8994->aifclk[1]) {
+		wm8958_micd_set_rate(codec);
 		return 0;
+	}
 
 	if (wm8994->aifclk[0] < wm8994->aifclk[1])
 		new = WM8994_SYSCLK_SRC;
@@ -236,6 +288,8 @@ static int configure_clock(struct snd_soc_codec *codec)
 
 	snd_soc_dapm_sync(&codec->dapm);
 
+	wm8958_micd_set_rate(codec);
+
 	return 0;
 }
 
@@ -2987,21 +3041,56 @@ static void wm8958_default_micdet(u16 status, void *data)
 {
 	struct snd_soc_codec *codec = data;
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-	int report = 0;
 
 	/* If nothing present then clear our statuses */
-	if (!(status & WM8958_MICD_STS))
-		goto done;
+	if (!(status & WM8958_MICD_STS)) {
+		dev_dbg(codec->dev, "Detected open circuit\n");
+		wm8994->jack_mic = false;
+		wm8994->detecting = true;
+
+		wm8958_micd_set_rate(codec);
 
-	report = SND_JACK_MICROPHONE;
+		snd_soc_jack_report(wm8994->micdet[0].jack, 0,
+				    SND_JACK_BTN_0 | SND_JACK_HEADSET);
+
+		return;
+	}
 
-	/* Everything else is buttons; just assign slots */
-	if (status & 0x1c)
-		report |= SND_JACK_BTN_0;
+	/* If the measurement is showing a high impedence we've got a
+	 * microphone.
+	 */
+	if (wm8994->detecting && (status & 0x600)) {
+		dev_dbg(codec->dev, "Detected microphone\n");
+
+		wm8994->detecting = false;
+		wm8994->jack_mic = true;
+
+		wm8958_micd_set_rate(codec);
+
+		snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET,
+				    SND_JACK_HEADSET);
+	}
 
-done:
-	snd_soc_jack_report(wm8994->micdet[0].jack, report,
-			    SND_JACK_BTN_0 | SND_JACK_MICROPHONE);
+
+	if (wm8994->detecting && status & 0x4) {
+		dev_dbg(codec->dev, "Detected headphone\n");
+		wm8994->detecting = false;
+
+		wm8958_micd_set_rate(codec);
+
+		snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE,
+				    SND_JACK_HEADSET);
+	}
+
+	/* Report short circuit as a button */
+	if (wm8994->jack_mic) {
+		if (status & 0x4)
+			snd_soc_jack_report(wm8994->micdet[0].jack,
+					    SND_JACK_BTN_0, SND_JACK_BTN_0);
+		else
+			snd_soc_jack_report(wm8994->micdet[0].jack,
+					    0, SND_JACK_BTN_0);
+	}
 }
 
 /**
@@ -3047,6 +3136,15 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 		wm8994->jack_cb = cb;
 		wm8994->jack_cb_data = cb_data;
 
+		wm8994->detecting = true;
+		wm8994->jack_mic = false;
+
+		wm8958_micd_set_rate(codec);
+
+		/* Detect microphones and short circuits */
+		snd_soc_update_bits(codec, WM8958_MIC_DETECT_2,
+				    WM8958_MICD_LVL_SEL_MASK, 0x41);
+
 		snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
 				    WM8958_MICD_ENA, WM8958_MICD_ENA);
 	} else {
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index f4f1355..1087425 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -126,6 +126,8 @@ struct wm8994_priv {
 	struct soc_enum enh_eq_enum;
 
 	struct wm8994_micdet micdet[2];
+	bool detecting;
+	bool jack_mic;
 
 	wm8958_micdet_cb jack_cb;
 	void *jack_cb_data;
-- 
1.7.7.3



More information about the Alsa-devel mailing list