blob: b10ed35ae34a7d2f05a1756bba87dd44b5c63272 [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
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700276static struct snd_soc_dsp_link lpa_fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700277 .playback = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278 .trigger = {
279 SND_SOC_DSP_TRIGGER_POST,
280 SND_SOC_DSP_TRIGGER_POST
281 },
282};
283
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284static struct snd_soc_dsp_link fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700285 .playback = true,
286 .capture = 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 slimbus0_hl_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 int_fm_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
311static int msm8960_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
312 struct snd_pcm_hw_params *params)
313{
314 struct snd_interval *rate = hw_param_interval(params,
315 SNDRV_PCM_HW_PARAM_RATE);
316
317 struct snd_interval *channels = hw_param_interval(params,
318 SNDRV_PCM_HW_PARAM_CHANNELS);
319
320 pr_debug("%s()\n", __func__);
321 rate->min = rate->max = 48000;
322 channels->min = channels->max = msm8960_slim_0_rx_ch;
323
324 return 0;
325}
326
327static int msm8960_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
328 struct snd_pcm_hw_params *params)
329{
330 struct snd_interval *rate = hw_param_interval(params,
331 SNDRV_PCM_HW_PARAM_RATE);
332
333 struct snd_interval *channels = hw_param_interval(params,
334 SNDRV_PCM_HW_PARAM_CHANNELS);
335
336 pr_debug("%s()\n", __func__);
337 rate->min = rate->max = 48000;
338 channels->min = channels->max = msm8960_slim_0_tx_ch;
339
340 return 0;
341}
342
343static int msm8960_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
344 struct snd_pcm_hw_params *params)
345{
346 struct snd_interval *rate = hw_param_interval(params,
347 SNDRV_PCM_HW_PARAM_RATE);
348
349 pr_debug("%s()\n", __func__);
350 rate->min = rate->max = 48000;
351
352 return 0;
353}
354
355static int msm8960_startup(struct snd_pcm_substream *substream)
356{
357 if (clk_users++)
358 return 0;
359
360 codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
361 if (codec_clk) {
362 clk_set_rate(codec_clk, 12288000);
363 clk_enable(codec_clk);
364 } else {
365 pr_err("%s: Error setting Tabla MCLK\n", __func__);
366 clk_users--;
367 return -EINVAL;
368 }
369 return 0;
370}
371
372static void msm8960_shutdown(struct snd_pcm_substream *substream)
373{
374 clk_users--;
375 if (!clk_users) {
376 clk_disable(codec_clk);
377 clk_put(codec_clk);
378 }
379}
380
381static struct snd_soc_ops msm8960_be_ops = {
382 .startup = msm8960_startup,
383 .shutdown = msm8960_shutdown,
384};
385
386/* Digital audio interface glue - connects codec <---> CPU */
387static struct snd_soc_dai_link msm8960_dai[] = {
388 /* FrontEnd DAI Links */
389 {
390 .name = "MSM8960 Media1",
391 .stream_name = "MultiMedia1",
392 .cpu_dai_name = "MultiMedia1",
393 .platform_name = "msm-pcm-dsp",
394 .dynamic = 1,
395 .dsp_link = &fe_media,
396 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
397 },
398 {
399 .name = "MSM8960 Media2",
400 .stream_name = "MultiMedia2",
401 .cpu_dai_name = "MultiMedia2",
402 .platform_name = "msm-pcm-dsp",
403 .dynamic = 1,
404 .dsp_link = &fe_media,
405 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
406 },
407 {
408 .name = "Circuit-Switch Voice",
409 .stream_name = "CS-Voice",
410 .cpu_dai_name = "CS-VOICE",
411 .platform_name = "msm-pcm-voice",
412 .dynamic = 1,
413 .dsp_link = &fe_media,
414 .be_id = MSM_FRONTEND_DAI_CS_VOICE,
415 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
416 },
417 {
418 .name = "MSM VoIP",
419 .stream_name = "VoIP",
420 .cpu_dai_name = "VoIP",
421 .platform_name = "msm-voip-dsp",
422 .dynamic = 1,
423 .dsp_link = &fe_media,
424 .be_id = MSM_FRONTEND_DAI_VOIP,
425 },
426 {
427 .name = "MSM8960 LPA",
428 .stream_name = "LPA",
429 .cpu_dai_name = "MultiMedia3",
430 .platform_name = "msm-pcm-lpa",
431 .dynamic = 1,
432 .dsp_link = &lpa_fe_media,
433 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
434 },
435 /* Hostless PMC purpose */
436 {
437 .name = "SLIMBUS_0 Hostless",
438 .stream_name = "SLIMBUS_0 Hostless",
439 .cpu_dai_name = "SLIMBUS0_HOSTLESS",
440 .platform_name = "msm-pcm-hostless",
441 .dynamic = 1,
442 .dsp_link = &slimbus0_hl_media,
443 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
444 /* .be_id = do not care */
445 },
446 {
447 .name = "INT_FM Hostless",
448 .stream_name = "INT_FM Hostless",
449 .cpu_dai_name = "INT_FM_HOSTLESS",
450 .platform_name = "msm-pcm-hostless",
451 .dynamic = 1,
452 .dsp_link = &int_fm_hl_media,
453 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
454 /* .be_id = do not care */
455 },
456 /* Backend DAI Links */
457 {
458 .name = LPASS_BE_SLIMBUS_0_RX,
459 .stream_name = "Slimbus Playback",
460 .cpu_dai_name = "msm-dai-q6.16384",
461 .platform_name = "msm-pcm-routing",
462 .codec_name = "tabla_codec",
463 .codec_dai_name = "tabla_rx1",
464 .no_pcm = 1,
465 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
466 .init = &msm8960_audrx_init,
467 .be_hw_params_fixup = msm8960_slim_0_rx_be_hw_params_fixup,
468 .ops = &msm8960_be_ops,
469 },
470 {
471 .name = LPASS_BE_SLIMBUS_0_TX,
472 .stream_name = "Slimbus Capture",
473 .cpu_dai_name = "msm-dai-q6.16385",
474 .platform_name = "msm-pcm-routing",
475 .codec_name = "tabla_codec",
476 .codec_dai_name = "tabla_tx1",
477 .no_pcm = 1,
478 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
479 .be_hw_params_fixup = msm8960_slim_0_tx_be_hw_params_fixup,
480 .ops = &msm8960_be_ops,
481 },
482 /* Backend BT/FM DAI Links */
483 {
484 .name = LPASS_BE_INT_BT_SCO_RX,
485 .stream_name = "Internal BT-SCO Playback",
486 .cpu_dai_name = "msm-dai-q6.12288",
487 .platform_name = "msm-pcm-routing",
488 .codec_name = "msm-stub-codec.1",
489 .codec_dai_name = "msm-stub-rx",
490 .no_pcm = 1,
491 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
492 },
493 {
494 .name = LPASS_BE_INT_BT_SCO_TX,
495 .stream_name = "Internal BT-SCO Capture",
496 .cpu_dai_name = "msm-dai-q6.12289",
497 .platform_name = "msm-pcm-routing",
498 .codec_name = "msm-stub-codec.1",
499 .codec_dai_name = "msm-stub-tx",
500 .no_pcm = 1,
501 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
502 },
503 {
504 .name = LPASS_BE_INT_FM_RX,
505 .stream_name = "Internal FM Playback",
506 .cpu_dai_name = "msm-dai-q6.12292",
507 .platform_name = "msm-pcm-routing",
508 .codec_name = "msm-stub-codec.1",
509 .codec_dai_name = "msm-stub-rx",
510 .no_pcm = 1,
511 .be_id = MSM_BACKEND_DAI_INT_FM_RX,
512 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
513 },
514 {
515 .name = LPASS_BE_INT_FM_TX,
516 .stream_name = "Internal FM Capture",
517 .cpu_dai_name = "msm-dai-q6.12293",
518 .platform_name = "msm-pcm-routing",
519 .codec_name = "msm-stub-codec.1",
520 .codec_dai_name = "msm-stub-tx",
521 .no_pcm = 1,
522 .be_id = MSM_BACKEND_DAI_INT_FM_TX,
523 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
524 },
525 /* HDMI BACK END DAI Link */
526 {
527 .name = LPASS_BE_HDMI,
528 .stream_name = "HDMI Playback",
529 .cpu_dai_name = "msm-dai-q6.8",
530 .platform_name = "msm-pcm-routing",
531 .codec_name = "msm-stub-codec.1",
532 .codec_dai_name = "msm-stub-rx",
533 .no_pcm = 1,
534 .no_codec = 1,
535 .be_id = MSM_BACKEND_DAI_HDMI_RX,
536 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
537 },
538};
539
540struct snd_soc_card snd_soc_card_msm8960 = {
541 .name = "msm8960-snd-card",
542 .dai_link = msm8960_dai,
543 .num_links = ARRAY_SIZE(msm8960_dai),
544};
545
546static struct platform_device *msm8960_snd_device;
547
548static int msm8960_configure_headset_mic_gpios(void)
549{
550 int ret;
551 struct pm_gpio param = {
552 .direction = PM_GPIO_DIR_OUT,
553 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
554 .output_value = 1,
555 .pull = PM_GPIO_PULL_NO,
556 .vin_sel = PM_GPIO_VIN_S4,
557 .out_strength = PM_GPIO_STRENGTH_MED,
558 .function = PM_GPIO_FUNC_NORMAL,
559 };
560
561 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
562 if (ret) {
563 pr_err("%s: Failed to request gpio %d\n", __func__,
564 PM8921_GPIO_PM_TO_SYS(23));
565 return ret;
566 }
567
568 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
569 if (ret)
570 pr_err("%s: Failed to configure gpio %d\n", __func__,
571 PM8921_GPIO_PM_TO_SYS(23));
572 else
573 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
574
575 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
576 if (ret) {
577 pr_err("%s: Failed to request gpio %d\n", __func__,
578 PM8921_GPIO_PM_TO_SYS(35));
579 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
580 return ret;
581 }
582 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
583 if (ret)
584 pr_err("%s: Failed to configure gpio %d\n", __func__,
585 PM8921_GPIO_PM_TO_SYS(35));
586 else
587 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 1);
588
589 return 0;
590}
591static void msm8960_free_headset_mic_gpios(void)
592{
593 if (msm8960_headset_gpios_configured) {
594 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
595 gpio_free(PM8921_GPIO_PM_TO_SYS(35));
596 }
597}
598
599static int __init msm8960_audio_init(void)
600{
601 int ret;
602
603 msm8960_snd_device = platform_device_alloc("soc-audio", 0);
604 if (!msm8960_snd_device) {
605 pr_err("Platform device allocation failed\n");
606 return -ENOMEM;
607 }
608
609 platform_set_drvdata(msm8960_snd_device, &snd_soc_card_msm8960);
610 ret = platform_device_add(msm8960_snd_device);
611 if (ret) {
612 platform_device_put(msm8960_snd_device);
613 return ret;
614 }
615
616 if (msm8960_configure_headset_mic_gpios()) {
617 pr_err("%s Fail to configure headset mic gpios\n", __func__);
618 msm8960_headset_gpios_configured = 0;
619 } else
620 msm8960_headset_gpios_configured = 1;
621
622 return ret;
623
624}
625module_init(msm8960_audio_init);
626
627static void __exit msm8960_audio_exit(void)
628{
629 msm8960_free_headset_mic_gpios();
630 platform_device_unregister(msm8960_snd_device);
631}
632module_exit(msm8960_audio_exit);
633
634MODULE_DESCRIPTION("ALSA SoC MSM8960");
635MODULE_LICENSE("GPL v2");