blob: 2ab3b5b8d89287eae0ef1f79d4065bd4f83c090c [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
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -070042#define BTSCO_RATE_8KHZ 8000
43#define BTSCO_RATE_16KHZ 16000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044
45static int msm8960_spk_control;
46static int msm8960_pamp_on;
47static int msm8960_slim_0_rx_ch = 1;
48static int msm8960_slim_0_tx_ch = 1;
49
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -070050static int msm8960_btsco_rate = BTSCO_RATE_8KHZ;
51static int msm8960_btsco_ch = 1;
52
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053struct tabla_mbhc_calibration tabla_cal = {
54 .bias = TABLA_MICBIAS2,
55 .tldoh = 100,
56 .bg_fast_settle = 100,
57 .mic_current = TABLA_PID_MIC_5_UA,
58 .mic_pid = 100,
59 .hph_current = TABLA_PID_MIC_5_UA,
60 .setup_plug_removal_delay = 1000000,
61 .shutdown_plug_removal = 100000,
62};
63
64static struct clk *codec_clk;
65static int clk_users;
66
67static int msm8960_headset_gpios_configured;
68
69static struct snd_soc_jack hs_jack;
Bradley Rubincb1e2732011-06-23 16:49:20 -070070static struct snd_soc_jack button_jack;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071
72static void codec_poweramp_on(void)
73{
74 int ret = 0;
75
76 struct pm_gpio param = {
77 .direction = PM_GPIO_DIR_OUT,
78 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
79 .output_value = 1,
80 .pull = PM_GPIO_PULL_NO,
81 .vin_sel = PM_GPIO_VIN_S4,
82 .out_strength = PM_GPIO_STRENGTH_MED,
83 .function = PM_GPIO_FUNC_NORMAL,
84 };
85
86 if (msm8960_pamp_on)
87 return;
88
89 pr_debug("%s: enable stereo spkr amp\n", __func__);
90 ret = gpio_request(MSM_CDC_PAMPL, "CDC PAMP1");
91 if (ret) {
92 pr_err("%s: Error requesting GPIO %d\n", __func__,
93 MSM_CDC_PAMPL);
94 return;
95 }
96 ret = pm8xxx_gpio_config(MSM_CDC_PAMPL, &param);
97 if (ret)
98 pr_err("%s: Failed to configure gpio %d\n", __func__,
99 MSM_CDC_PAMPL);
100 else
101 gpio_direction_output(MSM_CDC_PAMPL, 1);
102
103 ret = gpio_request(MSM_CDC_PAMPR, "CDC PAMPL");
104 if (ret) {
105 pr_err("%s: Error requesting GPIO %d\n", __func__,
106 MSM_CDC_PAMPR);
107 gpio_free(MSM_CDC_PAMPL);
108 return;
109 }
110 ret = pm8xxx_gpio_config(MSM_CDC_PAMPR, &param);
111 if (ret)
112 pr_err("%s: Failed to configure gpio %d\n", __func__,
113 MSM_CDC_PAMPR);
114 else
115 gpio_direction_output(MSM_CDC_PAMPR, 1);
116
117 msm8960_pamp_on = 1;
118}
119static void codec_poweramp_off(void)
120{
121 if (!msm8960_pamp_on)
122 return;
123
124 pr_debug("%s: disable stereo spkr amp\n", __func__);
125 gpio_direction_output(MSM_CDC_PAMPL, 0);
126 gpio_free(MSM_CDC_PAMPL);
127 gpio_direction_output(MSM_CDC_PAMPR, 0);
128 gpio_free(MSM_CDC_PAMPR);
129 msm8960_pamp_on = 0;
130}
131static void msm8960_ext_control(struct snd_soc_codec *codec)
132{
133 struct snd_soc_dapm_context *dapm = &codec->dapm;
134
135 pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
136 if (msm8960_spk_control == MSM8960_SPK_ON)
137 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
138 else
139 snd_soc_dapm_disable_pin(dapm, "Ext Spk");
140
141 snd_soc_dapm_sync(dapm);
142}
143
144static int msm8960_get_spk(struct snd_kcontrol *kcontrol,
145 struct snd_ctl_elem_value *ucontrol)
146{
147 pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
148 ucontrol->value.integer.value[0] = msm8960_spk_control;
149 return 0;
150}
151static int msm8960_set_spk(struct snd_kcontrol *kcontrol,
152 struct snd_ctl_elem_value *ucontrol)
153{
154 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
155
156 pr_debug("%s()\n", __func__);
157 if (msm8960_spk_control == ucontrol->value.integer.value[0])
158 return 0;
159
160 msm8960_spk_control = ucontrol->value.integer.value[0];
161 msm8960_ext_control(codec);
162 return 1;
163}
164static int msm8960_spkramp_event(struct snd_soc_dapm_widget *w,
165 struct snd_kcontrol *k, int event)
166{
167 pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
168 if (SND_SOC_DAPM_EVENT_ON(event))
169 codec_poweramp_on();
170 else
171 codec_poweramp_off();
172 return 0;
173}
174
175static const struct snd_soc_dapm_widget msm8960_dapm_widgets[] = {
176 SND_SOC_DAPM_SPK("Ext Spk", msm8960_spkramp_event),
177 SND_SOC_DAPM_MIC("Handset Mic", NULL),
178 SND_SOC_DAPM_MIC("Headset Mic", NULL),
179 SND_SOC_DAPM_MIC("Digital Mic1", NULL),
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700180 SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
181 SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700182
183 /* Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP. */
184 SND_SOC_DAPM_MIC("Digital Mic1", NULL),
185
186 /* Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP. */
187 SND_SOC_DAPM_MIC("Digital Mic2", NULL),
188
189 /* Digital Mic4. Back Top Digital Mic on Fluid. */
190 SND_SOC_DAPM_MIC("Digital Mic4", NULL),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191};
192
Bradley Rubin229c6a52011-07-12 16:18:48 -0700193static const struct snd_soc_dapm_route common_audio_map[] = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194 /* Speaker path */
195 {"Ext Spk", NULL, "LINEOUT"},
196
197 /* Microphone path */
Bradley Rubin229c6a52011-07-12 16:18:48 -0700198 {"AMIC1", NULL, "MIC BIAS1 Internal1"},
199 {"MIC BIAS1 Internal1", NULL, "Handset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700200
201 {"AMIC2", NULL, "MIC BIAS2 External"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202 {"MIC BIAS2 External", NULL, "Headset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700203
Kiran Kandie9bf86a2011-07-21 16:50:41 -0700204 {"HEADPHONE", NULL, "LDO_H"},
205
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700206 /**
207 * Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP.
208 * Conncted to DMIC2 Input on Tabla codec.
209 */
210 {"DMIC2", NULL, "MIC BIAS1 External"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700211 {"MIC BIAS1 External", NULL, "Digital Mic1"},
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700212
213 /**
214 * Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP.
215 * Conncted to DMIC1 Input on Tabla codec.
216 */
217 {"DMIC1", NULL, "MIC BIAS1 External"},
218 {"MIC BIAS1 External", NULL, "Digital Mic2"},
219
220 /**
221 * Digital Mic4. Back top Digital Mic on Fluid.
222 * Conncted to DMIC3 Input on Tabla codec.
223 */
224 {"DMIC3", NULL, "MIC BIAS3 External"},
225 {"MIC BIAS3 External", NULL, "Digital Mic4"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226};
227
Bradley Rubin229c6a52011-07-12 16:18:48 -0700228static const struct snd_soc_dapm_route cdp_audio_map[] = {
229 {"AMIC3", NULL, "MIC BIAS3 External"},
230 {"MIC BIAS3 External", NULL, "ANCRight Headset Mic"},
231
232 {"AMIC4", NULL, "MIC BIAS4 External"},
233 {"MIC BIAS4 External", NULL, "ANCLeft Headset Mic"},
234};
235
236static const struct snd_soc_dapm_route fluid_audio_map[] = {
237 {"AMIC3", NULL, "MIC BIAS3 Internal1"},
238 {"MIC BIAS3 Internal1", NULL, "ANCRight Headset Mic"},
239
240 {"AMIC4", NULL, "MIC BIAS1 Internal2"},
241 {"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
242};
243
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700244static const char *spk_function[] = {"Off", "On"};
Patrick Lai9f4b4292011-07-16 22:11:09 -0700245static const char *slim0_rx_ch_text[] = {"One", "Two"};
246static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
247
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248static const struct soc_enum msm8960_enum[] = {
249 SOC_ENUM_SINGLE_EXT(2, spk_function),
Patrick Lai9f4b4292011-07-16 22:11:09 -0700250 SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
251 SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252};
253
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700254static const char *btsco_rate_text[] = {"8000", "16000"};
255static const struct soc_enum msm8960_btsco_enum[] = {
256 SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
257};
258
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700259static int msm8960_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
260 struct snd_ctl_elem_value *ucontrol)
261{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700262 pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263 msm8960_slim_0_rx_ch);
Patrick Lai9f4b4292011-07-16 22:11:09 -0700264 ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265 return 0;
266}
267
268static int msm8960_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
269 struct snd_ctl_elem_value *ucontrol)
270{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700271 msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272
Patrick Lai9f4b4292011-07-16 22:11:09 -0700273 pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700274 msm8960_slim_0_rx_ch);
275 return 1;
276}
277
278static int msm8960_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
279 struct snd_ctl_elem_value *ucontrol)
280{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700281 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282 msm8960_slim_0_tx_ch);
Patrick Lai9f4b4292011-07-16 22:11:09 -0700283 ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284 return 0;
285}
286
287static int msm8960_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
288 struct snd_ctl_elem_value *ucontrol)
289{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700290 msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291
292 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
293 msm8960_slim_0_tx_ch);
294 return 1;
295}
296
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700297static int msm8960_btsco_rate_get(struct snd_kcontrol *kcontrol,
298 struct snd_ctl_elem_value *ucontrol)
299{
300 pr_debug("%s: msm8960_btsco_rate = %d", __func__,
301 msm8960_btsco_rate);
302 ucontrol->value.integer.value[0] = msm8960_btsco_rate;
303 return 0;
304}
305
306static int msm8960_btsco_rate_put(struct snd_kcontrol *kcontrol,
307 struct snd_ctl_elem_value *ucontrol)
308{
309 switch (ucontrol->value.integer.value[0]) {
310 case 0:
311 msm8960_btsco_rate = BTSCO_RATE_8KHZ;
312 break;
313 case 1:
314 msm8960_btsco_rate = BTSCO_RATE_16KHZ;
315 break;
316 default:
317 msm8960_btsco_rate = BTSCO_RATE_8KHZ;
318 break;
319 }
320 pr_debug("%s: msm8960_btsco_rate = %d\n", __func__,
321 msm8960_btsco_rate);
322 return 0;
323}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324
325static const struct snd_kcontrol_new tabla_msm8960_controls[] = {
326 SOC_ENUM_EXT("Speaker Function", msm8960_enum[0], msm8960_get_spk,
327 msm8960_set_spk),
Patrick Lai9f4b4292011-07-16 22:11:09 -0700328 SOC_ENUM_EXT("SLIM_0_RX Channels", msm8960_enum[1],
329 msm8960_slim_0_rx_ch_get, msm8960_slim_0_rx_ch_put),
330 SOC_ENUM_EXT("SLIM_0_TX Channels", msm8960_enum[2],
331 msm8960_slim_0_tx_ch_get, msm8960_slim_0_tx_ch_put),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700332};
333
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700334static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
335 SOC_ENUM_EXT("Internal BTSCO SampleRate", msm8960_btsco_enum[0],
336 msm8960_btsco_rate_get, msm8960_btsco_rate_put),
337};
338
339static int msm8960_btsco_init(struct snd_soc_pcm_runtime *rtd)
340{
341 int err = 0;
342 struct snd_soc_platform *platform = rtd->platform;
343
344 err = snd_soc_add_platform_controls(platform,
345 int_btsco_rate_mixer_controls,
346 ARRAY_SIZE(int_btsco_rate_mixer_controls));
347 if (err < 0)
348 return err;
349 return 0;
350}
351
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700352static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
353{
354 int err;
355 struct snd_soc_codec *codec = rtd->codec;
356 struct snd_soc_dapm_context *dapm = &codec->dapm;
357
358 pr_debug("%s()\n", __func__);
359
360 err = snd_soc_add_controls(codec, tabla_msm8960_controls,
361 ARRAY_SIZE(tabla_msm8960_controls));
362 if (err < 0)
363 return err;
364
365 snd_soc_dapm_new_controls(dapm, msm8960_dapm_widgets,
366 ARRAY_SIZE(msm8960_dapm_widgets));
367
Bradley Rubin229c6a52011-07-12 16:18:48 -0700368 snd_soc_dapm_add_routes(dapm, common_audio_map,
369 ARRAY_SIZE(common_audio_map));
370
371 if (machine_is_msm8960_cdp())
372 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
373 ARRAY_SIZE(cdp_audio_map));
374 else if (machine_is_msm8960_mtp())
375 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
376 ARRAY_SIZE(cdp_audio_map));
377 else if (machine_is_msm8960_fluid())
378 snd_soc_dapm_add_routes(dapm, fluid_audio_map,
379 ARRAY_SIZE(fluid_audio_map));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380
381 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
382
383 snd_soc_dapm_sync(dapm);
384
385 err = snd_soc_jack_new(codec, "Headset Jack",
386 SND_JACK_HEADSET, &hs_jack);
387 if (err) {
388 pr_err("failed to create new jack\n");
389 return err;
390 }
Bradley Rubincb1e2732011-06-23 16:49:20 -0700391
392 err = snd_soc_jack_new(codec, "Button Jack",
393 SND_JACK_BTN_0, &button_jack);
394 if (err) {
395 pr_err("failed to create new jack\n");
396 return err;
397 }
398
399 tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_cal);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700400
401 return 0;
402}
403
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404static struct snd_soc_dsp_link lpa_fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700405 .playback = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 .trigger = {
407 SND_SOC_DSP_TRIGGER_POST,
408 SND_SOC_DSP_TRIGGER_POST
409 },
410};
411
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700412static struct snd_soc_dsp_link fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700413 .playback = true,
414 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415 .trigger = {
416 SND_SOC_DSP_TRIGGER_POST,
417 SND_SOC_DSP_TRIGGER_POST
418 },
419};
420
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700421static struct snd_soc_dsp_link slimbus0_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700422 .playback = true,
423 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700424 .trigger = {
425 SND_SOC_DSP_TRIGGER_POST,
426 SND_SOC_DSP_TRIGGER_POST
427 },
428};
429
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700430static struct snd_soc_dsp_link int_fm_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700431 .playback = true,
432 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700433 .trigger = {
434 SND_SOC_DSP_TRIGGER_POST,
435 SND_SOC_DSP_TRIGGER_POST
436 },
437};
438
439static int msm8960_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
440 struct snd_pcm_hw_params *params)
441{
442 struct snd_interval *rate = hw_param_interval(params,
443 SNDRV_PCM_HW_PARAM_RATE);
444
445 struct snd_interval *channels = hw_param_interval(params,
446 SNDRV_PCM_HW_PARAM_CHANNELS);
447
448 pr_debug("%s()\n", __func__);
449 rate->min = rate->max = 48000;
450 channels->min = channels->max = msm8960_slim_0_rx_ch;
451
452 return 0;
453}
454
455static int msm8960_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
456 struct snd_pcm_hw_params *params)
457{
458 struct snd_interval *rate = hw_param_interval(params,
459 SNDRV_PCM_HW_PARAM_RATE);
460
461 struct snd_interval *channels = hw_param_interval(params,
462 SNDRV_PCM_HW_PARAM_CHANNELS);
463
464 pr_debug("%s()\n", __func__);
465 rate->min = rate->max = 48000;
466 channels->min = channels->max = msm8960_slim_0_tx_ch;
467
468 return 0;
469}
470
471static int msm8960_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
472 struct snd_pcm_hw_params *params)
473{
474 struct snd_interval *rate = hw_param_interval(params,
475 SNDRV_PCM_HW_PARAM_RATE);
476
477 pr_debug("%s()\n", __func__);
478 rate->min = rate->max = 48000;
479
480 return 0;
481}
482
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700483static int msm8960_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
484 struct snd_pcm_hw_params *params)
485{
486 struct snd_interval *rate = hw_param_interval(params,
487 SNDRV_PCM_HW_PARAM_RATE);
488
489 struct snd_interval *channels = hw_param_interval(params,
490 SNDRV_PCM_HW_PARAM_CHANNELS);
491
492 rate->min = rate->max = msm8960_btsco_rate;
493 channels->min = channels->max = msm8960_btsco_ch;
494
495 return 0;
496}
497
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700498static int msm8960_startup(struct snd_pcm_substream *substream)
499{
500 if (clk_users++)
501 return 0;
502
503 codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
504 if (codec_clk) {
505 clk_set_rate(codec_clk, 12288000);
506 clk_enable(codec_clk);
507 } else {
508 pr_err("%s: Error setting Tabla MCLK\n", __func__);
509 clk_users--;
510 return -EINVAL;
511 }
512 return 0;
513}
514
515static void msm8960_shutdown(struct snd_pcm_substream *substream)
516{
517 clk_users--;
518 if (!clk_users) {
519 clk_disable(codec_clk);
520 clk_put(codec_clk);
521 }
522}
523
524static struct snd_soc_ops msm8960_be_ops = {
525 .startup = msm8960_startup,
526 .shutdown = msm8960_shutdown,
527};
528
529/* Digital audio interface glue - connects codec <---> CPU */
530static struct snd_soc_dai_link msm8960_dai[] = {
531 /* FrontEnd DAI Links */
532 {
533 .name = "MSM8960 Media1",
534 .stream_name = "MultiMedia1",
535 .cpu_dai_name = "MultiMedia1",
536 .platform_name = "msm-pcm-dsp",
537 .dynamic = 1,
538 .dsp_link = &fe_media,
539 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
540 },
541 {
542 .name = "MSM8960 Media2",
543 .stream_name = "MultiMedia2",
544 .cpu_dai_name = "MultiMedia2",
545 .platform_name = "msm-pcm-dsp",
546 .dynamic = 1,
547 .dsp_link = &fe_media,
548 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
549 },
550 {
551 .name = "Circuit-Switch Voice",
552 .stream_name = "CS-Voice",
553 .cpu_dai_name = "CS-VOICE",
554 .platform_name = "msm-pcm-voice",
555 .dynamic = 1,
556 .dsp_link = &fe_media,
557 .be_id = MSM_FRONTEND_DAI_CS_VOICE,
558 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
559 },
560 {
561 .name = "MSM VoIP",
562 .stream_name = "VoIP",
563 .cpu_dai_name = "VoIP",
564 .platform_name = "msm-voip-dsp",
565 .dynamic = 1,
566 .dsp_link = &fe_media,
567 .be_id = MSM_FRONTEND_DAI_VOIP,
568 },
569 {
570 .name = "MSM8960 LPA",
571 .stream_name = "LPA",
572 .cpu_dai_name = "MultiMedia3",
573 .platform_name = "msm-pcm-lpa",
574 .dynamic = 1,
575 .dsp_link = &lpa_fe_media,
576 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
577 },
578 /* Hostless PMC purpose */
579 {
580 .name = "SLIMBUS_0 Hostless",
581 .stream_name = "SLIMBUS_0 Hostless",
582 .cpu_dai_name = "SLIMBUS0_HOSTLESS",
583 .platform_name = "msm-pcm-hostless",
584 .dynamic = 1,
585 .dsp_link = &slimbus0_hl_media,
586 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
587 /* .be_id = do not care */
588 },
589 {
590 .name = "INT_FM Hostless",
591 .stream_name = "INT_FM Hostless",
592 .cpu_dai_name = "INT_FM_HOSTLESS",
593 .platform_name = "msm-pcm-hostless",
594 .dynamic = 1,
595 .dsp_link = &int_fm_hl_media,
596 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
597 /* .be_id = do not care */
598 },
599 /* Backend DAI Links */
600 {
601 .name = LPASS_BE_SLIMBUS_0_RX,
602 .stream_name = "Slimbus Playback",
603 .cpu_dai_name = "msm-dai-q6.16384",
604 .platform_name = "msm-pcm-routing",
605 .codec_name = "tabla_codec",
606 .codec_dai_name = "tabla_rx1",
607 .no_pcm = 1,
608 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
609 .init = &msm8960_audrx_init,
610 .be_hw_params_fixup = msm8960_slim_0_rx_be_hw_params_fixup,
611 .ops = &msm8960_be_ops,
612 },
613 {
614 .name = LPASS_BE_SLIMBUS_0_TX,
615 .stream_name = "Slimbus Capture",
616 .cpu_dai_name = "msm-dai-q6.16385",
617 .platform_name = "msm-pcm-routing",
618 .codec_name = "tabla_codec",
619 .codec_dai_name = "tabla_tx1",
620 .no_pcm = 1,
621 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
622 .be_hw_params_fixup = msm8960_slim_0_tx_be_hw_params_fixup,
623 .ops = &msm8960_be_ops,
624 },
625 /* Backend BT/FM DAI Links */
626 {
627 .name = LPASS_BE_INT_BT_SCO_RX,
628 .stream_name = "Internal BT-SCO Playback",
629 .cpu_dai_name = "msm-dai-q6.12288",
630 .platform_name = "msm-pcm-routing",
631 .codec_name = "msm-stub-codec.1",
632 .codec_dai_name = "msm-stub-rx",
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700633 .init = &msm8960_btsco_init,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700634 .no_pcm = 1,
635 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700636 .be_hw_params_fixup = msm8960_btsco_be_hw_params_fixup,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700637 },
638 {
639 .name = LPASS_BE_INT_BT_SCO_TX,
640 .stream_name = "Internal BT-SCO Capture",
641 .cpu_dai_name = "msm-dai-q6.12289",
642 .platform_name = "msm-pcm-routing",
643 .codec_name = "msm-stub-codec.1",
644 .codec_dai_name = "msm-stub-tx",
645 .no_pcm = 1,
646 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700647 .be_hw_params_fixup = msm8960_btsco_be_hw_params_fixup,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700648 },
649 {
650 .name = LPASS_BE_INT_FM_RX,
651 .stream_name = "Internal FM Playback",
652 .cpu_dai_name = "msm-dai-q6.12292",
653 .platform_name = "msm-pcm-routing",
654 .codec_name = "msm-stub-codec.1",
655 .codec_dai_name = "msm-stub-rx",
656 .no_pcm = 1,
657 .be_id = MSM_BACKEND_DAI_INT_FM_RX,
658 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
659 },
660 {
661 .name = LPASS_BE_INT_FM_TX,
662 .stream_name = "Internal FM Capture",
663 .cpu_dai_name = "msm-dai-q6.12293",
664 .platform_name = "msm-pcm-routing",
665 .codec_name = "msm-stub-codec.1",
666 .codec_dai_name = "msm-stub-tx",
667 .no_pcm = 1,
668 .be_id = MSM_BACKEND_DAI_INT_FM_TX,
669 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
670 },
671 /* HDMI BACK END DAI Link */
672 {
673 .name = LPASS_BE_HDMI,
674 .stream_name = "HDMI Playback",
675 .cpu_dai_name = "msm-dai-q6.8",
676 .platform_name = "msm-pcm-routing",
677 .codec_name = "msm-stub-codec.1",
678 .codec_dai_name = "msm-stub-rx",
679 .no_pcm = 1,
680 .no_codec = 1,
681 .be_id = MSM_BACKEND_DAI_HDMI_RX,
682 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
683 },
684};
685
686struct snd_soc_card snd_soc_card_msm8960 = {
687 .name = "msm8960-snd-card",
688 .dai_link = msm8960_dai,
689 .num_links = ARRAY_SIZE(msm8960_dai),
690};
691
692static struct platform_device *msm8960_snd_device;
693
694static int msm8960_configure_headset_mic_gpios(void)
695{
696 int ret;
697 struct pm_gpio param = {
698 .direction = PM_GPIO_DIR_OUT,
699 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
700 .output_value = 1,
701 .pull = PM_GPIO_PULL_NO,
702 .vin_sel = PM_GPIO_VIN_S4,
703 .out_strength = PM_GPIO_STRENGTH_MED,
704 .function = PM_GPIO_FUNC_NORMAL,
705 };
706
707 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
708 if (ret) {
709 pr_err("%s: Failed to request gpio %d\n", __func__,
710 PM8921_GPIO_PM_TO_SYS(23));
711 return ret;
712 }
713
714 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
715 if (ret)
716 pr_err("%s: Failed to configure gpio %d\n", __func__,
717 PM8921_GPIO_PM_TO_SYS(23));
718 else
719 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
720
721 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
722 if (ret) {
723 pr_err("%s: Failed to request gpio %d\n", __func__,
724 PM8921_GPIO_PM_TO_SYS(35));
725 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
726 return ret;
727 }
728 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
729 if (ret)
730 pr_err("%s: Failed to configure gpio %d\n", __func__,
731 PM8921_GPIO_PM_TO_SYS(35));
732 else
733 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 1);
734
735 return 0;
736}
737static void msm8960_free_headset_mic_gpios(void)
738{
739 if (msm8960_headset_gpios_configured) {
740 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
741 gpio_free(PM8921_GPIO_PM_TO_SYS(35));
742 }
743}
744
745static int __init msm8960_audio_init(void)
746{
747 int ret;
748
749 msm8960_snd_device = platform_device_alloc("soc-audio", 0);
750 if (!msm8960_snd_device) {
751 pr_err("Platform device allocation failed\n");
752 return -ENOMEM;
753 }
754
755 platform_set_drvdata(msm8960_snd_device, &snd_soc_card_msm8960);
756 ret = platform_device_add(msm8960_snd_device);
757 if (ret) {
758 platform_device_put(msm8960_snd_device);
759 return ret;
760 }
761
762 if (msm8960_configure_headset_mic_gpios()) {
763 pr_err("%s Fail to configure headset mic gpios\n", __func__);
764 msm8960_headset_gpios_configured = 0;
765 } else
766 msm8960_headset_gpios_configured = 1;
767
768 return ret;
769
770}
771module_init(msm8960_audio_init);
772
773static void __exit msm8960_audio_exit(void)
774{
775 msm8960_free_headset_mic_gpios();
776 platform_device_unregister(msm8960_snd_device);
777}
778module_exit(msm8960_audio_exit);
779
780MODULE_DESCRIPTION("ALSA SoC MSM8960");
781MODULE_LICENSE("GPL v2");