blob: dc108ce395bfb727c66d05a9c7fda1553aad38e2 [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>
Bradley Rubin229c6a52011-07-12 16:18:48 -070025#include <asm/mach-types.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026#include "msm-pcm-routing.h"
27#include <../codecs/wcd9310.h>
28
29/* 8960 machine driver */
30
31#define PM8921_GPIO_BASE NR_GPIO_IRQS
32#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
33
34#define MSM_CDC_PAMPL (PM8921_GPIO_PM_TO_SYS(18))
35#define MSM_CDC_PAMPR (PM8921_GPIO_PM_TO_SYS(19))
36#define MSM8960_SPK_ON 1
37#define MSM8960_SPK_OFF 0
38
39#define msm8960_SLIM_0_RX_MAX_CHANNELS 2
40#define msm8960_SLIM_0_TX_MAX_CHANNELS 4
41
42
43static int msm8960_spk_control;
44static int msm8960_pamp_on;
45static int msm8960_slim_0_rx_ch = 1;
46static int msm8960_slim_0_tx_ch = 1;
47
48struct tabla_mbhc_calibration tabla_cal = {
49 .bias = TABLA_MICBIAS2,
50 .tldoh = 100,
51 .bg_fast_settle = 100,
52 .mic_current = TABLA_PID_MIC_5_UA,
53 .mic_pid = 100,
54 .hph_current = TABLA_PID_MIC_5_UA,
55 .setup_plug_removal_delay = 1000000,
56 .shutdown_plug_removal = 100000,
57};
58
59static struct clk *codec_clk;
60static int clk_users;
61
62static int msm8960_headset_gpios_configured;
63
64static struct snd_soc_jack hs_jack;
Bradley Rubincb1e2732011-06-23 16:49:20 -070065static struct snd_soc_jack button_jack;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066
67static void codec_poweramp_on(void)
68{
69 int ret = 0;
70
71 struct pm_gpio param = {
72 .direction = PM_GPIO_DIR_OUT,
73 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
74 .output_value = 1,
75 .pull = PM_GPIO_PULL_NO,
76 .vin_sel = PM_GPIO_VIN_S4,
77 .out_strength = PM_GPIO_STRENGTH_MED,
78 .function = PM_GPIO_FUNC_NORMAL,
79 };
80
81 if (msm8960_pamp_on)
82 return;
83
84 pr_debug("%s: enable stereo spkr amp\n", __func__);
85 ret = gpio_request(MSM_CDC_PAMPL, "CDC PAMP1");
86 if (ret) {
87 pr_err("%s: Error requesting GPIO %d\n", __func__,
88 MSM_CDC_PAMPL);
89 return;
90 }
91 ret = pm8xxx_gpio_config(MSM_CDC_PAMPL, &param);
92 if (ret)
93 pr_err("%s: Failed to configure gpio %d\n", __func__,
94 MSM_CDC_PAMPL);
95 else
96 gpio_direction_output(MSM_CDC_PAMPL, 1);
97
98 ret = gpio_request(MSM_CDC_PAMPR, "CDC PAMPL");
99 if (ret) {
100 pr_err("%s: Error requesting GPIO %d\n", __func__,
101 MSM_CDC_PAMPR);
102 gpio_free(MSM_CDC_PAMPL);
103 return;
104 }
105 ret = pm8xxx_gpio_config(MSM_CDC_PAMPR, &param);
106 if (ret)
107 pr_err("%s: Failed to configure gpio %d\n", __func__,
108 MSM_CDC_PAMPR);
109 else
110 gpio_direction_output(MSM_CDC_PAMPR, 1);
111
112 msm8960_pamp_on = 1;
113}
114static void codec_poweramp_off(void)
115{
116 if (!msm8960_pamp_on)
117 return;
118
119 pr_debug("%s: disable stereo spkr amp\n", __func__);
120 gpio_direction_output(MSM_CDC_PAMPL, 0);
121 gpio_free(MSM_CDC_PAMPL);
122 gpio_direction_output(MSM_CDC_PAMPR, 0);
123 gpio_free(MSM_CDC_PAMPR);
124 msm8960_pamp_on = 0;
125}
126static void msm8960_ext_control(struct snd_soc_codec *codec)
127{
128 struct snd_soc_dapm_context *dapm = &codec->dapm;
129
130 pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
131 if (msm8960_spk_control == MSM8960_SPK_ON)
132 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
133 else
134 snd_soc_dapm_disable_pin(dapm, "Ext Spk");
135
136 snd_soc_dapm_sync(dapm);
137}
138
139static int msm8960_get_spk(struct snd_kcontrol *kcontrol,
140 struct snd_ctl_elem_value *ucontrol)
141{
142 pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
143 ucontrol->value.integer.value[0] = msm8960_spk_control;
144 return 0;
145}
146static int msm8960_set_spk(struct snd_kcontrol *kcontrol,
147 struct snd_ctl_elem_value *ucontrol)
148{
149 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
150
151 pr_debug("%s()\n", __func__);
152 if (msm8960_spk_control == ucontrol->value.integer.value[0])
153 return 0;
154
155 msm8960_spk_control = ucontrol->value.integer.value[0];
156 msm8960_ext_control(codec);
157 return 1;
158}
159static int msm8960_spkramp_event(struct snd_soc_dapm_widget *w,
160 struct snd_kcontrol *k, int event)
161{
162 pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
163 if (SND_SOC_DAPM_EVENT_ON(event))
164 codec_poweramp_on();
165 else
166 codec_poweramp_off();
167 return 0;
168}
169
170static const struct snd_soc_dapm_widget msm8960_dapm_widgets[] = {
171 SND_SOC_DAPM_SPK("Ext Spk", msm8960_spkramp_event),
172 SND_SOC_DAPM_MIC("Handset Mic", NULL),
173 SND_SOC_DAPM_MIC("Headset Mic", NULL),
174 SND_SOC_DAPM_MIC("Digital Mic1", NULL),
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700175 SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
176 SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700177
178 /* Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP. */
179 SND_SOC_DAPM_MIC("Digital Mic1", NULL),
180
181 /* Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP. */
182 SND_SOC_DAPM_MIC("Digital Mic2", NULL),
183
184 /* Digital Mic4. Back Top Digital Mic on Fluid. */
185 SND_SOC_DAPM_MIC("Digital Mic4", NULL),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186};
187
Bradley Rubin229c6a52011-07-12 16:18:48 -0700188static const struct snd_soc_dapm_route common_audio_map[] = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 /* Speaker path */
190 {"Ext Spk", NULL, "LINEOUT"},
191
192 /* Microphone path */
Bradley Rubin229c6a52011-07-12 16:18:48 -0700193 {"AMIC1", NULL, "MIC BIAS1 Internal1"},
194 {"MIC BIAS1 Internal1", NULL, "Handset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700195
196 {"AMIC2", NULL, "MIC BIAS2 External"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197 {"MIC BIAS2 External", NULL, "Headset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700198
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700199 /**
200 * Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP.
201 * Conncted to DMIC2 Input on Tabla codec.
202 */
203 {"DMIC2", NULL, "MIC BIAS1 External"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700204 {"MIC BIAS1 External", NULL, "Digital Mic1"},
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700205
206 /**
207 * Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP.
208 * Conncted to DMIC1 Input on Tabla codec.
209 */
210 {"DMIC1", NULL, "MIC BIAS1 External"},
211 {"MIC BIAS1 External", NULL, "Digital Mic2"},
212
213 /**
214 * Digital Mic4. Back top Digital Mic on Fluid.
215 * Conncted to DMIC3 Input on Tabla codec.
216 */
217 {"DMIC3", NULL, "MIC BIAS3 External"},
218 {"MIC BIAS3 External", NULL, "Digital Mic4"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219};
220
Bradley Rubin229c6a52011-07-12 16:18:48 -0700221static const struct snd_soc_dapm_route cdp_audio_map[] = {
222 {"AMIC3", NULL, "MIC BIAS3 External"},
223 {"MIC BIAS3 External", NULL, "ANCRight Headset Mic"},
224
225 {"AMIC4", NULL, "MIC BIAS4 External"},
226 {"MIC BIAS4 External", NULL, "ANCLeft Headset Mic"},
227};
228
229static const struct snd_soc_dapm_route fluid_audio_map[] = {
230 {"AMIC3", NULL, "MIC BIAS3 Internal1"},
231 {"MIC BIAS3 Internal1", NULL, "ANCRight Headset Mic"},
232
233 {"AMIC4", NULL, "MIC BIAS1 Internal2"},
234 {"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
235};
236
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700237static const char *spk_function[] = {"Off", "On"};
238static const struct soc_enum msm8960_enum[] = {
239 SOC_ENUM_SINGLE_EXT(2, spk_function),
240};
241
242static int msm8960_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
243 struct snd_ctl_elem_value *ucontrol)
244{
245 pr_debug("%s: msm8960_slim_0_rx_ch = %d", __func__,
246 msm8960_slim_0_rx_ch);
247 ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch;
248 return 0;
249}
250
251static int msm8960_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
252 struct snd_ctl_elem_value *ucontrol)
253{
254 msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0];
255
256 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
257 msm8960_slim_0_rx_ch);
258 return 1;
259}
260
261static int msm8960_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
262 struct snd_ctl_elem_value *ucontrol)
263{
264 pr_debug("%s: msm8960_slim_0_tx_ch = %d", __func__,
265 msm8960_slim_0_tx_ch);
266 ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch;
267 return 0;
268}
269
270static int msm8960_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
271 struct snd_ctl_elem_value *ucontrol)
272{
273 msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0];
274
275 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
276 msm8960_slim_0_tx_ch);
277 return 1;
278}
279
280
281static const struct snd_kcontrol_new tabla_msm8960_controls[] = {
282 SOC_ENUM_EXT("Speaker Function", msm8960_enum[0], msm8960_get_spk,
283 msm8960_set_spk),
284 SOC_SINGLE_EXT("SLIM_0_RX Channels", 0, 0,
285 msm8960_SLIM_0_RX_MAX_CHANNELS, 0,
286 msm8960_slim_0_rx_ch_get, msm8960_slim_0_rx_ch_put),
287 SOC_SINGLE_EXT("SLIM_0_TX Channels", 0, 0,
288 msm8960_SLIM_0_TX_MAX_CHANNELS, 0,
289 msm8960_slim_0_tx_ch_get, msm8960_slim_0_tx_ch_put),
290};
291
292static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
293{
294 int err;
295 struct snd_soc_codec *codec = rtd->codec;
296 struct snd_soc_dapm_context *dapm = &codec->dapm;
297
298 pr_debug("%s()\n", __func__);
299
300 err = snd_soc_add_controls(codec, tabla_msm8960_controls,
301 ARRAY_SIZE(tabla_msm8960_controls));
302 if (err < 0)
303 return err;
304
305 snd_soc_dapm_new_controls(dapm, msm8960_dapm_widgets,
306 ARRAY_SIZE(msm8960_dapm_widgets));
307
Bradley Rubin229c6a52011-07-12 16:18:48 -0700308 snd_soc_dapm_add_routes(dapm, common_audio_map,
309 ARRAY_SIZE(common_audio_map));
310
311 if (machine_is_msm8960_cdp())
312 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
313 ARRAY_SIZE(cdp_audio_map));
314 else if (machine_is_msm8960_mtp())
315 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
316 ARRAY_SIZE(cdp_audio_map));
317 else if (machine_is_msm8960_fluid())
318 snd_soc_dapm_add_routes(dapm, fluid_audio_map,
319 ARRAY_SIZE(fluid_audio_map));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320
321 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
322
323 snd_soc_dapm_sync(dapm);
324
325 err = snd_soc_jack_new(codec, "Headset Jack",
326 SND_JACK_HEADSET, &hs_jack);
327 if (err) {
328 pr_err("failed to create new jack\n");
329 return err;
330 }
Bradley Rubincb1e2732011-06-23 16:49:20 -0700331
332 err = snd_soc_jack_new(codec, "Button Jack",
333 SND_JACK_BTN_0, &button_jack);
334 if (err) {
335 pr_err("failed to create new jack\n");
336 return err;
337 }
338
339 tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_cal);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340
341 return 0;
342}
343
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700344static struct snd_soc_dsp_link lpa_fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700345 .playback = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700346 .trigger = {
347 SND_SOC_DSP_TRIGGER_POST,
348 SND_SOC_DSP_TRIGGER_POST
349 },
350};
351
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700352static struct snd_soc_dsp_link fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700353 .playback = true,
354 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700355 .trigger = {
356 SND_SOC_DSP_TRIGGER_POST,
357 SND_SOC_DSP_TRIGGER_POST
358 },
359};
360
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700361static struct snd_soc_dsp_link slimbus0_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700362 .playback = true,
363 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364 .trigger = {
365 SND_SOC_DSP_TRIGGER_POST,
366 SND_SOC_DSP_TRIGGER_POST
367 },
368};
369
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370static struct snd_soc_dsp_link int_fm_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700371 .playback = true,
372 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373 .trigger = {
374 SND_SOC_DSP_TRIGGER_POST,
375 SND_SOC_DSP_TRIGGER_POST
376 },
377};
378
379static int msm8960_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
380 struct snd_pcm_hw_params *params)
381{
382 struct snd_interval *rate = hw_param_interval(params,
383 SNDRV_PCM_HW_PARAM_RATE);
384
385 struct snd_interval *channels = hw_param_interval(params,
386 SNDRV_PCM_HW_PARAM_CHANNELS);
387
388 pr_debug("%s()\n", __func__);
389 rate->min = rate->max = 48000;
390 channels->min = channels->max = msm8960_slim_0_rx_ch;
391
392 return 0;
393}
394
395static int msm8960_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
396 struct snd_pcm_hw_params *params)
397{
398 struct snd_interval *rate = hw_param_interval(params,
399 SNDRV_PCM_HW_PARAM_RATE);
400
401 struct snd_interval *channels = hw_param_interval(params,
402 SNDRV_PCM_HW_PARAM_CHANNELS);
403
404 pr_debug("%s()\n", __func__);
405 rate->min = rate->max = 48000;
406 channels->min = channels->max = msm8960_slim_0_tx_ch;
407
408 return 0;
409}
410
411static int msm8960_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
412 struct snd_pcm_hw_params *params)
413{
414 struct snd_interval *rate = hw_param_interval(params,
415 SNDRV_PCM_HW_PARAM_RATE);
416
417 pr_debug("%s()\n", __func__);
418 rate->min = rate->max = 48000;
419
420 return 0;
421}
422
423static int msm8960_startup(struct snd_pcm_substream *substream)
424{
425 if (clk_users++)
426 return 0;
427
428 codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
429 if (codec_clk) {
430 clk_set_rate(codec_clk, 12288000);
431 clk_enable(codec_clk);
432 } else {
433 pr_err("%s: Error setting Tabla MCLK\n", __func__);
434 clk_users--;
435 return -EINVAL;
436 }
437 return 0;
438}
439
440static void msm8960_shutdown(struct snd_pcm_substream *substream)
441{
442 clk_users--;
443 if (!clk_users) {
444 clk_disable(codec_clk);
445 clk_put(codec_clk);
446 }
447}
448
449static struct snd_soc_ops msm8960_be_ops = {
450 .startup = msm8960_startup,
451 .shutdown = msm8960_shutdown,
452};
453
454/* Digital audio interface glue - connects codec <---> CPU */
455static struct snd_soc_dai_link msm8960_dai[] = {
456 /* FrontEnd DAI Links */
457 {
458 .name = "MSM8960 Media1",
459 .stream_name = "MultiMedia1",
460 .cpu_dai_name = "MultiMedia1",
461 .platform_name = "msm-pcm-dsp",
462 .dynamic = 1,
463 .dsp_link = &fe_media,
464 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
465 },
466 {
467 .name = "MSM8960 Media2",
468 .stream_name = "MultiMedia2",
469 .cpu_dai_name = "MultiMedia2",
470 .platform_name = "msm-pcm-dsp",
471 .dynamic = 1,
472 .dsp_link = &fe_media,
473 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
474 },
475 {
476 .name = "Circuit-Switch Voice",
477 .stream_name = "CS-Voice",
478 .cpu_dai_name = "CS-VOICE",
479 .platform_name = "msm-pcm-voice",
480 .dynamic = 1,
481 .dsp_link = &fe_media,
482 .be_id = MSM_FRONTEND_DAI_CS_VOICE,
483 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
484 },
485 {
486 .name = "MSM VoIP",
487 .stream_name = "VoIP",
488 .cpu_dai_name = "VoIP",
489 .platform_name = "msm-voip-dsp",
490 .dynamic = 1,
491 .dsp_link = &fe_media,
492 .be_id = MSM_FRONTEND_DAI_VOIP,
493 },
494 {
495 .name = "MSM8960 LPA",
496 .stream_name = "LPA",
497 .cpu_dai_name = "MultiMedia3",
498 .platform_name = "msm-pcm-lpa",
499 .dynamic = 1,
500 .dsp_link = &lpa_fe_media,
501 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
502 },
503 /* Hostless PMC purpose */
504 {
505 .name = "SLIMBUS_0 Hostless",
506 .stream_name = "SLIMBUS_0 Hostless",
507 .cpu_dai_name = "SLIMBUS0_HOSTLESS",
508 .platform_name = "msm-pcm-hostless",
509 .dynamic = 1,
510 .dsp_link = &slimbus0_hl_media,
511 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
512 /* .be_id = do not care */
513 },
514 {
515 .name = "INT_FM Hostless",
516 .stream_name = "INT_FM Hostless",
517 .cpu_dai_name = "INT_FM_HOSTLESS",
518 .platform_name = "msm-pcm-hostless",
519 .dynamic = 1,
520 .dsp_link = &int_fm_hl_media,
521 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
522 /* .be_id = do not care */
523 },
524 /* Backend DAI Links */
525 {
526 .name = LPASS_BE_SLIMBUS_0_RX,
527 .stream_name = "Slimbus Playback",
528 .cpu_dai_name = "msm-dai-q6.16384",
529 .platform_name = "msm-pcm-routing",
530 .codec_name = "tabla_codec",
531 .codec_dai_name = "tabla_rx1",
532 .no_pcm = 1,
533 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
534 .init = &msm8960_audrx_init,
535 .be_hw_params_fixup = msm8960_slim_0_rx_be_hw_params_fixup,
536 .ops = &msm8960_be_ops,
537 },
538 {
539 .name = LPASS_BE_SLIMBUS_0_TX,
540 .stream_name = "Slimbus Capture",
541 .cpu_dai_name = "msm-dai-q6.16385",
542 .platform_name = "msm-pcm-routing",
543 .codec_name = "tabla_codec",
544 .codec_dai_name = "tabla_tx1",
545 .no_pcm = 1,
546 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
547 .be_hw_params_fixup = msm8960_slim_0_tx_be_hw_params_fixup,
548 .ops = &msm8960_be_ops,
549 },
550 /* Backend BT/FM DAI Links */
551 {
552 .name = LPASS_BE_INT_BT_SCO_RX,
553 .stream_name = "Internal BT-SCO Playback",
554 .cpu_dai_name = "msm-dai-q6.12288",
555 .platform_name = "msm-pcm-routing",
556 .codec_name = "msm-stub-codec.1",
557 .codec_dai_name = "msm-stub-rx",
558 .no_pcm = 1,
559 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
560 },
561 {
562 .name = LPASS_BE_INT_BT_SCO_TX,
563 .stream_name = "Internal BT-SCO Capture",
564 .cpu_dai_name = "msm-dai-q6.12289",
565 .platform_name = "msm-pcm-routing",
566 .codec_name = "msm-stub-codec.1",
567 .codec_dai_name = "msm-stub-tx",
568 .no_pcm = 1,
569 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
570 },
571 {
572 .name = LPASS_BE_INT_FM_RX,
573 .stream_name = "Internal FM Playback",
574 .cpu_dai_name = "msm-dai-q6.12292",
575 .platform_name = "msm-pcm-routing",
576 .codec_name = "msm-stub-codec.1",
577 .codec_dai_name = "msm-stub-rx",
578 .no_pcm = 1,
579 .be_id = MSM_BACKEND_DAI_INT_FM_RX,
580 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
581 },
582 {
583 .name = LPASS_BE_INT_FM_TX,
584 .stream_name = "Internal FM Capture",
585 .cpu_dai_name = "msm-dai-q6.12293",
586 .platform_name = "msm-pcm-routing",
587 .codec_name = "msm-stub-codec.1",
588 .codec_dai_name = "msm-stub-tx",
589 .no_pcm = 1,
590 .be_id = MSM_BACKEND_DAI_INT_FM_TX,
591 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
592 },
593 /* HDMI BACK END DAI Link */
594 {
595 .name = LPASS_BE_HDMI,
596 .stream_name = "HDMI Playback",
597 .cpu_dai_name = "msm-dai-q6.8",
598 .platform_name = "msm-pcm-routing",
599 .codec_name = "msm-stub-codec.1",
600 .codec_dai_name = "msm-stub-rx",
601 .no_pcm = 1,
602 .no_codec = 1,
603 .be_id = MSM_BACKEND_DAI_HDMI_RX,
604 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
605 },
606};
607
608struct snd_soc_card snd_soc_card_msm8960 = {
609 .name = "msm8960-snd-card",
610 .dai_link = msm8960_dai,
611 .num_links = ARRAY_SIZE(msm8960_dai),
612};
613
614static struct platform_device *msm8960_snd_device;
615
616static int msm8960_configure_headset_mic_gpios(void)
617{
618 int ret;
619 struct pm_gpio param = {
620 .direction = PM_GPIO_DIR_OUT,
621 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
622 .output_value = 1,
623 .pull = PM_GPIO_PULL_NO,
624 .vin_sel = PM_GPIO_VIN_S4,
625 .out_strength = PM_GPIO_STRENGTH_MED,
626 .function = PM_GPIO_FUNC_NORMAL,
627 };
628
629 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
630 if (ret) {
631 pr_err("%s: Failed to request gpio %d\n", __func__,
632 PM8921_GPIO_PM_TO_SYS(23));
633 return ret;
634 }
635
636 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
637 if (ret)
638 pr_err("%s: Failed to configure gpio %d\n", __func__,
639 PM8921_GPIO_PM_TO_SYS(23));
640 else
641 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
642
643 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
644 if (ret) {
645 pr_err("%s: Failed to request gpio %d\n", __func__,
646 PM8921_GPIO_PM_TO_SYS(35));
647 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
648 return ret;
649 }
650 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
651 if (ret)
652 pr_err("%s: Failed to configure gpio %d\n", __func__,
653 PM8921_GPIO_PM_TO_SYS(35));
654 else
655 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 1);
656
657 return 0;
658}
659static void msm8960_free_headset_mic_gpios(void)
660{
661 if (msm8960_headset_gpios_configured) {
662 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
663 gpio_free(PM8921_GPIO_PM_TO_SYS(35));
664 }
665}
666
667static int __init msm8960_audio_init(void)
668{
669 int ret;
670
671 msm8960_snd_device = platform_device_alloc("soc-audio", 0);
672 if (!msm8960_snd_device) {
673 pr_err("Platform device allocation failed\n");
674 return -ENOMEM;
675 }
676
677 platform_set_drvdata(msm8960_snd_device, &snd_soc_card_msm8960);
678 ret = platform_device_add(msm8960_snd_device);
679 if (ret) {
680 platform_device_put(msm8960_snd_device);
681 return ret;
682 }
683
684 if (msm8960_configure_headset_mic_gpios()) {
685 pr_err("%s Fail to configure headset mic gpios\n", __func__);
686 msm8960_headset_gpios_configured = 0;
687 } else
688 msm8960_headset_gpios_configured = 1;
689
690 return ret;
691
692}
693module_init(msm8960_audio_init);
694
695static void __exit msm8960_audio_exit(void)
696{
697 msm8960_free_headset_mic_gpios();
698 platform_device_unregister(msm8960_snd_device);
699}
700module_exit(msm8960_audio_exit);
701
702MODULE_DESCRIPTION("ALSA SoC MSM8960");
703MODULE_LICENSE("GPL v2");