blob: e234554b64e891327e15040c7f3b5f75ffc15839 [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),
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700174 SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
175 SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176};
177
178static const struct snd_soc_dapm_route audio_map[] = {
179 /* Speaker path */
180 {"Ext Spk", NULL, "LINEOUT"},
181
182 /* Microphone path */
183 {"AMIC1", NULL, "MIC BIAS1 Internal"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184 {"MIC BIAS1 Internal", NULL, "Handset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700185
186 {"AMIC2", NULL, "MIC BIAS2 External"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187 {"MIC BIAS2 External", NULL, "Headset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700188
189 {"AMIC3", NULL, "MIC BIAS3 External"},
190 {"MIC BIAS3 External", NULL, "ANCRight Headset Mic"},
191
192 {"AMIC4", NULL, "MIC BIAS4 External"},
193 {"MIC BIAS4 External", NULL, "ANCLeft Headset Mic"},
194
195 {"DMIC1 IN", NULL, "MIC BIAS1 External"},
196 {"MIC BIAS1 External", NULL, "Digital Mic1"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197};
198
199static const char *spk_function[] = {"Off", "On"};
200static const struct soc_enum msm8960_enum[] = {
201 SOC_ENUM_SINGLE_EXT(2, spk_function),
202};
203
204static int msm8960_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
205 struct snd_ctl_elem_value *ucontrol)
206{
207 pr_debug("%s: msm8960_slim_0_rx_ch = %d", __func__,
208 msm8960_slim_0_rx_ch);
209 ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch;
210 return 0;
211}
212
213static int msm8960_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
214 struct snd_ctl_elem_value *ucontrol)
215{
216 msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0];
217
218 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
219 msm8960_slim_0_rx_ch);
220 return 1;
221}
222
223static int msm8960_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
224 struct snd_ctl_elem_value *ucontrol)
225{
226 pr_debug("%s: msm8960_slim_0_tx_ch = %d", __func__,
227 msm8960_slim_0_tx_ch);
228 ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch;
229 return 0;
230}
231
232static int msm8960_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
233 struct snd_ctl_elem_value *ucontrol)
234{
235 msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0];
236
237 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
238 msm8960_slim_0_tx_ch);
239 return 1;
240}
241
242
243static const struct snd_kcontrol_new tabla_msm8960_controls[] = {
244 SOC_ENUM_EXT("Speaker Function", msm8960_enum[0], msm8960_get_spk,
245 msm8960_set_spk),
246 SOC_SINGLE_EXT("SLIM_0_RX Channels", 0, 0,
247 msm8960_SLIM_0_RX_MAX_CHANNELS, 0,
248 msm8960_slim_0_rx_ch_get, msm8960_slim_0_rx_ch_put),
249 SOC_SINGLE_EXT("SLIM_0_TX Channels", 0, 0,
250 msm8960_SLIM_0_TX_MAX_CHANNELS, 0,
251 msm8960_slim_0_tx_ch_get, msm8960_slim_0_tx_ch_put),
252};
253
254static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
255{
256 int err;
257 struct snd_soc_codec *codec = rtd->codec;
258 struct snd_soc_dapm_context *dapm = &codec->dapm;
259
260 pr_debug("%s()\n", __func__);
261
262 err = snd_soc_add_controls(codec, tabla_msm8960_controls,
263 ARRAY_SIZE(tabla_msm8960_controls));
264 if (err < 0)
265 return err;
266
267 snd_soc_dapm_new_controls(dapm, msm8960_dapm_widgets,
268 ARRAY_SIZE(msm8960_dapm_widgets));
269
270 snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
271
272 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
273
274 snd_soc_dapm_sync(dapm);
275
276 err = snd_soc_jack_new(codec, "Headset Jack",
277 SND_JACK_HEADSET, &hs_jack);
278 if (err) {
279 pr_err("failed to create new jack\n");
280 return err;
281 }
Bradley Rubincb1e2732011-06-23 16:49:20 -0700282
283 err = snd_soc_jack_new(codec, "Button Jack",
284 SND_JACK_BTN_0, &button_jack);
285 if (err) {
286 pr_err("failed to create new jack\n");
287 return err;
288 }
289
290 tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_cal);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291
292 return 0;
293}
294
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295static struct snd_soc_dsp_link lpa_fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700296 .playback = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297 .trigger = {
298 SND_SOC_DSP_TRIGGER_POST,
299 SND_SOC_DSP_TRIGGER_POST
300 },
301};
302
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303static struct snd_soc_dsp_link fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700304 .playback = true,
305 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700306 .trigger = {
307 SND_SOC_DSP_TRIGGER_POST,
308 SND_SOC_DSP_TRIGGER_POST
309 },
310};
311
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700312static struct snd_soc_dsp_link slimbus0_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700313 .playback = true,
314 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700315 .trigger = {
316 SND_SOC_DSP_TRIGGER_POST,
317 SND_SOC_DSP_TRIGGER_POST
318 },
319};
320
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321static struct snd_soc_dsp_link int_fm_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700322 .playback = true,
323 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324 .trigger = {
325 SND_SOC_DSP_TRIGGER_POST,
326 SND_SOC_DSP_TRIGGER_POST
327 },
328};
329
330static int msm8960_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
331 struct snd_pcm_hw_params *params)
332{
333 struct snd_interval *rate = hw_param_interval(params,
334 SNDRV_PCM_HW_PARAM_RATE);
335
336 struct snd_interval *channels = hw_param_interval(params,
337 SNDRV_PCM_HW_PARAM_CHANNELS);
338
339 pr_debug("%s()\n", __func__);
340 rate->min = rate->max = 48000;
341 channels->min = channels->max = msm8960_slim_0_rx_ch;
342
343 return 0;
344}
345
346static int msm8960_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
347 struct snd_pcm_hw_params *params)
348{
349 struct snd_interval *rate = hw_param_interval(params,
350 SNDRV_PCM_HW_PARAM_RATE);
351
352 struct snd_interval *channels = hw_param_interval(params,
353 SNDRV_PCM_HW_PARAM_CHANNELS);
354
355 pr_debug("%s()\n", __func__);
356 rate->min = rate->max = 48000;
357 channels->min = channels->max = msm8960_slim_0_tx_ch;
358
359 return 0;
360}
361
362static int msm8960_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
363 struct snd_pcm_hw_params *params)
364{
365 struct snd_interval *rate = hw_param_interval(params,
366 SNDRV_PCM_HW_PARAM_RATE);
367
368 pr_debug("%s()\n", __func__);
369 rate->min = rate->max = 48000;
370
371 return 0;
372}
373
374static int msm8960_startup(struct snd_pcm_substream *substream)
375{
376 if (clk_users++)
377 return 0;
378
379 codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
380 if (codec_clk) {
381 clk_set_rate(codec_clk, 12288000);
382 clk_enable(codec_clk);
383 } else {
384 pr_err("%s: Error setting Tabla MCLK\n", __func__);
385 clk_users--;
386 return -EINVAL;
387 }
388 return 0;
389}
390
391static void msm8960_shutdown(struct snd_pcm_substream *substream)
392{
393 clk_users--;
394 if (!clk_users) {
395 clk_disable(codec_clk);
396 clk_put(codec_clk);
397 }
398}
399
400static struct snd_soc_ops msm8960_be_ops = {
401 .startup = msm8960_startup,
402 .shutdown = msm8960_shutdown,
403};
404
405/* Digital audio interface glue - connects codec <---> CPU */
406static struct snd_soc_dai_link msm8960_dai[] = {
407 /* FrontEnd DAI Links */
408 {
409 .name = "MSM8960 Media1",
410 .stream_name = "MultiMedia1",
411 .cpu_dai_name = "MultiMedia1",
412 .platform_name = "msm-pcm-dsp",
413 .dynamic = 1,
414 .dsp_link = &fe_media,
415 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
416 },
417 {
418 .name = "MSM8960 Media2",
419 .stream_name = "MultiMedia2",
420 .cpu_dai_name = "MultiMedia2",
421 .platform_name = "msm-pcm-dsp",
422 .dynamic = 1,
423 .dsp_link = &fe_media,
424 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
425 },
426 {
427 .name = "Circuit-Switch Voice",
428 .stream_name = "CS-Voice",
429 .cpu_dai_name = "CS-VOICE",
430 .platform_name = "msm-pcm-voice",
431 .dynamic = 1,
432 .dsp_link = &fe_media,
433 .be_id = MSM_FRONTEND_DAI_CS_VOICE,
434 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
435 },
436 {
437 .name = "MSM VoIP",
438 .stream_name = "VoIP",
439 .cpu_dai_name = "VoIP",
440 .platform_name = "msm-voip-dsp",
441 .dynamic = 1,
442 .dsp_link = &fe_media,
443 .be_id = MSM_FRONTEND_DAI_VOIP,
444 },
445 {
446 .name = "MSM8960 LPA",
447 .stream_name = "LPA",
448 .cpu_dai_name = "MultiMedia3",
449 .platform_name = "msm-pcm-lpa",
450 .dynamic = 1,
451 .dsp_link = &lpa_fe_media,
452 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
453 },
454 /* Hostless PMC purpose */
455 {
456 .name = "SLIMBUS_0 Hostless",
457 .stream_name = "SLIMBUS_0 Hostless",
458 .cpu_dai_name = "SLIMBUS0_HOSTLESS",
459 .platform_name = "msm-pcm-hostless",
460 .dynamic = 1,
461 .dsp_link = &slimbus0_hl_media,
462 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
463 /* .be_id = do not care */
464 },
465 {
466 .name = "INT_FM Hostless",
467 .stream_name = "INT_FM Hostless",
468 .cpu_dai_name = "INT_FM_HOSTLESS",
469 .platform_name = "msm-pcm-hostless",
470 .dynamic = 1,
471 .dsp_link = &int_fm_hl_media,
472 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
473 /* .be_id = do not care */
474 },
475 /* Backend DAI Links */
476 {
477 .name = LPASS_BE_SLIMBUS_0_RX,
478 .stream_name = "Slimbus Playback",
479 .cpu_dai_name = "msm-dai-q6.16384",
480 .platform_name = "msm-pcm-routing",
481 .codec_name = "tabla_codec",
482 .codec_dai_name = "tabla_rx1",
483 .no_pcm = 1,
484 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
485 .init = &msm8960_audrx_init,
486 .be_hw_params_fixup = msm8960_slim_0_rx_be_hw_params_fixup,
487 .ops = &msm8960_be_ops,
488 },
489 {
490 .name = LPASS_BE_SLIMBUS_0_TX,
491 .stream_name = "Slimbus Capture",
492 .cpu_dai_name = "msm-dai-q6.16385",
493 .platform_name = "msm-pcm-routing",
494 .codec_name = "tabla_codec",
495 .codec_dai_name = "tabla_tx1",
496 .no_pcm = 1,
497 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
498 .be_hw_params_fixup = msm8960_slim_0_tx_be_hw_params_fixup,
499 .ops = &msm8960_be_ops,
500 },
501 /* Backend BT/FM DAI Links */
502 {
503 .name = LPASS_BE_INT_BT_SCO_RX,
504 .stream_name = "Internal BT-SCO Playback",
505 .cpu_dai_name = "msm-dai-q6.12288",
506 .platform_name = "msm-pcm-routing",
507 .codec_name = "msm-stub-codec.1",
508 .codec_dai_name = "msm-stub-rx",
509 .no_pcm = 1,
510 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
511 },
512 {
513 .name = LPASS_BE_INT_BT_SCO_TX,
514 .stream_name = "Internal BT-SCO Capture",
515 .cpu_dai_name = "msm-dai-q6.12289",
516 .platform_name = "msm-pcm-routing",
517 .codec_name = "msm-stub-codec.1",
518 .codec_dai_name = "msm-stub-tx",
519 .no_pcm = 1,
520 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
521 },
522 {
523 .name = LPASS_BE_INT_FM_RX,
524 .stream_name = "Internal FM Playback",
525 .cpu_dai_name = "msm-dai-q6.12292",
526 .platform_name = "msm-pcm-routing",
527 .codec_name = "msm-stub-codec.1",
528 .codec_dai_name = "msm-stub-rx",
529 .no_pcm = 1,
530 .be_id = MSM_BACKEND_DAI_INT_FM_RX,
531 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
532 },
533 {
534 .name = LPASS_BE_INT_FM_TX,
535 .stream_name = "Internal FM Capture",
536 .cpu_dai_name = "msm-dai-q6.12293",
537 .platform_name = "msm-pcm-routing",
538 .codec_name = "msm-stub-codec.1",
539 .codec_dai_name = "msm-stub-tx",
540 .no_pcm = 1,
541 .be_id = MSM_BACKEND_DAI_INT_FM_TX,
542 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
543 },
544 /* HDMI BACK END DAI Link */
545 {
546 .name = LPASS_BE_HDMI,
547 .stream_name = "HDMI Playback",
548 .cpu_dai_name = "msm-dai-q6.8",
549 .platform_name = "msm-pcm-routing",
550 .codec_name = "msm-stub-codec.1",
551 .codec_dai_name = "msm-stub-rx",
552 .no_pcm = 1,
553 .no_codec = 1,
554 .be_id = MSM_BACKEND_DAI_HDMI_RX,
555 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
556 },
557};
558
559struct snd_soc_card snd_soc_card_msm8960 = {
560 .name = "msm8960-snd-card",
561 .dai_link = msm8960_dai,
562 .num_links = ARRAY_SIZE(msm8960_dai),
563};
564
565static struct platform_device *msm8960_snd_device;
566
567static int msm8960_configure_headset_mic_gpios(void)
568{
569 int ret;
570 struct pm_gpio param = {
571 .direction = PM_GPIO_DIR_OUT,
572 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
573 .output_value = 1,
574 .pull = PM_GPIO_PULL_NO,
575 .vin_sel = PM_GPIO_VIN_S4,
576 .out_strength = PM_GPIO_STRENGTH_MED,
577 .function = PM_GPIO_FUNC_NORMAL,
578 };
579
580 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
581 if (ret) {
582 pr_err("%s: Failed to request gpio %d\n", __func__,
583 PM8921_GPIO_PM_TO_SYS(23));
584 return ret;
585 }
586
587 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
588 if (ret)
589 pr_err("%s: Failed to configure gpio %d\n", __func__,
590 PM8921_GPIO_PM_TO_SYS(23));
591 else
592 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
593
594 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
595 if (ret) {
596 pr_err("%s: Failed to request gpio %d\n", __func__,
597 PM8921_GPIO_PM_TO_SYS(35));
598 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
599 return ret;
600 }
601 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
602 if (ret)
603 pr_err("%s: Failed to configure gpio %d\n", __func__,
604 PM8921_GPIO_PM_TO_SYS(35));
605 else
606 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 1);
607
608 return 0;
609}
610static void msm8960_free_headset_mic_gpios(void)
611{
612 if (msm8960_headset_gpios_configured) {
613 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
614 gpio_free(PM8921_GPIO_PM_TO_SYS(35));
615 }
616}
617
618static int __init msm8960_audio_init(void)
619{
620 int ret;
621
622 msm8960_snd_device = platform_device_alloc("soc-audio", 0);
623 if (!msm8960_snd_device) {
624 pr_err("Platform device allocation failed\n");
625 return -ENOMEM;
626 }
627
628 platform_set_drvdata(msm8960_snd_device, &snd_soc_card_msm8960);
629 ret = platform_device_add(msm8960_snd_device);
630 if (ret) {
631 platform_device_put(msm8960_snd_device);
632 return ret;
633 }
634
635 if (msm8960_configure_headset_mic_gpios()) {
636 pr_err("%s Fail to configure headset mic gpios\n", __func__);
637 msm8960_headset_gpios_configured = 0;
638 } else
639 msm8960_headset_gpios_configured = 1;
640
641 return ret;
642
643}
644module_init(msm8960_audio_init);
645
646static void __exit msm8960_audio_exit(void)
647{
648 msm8960_free_headset_mic_gpios();
649 platform_device_unregister(msm8960_snd_device);
650}
651module_exit(msm8960_audio_exit);
652
653MODULE_DESCRIPTION("ALSA SoC MSM8960");
654MODULE_LICENSE("GPL v2");