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