Skip to content

Commit

Permalink
ASoC: Enhance default WM8958 microphone detection
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Mark Brown committed Nov 29, 2011
1 parent 500fa30 commit b00adf7
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 11 deletions.
120 changes: 109 additions & 11 deletions sound/soc/codecs/wm8994.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
}
}

/**
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions sound/soc/codecs/wm8994.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit b00adf7

Please sign in to comment.