blob: 5ee84776ffd108986e0feba97283a537444ed624 [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>
Kiran Kandidb0a4b02011-08-23 09:32:09 -070014#include <linux/delay.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070015#include <linux/gpio.h>
16#include <linux/mfd/pm8xxx/pm8921.h>
17#include <linux/platform_device.h>
18#include <linux/gpio.h>
19#include <linux/mfd/pm8xxx/pm8921.h>
20#include <sound/core.h>
21#include <sound/soc.h>
22#include <sound/soc-dapm.h>
23#include <sound/soc-dsp.h>
24#include <sound/pcm.h>
25#include <sound/jack.h>
Bradley Rubin229c6a52011-07-12 16:18:48 -070026#include <asm/mach-types.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027#include "msm-pcm-routing.h"
28#include <../codecs/wcd9310.h>
29
30/* 8960 machine driver */
31
32#define PM8921_GPIO_BASE NR_GPIO_IRQS
33#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
34
Kiran Kandidb0a4b02011-08-23 09:32:09 -070035#define TOP_SPK_PAMP (PM8921_GPIO_PM_TO_SYS(18))
36#define BOTTOM_SPK_PAMP (PM8921_GPIO_PM_TO_SYS(19))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037#define MSM8960_SPK_ON 1
38#define MSM8960_SPK_OFF 0
39
40#define msm8960_SLIM_0_RX_MAX_CHANNELS 2
41#define msm8960_SLIM_0_TX_MAX_CHANNELS 4
42
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -070043#define BTSCO_RATE_8KHZ 8000
44#define BTSCO_RATE_16KHZ 16000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045
46static int msm8960_spk_control;
Kiran Kandidb0a4b02011-08-23 09:32:09 -070047static int msm8960_bottom_spk_pamp_on;
48static int msm8960_top_spk_pamp_on;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049static int msm8960_slim_0_rx_ch = 1;
50static int msm8960_slim_0_tx_ch = 1;
51
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -070052static int msm8960_btsco_rate = BTSCO_RATE_8KHZ;
53static int msm8960_btsco_ch = 1;
54
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055struct tabla_mbhc_calibration tabla_cal = {
56 .bias = TABLA_MICBIAS2,
57 .tldoh = 100,
58 .bg_fast_settle = 100,
59 .mic_current = TABLA_PID_MIC_5_UA,
60 .mic_pid = 100,
61 .hph_current = TABLA_PID_MIC_5_UA,
62 .setup_plug_removal_delay = 1000000,
63 .shutdown_plug_removal = 100000,
64};
65
66static struct clk *codec_clk;
67static int clk_users;
68
69static int msm8960_headset_gpios_configured;
70
71static struct snd_soc_jack hs_jack;
Bradley Rubincb1e2732011-06-23 16:49:20 -070072static struct snd_soc_jack button_jack;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070073
Kiran Kandidb0a4b02011-08-23 09:32:09 -070074static void codec_poweramp_on(int bottom_spk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070075{
76 int ret = 0;
77
78 struct pm_gpio param = {
79 .direction = PM_GPIO_DIR_OUT,
80 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
81 .output_value = 1,
82 .pull = PM_GPIO_PULL_NO,
83 .vin_sel = PM_GPIO_VIN_S4,
84 .out_strength = PM_GPIO_STRENGTH_MED,
85 .function = PM_GPIO_FUNC_NORMAL,
86 };
87
Kiran Kandidb0a4b02011-08-23 09:32:09 -070088 if (bottom_spk) {
89 if (msm8960_bottom_spk_pamp_on)
90 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091
Kiran Kandidb0a4b02011-08-23 09:32:09 -070092 ret = gpio_request(BOTTOM_SPK_PAMP, "BOTTOM_SPK_AMP");
93 if (ret) {
94 pr_err("%s: Error requesting GPIO %d\n", __func__,
95 BOTTOM_SPK_PAMP);
96 return;
97 }
98 ret = pm8xxx_gpio_config(BOTTOM_SPK_PAMP, &param);
99 if (ret)
100 pr_err("%s: Failed to configure gpio %d\n", __func__,
101 BOTTOM_SPK_PAMP);
102 else {
103 pr_debug("%s: enable Bottom spkr amp\n", __func__);
104 gpio_direction_output(BOTTOM_SPK_PAMP, 1);
105 msm8960_bottom_spk_pamp_on = 1;
106 }
107
108 } else {
109 if (msm8960_top_spk_pamp_on)
110 return;
111
112 ret = gpio_request(TOP_SPK_PAMP, "TOP_SPK_AMP");
113 if (ret) {
114 pr_err("%s: Error requesting GPIO %d\n", __func__,
115 TOP_SPK_PAMP);
116 return;
117 }
118 ret = pm8xxx_gpio_config(TOP_SPK_PAMP, &param);
119 if (ret)
120 pr_err("%s: Failed to configure gpio %d\n", __func__,
121 TOP_SPK_PAMP);
122 else {
123 pr_debug("%s: enable Top spkr amp\n", __func__);
124 gpio_direction_output(TOP_SPK_PAMP, 1);
125 msm8960_top_spk_pamp_on = 1;
126 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 }
Kiran Kandidb0a4b02011-08-23 09:32:09 -0700128 pr_debug("%s: slepping 4 ms", __func__);
129 usleep_range(4000, 4000);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700130}
Kiran Kandidb0a4b02011-08-23 09:32:09 -0700131
132static void codec_poweramp_off(int bottom_spk)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700133{
Kiran Kandidb0a4b02011-08-23 09:32:09 -0700134 if (bottom_spk) {
135 if (!msm8960_bottom_spk_pamp_on)
136 return;
137 pr_debug("%s: disable Bottom spkr amp\n", __func__);
138 gpio_direction_output(BOTTOM_SPK_PAMP, 0);
139 gpio_free(BOTTOM_SPK_PAMP);
140 msm8960_bottom_spk_pamp_on = 0;
141 } else {
142 if (!msm8960_top_spk_pamp_on)
143 return;
144 pr_debug("%s: disable To spkr amp\n", __func__);
145 gpio_direction_output(TOP_SPK_PAMP, 0);
146 gpio_free(TOP_SPK_PAMP);
147 msm8960_top_spk_pamp_on = 0;
148 }
149 pr_debug("%s: slepping 4 ms", __func__);
150 usleep_range(4000, 4000);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151}
Kiran Kandidb0a4b02011-08-23 09:32:09 -0700152
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700153static void msm8960_ext_control(struct snd_soc_codec *codec)
154{
155 struct snd_soc_dapm_context *dapm = &codec->dapm;
156
157 pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
158 if (msm8960_spk_control == MSM8960_SPK_ON)
159 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
160 else
161 snd_soc_dapm_disable_pin(dapm, "Ext Spk");
162
163 snd_soc_dapm_sync(dapm);
164}
165
166static int msm8960_get_spk(struct snd_kcontrol *kcontrol,
167 struct snd_ctl_elem_value *ucontrol)
168{
169 pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
170 ucontrol->value.integer.value[0] = msm8960_spk_control;
171 return 0;
172}
173static int msm8960_set_spk(struct snd_kcontrol *kcontrol,
174 struct snd_ctl_elem_value *ucontrol)
175{
176 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
177
178 pr_debug("%s()\n", __func__);
179 if (msm8960_spk_control == ucontrol->value.integer.value[0])
180 return 0;
181
182 msm8960_spk_control = ucontrol->value.integer.value[0];
183 msm8960_ext_control(codec);
184 return 1;
185}
186static int msm8960_spkramp_event(struct snd_soc_dapm_widget *w,
187 struct snd_kcontrol *k, int event)
188{
189 pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
Kiran Kandidb0a4b02011-08-23 09:32:09 -0700190
191 if (SND_SOC_DAPM_EVENT_ON(event)) {
192 if (!strncmp(w->name, "Ext Spk Bottom", 14))
193 codec_poweramp_on(1);
194 else
195 codec_poweramp_on(0);
196 } else {
197 if (!strncmp(w->name, "Ext Spk Bottom", 14))
198 codec_poweramp_off(1);
199 else
200 codec_poweramp_off(0);
201 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202 return 0;
203}
204
Kiran Kandi6fae8bf2011-08-15 10:36:42 -0700205static int msm8960_mclk_event(struct snd_soc_dapm_widget *w,
206 struct snd_kcontrol *kcontrol, int event)
207{
208 pr_debug("%s: event = %d\n", __func__, event);
209
210 switch (event) {
211 case SND_SOC_DAPM_PRE_PMU:
212
213 clk_users++;
214 pr_debug("%s: clk_users = %d\n", __func__, clk_users);
215
216 if (clk_users != 1)
217 return 0;
218
219 codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
220 if (codec_clk) {
221 clk_set_rate(codec_clk, 12288000);
222 clk_enable(codec_clk);
223 tabla_mclk_enable(w->codec, 1);
224
225 } else {
226 pr_err("%s: Error setting Tabla MCLK\n", __func__);
227 clk_users--;
228 return -EINVAL;
229 }
230 break;
231 case SND_SOC_DAPM_POST_PMD:
232
233 pr_debug("%s: clk_users = %d\n", __func__, clk_users);
234
235 if (clk_users == 0)
236 return 0;
237
238 clk_users--;
239
240 if (!clk_users) {
241 pr_debug("%s: disabling MCLK. clk_users = %d\n",
242 __func__, clk_users);
243
244 clk_disable(codec_clk);
245 clk_put(codec_clk);
246 tabla_mclk_enable(w->codec, 0);
247 }
248 break;
249 }
250 return 0;
251}
252
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253static const struct snd_soc_dapm_widget msm8960_dapm_widgets[] = {
Kiran Kandi3a30bda2011-08-15 10:36:42 -0700254
Kiran Kandi6fae8bf2011-08-15 10:36:42 -0700255 SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
256 msm8960_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
257
Kiran Kandidb0a4b02011-08-23 09:32:09 -0700258 SND_SOC_DAPM_SPK("Ext Spk Bottom", msm8960_spkramp_event),
259 SND_SOC_DAPM_SPK("Ext Spk Top", msm8960_spkramp_event),
Kiran Kandi3a30bda2011-08-15 10:36:42 -0700260
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261 SND_SOC_DAPM_MIC("Handset Mic", NULL),
262 SND_SOC_DAPM_MIC("Headset Mic", NULL),
263 SND_SOC_DAPM_MIC("Digital Mic1", NULL),
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700264 SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
265 SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700266
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700267 SND_SOC_DAPM_MIC("Digital Mic1", NULL),
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700268 SND_SOC_DAPM_MIC("Digital Mic2", NULL),
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700269 SND_SOC_DAPM_MIC("Digital Mic3", NULL),
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700270 SND_SOC_DAPM_MIC("Digital Mic4", NULL),
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700271 SND_SOC_DAPM_MIC("Digital Mic5", NULL),
272 SND_SOC_DAPM_MIC("Digital Mic6", NULL),
273
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700274};
275
Bradley Rubin229c6a52011-07-12 16:18:48 -0700276static const struct snd_soc_dapm_route common_audio_map[] = {
Kiran Kandidb0a4b02011-08-23 09:32:09 -0700277
Kiran Kandi6fae8bf2011-08-15 10:36:42 -0700278 {"RX_BIAS", NULL, "MCLK"},
279 {"LDO_H", NULL, "MCLK"},
280
281 /* Speaker path */
Kiran Kandidb0a4b02011-08-23 09:32:09 -0700282 {"Ext Spk Bottom", NULL, "LINEOUT1"},
283 {"Ext Spk Bottom", NULL, "LINEOUT3"},
284
285 {"Ext Spk Top", NULL, "LINEOUT2"},
286 {"Ext Spk Top", NULL, "LINEOUT4"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287
288 /* Microphone path */
Bradley Rubin229c6a52011-07-12 16:18:48 -0700289 {"AMIC1", NULL, "MIC BIAS1 Internal1"},
290 {"MIC BIAS1 Internal1", NULL, "Handset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700291
292 {"AMIC2", NULL, "MIC BIAS2 External"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293 {"MIC BIAS2 External", NULL, "Headset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700294
Kiran Kandie9bf86a2011-07-21 16:50:41 -0700295 {"HEADPHONE", NULL, "LDO_H"},
296
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700297 /**
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700298 * The digital Mic routes are setup considering
299 * fluid as default device.
300 */
301
302 /**
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700303 * Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP.
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700304 * Digital Mic GM5 on CDP mainboard.
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700305 * Conncted to DMIC2 Input on Tabla codec.
306 */
307 {"DMIC2", NULL, "MIC BIAS1 External"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700308 {"MIC BIAS1 External", NULL, "Digital Mic1"},
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700309
310 /**
311 * Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP.
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700312 * Digital Mic GM6 on CDP mainboard.
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700313 * Conncted to DMIC1 Input on Tabla codec.
314 */
315 {"DMIC1", NULL, "MIC BIAS1 External"},
316 {"MIC BIAS1 External", NULL, "Digital Mic2"},
317
318 /**
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700319 * Digital Mic3. Back Bottom Digital Mic on Fluid.
320 * Digital Mic GM1 on CDP mainboard.
321 * Conncted to DMIC4 Input on Tabla codec.
322 */
323 {"DMIC4", NULL, "MIC BIAS3 External"},
324 {"MIC BIAS3 External", NULL, "Digital Mic3"},
325
326 /**
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700327 * Digital Mic4. Back top Digital Mic on Fluid.
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700328 * Digital Mic GM2 on CDP mainboard.
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700329 * Conncted to DMIC3 Input on Tabla codec.
330 */
331 {"DMIC3", NULL, "MIC BIAS3 External"},
332 {"MIC BIAS3 External", NULL, "Digital Mic4"},
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700333
334 /**
335 * Digital Mic5. Front top Digital Mic on Fluid.
336 * Digital Mic GM3 on CDP mainboard.
337 * Conncted to DMIC5 Input on Tabla codec.
338 */
339 {"DMIC5", NULL, "MIC BIAS4 External"},
340 {"MIC BIAS4 External", NULL, "Digital Mic5"},
341
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700342};
343
Bradley Rubin229c6a52011-07-12 16:18:48 -0700344static const struct snd_soc_dapm_route cdp_audio_map[] = {
345 {"AMIC3", NULL, "MIC BIAS3 External"},
346 {"MIC BIAS3 External", NULL, "ANCRight Headset Mic"},
347
348 {"AMIC4", NULL, "MIC BIAS4 External"},
349 {"MIC BIAS4 External", NULL, "ANCLeft Headset Mic"},
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700350
351 /** Digital Mic GM4 on CDP mainboard.
352 * Connected to DMIC6 input on Tabla codec.
353 */
354 {"DMIC6", NULL, "MIC BIAS4 External"},
355 {"MIC BIAS4 External", NULL, "Digital Mic6"},
356
Bradley Rubin229c6a52011-07-12 16:18:48 -0700357};
358
359static const struct snd_soc_dapm_route fluid_audio_map[] = {
360 {"AMIC3", NULL, "MIC BIAS3 Internal1"},
361 {"MIC BIAS3 Internal1", NULL, "ANCRight Headset Mic"},
362
363 {"AMIC4", NULL, "MIC BIAS1 Internal2"},
364 {"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
365};
366
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700367static const char *spk_function[] = {"Off", "On"};
Patrick Lai9f4b4292011-07-16 22:11:09 -0700368static const char *slim0_rx_ch_text[] = {"One", "Two"};
369static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
370
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700371static const struct soc_enum msm8960_enum[] = {
372 SOC_ENUM_SINGLE_EXT(2, spk_function),
Patrick Lai9f4b4292011-07-16 22:11:09 -0700373 SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
374 SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700375};
376
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700377static const char *btsco_rate_text[] = {"8000", "16000"};
378static const struct soc_enum msm8960_btsco_enum[] = {
379 SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
380};
381
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382static int msm8960_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
383 struct snd_ctl_elem_value *ucontrol)
384{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700385 pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700386 msm8960_slim_0_rx_ch);
Patrick Lai9f4b4292011-07-16 22:11:09 -0700387 ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388 return 0;
389}
390
391static int msm8960_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
392 struct snd_ctl_elem_value *ucontrol)
393{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700394 msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700395
Patrick Lai9f4b4292011-07-16 22:11:09 -0700396 pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700397 msm8960_slim_0_rx_ch);
398 return 1;
399}
400
401static int msm8960_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
402 struct snd_ctl_elem_value *ucontrol)
403{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700404 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700405 msm8960_slim_0_tx_ch);
Patrick Lai9f4b4292011-07-16 22:11:09 -0700406 ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407 return 0;
408}
409
410static int msm8960_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
411 struct snd_ctl_elem_value *ucontrol)
412{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700413 msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414
415 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
416 msm8960_slim_0_tx_ch);
417 return 1;
418}
419
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700420static int msm8960_btsco_rate_get(struct snd_kcontrol *kcontrol,
421 struct snd_ctl_elem_value *ucontrol)
422{
423 pr_debug("%s: msm8960_btsco_rate = %d", __func__,
424 msm8960_btsco_rate);
425 ucontrol->value.integer.value[0] = msm8960_btsco_rate;
426 return 0;
427}
428
429static int msm8960_btsco_rate_put(struct snd_kcontrol *kcontrol,
430 struct snd_ctl_elem_value *ucontrol)
431{
432 switch (ucontrol->value.integer.value[0]) {
433 case 0:
434 msm8960_btsco_rate = BTSCO_RATE_8KHZ;
435 break;
436 case 1:
437 msm8960_btsco_rate = BTSCO_RATE_16KHZ;
438 break;
439 default:
440 msm8960_btsco_rate = BTSCO_RATE_8KHZ;
441 break;
442 }
443 pr_debug("%s: msm8960_btsco_rate = %d\n", __func__,
444 msm8960_btsco_rate);
445 return 0;
446}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700447
448static const struct snd_kcontrol_new tabla_msm8960_controls[] = {
449 SOC_ENUM_EXT("Speaker Function", msm8960_enum[0], msm8960_get_spk,
450 msm8960_set_spk),
Patrick Lai9f4b4292011-07-16 22:11:09 -0700451 SOC_ENUM_EXT("SLIM_0_RX Channels", msm8960_enum[1],
452 msm8960_slim_0_rx_ch_get, msm8960_slim_0_rx_ch_put),
453 SOC_ENUM_EXT("SLIM_0_TX Channels", msm8960_enum[2],
454 msm8960_slim_0_tx_ch_get, msm8960_slim_0_tx_ch_put),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700455};
456
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700457static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
458 SOC_ENUM_EXT("Internal BTSCO SampleRate", msm8960_btsco_enum[0],
459 msm8960_btsco_rate_get, msm8960_btsco_rate_put),
460};
461
462static int msm8960_btsco_init(struct snd_soc_pcm_runtime *rtd)
463{
464 int err = 0;
465 struct snd_soc_platform *platform = rtd->platform;
466
467 err = snd_soc_add_platform_controls(platform,
468 int_btsco_rate_mixer_controls,
469 ARRAY_SIZE(int_btsco_rate_mixer_controls));
470 if (err < 0)
471 return err;
472 return 0;
473}
474
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700475static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
476{
477 int err;
478 struct snd_soc_codec *codec = rtd->codec;
479 struct snd_soc_dapm_context *dapm = &codec->dapm;
480
481 pr_debug("%s()\n", __func__);
482
483 err = snd_soc_add_controls(codec, tabla_msm8960_controls,
484 ARRAY_SIZE(tabla_msm8960_controls));
485 if (err < 0)
486 return err;
487
488 snd_soc_dapm_new_controls(dapm, msm8960_dapm_widgets,
489 ARRAY_SIZE(msm8960_dapm_widgets));
490
Bradley Rubin229c6a52011-07-12 16:18:48 -0700491 snd_soc_dapm_add_routes(dapm, common_audio_map,
492 ARRAY_SIZE(common_audio_map));
493
494 if (machine_is_msm8960_cdp())
495 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
496 ARRAY_SIZE(cdp_audio_map));
497 else if (machine_is_msm8960_mtp())
498 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
499 ARRAY_SIZE(cdp_audio_map));
500 else if (machine_is_msm8960_fluid())
501 snd_soc_dapm_add_routes(dapm, fluid_audio_map,
502 ARRAY_SIZE(fluid_audio_map));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503
504 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
505
506 snd_soc_dapm_sync(dapm);
507
508 err = snd_soc_jack_new(codec, "Headset Jack",
509 SND_JACK_HEADSET, &hs_jack);
510 if (err) {
511 pr_err("failed to create new jack\n");
512 return err;
513 }
Bradley Rubincb1e2732011-06-23 16:49:20 -0700514
515 err = snd_soc_jack_new(codec, "Button Jack",
516 SND_JACK_BTN_0, &button_jack);
517 if (err) {
518 pr_err("failed to create new jack\n");
519 return err;
520 }
521
522 tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_cal);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700523
524 return 0;
525}
526
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527static struct snd_soc_dsp_link lpa_fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700528 .playback = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700529 .trigger = {
530 SND_SOC_DSP_TRIGGER_POST,
531 SND_SOC_DSP_TRIGGER_POST
532 },
533};
534
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700535static struct snd_soc_dsp_link fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700536 .playback = true,
537 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538 .trigger = {
539 SND_SOC_DSP_TRIGGER_POST,
540 SND_SOC_DSP_TRIGGER_POST
541 },
542};
543
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700544static struct snd_soc_dsp_link slimbus0_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700545 .playback = true,
546 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547 .trigger = {
548 SND_SOC_DSP_TRIGGER_POST,
549 SND_SOC_DSP_TRIGGER_POST
550 },
551};
552
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700553static struct snd_soc_dsp_link int_fm_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700554 .playback = true,
555 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556 .trigger = {
557 SND_SOC_DSP_TRIGGER_POST,
558 SND_SOC_DSP_TRIGGER_POST
559 },
560};
561
562static int msm8960_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
563 struct snd_pcm_hw_params *params)
564{
565 struct snd_interval *rate = hw_param_interval(params,
566 SNDRV_PCM_HW_PARAM_RATE);
567
568 struct snd_interval *channels = hw_param_interval(params,
569 SNDRV_PCM_HW_PARAM_CHANNELS);
570
571 pr_debug("%s()\n", __func__);
572 rate->min = rate->max = 48000;
573 channels->min = channels->max = msm8960_slim_0_rx_ch;
574
575 return 0;
576}
577
578static int msm8960_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
579 struct snd_pcm_hw_params *params)
580{
581 struct snd_interval *rate = hw_param_interval(params,
582 SNDRV_PCM_HW_PARAM_RATE);
583
584 struct snd_interval *channels = hw_param_interval(params,
585 SNDRV_PCM_HW_PARAM_CHANNELS);
586
587 pr_debug("%s()\n", __func__);
588 rate->min = rate->max = 48000;
589 channels->min = channels->max = msm8960_slim_0_tx_ch;
590
591 return 0;
592}
593
594static int msm8960_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
595 struct snd_pcm_hw_params *params)
596{
597 struct snd_interval *rate = hw_param_interval(params,
598 SNDRV_PCM_HW_PARAM_RATE);
599
600 pr_debug("%s()\n", __func__);
601 rate->min = rate->max = 48000;
602
603 return 0;
604}
605
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700606static int msm8960_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
607 struct snd_pcm_hw_params *params)
608{
609 struct snd_interval *rate = hw_param_interval(params,
610 SNDRV_PCM_HW_PARAM_RATE);
611
612 struct snd_interval *channels = hw_param_interval(params,
613 SNDRV_PCM_HW_PARAM_CHANNELS);
614
615 rate->min = rate->max = msm8960_btsco_rate;
616 channels->min = channels->max = msm8960_btsco_ch;
617
618 return 0;
619}
620
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700621static int msm8960_startup(struct snd_pcm_substream *substream)
622{
Kiran Kandi6fae8bf2011-08-15 10:36:42 -0700623 pr_debug("%s(): substream = %s stream = %d\n", __func__,
624 substream->name, substream->stream);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700625 return 0;
626}
627
628static void msm8960_shutdown(struct snd_pcm_substream *substream)
629{
Kiran Kandi6fae8bf2011-08-15 10:36:42 -0700630 pr_debug("%s(): substream = %s stream = %d\n", __func__,
631 substream->name, substream->stream);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700632}
633
634static struct snd_soc_ops msm8960_be_ops = {
635 .startup = msm8960_startup,
636 .shutdown = msm8960_shutdown,
637};
638
639/* Digital audio interface glue - connects codec <---> CPU */
640static struct snd_soc_dai_link msm8960_dai[] = {
641 /* FrontEnd DAI Links */
642 {
643 .name = "MSM8960 Media1",
644 .stream_name = "MultiMedia1",
645 .cpu_dai_name = "MultiMedia1",
646 .platform_name = "msm-pcm-dsp",
647 .dynamic = 1,
648 .dsp_link = &fe_media,
649 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
650 },
651 {
652 .name = "MSM8960 Media2",
653 .stream_name = "MultiMedia2",
654 .cpu_dai_name = "MultiMedia2",
655 .platform_name = "msm-pcm-dsp",
656 .dynamic = 1,
657 .dsp_link = &fe_media,
658 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
659 },
660 {
661 .name = "Circuit-Switch Voice",
662 .stream_name = "CS-Voice",
663 .cpu_dai_name = "CS-VOICE",
664 .platform_name = "msm-pcm-voice",
665 .dynamic = 1,
666 .dsp_link = &fe_media,
667 .be_id = MSM_FRONTEND_DAI_CS_VOICE,
668 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
669 },
670 {
671 .name = "MSM VoIP",
672 .stream_name = "VoIP",
673 .cpu_dai_name = "VoIP",
674 .platform_name = "msm-voip-dsp",
675 .dynamic = 1,
676 .dsp_link = &fe_media,
677 .be_id = MSM_FRONTEND_DAI_VOIP,
678 },
679 {
680 .name = "MSM8960 LPA",
681 .stream_name = "LPA",
682 .cpu_dai_name = "MultiMedia3",
683 .platform_name = "msm-pcm-lpa",
684 .dynamic = 1,
685 .dsp_link = &lpa_fe_media,
686 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
687 },
688 /* Hostless PMC purpose */
689 {
690 .name = "SLIMBUS_0 Hostless",
691 .stream_name = "SLIMBUS_0 Hostless",
692 .cpu_dai_name = "SLIMBUS0_HOSTLESS",
693 .platform_name = "msm-pcm-hostless",
694 .dynamic = 1,
695 .dsp_link = &slimbus0_hl_media,
696 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
697 /* .be_id = do not care */
698 },
699 {
700 .name = "INT_FM Hostless",
701 .stream_name = "INT_FM Hostless",
702 .cpu_dai_name = "INT_FM_HOSTLESS",
703 .platform_name = "msm-pcm-hostless",
704 .dynamic = 1,
705 .dsp_link = &int_fm_hl_media,
706 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
707 /* .be_id = do not care */
708 },
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530709 {
710 .name = "MSM AFE-PCM RX",
711 .stream_name = "AFE-PROXY RX",
712 .cpu_dai_name = "msm-dai-q6.241",
713 .codec_name = "msm-stub-codec.1",
714 .codec_dai_name = "msm-stub-rx",
715 .platform_name = "msm-pcm-afe",
716 },
717 {
718 .name = "MSM AFE-PCM TX",
719 .stream_name = "AFE-PROXY TX",
720 .cpu_dai_name = "msm-dai-q6.240",
721 .codec_name = "msm-stub-codec.1",
722 .codec_dai_name = "msm-stub-tx",
723 .platform_name = "msm-pcm-afe",
724 },
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700725 /* Backend DAI Links */
726 {
727 .name = LPASS_BE_SLIMBUS_0_RX,
728 .stream_name = "Slimbus Playback",
729 .cpu_dai_name = "msm-dai-q6.16384",
730 .platform_name = "msm-pcm-routing",
731 .codec_name = "tabla_codec",
732 .codec_dai_name = "tabla_rx1",
733 .no_pcm = 1,
734 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
735 .init = &msm8960_audrx_init,
736 .be_hw_params_fixup = msm8960_slim_0_rx_be_hw_params_fixup,
737 .ops = &msm8960_be_ops,
738 },
739 {
740 .name = LPASS_BE_SLIMBUS_0_TX,
741 .stream_name = "Slimbus Capture",
742 .cpu_dai_name = "msm-dai-q6.16385",
743 .platform_name = "msm-pcm-routing",
744 .codec_name = "tabla_codec",
745 .codec_dai_name = "tabla_tx1",
746 .no_pcm = 1,
747 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
748 .be_hw_params_fixup = msm8960_slim_0_tx_be_hw_params_fixup,
749 .ops = &msm8960_be_ops,
750 },
751 /* Backend BT/FM DAI Links */
752 {
753 .name = LPASS_BE_INT_BT_SCO_RX,
754 .stream_name = "Internal BT-SCO Playback",
755 .cpu_dai_name = "msm-dai-q6.12288",
756 .platform_name = "msm-pcm-routing",
757 .codec_name = "msm-stub-codec.1",
758 .codec_dai_name = "msm-stub-rx",
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700759 .init = &msm8960_btsco_init,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700760 .no_pcm = 1,
761 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700762 .be_hw_params_fixup = msm8960_btsco_be_hw_params_fixup,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700763 },
764 {
765 .name = LPASS_BE_INT_BT_SCO_TX,
766 .stream_name = "Internal BT-SCO Capture",
767 .cpu_dai_name = "msm-dai-q6.12289",
768 .platform_name = "msm-pcm-routing",
769 .codec_name = "msm-stub-codec.1",
770 .codec_dai_name = "msm-stub-tx",
771 .no_pcm = 1,
772 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700773 .be_hw_params_fixup = msm8960_btsco_be_hw_params_fixup,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700774 },
775 {
776 .name = LPASS_BE_INT_FM_RX,
777 .stream_name = "Internal FM Playback",
778 .cpu_dai_name = "msm-dai-q6.12292",
779 .platform_name = "msm-pcm-routing",
780 .codec_name = "msm-stub-codec.1",
781 .codec_dai_name = "msm-stub-rx",
782 .no_pcm = 1,
783 .be_id = MSM_BACKEND_DAI_INT_FM_RX,
784 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
785 },
786 {
787 .name = LPASS_BE_INT_FM_TX,
788 .stream_name = "Internal FM Capture",
789 .cpu_dai_name = "msm-dai-q6.12293",
790 .platform_name = "msm-pcm-routing",
791 .codec_name = "msm-stub-codec.1",
792 .codec_dai_name = "msm-stub-tx",
793 .no_pcm = 1,
794 .be_id = MSM_BACKEND_DAI_INT_FM_TX,
795 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
796 },
797 /* HDMI BACK END DAI Link */
798 {
799 .name = LPASS_BE_HDMI,
800 .stream_name = "HDMI Playback",
801 .cpu_dai_name = "msm-dai-q6.8",
802 .platform_name = "msm-pcm-routing",
803 .codec_name = "msm-stub-codec.1",
804 .codec_dai_name = "msm-stub-rx",
805 .no_pcm = 1,
806 .no_codec = 1,
807 .be_id = MSM_BACKEND_DAI_HDMI_RX,
808 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
809 },
Laxminath Kasam32657ec2011-08-01 19:26:57 +0530810 /* Backend AFE DAI Links */
811 {
812 .name = LPASS_BE_AFE_PCM_RX,
813 .stream_name = "AFE Playback",
814 .cpu_dai_name = "msm-dai-q6.224",
815 .platform_name = "msm-pcm-routing",
816 .codec_name = "msm-stub-codec.1",
817 .codec_dai_name = "msm-stub-rx",
818 .no_codec = 1,
819 .no_pcm = 1,
820 .be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
821 },
822 {
823 .name = LPASS_BE_AFE_PCM_TX,
824 .stream_name = "AFE Capture",
825 .cpu_dai_name = "msm-dai-q6.225",
826 .platform_name = "msm-pcm-routing",
827 .codec_name = "msm-stub-codec.1",
828 .codec_dai_name = "msm-stub-tx",
829 .no_codec = 1,
830 .no_pcm = 1,
831 .be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
832 },
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700833};
834
835struct snd_soc_card snd_soc_card_msm8960 = {
836 .name = "msm8960-snd-card",
837 .dai_link = msm8960_dai,
838 .num_links = ARRAY_SIZE(msm8960_dai),
839};
840
841static struct platform_device *msm8960_snd_device;
842
843static int msm8960_configure_headset_mic_gpios(void)
844{
845 int ret;
846 struct pm_gpio param = {
847 .direction = PM_GPIO_DIR_OUT,
848 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
849 .output_value = 1,
850 .pull = PM_GPIO_PULL_NO,
851 .vin_sel = PM_GPIO_VIN_S4,
852 .out_strength = PM_GPIO_STRENGTH_MED,
853 .function = PM_GPIO_FUNC_NORMAL,
854 };
855
856 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
857 if (ret) {
858 pr_err("%s: Failed to request gpio %d\n", __func__,
859 PM8921_GPIO_PM_TO_SYS(23));
860 return ret;
861 }
862
863 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
864 if (ret)
865 pr_err("%s: Failed to configure gpio %d\n", __func__,
866 PM8921_GPIO_PM_TO_SYS(23));
867 else
868 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
869
870 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
871 if (ret) {
872 pr_err("%s: Failed to request gpio %d\n", __func__,
873 PM8921_GPIO_PM_TO_SYS(35));
874 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
875 return ret;
876 }
877 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
878 if (ret)
879 pr_err("%s: Failed to configure gpio %d\n", __func__,
880 PM8921_GPIO_PM_TO_SYS(35));
881 else
882 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 1);
883
884 return 0;
885}
886static void msm8960_free_headset_mic_gpios(void)
887{
888 if (msm8960_headset_gpios_configured) {
889 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
890 gpio_free(PM8921_GPIO_PM_TO_SYS(35));
891 }
892}
893
894static int __init msm8960_audio_init(void)
895{
896 int ret;
897
898 msm8960_snd_device = platform_device_alloc("soc-audio", 0);
899 if (!msm8960_snd_device) {
900 pr_err("Platform device allocation failed\n");
901 return -ENOMEM;
902 }
903
904 platform_set_drvdata(msm8960_snd_device, &snd_soc_card_msm8960);
905 ret = platform_device_add(msm8960_snd_device);
906 if (ret) {
907 platform_device_put(msm8960_snd_device);
908 return ret;
909 }
910
911 if (msm8960_configure_headset_mic_gpios()) {
912 pr_err("%s Fail to configure headset mic gpios\n", __func__);
913 msm8960_headset_gpios_configured = 0;
914 } else
915 msm8960_headset_gpios_configured = 1;
916
917 return ret;
918
919}
920module_init(msm8960_audio_init);
921
922static void __exit msm8960_audio_exit(void)
923{
924 msm8960_free_headset_mic_gpios();
925 platform_device_unregister(msm8960_snd_device);
926}
927module_exit(msm8960_audio_exit);
928
929MODULE_DESCRIPTION("ALSA SoC MSM8960");
930MODULE_LICENSE("GPL v2");