Skip to content

Commit

Permalink
ASoC: Implement WM5100 accessory detection support
Browse files Browse the repository at this point in the history
The WM5100 includes an advanced, low power, accessory detect subsystem
capable of detecting both accessory presence and button presses while
the device is in an ultra low power mode. Implement initial support for
this.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
  • Loading branch information
Mark Brown committed Oct 11, 2011
1 parent 3a53d82 commit ba896ed
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 0 deletions.
162 changes: 162 additions & 0 deletions sound/soc/codecs/wm5100.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/wm5100.h>
Expand Down Expand Up @@ -68,6 +69,11 @@ struct wm5100_priv {

bool out_ena[2];

struct snd_soc_jack *jack;
bool jack_detecting;
bool jack_mic;
int jack_mode;

struct wm5100_fll fll[2];

struct wm5100_pdata pdata;
Expand Down Expand Up @@ -2113,6 +2119,159 @@ static int wm5100_dig_vu[] = {
WM5100_DAC_DIGITAL_VOLUME_6R,
};

static void wm5100_set_detect_mode(struct snd_soc_codec *codec, int the_mode)
{
struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
struct wm5100_jack_mode *mode = &wm5100->pdata.jack_modes[the_mode];

BUG_ON(the_mode >= ARRAY_SIZE(wm5100->pdata.jack_modes));

gpio_set_value_cansleep(wm5100->pdata.hp_pol, mode->hp_pol);
snd_soc_update_bits(codec, WM5100_ACCESSORY_DETECT_MODE_1,
WM5100_ACCDET_BIAS_SRC_MASK |
WM5100_ACCDET_SRC,
(mode->bias << WM5100_ACCDET_BIAS_SRC_SHIFT) |
mode->micd_src << WM5100_ACCDET_SRC_SHIFT);

wm5100->jack_mode = the_mode;

dev_dbg(codec->dev, "Set microphone polarity to %d\n",
wm5100->jack_mode);
}

static void wm5100_micd_irq(struct snd_soc_codec *codec)
{
struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
int val;

val = snd_soc_read(codec, WM5100_MIC_DETECT_3);

dev_dbg(codec->dev, "Microphone event: %x\n", val);

if (!(val & WM5100_ACCDET_VALID)) {
dev_warn(codec->dev, "Microphone detection state invalid\n");
return;
}

/* No accessory, reset everything and report removal */
if (!(val & WM5100_ACCDET_STS)) {
dev_dbg(codec->dev, "Jack removal detected\n");
wm5100->jack_mic = false;
wm5100->jack_detecting = true;
snd_soc_jack_report(wm5100->jack, 0,
SND_JACK_LINEOUT | SND_JACK_HEADSET |
SND_JACK_BTN_0);

snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
WM5100_ACCDET_RATE_MASK,
WM5100_ACCDET_RATE_MASK);
return;
}

/* If the measurement is very high we've got a microphone,
* either we just detected one or if we already reported then
* we've got a button release event.
*/
if (val & 0x400) {
if (wm5100->jack_detecting) {
dev_dbg(codec->dev, "Microphone detected\n");
wm5100->jack_mic = true;
snd_soc_jack_report(wm5100->jack,
SND_JACK_HEADSET,
SND_JACK_HEADSET | SND_JACK_BTN_0);

/* Increase poll rate to give better responsiveness
* for buttons */
snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
WM5100_ACCDET_RATE_MASK,
5 << WM5100_ACCDET_RATE_SHIFT);
} else {
dev_dbg(codec->dev, "Mic button up\n");
snd_soc_jack_report(wm5100->jack, 0, SND_JACK_BTN_0);
}

return;
}

/* If we detected a lower impedence during initial startup
* then we probably have the wrong polarity, flip it. Don't
* do this for the lowest impedences to speed up detection of
* plain headphones.
*/
if (wm5100->jack_detecting && (val & 0x3f8)) {
wm5100_set_detect_mode(codec, !wm5100->jack_mode);

return;
}

/* Don't distinguish between buttons, just report any low
* impedence as BTN_0.
*/
if (val & 0x3fc) {
if (wm5100->jack_mic) {
dev_dbg(codec->dev, "Mic button detected\n");
snd_soc_jack_report(wm5100->jack, SND_JACK_BTN_0,
SND_JACK_BTN_0);
} else if (wm5100->jack_detecting) {
dev_dbg(codec->dev, "Headphone detected\n");
snd_soc_jack_report(wm5100->jack, SND_JACK_HEADPHONE,
SND_JACK_HEADPHONE);

/* Increase the detection rate a bit for
* responsiveness.
*/
snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
WM5100_ACCDET_RATE_MASK,
7 << WM5100_ACCDET_RATE_SHIFT);
}
}
}

int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
{
struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);

if (jack) {
wm5100->jack = jack;
wm5100->jack_detecting = true;

wm5100_set_detect_mode(codec, 0);

/* Slowest detection rate, gives debounce for initial
* detection */
snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
WM5100_ACCDET_BIAS_STARTTIME_MASK |
WM5100_ACCDET_RATE_MASK,
(7 << WM5100_ACCDET_BIAS_STARTTIME_SHIFT) |
WM5100_ACCDET_RATE_MASK);

/* We need the charge pump to power MICBIAS */
snd_soc_dapm_force_enable_pin(&codec->dapm, "CP2");
snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");
snd_soc_dapm_sync(&codec->dapm);

/* We start off just enabling microphone detection - even a
* plain headphone will trigger detection.
*/
snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
WM5100_ACCDET_ENA, WM5100_ACCDET_ENA);

snd_soc_update_bits(codec, WM5100_INTERRUPT_STATUS_3_MASK,
WM5100_IM_ACCDET_EINT, 0);
} else {
snd_soc_update_bits(codec, WM5100_INTERRUPT_STATUS_3_MASK,
WM5100_IM_HPDET_EINT |
WM5100_IM_ACCDET_EINT,
WM5100_IM_HPDET_EINT |
WM5100_IM_ACCDET_EINT);
snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
WM5100_ACCDET_ENA, 0);
wm5100->jack = NULL;
}

return 0;
}

static irqreturn_t wm5100_irq(int irq, void *data)
{
struct snd_soc_codec *codec = data;
Expand Down Expand Up @@ -2144,6 +2303,9 @@ static irqreturn_t wm5100_irq(int irq, void *data)
complete(&wm5100->fll[1].lock);
}

if (irq_val & WM5100_ACCDET_EINT)
wm5100_micd_irq(codec);

irq_val = snd_soc_read(codec, WM5100_INTERRUPT_STATUS_4);
if (irq_val < 0) {
dev_err(codec->dev, "Failed to read IRQ status 4: %d\n",
Expand Down
2 changes: 2 additions & 0 deletions sound/soc/codecs/wm5100.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include <sound/soc.h>

int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);

#define WM5100_CLK_AIF1 1
#define WM5100_CLK_AIF2 2
#define WM5100_CLK_AIF3 3
Expand Down

0 comments on commit ba896ed

Please sign in to comment.