ASOC: Tegra: Add control for I2s Loopback
Mubashra Khan [Tue, 17 Dec 2013 13:15:42 +0000 (18:15 +0530)]
It creates a control 'I2S Loopback'.If it is set ON,
then playback file traverses through I2S to record path.

Bug number: 1411829

Change-Id: I352cad7ceadbac06efcf75d7a9045d04f4e3a8cb
Signed-off-by: Mubashra Khan <mukhan@nvidia.com>
Reviewed-on: http://git-master/r/346496
GVS: Gerrit_Virtual_Submit
Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com>

sound/soc/tegra/tegra30_i2s.c
sound/soc/tegra/tegra_asoc_utils.c

index b3ec5f9..e752f13 100644 (file)
@@ -50,6 +50,7 @@
 
 #define RETRY_CNT      10
 
+extern int tegra_i2sloopback_func;
 static struct tegra30_i2s *i2scont[TEGRA30_NR_I2S_IFC];
 #if defined(CONFIG_ARCH_TEGRA_14x_SOC)
 static struct tegra30_i2s bbc1cont;
@@ -490,6 +491,13 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
        regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, mask, val);
 
        regmap_read(i2s->regmap, TEGRA30_I2S_CTRL, &reg_ctrl);
+       /* I2S loopback*/
+       if (tegra_i2sloopback_func)
+               reg_ctrl |= TEGRA30_I2S_CTRL_LPBK_ENABLE;
+       else
+               reg_ctrl &= ~TEGRA30_I2S_CTRL_LPBK_ENABLE;
+       mask = TEGRA30_I2S_CTRL_LPBK_ENABLE;
+       regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, mask, reg_ctrl);
        /* TDM mode */
        if ((reg_ctrl & TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC) &&
                (i2s->dsp_config.slot_width > 2))
@@ -898,6 +906,7 @@ static int tegra30_i2s_probe(struct snd_soc_dai *dai)
        i2s->dsp_config.rx_mask = 1;
        i2s->dsp_config.rx_data_offset = 1;
        i2s->dsp_config.tx_data_offset = 1;
+       tegra_i2sloopback_func = 0;
 
 
        return 0;
index f32613c..0e22b62 100644 (file)
 
 int g_is_call_mode;
 static atomic_t dap_ref_count[5];
+int tegra_i2sloopback_func;
 
+static const char * const loopback_function[] = {
+       "Off",
+       "On"
+};
+
+static const struct soc_enum tegra_enum =
+       SOC_ENUM_SINGLE_EXT(2, loopback_function);
 #ifdef CONFIG_SWITCH
 static bool is_switch_registered;
 struct switch_dev *psdev;
@@ -293,6 +301,23 @@ static int tegra_get_dma_addr(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+static int tegra_get_i2sloopback(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = tegra_i2sloopback_func;
+       return 0;
+}
+
+static int tegra_set_i2sloopback(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       if (tegra_i2sloopback_func == ucontrol->value.integer.value[0])
+               return 0;
+
+       tegra_i2sloopback_func = ucontrol->value.integer.value[0];
+       return 1;
+}
+
 struct snd_kcontrol_new tegra_avp_controls[] = {
        SOC_SINGLE_EXT("AVP alsa device select", 0, 0, TEGRA_ALSA_MAX_DEVICES, \
                        0, tegra_get_avp_device, tegra_set_avp_device),
@@ -302,6 +327,10 @@ struct snd_kcontrol_new tegra_avp_controls[] = {
                        0, tegra_get_dma_addr, tegra_set_dma_addr),
 };
 
+static const struct snd_kcontrol_new tegra_i2s_lpbk_control =
+       SOC_ENUM_EXT("I2S LoopBack", tegra_enum, tegra_get_i2sloopback,
+                       tegra_set_i2sloopback);
+
 static int tegra_set_headset_plug_state(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
@@ -469,6 +498,13 @@ int tegra_asoc_utils_register_ctls(struct tegra_asoc_utils_data *data)
                return ret;
        }
 
+       ret = snd_ctl_add(data->card->snd_card,
+                       snd_ctl_new1(&tegra_i2s_lpbk_control, data));
+       if (ret < 0) {
+               dev_err(data->dev, "Can't add i2s loopback control");
+               return ret;
+       }
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(tegra_asoc_utils_register_ctls);