diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-04-12 03:00:36 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-04-13 13:02:06 -0400 |
commit | 68688e78ed4bc8d1a811ca29e2f8681561706db3 (patch) | |
tree | 1dabf09546d81a792b19e1b6b3874539afae06a2 /sound/soc/samsung/speyside.c | |
parent | ea3e98e75a6f38522450b66e22e34267977915ef (diff) |
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: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Jassi Brar <jassisinghbrar@gmail.com>
Acked-by: Liam Girdwood <lrg@ti.com>
Diffstat (limited to 'sound/soc/samsung/speyside.c')
-rw-r--r-- | sound/soc/samsung/speyside.c | 78 |
1 files changed, 77 insertions, 1 deletions
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 0adaf7fe6814..612a39e0e108 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c | |||
@@ -11,10 +11,14 @@ | |||
11 | 11 | ||
12 | #include <sound/soc.h> | 12 | #include <sound/soc.h> |
13 | #include <sound/soc-dapm.h> | 13 | #include <sound/soc-dapm.h> |
14 | #include <sound/jack.h> | ||
15 | #include <linux/gpio.h> | ||
14 | 16 | ||
15 | #include "../codecs/wm8915.h" | 17 | #include "../codecs/wm8915.h" |
16 | #include "../codecs/wm9081.h" | 18 | #include "../codecs/wm9081.h" |
17 | 19 | ||
20 | #define WM8915_HPSEL_GPIO 214 | ||
21 | |||
18 | static int speyside_set_bias_level(struct snd_soc_card *card, | 22 | static int speyside_set_bias_level(struct snd_soc_card *card, |
19 | enum snd_soc_bias_level level) | 23 | enum snd_soc_bias_level level) |
20 | { | 24 | { |
@@ -79,11 +83,74 @@ static struct snd_soc_ops speyside_ops = { | |||
79 | .hw_params = speyside_hw_params, | 83 | .hw_params = speyside_hw_params, |
80 | }; | 84 | }; |
81 | 85 | ||
86 | static struct snd_soc_jack speyside_headset; | ||
87 | |||
88 | /* Headset jack detection DAPM pins */ | ||
89 | static struct snd_soc_jack_pin speyside_headset_pins[] = { | ||
90 | { | ||
91 | .pin = "Headset Mic", | ||
92 | .mask = SND_JACK_MICROPHONE, | ||
93 | }, | ||
94 | { | ||
95 | .pin = "Headphone", | ||
96 | .mask = SND_JACK_HEADPHONE, | ||
97 | }, | ||
98 | }; | ||
99 | |||
100 | /* Default the headphone selection to active high */ | ||
101 | static int speyside_jack_polarity; | ||
102 | |||
103 | static int speyside_get_micbias(struct snd_soc_dapm_widget *source, | ||
104 | struct snd_soc_dapm_widget *sink) | ||
105 | { | ||
106 | if (speyside_jack_polarity && (strcmp(source->name, "MICB1") == 0)) | ||
107 | return 1; | ||
108 | if (!speyside_jack_polarity && (strcmp(source->name, "MICB2") == 0)) | ||
109 | return 1; | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static void speyside_set_polarity(struct snd_soc_codec *codec, | ||
115 | int polarity) | ||
116 | { | ||
117 | speyside_jack_polarity = !polarity; | ||
118 | gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity); | ||
119 | |||
120 | /* Re-run DAPM to make sure we're using the correct mic bias */ | ||
121 | snd_soc_dapm_sync(&codec->dapm); | ||
122 | } | ||
123 | |||
82 | static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd) | 124 | static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd) |
83 | { | 125 | { |
84 | struct snd_soc_dai *dai = rtd->codec_dai; | 126 | struct snd_soc_dai *dai = rtd->codec_dai; |
127 | struct snd_soc_codec *codec = rtd->codec; | ||
128 | int ret; | ||
129 | |||
130 | ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0); | ||
131 | if (ret < 0) | ||
132 | return ret; | ||
133 | |||
134 | ret = gpio_request(WM8915_HPSEL_GPIO, "HP_SEL"); | ||
135 | if (ret != 0) | ||
136 | pr_err("Failed to request HP_SEL GPIO: %d\n", ret); | ||
137 | gpio_direction_output(WM8915_HPSEL_GPIO, speyside_jack_polarity); | ||
85 | 138 | ||
86 | return snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0); | 139 | ret = snd_soc_jack_new(codec, "Headset", |
140 | SND_JACK_HEADSET | SND_JACK_BTN_0, | ||
141 | &speyside_headset); | ||
142 | if (ret) | ||
143 | return ret; | ||
144 | |||
145 | ret = snd_soc_jack_add_pins(&speyside_headset, | ||
146 | ARRAY_SIZE(speyside_headset_pins), | ||
147 | speyside_headset_pins); | ||
148 | if (ret) | ||
149 | return ret; | ||
150 | |||
151 | wm8915_detect(codec, &speyside_headset, speyside_set_polarity); | ||
152 | |||
153 | return 0; | ||
87 | } | 154 | } |
88 | 155 | ||
89 | static struct snd_soc_dai_link speyside_dai[] = { | 156 | static struct snd_soc_dai_link speyside_dai[] = { |
@@ -125,6 +192,7 @@ static struct snd_soc_codec_conf speyside_codec_conf[] = { | |||
125 | 192 | ||
126 | static struct snd_soc_dapm_widget widgets[] = { | 193 | static struct snd_soc_dapm_widget widgets[] = { |
127 | SND_SOC_DAPM_HP("Headphone", NULL), | 194 | SND_SOC_DAPM_HP("Headphone", NULL), |
195 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | ||
128 | 196 | ||
129 | SND_SOC_DAPM_SPK("Main Speaker", NULL), | 197 | SND_SOC_DAPM_SPK("Main Speaker", NULL), |
130 | 198 | ||
@@ -133,7 +201,15 @@ static struct snd_soc_dapm_widget widgets[] = { | |||
133 | }; | 201 | }; |
134 | 202 | ||
135 | static struct snd_soc_dapm_route audio_paths[] = { | 203 | static struct snd_soc_dapm_route audio_paths[] = { |
204 | { "IN1RN", NULL, "MICB1" }, | ||
205 | { "IN1RP", NULL, "MICB1" }, | ||
206 | { "IN1RN", NULL, "MICB2" }, | ||
207 | { "IN1RP", NULL, "MICB2" }, | ||
208 | { "MICB1", NULL, "Headset Mic", speyside_get_micbias }, | ||
209 | { "MICB2", NULL, "Headset Mic", speyside_get_micbias }, | ||
210 | |||
136 | { "IN1LP", NULL, "MICB2" }, | 211 | { "IN1LP", NULL, "MICB2" }, |
212 | { "IN1RN", NULL, "MICB1" }, | ||
137 | { "MICB2", NULL, "Main AMIC" }, | 213 | { "MICB2", NULL, "Main AMIC" }, |
138 | 214 | ||
139 | { "DMIC1DAT", NULL, "MICB1" }, | 215 | { "DMIC1DAT", NULL, "MICB1" }, |