blob: 8c0df5c8f5d451f4faa564944d175e8d3d47e937 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/clk.h>
14#include <linux/gpio.h>
15#include <linux/mfd/pm8xxx/pm8921.h>
16#include <linux/platform_device.h>
17#include <linux/gpio.h>
18#include <linux/mfd/pm8xxx/pm8921.h>
19#include <sound/core.h>
20#include <sound/soc.h>
21#include <sound/soc-dapm.h>
22#include <sound/soc-dsp.h>
23#include <sound/pcm.h>
24#include <sound/jack.h>
25#include "msm-pcm-routing.h"
26#include <../codecs/wcd9310.h>
27
28/* 8960 machine driver */
29
30#define PM8921_GPIO_BASE NR_GPIO_IRQS
31#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
32
33#define MSM_CDC_PAMPL (PM8921_GPIO_PM_TO_SYS(18))
34#define MSM_CDC_PAMPR (PM8921_GPIO_PM_TO_SYS(19))
35#define MSM8960_SPK_ON 1
36#define MSM8960_SPK_OFF 0
37
38#define msm8960_SLIM_0_RX_MAX_CHANNELS 2
39#define msm8960_SLIM_0_TX_MAX_CHANNELS 4
40
41
42static int msm8960_spk_control;
43static int msm8960_pamp_on;
44static int msm8960_slim_0_rx_ch = 1;
45static int msm8960_slim_0_tx_ch = 1;
46
47struct tabla_mbhc_calibration tabla_cal = {
48 .bias = TABLA_MICBIAS2,
49 .tldoh = 100,
50 .bg_fast_settle = 100,
51 .mic_current = TABLA_PID_MIC_5_UA,
52 .mic_pid = 100,
53 .hph_current = TABLA_PID_MIC_5_UA,
54 .setup_plug_removal_delay = 1000000,
55 .shutdown_plug_removal = 100000,
56};
57
58static struct clk *codec_clk;
59static int clk_users;
60
61static int msm8960_headset_gpios_configured;
62
63static struct snd_soc_jack hs_jack;
64
65static void codec_poweramp_on(void)
66{
67 int ret = 0;
68
69 struct pm_gpio param = {
70 .direction = PM_GPIO_DIR_OUT,
71 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
72 .output_value = 1,
73 .pull = PM_GPIO_PULL_NO,
74 .vin_sel = PM_GPIO_VIN_S4,
75 .out_strength = PM_GPIO_STRENGTH_MED,
76 .function = PM_GPIO_FUNC_NORMAL,
77 };
78
79 if (msm8960_pamp_on)
80 return;
81
82 pr_debug("%s: enable stereo spkr amp\n", __func__);
83 ret = gpio_request(MSM_CDC_PAMPL, "CDC PAMP1");
84 if (ret) {
85 pr_err("%s: Error requesting GPIO %d\n", __func__,
86 MSM_CDC_PAMPL);
87 return;
88 }
89 ret = pm8xxx_gpio_config(MSM_CDC_PAMPL, &param);
90 if (ret)
91 pr_err("%s: Failed to configure gpio %d\n", __func__,
92 MSM_CDC_PAMPL);
93 else
94 gpio_direction_output(MSM_CDC_PAMPL, 1);
95
96 ret = gpio_request(MSM_CDC_PAMPR, "CDC PAMPL");
97 if (ret) {
98 pr_err("%s: Error requesting GPIO %d\n", __func__,
99 MSM_CDC_PAMPR);
100 gpio_free(MSM_CDC_PAMPL);
101 return;
102 }
103 ret = pm8xxx_gpio_config(MSM_CDC_PAMPR, &param);
104 if (ret)
105 pr_err("%s: Failed to configure gpio %d\n", __func__,
106 MSM_CDC_PAMPR);
107 else
108 gpio_direction_output(MSM_CDC_PAMPR, 1);
109
110 msm8960_pamp_on = 1;
111}
112static void codec_poweramp_off(void)
113{
114 if (!msm8960_pamp_on)
115 return;
116
117 pr_debug("%s: disable stereo spkr amp\n", __func__);
118 gpio_direction_output(MSM_CDC_PAMPL, 0);
119 gpio_free(MSM_CDC_PAMPL);
120 gpio_direction_output(MSM_CDC_PAMPR, 0);
121 gpio_free(MSM_CDC_PAMPR);
122 msm8960_pamp_on = 0;
123}
124static void msm8960_ext_control(struct snd_soc_codec *codec)
125{
126 struct snd_soc_dapm_context *dapm = &codec->dapm;
127
128 pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
129 if (msm8960_spk_control == MSM8960_SPK_ON)
130 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
131 else
132 snd_soc_dapm_disable_pin(dapm, "Ext Spk");
133
134 snd_soc_dapm_sync(dapm);
135}
136
137static int msm8960_get_spk(struct snd_kcontrol *kcontrol,
138 struct snd_ctl_elem_value *ucontrol)
139{
140 pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
141 ucontrol->value.integer.value[0] = msm8960_spk_control;
142 return 0;
143}
144static int msm8960_set_spk(struct snd_kcontrol *kcontrol,
145 struct snd_ctl_elem_value *ucontrol)
146{
147 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
148
149 pr_debug("%s()\n", __func__);
150 if (msm8960_spk_control == ucontrol->value.integer.value[0])
151 return 0;
152
153 msm8960_spk_control = ucontrol->value.integer.value[0];
154 msm8960_ext_control(codec);
155 return 1;
156}
157static int msm8960_spkramp_event(struct snd_soc_dapm_widget *w,
158 struct snd_kcontrol *k, int event)
159{
160 pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
161 if (SND_SOC_DAPM_EVENT_ON(event))
162 codec_poweramp_on();
163 else
164 codec_poweramp_off();
165 return 0;
166}
167
168static const struct snd_soc_dapm_widget msm8960_dapm_widgets[] = {
169 SND_SOC_DAPM_SPK("Ext Spk", msm8960_spkramp_event),
170 SND_SOC_DAPM_MIC("Handset Mic", NULL),
171 SND_SOC_DAPM_MIC("Headset Mic", NULL),
172 SND_SOC_DAPM_MIC("Digital Mic1", NULL),
173};
174
175static const struct snd_soc_dapm_route audio_map[] = {
176 /* Speaker path */
177 {"Ext Spk", NULL, "LINEOUT"},
178
179 /* Microphone path */
180 {"AMIC1", NULL, "MIC BIAS1 Internal"},
181 {"DMIC1 IN", NULL, "MIC BIAS1 External"},
182 {"AMIC2", NULL, "MIC BIAS2 External"},
183 {"MIC BIAS1 Internal", NULL, "Handset Mic"},
184 {"MIC BIAS1 External", NULL, "Digital Mic1"},
185 {"MIC BIAS2 External", NULL, "Headset Mic"},
186};
187
188static const char *spk_function[] = {"Off", "On"};
189static const struct soc_enum msm8960_enum[] = {
190 SOC_ENUM_SINGLE_EXT(2, spk_function),
191};
192
193static int msm8960_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
194 struct snd_ctl_elem_value *ucontrol)
195{
196 pr_debug("%s: msm8960_slim_0_rx_ch = %d", __func__,
197 msm8960_slim_0_rx_ch);
198 ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch;
199 return 0;
200}
201
202static int msm8960_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
203 struct snd_ctl_elem_value *ucontrol)
204{
205 msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0];
206
207 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
208 msm8960_slim_0_rx_ch);
209 return 1;
210}
211
212static int msm8960_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
213 struct snd_ctl_elem_value *ucontrol)
214{
215 pr_debug("%s: msm8960_slim_0_tx_ch = %d", __func__,
216 msm8960_slim_0_tx_ch);
217 ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch;
218 return 0;
219}
220
221static int msm8960_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
222 struct snd_ctl_elem_value *ucontrol)
223{
224 msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0];
225
226 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
227 msm8960_slim_0_tx_ch);
228 return 1;
229}
230
231
232static const struct snd_kcontrol_new tabla_msm8960_controls[] = {
233 SOC_ENUM_EXT("Speaker Function", msm8960_enum[0], msm8960_get_spk,
234 msm8960_set_spk),
235 SOC_SINGLE_EXT("SLIM_0_RX Channels", 0, 0,
236 msm8960_SLIM_0_RX_MAX_CHANNELS, 0,
237 msm8960_slim_0_rx_ch_get, msm8960_slim_0_rx_ch_put),
238 SOC_SINGLE_EXT("SLIM_0_TX Channels", 0, 0,
239 msm8960_SLIM_0_TX_MAX_CHANNELS, 0,
240 msm8960_slim_0_tx_ch_get, msm8960_slim_0_tx_ch_put),
241};
242
243static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
244{
245 int err;
246 struct snd_soc_codec *codec = rtd->codec;
247 struct snd_soc_dapm_context *dapm = &codec->dapm;
248
249 pr_debug("%s()\n", __func__);
250
251 err = snd_soc_add_controls(codec, tabla_msm8960_controls,
252 ARRAY_SIZE(tabla_msm8960_controls));
253 if (err < 0)
254 return err;
255
256 snd_soc_dapm_new_controls(dapm, msm8960_dapm_widgets,
257 ARRAY_SIZE(msm8960_dapm_widgets));
258
259 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
260
261 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
262
263 snd_soc_dapm_sync(dapm);
264
265 err = snd_soc_jack_new(codec, "Headset Jack",
266 SND_JACK_HEADSET, &hs_jack);
267 if (err) {
268 pr_err("failed to create new jack\n");
269 return err;
270 }
271 tabla_hs_detect(codec, &hs_jack, &tabla_cal);
272
273 return 0;
274}
275
276/*
277 * LPA Needs only RX BE DAI links.
278 * Hence define seperate BE list for lpa
279 */
280
281static const char *lpa_mm_be[] = {
282 LPASS_BE_SLIMBUS_0_RX,
283};
284
285static struct snd_soc_dsp_link lpa_fe_media = {
286 .supported_be = lpa_mm_be,
287 .num_be = ARRAY_SIZE(lpa_mm_be),
288 .fe_playback_channels = 2,
289 .fe_capture_channels = 1,
290 .trigger = {
291 SND_SOC_DSP_TRIGGER_POST,
292 SND_SOC_DSP_TRIGGER_POST
293 },
294};
295
296static const char *mm_be[] = {
297 LPASS_BE_SLIMBUS_0_RX,
298 LPASS_BE_SLIMBUS_0_TX,
299 LPASS_BE_HDMI,
300 LPASS_BE_INT_BT_SCO_RX,
301 LPASS_BE_INT_BT_SCO_TX,
302 LPASS_BE_INT_FM_RX,
303 LPASS_BE_INT_FM_TX,
304};
305
306static struct snd_soc_dsp_link fe_media = {
307 .supported_be = mm_be,
308 .num_be = ARRAY_SIZE(mm_be),
309 .fe_playback_channels = 2,
310 .fe_capture_channels = 1,
311 .trigger = {
312 SND_SOC_DSP_TRIGGER_POST,
313 SND_SOC_DSP_TRIGGER_POST
314 },
315};
316
317static const char *slimbus0_hl_be[] = {
318 LPASS_BE_SLIMBUS_0_RX,
319 LPASS_BE_SLIMBUS_0_TX,
320};
321
322static struct snd_soc_dsp_link slimbus0_hl_media = {
323 .supported_be = slimbus0_hl_be,
324 .num_be = ARRAY_SIZE(slimbus0_hl_be),
325 .fe_playback_channels = 2,
326 .fe_capture_channels = 2,
327 .trigger = {
328 SND_SOC_DSP_TRIGGER_POST,
329 SND_SOC_DSP_TRIGGER_POST
330 },
331};
332
333static const char *int_fm_hl_be[] = {
334 LPASS_BE_INT_FM_RX,
335 LPASS_BE_INT_FM_TX,
336};
337
338static struct snd_soc_dsp_link int_fm_hl_media = {
339 .supported_be = int_fm_hl_be,
340 .num_be = ARRAY_SIZE(int_fm_hl_be),
341 .fe_playback_channels = 2,
342 .fe_capture_channels = 2,
343 .trigger = {
344 SND_SOC_DSP_TRIGGER_POST,
345 SND_SOC_DSP_TRIGGER_POST
346 },
347};
348
349static int msm8960_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
350 struct snd_pcm_hw_params *params)
351{
352 struct snd_interval *rate = hw_param_interval(params,
353 SNDRV_PCM_HW_PARAM_RATE);
354
355 struct snd_interval *channels = hw_param_interval(params,
356 SNDRV_PCM_HW_PARAM_CHANNELS);
357
358 pr_debug("%s()\n", __func__);
359 rate->min = rate->max = 48000;
360 channels->min = channels->max = msm8960_slim_0_rx_ch;
361
362 return 0;
363}
364
365static int msm8960_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
366 struct snd_pcm_hw_params *params)
367{
368 struct snd_interval *rate = hw_param_interval(params,
369 SNDRV_PCM_HW_PARAM_RATE);
370
371 struct snd_interval *channels = hw_param_interval(params,
372 SNDRV_PCM_HW_PARAM_CHANNELS);
373
374 pr_debug("%s()\n", __func__);
375 rate->min = rate->max = 48000;
376 channels->min = channels->max = msm8960_slim_0_tx_ch;
377
378 return 0;
379}
380
381static int msm8960_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
382 struct snd_pcm_hw_params *params)
383{
384 struct snd_interval *rate = hw_param_interval(params,
385 SNDRV_PCM_HW_PARAM_RATE);
386
387 pr_debug("%s()\n", __func__);
388 rate->min = rate->max = 48000;
389
390 return 0;
391}
392
393static int msm8960_startup(struct snd_pcm_substream *substream)
394{
395 if (clk_users++)
396 return 0;
397
398 codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
399 if (codec_clk) {
400 clk_set_rate(codec_clk, 12288000);
401 clk_enable(codec_clk);
402 } else {
403 pr_err("%s: Error setting Tabla MCLK\n", __func__);
404 clk_users--;
405 return -EINVAL;
406 }
407 return 0;
408}
409
410static void msm8960_shutdown(struct snd_pcm_substream *substream)
411{
412 clk_users--;
413 if (!clk_users) {
414 clk_disable(codec_clk);
415 clk_put(codec_clk);
416 }
417}
418
419static struct snd_soc_ops msm8960_be_ops = {
420 .startup = msm8960_startup,
421 .shutdown = msm8960_shutdown,
422};
423
424/* Digital audio interface glue - connects codec <---> CPU */
425static struct snd_soc_dai_link msm8960_dai[] = {
426 /* FrontEnd DAI Links */
427 {
428 .name = "MSM8960 Media1",
429 .stream_name = "MultiMedia1",
430 .cpu_dai_name = "MultiMedia1",
431 .platform_name = "msm-pcm-dsp",
432 .dynamic = 1,
433 .dsp_link = &fe_media,
434 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
435 },
436 {
437 .name = "MSM8960 Media2",
438 .stream_name = "MultiMedia2",
439 .cpu_dai_name = "MultiMedia2",
440 .platform_name = "msm-pcm-dsp",
441 .dynamic = 1,
442 .dsp_link = &fe_media,
443 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
444 },
445 {
446 .name = "Circuit-Switch Voice",
447 .stream_name = "CS-Voice",
448 .cpu_dai_name = "CS-VOICE",
449 .platform_name = "msm-pcm-voice",
450 .dynamic = 1,
451 .dsp_link = &fe_media,
452 .be_id = MSM_FRONTEND_DAI_CS_VOICE,
453 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
454 },
455 {
456 .name = "MSM VoIP",
457 .stream_name = "VoIP",
458 .cpu_dai_name = "VoIP",
459 .platform_name = "msm-voip-dsp",
460 .dynamic = 1,
461 .dsp_link = &fe_media,
462 .be_id = MSM_FRONTEND_DAI_VOIP,
463 },
464 {
465 .name = "MSM8960 LPA",
466 .stream_name = "LPA",
467 .cpu_dai_name = "MultiMedia3",
468 .platform_name = "msm-pcm-lpa",
469 .dynamic = 1,
470 .dsp_link = &lpa_fe_media,
471 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
472 },
473 /* Hostless PMC purpose */
474 {
475 .name = "SLIMBUS_0 Hostless",
476 .stream_name = "SLIMBUS_0 Hostless",
477 .cpu_dai_name = "SLIMBUS0_HOSTLESS",
478 .platform_name = "msm-pcm-hostless",
479 .dynamic = 1,
480 .dsp_link = &slimbus0_hl_media,
481 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
482 /* .be_id = do not care */
483 },
484 {
485 .name = "INT_FM Hostless",
486 .stream_name = "INT_FM Hostless",
487 .cpu_dai_name = "INT_FM_HOSTLESS",
488 .platform_name = "msm-pcm-hostless",
489 .dynamic = 1,
490 .dsp_link = &int_fm_hl_media,
491 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
492 /* .be_id = do not care */
493 },
494 /* Backend DAI Links */
495 {
496 .name = LPASS_BE_SLIMBUS_0_RX,
497 .stream_name = "Slimbus Playback",
498 .cpu_dai_name = "msm-dai-q6.16384",
499 .platform_name = "msm-pcm-routing",
500 .codec_name = "tabla_codec",
501 .codec_dai_name = "tabla_rx1",
502 .no_pcm = 1,
503 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
504 .init = &msm8960_audrx_init,
505 .be_hw_params_fixup = msm8960_slim_0_rx_be_hw_params_fixup,
506 .ops = &msm8960_be_ops,
507 },
508 {
509 .name = LPASS_BE_SLIMBUS_0_TX,
510 .stream_name = "Slimbus Capture",
511 .cpu_dai_name = "msm-dai-q6.16385",
512 .platform_name = "msm-pcm-routing",
513 .codec_name = "tabla_codec",
514 .codec_dai_name = "tabla_tx1",
515 .no_pcm = 1,
516 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
517 .be_hw_params_fixup = msm8960_slim_0_tx_be_hw_params_fixup,
518 .ops = &msm8960_be_ops,
519 },
520 /* Backend BT/FM DAI Links */
521 {
522 .name = LPASS_BE_INT_BT_SCO_RX,
523 .stream_name = "Internal BT-SCO Playback",
524 .cpu_dai_name = "msm-dai-q6.12288",
525 .platform_name = "msm-pcm-routing",
526 .codec_name = "msm-stub-codec.1",
527 .codec_dai_name = "msm-stub-rx",
528 .no_pcm = 1,
529 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
530 },
531 {
532 .name = LPASS_BE_INT_BT_SCO_TX,
533 .stream_name = "Internal BT-SCO Capture",
534 .cpu_dai_name = "msm-dai-q6.12289",
535 .platform_name = "msm-pcm-routing",
536 .codec_name = "msm-stub-codec.1",
537 .codec_dai_name = "msm-stub-tx",
538 .no_pcm = 1,
539 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
540 },
541 {
542 .name = LPASS_BE_INT_FM_RX,
543 .stream_name = "Internal FM Playback",
544 .cpu_dai_name = "msm-dai-q6.12292",
545 .platform_name = "msm-pcm-routing",
546 .codec_name = "msm-stub-codec.1",
547 .codec_dai_name = "msm-stub-rx",
548 .no_pcm = 1,
549 .be_id = MSM_BACKEND_DAI_INT_FM_RX,
550 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
551 },
552 {
553 .name = LPASS_BE_INT_FM_TX,
554 .stream_name = "Internal FM Capture",
555 .cpu_dai_name = "msm-dai-q6.12293",
556 .platform_name = "msm-pcm-routing",
557 .codec_name = "msm-stub-codec.1",
558 .codec_dai_name = "msm-stub-tx",
559 .no_pcm = 1,
560 .be_id = MSM_BACKEND_DAI_INT_FM_TX,
561 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
562 },
563 /* HDMI BACK END DAI Link */
564 {
565 .name = LPASS_BE_HDMI,
566 .stream_name = "HDMI Playback",
567 .cpu_dai_name = "msm-dai-q6.8",
568 .platform_name = "msm-pcm-routing",
569 .codec_name = "msm-stub-codec.1",
570 .codec_dai_name = "msm-stub-rx",
571 .no_pcm = 1,
572 .no_codec = 1,
573 .be_id = MSM_BACKEND_DAI_HDMI_RX,
574 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
575 },
576};
577
578struct snd_soc_card snd_soc_card_msm8960 = {
579 .name = "msm8960-snd-card",
580 .dai_link = msm8960_dai,
581 .num_links = ARRAY_SIZE(msm8960_dai),
582};
583
584static struct platform_device *msm8960_snd_device;
585
586static int msm8960_configure_headset_mic_gpios(void)
587{
588 int ret;
589 struct pm_gpio param = {
590 .direction = PM_GPIO_DIR_OUT,
591 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
592 .output_value = 1,
593 .pull = PM_GPIO_PULL_NO,
594 .vin_sel = PM_GPIO_VIN_S4,
595 .out_strength = PM_GPIO_STRENGTH_MED,
596 .function = PM_GPIO_FUNC_NORMAL,
597 };
598
599 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
600 if (ret) {
601 pr_err("%s: Failed to request gpio %d\n", __func__,
602 PM8921_GPIO_PM_TO_SYS(23));
603 return ret;
604 }
605
606 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
607 if (ret)
608 pr_err("%s: Failed to configure gpio %d\n", __func__,
609 PM8921_GPIO_PM_TO_SYS(23));
610 else
611 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
612
613 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
614 if (ret) {
615 pr_err("%s: Failed to request gpio %d\n", __func__,
616 PM8921_GPIO_PM_TO_SYS(35));
617 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
618 return ret;
619 }
620 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
621 if (ret)
622 pr_err("%s: Failed to configure gpio %d\n", __func__,
623 PM8921_GPIO_PM_TO_SYS(35));
624 else
625 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 1);
626
627 return 0;
628}
629static void msm8960_free_headset_mic_gpios(void)
630{
631 if (msm8960_headset_gpios_configured) {
632 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
633 gpio_free(PM8921_GPIO_PM_TO_SYS(35));
634 }
635}
636
637static int __init msm8960_audio_init(void)
638{
639 int ret;
640
641 msm8960_snd_device = platform_device_alloc("soc-audio", 0);
642 if (!msm8960_snd_device) {
643 pr_err("Platform device allocation failed\n");
644 return -ENOMEM;
645 }
646
647 platform_set_drvdata(msm8960_snd_device, &snd_soc_card_msm8960);
648 ret = platform_device_add(msm8960_snd_device);
649 if (ret) {
650 platform_device_put(msm8960_snd_device);
651 return ret;
652 }
653
654 if (msm8960_configure_headset_mic_gpios()) {
655 pr_err("%s Fail to configure headset mic gpios\n", __func__);
656 msm8960_headset_gpios_configured = 0;
657 } else
658 msm8960_headset_gpios_configured = 1;
659
660 return ret;
661
662}
663module_init(msm8960_audio_init);
664
665static void __exit msm8960_audio_exit(void)
666{
667 msm8960_free_headset_mic_gpios();
668 platform_device_unregister(msm8960_snd_device);
669}
670module_exit(msm8960_audio_exit);
671
672MODULE_DESCRIPTION("ALSA SoC MSM8960");
673MODULE_LICENSE("GPL v2");