blob: 0d05721645acf44852de4df9265a60f8e6963740 [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
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700183 SND_SOC_DAPM_MIC("Digital Mic1", NULL),
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700184 SND_SOC_DAPM_MIC("Digital Mic2", NULL),
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700185 SND_SOC_DAPM_MIC("Digital Mic3", NULL),
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700186 SND_SOC_DAPM_MIC("Digital Mic4", NULL),
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700187 SND_SOC_DAPM_MIC("Digital Mic5", NULL),
188 SND_SOC_DAPM_MIC("Digital Mic6", NULL),
189
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700190};
191
Bradley Rubin229c6a52011-07-12 16:18:48 -0700192static const struct snd_soc_dapm_route common_audio_map[] = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193 /* Speaker path */
194 {"Ext Spk", NULL, "LINEOUT"},
195
196 /* Microphone path */
Bradley Rubin229c6a52011-07-12 16:18:48 -0700197 {"AMIC1", NULL, "MIC BIAS1 Internal1"},
198 {"MIC BIAS1 Internal1", NULL, "Handset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700199
200 {"AMIC2", NULL, "MIC BIAS2 External"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 {"MIC BIAS2 External", NULL, "Headset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700202
Kiran Kandie9bf86a2011-07-21 16:50:41 -0700203 {"HEADPHONE", NULL, "LDO_H"},
204
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700205 /**
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700206 * The digital Mic routes are setup considering
207 * fluid as default device.
208 */
209
210 /**
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700211 * Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP.
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700212 * Digital Mic GM5 on CDP mainboard.
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700213 * Conncted to DMIC2 Input on Tabla codec.
214 */
215 {"DMIC2", NULL, "MIC BIAS1 External"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700216 {"MIC BIAS1 External", NULL, "Digital Mic1"},
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700217
218 /**
219 * Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP.
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700220 * Digital Mic GM6 on CDP mainboard.
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700221 * Conncted to DMIC1 Input on Tabla codec.
222 */
223 {"DMIC1", NULL, "MIC BIAS1 External"},
224 {"MIC BIAS1 External", NULL, "Digital Mic2"},
225
226 /**
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700227 * Digital Mic3. Back Bottom Digital Mic on Fluid.
228 * Digital Mic GM1 on CDP mainboard.
229 * Conncted to DMIC4 Input on Tabla codec.
230 */
231 {"DMIC4", NULL, "MIC BIAS3 External"},
232 {"MIC BIAS3 External", NULL, "Digital Mic3"},
233
234 /**
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700235 * Digital Mic4. Back top Digital Mic on Fluid.
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700236 * Digital Mic GM2 on CDP mainboard.
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700237 * Conncted to DMIC3 Input on Tabla codec.
238 */
239 {"DMIC3", NULL, "MIC BIAS3 External"},
240 {"MIC BIAS3 External", NULL, "Digital Mic4"},
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700241
242 /**
243 * Digital Mic5. Front top Digital Mic on Fluid.
244 * Digital Mic GM3 on CDP mainboard.
245 * Conncted to DMIC5 Input on Tabla codec.
246 */
247 {"DMIC5", NULL, "MIC BIAS4 External"},
248 {"MIC BIAS4 External", NULL, "Digital Mic5"},
249
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700250};
251
Bradley Rubin229c6a52011-07-12 16:18:48 -0700252static const struct snd_soc_dapm_route cdp_audio_map[] = {
253 {"AMIC3", NULL, "MIC BIAS3 External"},
254 {"MIC BIAS3 External", NULL, "ANCRight Headset Mic"},
255
256 {"AMIC4", NULL, "MIC BIAS4 External"},
257 {"MIC BIAS4 External", NULL, "ANCLeft Headset Mic"},
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700258
259 /** Digital Mic GM4 on CDP mainboard.
260 * Connected to DMIC6 input on Tabla codec.
261 */
262 {"DMIC6", NULL, "MIC BIAS4 External"},
263 {"MIC BIAS4 External", NULL, "Digital Mic6"},
264
Bradley Rubin229c6a52011-07-12 16:18:48 -0700265};
266
267static const struct snd_soc_dapm_route fluid_audio_map[] = {
268 {"AMIC3", NULL, "MIC BIAS3 Internal1"},
269 {"MIC BIAS3 Internal1", NULL, "ANCRight Headset Mic"},
270
271 {"AMIC4", NULL, "MIC BIAS1 Internal2"},
272 {"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
273};
274
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275static const char *spk_function[] = {"Off", "On"};
Patrick Lai9f4b4292011-07-16 22:11:09 -0700276static const char *slim0_rx_ch_text[] = {"One", "Two"};
277static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
278
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279static const struct soc_enum msm8960_enum[] = {
280 SOC_ENUM_SINGLE_EXT(2, spk_function),
Patrick Lai9f4b4292011-07-16 22:11:09 -0700281 SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
282 SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700283};
284
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700285static const char *btsco_rate_text[] = {"8000", "16000"};
286static const struct soc_enum msm8960_btsco_enum[] = {
287 SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
288};
289
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700290static int msm8960_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
291 struct snd_ctl_elem_value *ucontrol)
292{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700293 pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294 msm8960_slim_0_rx_ch);
Patrick Lai9f4b4292011-07-16 22:11:09 -0700295 ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296 return 0;
297}
298
299static int msm8960_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
300 struct snd_ctl_elem_value *ucontrol)
301{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700302 msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303
Patrick Lai9f4b4292011-07-16 22:11:09 -0700304 pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305 msm8960_slim_0_rx_ch);
306 return 1;
307}
308
309static int msm8960_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
310 struct snd_ctl_elem_value *ucontrol)
311{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700312 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313 msm8960_slim_0_tx_ch);
Patrick Lai9f4b4292011-07-16 22:11:09 -0700314 ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700315 return 0;
316}
317
318static int msm8960_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
319 struct snd_ctl_elem_value *ucontrol)
320{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700321 msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700322
323 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
324 msm8960_slim_0_tx_ch);
325 return 1;
326}
327
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700328static int msm8960_btsco_rate_get(struct snd_kcontrol *kcontrol,
329 struct snd_ctl_elem_value *ucontrol)
330{
331 pr_debug("%s: msm8960_btsco_rate = %d", __func__,
332 msm8960_btsco_rate);
333 ucontrol->value.integer.value[0] = msm8960_btsco_rate;
334 return 0;
335}
336
337static int msm8960_btsco_rate_put(struct snd_kcontrol *kcontrol,
338 struct snd_ctl_elem_value *ucontrol)
339{
340 switch (ucontrol->value.integer.value[0]) {
341 case 0:
342 msm8960_btsco_rate = BTSCO_RATE_8KHZ;
343 break;
344 case 1:
345 msm8960_btsco_rate = BTSCO_RATE_16KHZ;
346 break;
347 default:
348 msm8960_btsco_rate = BTSCO_RATE_8KHZ;
349 break;
350 }
351 pr_debug("%s: msm8960_btsco_rate = %d\n", __func__,
352 msm8960_btsco_rate);
353 return 0;
354}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700355
356static const struct snd_kcontrol_new tabla_msm8960_controls[] = {
357 SOC_ENUM_EXT("Speaker Function", msm8960_enum[0], msm8960_get_spk,
358 msm8960_set_spk),
Patrick Lai9f4b4292011-07-16 22:11:09 -0700359 SOC_ENUM_EXT("SLIM_0_RX Channels", msm8960_enum[1],
360 msm8960_slim_0_rx_ch_get, msm8960_slim_0_rx_ch_put),
361 SOC_ENUM_EXT("SLIM_0_TX Channels", msm8960_enum[2],
362 msm8960_slim_0_tx_ch_get, msm8960_slim_0_tx_ch_put),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700363};
364
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700365static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
366 SOC_ENUM_EXT("Internal BTSCO SampleRate", msm8960_btsco_enum[0],
367 msm8960_btsco_rate_get, msm8960_btsco_rate_put),
368};
369
370static int msm8960_btsco_init(struct snd_soc_pcm_runtime *rtd)
371{
372 int err = 0;
373 struct snd_soc_platform *platform = rtd->platform;
374
375 err = snd_soc_add_platform_controls(platform,
376 int_btsco_rate_mixer_controls,
377 ARRAY_SIZE(int_btsco_rate_mixer_controls));
378 if (err < 0)
379 return err;
380 return 0;
381}
382
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
384{
385 int err;
386 struct snd_soc_codec *codec = rtd->codec;
387 struct snd_soc_dapm_context *dapm = &codec->dapm;
388
389 pr_debug("%s()\n", __func__);
390
391 err = snd_soc_add_controls(codec, tabla_msm8960_controls,
392 ARRAY_SIZE(tabla_msm8960_controls));
393 if (err < 0)
394 return err;
395
396 snd_soc_dapm_new_controls(dapm, msm8960_dapm_widgets,
397 ARRAY_SIZE(msm8960_dapm_widgets));
398
Bradley Rubin229c6a52011-07-12 16:18:48 -0700399 snd_soc_dapm_add_routes(dapm, common_audio_map,
400 ARRAY_SIZE(common_audio_map));
401
402 if (machine_is_msm8960_cdp())
403 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
404 ARRAY_SIZE(cdp_audio_map));
405 else if (machine_is_msm8960_mtp())
406 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
407 ARRAY_SIZE(cdp_audio_map));
408 else if (machine_is_msm8960_fluid())
409 snd_soc_dapm_add_routes(dapm, fluid_audio_map,
410 ARRAY_SIZE(fluid_audio_map));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411
412 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
413
414 snd_soc_dapm_sync(dapm);
415
416 err = snd_soc_jack_new(codec, "Headset Jack",
417 SND_JACK_HEADSET, &hs_jack);
418 if (err) {
419 pr_err("failed to create new jack\n");
420 return err;
421 }
Bradley Rubincb1e2732011-06-23 16:49:20 -0700422
423 err = snd_soc_jack_new(codec, "Button Jack",
424 SND_JACK_BTN_0, &button_jack);
425 if (err) {
426 pr_err("failed to create new jack\n");
427 return err;
428 }
429
430 tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_cal);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431
432 return 0;
433}
434
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435static struct snd_soc_dsp_link lpa_fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700436 .playback = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437 .trigger = {
438 SND_SOC_DSP_TRIGGER_POST,
439 SND_SOC_DSP_TRIGGER_POST
440 },
441};
442
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700443static struct snd_soc_dsp_link fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700444 .playback = true,
445 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700446 .trigger = {
447 SND_SOC_DSP_TRIGGER_POST,
448 SND_SOC_DSP_TRIGGER_POST
449 },
450};
451
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452static struct snd_soc_dsp_link slimbus0_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700453 .playback = true,
454 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700455 .trigger = {
456 SND_SOC_DSP_TRIGGER_POST,
457 SND_SOC_DSP_TRIGGER_POST
458 },
459};
460
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700461static struct snd_soc_dsp_link int_fm_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700462 .playback = true,
463 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700464 .trigger = {
465 SND_SOC_DSP_TRIGGER_POST,
466 SND_SOC_DSP_TRIGGER_POST
467 },
468};
469
470static int msm8960_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
471 struct snd_pcm_hw_params *params)
472{
473 struct snd_interval *rate = hw_param_interval(params,
474 SNDRV_PCM_HW_PARAM_RATE);
475
476 struct snd_interval *channels = hw_param_interval(params,
477 SNDRV_PCM_HW_PARAM_CHANNELS);
478
479 pr_debug("%s()\n", __func__);
480 rate->min = rate->max = 48000;
481 channels->min = channels->max = msm8960_slim_0_rx_ch;
482
483 return 0;
484}
485
486static int msm8960_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
487 struct snd_pcm_hw_params *params)
488{
489 struct snd_interval *rate = hw_param_interval(params,
490 SNDRV_PCM_HW_PARAM_RATE);
491
492 struct snd_interval *channels = hw_param_interval(params,
493 SNDRV_PCM_HW_PARAM_CHANNELS);
494
495 pr_debug("%s()\n", __func__);
496 rate->min = rate->max = 48000;
497 channels->min = channels->max = msm8960_slim_0_tx_ch;
498
499 return 0;
500}
501
502static int msm8960_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
503 struct snd_pcm_hw_params *params)
504{
505 struct snd_interval *rate = hw_param_interval(params,
506 SNDRV_PCM_HW_PARAM_RATE);
507
508 pr_debug("%s()\n", __func__);
509 rate->min = rate->max = 48000;
510
511 return 0;
512}
513
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700514static int msm8960_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
515 struct snd_pcm_hw_params *params)
516{
517 struct snd_interval *rate = hw_param_interval(params,
518 SNDRV_PCM_HW_PARAM_RATE);
519
520 struct snd_interval *channels = hw_param_interval(params,
521 SNDRV_PCM_HW_PARAM_CHANNELS);
522
523 rate->min = rate->max = msm8960_btsco_rate;
524 channels->min = channels->max = msm8960_btsco_ch;
525
526 return 0;
527}
528
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700529static int msm8960_startup(struct snd_pcm_substream *substream)
530{
531 if (clk_users++)
532 return 0;
533
534 codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
535 if (codec_clk) {
536 clk_set_rate(codec_clk, 12288000);
537 clk_enable(codec_clk);
538 } else {
539 pr_err("%s: Error setting Tabla MCLK\n", __func__);
540 clk_users--;
541 return -EINVAL;
542 }
543 return 0;
544}
545
546static void msm8960_shutdown(struct snd_pcm_substream *substream)
547{
548 clk_users--;
549 if (!clk_users) {
550 clk_disable(codec_clk);
551 clk_put(codec_clk);
552 }
553}
554
555static struct snd_soc_ops msm8960_be_ops = {
556 .startup = msm8960_startup,
557 .shutdown = msm8960_shutdown,
558};
559
560/* Digital audio interface glue - connects codec <---> CPU */
561static struct snd_soc_dai_link msm8960_dai[] = {
562 /* FrontEnd DAI Links */
563 {
564 .name = "MSM8960 Media1",
565 .stream_name = "MultiMedia1",
566 .cpu_dai_name = "MultiMedia1",
567 .platform_name = "msm-pcm-dsp",
568 .dynamic = 1,
569 .dsp_link = &fe_media,
570 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
571 },
572 {
573 .name = "MSM8960 Media2",
574 .stream_name = "MultiMedia2",
575 .cpu_dai_name = "MultiMedia2",
576 .platform_name = "msm-pcm-dsp",
577 .dynamic = 1,
578 .dsp_link = &fe_media,
579 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
580 },
581 {
582 .name = "Circuit-Switch Voice",
583 .stream_name = "CS-Voice",
584 .cpu_dai_name = "CS-VOICE",
585 .platform_name = "msm-pcm-voice",
586 .dynamic = 1,
587 .dsp_link = &fe_media,
588 .be_id = MSM_FRONTEND_DAI_CS_VOICE,
589 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
590 },
591 {
592 .name = "MSM VoIP",
593 .stream_name = "VoIP",
594 .cpu_dai_name = "VoIP",
595 .platform_name = "msm-voip-dsp",
596 .dynamic = 1,
597 .dsp_link = &fe_media,
598 .be_id = MSM_FRONTEND_DAI_VOIP,
599 },
600 {
601 .name = "MSM8960 LPA",
602 .stream_name = "LPA",
603 .cpu_dai_name = "MultiMedia3",
604 .platform_name = "msm-pcm-lpa",
605 .dynamic = 1,
606 .dsp_link = &lpa_fe_media,
607 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
608 },
609 /* Hostless PMC purpose */
610 {
611 .name = "SLIMBUS_0 Hostless",
612 .stream_name = "SLIMBUS_0 Hostless",
613 .cpu_dai_name = "SLIMBUS0_HOSTLESS",
614 .platform_name = "msm-pcm-hostless",
615 .dynamic = 1,
616 .dsp_link = &slimbus0_hl_media,
617 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
618 /* .be_id = do not care */
619 },
620 {
621 .name = "INT_FM Hostless",
622 .stream_name = "INT_FM Hostless",
623 .cpu_dai_name = "INT_FM_HOSTLESS",
624 .platform_name = "msm-pcm-hostless",
625 .dynamic = 1,
626 .dsp_link = &int_fm_hl_media,
627 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
628 /* .be_id = do not care */
629 },
630 /* Backend DAI Links */
631 {
632 .name = LPASS_BE_SLIMBUS_0_RX,
633 .stream_name = "Slimbus Playback",
634 .cpu_dai_name = "msm-dai-q6.16384",
635 .platform_name = "msm-pcm-routing",
636 .codec_name = "tabla_codec",
637 .codec_dai_name = "tabla_rx1",
638 .no_pcm = 1,
639 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
640 .init = &msm8960_audrx_init,
641 .be_hw_params_fixup = msm8960_slim_0_rx_be_hw_params_fixup,
642 .ops = &msm8960_be_ops,
643 },
644 {
645 .name = LPASS_BE_SLIMBUS_0_TX,
646 .stream_name = "Slimbus Capture",
647 .cpu_dai_name = "msm-dai-q6.16385",
648 .platform_name = "msm-pcm-routing",
649 .codec_name = "tabla_codec",
650 .codec_dai_name = "tabla_tx1",
651 .no_pcm = 1,
652 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
653 .be_hw_params_fixup = msm8960_slim_0_tx_be_hw_params_fixup,
654 .ops = &msm8960_be_ops,
655 },
656 /* Backend BT/FM DAI Links */
657 {
658 .name = LPASS_BE_INT_BT_SCO_RX,
659 .stream_name = "Internal BT-SCO Playback",
660 .cpu_dai_name = "msm-dai-q6.12288",
661 .platform_name = "msm-pcm-routing",
662 .codec_name = "msm-stub-codec.1",
663 .codec_dai_name = "msm-stub-rx",
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700664 .init = &msm8960_btsco_init,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700665 .no_pcm = 1,
666 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700667 .be_hw_params_fixup = msm8960_btsco_be_hw_params_fixup,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700668 },
669 {
670 .name = LPASS_BE_INT_BT_SCO_TX,
671 .stream_name = "Internal BT-SCO Capture",
672 .cpu_dai_name = "msm-dai-q6.12289",
673 .platform_name = "msm-pcm-routing",
674 .codec_name = "msm-stub-codec.1",
675 .codec_dai_name = "msm-stub-tx",
676 .no_pcm = 1,
677 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700678 .be_hw_params_fixup = msm8960_btsco_be_hw_params_fixup,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700679 },
680 {
681 .name = LPASS_BE_INT_FM_RX,
682 .stream_name = "Internal FM Playback",
683 .cpu_dai_name = "msm-dai-q6.12292",
684 .platform_name = "msm-pcm-routing",
685 .codec_name = "msm-stub-codec.1",
686 .codec_dai_name = "msm-stub-rx",
687 .no_pcm = 1,
688 .be_id = MSM_BACKEND_DAI_INT_FM_RX,
689 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
690 },
691 {
692 .name = LPASS_BE_INT_FM_TX,
693 .stream_name = "Internal FM Capture",
694 .cpu_dai_name = "msm-dai-q6.12293",
695 .platform_name = "msm-pcm-routing",
696 .codec_name = "msm-stub-codec.1",
697 .codec_dai_name = "msm-stub-tx",
698 .no_pcm = 1,
699 .be_id = MSM_BACKEND_DAI_INT_FM_TX,
700 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
701 },
702 /* HDMI BACK END DAI Link */
703 {
704 .name = LPASS_BE_HDMI,
705 .stream_name = "HDMI Playback",
706 .cpu_dai_name = "msm-dai-q6.8",
707 .platform_name = "msm-pcm-routing",
708 .codec_name = "msm-stub-codec.1",
709 .codec_dai_name = "msm-stub-rx",
710 .no_pcm = 1,
711 .no_codec = 1,
712 .be_id = MSM_BACKEND_DAI_HDMI_RX,
713 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
714 },
715};
716
717struct snd_soc_card snd_soc_card_msm8960 = {
718 .name = "msm8960-snd-card",
719 .dai_link = msm8960_dai,
720 .num_links = ARRAY_SIZE(msm8960_dai),
721};
722
723static struct platform_device *msm8960_snd_device;
724
725static int msm8960_configure_headset_mic_gpios(void)
726{
727 int ret;
728 struct pm_gpio param = {
729 .direction = PM_GPIO_DIR_OUT,
730 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
731 .output_value = 1,
732 .pull = PM_GPIO_PULL_NO,
733 .vin_sel = PM_GPIO_VIN_S4,
734 .out_strength = PM_GPIO_STRENGTH_MED,
735 .function = PM_GPIO_FUNC_NORMAL,
736 };
737
738 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
739 if (ret) {
740 pr_err("%s: Failed to request gpio %d\n", __func__,
741 PM8921_GPIO_PM_TO_SYS(23));
742 return ret;
743 }
744
745 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
746 if (ret)
747 pr_err("%s: Failed to configure gpio %d\n", __func__,
748 PM8921_GPIO_PM_TO_SYS(23));
749 else
750 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
751
752 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
753 if (ret) {
754 pr_err("%s: Failed to request gpio %d\n", __func__,
755 PM8921_GPIO_PM_TO_SYS(35));
756 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
757 return ret;
758 }
759 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
760 if (ret)
761 pr_err("%s: Failed to configure gpio %d\n", __func__,
762 PM8921_GPIO_PM_TO_SYS(35));
763 else
764 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 1);
765
766 return 0;
767}
768static void msm8960_free_headset_mic_gpios(void)
769{
770 if (msm8960_headset_gpios_configured) {
771 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
772 gpio_free(PM8921_GPIO_PM_TO_SYS(35));
773 }
774}
775
776static int __init msm8960_audio_init(void)
777{
778 int ret;
779
780 msm8960_snd_device = platform_device_alloc("soc-audio", 0);
781 if (!msm8960_snd_device) {
782 pr_err("Platform device allocation failed\n");
783 return -ENOMEM;
784 }
785
786 platform_set_drvdata(msm8960_snd_device, &snd_soc_card_msm8960);
787 ret = platform_device_add(msm8960_snd_device);
788 if (ret) {
789 platform_device_put(msm8960_snd_device);
790 return ret;
791 }
792
793 if (msm8960_configure_headset_mic_gpios()) {
794 pr_err("%s Fail to configure headset mic gpios\n", __func__);
795 msm8960_headset_gpios_configured = 0;
796 } else
797 msm8960_headset_gpios_configured = 1;
798
799 return ret;
800
801}
802module_init(msm8960_audio_init);
803
804static void __exit msm8960_audio_exit(void)
805{
806 msm8960_free_headset_mic_gpios();
807 platform_device_unregister(msm8960_snd_device);
808}
809module_exit(msm8960_audio_exit);
810
811MODULE_DESCRIPTION("ALSA SoC MSM8960");
812MODULE_LICENSE("GPL v2");