blob: 06f72b71067ed89ddb362c9b2dede687b83e94c6 [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{
226 struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
227 struct soc_mixer_control *mc =
228 (struct soc_mixer_control *)kcontrol->private_value;
229
230
231 if (ucontrol->value.integer.value[0]) {
232 msm_pcm_routing_process_audio(mc->reg, mc->shift, 1);
233 snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
234 } else {
235 msm_pcm_routing_process_audio(mc->reg, mc->shift, 0);
236 snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
237 }
238
239 return 1;
240}
241
242static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set)
243{
244
245 u32 port_map_id;
246
247 pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
248
249 port_map_id = voice_mixers[reg].port_id;
250
251 if (set)
252 set_bit(val, &voice_mixers[reg].dai_sessions);
253 else
254 clear_bit(val, &voice_mixers[reg].dai_sessions);
255
256 if (voice_mixers[reg].mixer_type == SNDRV_PCM_STREAM_PLAYBACK) {
257 voc_set_route_flag(RX_PATH, set);
258 if (set) {
259 voc_set_rxtx_port(bedai_port_map[port_map_id], DEV_RX);
260
261 if (voc_get_route_flag(RX_PATH) &&
262 voc_get_route_flag(TX_PATH))
263 voc_enable_cvp();
264 } else {
265 voc_disable_cvp();
266 }
267 } else {
268 voc_set_route_flag(TX_PATH, set);
269 if (set) {
270 voc_set_rxtx_port(bedai_port_map[port_map_id], DEV_TX);
271
272 if (voc_get_route_flag(RX_PATH) &&
273 voc_get_route_flag(TX_PATH))
274 voc_enable_cvp();
275 } else {
276 voc_disable_cvp();
277 }
278 }
279}
280
281static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol,
282 struct snd_ctl_elem_value *ucontrol)
283{
284 struct soc_mixer_control *mc =
285 (struct soc_mixer_control *)kcontrol->private_value;
286
287 if (test_bit(mc->shift, &voice_mixers[mc->reg].dai_sessions))
288 ucontrol->value.integer.value[0] = 1;
289 else
290 ucontrol->value.integer.value[0] = 0;
291
292 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
293 ucontrol->value.integer.value[0]);
294
295 return 0;
296}
297
298static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol,
299 struct snd_ctl_elem_value *ucontrol)
300{
301 struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
302 struct soc_mixer_control *mc =
303 (struct soc_mixer_control *)kcontrol->private_value;
304
305 if (ucontrol->value.integer.value[0]) {
306 msm_pcm_routing_process_voice(mc->reg, mc->shift, 1);
307 snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
308 } else {
309 msm_pcm_routing_process_voice(mc->reg, mc->shift, 0);
310 snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
311 }
312
313 return 1;
314}
315
316static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol,
317 struct snd_ctl_elem_value *ucontrol)
318{
319 struct soc_mixer_control *mc =
320 (struct soc_mixer_control *)kcontrol->private_value;
321
322 if (test_bit(mc->shift, &audio_port_mixers[mc->reg].dai_sessions))
323 ucontrol->value.integer.value[0] = 1;
324 else
325 ucontrol->value.integer.value[0] = 0;
326
327 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
328 ucontrol->value.integer.value[0]);
329
330 return 0;
331}
332
333static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol,
334 struct snd_ctl_elem_value *ucontrol)
335{
336 struct soc_mixer_control *mc =
337 (struct soc_mixer_control *)kcontrol->private_value;
338
339 pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg,
340 mc->shift, ucontrol->value.integer.value[0]);
341
342 if (ucontrol->value.integer.value[0]) {
343 afe_loopback(1, audio_port_mixers[mc->reg].port_id,
344 bedai_port_map[mc->shift]);
345 set_bit(mc->shift,
346 &audio_port_mixers[mc->reg].dai_sessions);
347 } else {
348 afe_loopback(0, audio_port_mixers[mc->reg].port_id,
349 bedai_port_map[mc->shift]);
350 clear_bit(mc->shift,
351 &audio_port_mixers[mc->reg].dai_sessions);
352 }
353
354 return 1;
355}
356
357static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
358 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_PRI_I2S_RX ,
359 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
360 msm_routing_put_audio_mixer),
361 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_PRI_I2S_RX,
362 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
363 msm_routing_put_audio_mixer),
364 SOC_SINGLE_EXT("MultiMedia3", AUDIO_MIXER_PRI_I2S_RX,
365 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
366 msm_routing_put_audio_mixer),
367};
368
369static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
370 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_SLIMBUS_0_RX ,
371 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
372 msm_routing_put_audio_mixer),
373 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_SLIMBUS_0_RX,
374 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
375 msm_routing_put_audio_mixer),
376 SOC_SINGLE_EXT("MultiMedia3", AUDIO_MIXER_SLIMBUS_0_RX,
377 MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
378 msm_routing_put_audio_mixer),
379};
380
381static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
382 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_HDMI_RX,
383 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
384 msm_routing_put_audio_mixer),
385 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_HDMI_RX,
386 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
387 msm_routing_put_audio_mixer),
388};
389
390static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
391 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_INT_BT_SCO_RX,
392 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
393 msm_routing_put_audio_mixer),
394 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_INT_BT_SCO_RX,
395 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
396 msm_routing_put_audio_mixer),
397};
398
399static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
400 SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_INT_FM_RX,
401 MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
402 msm_routing_put_audio_mixer),
403 SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_INT_FM_RX,
404 MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
405 msm_routing_put_audio_mixer),
406};
407
408static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
409 SOC_SINGLE_EXT("PRI_TX", AUDIO_MIXER_MM_UL1,
410 MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_audio_mixer,
411 msm_routing_put_audio_mixer),
412 SOC_SINGLE_EXT("SLIM_0_TX", AUDIO_MIXER_MM_UL1,
413 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_audio_mixer,
414 msm_routing_put_audio_mixer),
415 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", AUDIO_MIXER_MM_UL1,
416 MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_audio_mixer,
417 msm_routing_put_audio_mixer),
418 SOC_SINGLE_EXT("INTERNAL_FM_TX", AUDIO_MIXER_MM_UL1,
419 MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_audio_mixer,
420 msm_routing_put_audio_mixer),
421};
422
423static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
424 SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_PRI_I2S_RX,
425 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
426 msm_routing_put_voice_mixer),
427 SOC_SINGLE_EXT("Voip", VOICE_MIXER_PRI_I2S_RX ,
428 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
429 msm_routing_put_voice_mixer),
430};
431
432static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
433 SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_SLIMBUS_0_RX,
434 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
435 msm_routing_put_voice_mixer),
436 SOC_SINGLE_EXT("Voip", VOICE_MIXER_SLIMBUS_0_RX ,
437 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
438 msm_routing_put_voice_mixer),
439};
440
441static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
442 SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_INT_BT_SCO_RX,
443 MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
444 msm_routing_put_voice_mixer),
445 SOC_SINGLE_EXT("Voip", VOICE_MIXER_INT_BT_SCO_RX ,
446 MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
447 msm_routing_put_voice_mixer),
448};
449
450static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
451 SOC_SINGLE_EXT("PRI_TX_Voice", VOICE_MIXER_PRI_I2S_TX,
452 MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_voice_mixer,
453 msm_routing_put_voice_mixer),
454 SOC_SINGLE_EXT("SLIM_0_TX_Voice", VOICE_MIXER_SLIMBUS_0_TX,
455 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_voice_mixer,
456 msm_routing_put_voice_mixer),
457 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice", VOICE_MIXER_INT_BT_SCO_TX,
458 MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_voice_mixer,
459 msm_routing_put_voice_mixer),
460};
461
462static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
463 SOC_SINGLE_EXT("PRI_TX_Voip", VOICE_MIXER_PRI_I2S_TX,
464 MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_voice_mixer,
465 msm_routing_put_voice_mixer),
466 SOC_SINGLE_EXT("SLIM_0_TX_Voip", VOICE_MIXER_SLIMBUS_0_TX,
467 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_voice_mixer,
468 msm_routing_put_voice_mixer),
469 SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voip", VOICE_MIXER_INT_BT_SCO_TX,
470 MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_voice_mixer,
471 msm_routing_put_voice_mixer),
472};
473
474static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
475 SOC_SINGLE_EXT("INTERNAL_FM_TX", AUDIO_PORT_MIXER_SLIM_0_RX,
476 MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
477 msm_routing_put_port_mixer),
478 SOC_SINGLE_EXT("SLIM_0_TX", AUDIO_PORT_MIXER_SLIM_0_RX,
479 MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
480 msm_routing_put_port_mixer),
481};
482
483static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
484 /* Frontend AIF */
485 /* Widget name equals to Front-End DAI name<Need confirmation>,
486 * Stream name must contains substring of front-end dai name
487 */
488 SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
489 SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
490 SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
491 SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
492 SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
493 SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
494 SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
495 SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
496 SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
497 0, 0, 0, 0),
498 SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture",
499 0, 0, 0, 0),
500 SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback",
501 0, 0, 0, 0),
502 SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture",
503 0, 0, 0, 0),
504 /* Backend AIF */
505 /* Stream name equals to backend dai link stream name
506 */
507 SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0),
508 SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
509 SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
510 SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
511 SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
512 SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
513 0, 0, 0 , 0),
514 SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture",
515 0, 0, 0, 0),
516 SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback",
517 0, 0, 0 , 0),
518 SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture",
519 0, 0, 0, 0),
520
521 /* Mixer definitions */
522 SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
523 pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)),
524 SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
525 slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)),
526 SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
527 hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
528 SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
529 mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
530 /* Voice Mixer */
531 SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
532 SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
533 ARRAY_SIZE(pri_rx_voice_mixer_controls)),
534 SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer",
535 SND_SOC_NOPM, 0, 0,
536 slimbus_rx_voice_mixer_controls,
537 ARRAY_SIZE(slimbus_rx_voice_mixer_controls)),
538 SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer",
539 SND_SOC_NOPM, 0, 0,
540 bt_sco_rx_voice_mixer_controls,
541 ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)),
542 SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
543 SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
544 ARRAY_SIZE(tx_voice_mixer_controls)),
545 SND_SOC_DAPM_MIXER("Voip_Tx Mixer",
546 SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls,
547 ARRAY_SIZE(tx_voip_mixer_controls)),
548 SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
549 int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
550 SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
551 int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)),
552 SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer",
553 SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls,
554 ARRAY_SIZE(sbus_0_rx_port_mixer_controls)),
555};
556
557static const struct snd_soc_dapm_route intercon[] = {
558 {"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
559 {"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
560 {"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
561 {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
562
563 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
564 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
565 {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
566 {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
567
568 {"HDMI Mixer", "MultiMedia1", "MM_DL1"},
569 {"HDMI Mixer", "MultiMedia2", "MM_DL2"},
570 {"HDMI", NULL, "HDMI Mixer"},
571
572 {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
573 {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
574
575 {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
576 {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
577 {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
578
579 {"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
580 {"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
581 {"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
582
583 {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
584 {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
585 {"MM_UL1", NULL, "MultiMedia1 Mixer"},
586
587 {"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
588 {"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
589 {"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
590
591 {"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
592 {"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
593 {"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
594
595 {"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
596 {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
597 {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
598
599 {"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
600 {"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
601 {"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
602 {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
603 {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
604 {"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
605 {"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
606 {"VOIP_UL", NULL, "Voip_Tx Mixer"},
607 {"SLIMBUS_0_RX", NULL, "SLIM0_DL_HL"},
608 {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
609 {"INT_FM_RX", NULL, "INTFM_DL_HL"},
610 {"INTFM_UL_HL", NULL, "INT_FM_TX"},
611 {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
612 {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
613 {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"},
614};
615
616static struct snd_pcm_ops msm_routing_pcm_ops = {};
617
618static unsigned int msm_routing_read(struct snd_soc_platform *platform,
619 unsigned int reg)
620{
621 dev_dbg(platform->dev, "reg %x\n", reg);
622 return 0;
623}
624
625/* Not used but frame seems to require it */
626static int msm_routing_write(struct snd_soc_platform *platform,
627 unsigned int reg, unsigned int val)
628{
629 dev_dbg(platform->dev, "reg %x val %x\n", reg, val);
630 return 0;
631}
632
633/* Not used but frame seems to require it */
634static int msm_routing_probe(struct snd_soc_platform *platform)
635{
636 snd_soc_dapm_new_controls(&platform->dapm, msm_qdsp6_widgets,
637 ARRAY_SIZE(msm_qdsp6_widgets));
638 snd_soc_dapm_add_routes(&platform->dapm, intercon,
639 ARRAY_SIZE(intercon));
640
641 snd_soc_dapm_new_widgets(&platform->dapm);
642
643 return 0;
644}
645
646static struct snd_soc_platform_driver msm_soc_routing_platform = {
647 .ops = &msm_routing_pcm_ops,
648 .probe = msm_routing_probe,
649 .read = msm_routing_read,
650 .write = msm_routing_write,
651};
652
653static __devinit int msm_routing_pcm_probe(struct platform_device *pdev)
654{
655 dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
656 return snd_soc_register_platform(&pdev->dev,
657 &msm_soc_routing_platform);
658}
659
660static int msm_routing_pcm_remove(struct platform_device *pdev)
661{
662 snd_soc_unregister_platform(&pdev->dev);
663 return 0;
664}
665
666static struct platform_driver msm_routing_pcm_driver = {
667 .driver = {
668 .name = "msm-pcm-routing",
669 .owner = THIS_MODULE,
670 },
671 .probe = msm_routing_pcm_probe,
672 .remove = __devexit_p(msm_routing_pcm_remove),
673};
674
675static int __init msm_soc_routing_platform_init(void)
676{
677 return platform_driver_register(&msm_routing_pcm_driver);
678}
679module_init(msm_soc_routing_platform_init);
680
681static void __exit msm_soc_routing_platform_exit(void)
682{
683 platform_driver_unregister(&msm_routing_pcm_driver);
684}
685module_exit(msm_soc_routing_platform_exit);
686
687MODULE_DESCRIPTION("MSM routing platform driver");
688MODULE_LICENSE("GPL v2");