blob: 961f8332d4d83dfd8621761fc59d757562869c60 [file] [log] [blame]
Santosh Mardi603fbb92012-03-22 03:45:46 +05301/* Copyright (c) 2012, 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/delay.h>
15#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 <linux/slab.h>
21#include <sound/core.h>
22#include <sound/soc.h>
23#include <sound/soc-dapm.h>
24#include <sound/soc-dsp.h>
25#include <sound/pcm.h>
26#include <sound/jack.h>
27#include <sound/pcm_params.h>
28#include <asm/mach-types.h>
29#include <mach/socinfo.h>
30#include "msm-pcm-routing.h"
31#include "../codecs/wcd9310.h"
32
33/* 8064 machine driver */
34
35#define PM8921_GPIO_BASE NR_GPIO_IRQS
36#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
37
38#define MPQ8064_SPK_ON 1
39#define MPQ8064_SPK_OFF 0
40
41#define MSM_SLIM_0_RX_MAX_CHANNELS 2
42#define MSM_SLIM_0_TX_MAX_CHANNELS 4
43
44#define BOTTOM_SPK_AMP_POS 0x1
45#define BOTTOM_SPK_AMP_NEG 0x2
46#define TOP_SPK_AMP_POS 0x4
47#define TOP_SPK_AMP_NEG 0x8
48
49#define TABLA_EXT_CLK_RATE 12288000
50
51#define TABLA_MBHC_DEF_BUTTONS 8
52#define TABLA_MBHC_DEF_RLOADS 5
53
54#define GPIO_SEC_I2S_RX_SCK 47
55#define GPIO_SEC_I2S_RX_WS 48
56#define GPIO_SEC_I2S_RX_DOUT 49
57#define GPIO_SEC_I2S_RX_MCLK 50
58#define I2S_MCLK_RATE 1536000
59
60static struct clk *sec_i2s_rx_osr_clk;
61static struct clk *sec_i2s_rx_bit_clk;
62
63struct request_gpio {
64 unsigned gpio_no;
65 char *gpio_name;
66};
67
68static struct request_gpio sec_i2s_rx_gpio[] = {
69 {
70 .gpio_no = GPIO_SEC_I2S_RX_MCLK,
71 .gpio_name = "SEC_I2S_RX_MCLK",
72 },
73 {
74 .gpio_no = GPIO_SEC_I2S_RX_SCK,
75 .gpio_name = "SEC_I2S_RX_SCK",
76 },
77 {
78 .gpio_no = GPIO_SEC_I2S_RX_WS,
79 .gpio_name = "SEC_I2S_RX_WS",
80 },
81 {
82 .gpio_no = GPIO_SEC_I2S_RX_DOUT,
83 .gpio_name = "SEC_I2S_RX_DOUT",
84 },
85};
86
87static u32 top_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(18);
88static u32 bottom_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(19);
89static int msm_spk_control;
90static int msm_ext_bottom_spk_pamp;
91static int msm_ext_top_spk_pamp;
92static int msm_slim_0_rx_ch = 1;
93static int msm_slim_0_tx_ch = 1;
94
95static struct clk *codec_clk;
96static int clk_users;
97
98static int msm_headset_gpios_configured;
99
100static struct snd_soc_jack hs_jack;
101static struct snd_soc_jack button_jack;
102
103static int msm_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
104 bool dapm);
105
106static struct tabla_mbhc_config mbhc_cfg = {
107 .headset_jack = &hs_jack,
108 .button_jack = &button_jack,
109 .read_fw_bin = false,
110 .calibration = NULL,
111 .micbias = TABLA_MICBIAS2,
112 .mclk_cb_fn = msm_enable_codec_ext_clk,
113 .mclk_rate = TABLA_EXT_CLK_RATE,
114 .gpio = 0, /* MBHC GPIO is not configured */
115 .gpio_irq = 0,
116 .gpio_level_insert = 1,
117};
118
119static void msm_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
120{
121 int ret = 0;
122
123 struct pm_gpio param = {
124 .direction = PM_GPIO_DIR_OUT,
125 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
126 .output_value = 1,
127 .pull = PM_GPIO_PULL_NO,
128 .vin_sel = PM_GPIO_VIN_S4,
129 .out_strength = PM_GPIO_STRENGTH_MED,
130 .
131 function = PM_GPIO_FUNC_NORMAL,
132 };
133
134 if (spk_amp_gpio == bottom_spk_pamp_gpio) {
135
136 ret = gpio_request(bottom_spk_pamp_gpio, "BOTTOM_SPK_AMP");
137 if (ret) {
138 pr_err("%s: Error requesting BOTTOM SPK AMP GPIO %u\n",
139 __func__, bottom_spk_pamp_gpio);
140 return;
141 }
142 ret = pm8xxx_gpio_config(bottom_spk_pamp_gpio, &param);
143 if (ret)
144 pr_err("%s: Failed to configure Bottom Spk Ampl"
145 " gpio %u\n", __func__, bottom_spk_pamp_gpio);
146 else {
147 pr_debug("%s: enable Bottom spkr amp gpio\n", __func__);
148 gpio_direction_output(bottom_spk_pamp_gpio, 1);
149 }
150
151 } else if (spk_amp_gpio == top_spk_pamp_gpio) {
152
153 ret = gpio_request(top_spk_pamp_gpio, "TOP_SPK_AMP");
154 if (ret) {
155 pr_err("%s: Error requesting GPIO %d\n", __func__,
156 top_spk_pamp_gpio);
157 return;
158 }
159 ret = pm8xxx_gpio_config(top_spk_pamp_gpio, &param);
160 if (ret)
161 pr_err("%s: Failed to configure Top Spk Ampl"
162 " gpio %u\n", __func__, top_spk_pamp_gpio);
163 else {
164 pr_debug("%s: enable Top spkr amp gpio\n", __func__);
165 gpio_direction_output(top_spk_pamp_gpio, 1);
166 }
167 } else {
168 pr_err("%s: ERROR : Invalid External Speaker Ampl GPIO."
169 " gpio = %u\n", __func__, spk_amp_gpio);
170 return;
171 }
172}
173
174static void msm_ext_spk_power_amp_on(u32 spk)
175{
176 if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
177
178 if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
179 (msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
180
181 pr_debug("%s() External Bottom Speaker Ampl already "
182 "turned on. spk = 0x%08x\n", __func__, spk);
183 return;
184 }
185
186 msm_ext_bottom_spk_pamp |= spk;
187
188 if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
189 (msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
190
191 msm_enable_ext_spk_amp_gpio(bottom_spk_pamp_gpio);
192 pr_debug("%s: slepping 4 ms after turning on external "
193 " Bottom Speaker Ampl\n", __func__);
194 usleep_range(4000, 4000);
195 }
196
197 } else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
198
199 if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
200 (msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
201
202 pr_debug("%s() External Top Speaker Ampl already"
203 "turned on. spk = 0x%08x\n", __func__, spk);
204 return;
205 }
206
207 msm_ext_top_spk_pamp |= spk;
208
209 if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
210 (msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
211
212 msm_enable_ext_spk_amp_gpio(top_spk_pamp_gpio);
213 pr_debug("%s: sleeping 4 ms after turning on "
214 " external Top Speaker Ampl\n", __func__);
215 usleep_range(4000, 4000);
216 }
217 } else {
218
219 pr_err("%s: ERROR : Invalid External Speaker Ampl. spk = 0x%08x\n",
220 __func__, spk);
221 return;
222 }
223}
224
225static void msm_ext_spk_power_amp_off(u32 spk)
226{
227 if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
228
229 if (!msm_ext_bottom_spk_pamp)
230 return;
231
232 gpio_direction_output(bottom_spk_pamp_gpio, 0);
233 gpio_free(bottom_spk_pamp_gpio);
234 msm_ext_bottom_spk_pamp = 0;
235
236 pr_debug("%s: sleeping 4 ms after turning off external Bottom"
237 " Speaker Ampl\n", __func__);
238
239 usleep_range(4000, 4000);
240
241 } else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
242
243 if (!msm_ext_top_spk_pamp)
244 return;
245
246 gpio_direction_output(top_spk_pamp_gpio, 0);
247 gpio_free(top_spk_pamp_gpio);
248 msm_ext_top_spk_pamp = 0;
249
250 pr_debug("%s: sleeping 4 ms after turning off external Top"
251 " Spkaker Ampl\n", __func__);
252
253 usleep_range(4000, 4000);
254 } else {
255
256 pr_err("%s: ERROR : Invalid Ext Spk Ampl. spk = 0x%08x\n",
257 __func__, spk);
258 return;
259 }
260}
261
262static void msm_ext_control(struct snd_soc_codec *codec)
263{
264 struct snd_soc_dapm_context *dapm = &codec->dapm;
265
266 pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
267 if (msm_spk_control == MPQ8064_SPK_ON) {
268 snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
269 snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
270 snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
271 snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
272 } else {
273 snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Pos");
274 snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Neg");
275 snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Pos");
276 snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Neg");
277 }
278
279 snd_soc_dapm_sync(dapm);
280}
281
282static int msm_get_spk(struct snd_kcontrol *kcontrol,
283 struct snd_ctl_elem_value *ucontrol)
284{
285 pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
286 ucontrol->value.integer.value[0] = msm_spk_control;
287 return 0;
288}
289static int msm_set_spk(struct snd_kcontrol *kcontrol,
290 struct snd_ctl_elem_value *ucontrol)
291{
292 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
293
294 pr_debug("%s()\n", __func__);
295 if (msm_spk_control == ucontrol->value.integer.value[0])
296 return 0;
297
298 msm_spk_control = ucontrol->value.integer.value[0];
299 msm_ext_control(codec);
300 return 1;
301}
302static int msm_spkramp_event(struct snd_soc_dapm_widget *w,
303 struct snd_kcontrol *k, int event)
304{
305 pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
306
307 if (SND_SOC_DAPM_EVENT_ON(event)) {
308 if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
309 msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_POS);
310 else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
311 msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_NEG);
312 else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
313 msm_ext_spk_power_amp_on(TOP_SPK_AMP_POS);
314 else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
315 msm_ext_spk_power_amp_on(TOP_SPK_AMP_NEG);
316 else {
317 pr_err("%s() Invalid Speaker Widget = %s\n",
318 __func__, w->name);
319 return -EINVAL;
320 }
321
322 } else {
323 if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
324 msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_POS);
325 else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
326 msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_NEG);
327 else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
328 msm_ext_spk_power_amp_off(TOP_SPK_AMP_POS);
329 else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
330 msm_ext_spk_power_amp_off(TOP_SPK_AMP_NEG);
331 else {
332 pr_err("%s() Invalid Speaker Widget = %s\n",
333 __func__, w->name);
334 return -EINVAL;
335 }
336 }
337 return 0;
338}
339
340static int msm_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
341 bool dapm)
342{
343 pr_debug("%s: enable = %d\n", __func__, enable);
344 if (enable) {
345 clk_users++;
346 pr_debug("%s: clk_users = %d\n", __func__, clk_users);
347 if (clk_users != 1)
348 return 0;
349
350 if (codec_clk) {
351 clk_set_rate(codec_clk, TABLA_EXT_CLK_RATE);
352 clk_enable(codec_clk);
353 tabla_mclk_enable(codec, 1, dapm);
354 } else {
355 pr_err("%s: Error setting Tabla MCLK\n", __func__);
356 clk_users--;
357 return -EINVAL;
358 }
359 } else {
360 pr_debug("%s: clk_users = %d\n", __func__, clk_users);
361 if (clk_users == 0)
362 return 0;
363 clk_users--;
364 if (!clk_users) {
365 pr_debug("%s: disabling MCLK. clk_users = %d\n",
366 __func__, clk_users);
367 clk_disable(codec_clk);
368 tabla_mclk_enable(codec, 0, dapm);
369 }
370 }
371 return 0;
372}
373
374static int msm_mclk_event(struct snd_soc_dapm_widget *w,
375 struct snd_kcontrol *kcontrol, int event)
376{
377 pr_debug("%s: event = %d\n", __func__, event);
378
379 switch (event) {
380 case SND_SOC_DAPM_PRE_PMU:
381
382 clk_users++;
383 pr_debug("%s: clk_users = %d\n", __func__, clk_users);
384
385 if (clk_users != 1)
386 return 0;
387
388 if (codec_clk) {
389 clk_set_rate(codec_clk, 12288000);
390 clk_enable(codec_clk);
391 tabla_mclk_enable(w->codec, 1, true);
392
393 } else {
394 pr_err("%s: Error setting Tabla MCLK\n", __func__);
395 clk_users--;
396 return -EINVAL;
397 }
398 break;
399 case SND_SOC_DAPM_POST_PMD:
400
401 pr_debug("%s: clk_users = %d\n", __func__, clk_users);
402
403 if (clk_users == 0)
404 return 0;
405
406 clk_users--;
407
408 if (!clk_users) {
409 pr_debug("%s: disabling MCLK. clk_users = %d\n",
410 __func__, clk_users);
411
412 clk_disable(codec_clk);
413 tabla_mclk_enable(w->codec, 0, true);
414 }
415 break;
416 }
417 return 0;
418}
419
420static const struct snd_soc_dapm_widget msm_dapm_widgets[] = {
421
422 SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
423 msm_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
424
425 SND_SOC_DAPM_SPK("Ext Spk Bottom Pos", msm_spkramp_event),
426 SND_SOC_DAPM_SPK("Ext Spk Bottom Neg", msm_spkramp_event),
427
428 SND_SOC_DAPM_SPK("Ext Spk Top Pos", msm_spkramp_event),
429 SND_SOC_DAPM_SPK("Ext Spk Top Neg", msm_spkramp_event),
430
431 SND_SOC_DAPM_MIC("Handset Mic", NULL),
432 SND_SOC_DAPM_MIC("Headset Mic", NULL),
433
434 SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
435 SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
436};
437
438static const struct snd_soc_dapm_route common_audio_map[] = {
439
440 {"RX_BIAS", NULL, "MCLK"},
441 {"LDO_H", NULL, "MCLK"},
442
443 /* Speaker path */
444 {"Ext Spk Bottom Pos", NULL, "LINEOUT1"},
445 {"Ext Spk Bottom Neg", NULL, "LINEOUT3"},
446
447 {"Ext Spk Top Pos", NULL, "LINEOUT2"},
448 {"Ext Spk Top Neg", NULL, "LINEOUT4"},
449
450 /* Microphone path */
451 {"AMIC1", NULL, "MIC BIAS1 Internal1"},
452 {"MIC BIAS1 Internal1", NULL, "Handset Mic"},
453
454 {"AMIC2", NULL, "MIC BIAS2 External"},
455 {"MIC BIAS2 External", NULL, "Headset Mic"},
456
457 /**
458 * AMIC3 and AMIC4 inputs are connected to ANC microphones
459 * These mics are biased differently on CDP and FLUID
460 * routing entries below are based on bias arrangement
461 * on FLUID.
462 */
463 {"AMIC3", NULL, "MIC BIAS3 Internal1"},
464 {"MIC BIAS3 Internal1", NULL, "ANCRight Headset Mic"},
465
466 {"AMIC4", NULL, "MIC BIAS1 Internal2"},
467 {"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
468
469 {"HEADPHONE", NULL, "LDO_H"},
470};
471
472static const char *spk_function[] = {"Off", "On"};
473static const char *slim0_rx_ch_text[] = {"One", "Two"};
474static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
475
476static const struct soc_enum msm_enum[] = {
477 SOC_ENUM_SINGLE_EXT(2, spk_function),
478 SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
479 SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
480};
481
482static int msm_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
483 struct snd_ctl_elem_value *ucontrol)
484{
485 pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
486 msm_slim_0_rx_ch);
487 ucontrol->value.integer.value[0] = msm_slim_0_rx_ch - 1;
488 return 0;
489}
490
491static int msm_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
492 struct snd_ctl_elem_value *ucontrol)
493{
494 msm_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
495
496 pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
497 msm_slim_0_rx_ch);
498 return 1;
499}
500
501static int msm_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
502 struct snd_ctl_elem_value *ucontrol)
503{
504 pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
505 msm_slim_0_tx_ch);
506 ucontrol->value.integer.value[0] = msm_slim_0_tx_ch - 1;
507 return 0;
508}
509
510static int msm_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
511 struct snd_ctl_elem_value *ucontrol)
512{
513 msm_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
514
515 pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
516 msm_slim_0_tx_ch);
517 return 1;
518}
519
520static const struct snd_kcontrol_new tabla_msm_controls[] = {
521 SOC_ENUM_EXT("Speaker Function", msm_enum[0], msm_get_spk,
522 msm_set_spk),
523 SOC_ENUM_EXT("SLIM_0_RX Channels", msm_enum[1],
524 msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
525 SOC_ENUM_EXT("SLIM_0_TX Channels", msm_enum[2],
526 msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
527};
528
529static void *def_tabla_mbhc_cal(void)
530{
531 void *tabla_cal;
532 struct tabla_mbhc_btn_detect_cfg *btn_cfg;
533 u16 *btn_low, *btn_high;
534 u8 *n_ready, *n_cic, *gain;
535
536 tabla_cal = kzalloc(TABLA_MBHC_CAL_SIZE(TABLA_MBHC_DEF_BUTTONS,
537 TABLA_MBHC_DEF_RLOADS),
538 GFP_KERNEL);
539 if (!tabla_cal) {
540 pr_err("%s: out of memory\n", __func__);
541 return NULL;
542 }
543
544#define S(X, Y) ((TABLA_MBHC_CAL_GENERAL_PTR(tabla_cal)->X) = (Y))
545 S(t_ldoh, 100);
546 S(t_bg_fast_settle, 100);
547 S(t_shutdown_plug_rem, 255);
548 S(mbhc_nsa, 4);
549 S(mbhc_navg, 4);
550#undef S
551#define S(X, Y) ((TABLA_MBHC_CAL_PLUG_DET_PTR(tabla_cal)->X) = (Y))
552 S(mic_current, TABLA_PID_MIC_5_UA);
553 S(hph_current, TABLA_PID_MIC_5_UA);
554 S(t_mic_pid, 100);
555 S(t_ins_complete, 250);
556 S(t_ins_retry, 200);
557#undef S
558#define S(X, Y) ((TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla_cal)->X) = (Y))
559 S(v_no_mic, 30);
560 S(v_hs_max, 1550);
561#undef S
562#define S(X, Y) ((TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal)->X) = (Y))
563 S(c[0], 62);
564 S(c[1], 124);
565 S(nc, 1);
566 S(n_meas, 3);
567 S(mbhc_nsc, 11);
568 S(n_btn_meas, 1);
569 S(n_btn_con, 2);
570 S(num_btn, TABLA_MBHC_DEF_BUTTONS);
571 S(v_btn_press_delta_sta, 100);
572 S(v_btn_press_delta_cic, 50);
573#undef S
574 btn_cfg = TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal);
575 btn_low = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_LOW);
576 btn_high = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_HIGH);
577 btn_low[0] = -50;
578 btn_high[0] = 10;
579 btn_low[1] = 11;
580 btn_high[1] = 38;
581 btn_low[2] = 39;
582 btn_high[2] = 64;
583 btn_low[3] = 65;
584 btn_high[3] = 91;
585 btn_low[4] = 92;
586 btn_high[4] = 115;
587 btn_low[5] = 116;
588 btn_high[5] = 141;
589 btn_low[6] = 142;
590 btn_high[6] = 163;
591 btn_low[7] = 164;
592 btn_high[7] = 250;
593 n_ready = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_READY);
594 n_ready[0] = 48;
595 n_ready[1] = 38;
596 n_cic = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_CIC);
597 n_cic[0] = 60;
598 n_cic[1] = 47;
599 gain = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_GAIN);
600 gain[0] = 11;
601 gain[1] = 9;
602
603 return tabla_cal;
604}
605
606static int msm_hw_params(struct snd_pcm_substream *substream,
607 struct snd_pcm_hw_params *params)
608{
609 struct snd_soc_pcm_runtime *rtd = substream->private_data;
610 struct snd_soc_dai *codec_dai = rtd->codec_dai;
611 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
612 int ret = 0;
613 unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
614 unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
615
616 pr_debug("%s: ch=%d\n", __func__,
617 msm_slim_0_rx_ch);
618 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
619 ret = snd_soc_dai_get_channel_map(codec_dai,
620 &tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
621 if (ret < 0) {
622 pr_err("%s: failed to get codec chan map\n", __func__);
623 goto end;
624 }
625
626 ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
627 msm_slim_0_rx_ch, rx_ch);
628 if (ret < 0) {
629 pr_err("%s: failed to set cpu chan map\n", __func__);
630 goto end;
631 }
632 ret = snd_soc_dai_set_channel_map(codec_dai, 0, 0,
633 msm_slim_0_rx_ch, rx_ch);
634 if (ret < 0) {
635 pr_err("%s: failed to set codec channel map\n",
636 __func__);
637 goto end;
638 }
639 } else {
640 ret = snd_soc_dai_get_channel_map(codec_dai,
641 &tx_ch_cnt, tx_ch, &rx_ch_cnt , rx_ch);
642 if (ret < 0) {
643 pr_err("%s: failed to get codec chan map\n", __func__);
644 goto end;
645 }
646 ret = snd_soc_dai_set_channel_map(cpu_dai,
647 msm_slim_0_tx_ch, tx_ch, 0 , 0);
648 if (ret < 0) {
649 pr_err("%s: failed to set cpu chan map\n", __func__);
650 goto end;
651 }
652 ret = snd_soc_dai_set_channel_map(codec_dai,
653 msm_slim_0_tx_ch, tx_ch, 0, 0);
654 if (ret < 0) {
655 pr_err("%s: failed to set codec channel map\n",
656 __func__);
657 goto end;
658 }
659
660
661 }
662end:
663 return ret;
664}
665
666static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
667{
668 int err;
669 struct snd_soc_codec *codec = rtd->codec;
670 struct snd_soc_dapm_context *dapm = &codec->dapm;
671 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
672
673 pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
674
675 /*if (machine_is_msm_liquid()) {
676 top_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(19));
677 bottom_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(18));
678 }*/
679
680 rtd->pmdown_time = 0;
681
682 err = snd_soc_add_controls(codec, tabla_msm_controls,
683 ARRAY_SIZE(tabla_msm_controls));
684 if (err < 0)
685 return err;
686
687 snd_soc_dapm_new_controls(dapm, msm_dapm_widgets,
688 ARRAY_SIZE(msm_dapm_widgets));
689
690 snd_soc_dapm_add_routes(dapm, common_audio_map,
691 ARRAY_SIZE(common_audio_map));
692
693 snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
694 snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
695 snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
696 snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
697
698 snd_soc_dapm_sync(dapm);
699
700 err = snd_soc_jack_new(codec, "Headset Jack",
701 (SND_JACK_HEADSET | SND_JACK_OC_HPHL | SND_JACK_OC_HPHR),
702 &hs_jack);
703 if (err) {
704 pr_err("failed to create new jack\n");
705 return err;
706 }
707
708 err = snd_soc_jack_new(codec, "Button Jack",
709 TABLA_JACK_BUTTON_MASK, &button_jack);
710 if (err) {
711 pr_err("failed to create new jack\n");
712 return err;
713 }
714
715 codec_clk = clk_get(cpu_dai->dev, "osr_clk");
716
717 err = tabla_hs_detect(codec, &mbhc_cfg);
718
719 return err;
720}
721
722static struct snd_soc_dsp_link lpa_fe_media = {
723 .playback = true,
724 .trigger = {
725 SND_SOC_DSP_TRIGGER_POST,
726 SND_SOC_DSP_TRIGGER_POST
727 },
728};
729
730static struct snd_soc_dsp_link fe_media = {
731 .playback = true,
732 .capture = true,
733 .trigger = {
734 SND_SOC_DSP_TRIGGER_POST,
735 SND_SOC_DSP_TRIGGER_POST
736 },
737};
738
739static struct snd_soc_dsp_link slimbus0_hl_media = {
740 .playback = true,
741 .capture = true,
742 .trigger = {
743 SND_SOC_DSP_TRIGGER_POST,
744 SND_SOC_DSP_TRIGGER_POST
745 },
746};
747
748static struct snd_soc_dsp_link int_fm_hl_media = {
749 .playback = true,
750 .capture = true,
751 .trigger = {
752 SND_SOC_DSP_TRIGGER_POST,
753 SND_SOC_DSP_TRIGGER_POST
754 },
755};
756
757static int msm_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
758 struct snd_pcm_hw_params *params)
759{
760 struct snd_interval *rate = hw_param_interval(params,
761 SNDRV_PCM_HW_PARAM_RATE);
762
763 struct snd_interval *channels = hw_param_interval(params,
764 SNDRV_PCM_HW_PARAM_CHANNELS);
765
766 pr_debug("%s()\n", __func__);
767 rate->min = rate->max = 48000;
768 channels->min = channels->max = msm_slim_0_rx_ch;
769
770 return 0;
771}
772
773static int msm_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
774 struct snd_pcm_hw_params *params)
775{
776 struct snd_interval *rate = hw_param_interval(params,
777 SNDRV_PCM_HW_PARAM_RATE);
778
779 struct snd_interval *channels = hw_param_interval(params,
780 SNDRV_PCM_HW_PARAM_CHANNELS);
781
782 pr_debug("%s()\n", __func__);
783 rate->min = rate->max = 48000;
784 channels->min = channels->max = msm_slim_0_tx_ch;
785
786 return 0;
787}
788
789static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
790 struct snd_pcm_hw_params *params)
791{
792 struct snd_interval *rate = hw_param_interval(params,
793 SNDRV_PCM_HW_PARAM_RATE);
794
795 pr_debug("%s()\n", __func__);
796 rate->min = rate->max = 48000;
797
798 return 0;
799}
800
801static int msm_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
802 struct snd_pcm_hw_params *params)
803{
804 struct snd_interval *rate = hw_param_interval(params,
805 SNDRV_PCM_HW_PARAM_RATE);
806
807 struct snd_interval *channels = hw_param_interval(params,
808 SNDRV_PCM_HW_PARAM_CHANNELS);
809
810 pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
811 channels->min, channels->max);
812
813 rate->min = rate->max = 48000;
814
815 return 0;
816}
817
818static int msm_startup(struct snd_pcm_substream *substream)
819{
820 pr_debug("%s(): substream = %s stream = %d\n", __func__,
821 substream->name, substream->stream);
822 return 0;
823}
824
825static void msm_shutdown(struct snd_pcm_substream *substream)
826{
827 pr_debug("%s(): substream = %s stream = %d\n", __func__,
828 substream->name, substream->stream);
829}
830
831static struct snd_soc_ops msm_be_ops = {
832 .startup = msm_startup,
833 .hw_params = msm_hw_params,
834 .shutdown = msm_shutdown,
835};
836
837static int mpq8064_sec_i2s_rx_free_gpios(void)
838{
839 int i;
840 for (i = 0; i < ARRAY_SIZE(sec_i2s_rx_gpio); i++)
841 gpio_free(sec_i2s_rx_gpio[i].gpio_no);
842 return 0;
843}
844
845static int mpq8064_sec_i2s_rx_hw_params(struct snd_pcm_substream *substream,
846 struct snd_pcm_hw_params *params)
847{
848
849 int rate = params_rate(params);
850 int bit_clk_set = 0;
851 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
852 switch (params_format(params)) {
853 case SNDRV_PCM_FORMAT_S16_LE:
854 bit_clk_set = I2S_MCLK_RATE/(rate * 2 * 16);
855 clk_set_rate(sec_i2s_rx_bit_clk, bit_clk_set);
856 break;
857 case SNDRV_PCM_FORMAT_S24_LE:
858 bit_clk_set = I2S_MCLK_RATE/(rate * 2 * 24);
859 clk_set_rate(sec_i2s_rx_bit_clk, bit_clk_set);
860 break;
861 default:
862 pr_err("wrong format\n");
863 break;
864 }
865 }
866 return 0;
867}
868
869static void mpq8064_sec_i2s_rx_shutdown(struct snd_pcm_substream *substream)
870{
871 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
872 if (sec_i2s_rx_bit_clk) {
873 clk_disable(sec_i2s_rx_bit_clk);
874 clk_put(sec_i2s_rx_bit_clk);
875 sec_i2s_rx_bit_clk = NULL;
876 }
877 if (sec_i2s_rx_osr_clk) {
878 clk_disable(sec_i2s_rx_osr_clk);
879 clk_put(sec_i2s_rx_osr_clk);
880 sec_i2s_rx_osr_clk = NULL;
881 }
882 mpq8064_sec_i2s_rx_free_gpios();
883 }
884 pr_info("%s(): substream = %s stream = %d\n", __func__,
885 substream->name, substream->stream);
886}
887
888static int configure_sec_i2s_rx_gpio(void)
889{
890 int rtn;
891 int i;
892 int j;
893 for (i = 0; i < ARRAY_SIZE(sec_i2s_rx_gpio); i++) {
894 rtn = gpio_request(sec_i2s_rx_gpio[i].gpio_no,
895 sec_i2s_rx_gpio[i].gpio_name);
896 pr_debug("%s: gpio = %d, gpio name = %s, rtn = %d\n",
897 __func__,
898 sec_i2s_rx_gpio[i].gpio_no,
899 sec_i2s_rx_gpio[i].gpio_name,
900 rtn);
901 if (rtn) {
902 pr_err("%s: Failed to request gpio %d\n",
903 __func__,
904 sec_i2s_rx_gpio[i].gpio_no);
905 for (j = i; j >= 0; j--)
906 gpio_free(sec_i2s_rx_gpio[j].gpio_no);
907
908 goto err;
909 }
910 }
911err:
912 return rtn;
913}
914
915static int mpq8064_sec_i2s_rx_startup(struct snd_pcm_substream *substream)
916{
917 int ret = 0;
918 struct snd_soc_pcm_runtime *rtd = substream->private_data;
919 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
920 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
921 configure_sec_i2s_rx_gpio();
922 sec_i2s_rx_osr_clk = clk_get(cpu_dai->dev, "osr_clk");
923 if (IS_ERR(sec_i2s_rx_osr_clk)) {
924 pr_err("Failed to get sec_i2s_rx_osr_clk\n");
925 return PTR_ERR(sec_i2s_rx_osr_clk);
926 }
927 clk_set_rate(sec_i2s_rx_osr_clk, I2S_MCLK_RATE);
928 clk_enable(sec_i2s_rx_osr_clk);
929 sec_i2s_rx_bit_clk = clk_get(cpu_dai->dev, "bit_clk");
930 if (IS_ERR(sec_i2s_rx_bit_clk)) {
931 pr_err("Failed to get sec i2s osr_clk\n");
932 clk_disable(sec_i2s_rx_osr_clk);
933 clk_put(sec_i2s_rx_osr_clk);
934 return PTR_ERR(sec_i2s_rx_bit_clk);
935 }
936 clk_set_rate(sec_i2s_rx_bit_clk, 1);
937 ret = clk_enable(sec_i2s_rx_bit_clk);
938 if (ret != 0) {
939 pr_err("Unable to enable sec i2s rx_bit_clk\n");
940 clk_put(sec_i2s_rx_bit_clk);
941 clk_disable(sec_i2s_rx_osr_clk);
942 clk_put(sec_i2s_rx_osr_clk);
943 return ret;
944 }
945 ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
946 if (ret < 0)
947 pr_err("set format for codec dai failed\n");
948 }
949 pr_debug("%s: ret = %d\n", __func__, ret);
950 pr_info("%s(): substream = %s stream = %d\n", __func__,
951 substream->name, substream->stream);
952 return ret;
953}
954
955static struct snd_soc_ops mpq8064_sec_i2s_rx_be_ops = {
956 .startup = mpq8064_sec_i2s_rx_startup,
957 .shutdown = mpq8064_sec_i2s_rx_shutdown,
958 .hw_params = mpq8064_sec_i2s_rx_hw_params,
959};
960
961/* Digital audio interface glue - connects codec <---> CPU */
962static struct snd_soc_dai_link msm_dai[] = {
963 /* FrontEnd DAI Links */
964 {
965 .name = "MSM8960 Media1",
966 .stream_name = "MultiMedia1",
967 .cpu_dai_name = "MultiMedia1",
968 .platform_name = "msm-pcm-dsp",
969 .dynamic = 1,
970 .dsp_link = &fe_media,
971 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
972 },
973 {
974 .name = "MSM8960 Media2",
975 .stream_name = "MultiMedia2",
976 .cpu_dai_name = "MultiMedia2",
977 .platform_name = "msm-pcm-dsp",
978 .dynamic = 1,
979 .dsp_link = &fe_media,
980 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
981 },
982 {
983 .name = "Circuit-Switch Voice",
984 .stream_name = "CS-Voice",
985 .cpu_dai_name = "CS-VOICE",
986 .platform_name = "msm-pcm-voice",
987 .dynamic = 1,
988 .dsp_link = &fe_media,
989 .be_id = MSM_FRONTEND_DAI_CS_VOICE,
990 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
991 .ignore_suspend = 1,
992 },
993 {
994 .name = "MSM VoIP",
995 .stream_name = "VoIP",
996 .cpu_dai_name = "VoIP",
997 .platform_name = "msm-voip-dsp",
998 .dynamic = 1,
999 .dsp_link = &fe_media,
1000 .be_id = MSM_FRONTEND_DAI_VOIP,
1001 },
1002 {
1003 .name = "MSM8960 LPA",
1004 .stream_name = "LPA",
1005 .cpu_dai_name = "MultiMedia3",
1006 .platform_name = "msm-pcm-lpa",
1007 .dynamic = 1,
1008 .dsp_link = &lpa_fe_media,
1009 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
1010 },
1011 /* Hostless PMC purpose */
1012 {
1013 .name = "SLIMBUS_0 Hostless",
1014 .stream_name = "SLIMBUS_0 Hostless",
1015 .cpu_dai_name = "SLIMBUS0_HOSTLESS",
1016 .platform_name = "msm-pcm-hostless",
1017 .dynamic = 1,
1018 .dsp_link = &slimbus0_hl_media,
1019 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
1020 .ignore_suspend = 1,
1021 /* .be_id = do not care */
1022 },
1023 {
1024 .name = "INT_FM Hostless",
1025 .stream_name = "INT_FM Hostless",
1026 .cpu_dai_name = "INT_FM_HOSTLESS",
1027 .platform_name = "msm-pcm-hostless",
1028 .dynamic = 1,
1029 .dsp_link = &int_fm_hl_media,
1030 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
1031 .ignore_suspend = 1,
1032 /* .be_id = do not care */
1033 },
1034 {
1035 .name = "MSM AFE-PCM RX",
1036 .stream_name = "AFE-PROXY RX",
1037 .cpu_dai_name = "msm-dai-q6.241",
1038 .codec_name = "msm-stub-codec.1",
1039 .codec_dai_name = "msm-stub-rx",
1040 .platform_name = "msm-pcm-afe",
1041 .ignore_suspend = 1,
1042 },
1043 {
1044 .name = "MSM AFE-PCM TX",
1045 .stream_name = "AFE-PROXY TX",
1046 .cpu_dai_name = "msm-dai-q6.240",
1047 .codec_name = "msm-stub-codec.1",
1048 .codec_dai_name = "msm-stub-tx",
1049 .platform_name = "msm-pcm-afe",
1050 .ignore_suspend = 1,
1051 },
1052 {
1053 .name = "MSM8960 Compr",
1054 .stream_name = "COMPR",
1055 .cpu_dai_name = "MultiMedia4",
1056 .platform_name = "msm-compr-dsp",
1057 .dynamic = 1,
1058 .dsp_link = &lpa_fe_media,
1059 .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
1060 },
1061 {
1062 .name = "Voice Stub",
1063 .stream_name = "Voice Stub",
1064 .cpu_dai_name = "VOICE_STUB",
1065 .platform_name = "msm-pcm-hostless",
1066 .dynamic = 1,
1067 .dsp_link = &fe_media,
1068 .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
1069 .ignore_suspend = 1,
1070 /* .be_id = do not care */
1071 },
1072 /* Backend DAI Links */
1073 {
1074 .name = LPASS_BE_SLIMBUS_0_RX,
1075 .stream_name = "Slimbus Playback",
1076 .cpu_dai_name = "msm-dai-q6.16384",
1077 .platform_name = "msm-pcm-routing",
1078 .codec_name = "tabla_codec",
1079 .codec_dai_name = "tabla_rx1",
1080 .no_pcm = 1,
1081 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
1082 .init = &msm_audrx_init,
1083 .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
1084 .ops = &msm_be_ops,
1085 },
1086 {
1087 .name = LPASS_BE_SLIMBUS_0_TX,
1088 .stream_name = "Slimbus Capture",
1089 .cpu_dai_name = "msm-dai-q6.16385",
1090 .platform_name = "msm-pcm-routing",
1091 .codec_name = "tabla_codec",
1092 .codec_dai_name = "tabla_tx1",
1093 .no_pcm = 1,
1094 .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
1095 .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
1096 .ops = &msm_be_ops,
1097 },
1098 {
1099 .name = LPASS_BE_SEC_I2S_RX,
1100 .stream_name = "Secondary I2S Playback",
1101 .cpu_dai_name = "msm-dai-q6.4",
1102 .platform_name = "msm-pcm-routing",
1103 .codec_name = "cs8427-spdif.5-0014",
1104 .codec_dai_name = "spdif_rx",
1105 .no_pcm = 1,
1106 .be_id = MSM_BACKEND_DAI_SEC_I2S_RX,
1107 .be_hw_params_fixup = msm_be_hw_params_fixup,
1108 .ops = &mpq8064_sec_i2s_rx_be_ops,
1109 },
1110 {
1111 .name = LPASS_BE_INT_FM_RX,
1112 .stream_name = "Internal FM Playback",
1113 .cpu_dai_name = "msm-dai-q6.12292",
1114 .platform_name = "msm-pcm-routing",
1115 .codec_name = "msm-stub-codec.1",
1116 .codec_dai_name = "msm-stub-rx",
1117 .no_pcm = 1,
1118 .be_id = MSM_BACKEND_DAI_INT_FM_RX,
1119 .be_hw_params_fixup = msm_be_hw_params_fixup,
1120 },
1121 {
1122 .name = LPASS_BE_INT_FM_TX,
1123 .stream_name = "Internal FM Capture",
1124 .cpu_dai_name = "msm-dai-q6.12293",
1125 .platform_name = "msm-pcm-routing",
1126 .codec_name = "msm-stub-codec.1",
1127 .codec_dai_name = "msm-stub-tx",
1128 .no_pcm = 1,
1129 .be_id = MSM_BACKEND_DAI_INT_FM_TX,
1130 .be_hw_params_fixup = msm_be_hw_params_fixup,
1131 },
1132 /* HDMI BACK END DAI Link */
1133 {
1134 .name = LPASS_BE_HDMI,
1135 .stream_name = "HDMI Playback",
1136 .cpu_dai_name = "msm-dai-q6-hdmi.8",
1137 .platform_name = "msm-pcm-routing",
1138 .codec_name = "msm-stub-codec.1",
1139 .codec_dai_name = "msm-stub-rx",
1140 .no_pcm = 1,
1141 .no_codec = 1,
1142 .be_id = MSM_BACKEND_DAI_HDMI_RX,
1143 .be_hw_params_fixup = msm_hdmi_be_hw_params_fixup,
1144 },
1145 /* Backend AFE DAI Links */
1146 {
1147 .name = LPASS_BE_AFE_PCM_RX,
1148 .stream_name = "AFE Playback",
1149 .cpu_dai_name = "msm-dai-q6.224",
1150 .platform_name = "msm-pcm-routing",
1151 .codec_name = "msm-stub-codec.1",
1152 .codec_dai_name = "msm-stub-rx",
1153 .no_codec = 1,
1154 .no_pcm = 1,
1155 .be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
1156 },
1157 {
1158 .name = LPASS_BE_AFE_PCM_TX,
1159 .stream_name = "AFE Capture",
1160 .cpu_dai_name = "msm-dai-q6.225",
1161 .platform_name = "msm-pcm-routing",
1162 .codec_name = "msm-stub-codec.1",
1163 .codec_dai_name = "msm-stub-tx",
1164 .no_codec = 1,
1165 .no_pcm = 1,
1166 .be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
1167 },
1168};
1169
1170
1171static struct snd_soc_card snd_soc_card_msm = {
1172 .name = "mpq8064-tabla-snd-card",
1173 .dai_link = msm_dai,
1174 .num_links = ARRAY_SIZE(msm_dai),
1175};
1176
1177static struct platform_device *msm_snd_device;
1178
1179static int msm_configure_headset_mic_gpios(void)
1180{
1181 int ret;
1182 struct pm_gpio param = {
1183 .direction = PM_GPIO_DIR_OUT,
1184 .output_buffer = PM_GPIO_OUT_BUF_CMOS,
1185 .output_value = 1,
1186 .pull = PM_GPIO_PULL_NO,
1187 .vin_sel = PM_GPIO_VIN_S4,
1188 .out_strength = PM_GPIO_STRENGTH_MED,
1189 .function = PM_GPIO_FUNC_NORMAL,
1190 };
1191
1192 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
1193 if (ret) {
1194 pr_err("%s: Failed to request gpio %d\n", __func__,
1195 PM8921_GPIO_PM_TO_SYS(23));
1196 return ret;
1197 }
1198
1199 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), &param);
1200 if (ret)
1201 pr_err("%s: Failed to configure gpio %d\n", __func__,
1202 PM8921_GPIO_PM_TO_SYS(23));
1203 else
1204 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
1205
1206 ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
1207 if (ret) {
1208 pr_err("%s: Failed to request gpio %d\n", __func__,
1209 PM8921_GPIO_PM_TO_SYS(35));
1210 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
1211 return ret;
1212 }
1213 ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), &param);
1214 if (ret)
1215 pr_err("%s: Failed to configure gpio %d\n", __func__,
1216 PM8921_GPIO_PM_TO_SYS(35));
1217 else
1218 gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 0);
1219
1220 return 0;
1221}
1222static void msm_free_headset_mic_gpios(void)
1223{
1224 if (msm_headset_gpios_configured) {
1225 gpio_free(PM8921_GPIO_PM_TO_SYS(23));
1226 gpio_free(PM8921_GPIO_PM_TO_SYS(35));
1227 }
1228}
1229
1230static int __init msm_audio_init(void)
1231{
1232 int ret;
1233
1234 if (socinfo_get_id() != 130) {
1235 pr_err("%s: Not the right machine type\n", __func__);
1236 return -ENODEV;
1237 }
1238
1239 mbhc_cfg.calibration = def_tabla_mbhc_cal();
1240 if (!mbhc_cfg.calibration) {
1241 pr_err("Calibration data allocation failed\n");
1242 return -ENOMEM;
1243 }
1244
1245 msm_snd_device = platform_device_alloc("soc-audio", 0);
1246 if (!msm_snd_device) {
1247 pr_err("Platform device allocation failed\n");
1248 kfree(mbhc_cfg.calibration);
1249 return -ENOMEM;
1250 }
1251
1252 platform_set_drvdata(msm_snd_device, &snd_soc_card_msm);
1253 ret = platform_device_add(msm_snd_device);
1254 if (ret) {
1255 platform_device_put(msm_snd_device);
1256 kfree(mbhc_cfg.calibration);
1257 return ret;
1258 }
1259
1260 if (msm_configure_headset_mic_gpios()) {
1261 pr_err("%s Fail to configure headset mic gpios\n", __func__);
1262 msm_headset_gpios_configured = 0;
1263 } else
1264 msm_headset_gpios_configured = 1;
1265
1266 return ret;
1267
1268}
1269module_init(msm_audio_init);
1270
1271static void __exit msm_audio_exit(void)
1272{
1273 if (socinfo_get_id() != 130) {
1274 pr_err("%s: Not the right machine type\n", __func__);
1275 return ;
1276 }
1277 msm_free_headset_mic_gpios();
1278 platform_device_unregister(msm_snd_device);
1279 kfree(mbhc_cfg.calibration);
1280}
1281module_exit(msm_audio_exit);
1282
1283MODULE_DESCRIPTION("ALSA SoC mpq8064");
1284MODULE_LICENSE("GPL v2");