summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/wm8994.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-08-13 11:57:18 +0900
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-11-29 20:22:00 +0000
commitb00adf76a6fa492c39f8225fc42debc01bbbdc1d (patch)
treeab0f53480c9a0a4dc57a61ce15bade43f5748fd7 /sound/soc/codecs/wm8994.c
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/soc/codecs/wm8994.c')
-rw-r--r--sound/soc/codecs/wm8994.c120
1 files changed, 109 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[] = {
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 {