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