Skip to content

Commit

Permalink
ASoC: Implement interrupt driven microphone detection for WM8903
Browse files Browse the repository at this point in the history
Support use of the WM8903 IRQ for reporting of microphone presence
and short detection.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
  • Loading branch information
Mark Brown committed Mar 16, 2010
1 parent 8abd16a commit 7245387
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 3 deletions.
2 changes: 2 additions & 0 deletions include/sound/wm8903.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ struct wm8903_platform_data {
*/
u16 micdet_cfg;

int micdet_delay; /* Delay after microphone detection (ms) */

u32 gpio_cfg[5]; /* Default register values for GPIO pin mux */
};

Expand Down
106 changes: 103 additions & 3 deletions sound/soc/codecs/wm8903.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
Expand Down Expand Up @@ -223,6 +224,12 @@ struct wm8903_priv {

struct completion wseq;

struct snd_soc_jack *mic_jack;
int mic_det;
int mic_short;
int mic_last_report;
int mic_delay;

struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream;
};
Expand Down Expand Up @@ -1437,19 +1444,112 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
return 0;
}

/**
* wm8903_mic_detect - Enable microphone detection via the WM8903 IRQ
*
* @codec: WM8903 codec
* @jack: jack to report detection events on
* @det: value to report for presence detection
* @shrt: value to report for short detection
*
* Enable microphone detection via IRQ on the WM8903. If GPIOs are
* being used to bring out signals to the processor then only platform
* data configuration is needed for WM8903 and processor GPIOs should
* be configured using snd_soc_jack_add_gpios() instead.
*
* The current threasholds for detection should be configured using
* micdet_cfg in the platform data. Using this function will force on
* the microphone bias for the device.
*/
int wm8903_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
int det, int shrt)
{
struct wm8903_priv *wm8903 = codec->private_data;
int irq_mask = 0;

dev_dbg(codec->dev, "Enabling microphone detection: %x %x\n",
det, shrt);

/* Store the configuration */
wm8903->mic_jack = jack;
wm8903->mic_det = det;
wm8903->mic_short = shrt;

/* Enable interrupts we've got a report configured for */
if (det)
irq_mask &= ~WM8903_MICDET_EINT;
if (shrt)
irq_mask &= ~WM8903_MICSHRT_EINT;

snd_soc_update_bits(codec, WM8903_INTERRUPT_STATUS_1_MASK,
WM8903_MICDET_EINT | WM8903_MICSHRT_EINT,
irq_mask);

/* Enable mic detection, this may not have been set through
* platform data (eg, if the defaults are OK). */
snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0,
WM8903_WSEQ_ENA, WM8903_WSEQ_ENA);
snd_soc_update_bits(codec, WM8903_MIC_BIAS_CONTROL_0,
WM8903_MICDET_ENA, WM8903_MICDET_ENA);

/* Force the microphone bias on; this will trigger an initial
* detection. */
snd_soc_dapm_force_enable_pin(codec, "Mic Bias");

return 0;
}
EXPORT_SYMBOL_GPL(wm8903_mic_detect);

static irqreturn_t wm8903_irq(int irq, void *data)
{
struct wm8903_priv *wm8903 = data;
struct snd_soc_codec *codec = &wm8903->codec;
int reg;
int mic_report;
int int_pol;
int int_val = 0;
int mask = ~snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1_MASK);

reg = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1);
int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask;

if (reg & WM8903_WSEQ_BUSY_EINT) {
if (int_val & WM8903_WSEQ_BUSY_EINT) {
dev_dbg(codec->dev, "Write sequencer done\n");
complete(&wm8903->wseq);
}

/*
* The rest is microphone jack detection. We need to manually
* invert the polarity of the interrupt after each event - to
* simplify the code keep track of the last state we reported
* and just invert the relevant bits in both the report and
* the polarity register.
*/
mic_report = wm8903->mic_last_report;
int_pol = snd_soc_read(codec, WM8903_INTERRUPT_POLARITY_1);

if (int_val & WM8903_MICSHRT_EINT) {
dev_dbg(codec->dev, "Microphone short (pol=%x)\n", int_pol);

mic_report ^= wm8903->mic_short;
int_pol ^= WM8903_MICSHRT_INV;
}

if (int_val & WM8903_MICDET_EINT) {
dev_dbg(codec->dev, "Microphone detect (pol=%x)\n", int_pol);

mic_report ^= wm8903->mic_det;
int_pol ^= WM8903_MICDET_INV;

msleep(wm8903->mic_delay);
}

snd_soc_update_bits(codec, WM8903_INTERRUPT_POLARITY_1,
WM8903_MICSHRT_INV | WM8903_MICDET_INV, int_pol);

snd_soc_jack_report(wm8903->mic_jack, mic_report,
wm8903->mic_short | wm8903->mic_det);

wm8903->mic_last_report = mic_report;

return IRQ_HANDLED;
}

Expand Down
4 changes: 4 additions & 0 deletions sound/soc/codecs/wm8903.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
extern struct snd_soc_dai wm8903_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8903;

extern int wm8903_mic_detect(struct snd_soc_codec *codec,
struct snd_soc_jack *jack,
int det, int shrt);

#define WM8903_MCLK_DIV_2 1
#define WM8903_CLK_SYS 2
#define WM8903_BCLK 3
Expand Down

0 comments on commit 7245387

Please sign in to comment.