Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 68688e78 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: Add Speyside headset jack detection support



Speyside makes use of support the WM8915 has for detecting the polarity
of the microphone and ground connections on headsets, using a GPIO to
control the polarity of the ground connection and switching between the
two microphone bias supplies available on the device in order to do so.
As a result of this the detection support is more involved than for most
other CODECs, using a callback to configure the current polarity of the
jack and translate this into the board-specific connections required for
the current scenario.

On Android some additional work is required to hook this up to the
application layer as the Android HeadsetObserver monitors a custom
drivers/switch API rather than the standard Linux APIs.  This can be
done by either updating HeadsetObserver or modifying the ALSA core to
report via drivers/switch as well.

Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: default avatarJassi Brar <jassisinghbrar@gmail.com>
Acked-by: default avatarLiam Girdwood <lrg@ti.com>
parent ea3e98e7
Loading
Loading
Loading
Loading
+77 −1
Original line number Diff line number Diff line
@@ -11,10 +11,14 @@

#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <linux/gpio.h>

#include "../codecs/wm8915.h"
#include "../codecs/wm9081.h"

#define WM8915_HPSEL_GPIO 214

static int speyside_set_bias_level(struct snd_soc_card *card,
				   enum snd_soc_bias_level level)
{
@@ -79,11 +83,74 @@ static struct snd_soc_ops speyside_ops = {
	.hw_params = speyside_hw_params,
};

static struct snd_soc_jack speyside_headset;

/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin speyside_headset_pins[] = {
	{
		.pin = "Headset Mic",
		.mask = SND_JACK_MICROPHONE,
	},
	{
		.pin = "Headphone",
		.mask = SND_JACK_HEADPHONE,
	},
};

/* Default the headphone selection to active high */
static int speyside_jack_polarity;

static int speyside_get_micbias(struct snd_soc_dapm_widget *source,
				struct snd_soc_dapm_widget *sink)
{
	if (speyside_jack_polarity && (strcmp(source->name, "MICB1") == 0))
		return 1;
	if (!speyside_jack_polarity && (strcmp(source->name, "MICB2") == 0))
		return 1;

	return 0;
}

static void speyside_set_polarity(struct snd_soc_codec *codec,
				  int polarity)
{
	speyside_jack_polarity = !polarity;
	gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);

	/* Re-run DAPM to make sure we're using the correct mic bias */
	snd_soc_dapm_sync(&codec->dapm);
}

static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd)
{
	struct snd_soc_dai *dai = rtd->codec_dai;
	struct snd_soc_codec *codec = rtd->codec;
	int ret;

	ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0);
	if (ret < 0)
		return ret;

	ret = gpio_request(WM8915_HPSEL_GPIO, "HP_SEL");
	if (ret != 0)
		pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
	gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity);

	return snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0);
	ret = snd_soc_jack_new(codec, "Headset",
			       SND_JACK_HEADSET | SND_JACK_BTN_0,
			       &speyside_headset);
	if (ret)
		return ret;

	ret = snd_soc_jack_add_pins(&speyside_headset,
				    ARRAY_SIZE(speyside_headset_pins),
				    speyside_headset_pins);
	if (ret)
		return ret;

	wm8915_detect(codec, &speyside_headset, speyside_set_polarity);

	return 0;
}

static struct snd_soc_dai_link speyside_dai[] = {
@@ -125,6 +192,7 @@ static struct snd_soc_codec_conf speyside_codec_conf[] = {

static struct snd_soc_dapm_widget widgets[] = {
	SND_SOC_DAPM_HP("Headphone", NULL),
	SND_SOC_DAPM_MIC("Headset Mic", NULL),

	SND_SOC_DAPM_SPK("Main Speaker", NULL),

@@ -133,7 +201,15 @@ static struct snd_soc_dapm_widget widgets[] = {
};

static struct snd_soc_dapm_route audio_paths[] = {
	{ "IN1RN", NULL, "MICB1" },
	{ "IN1RP", NULL, "MICB1" },
	{ "IN1RN", NULL, "MICB2" },
	{ "IN1RP", NULL, "MICB2" },
	{ "MICB1", NULL, "Headset Mic", speyside_get_micbias },
	{ "MICB2", NULL, "Headset Mic", speyside_get_micbias },

	{ "IN1LP", NULL, "MICB2" },
	{ "IN1RN", NULL, "MICB1" },
	{ "MICB2", NULL, "Main AMIC" },

	{ "DMIC1DAT", NULL, "MICB1" },