Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
new file mode 100644
index 0000000..06f72b7
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -0,0 +1,688 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6adm.h>
+#include <sound/q6afe.h>
+
+#include "msm-pcm-routing.h"
+#include "qdsp6/q6voice.h"
+
+struct audio_mixer_data {
+ u32 port_id; /* AFE port ID for Rx, FE DAI ID for TX */
+ unsigned long dai_sessions; /* Rx: FE DAIs Tx: BE DAI */
+ u32 mixer_type; /* playback or capture */
+};
+
+#define INVALID_SESSION -1
+
+enum {
+ AUDIO_MIXER_PRI_I2S_RX = 0,
+ AUDIO_MIXER_SLIMBUS_0_RX,
+ AUDIO_MIXER_HDMI_RX,
+ AUDIO_MIXER_MM_UL1,
+ AUDIO_MIXER_INT_BT_SCO_RX,
+ AUDIO_MIXER_INT_FM_RX,
+ AUDIO_MIXER_MAX,
+};
+
+enum {
+ AUDIO_PORT_MIXER_SLIM_0_RX = 0,
+ AUDIO_PORT_MIXER_MAX,
+};
+
+/* Tx mixer session is stored based on BE DAI ID
+ * Need to map to actual AFE port ID since AFE port
+ * ID can get really large.
+ * The table convert DAI back to AFE port ID
+ */
+static int bedai_port_map[MSM_BACKEND_DAI_MAX] = {
+ PRIMARY_I2S_RX,
+ PRIMARY_I2S_TX,
+ SLIMBUS_0_RX,
+ SLIMBUS_0_TX,
+ HDMI_RX,
+ INT_BT_SCO_RX,
+ INT_BT_SCO_TX,
+ INT_FM_RX,
+ INT_FM_TX,
+};
+
+/* Track ASM playback & capture sessions of DAI */
+static int fe_dai_map[MSM_FRONTEND_DAI_MAX][2] = {
+ /* MULTIMEDIA1 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA2 */
+ {INVALID_SESSION, INVALID_SESSION},
+ /* MULTIMEDIA3 */
+ {INVALID_SESSION, INVALID_SESSION},
+};
+
+static struct audio_mixer_data audio_mixers[AUDIO_MIXER_MAX] = {
+ /* AUDIO_MIXER_PRI_I2S_RX */
+ {PRIMARY_I2S_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* AUDIO_MIXER_SLIMBUS_0_RX */
+ {SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* AUDIO_MIXER_HDMI_RX */
+ {HDMI_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* AUDIO_MIXER_MM_UL1 */
+ {MSM_FRONTEND_DAI_MULTIMEDIA1, 0, SNDRV_PCM_STREAM_CAPTURE},
+ /* AUDIO_MIXER_INT_BT_SCO_RX */
+ {INT_BT_SCO_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* AUDIO_MIXER_INT_FM_RX */
+ {INT_FM_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+};
+static struct voice_mixer_data voice_mixers[VOICE_MIXER_MAX] = {
+ /* VOICE_MIXER_PRI_I2S_RX */
+ {VOICE_PRI_I2S_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* VOICE_MIXER_SLIMBUS_0_RX */
+ {VOICE_SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* VOICE_MIXER_PRI_I2S_TX */
+ {VOICE_PRI_I2S_TX, 0, SNDRV_PCM_STREAM_CAPTURE},
+ /* VOICE_MIXER_SLIMBUS_0_TX */
+ {VOICE_SLIMBUS_0_TX, 0, SNDRV_PCM_STREAM_CAPTURE},
+ /* VOICE_MIXER_INT_BT_SCO_RX */
+ {VOICE_INT_BT_SCO_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+ /* VOICE_MIXER_INT_BT_SCO_TX */
+ {VOICE_INT_BT_SCO_TX, 0, SNDRV_PCM_STREAM_CAPTURE}
+};
+
+/* Reuse audio_mixer_data struct but ignore mixer type field
+ * unless there is use case for RX -> TX
+ */
+static struct audio_mixer_data audio_port_mixers[AUDIO_PORT_MIXER_MAX] = {
+ /* AUDIO_PORT_MIXER_SLIM_0_RX */
+ {SLIMBUS_0_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+};
+
+void msm_pcm_routing_reg_phy_stream(int fedai_id, int dspst_id, int stream_type)
+{
+ int i, be_id;
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ fe_dai_map[fedai_id][0] = dspst_id;
+ for (i = 0; i < AUDIO_MIXER_MAX; i++) {
+ if ((audio_mixers[i].mixer_type == stream_type) &&
+ (test_bit(fedai_id, &audio_mixers[i].dai_sessions)))
+ /* To do: multiple devices case */
+ adm_route_session(audio_mixers[i].port_id,
+ dspst_id, 1);
+ }
+ } else {
+ fe_dai_map[fedai_id][1] = dspst_id;
+ for (i = 0; i < AUDIO_MIXER_MAX; i++) {
+ if ((audio_mixers[i].mixer_type == stream_type) &&
+ (fedai_id == audio_mixers[i].port_id)) {
+ /* To-do: Handle mixing of inputs */
+ be_id = find_next_bit(
+ &audio_mixers[i].dai_sessions,
+ MSM_BACKEND_DAI_MAX, 0);
+ if (be_id < MSM_BACKEND_DAI_MAX)
+ adm_route_session(bedai_port_map[be_id],
+ dspst_id, 1);
+ else
+ pr_err("%s: no routing\n", __func__);
+ }
+ }
+ }
+}
+
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type)
+{
+ int i, be_id;
+
+ if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < AUDIO_MIXER_MAX; i++) {
+ if ((audio_mixers[i].mixer_type == stream_type) &&
+ (test_bit(fedai_id, &audio_mixers[i].dai_sessions))) {
+ /* To do: multiple devices case */
+ adm_route_session(audio_mixers[i].port_id,
+ fe_dai_map[fedai_id][0], 0);
+ }
+ }
+ fe_dai_map[fedai_id][0] = INVALID_SESSION;
+ } else {
+ for (i = 0; i < AUDIO_MIXER_MAX; i++) {
+ if ((audio_mixers[i].mixer_type == stream_type) &&
+ (fedai_id == audio_mixers[i].port_id)) {
+ /* To-do: Handle mixing of inputs */
+ be_id = find_next_bit(
+ &audio_mixers[i].dai_sessions,
+ MSM_BACKEND_DAI_MAX, 0);
+ if (be_id < MSM_BACKEND_DAI_MAX)
+ adm_route_session(bedai_port_map[be_id],
+ fe_dai_map[fedai_id][1], 0);
+ else
+ pr_err("%s: no routing\n", __func__);
+ }
+ }
+ fe_dai_map[fedai_id][1] = INVALID_SESSION;
+ }
+}
+
+static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
+{
+
+ pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
+
+ if (set)
+ set_bit(val, &audio_mixers[reg].dai_sessions);
+ else
+ clear_bit(val, &audio_mixers[reg].dai_sessions);
+
+ if (audio_mixers[reg].mixer_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (fe_dai_map[val][0] != INVALID_SESSION)
+ adm_route_session(audio_mixers[reg].port_id,
+ fe_dai_map[val][0], set);
+ } else {
+ int fe_id = audio_mixers[reg].port_id;
+ if (fe_dai_map[fe_id][1] != INVALID_SESSION)
+ adm_route_session(bedai_port_map[val],
+ fe_dai_map[fe_id][1], set);
+ }
+}
+
+static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->shift, &audio_mixers[mc->reg].dai_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+
+ if (ucontrol->value.integer.value[0]) {
+ msm_pcm_routing_process_audio(mc->reg, mc->shift, 1);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else {
+ msm_pcm_routing_process_audio(mc->reg, mc->shift, 0);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+
+ return 1;
+}
+
+static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set)
+{
+
+ u32 port_map_id;
+
+ pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
+
+ port_map_id = voice_mixers[reg].port_id;
+
+ if (set)
+ set_bit(val, &voice_mixers[reg].dai_sessions);
+ else
+ clear_bit(val, &voice_mixers[reg].dai_sessions);
+
+ if (voice_mixers[reg].mixer_type == SNDRV_PCM_STREAM_PLAYBACK) {
+ voc_set_route_flag(RX_PATH, set);
+ if (set) {
+ voc_set_rxtx_port(bedai_port_map[port_map_id], DEV_RX);
+
+ if (voc_get_route_flag(RX_PATH) &&
+ voc_get_route_flag(TX_PATH))
+ voc_enable_cvp();
+ } else {
+ voc_disable_cvp();
+ }
+ } else {
+ voc_set_route_flag(TX_PATH, set);
+ if (set) {
+ voc_set_rxtx_port(bedai_port_map[port_map_id], DEV_TX);
+
+ if (voc_get_route_flag(RX_PATH) &&
+ voc_get_route_flag(TX_PATH))
+ voc_enable_cvp();
+ } else {
+ voc_disable_cvp();
+ }
+ }
+}
+
+static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->shift, &voice_mixers[mc->reg].dai_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (ucontrol->value.integer.value[0]) {
+ msm_pcm_routing_process_voice(mc->reg, mc->shift, 1);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 1);
+ } else {
+ msm_pcm_routing_process_voice(mc->reg, mc->shift, 0);
+ snd_soc_dapm_mixer_update_power(widget, kcontrol, 0);
+ }
+
+ return 1;
+}
+
+static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (test_bit(mc->shift, &audio_port_mixers[mc->reg].dai_sessions))
+ ucontrol->value.integer.value[0] = 1;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+ ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg,
+ mc->shift, ucontrol->value.integer.value[0]);
+
+ if (ucontrol->value.integer.value[0]) {
+ afe_loopback(1, audio_port_mixers[mc->reg].port_id,
+ bedai_port_map[mc->shift]);
+ set_bit(mc->shift,
+ &audio_port_mixers[mc->reg].dai_sessions);
+ } else {
+ afe_loopback(0, audio_port_mixers[mc->reg].port_id,
+ bedai_port_map[mc->shift]);
+ clear_bit(mc->shift,
+ &audio_port_mixers[mc->reg].dai_sessions);
+ }
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_PRI_I2S_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", AUDIO_MIXER_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia3", AUDIO_MIXER_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_HDMI_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_INT_FM_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX", AUDIO_MIXER_MM_UL1,
+ MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", AUDIO_MIXER_MM_UL1,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", AUDIO_MIXER_MM_UL1,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", AUDIO_MIXER_MM_UL1,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", VOICE_MIXER_PRI_I2S_RX ,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_SLIMBUS_0_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", VOICE_MIXER_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("CSVoice", VOICE_MIXER_INT_BT_SCO_RX,
+ MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("Voip", VOICE_MIXER_INT_BT_SCO_RX ,
+ MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX_Voice", VOICE_MIXER_PRI_I2S_TX,
+ MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX_Voice", VOICE_MIXER_SLIMBUS_0_TX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice", VOICE_MIXER_INT_BT_SCO_TX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX_Voip", VOICE_MIXER_PRI_I2S_TX,
+ MSM_BACKEND_DAI_PRI_I2S_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX_Voip", VOICE_MIXER_SLIMBUS_0_TX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voip", VOICE_MIXER_INT_BT_SCO_TX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", AUDIO_PORT_MIXER_SLIM_0_RX,
+ MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX", AUDIO_PORT_MIXER_SLIM_0_RX,
+ MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
+};
+
+static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
+ /* Frontend AIF */
+ /* Widget name equals to Front-End DAI name<Need confirmation>,
+ * Stream name must contains substring of front-end dai name
+ */
+ SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture",
+ 0, 0, 0, 0),
+ /* Backend AIF */
+ /* Stream name equals to backend dai link stream name
+ */
+ SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture",
+ 0, 0, 0, 0),
+
+ /* Mixer definitions */
+ SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
+ hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
+ /* Voice Mixer */
+ SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
+ ARRAY_SIZE(pri_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ slimbus_rx_voice_mixer_controls,
+ ARRAY_SIZE(slimbus_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ bt_sco_rx_voice_mixer_controls,
+ ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
+ ARRAY_SIZE(tx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Voip_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls,
+ ARRAY_SIZE(tx_voip_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer",
+ SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls,
+ ARRAY_SIZE(sbus_0_rx_port_mixer_controls)),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
+
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
+
+ {"HDMI Mixer", "MultiMedia1", "MM_DL1"},
+ {"HDMI Mixer", "MultiMedia2", "MM_DL2"},
+ {"HDMI", NULL, "HDMI Mixer"},
+
+ {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
+ {"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
+
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"MM_UL1", NULL, "MultiMedia1 Mixer"},
+
+ {"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
+
+ {"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
+
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
+ {"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
+
+ {"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
+ {"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
+ {"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
+ {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
+ {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
+ {"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
+ {"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
+ {"VOIP_UL", NULL, "Voip_Tx Mixer"},
+ {"SLIMBUS_0_RX", NULL, "SLIM0_DL_HL"},
+ {"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
+ {"INT_FM_RX", NULL, "INTFM_DL_HL"},
+ {"INTFM_UL_HL", NULL, "INT_FM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+ {"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"},
+};
+
+static struct snd_pcm_ops msm_routing_pcm_ops = {};
+
+static unsigned int msm_routing_read(struct snd_soc_platform *platform,
+ unsigned int reg)
+{
+ dev_dbg(platform->dev, "reg %x\n", reg);
+ return 0;
+}
+
+/* Not used but frame seems to require it */
+static int msm_routing_write(struct snd_soc_platform *platform,
+ unsigned int reg, unsigned int val)
+{
+ dev_dbg(platform->dev, "reg %x val %x\n", reg, val);
+ return 0;
+}
+
+/* Not used but frame seems to require it */
+static int msm_routing_probe(struct snd_soc_platform *platform)
+{
+ snd_soc_dapm_new_controls(&platform->dapm, msm_qdsp6_widgets,
+ ARRAY_SIZE(msm_qdsp6_widgets));
+ snd_soc_dapm_add_routes(&platform->dapm, intercon,
+ ARRAY_SIZE(intercon));
+
+ snd_soc_dapm_new_widgets(&platform->dapm);
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_routing_platform = {
+ .ops = &msm_routing_pcm_ops,
+ .probe = msm_routing_probe,
+ .read = msm_routing_read,
+ .write = msm_routing_write,
+};
+
+static __devinit int msm_routing_pcm_probe(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+ return snd_soc_register_platform(&pdev->dev,
+ &msm_soc_routing_platform);
+}
+
+static int msm_routing_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver msm_routing_pcm_driver = {
+ .driver = {
+ .name = "msm-pcm-routing",
+ .owner = THIS_MODULE,
+ },
+ .probe = msm_routing_pcm_probe,
+ .remove = __devexit_p(msm_routing_pcm_remove),
+};
+
+static int __init msm_soc_routing_platform_init(void)
+{
+ return platform_driver_register(&msm_routing_pcm_driver);
+}
+module_init(msm_soc_routing_platform_init);
+
+static void __exit msm_soc_routing_platform_exit(void)
+{
+ platform_driver_unregister(&msm_routing_pcm_driver);
+}
+module_exit(msm_soc_routing_platform_exit);
+
+MODULE_DESCRIPTION("MSM routing platform driver");
+MODULE_LICENSE("GPL v2");