blob: 33aeac7a887443766ad0136ba62468a807f2dc94 [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 Kandie9bf86a2011-07-21 16:50:41 -0700199 {"HEADPHONE", NULL, "LDO_H"},
200
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700201 /**
202 * Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP.
203 * Conncted to DMIC2 Input on Tabla codec.
204 */
205 {"DMIC2", NULL, "MIC BIAS1 External"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700206 {"MIC BIAS1 External", NULL, "Digital Mic1"},
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700207
208 /**
209 * Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP.
210 * Conncted to DMIC1 Input on Tabla codec.
211 */
212 {"DMIC1", NULL, "MIC BIAS1 External"},
213 {"MIC BIAS1 External", NULL, "Digital Mic2"},
214
215 /**
216 * Digital Mic4. Back top Digital Mic on Fluid.
217 * Conncted to DMIC3 Input on Tabla codec.
218 */
219 {"DMIC3", NULL, "MIC BIAS3 External"},
220 {"MIC BIAS3 External", NULL, "Digital Mic4"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700221};
222
Bradley Rubin229c6a52011-07-12 16:18:48 -0700223static const struct snd_soc_dapm_route cdp_audio_map[] = {
224 {"AMIC3", NULL, "MIC BIAS3 External"},
225 {"MIC BIAS3 External", NULL, "ANCRight Headset Mic"},
226
227 {"AMIC4", NULL, "MIC BIAS4 External"},
228 {"MIC BIAS4 External", NULL, "ANCLeft Headset Mic"},
229};
230
231static const struct snd_soc_dapm_route fluid_audio_map[] = {
232 {"AMIC3", NULL, "MIC BIAS3 Internal1"},
233 {"MIC BIAS3 Internal1", NULL, "ANCRight Headset Mic"},
234
235 {"AMIC4", NULL, "MIC BIAS1 Internal2"},
236 {"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
237};
238
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239static const char *spk_function[] = {"Off", "On"};
Patrick Lai9f4b4292011-07-16 22:11:09 -0700240static const char *slim0_rx_ch_text[] = {"One", "Two"};
241static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
242
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243static const struct soc_enum msm8960_enum[] = {
244 SOC_ENUM_SINGLE_EXT(2, spk_function),
Patrick Lai9f4b4292011-07-16 22:11:09 -0700245 SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
246 SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247};
248
249static int msm8960_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
250 struct snd_ctl_elem_value *ucontrol)
251{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700252 pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253 msm8960_slim_0_rx_ch);
Patrick Lai9f4b4292011-07-16 22:11:09 -0700254 ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255 return 0;
256}
257
258static int msm8960_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
259 struct snd_ctl_elem_value *ucontrol)
260{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700261 msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700262
Patrick Lai9f4b4292011-07-16 22:11:09 -0700263 pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264 msm8960_slim_0_rx_ch);
265 return 1;
266}
267
268static int msm8960_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
269 struct snd_ctl_elem_value *ucontrol)
270{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700271 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272 msm8960_slim_0_tx_ch);
Patrick Lai9f4b4292011-07-16 22:11:09 -0700273 ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700274 return 0;
275}
276
277static int msm8960_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
278 struct snd_ctl_elem_value *ucontrol)
279{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700280 msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281
282 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
283 msm8960_slim_0_tx_ch);
284 return 1;
285}
286
287
288static const struct snd_kcontrol_new tabla_msm8960_controls[] = {
289 SOC_ENUM_EXT("Speaker Function", msm8960_enum[0], msm8960_get_spk,
290 msm8960_set_spk),
Patrick Lai9f4b4292011-07-16 22:11:09 -0700291 SOC_ENUM_EXT("SLIM_0_RX Channels", msm8960_enum[1],
292 msm8960_slim_0_rx_ch_get, msm8960_slim_0_rx_ch_put),
293 SOC_ENUM_EXT("SLIM_0_TX Channels", msm8960_enum[2],
294 msm8960_slim_0_tx_ch_get, msm8960_slim_0_tx_ch_put),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295};
296
297static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
298{
299 int err;
300 struct snd_soc_codec *codec = rtd->codec;
301 struct snd_soc_dapm_context *dapm = &codec->dapm;
302
303 pr_debug("%s()\n", __func__);
304
305 err = snd_soc_add_controls(codec, tabla_msm8960_controls,
306 ARRAY_SIZE(tabla_msm8960_controls));
307 if (err < 0)
308 return err;
309
310 snd_soc_dapm_new_controls(dapm, msm8960_dapm_widgets,
311 ARRAY_SIZE(msm8960_dapm_widgets));
312
Bradley Rubin229c6a52011-07-12 16:18:48 -0700313 snd_soc_dapm_add_routes(dapm, common_audio_map,
314 ARRAY_SIZE(common_audio_map));
315
316 if (machine_is_msm8960_cdp())
317 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
318 ARRAY_SIZE(cdp_audio_map));
319 else if (machine_is_msm8960_mtp())
320 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
321 ARRAY_SIZE(cdp_audio_map));
322 else if (machine_is_msm8960_fluid())
323 snd_soc_dapm_add_routes(dapm, fluid_audio_map,
324 ARRAY_SIZE(fluid_audio_map));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325
326 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
327
328 snd_soc_dapm_sync(dapm);
329
330 err = snd_soc_jack_new(codec, "Headset Jack",
331 SND_JACK_HEADSET, &hs_jack);
332 if (err) {
333 pr_err("failed to create new jack\n");
334 return err;
335 }
Bradley Rubincb1e2732011-06-23 16:49:20 -0700336
337 err = snd_soc_jack_new(codec, "Button Jack",
338 SND_JACK_BTN_0, &button_jack);
339 if (err) {
340 pr_err("failed to create new jack\n");
341 return err;
342 }
343
344 tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_cal);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345
346 return 0;
347}
348
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700349static struct snd_soc_dsp_link lpa_fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700350 .playback = 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
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357static struct snd_soc_dsp_link fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700358 .playback = true,
359 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360 .trigger = {
361 SND_SOC_DSP_TRIGGER_POST,
362 SND_SOC_DSP_TRIGGER_POST
363 },
364};
365
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700366static struct snd_soc_dsp_link slimbus0_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700367 .playback = true,
368 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369 .trigger = {
370 SND_SOC_DSP_TRIGGER_POST,
371 SND_SOC_DSP_TRIGGER_POST
372 },
373};
374
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700375static struct snd_soc_dsp_link int_fm_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700376 .playback = true,
377 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378 .trigger = {
379 SND_SOC_DSP_TRIGGER_POST,
380 SND_SOC_DSP_TRIGGER_POST
381 },
382};
383
384static int msm8960_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
385 struct snd_pcm_hw_params *params)
386{
387 struct snd_interval *rate = hw_param_interval(params,
388 SNDRV_PCM_HW_PARAM_RATE);
389
390 struct snd_interval *channels = hw_param_interval(params,
391 SNDRV_PCM_HW_PARAM_CHANNELS);
392
393 pr_debug("%s()\n", __func__);
394 rate->min = rate->max = 48000;
395 channels->min = channels->max = msm8960_slim_0_rx_ch;
396
397 return 0;
398}
399
400static int msm8960_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
401 struct snd_pcm_hw_params *params)
402{
403 struct snd_interval *rate = hw_param_interval(params,
404 SNDRV_PCM_HW_PARAM_RATE);
405
406 struct snd_interval *channels = hw_param_interval(params,
407 SNDRV_PCM_HW_PARAM_CHANNELS);
408
409 pr_debug("%s()\n", __func__);
410 rate->min = rate->max = 48000;
411 channels->min = channels->max = msm8960_slim_0_tx_ch;
412
413 return 0;
414}
415
416static int msm8960_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
417 struct snd_pcm_hw_params *params)
418{
419 struct snd_interval *rate = hw_param_interval(params,
420 SNDRV_PCM_HW_PARAM_RATE);
421
422 pr_debug("%s()\n", __func__);
423 rate->min = rate->max = 48000;
424
425 return 0;
426}
427
428static int msm8960_startup(struct snd_pcm_substream *substream)
429{
430 if (clk_users++)
431 return 0;
432
433 codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
434 if (codec_clk) {
435 clk_set_rate(codec_clk, 12288000);
436 clk_enable(codec_clk);
437 } else {
438 pr_err("%s: Error setting Tabla MCLK\n", __func__);
439 clk_users--;
440 return -EINVAL;
441 }
442 return 0;
443}
444
445static void msm8960_shutdown(struct snd_pcm_substream *substream)
446{
447 clk_users--;
448 if (!clk_users) {
449 clk_disable(codec_clk);
450 clk_put(codec_clk);
451 }
452}
453
454static struct snd_soc_ops msm8960_be_ops = {
455 .startup = msm8960_startup,
456 .shutdown = msm8960_shutdown,
457};
458
459/* Digital audio interface glue - connects codec <---> CPU */
460static struct snd_soc_dai_link msm8960_dai[] = {
461 /* FrontEnd DAI Links */
462 {
463 .name = "MSM8960 Media1",
464 .stream_name = "MultiMedia1",
465 .cpu_dai_name = "MultiMedia1",
466 .platform_name = "msm-pcm-dsp",
467 .dynamic = 1,
468 .dsp_link = &fe_media,
469 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
470 },
471 {
472 .name = "MSM8960 Media2",
473 .stream_name = "MultiMedia2",
474 .cpu_dai_name = "MultiMedia2",
475 .platform_name = "msm-pcm-dsp",
476 .dynamic = 1,
477 .dsp_link = &fe_media,
478 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
479 },
480 {
481 .name = "Circuit-Switch Voice",
482 .stream_name = "CS-Voice",
483 .cpu_dai_name = "CS-VOICE",
484 .platform_name = "msm-pcm-voice",
485 .dynamic = 1,
486 .dsp_link = &fe_media,
487 .be_id = MSM_FRONTEND_DAI_CS_VOICE,
488 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
489 },
490 {
491 .name = "MSM VoIP",
492 .stream_name = "VoIP",
493 .cpu_dai_name = "VoIP",
494 .platform_name = "msm-voip-dsp",
495 .dynamic = 1,
496 .dsp_link = &fe_media,
497 .be_id = MSM_FRONTEND_DAI_VOIP,
498 },
499 {
500 .name = "MSM8960 LPA",
501 .stream_name = "LPA",
502 .cpu_dai_name = "MultiMedia3",
503 .platform_name = "msm-pcm-lpa",
504 .dynamic = 1,
505 .dsp_link = &lpa_fe_media,
506 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
507 },
508 /* Hostless PMC purpose */
509 {
510 .name = "SLIMBUS_0 Hostless",
511 .stream_name = "SLIMBUS_0 Hostless",
512 .cpu_dai_name = "SLIMBUS0_HOSTLESS",
513 .platform_name = "msm-pcm-hostless",
514 .dynamic = 1,
515 .dsp_link = &slimbus0_hl_media,
516 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
517 /* .be_id = do not care */
518 },
519 {
520 .name = "INT_FM Hostless",
521 .stream_name = "INT_FM Hostless",
522 .cpu_dai_name = "INT_FM_HOSTLESS",
523 .platform_name = "msm-pcm-hostless",
524 .dynamic = 1,
525 .dsp_link = &int_fm_hl_media,
526 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
527 /* .be_id = do not care */
528 },
529 /* Backend DAI Links */
530 {
531 .name = LPASS_BE_SLIMBUS_0_RX,
532 .stream_name = "Slimbus Playback",
533 .cpu_dai_name = "msm-dai-q6.16384",
534 .platform_name = "msm-pcm-routing",
535 .codec_name = "tabla_codec",
536 .codec_dai_name = "tabla_rx1",
537 .no_pcm = 1,
538 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
539 .init = &msm8960_audrx_init,
540 .be_hw_params_fixup = msm8960_slim_0_rx_be_hw_params_fixup,
541 .ops = &msm8960_be_ops,
542 },
543 {
544 .name = LPASS_BE_SLIMBUS_0_TX,
545 .stream_name = "Slimbus Capture",
546 .cpu_dai_name = "msm-dai-q6.16385",
547 .platform_name = "msm-pcm-routing",
548 .codec_name = "tabla_codec",
549 .codec_dai_name = "tabla_tx1",
550 .no_pcm = 1,
551 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
552 .be_hw_params_fixup = msm8960_slim_0_tx_be_hw_params_fixup,
553 .ops = &msm8960_be_ops,
554 },
555 /* Backend BT/FM DAI Links */
556 {
557 .name = LPASS_BE_INT_BT_SCO_RX,
558 .stream_name = "Internal BT-SCO Playback",
559 .cpu_dai_name = "msm-dai-q6.12288",
560 .platform_name = "msm-pcm-routing",
561 .codec_name = "msm-stub-codec.1",
562 .codec_dai_name = "msm-stub-rx",
563 .no_pcm = 1,
564 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
565 },
566 {
567 .name = LPASS_BE_INT_BT_SCO_TX,
568 .stream_name = "Internal BT-SCO Capture",
569 .cpu_dai_name = "msm-dai-q6.12289",
570 .platform_name = "msm-pcm-routing",
571 .codec_name = "msm-stub-codec.1",
572 .codec_dai_name = "msm-stub-tx",
573 .no_pcm = 1,
574 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
575 },
576 {
577 .name = LPASS_BE_INT_FM_RX,
578 .stream_name = "Internal FM Playback",
579 .cpu_dai_name = "msm-dai-q6.12292",
580 .platform_name = "msm-pcm-routing",
581 .codec_name = "msm-stub-codec.1",
582 .codec_dai_name = "msm-stub-rx",
583 .no_pcm = 1,
584 .be_id = MSM_BACKEND_DAI_INT_FM_RX,
585 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
586 },
587 {
588 .name = LPASS_BE_INT_FM_TX,
589 .stream_name = "Internal FM Capture",
590 .cpu_dai_name = "msm-dai-q6.12293",
591 .platform_name = "msm-pcm-routing",
592 .codec_name = "msm-stub-codec.1",
593 .codec_dai_name = "msm-stub-tx",
594 .no_pcm = 1,
595 .be_id = MSM_BACKEND_DAI_INT_FM_TX,
596 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
597 },
598 /* HDMI BACK END DAI Link */
599 {
600 .name = LPASS_BE_HDMI,
601 .stream_name = "HDMI Playback",
602 .cpu_dai_name = "msm-dai-q6.8",
603 .platform_name = "msm-pcm-routing",
604 .codec_name = "msm-stub-codec.1",
605 .codec_dai_name = "msm-stub-rx",
606 .no_pcm = 1,
607 .no_codec = 1,
608 .be_id = MSM_BACKEND_DAI_HDMI_RX,
609 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
610 },
611};
612
613struct snd_soc_card snd_soc_card_msm8960 = {
614 .name = "msm8960-snd-card",
615 .dai_link = msm8960_dai,
616 .num_links = ARRAY_SIZE(msm8960_dai),
617};
618
619static struct platform_device *msm8960_snd_device;
620
621static int msm8960_configure_headset_mic_gpios(void)
622{
623 int ret;
624 struct pm_gpio param = {
625 .direction = PM_GPIO_DIR_OUT,
626 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
627 .output_value = 1,
628 .pull = PM_GPIO_PULL_NO,
629 .vin_sel = PM_GPIO_VIN_S4,
630 .out_strength = PM_GPIO_STRENGTH_MED,
631 .function = PM_GPIO_FUNC_NORMAL,
632 };
633
634 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
635 if (ret) {
636 pr_err("%s: Failed to request gpio %d\n", __func__,
637 PM8921_GPIO_PM_TO_SYS(23));
638 return ret;
639 }
640
641 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
642 if (ret)
643 pr_err("%s: Failed to configure gpio %d\n", __func__,
644 PM8921_GPIO_PM_TO_SYS(23));
645 else
646 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
647
648 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
649 if (ret) {
650 pr_err("%s: Failed to request gpio %d\n", __func__,
651 PM8921_GPIO_PM_TO_SYS(35));
652 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
653 return ret;
654 }
655 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
656 if (ret)
657 pr_err("%s: Failed to configure gpio %d\n", __func__,
658 PM8921_GPIO_PM_TO_SYS(35));
659 else
660 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 1);
661
662 return 0;
663}
664static void msm8960_free_headset_mic_gpios(void)
665{
666 if (msm8960_headset_gpios_configured) {
667 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
668 gpio_free(PM8921_GPIO_PM_TO_SYS(35));
669 }
670}
671
672static int __init msm8960_audio_init(void)
673{
674 int ret;
675
676 msm8960_snd_device = platform_device_alloc("soc-audio", 0);
677 if (!msm8960_snd_device) {
678 pr_err("Platform device allocation failed\n");
679 return -ENOMEM;
680 }
681
682 platform_set_drvdata(msm8960_snd_device, &snd_soc_card_msm8960);
683 ret = platform_device_add(msm8960_snd_device);
684 if (ret) {
685 platform_device_put(msm8960_snd_device);
686 return ret;
687 }
688
689 if (msm8960_configure_headset_mic_gpios()) {
690 pr_err("%s Fail to configure headset mic gpios\n", __func__);
691 msm8960_headset_gpios_configured = 0;
692 } else
693 msm8960_headset_gpios_configured = 1;
694
695 return ret;
696
697}
698module_init(msm8960_audio_init);
699
700static void __exit msm8960_audio_exit(void)
701{
702 msm8960_free_headset_mic_gpios();
703 platform_device_unregister(msm8960_snd_device);
704}
705module_exit(msm8960_audio_exit);
706
707MODULE_DESCRIPTION("ALSA SoC MSM8960");
708MODULE_LICENSE("GPL v2");