blob: edda55007891ff0c2aa1a12dea57c229cf1866f2 [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}
Kiran Kandi3a30bda2011-08-15 10:36:42 -0700204static int msm8960_mclk_enable_event(struct snd_soc_dapm_widget *w,
205 struct snd_kcontrol *kcontrol, int event)
206{
207 pr_debug("%s: Event = %d\n", __func__, event);
208
209 switch (event) {
210 case SND_SOC_DAPM_PRE_PMU:
211
212 pr_debug("%s: clk_users = %d\n", __func__, clk_users);
213
214 if (clk_users++)
215 return 0;
216 codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
217 if (codec_clk) {
218 clk_set_rate(codec_clk, 12288000);
219 clk_enable(codec_clk);
220 tabla_mclk_enable(w->codec, 1);
221
222 } else {
223 pr_err("%s: Error setting Tabla MCLK\n", __func__);
224 clk_users--;
225 return -EINVAL;
226 }
227 break;
228 }
229 return 0;
230}
231
232static int msm8960_mclk_disable_event(struct snd_soc_dapm_widget *w,
233 struct snd_kcontrol *kcontrol, int event)
234{
235 pr_debug("%s: Event = %d\n", __func__, event);
236
237 switch (event) {
238 case SND_SOC_DAPM_POST_PMD:
Kiran Kandi3a30bda2011-08-15 10:36:42 -0700239
240 if (clk_users == 0)
241 return 0;
242
243 clk_users--;
244 if (!clk_users) {
245 pr_debug("%s: disabling MCLK. clk_users = %d\n",
246 __func__, clk_users);
247
248 clk_disable(codec_clk);
249 clk_put(codec_clk);
250 tabla_mclk_enable(w->codec, 0);
251 }
252 break;
253 }
254 return 0;
255}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256
257static const struct snd_soc_dapm_widget msm8960_dapm_widgets[] = {
Kiran Kandi3a30bda2011-08-15 10:36:42 -0700258
259 SND_SOC_DAPM_PRE("CODEC MCLK_ON", msm8960_mclk_enable_event),
260 SND_SOC_DAPM_POST("CODEC MCLK_OFF", msm8960_mclk_disable_event),
261
Kiran Kandidb0a4b02011-08-23 09:32:09 -0700262 SND_SOC_DAPM_SPK("Ext Spk Bottom", msm8960_spkramp_event),
263 SND_SOC_DAPM_SPK("Ext Spk Top", msm8960_spkramp_event),
Kiran Kandi3a30bda2011-08-15 10:36:42 -0700264
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265 SND_SOC_DAPM_MIC("Handset Mic", NULL),
266 SND_SOC_DAPM_MIC("Headset Mic", NULL),
267 SND_SOC_DAPM_MIC("Digital Mic1", NULL),
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700268 SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
269 SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700270
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700271 SND_SOC_DAPM_MIC("Digital Mic1", NULL),
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700272 SND_SOC_DAPM_MIC("Digital Mic2", NULL),
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700273 SND_SOC_DAPM_MIC("Digital Mic3", NULL),
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700274 SND_SOC_DAPM_MIC("Digital Mic4", NULL),
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700275 SND_SOC_DAPM_MIC("Digital Mic5", NULL),
276 SND_SOC_DAPM_MIC("Digital Mic6", NULL),
277
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278};
279
Bradley Rubin229c6a52011-07-12 16:18:48 -0700280static const struct snd_soc_dapm_route common_audio_map[] = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281 /* Speaker path */
Kiran Kandidb0a4b02011-08-23 09:32:09 -0700282
283 {"Ext Spk Bottom", NULL, "LINEOUT1"},
284 {"Ext Spk Bottom", NULL, "LINEOUT3"},
285
286 {"Ext Spk Top", NULL, "LINEOUT2"},
287 {"Ext Spk Top", NULL, "LINEOUT4"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288
289 /* Microphone path */
Bradley Rubin229c6a52011-07-12 16:18:48 -0700290 {"AMIC1", NULL, "MIC BIAS1 Internal1"},
291 {"MIC BIAS1 Internal1", NULL, "Handset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700292
293 {"AMIC2", NULL, "MIC BIAS2 External"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294 {"MIC BIAS2 External", NULL, "Headset Mic"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700295
Kiran Kandie9bf86a2011-07-21 16:50:41 -0700296 {"HEADPHONE", NULL, "LDO_H"},
297
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700298 /**
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700299 * The digital Mic routes are setup considering
300 * fluid as default device.
301 */
302
303 /**
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700304 * Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP.
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700305 * Digital Mic GM5 on CDP mainboard.
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700306 * Conncted to DMIC2 Input on Tabla codec.
307 */
308 {"DMIC2", NULL, "MIC BIAS1 External"},
Bhalchandra Gajarecc6ffa02011-07-14 18:35:41 -0700309 {"MIC BIAS1 External", NULL, "Digital Mic1"},
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700310
311 /**
312 * Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP.
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700313 * Digital Mic GM6 on CDP mainboard.
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700314 * Conncted to DMIC1 Input on Tabla codec.
315 */
316 {"DMIC1", NULL, "MIC BIAS1 External"},
317 {"MIC BIAS1 External", NULL, "Digital Mic2"},
318
319 /**
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700320 * Digital Mic3. Back Bottom Digital Mic on Fluid.
321 * Digital Mic GM1 on CDP mainboard.
322 * Conncted to DMIC4 Input on Tabla codec.
323 */
324 {"DMIC4", NULL, "MIC BIAS3 External"},
325 {"MIC BIAS3 External", NULL, "Digital Mic3"},
326
327 /**
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700328 * Digital Mic4. Back top Digital Mic on Fluid.
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700329 * Digital Mic GM2 on CDP mainboard.
Kiran Kandia21d6bc2011-07-17 21:19:59 -0700330 * Conncted to DMIC3 Input on Tabla codec.
331 */
332 {"DMIC3", NULL, "MIC BIAS3 External"},
333 {"MIC BIAS3 External", NULL, "Digital Mic4"},
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700334
335 /**
336 * Digital Mic5. Front top Digital Mic on Fluid.
337 * Digital Mic GM3 on CDP mainboard.
338 * Conncted to DMIC5 Input on Tabla codec.
339 */
340 {"DMIC5", NULL, "MIC BIAS4 External"},
341 {"MIC BIAS4 External", NULL, "Digital Mic5"},
342
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700343};
344
Bradley Rubin229c6a52011-07-12 16:18:48 -0700345static const struct snd_soc_dapm_route cdp_audio_map[] = {
346 {"AMIC3", NULL, "MIC BIAS3 External"},
347 {"MIC BIAS3 External", NULL, "ANCRight Headset Mic"},
348
349 {"AMIC4", NULL, "MIC BIAS4 External"},
350 {"MIC BIAS4 External", NULL, "ANCLeft Headset Mic"},
Bhalchandra Gajarea0101ba2011-08-11 20:05:56 -0700351
352 /** Digital Mic GM4 on CDP mainboard.
353 * Connected to DMIC6 input on Tabla codec.
354 */
355 {"DMIC6", NULL, "MIC BIAS4 External"},
356 {"MIC BIAS4 External", NULL, "Digital Mic6"},
357
Bradley Rubin229c6a52011-07-12 16:18:48 -0700358};
359
360static const struct snd_soc_dapm_route fluid_audio_map[] = {
361 {"AMIC3", NULL, "MIC BIAS3 Internal1"},
362 {"MIC BIAS3 Internal1", NULL, "ANCRight Headset Mic"},
363
364 {"AMIC4", NULL, "MIC BIAS1 Internal2"},
365 {"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
366};
367
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368static const char *spk_function[] = {"Off", "On"};
Patrick Lai9f4b4292011-07-16 22:11:09 -0700369static const char *slim0_rx_ch_text[] = {"One", "Two"};
370static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
371
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372static const struct soc_enum msm8960_enum[] = {
373 SOC_ENUM_SINGLE_EXT(2, spk_function),
Patrick Lai9f4b4292011-07-16 22:11:09 -0700374 SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
375 SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700376};
377
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700378static const char *btsco_rate_text[] = {"8000", "16000"};
379static const struct soc_enum msm8960_btsco_enum[] = {
380 SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
381};
382
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383static int msm8960_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
384 struct snd_ctl_elem_value *ucontrol)
385{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700386 pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 msm8960_slim_0_rx_ch);
Patrick Lai9f4b4292011-07-16 22:11:09 -0700388 ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389 return 0;
390}
391
392static int msm8960_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
393 struct snd_ctl_elem_value *ucontrol)
394{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700395 msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396
Patrick Lai9f4b4292011-07-16 22:11:09 -0700397 pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398 msm8960_slim_0_rx_ch);
399 return 1;
400}
401
402static int msm8960_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
403 struct snd_ctl_elem_value *ucontrol)
404{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700405 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 msm8960_slim_0_tx_ch);
Patrick Lai9f4b4292011-07-16 22:11:09 -0700407 ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch - 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700408 return 0;
409}
410
411static int msm8960_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
412 struct snd_ctl_elem_value *ucontrol)
413{
Patrick Lai9f4b4292011-07-16 22:11:09 -0700414 msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415
416 pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
417 msm8960_slim_0_tx_ch);
418 return 1;
419}
420
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700421static int msm8960_btsco_rate_get(struct snd_kcontrol *kcontrol,
422 struct snd_ctl_elem_value *ucontrol)
423{
424 pr_debug("%s: msm8960_btsco_rate = %d", __func__,
425 msm8960_btsco_rate);
426 ucontrol->value.integer.value[0] = msm8960_btsco_rate;
427 return 0;
428}
429
430static int msm8960_btsco_rate_put(struct snd_kcontrol *kcontrol,
431 struct snd_ctl_elem_value *ucontrol)
432{
433 switch (ucontrol->value.integer.value[0]) {
434 case 0:
435 msm8960_btsco_rate = BTSCO_RATE_8KHZ;
436 break;
437 case 1:
438 msm8960_btsco_rate = BTSCO_RATE_16KHZ;
439 break;
440 default:
441 msm8960_btsco_rate = BTSCO_RATE_8KHZ;
442 break;
443 }
444 pr_debug("%s: msm8960_btsco_rate = %d\n", __func__,
445 msm8960_btsco_rate);
446 return 0;
447}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700448
449static const struct snd_kcontrol_new tabla_msm8960_controls[] = {
450 SOC_ENUM_EXT("Speaker Function", msm8960_enum[0], msm8960_get_spk,
451 msm8960_set_spk),
Patrick Lai9f4b4292011-07-16 22:11:09 -0700452 SOC_ENUM_EXT("SLIM_0_RX Channels", msm8960_enum[1],
453 msm8960_slim_0_rx_ch_get, msm8960_slim_0_rx_ch_put),
454 SOC_ENUM_EXT("SLIM_0_TX Channels", msm8960_enum[2],
455 msm8960_slim_0_tx_ch_get, msm8960_slim_0_tx_ch_put),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700456};
457
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700458static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
459 SOC_ENUM_EXT("Internal BTSCO SampleRate", msm8960_btsco_enum[0],
460 msm8960_btsco_rate_get, msm8960_btsco_rate_put),
461};
462
463static int msm8960_btsco_init(struct snd_soc_pcm_runtime *rtd)
464{
465 int err = 0;
466 struct snd_soc_platform *platform = rtd->platform;
467
468 err = snd_soc_add_platform_controls(platform,
469 int_btsco_rate_mixer_controls,
470 ARRAY_SIZE(int_btsco_rate_mixer_controls));
471 if (err < 0)
472 return err;
473 return 0;
474}
475
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700476static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
477{
478 int err;
479 struct snd_soc_codec *codec = rtd->codec;
480 struct snd_soc_dapm_context *dapm = &codec->dapm;
481
482 pr_debug("%s()\n", __func__);
483
484 err = snd_soc_add_controls(codec, tabla_msm8960_controls,
485 ARRAY_SIZE(tabla_msm8960_controls));
486 if (err < 0)
487 return err;
488
489 snd_soc_dapm_new_controls(dapm, msm8960_dapm_widgets,
490 ARRAY_SIZE(msm8960_dapm_widgets));
491
Bradley Rubin229c6a52011-07-12 16:18:48 -0700492 snd_soc_dapm_add_routes(dapm, common_audio_map,
493 ARRAY_SIZE(common_audio_map));
494
495 if (machine_is_msm8960_cdp())
496 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
497 ARRAY_SIZE(cdp_audio_map));
498 else if (machine_is_msm8960_mtp())
499 snd_soc_dapm_add_routes(dapm, cdp_audio_map,
500 ARRAY_SIZE(cdp_audio_map));
501 else if (machine_is_msm8960_fluid())
502 snd_soc_dapm_add_routes(dapm, fluid_audio_map,
503 ARRAY_SIZE(fluid_audio_map));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700504
505 snd_soc_dapm_enable_pin(dapm, "Ext Spk");
506
507 snd_soc_dapm_sync(dapm);
508
509 err = snd_soc_jack_new(codec, "Headset Jack",
510 SND_JACK_HEADSET, &hs_jack);
511 if (err) {
512 pr_err("failed to create new jack\n");
513 return err;
514 }
Bradley Rubincb1e2732011-06-23 16:49:20 -0700515
516 err = snd_soc_jack_new(codec, "Button Jack",
517 SND_JACK_BTN_0, &button_jack);
518 if (err) {
519 pr_err("failed to create new jack\n");
520 return err;
521 }
522
523 tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_cal);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700524
525 return 0;
526}
527
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700528static struct snd_soc_dsp_link lpa_fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700529 .playback = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530 .trigger = {
531 SND_SOC_DSP_TRIGGER_POST,
532 SND_SOC_DSP_TRIGGER_POST
533 },
534};
535
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700536static struct snd_soc_dsp_link fe_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700537 .playback = true,
538 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700539 .trigger = {
540 SND_SOC_DSP_TRIGGER_POST,
541 SND_SOC_DSP_TRIGGER_POST
542 },
543};
544
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545static struct snd_soc_dsp_link slimbus0_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700546 .playback = true,
547 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548 .trigger = {
549 SND_SOC_DSP_TRIGGER_POST,
550 SND_SOC_DSP_TRIGGER_POST
551 },
552};
553
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700554static struct snd_soc_dsp_link int_fm_hl_media = {
Patrick Lai5b3fdfc2011-09-01 11:04:56 -0700555 .playback = true,
556 .capture = true,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700557 .trigger = {
558 SND_SOC_DSP_TRIGGER_POST,
559 SND_SOC_DSP_TRIGGER_POST
560 },
561};
562
563static int msm8960_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
564 struct snd_pcm_hw_params *params)
565{
566 struct snd_interval *rate = hw_param_interval(params,
567 SNDRV_PCM_HW_PARAM_RATE);
568
569 struct snd_interval *channels = hw_param_interval(params,
570 SNDRV_PCM_HW_PARAM_CHANNELS);
571
572 pr_debug("%s()\n", __func__);
573 rate->min = rate->max = 48000;
574 channels->min = channels->max = msm8960_slim_0_rx_ch;
575
576 return 0;
577}
578
579static int msm8960_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
580 struct snd_pcm_hw_params *params)
581{
582 struct snd_interval *rate = hw_param_interval(params,
583 SNDRV_PCM_HW_PARAM_RATE);
584
585 struct snd_interval *channels = hw_param_interval(params,
586 SNDRV_PCM_HW_PARAM_CHANNELS);
587
588 pr_debug("%s()\n", __func__);
589 rate->min = rate->max = 48000;
590 channels->min = channels->max = msm8960_slim_0_tx_ch;
591
592 return 0;
593}
594
595static int msm8960_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
596 struct snd_pcm_hw_params *params)
597{
598 struct snd_interval *rate = hw_param_interval(params,
599 SNDRV_PCM_HW_PARAM_RATE);
600
601 pr_debug("%s()\n", __func__);
602 rate->min = rate->max = 48000;
603
604 return 0;
605}
606
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700607static int msm8960_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
608 struct snd_pcm_hw_params *params)
609{
610 struct snd_interval *rate = hw_param_interval(params,
611 SNDRV_PCM_HW_PARAM_RATE);
612
613 struct snd_interval *channels = hw_param_interval(params,
614 SNDRV_PCM_HW_PARAM_CHANNELS);
615
616 rate->min = rate->max = msm8960_btsco_rate;
617 channels->min = channels->max = msm8960_btsco_ch;
618
619 return 0;
620}
621
Kiran Kandi3a30bda2011-08-15 10:36:42 -0700622
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700623static int msm8960_startup(struct snd_pcm_substream *substream)
624{
Kiran Kandi3a30bda2011-08-15 10:36:42 -0700625 pr_debug("%s(): substream = %s\n", __func__, substream->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700626 return 0;
627}
628
629static void msm8960_shutdown(struct snd_pcm_substream *substream)
630{
Kiran Kandi3a30bda2011-08-15 10:36:42 -0700631 pr_debug("%s(): substream = %s\n", __func__, substream->name);
632 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700633}
634
635static struct snd_soc_ops msm8960_be_ops = {
636 .startup = msm8960_startup,
637 .shutdown = msm8960_shutdown,
638};
639
640/* Digital audio interface glue - connects codec <---> CPU */
641static struct snd_soc_dai_link msm8960_dai[] = {
642 /* FrontEnd DAI Links */
643 {
644 .name = "MSM8960 Media1",
645 .stream_name = "MultiMedia1",
646 .cpu_dai_name = "MultiMedia1",
647 .platform_name = "msm-pcm-dsp",
648 .dynamic = 1,
649 .dsp_link = &fe_media,
650 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
651 },
652 {
653 .name = "MSM8960 Media2",
654 .stream_name = "MultiMedia2",
655 .cpu_dai_name = "MultiMedia2",
656 .platform_name = "msm-pcm-dsp",
657 .dynamic = 1,
658 .dsp_link = &fe_media,
659 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
660 },
661 {
662 .name = "Circuit-Switch Voice",
663 .stream_name = "CS-Voice",
664 .cpu_dai_name = "CS-VOICE",
665 .platform_name = "msm-pcm-voice",
666 .dynamic = 1,
667 .dsp_link = &fe_media,
668 .be_id = MSM_FRONTEND_DAI_CS_VOICE,
669 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
670 },
671 {
672 .name = "MSM VoIP",
673 .stream_name = "VoIP",
674 .cpu_dai_name = "VoIP",
675 .platform_name = "msm-voip-dsp",
676 .dynamic = 1,
677 .dsp_link = &fe_media,
678 .be_id = MSM_FRONTEND_DAI_VOIP,
679 },
680 {
681 .name = "MSM8960 LPA",
682 .stream_name = "LPA",
683 .cpu_dai_name = "MultiMedia3",
684 .platform_name = "msm-pcm-lpa",
685 .dynamic = 1,
686 .dsp_link = &lpa_fe_media,
687 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
688 },
689 /* Hostless PMC purpose */
690 {
691 .name = "SLIMBUS_0 Hostless",
692 .stream_name = "SLIMBUS_0 Hostless",
693 .cpu_dai_name = "SLIMBUS0_HOSTLESS",
694 .platform_name = "msm-pcm-hostless",
695 .dynamic = 1,
696 .dsp_link = &slimbus0_hl_media,
697 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
698 /* .be_id = do not care */
699 },
700 {
701 .name = "INT_FM Hostless",
702 .stream_name = "INT_FM Hostless",
703 .cpu_dai_name = "INT_FM_HOSTLESS",
704 .platform_name = "msm-pcm-hostless",
705 .dynamic = 1,
706 .dsp_link = &int_fm_hl_media,
707 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
708 /* .be_id = do not care */
709 },
710 /* Backend DAI Links */
711 {
712 .name = LPASS_BE_SLIMBUS_0_RX,
713 .stream_name = "Slimbus Playback",
714 .cpu_dai_name = "msm-dai-q6.16384",
715 .platform_name = "msm-pcm-routing",
716 .codec_name = "tabla_codec",
717 .codec_dai_name = "tabla_rx1",
718 .no_pcm = 1,
719 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
720 .init = &msm8960_audrx_init,
721 .be_hw_params_fixup = msm8960_slim_0_rx_be_hw_params_fixup,
722 .ops = &msm8960_be_ops,
723 },
724 {
725 .name = LPASS_BE_SLIMBUS_0_TX,
726 .stream_name = "Slimbus Capture",
727 .cpu_dai_name = "msm-dai-q6.16385",
728 .platform_name = "msm-pcm-routing",
729 .codec_name = "tabla_codec",
730 .codec_dai_name = "tabla_tx1",
731 .no_pcm = 1,
732 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
733 .be_hw_params_fixup = msm8960_slim_0_tx_be_hw_params_fixup,
734 .ops = &msm8960_be_ops,
735 },
736 /* Backend BT/FM DAI Links */
737 {
738 .name = LPASS_BE_INT_BT_SCO_RX,
739 .stream_name = "Internal BT-SCO Playback",
740 .cpu_dai_name = "msm-dai-q6.12288",
741 .platform_name = "msm-pcm-routing",
742 .codec_name = "msm-stub-codec.1",
743 .codec_dai_name = "msm-stub-rx",
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700744 .init = &msm8960_btsco_init,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700745 .no_pcm = 1,
746 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700747 .be_hw_params_fixup = msm8960_btsco_be_hw_params_fixup,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700748 },
749 {
750 .name = LPASS_BE_INT_BT_SCO_TX,
751 .stream_name = "Internal BT-SCO Capture",
752 .cpu_dai_name = "msm-dai-q6.12289",
753 .platform_name = "msm-pcm-routing",
754 .codec_name = "msm-stub-codec.1",
755 .codec_dai_name = "msm-stub-tx",
756 .no_pcm = 1,
757 .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
Jayasena Sangaraboina47c08242011-08-10 11:30:57 -0700758 .be_hw_params_fixup = msm8960_btsco_be_hw_params_fixup,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700759 },
760 {
761 .name = LPASS_BE_INT_FM_RX,
762 .stream_name = "Internal FM Playback",
763 .cpu_dai_name = "msm-dai-q6.12292",
764 .platform_name = "msm-pcm-routing",
765 .codec_name = "msm-stub-codec.1",
766 .codec_dai_name = "msm-stub-rx",
767 .no_pcm = 1,
768 .be_id = MSM_BACKEND_DAI_INT_FM_RX,
769 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
770 },
771 {
772 .name = LPASS_BE_INT_FM_TX,
773 .stream_name = "Internal FM Capture",
774 .cpu_dai_name = "msm-dai-q6.12293",
775 .platform_name = "msm-pcm-routing",
776 .codec_name = "msm-stub-codec.1",
777 .codec_dai_name = "msm-stub-tx",
778 .no_pcm = 1,
779 .be_id = MSM_BACKEND_DAI_INT_FM_TX,
780 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
781 },
782 /* HDMI BACK END DAI Link */
783 {
784 .name = LPASS_BE_HDMI,
785 .stream_name = "HDMI Playback",
786 .cpu_dai_name = "msm-dai-q6.8",
787 .platform_name = "msm-pcm-routing",
788 .codec_name = "msm-stub-codec.1",
789 .codec_dai_name = "msm-stub-rx",
790 .no_pcm = 1,
791 .no_codec = 1,
792 .be_id = MSM_BACKEND_DAI_HDMI_RX,
793 .be_hw_params_fixup = msm8960_be_hw_params_fixup,
794 },
795};
796
797struct snd_soc_card snd_soc_card_msm8960 = {
798 .name = "msm8960-snd-card",
799 .dai_link = msm8960_dai,
800 .num_links = ARRAY_SIZE(msm8960_dai),
801};
802
803static struct platform_device *msm8960_snd_device;
804
805static int msm8960_configure_headset_mic_gpios(void)
806{
807 int ret;
808 struct pm_gpio param = {
809 .direction = PM_GPIO_DIR_OUT,
810 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
811 .output_value = 1,
812 .pull = PM_GPIO_PULL_NO,
813 .vin_sel = PM_GPIO_VIN_S4,
814 .out_strength = PM_GPIO_STRENGTH_MED,
815 .function = PM_GPIO_FUNC_NORMAL,
816 };
817
818 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
819 if (ret) {
820 pr_err("%s: Failed to request gpio %d\n", __func__,
821 PM8921_GPIO_PM_TO_SYS(23));
822 return ret;
823 }
824
825 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
826 if (ret)
827 pr_err("%s: Failed to configure gpio %d\n", __func__,
828 PM8921_GPIO_PM_TO_SYS(23));
829 else
830 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
831
832 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
833 if (ret) {
834 pr_err("%s: Failed to request gpio %d\n", __func__,
835 PM8921_GPIO_PM_TO_SYS(35));
836 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
837 return ret;
838 }
839 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
840 if (ret)
841 pr_err("%s: Failed to configure gpio %d\n", __func__,
842 PM8921_GPIO_PM_TO_SYS(35));
843 else
844 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 1);
845
846 return 0;
847}
848static void msm8960_free_headset_mic_gpios(void)
849{
850 if (msm8960_headset_gpios_configured) {
851 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
852 gpio_free(PM8921_GPIO_PM_TO_SYS(35));
853 }
854}
855
856static int __init msm8960_audio_init(void)
857{
858 int ret;
859
860 msm8960_snd_device = platform_device_alloc("soc-audio", 0);
861 if (!msm8960_snd_device) {
862 pr_err("Platform device allocation failed\n");
863 return -ENOMEM;
864 }
865
866 platform_set_drvdata(msm8960_snd_device, &snd_soc_card_msm8960);
867 ret = platform_device_add(msm8960_snd_device);
868 if (ret) {
869 platform_device_put(msm8960_snd_device);
870 return ret;
871 }
872
873 if (msm8960_configure_headset_mic_gpios()) {
874 pr_err("%s Fail to configure headset mic gpios\n", __func__);
875 msm8960_headset_gpios_configured = 0;
876 } else
877 msm8960_headset_gpios_configured = 1;
878
879 return ret;
880
881}
882module_init(msm8960_audio_init);
883
884static void __exit msm8960_audio_exit(void)
885{
886 msm8960_free_headset_mic_gpios();
887 platform_device_unregister(msm8960_snd_device);
888}
889module_exit(msm8960_audio_exit);
890
891MODULE_DESCRIPTION("ALSA SoC MSM8960");
892MODULE_LICENSE("GPL v2");