Commit b00adf76 authored by Mark Brown's avatar Mark Brown
Browse files

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: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 500fa30e
Loading
Loading
Loading
Loading
+109 −11
Original line number Diff line number Diff line
@@ -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;

	report = SND_JACK_MICROPHONE;
		wm8958_micd_set_rate(codec);

	/* Everything else is buttons; just assign slots */
	if (status & 0x1c)
		report |= SND_JACK_BTN_0;
		snd_soc_jack_report(wm8994->micdet[0].jack, 0,
				    SND_JACK_BTN_0 | SND_JACK_HEADSET);

done:
	snd_soc_jack_report(wm8994->micdet[0].jack, report,
			    SND_JACK_BTN_0 | SND_JACK_MICROPHONE);
		return;
	}

	/* 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);
	}


	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 {
+2 −0
Original line number Diff line number Diff line
@@ -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;