diff options
author | Vinod Koul <vinod.koul@intel.com> | 2011-02-09 11:14:33 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-02-09 17:32:39 -0500 |
commit | 42aee9b43e76645cda59bce30a101e7574ce913e (patch) | |
tree | 5cae2427be0c6000e27f728d19a8921fb7935115 /sound/soc/mid-x86 | |
parent | 1e2f5932e472f11b987e8339ffc855aa00ecebf5 (diff) |
ASoC: mfld_machine: Add support for jack detection
This patch adds support for registering jack interupt
and registering jack with core
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Harsha Priya <priya.harsha@intel.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/mid-x86')
-rw-r--r-- | sound/soc/mid-x86/mfld_machine.c | 203 |
1 files changed, 176 insertions, 27 deletions
diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c index 7925851a5de1..45a006708022 100644 --- a/sound/soc/mid-x86/mfld_machine.c +++ b/sound/soc/mid-x86/mfld_machine.c | |||
@@ -27,18 +27,53 @@ | |||
27 | #include <linux/init.h> | 27 | #include <linux/init.h> |
28 | #include <linux/device.h> | 28 | #include <linux/device.h> |
29 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <linux/io.h> | ||
30 | #include <sound/pcm.h> | 31 | #include <sound/pcm.h> |
31 | #include <sound/pcm_params.h> | 32 | #include <sound/pcm_params.h> |
32 | #include <sound/soc.h> | 33 | #include <sound/soc.h> |
34 | #include <sound/jack.h> | ||
33 | #include "../codecs/sn95031.h" | 35 | #include "../codecs/sn95031.h" |
34 | 36 | ||
35 | #define MID_MONO 1 | 37 | #define MID_MONO 1 |
36 | #define MID_STEREO 2 | 38 | #define MID_STEREO 2 |
37 | #define MID_MAX_CAP 5 | 39 | #define MID_MAX_CAP 5 |
40 | #define MFLD_JACK_INSERT 0x04 | ||
41 | |||
42 | enum soc_mic_bias_zones { | ||
43 | MFLD_MV_START = 0, | ||
44 | /* mic bias volutage range for Headphones*/ | ||
45 | MFLD_MV_HP = 400, | ||
46 | /* mic bias volutage range for American Headset*/ | ||
47 | MFLD_MV_AM_HS = 650, | ||
48 | /* mic bias volutage range for Headset*/ | ||
49 | MFLD_MV_HS = 2000, | ||
50 | MFLD_MV_UNDEFINED, | ||
51 | }; | ||
38 | 52 | ||
39 | static unsigned int hs_switch; | 53 | static unsigned int hs_switch; |
40 | static unsigned int lo_dac; | 54 | static unsigned int lo_dac; |
41 | 55 | ||
56 | struct mfld_mc_private { | ||
57 | struct platform_device *socdev; | ||
58 | void __iomem *int_base; | ||
59 | struct snd_soc_codec *codec; | ||
60 | u8 interrupt_status; | ||
61 | }; | ||
62 | |||
63 | struct snd_soc_jack mfld_jack; | ||
64 | |||
65 | /*Headset jack detection DAPM pins */ | ||
66 | static struct snd_soc_jack_pin mfld_jack_pins[] = { | ||
67 | { | ||
68 | .pin = "Headphones", | ||
69 | .mask = SND_JACK_HEADPHONE, | ||
70 | }, | ||
71 | { | ||
72 | .pin = "AMIC1", | ||
73 | .mask = SND_JACK_MICROPHONE, | ||
74 | }, | ||
75 | }; | ||
76 | |||
42 | /* sound card controls */ | 77 | /* sound card controls */ |
43 | static const char *headset_switch_text[] = {"Earpiece", "Headset"}; | 78 | static const char *headset_switch_text[] = {"Earpiece", "Headset"}; |
44 | 79 | ||
@@ -67,13 +102,11 @@ static int headset_set_switch(struct snd_kcontrol *kcontrol, | |||
67 | 102 | ||
68 | if (ucontrol->value.integer.value[0]) { | 103 | if (ucontrol->value.integer.value[0]) { |
69 | pr_debug("hs_set HS path\n"); | 104 | pr_debug("hs_set HS path\n"); |
70 | snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTL"); | 105 | snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); |
71 | snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTR"); | ||
72 | snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); | 106 | snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); |
73 | } else { | 107 | } else { |
74 | pr_debug("hs_set EP path\n"); | 108 | pr_debug("hs_set EP path\n"); |
75 | snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL"); | 109 | snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); |
76 | snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR"); | ||
77 | snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); | 110 | snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); |
78 | } | 111 | } |
79 | snd_soc_dapm_sync(&codec->dapm); | 112 | snd_soc_dapm_sync(&codec->dapm); |
@@ -91,12 +124,10 @@ static void lo_enable_out_pins(struct snd_soc_codec *codec) | |||
91 | snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT"); | 124 | snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT"); |
92 | snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT"); | 125 | snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT"); |
93 | if (hs_switch) { | 126 | if (hs_switch) { |
94 | snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTL"); | 127 | snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); |
95 | snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTR"); | ||
96 | snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); | 128 | snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); |
97 | } else { | 129 | } else { |
98 | snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL"); | 130 | snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); |
99 | snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR"); | ||
100 | snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); | 131 | snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); |
101 | } | 132 | } |
102 | } | 133 | } |
@@ -130,8 +161,7 @@ static int lo_set_switch(struct snd_kcontrol *kcontrol, | |||
130 | 161 | ||
131 | case 1: | 162 | case 1: |
132 | pr_debug("set hs path\n"); | 163 | pr_debug("set hs path\n"); |
133 | snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL"); | 164 | snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); |
134 | snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR"); | ||
135 | snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); | 165 | snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); |
136 | snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22); | 166 | snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22); |
137 | break; | 167 | break; |
@@ -162,12 +192,45 @@ static const struct snd_kcontrol_new mfld_snd_controls[] = { | |||
162 | lo_get_switch, lo_set_switch), | 192 | lo_get_switch, lo_set_switch), |
163 | }; | 193 | }; |
164 | 194 | ||
195 | static const struct snd_soc_dapm_widget mfld_widgets[] = { | ||
196 | SND_SOC_DAPM_HP("Headphones", NULL), | ||
197 | SND_SOC_DAPM_MIC("Mic", NULL), | ||
198 | }; | ||
199 | |||
200 | static const struct snd_soc_dapm_route mfld_map[] = { | ||
201 | {"Headphones", NULL, "HPOUTR"}, | ||
202 | {"Headphones", NULL, "HPOUTL"}, | ||
203 | {"Mic", NULL, "AMIC1"}, | ||
204 | }; | ||
205 | |||
206 | static void mfld_jack_check(unsigned int intr_status) | ||
207 | { | ||
208 | struct mfld_jack_data jack_data; | ||
209 | |||
210 | jack_data.mfld_jack = &mfld_jack; | ||
211 | jack_data.intr_id = intr_status; | ||
212 | |||
213 | sn95031_jack_detection(&jack_data); | ||
214 | /* TODO: add american headset detection post gpiolib support */ | ||
215 | } | ||
216 | |||
165 | static int mfld_init(struct snd_soc_pcm_runtime *runtime) | 217 | static int mfld_init(struct snd_soc_pcm_runtime *runtime) |
166 | { | 218 | { |
167 | struct snd_soc_codec *codec = runtime->codec; | 219 | struct snd_soc_codec *codec = runtime->codec; |
168 | struct snd_soc_dapm_context *dapm = &codec->dapm; | 220 | struct snd_soc_dapm_context *dapm = &codec->dapm; |
169 | int ret_val; | 221 | int ret_val; |
170 | 222 | ||
223 | /* Add jack sense widgets */ | ||
224 | snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets)); | ||
225 | |||
226 | /* Set up the map */ | ||
227 | snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map)); | ||
228 | |||
229 | /* always connected */ | ||
230 | snd_soc_dapm_enable_pin(dapm, "Headphones"); | ||
231 | snd_soc_dapm_enable_pin(dapm, "Mic"); | ||
232 | snd_soc_dapm_sync(dapm); | ||
233 | |||
171 | ret_val = snd_soc_add_controls(codec, mfld_snd_controls, | 234 | ret_val = snd_soc_add_controls(codec, mfld_snd_controls, |
172 | ARRAY_SIZE(mfld_snd_controls)); | 235 | ARRAY_SIZE(mfld_snd_controls)); |
173 | if (ret_val) { | 236 | if (ret_val) { |
@@ -175,8 +238,7 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime) | |||
175 | return ret_val; | 238 | return ret_val; |
176 | } | 239 | } |
177 | /* default is earpiece pin, userspace sets it explcitly */ | 240 | /* default is earpiece pin, userspace sets it explcitly */ |
178 | snd_soc_dapm_disable_pin(dapm, "HPOUTL"); | 241 | snd_soc_dapm_disable_pin(dapm, "Headphones"); |
179 | snd_soc_dapm_disable_pin(dapm, "HPOUTR"); | ||
180 | /* default is lineout NC, userspace sets it explcitly */ | 242 | /* default is lineout NC, userspace sets it explcitly */ |
181 | snd_soc_dapm_disable_pin(dapm, "LINEOUTL"); | 243 | snd_soc_dapm_disable_pin(dapm, "LINEOUTL"); |
182 | snd_soc_dapm_disable_pin(dapm, "LINEOUTR"); | 244 | snd_soc_dapm_disable_pin(dapm, "LINEOUTR"); |
@@ -185,7 +247,29 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime) | |||
185 | /* we dont use linein in this so set to NC */ | 247 | /* we dont use linein in this so set to NC */ |
186 | snd_soc_dapm_disable_pin(dapm, "LINEINL"); | 248 | snd_soc_dapm_disable_pin(dapm, "LINEINL"); |
187 | snd_soc_dapm_disable_pin(dapm, "LINEINR"); | 249 | snd_soc_dapm_disable_pin(dapm, "LINEINR"); |
188 | return snd_soc_dapm_sync(dapm); | 250 | snd_soc_dapm_sync(dapm); |
251 | |||
252 | /* Headset and button jack detection */ | ||
253 | ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack", | ||
254 | SND_JACK_HEADSET | SND_JACK_BTN_0 | | ||
255 | SND_JACK_BTN_1, &mfld_jack); | ||
256 | if (ret_val) { | ||
257 | pr_err("jack creation failed\n"); | ||
258 | return ret_val; | ||
259 | } | ||
260 | |||
261 | ret_val = snd_soc_jack_add_pins(&mfld_jack, | ||
262 | ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins); | ||
263 | if (ret_val) { | ||
264 | pr_err("adding jack pins failed\n"); | ||
265 | return ret_val; | ||
266 | } | ||
267 | |||
268 | /* we want to check if anything is inserted at boot, | ||
269 | * so send a fake event to codec and it will read adc | ||
270 | * to find if anything is there or not */ | ||
271 | mfld_jack_check(MFLD_JACK_INSERT); | ||
272 | return ret_val; | ||
189 | } | 273 | } |
190 | 274 | ||
191 | struct snd_soc_dai_link mfld_msic_dailink[] = { | 275 | struct snd_soc_dai_link mfld_msic_dailink[] = { |
@@ -234,37 +318,102 @@ static struct snd_soc_card snd_soc_card_mfld = { | |||
234 | .num_links = ARRAY_SIZE(mfld_msic_dailink), | 318 | .num_links = ARRAY_SIZE(mfld_msic_dailink), |
235 | }; | 319 | }; |
236 | 320 | ||
321 | static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev) | ||
322 | { | ||
323 | struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev; | ||
324 | |||
325 | memcpy_fromio(&mc_private->interrupt_status, | ||
326 | ((void *)(mc_private->int_base)), | ||
327 | sizeof(u8)); | ||
328 | return IRQ_WAKE_THREAD; | ||
329 | } | ||
330 | |||
331 | static irqreturn_t snd_mfld_jack_detection(int irq, void *data) | ||
332 | { | ||
333 | struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data; | ||
334 | |||
335 | if (mfld_jack.codec == NULL) | ||
336 | return IRQ_HANDLED; | ||
337 | mfld_jack_check(mc_drv_ctx->interrupt_status); | ||
338 | |||
339 | return IRQ_HANDLED; | ||
340 | } | ||
341 | |||
237 | static int __devinit snd_mfld_mc_probe(struct platform_device *pdev) | 342 | static int __devinit snd_mfld_mc_probe(struct platform_device *pdev) |
238 | { | 343 | { |
239 | struct platform_device *socdev; | 344 | int ret_val = 0, irq; |
240 | int ret_val = 0; | 345 | struct mfld_mc_private *mc_drv_ctx; |
346 | struct resource *irq_mem; | ||
241 | 347 | ||
242 | pr_debug("snd_mfld_mc_probe called\n"); | 348 | pr_debug("snd_mfld_mc_probe called\n"); |
243 | 349 | ||
244 | socdev = platform_device_alloc("soc-audio", -1); | 350 | /* retrive the irq number */ |
245 | if (!socdev) { | 351 | irq = platform_get_irq(pdev, 0); |
246 | pr_err("soc-audio device allocation failed\n"); | 352 | |
353 | /* audio interrupt base of SRAM location where | ||
354 | * interrupts are stored by System FW */ | ||
355 | mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC); | ||
356 | if (!mc_drv_ctx) { | ||
357 | pr_err("allocation failed\n"); | ||
247 | return -ENOMEM; | 358 | return -ENOMEM; |
248 | } | 359 | } |
249 | platform_set_drvdata(socdev, &snd_soc_card_mfld); | 360 | |
250 | ret_val = platform_device_add(socdev); | 361 | irq_mem = platform_get_resource_byname( |
362 | pdev, IORESOURCE_MEM, "IRQ_BASE"); | ||
363 | if (!irq_mem) { | ||
364 | pr_err("no mem resource given\n"); | ||
365 | ret_val = -ENODEV; | ||
366 | goto unalloc; | ||
367 | } | ||
368 | mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start, | ||
369 | resource_size(irq_mem)); | ||
370 | if (!mc_drv_ctx->int_base) { | ||
371 | pr_err("Mapping of cache failed\n"); | ||
372 | ret_val = -ENOMEM; | ||
373 | goto unalloc; | ||
374 | } | ||
375 | /* register for interrupt */ | ||
376 | ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler, | ||
377 | snd_mfld_jack_detection, | ||
378 | IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx); | ||
379 | if (ret_val) { | ||
380 | pr_err("cannot register IRQ\n"); | ||
381 | goto unalloc; | ||
382 | } | ||
383 | /* create soc device */ | ||
384 | mc_drv_ctx->socdev = platform_device_alloc("soc-audio", -1); | ||
385 | if (!mc_drv_ctx->socdev) { | ||
386 | pr_err("soc-audio device allocation failed\n"); | ||
387 | ret_val = -ENOMEM; | ||
388 | goto freeirq; | ||
389 | } | ||
390 | platform_set_drvdata(mc_drv_ctx->socdev, &snd_soc_card_mfld); | ||
391 | ret_val = platform_device_add(mc_drv_ctx->socdev); | ||
251 | if (ret_val) { | 392 | if (ret_val) { |
252 | pr_err("Unable to add soc-audio device, err %d\n", ret_val); | 393 | pr_err("Unable to add soc-audio device, err %d\n", ret_val); |
253 | platform_device_put(socdev); | 394 | goto unregister; |
254 | } | 395 | } |
255 | 396 | platform_set_drvdata(pdev, mc_drv_ctx); | |
256 | platform_set_drvdata(pdev, socdev); | ||
257 | |||
258 | pr_debug("successfully exited probe\n"); | 397 | pr_debug("successfully exited probe\n"); |
259 | return ret_val; | 398 | return ret_val; |
399 | |||
400 | unregister: | ||
401 | platform_device_put(mc_drv_ctx->socdev); | ||
402 | freeirq: | ||
403 | free_irq(irq, mc_drv_ctx); | ||
404 | unalloc: | ||
405 | kfree(mc_drv_ctx); | ||
406 | return ret_val; | ||
260 | } | 407 | } |
261 | 408 | ||
262 | static int __devexit snd_mfld_mc_remove(struct platform_device *pdev) | 409 | static int __devexit snd_mfld_mc_remove(struct platform_device *pdev) |
263 | { | 410 | { |
264 | struct platform_device *socdev = platform_get_drvdata(pdev); | 411 | struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev); |
265 | pr_debug("snd_mfld_mc_remove called\n"); | ||
266 | 412 | ||
267 | platform_device_unregister(socdev); | 413 | pr_debug("snd_mfld_mc_remove called\n"); |
414 | free_irq(platform_get_irq(pdev, 0), mc_drv_ctx); | ||
415 | platform_device_unregister(mc_drv_ctx->socdev); | ||
416 | kfree(mc_drv_ctx); | ||
268 | platform_set_drvdata(pdev, NULL); | 417 | platform_set_drvdata(pdev, NULL); |
269 | return 0; | 418 | return 0; |
270 | } | 419 | } |