blob: 097bd1cb4cdc1fbfde6d58fa0da6856c2fc30e79 [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
14#include <linux/init.h>
15#include <linux/err.h>
16#include <linux/module.h>
17#include <linux/moduleparam.h>
18#include <linux/platform_device.h>
19#include <linux/bitops.h>
20#include <sound/core.h>
21#include <sound/soc.h>
22#include <sound/soc-dapm.h>
23#include <sound/pcm.h>
24#include <sound/initval.h>
25#include <sound/control.h>
26#include <sound/q6adm.h>
27#include <sound/q6afe.h>
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -070028#include <sound/tlv.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029#include "msm-pcm-routing.h"
30#include "qdsp6/q6voice.h"
31
32struct audio_mixer_data {
33 u32 port_id; /* AFE port ID for Rx, FE DAI ID for TX */
34 unsigned long dai_sessions; /* Rx: FE DAIs Tx: BE DAI */
35 u32 mixer_type; /* playback or capture */
36};
37
38#define INVALID_SESSION -1
39
40enum {
41 AUDIO_MIXER_PRI_I2S_RX = 0,
42 AUDIO_MIXER_SLIMBUS_0_RX,
43 AUDIO_MIXER_HDMI_RX,
44 AUDIO_MIXER_MM_UL1,
45 AUDIO_MIXER_INT_BT_SCO_RX,
46 AUDIO_MIXER_INT_FM_RX,
47 AUDIO_MIXER_MAX,
48};
49
50enum {
51 AUDIO_PORT_MIXER_SLIM_0_RX = 0,
52 AUDIO_PORT_MIXER_MAX,
53};
54
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -070055static int fm_switch_enable;
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -070056#define INT_FM_RX_VOL_MAX_STEPS 100
57#define INT_FM_RX_VOL_GAIN 2000
58
59static int msm_route_fm_vol_control;
60static const DECLARE_TLV_DB_SCALE(fm_rx_vol_gain, 0,
61 INT_FM_RX_VOL_MAX_STEPS, 0);
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -070062
Asish Bhattacharya0ec76182011-07-29 16:58:11 +053063#define INT_LPA_RX_VOL_MAX_STEPS 0x100
64#define INT_LPA_RX_VOL_GAIN 0x3FFF
65
66static int msm_route_lpa_vol_control;
67static const DECLARE_TLV_DB_SCALE(lpa_rx_vol_gain, 0,
68 INT_LPA_RX_VOL_MAX_STEPS, 0);
69
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070/* Tx mixer session is stored based on BE DAI ID
71 * Need to map to actual AFE port ID since AFE port
72 * ID can get really large.
73 * The table convert DAI back to AFE port ID
74 */
75static int bedai_port_map[MSM_BACKEND_DAI_MAX] = {
76 PRIMARY_I2S_RX,
77 PRIMARY_I2S_TX,
78 SLIMBUS_0_RX,
79 SLIMBUS_0_TX,
80 HDMI_RX,
81 INT_BT_SCO_RX,
82 INT_BT_SCO_TX,
83 INT_FM_RX,
84 INT_FM_TX,
85};
86
87/* Track ASM playback & capture sessions of DAI */
88static int fe_dai_map[MSM_FRONTEND_DAI_MAX][2] = {
89 /* MULTIMEDIA1 */
90 {INVALID_SESSION, INVALID_SESSION},
91 /* MULTIMEDIA2 */
92 {INVALID_SESSION, INVALID_SESSION},
93 /* MULTIMEDIA3 */
94 {INVALID_SESSION, INVALID_SESSION},
95};
96
97static struct audio_mixer_data audio_mixers[AUDIO_MIXER_MAX] = {
98 /* AUDIO_MIXER_PRI_I2S_RX */
99 {PRIMARY_I2S_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
100 /* AUDIO_MIXER_SLIMBUS_0_RX */
101 {SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
102 /* AUDIO_MIXER_HDMI_RX */
103 {HDMI_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
104 /* AUDIO_MIXER_MM_UL1 */
105 {MSM_FRONTEND_DAI_MULTIMEDIA1, 0, SNDRV_PCM_STREAM_CAPTURE},
106 /* AUDIO_MIXER_INT_BT_SCO_RX */
107 {INT_BT_SCO_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
108 /* AUDIO_MIXER_INT_FM_RX */
109 {INT_FM_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
110};
111static struct voice_mixer_data voice_mixers[VOICE_MIXER_MAX] = {
112 /* VOICE_MIXER_PRI_I2S_RX */
113 {VOICE_PRI_I2S_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
114 /* VOICE_MIXER_SLIMBUS_0_RX */
115 {VOICE_SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
116 /* VOICE_MIXER_PRI_I2S_TX */
117 {VOICE_PRI_I2S_TX, 0, SNDRV_PCM_STREAM_CAPTURE},
118 /* VOICE_MIXER_SLIMBUS_0_TX */
119 {VOICE_SLIMBUS_0_TX, 0, SNDRV_PCM_STREAM_CAPTURE},
120 /* VOICE_MIXER_INT_BT_SCO_RX */
121 {VOICE_INT_BT_SCO_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
122 /* VOICE_MIXER_INT_BT_SCO_TX */
123 {VOICE_INT_BT_SCO_TX, 0, SNDRV_PCM_STREAM_CAPTURE}
124};
125
126/* Reuse audio_mixer_data struct but ignore mixer type field
127 * unless there is use case for RX -> TX
128 */
129static struct audio_mixer_data audio_port_mixers[AUDIO_PORT_MIXER_MAX] = {
130 /* AUDIO_PORT_MIXER_SLIM_0_RX */
131 {SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
132};
133
134void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id, int stream_type)
135{
136 int i, be_id;
137
138 if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
139 fe_dai_map[fedai_id][0] = dspst_id;
140 for (i = 0; i < AUDIO_MIXER_MAX; i++) {
141 if ((audio_mixers[i].mixer_type == stream_type) &&
142 (test_bit(fedai_id, &audio_mixers[i].dai_sessions)))
143 /* To do: multiple devices case */
144 adm_route_session(audio_mixers[i].port_id,
145 dspst_id, 1);
146 }
147 } else {
148 fe_dai_map[fedai_id][1] = dspst_id;
149 for (i = 0; i < AUDIO_MIXER_MAX; i++) {
150 if ((audio_mixers[i].mixer_type == stream_type) &&
151 (fedai_id == audio_mixers[i].port_id)) {
152 /* To-do: Handle mixing of inputs */
153 be_id = find_next_bit(
154 &audio_mixers[i].dai_sessions,
155 MSM_BACKEND_DAI_MAX, 0);
156 if (be_id < MSM_BACKEND_DAI_MAX)
157 adm_route_session(bedai_port_map[be_id],
158 dspst_id, 1);
159 else
160 pr_err("%s: no routing\n", __func__);
161 }
162 }
163 }
164}
165
166void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type)
167{
168 int i, be_id;
169
170 if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
171 for (i = 0; i < AUDIO_MIXER_MAX; i++) {
172 if ((audio_mixers[i].mixer_type == stream_type) &&
173 (test_bit(fedai_id, &audio_mixers[i].dai_sessions))) {
174 /* To do: multiple devices case */
175 adm_route_session(audio_mixers[i].port_id,
176 fe_dai_map[fedai_id][0], 0);
177 }
178 }
179 fe_dai_map[fedai_id][0] = INVALID_SESSION;
180 } else {
181 for (i = 0; i < AUDIO_MIXER_MAX; i++) {
182 if ((audio_mixers[i].mixer_type == stream_type) &&
183 (fedai_id == audio_mixers[i].port_id)) {
184 /* To-do: Handle mixing of inputs */
185 be_id = find_next_bit(
186 &audio_mixers[i].dai_sessions,
187 MSM_BACKEND_DAI_MAX, 0);
188 if (be_id < MSM_BACKEND_DAI_MAX)
189 adm_route_session(bedai_port_map[be_id],
190 fe_dai_map[fedai_id][1], 0);
191 else
192 pr_err("%s: no routing\n", __func__);
193 }
194 }
195 fe_dai_map[fedai_id][1] = INVALID_SESSION;
196 }
197}
198
199static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
200{
201
202 pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
203
204 if (set)
205 set_bit(val, &audio_mixers[reg].dai_sessions);
206 else
207 clear_bit(val, &audio_mixers[reg].dai_sessions);
208
209 if (audio_mixers[reg].mixer_type == SNDRV_PCM_STREAM_PLAYBACK) {
210 if (fe_dai_map[val][0] != INVALID_SESSION)
211 adm_route_session(audio_mixers[reg].port_id,
212 fe_dai_map[val][0], set);
213 } else {
214 int fe_id = audio_mixers[reg].port_id;
215 if (fe_dai_map[fe_id][1] != INVALID_SESSION)
216 adm_route_session(bedai_port_map[val],
217 fe_dai_map[fe_id][1], set);
218 }
219}
220
221static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
222 struct snd_ctl_elem_value *ucontrol)
223{
224 struct soc_mixer_control *mc =
225 (struct soc_mixer_control *)kcontrol->private_value;
226
227 if (test_bit(mc->shift, &audio_mixers[mc->reg].dai_sessions))
228 ucontrol->value.integer.value[0] = 1;
229 else
230 ucontrol->value.integer.value[0] = 0;
231
232 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
233 ucontrol->value.integer.value[0]);
234
235 return 0;
236}
237
238static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
239 struct snd_ctl_elem_value *ucontrol)
240{
Patrick Laiec2b8942011-09-01 11:01:51 -0700241 struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
242 struct snd_soc_dapm_widget *widget = wlist->widgets[0];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700243 struct soc_mixer_control *mc =
244 (struct soc_mixer_control *)kcontrol->private_value;
245
246
247 if (ucontrol->value.integer.value[0]) {
248 msm_pcm_routing_process_audio(mc->reg, mc->shift, 1);
249 snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
250 } else {
251 msm_pcm_routing_process_audio(mc->reg, mc->shift, 0);
252 snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
253 }
254
255 return 1;
256}
257
258static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set)
259{
260
261 u32 port_map_id;
262
263 pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
264
265 port_map_id = voice_mixers[reg].port_id;
266
267 if (set)
268 set_bit(val, &voice_mixers[reg].dai_sessions);
269 else
270 clear_bit(val, &voice_mixers[reg].dai_sessions);
271
272 if (voice_mixers[reg].mixer_type == SNDRV_PCM_STREAM_PLAYBACK) {
273 voc_set_route_flag(RX_PATH, set);
274 if (set) {
275 voc_set_rxtx_port(bedai_port_map[port_map_id], DEV_RX);
276
277 if (voc_get_route_flag(RX_PATH) &&
278 voc_get_route_flag(TX_PATH))
279 voc_enable_cvp();
280 } else {
281 voc_disable_cvp();
282 }
283 } else {
284 voc_set_route_flag(TX_PATH, set);
285 if (set) {
286 voc_set_rxtx_port(bedai_port_map[port_map_id], DEV_TX);
287
288 if (voc_get_route_flag(RX_PATH) &&
289 voc_get_route_flag(TX_PATH))
290 voc_enable_cvp();
291 } else {
292 voc_disable_cvp();
293 }
294 }
295}
296
297static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol,
298 struct snd_ctl_elem_value *ucontrol)
299{
300 struct soc_mixer_control *mc =
301 (struct soc_mixer_control *)kcontrol->private_value;
302
303 if (test_bit(mc->shift, &voice_mixers[mc->reg].dai_sessions))
304 ucontrol->value.integer.value[0] = 1;
305 else
306 ucontrol->value.integer.value[0] = 0;
307
308 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
309 ucontrol->value.integer.value[0]);
310
311 return 0;
312}
313
314static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol,
315 struct snd_ctl_elem_value *ucontrol)
316{
Patrick Laiec2b8942011-09-01 11:01:51 -0700317 struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
318 struct snd_soc_dapm_widget *widget = wlist->widgets[0];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319 struct soc_mixer_control *mc =
320 (struct soc_mixer_control *)kcontrol->private_value;
321
322 if (ucontrol->value.integer.value[0]) {
323 msm_pcm_routing_process_voice(mc->reg, mc->shift, 1);
324 snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
325 } else {
326 msm_pcm_routing_process_voice(mc->reg, mc->shift, 0);
327 snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
328 }
329
330 return 1;
331}
332
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700333static int msm_routing_get_switch_mixer(struct snd_kcontrol *kcontrol,
334 struct snd_ctl_elem_value *ucontrol)
335{
336 ucontrol->value.integer.value[0] = fm_switch_enable;
337 pr_debug("%s: FM Switch enable %ld\n", __func__,
338 ucontrol->value.integer.value[0]);
339 return 0;
340}
341
342static int msm_routing_put_switch_mixer(struct snd_kcontrol *kcontrol,
343 struct snd_ctl_elem_value *ucontrol)
344{
345 struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
346
347 pr_debug("%s: FM Switch enable %ld\n", __func__,
348 ucontrol->value.integer.value[0]);
349 if (ucontrol->value.integer.value[0])
350 snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
351 else
352 snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
353 fm_switch_enable = ucontrol->value.integer.value[0];
354 return 1;
355}
356
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol,
358 struct snd_ctl_elem_value *ucontrol)
359{
360 struct soc_mixer_control *mc =
361 (struct soc_mixer_control *)kcontrol->private_value;
362
363 if (test_bit(mc->shift, &audio_port_mixers[mc->reg].dai_sessions))
364 ucontrol->value.integer.value[0] = 1;
365 else
366 ucontrol->value.integer.value[0] = 0;
367
368 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
369 ucontrol->value.integer.value[0]);
370
371 return 0;
372}
373
374static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol,
375 struct snd_ctl_elem_value *ucontrol)
376{
377 struct soc_mixer_control *mc =
378 (struct soc_mixer_control *)kcontrol->private_value;
379
380 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg,
381 mc->shift, ucontrol->value.integer.value[0]);
382
383 if (ucontrol->value.integer.value[0]) {
384 afe_loopback(1, audio_port_mixers[mc->reg].port_id,
385 bedai_port_map[mc->shift]);
386 set_bit(mc->shift,
387 &audio_port_mixers[mc->reg].dai_sessions);
388 } else {
389 afe_loopback(0, audio_port_mixers[mc->reg].port_id,
390 bedai_port_map[mc->shift]);
391 clear_bit(mc->shift,
392 &audio_port_mixers[mc->reg].dai_sessions);
393 }
394
395 return 1;
396}
397
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -0700398static int msm_routing_get_fm_vol_mixer(struct snd_kcontrol *kcontrol,
399 struct snd_ctl_elem_value *ucontrol)
400{
401 ucontrol->value.integer.value[0] = msm_route_fm_vol_control;
402 return 0;
403}
404
405static int msm_routing_set_fm_vol_mixer(struct snd_kcontrol *kcontrol,
406 struct snd_ctl_elem_value *ucontrol)
407{
408 afe_loopback_gain(INT_FM_TX , ucontrol->value.integer.value[0]);
409
410 msm_route_fm_vol_control = ucontrol->value.integer.value[0];
411
412 return 0;
413}
414
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530415static int msm_routing_get_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
416 struct snd_ctl_elem_value *ucontrol)
417{
418 ucontrol->value.integer.value[0] = msm_route_lpa_vol_control;
419 return 0;
420}
421
422static int msm_routing_set_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
423 struct snd_ctl_elem_value *ucontrol)
424{
425 if (!lpa_set_volume(ucontrol->value.integer.value[0]))
426 msm_route_lpa_vol_control =
427 ucontrol->value.integer.value[0];
428
429 return 0;
430}
431
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700432static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
433 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_PRI_I2S_RX ,
434 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
435 msm_routing_put_audio_mixer),
436 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_PRI_I2S_RX,
437 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
438 msm_routing_put_audio_mixer),
439 SOC_SINGLE_EXT("MultiMedia3", AUDIO_MIXER_PRI_I2S_RX,
440 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
441 msm_routing_put_audio_mixer),
442};
443
444static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
445 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_SLIMBUS_0_RX ,
446 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
447 msm_routing_put_audio_mixer),
448 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_SLIMBUS_0_RX,
449 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
450 msm_routing_put_audio_mixer),
451 SOC_SINGLE_EXT("MultiMedia3", AUDIO_MIXER_SLIMBUS_0_RX,
452 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
453 msm_routing_put_audio_mixer),
454};
455
456static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
457 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_HDMI_RX,
458 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
459 msm_routing_put_audio_mixer),
460 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_HDMI_RX,
461 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
462 msm_routing_put_audio_mixer),
463};
464
465static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
466 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_INT_BT_SCO_RX,
467 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
468 msm_routing_put_audio_mixer),
469 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_INT_BT_SCO_RX,
470 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
471 msm_routing_put_audio_mixer),
472};
473
474static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
475 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_INT_FM_RX,
476 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
477 msm_routing_put_audio_mixer),
478 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_INT_FM_RX,
479 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
480 msm_routing_put_audio_mixer),
481};
482
483static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
484 SOC_SINGLE_EXT("PRI_TX", AUDIO_MIXER_MM_UL1,
485 MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_audio_mixer,
486 msm_routing_put_audio_mixer),
487 SOC_SINGLE_EXT("SLIM_0_TX", AUDIO_MIXER_MM_UL1,
488 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_audio_mixer,
489 msm_routing_put_audio_mixer),
490 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", AUDIO_MIXER_MM_UL1,
491 MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_audio_mixer,
492 msm_routing_put_audio_mixer),
493 SOC_SINGLE_EXT("INTERNAL_FM_TX", AUDIO_MIXER_MM_UL1,
494 MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_audio_mixer,
495 msm_routing_put_audio_mixer),
496};
497
498static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
499 SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_PRI_I2S_RX,
500 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
501 msm_routing_put_voice_mixer),
502 SOC_SINGLE_EXT("Voip", VOICE_MIXER_PRI_I2S_RX ,
503 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
504 msm_routing_put_voice_mixer),
505};
506
507static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
508 SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_SLIMBUS_0_RX,
509 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
510 msm_routing_put_voice_mixer),
511 SOC_SINGLE_EXT("Voip", VOICE_MIXER_SLIMBUS_0_RX ,
512 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
513 msm_routing_put_voice_mixer),
514};
515
516static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
517 SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_INT_BT_SCO_RX,
518 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
519 msm_routing_put_voice_mixer),
520 SOC_SINGLE_EXT("Voip", VOICE_MIXER_INT_BT_SCO_RX ,
521 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
522 msm_routing_put_voice_mixer),
523};
524
525static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
526 SOC_SINGLE_EXT("PRI_TX_Voice", VOICE_MIXER_PRI_I2S_TX,
527 MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_voice_mixer,
528 msm_routing_put_voice_mixer),
529 SOC_SINGLE_EXT("SLIM_0_TX_Voice", VOICE_MIXER_SLIMBUS_0_TX,
530 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_voice_mixer,
531 msm_routing_put_voice_mixer),
532 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice", VOICE_MIXER_INT_BT_SCO_TX,
533 MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_voice_mixer,
534 msm_routing_put_voice_mixer),
535};
536
537static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
538 SOC_SINGLE_EXT("PRI_TX_Voip", VOICE_MIXER_PRI_I2S_TX,
539 MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_voice_mixer,
540 msm_routing_put_voice_mixer),
541 SOC_SINGLE_EXT("SLIM_0_TX_Voip", VOICE_MIXER_SLIMBUS_0_TX,
542 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_voice_mixer,
543 msm_routing_put_voice_mixer),
544 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voip", VOICE_MIXER_INT_BT_SCO_TX,
545 MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_voice_mixer,
546 msm_routing_put_voice_mixer),
547};
548
549static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
550 SOC_SINGLE_EXT("INTERNAL_FM_TX", AUDIO_PORT_MIXER_SLIM_0_RX,
551 MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
552 msm_routing_put_port_mixer),
553 SOC_SINGLE_EXT("SLIM_0_TX", AUDIO_PORT_MIXER_SLIM_0_RX,
554 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
555 msm_routing_put_port_mixer),
556};
557
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700558static const struct snd_kcontrol_new fm_switch_mixer_controls =
559 SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
560 0, 1, 0, msm_routing_get_switch_mixer,
561 msm_routing_put_switch_mixer);
562
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -0700563static const struct snd_kcontrol_new int_fm_vol_mixer_controls[] = {
564 SOC_SINGLE_EXT_TLV("Internal FM RX Volume", SND_SOC_NOPM, 0,
565 INT_FM_RX_VOL_GAIN, 0, msm_routing_get_fm_vol_mixer,
566 msm_routing_set_fm_vol_mixer, fm_rx_vol_gain),
567};
568
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530569static const struct snd_kcontrol_new lpa_vol_mixer_controls[] = {
570 SOC_SINGLE_EXT_TLV("LPA RX Volume", SND_SOC_NOPM, 0,
571 INT_LPA_RX_VOL_GAIN, 0, msm_routing_get_lpa_vol_mixer,
572 msm_routing_set_lpa_vol_mixer, lpa_rx_vol_gain),
573};
574
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
576 /* Frontend AIF */
577 /* Widget name equals to Front-End DAI name<Need confirmation>,
578 * Stream name must contains substring of front-end dai name
579 */
580 SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
581 SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
582 SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
583 SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
584 SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
585 SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
586 SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
587 SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
588 SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
589 0, 0, 0, 0),
590 SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture",
591 0, 0, 0, 0),
592 SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback",
593 0, 0, 0, 0),
594 SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture",
595 0, 0, 0, 0),
596 /* Backend AIF */
597 /* Stream name equals to backend dai link stream name
598 */
599 SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0),
600 SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
601 SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
602 SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
603 SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
604 SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
605 0, 0, 0 , 0),
606 SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture",
607 0, 0, 0, 0),
608 SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback",
609 0, 0, 0 , 0),
610 SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture",
611 0, 0, 0, 0),
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700612 /* Switch Definitions */
613 SND_SOC_DAPM_SWITCH("SBUS_0_RX", SND_SOC_NOPM, 0, 0,
614 &fm_switch_mixer_controls),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700615 /* Mixer definitions */
616 SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
617 pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)),
618 SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
619 slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)),
620 SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
621 hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
622 SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
623 mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
624 /* Voice Mixer */
625 SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
626 SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
627 ARRAY_SIZE(pri_rx_voice_mixer_controls)),
628 SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer",
629 SND_SOC_NOPM, 0, 0,
630 slimbus_rx_voice_mixer_controls,
631 ARRAY_SIZE(slimbus_rx_voice_mixer_controls)),
632 SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer",
633 SND_SOC_NOPM, 0, 0,
634 bt_sco_rx_voice_mixer_controls,
635 ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)),
636 SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
637 SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
638 ARRAY_SIZE(tx_voice_mixer_controls)),
639 SND_SOC_DAPM_MIXER("Voip_Tx Mixer",
640 SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls,
641 ARRAY_SIZE(tx_voip_mixer_controls)),
642 SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
643 int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
644 SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
645 int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)),
646 SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer",
647 SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls,
648 ARRAY_SIZE(sbus_0_rx_port_mixer_controls)),
649};
650
651static const struct snd_soc_dapm_route intercon[] = {
652 {"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
653 {"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
654 {"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
655 {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
656
657 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
658 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
659 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
660 {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
661
662 {"HDMI Mixer", "MultiMedia1", "MM_DL1"},
663 {"HDMI Mixer", "MultiMedia2", "MM_DL2"},
664 {"HDMI", NULL, "HDMI Mixer"},
665
666 {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
667 {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
668
669 {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
670 {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
671 {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
672
673 {"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
674 {"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
675 {"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
676
677 {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
678 {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
679 {"MM_UL1", NULL, "MultiMedia1 Mixer"},
680
681 {"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
682 {"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
683 {"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
684
685 {"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
686 {"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
687 {"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
688
689 {"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
690 {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
691 {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
692
693 {"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
694 {"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
695 {"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
696 {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
697 {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
698 {"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
699 {"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
700 {"VOIP_UL", NULL, "Voip_Tx Mixer"},
Sriranjan Srikantama4969dd2011-07-14 00:34:56 -0700701 {"SLIMBUS_0_RX", "Switch", "SLIM0_DL_HL"},
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700702 {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
703 {"INT_FM_RX", NULL, "INTFM_DL_HL"},
704 {"INTFM_UL_HL", NULL, "INT_FM_TX"},
705 {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
706 {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
707 {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"},
708};
709
710static struct snd_pcm_ops msm_routing_pcm_ops = {};
711
712static unsigned int msm_routing_read(struct snd_soc_platform *platform,
713 unsigned int reg)
714{
715 dev_dbg(platform->dev, "reg %x\n", reg);
716 return 0;
717}
718
719/* Not used but frame seems to require it */
720static int msm_routing_write(struct snd_soc_platform *platform,
721 unsigned int reg, unsigned int val)
722{
723 dev_dbg(platform->dev, "reg %x val %x\n", reg, val);
724 return 0;
725}
726
727/* Not used but frame seems to require it */
728static int msm_routing_probe(struct snd_soc_platform *platform)
729{
730 snd_soc_dapm_new_controls(&platform->dapm, msm_qdsp6_widgets,
731 ARRAY_SIZE(msm_qdsp6_widgets));
732 snd_soc_dapm_add_routes(&platform->dapm, intercon,
733 ARRAY_SIZE(intercon));
734
735 snd_soc_dapm_new_widgets(&platform->dapm);
736
Jayasena Sangaraboinacb1e22f2011-07-18 10:36:57 -0700737 snd_soc_add_platform_controls(platform,
738 int_fm_vol_mixer_controls,
739 ARRAY_SIZE(int_fm_vol_mixer_controls));
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530740
741 snd_soc_add_platform_controls(platform,
742 lpa_vol_mixer_controls,
743 ARRAY_SIZE(lpa_vol_mixer_controls));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700744 return 0;
745}
746
747static struct snd_soc_platform_driver msm_soc_routing_platform = {
748 .ops = &msm_routing_pcm_ops,
749 .probe = msm_routing_probe,
750 .read = msm_routing_read,
751 .write = msm_routing_write,
752};
753
754static __devinit int msm_routing_pcm_probe(struct platform_device *pdev)
755{
756 dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
757 return snd_soc_register_platform(&pdev->dev,
758 &msm_soc_routing_platform);
759}
760
761static int msm_routing_pcm_remove(struct platform_device *pdev)
762{
763 snd_soc_unregister_platform(&pdev->dev);
764 return 0;
765}
766
767static struct platform_driver msm_routing_pcm_driver = {
768 .driver = {
769 .name = "msm-pcm-routing",
770 .owner = THIS_MODULE,
771 },
772 .probe = msm_routing_pcm_probe,
773 .remove = __devexit_p(msm_routing_pcm_remove),
774};
775
776static int __init msm_soc_routing_platform_init(void)
777{
778 return platform_driver_register(&msm_routing_pcm_driver);
779}
780module_init(msm_soc_routing_platform_init);
781
782static void __exit msm_soc_routing_platform_exit(void)
783{
784 platform_driver_unregister(&msm_routing_pcm_driver);
785}
786module_exit(msm_soc_routing_platform_exit);
787
788MODULE_DESCRIPTION("MSM routing platform driver");
789MODULE_LICENSE("GPL v2");