blob: f490e280e3770e5a6a8bcd61c91ae91601cde17a [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;
Bradley Rubincb1e2732011-06-23 16:49:20 -070064static struct snd_soc_jack button_jack;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065
66static void codec_poweramp_on(void)
67{
68 int ret = 0;
69
70 struct pm_gpio param = {
71 .direction = PM_GPIO_DIR_OUT,
72 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
73 .output_value = 1,
74 .pull = PM_GPIO_PULL_NO,
75 .vin_sel = PM_GPIO_VIN_S4,
76 .out_strength = PM_GPIO_STRENGTH_MED,
77 .function = PM_GPIO_FUNC_NORMAL,
78 };
79
80 if (msm8960_pamp_on)
81 return;
82
83 pr_debug("%s: enable stereo spkr amp\n", __func__);
84 ret = gpio_request(MSM_CDC_PAMPL, "CDC PAMP1");
85 if (ret) {
86 pr_err("%s: Error requesting GPIO %d\n", __func__,
87 MSM_CDC_PAMPL);
88 return;
89 }
90 ret = pm8xxx_gpio_config(MSM_CDC_PAMPL, &param);
91 if (ret)
92 pr_err("%s: Failed to configure gpio %d\n", __func__,
93 MSM_CDC_PAMPL);
94 else
95 gpio_direction_output(MSM_CDC_PAMPL, 1);
96
97 ret = gpio_request(MSM_CDC_PAMPR, "CDC PAMPL");
98 if (ret) {
99 pr_err("%s: Error requesting GPIO %d\n", __func__,
100 MSM_CDC_PAMPR);
101 gpio_free(MSM_CDC_PAMPL);
102 return;
103 }
104 ret = pm8xxx_gpio_config(MSM_CDC_PAMPR, &param);
105 if (ret)
106 pr_err("%s: Failed to configure gpio %d\n", __func__,
107 MSM_CDC_PAMPR);
108 else
109 gpio_direction_output(MSM_CDC_PAMPR, 1);
110
111 msm8960_pamp_on = 1;
112}
113static void codec_poweramp_off(void)
114{
115 if (!msm8960_pamp_on)
116 return;
117
118 pr_debug("%s: disable stereo spkr amp\n", __func__);
119 gpio_direction_output(MSM_CDC_PAMPL, 0);
120 gpio_free(MSM_CDC_PAMPL);
121 gpio_direction_output(MSM_CDC_PAMPR, 0);
122 gpio_free(MSM_CDC_PAMPR);
123 msm8960_pamp_on = 0;
124}
125static void msm8960_ext_control(struct snd_soc_codec *codec)
126{
127 struct snd_soc_dapm_context *dapm = &codec->dapm;
128
129 pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
130 if (msm8960_spk_control == MSM8960_SPK_ON)
131 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
132 else
133 snd_soc_dapm_disable_pin(dapm, "Ext Spk");
134
135 snd_soc_dapm_sync(dapm);
136}
137
138static int msm8960_get_spk(struct snd_kcontrol *kcontrol,
139 struct snd_ctl_elem_value *ucontrol)
140{
141 pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
142 ucontrol->value.integer.value[0] = msm8960_spk_control;
143 return 0;
144}
145static int msm8960_set_spk(struct snd_kcontrol *kcontrol,
146 struct snd_ctl_elem_value *ucontrol)
147{
148 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
149
150 pr_debug("%s()\n", __func__);
151 if (msm8960_spk_control == ucontrol->value.integer.value[0])
152 return 0;
153
154 msm8960_spk_control = ucontrol->value.integer.value[0];
155 msm8960_ext_control(codec);
156 return 1;
157}
158static int msm8960_spkramp_event(struct snd_soc_dapm_widget *w,
159 struct snd_kcontrol *k, int event)
160{
161 pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
162 if (SND_SOC_DAPM_EVENT_ON(event))
163 codec_poweramp_on();
164 else
165 codec_poweramp_off();
166 return 0;
167}
168
169static const struct snd_soc_dapm_widget msm8960_dapm_widgets[] = {
170 SND_SOC_DAPM_SPK("Ext Spk", msm8960_spkramp_event),
171 SND_SOC_DAPM_MIC("Handset Mic", NULL),
172 SND_SOC_DAPM_MIC("Headset Mic", NULL),
173 SND_SOC_DAPM_MIC("Digital Mic1", NULL),
174};
175
176static const struct snd_soc_dapm_route audio_map[] = {
177 /* Speaker path */
178 {"Ext Spk", NULL, "LINEOUT"},
179
180 /* Microphone path */
181 {"AMIC1", NULL, "MIC BIAS1 Internal"},
182 {"DMIC1 IN", NULL, "MIC BIAS1 External"},
183 {"AMIC2", NULL, "MIC BIAS2 External"},
184 {"MIC BIAS1 Internal", NULL, "Handset Mic"},
185 {"MIC BIAS1 External", NULL, "Digital Mic1"},
186 {"MIC BIAS2 External", NULL, "Headset Mic"},
187};
188
189static const char *spk_function[] = {"Off", "On"};
190static const struct soc_enum msm8960_enum[] = {
191 SOC_ENUM_SINGLE_EXT(2, spk_function),
192};
193
194static int msm8960_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
195 struct snd_ctl_elem_value *ucontrol)
196{
197 pr_debug("%s: msm8960_slim_0_rx_ch = %d", __func__,
198 msm8960_slim_0_rx_ch);
199 ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch;
200 return 0;
201}
202
203static int msm8960_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
204 struct snd_ctl_elem_value *ucontrol)
205{
206 msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0];
207
208 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
209 msm8960_slim_0_rx_ch);
210 return 1;
211}
212
213static int msm8960_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
214 struct snd_ctl_elem_value *ucontrol)
215{
216 pr_debug("%s: msm8960_slim_0_tx_ch = %d", __func__,
217 msm8960_slim_0_tx_ch);
218 ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch;
219 return 0;
220}
221
222static int msm8960_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
223 struct snd_ctl_elem_value *ucontrol)
224{
225 msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0];
226
227 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
228 msm8960_slim_0_tx_ch);
229 return 1;
230}
231
232
233static const struct snd_kcontrol_new tabla_msm8960_controls[] = {
234 SOC_ENUM_EXT("Speaker Function", msm8960_enum[0], msm8960_get_spk,
235 msm8960_set_spk),
236 SOC_SINGLE_EXT("SLIM_0_RX Channels", 0, 0,
237 msm8960_SLIM_0_RX_MAX_CHANNELS, 0,
238 msm8960_slim_0_rx_ch_get, msm8960_slim_0_rx_ch_put),
239 SOC_SINGLE_EXT("SLIM_0_TX Channels", 0, 0,
240 msm8960_SLIM_0_TX_MAX_CHANNELS, 0,
241 msm8960_slim_0_tx_ch_get, msm8960_slim_0_tx_ch_put),
242};
243
244static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
245{
246 int err;
247 struct snd_soc_codec *codec = rtd->codec;
248 struct snd_soc_dapm_context *dapm = &codec->dapm;
249
250 pr_debug("%s()\n", __func__);
251
252 err = snd_soc_add_controls(codec, tabla_msm8960_controls,
253 ARRAY_SIZE(tabla_msm8960_controls));
254 if (err < 0)
255 return err;
256
257 snd_soc_dapm_new_controls(dapm, msm8960_dapm_widgets,
258 ARRAY_SIZE(msm8960_dapm_widgets));
259
260 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
261
262 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
263
264 snd_soc_dapm_sync(dapm);
265
266 err = snd_soc_jack_new(codec, "Headset Jack",
267 SND_JACK_HEADSET, &hs_jack);
268 if (err) {
269 pr_err("failed to create new jack\n");
270 return err;
271 }
Bradley Rubincb1e2732011-06-23 16:49:20 -0700272
273 err = snd_soc_jack_new(codec, "Button Jack",
274 SND_JACK_BTN_0, &button_jack);
275 if (err) {
276 pr_err("failed to create new jack\n");
277 return err;
278 }
279
280 tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_cal);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281
282 return 0;
283}
284
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285static struct snd_soc_dsp_link lpa_fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700286 .playback = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287 .trigger = {
288 SND_SOC_DSP_TRIGGER_POST,
289 SND_SOC_DSP_TRIGGER_POST
290 },
291};
292
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293static struct snd_soc_dsp_link fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700294 .playback = true,
295 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296 .trigger = {
297 SND_SOC_DSP_TRIGGER_POST,
298 SND_SOC_DSP_TRIGGER_POST
299 },
300};
301
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700302static struct snd_soc_dsp_link slimbus0_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700303 .playback = true,
304 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305 .trigger = {
306 SND_SOC_DSP_TRIGGER_POST,
307 SND_SOC_DSP_TRIGGER_POST
308 },
309};
310
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700311static struct snd_soc_dsp_link int_fm_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700312 .playback = true,
313 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700314 .trigger = {
315 SND_SOC_DSP_TRIGGER_POST,
316 SND_SOC_DSP_TRIGGER_POST
317 },
318};
319
320static int msm8960_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
321 struct snd_pcm_hw_params *params)
322{
323 struct snd_interval *rate = hw_param_interval(params,
324 SNDRV_PCM_HW_PARAM_RATE);
325
326 struct snd_interval *channels = hw_param_interval(params,
327 SNDRV_PCM_HW_PARAM_CHANNELS);
328
329 pr_debug("%s()\n", __func__);
330 rate->min = rate->max = 48000;
331 channels->min = channels->max = msm8960_slim_0_rx_ch;
332
333 return 0;
334}
335
336static int msm8960_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
337 struct snd_pcm_hw_params *params)
338{
339 struct snd_interval *rate = hw_param_interval(params,
340 SNDRV_PCM_HW_PARAM_RATE);
341
342 struct snd_interval *channels = hw_param_interval(params,
343 SNDRV_PCM_HW_PARAM_CHANNELS);
344
345 pr_debug("%s()\n", __func__);
346 rate->min = rate->max = 48000;
347 channels->min = channels->max = msm8960_slim_0_tx_ch;
348
349 return 0;
350}
351
352static int msm8960_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
353 struct snd_pcm_hw_params *params)
354{
355 struct snd_interval *rate = hw_param_interval(params,
356 SNDRV_PCM_HW_PARAM_RATE);
357
358 pr_debug("%s()\n", __func__);
359 rate->min = rate->max = 48000;
360
361 return 0;
362}
363
364static int msm8960_startup(struct snd_pcm_substream *substream)
365{
366 if (clk_users++)
367 return 0;
368
369 codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
370 if (codec_clk) {
371 clk_set_rate(codec_clk, 12288000);
372 clk_enable(codec_clk);
373 } else {
374 pr_err("%s: Error setting Tabla MCLK\n", __func__);
375 clk_users--;
376 return -EINVAL;
377 }
378 return 0;
379}
380
381static void msm8960_shutdown(struct snd_pcm_substream *substream)
382{
383 clk_users--;
384 if (!clk_users) {
385 clk_disable(codec_clk);
386 clk_put(codec_clk);
387 }
388}
389
390static struct snd_soc_ops msm8960_be_ops = {
391 .startup = msm8960_startup,
392 .shutdown = msm8960_shutdown,
393};
394
395/* Digital audio interface glue - connects codec <---> CPU */
396static struct snd_soc_dai_link msm8960_dai[] = {
397 /* FrontEnd DAI Links */
398 {
399 .name = "MSM8960 Media1",
400 .stream_name = "MultiMedia1",
401 .cpu_dai_name = "MultiMedia1",
402 .platform_name = "msm-pcm-dsp",
403 .dynamic = 1,
404 .dsp_link = &fe_media,
405 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
406 },
407 {
408 .name = "MSM8960 Media2",
409 .stream_name = "MultiMedia2",
410 .cpu_dai_name = "MultiMedia2",
411 .platform_name = "msm-pcm-dsp",
412 .dynamic = 1,
413 .dsp_link = &fe_media,
414 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
415 },
416 {
417 .name = "Circuit-Switch Voice",
418 .stream_name = "CS-Voice",
419 .cpu_dai_name = "CS-VOICE",
420 .platform_name = "msm-pcm-voice",
421 .dynamic = 1,
422 .dsp_link = &fe_media,
423 .be_id = MSM_FRONTEND_DAI_CS_VOICE,
424 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
425 },
426 {
427 .name = "MSM VoIP",
428 .stream_name = "VoIP",
429 .cpu_dai_name = "VoIP",
430 .platform_name = "msm-voip-dsp",
431 .dynamic = 1,
432 .dsp_link = &fe_media,
433 .be_id = MSM_FRONTEND_DAI_VOIP,
434 },
435 {
436 .name = "MSM8960 LPA",
437 .stream_name = "LPA",
438 .cpu_dai_name = "MultiMedia3",
439 .platform_name = "msm-pcm-lpa",
440 .dynamic = 1,
441 .dsp_link = &lpa_fe_media,
442 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
443 },
444 /* Hostless PMC purpose */
445 {
446 .name = "SLIMBUS_0 Hostless",
447 .stream_name = "SLIMBUS_0 Hostless",
448 .cpu_dai_name = "SLIMBUS0_HOSTLESS",
449 .platform_name = "msm-pcm-hostless",
450 .dynamic = 1,
451 .dsp_link = &slimbus0_hl_media,
452 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
453 /* .be_id = do not care */
454 },
455 {
456 .name = "INT_FM Hostless",
457 .stream_name = "INT_FM Hostless",
458 .cpu_dai_name = "INT_FM_HOSTLESS",
459 .platform_name = "msm-pcm-hostless",
460 .dynamic = 1,
461 .dsp_link = &int_fm_hl_media,
462 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
463 /* .be_id = do not care */
464 },
465 /* Backend DAI Links */
466 {
467 .name = LPASS_BE_SLIMBUS_0_RX,
468 .stream_name = "Slimbus Playback",
469 .cpu_dai_name = "msm-dai-q6.16384",
470 .platform_name = "msm-pcm-routing",
471 .codec_name = "tabla_codec",
472 .codec_dai_name = "tabla_rx1",
473 .no_pcm = 1,
474 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
475 .init = &msm8960_audrx_init,
476 .be_hw_params_fixup = msm8960_slim_0_rx_be_hw_params_fixup,
477 .ops = &msm8960_be_ops,
478 },
479 {
480 .name = LPASS_BE_SLIMBUS_0_TX,
481 .stream_name = "Slimbus Capture",
482 .cpu_dai_name = "msm-dai-q6.16385",
483 .platform_name = "msm-pcm-routing",
484 .codec_name = "tabla_codec",
485 .codec_dai_name = "tabla_tx1",
486 .no_pcm = 1,
487 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
488 .be_hw_params_fixup = msm8960_slim_0_tx_be_hw_params_fixup,
489 .ops = &msm8960_be_ops,
490 },
491 /* Backend BT/FM DAI Links */
492 {
493 .name = LPASS_BE_INT_BT_SCO_RX,
494 .stream_name = "Internal BT-SCO Playback",
495 .cpu_dai_name = "msm-dai-q6.12288",
496 .platform_name = "msm-pcm-routing",
497 .codec_name = "msm-stub-codec.1",
498 .codec_dai_name = "msm-stub-rx",
499 .no_pcm = 1,
500 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
501 },
502 {
503 .name = LPASS_BE_INT_BT_SCO_TX,
504 .stream_name = "Internal BT-SCO Capture",
505 .cpu_dai_name = "msm-dai-q6.12289",
506 .platform_name = "msm-pcm-routing",
507 .codec_name = "msm-stub-codec.1",
508 .codec_dai_name = "msm-stub-tx",
509 .no_pcm = 1,
510 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
511 },
512 {
513 .name = LPASS_BE_INT_FM_RX,
514 .stream_name = "Internal FM Playback",
515 .cpu_dai_name = "msm-dai-q6.12292",
516 .platform_name = "msm-pcm-routing",
517 .codec_name = "msm-stub-codec.1",
518 .codec_dai_name = "msm-stub-rx",
519 .no_pcm = 1,
520 .be_id = MSM_BACKEND_DAI_INT_FM_RX,
521 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
522 },
523 {
524 .name = LPASS_BE_INT_FM_TX,
525 .stream_name = "Internal FM Capture",
526 .cpu_dai_name = "msm-dai-q6.12293",
527 .platform_name = "msm-pcm-routing",
528 .codec_name = "msm-stub-codec.1",
529 .codec_dai_name = "msm-stub-tx",
530 .no_pcm = 1,
531 .be_id = MSM_BACKEND_DAI_INT_FM_TX,
532 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
533 },
534 /* HDMI BACK END DAI Link */
535 {
536 .name = LPASS_BE_HDMI,
537 .stream_name = "HDMI Playback",
538 .cpu_dai_name = "msm-dai-q6.8",
539 .platform_name = "msm-pcm-routing",
540 .codec_name = "msm-stub-codec.1",
541 .codec_dai_name = "msm-stub-rx",
542 .no_pcm = 1,
543 .no_codec = 1,
544 .be_id = MSM_BACKEND_DAI_HDMI_RX,
545 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
546 },
547};
548
549struct snd_soc_card snd_soc_card_msm8960 = {
550 .name = "msm8960-snd-card",
551 .dai_link = msm8960_dai,
552 .num_links = ARRAY_SIZE(msm8960_dai),
553};
554
555static struct platform_device *msm8960_snd_device;
556
557static int msm8960_configure_headset_mic_gpios(void)
558{
559 int ret;
560 struct pm_gpio param = {
561 .direction = PM_GPIO_DIR_OUT,
562 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
563 .output_value = 1,
564 .pull = PM_GPIO_PULL_NO,
565 .vin_sel = PM_GPIO_VIN_S4,
566 .out_strength = PM_GPIO_STRENGTH_MED,
567 .function = PM_GPIO_FUNC_NORMAL,
568 };
569
570 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
571 if (ret) {
572 pr_err("%s: Failed to request gpio %d\n", __func__,
573 PM8921_GPIO_PM_TO_SYS(23));
574 return ret;
575 }
576
577 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
578 if (ret)
579 pr_err("%s: Failed to configure gpio %d\n", __func__,
580 PM8921_GPIO_PM_TO_SYS(23));
581 else
582 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
583
584 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
585 if (ret) {
586 pr_err("%s: Failed to request gpio %d\n", __func__,
587 PM8921_GPIO_PM_TO_SYS(35));
588 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
589 return ret;
590 }
591 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
592 if (ret)
593 pr_err("%s: Failed to configure gpio %d\n", __func__,
594 PM8921_GPIO_PM_TO_SYS(35));
595 else
596 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 1);
597
598 return 0;
599}
600static void msm8960_free_headset_mic_gpios(void)
601{
602 if (msm8960_headset_gpios_configured) {
603 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
604 gpio_free(PM8921_GPIO_PM_TO_SYS(35));
605 }
606}
607
608static int __init msm8960_audio_init(void)
609{
610 int ret;
611
612 msm8960_snd_device = platform_device_alloc("soc-audio", 0);
613 if (!msm8960_snd_device) {
614 pr_err("Platform device allocation failed\n");
615 return -ENOMEM;
616 }
617
618 platform_set_drvdata(msm8960_snd_device, &snd_soc_card_msm8960);
619 ret = platform_device_add(msm8960_snd_device);
620 if (ret) {
621 platform_device_put(msm8960_snd_device);
622 return ret;
623 }
624
625 if (msm8960_configure_headset_mic_gpios()) {
626 pr_err("%s Fail to configure headset mic gpios\n", __func__);
627 msm8960_headset_gpios_configured = 0;
628 } else
629 msm8960_headset_gpios_configured = 1;
630
631 return ret;
632
633}
634module_init(msm8960_audio_init);
635
636static void __exit msm8960_audio_exit(void)
637{
638 msm8960_free_headset_mic_gpios();
639 platform_device_unregister(msm8960_snd_device);
640}
641module_exit(msm8960_audio_exit);
642
643MODULE_DESCRIPTION("ALSA SoC MSM8960");
644MODULE_LICENSE("GPL v2");