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