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