soc: codecs: rt5639: Implement i2c shutdown
[linux-2.6.git] / sound / soc / tegra / tegra_cs42l73.c
index 0a29bcf..050984a 100644 (file)
@@ -2,9 +2,8 @@
  * tegra_cs42l73.c - Tegra machine ASoC driver for boards using CS42L73 codec.
  *
  * Author: Vijay Mali <vmali@nvidia.com>
- * Copyright (C) 2011-2012, NVIDIA, Inc.
  *
- * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2012-2013, NVIDIA CORPORATION. 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 as published by the Free Software Foundation.
@@ -31,6 +30,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/delay.h>
 #include <linux/a2220.h>
+#include <linux/edp.h>
 #ifdef CONFIG_SWITCH
 #include <linux/switch.h>
 #endif
@@ -91,6 +91,8 @@ struct tegra_cs42l73 {
        struct regulator *dmic_reg;
        struct regulator *dmic_1v8_reg;
        struct regulator *hmic_reg;
+       struct regulator *spkr_reg;
+       struct edp_client *spk_edp_client;
        enum snd_soc_bias_level bias_level;
        struct snd_soc_card *pcard;
 #ifdef CONFIG_SWITCH
@@ -126,14 +128,18 @@ static int tegra_call_mode_put(struct snd_kcontrol *kcontrol,
        int is_call_mode_new = ucontrol->value.integer.value[0];
        int codec_index;
        unsigned int i;
+       int uses_voice_codec;
 
        if (machine->is_call_mode == is_call_mode_new)
                return 0;
 
-       if (machine->is_device_bt)
+       if (machine->is_device_bt) {
                codec_index = BT_SCO;
-       else
-               codec_index = HIFI_CODEC;
+               uses_voice_codec = 0;
+       } else {
+               codec_index = VOICE_CODEC;
+               uses_voice_codec = 1;
+       }
 
        if (is_call_mode_new) {
                if (machine->codec_info[codec_index].rate == 0 ||
@@ -145,11 +151,11 @@ static int tegra_call_mode_put(struct snd_kcontrol *kcontrol,
 
                tegra30_make_voice_call_connections(
                        &machine->codec_info[codec_index],
-                       &machine->codec_info[BASEBAND]);
+                       &machine->codec_info[BASEBAND], uses_voice_codec);
        } else {
                tegra30_break_voice_call_connections(
                        &machine->codec_info[codec_index],
-                       &machine->codec_info[BASEBAND]);
+                       &machine->codec_info[BASEBAND], uses_voice_codec);
 
                for (i = 0; i < machine->pcard->num_links; i++)
                        machine->pcard->dai_link[i].ignore_suspend = 0;
@@ -181,19 +187,33 @@ static int tegra_cs42l73_set_dam_cif(int dam_ifc, int srate,
                                srate);
        tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHIN1,
                                srate);
+#ifndef CONFIG_ARCH_TEGRA_3x_SOC
+       tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHIN1,
+               channels, bit_size, channels,
+                               32);
+       tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHOUT,
+               channels, bit_size, channels,
+                               32);
+#else
        tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHIN1,
                channels, bit_size, channels,
                                bit_size);
        tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHOUT,
                channels, bit_size, channels,
                                bit_size);
+#endif
 
        tegra30_dam_set_gain(dam_ifc, TEGRA30_DAM_CHIN0_SRC, 0x1000);
        if (src_on) {
                tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHIN0_SRC,
                        src_srate);
+#ifndef CONFIG_ARCH_TEGRA_3x_SOC
                tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHIN0_SRC,
                        src_channels, src_bit_size, 1, 32);
+#else
+               tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHIN0_SRC,
+                       src_channels, src_bit_size, 1, 16);
+#endif
        }
 
        return 0;
@@ -271,8 +291,8 @@ static int tegra_cs42l73_hw_params(struct snd_pcm_substream *substream,
        }
 
        a2220_port_path_change(pdata->i2s_param[HIFI_CODEC].is_i2s_master ?
-                                                  A100_msg_PortC_A_PASS :
-                                                  A100_msg_PortA_C_PASS);
+                                                  A100_msg_PortC_D_PASS :
+                                                  A100_msg_PortD_C_PASS);
 
        err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
        if (err < 0) {
@@ -307,9 +327,10 @@ static int tegra_cs42l73_hw_params(struct snd_pcm_substream *substream,
        }
 
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+       if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+               i2s->is_dam_used)
                tegra_cs42l73_set_dam_cif(i2s->dam_ifc, srate,
-                       params_channels(params), sample_size, 0, 0, 0, 0);
+               params_channels(params), sample_size, 0, 0, 0, 0);
 #endif
 
        return 0;
@@ -448,9 +469,10 @@ static int tegra_bt_hw_params(struct snd_pcm_substream *substream,
        }
 
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+       if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+               i2s->is_dam_used)
                tegra_cs42l73_set_dam_cif(i2s->dam_ifc, params_rate(params),
-                       params_channels(params), sample_size, 0, 0, 0, 0);
+               params_channels(params), sample_size, 0, 0, 0, 0);
 #endif
 
        return 0;
@@ -475,6 +497,7 @@ static int tegra_cs42l73_startup(struct snd_pcm_substream *substream)
        struct tegra_cs42l73 *machine = snd_soc_card_get_drvdata(rtd->card);
        struct codec_config *codec_info;
        struct codec_config *bb_info;
+       struct codec_config *hifi_info;
        int codec_index;
 
        if (!i2s->is_dam_used)
@@ -484,7 +507,8 @@ static int tegra_cs42l73_startup(struct snd_pcm_substream *substream)
                /*dam configuration*/
                if (!i2s->dam_ch_refcount)
                        i2s->dam_ifc = tegra30_dam_allocate_controller();
-
+               if (i2s->dam_ifc < 0)
+                       return i2s->dam_ifc;
                tegra30_dam_allocate_channel(i2s->dam_ifc, TEGRA30_DAM_CHIN1);
                i2s->dam_ch_refcount++;
                tegra30_dam_enable_clock(i2s->dam_ifc);
@@ -505,7 +529,6 @@ static int tegra_cs42l73_startup(struct snd_pcm_substream *substream)
                tegra30_dam_enable(i2s->dam_ifc, TEGRA30_DAM_ENABLE,
                                TEGRA30_DAM_CHIN1);
        } else {
-
                i2s->is_call_mode_rec = machine->is_call_mode;
 
                if (!i2s->is_call_mode_rec)
@@ -514,14 +537,19 @@ static int tegra_cs42l73_startup(struct snd_pcm_substream *substream)
                if (machine->is_device_bt)
                        codec_index = BT_SCO;
                else
-                       codec_index = HIFI_CODEC;
+                       codec_index = VOICE_CODEC;
 
                codec_info = &machine->codec_info[codec_index];
                bb_info = &machine->codec_info[BASEBAND];
+               hifi_info = &machine->codec_info[HIFI_CODEC];
 
                /* allocate a dam for voice call recording */
 
                i2s->call_record_dam_ifc = tegra30_dam_allocate_controller();
+
+               if (i2s->call_record_dam_ifc < 0)
+                       return i2s->call_record_dam_ifc;
+
                tegra30_dam_allocate_channel(i2s->call_record_dam_ifc,
                        TEGRA30_DAM_CHIN0_SRC);
                tegra30_dam_allocate_channel(i2s->call_record_dam_ifc,
@@ -535,7 +563,6 @@ static int tegra_cs42l73_startup(struct snd_pcm_substream *substream)
                        bb_info->channels, bb_info->bitsize);
 
                /* setup the connections for voice call record */
-
                tegra30_ahub_unset_rx_cif_source(i2s->rxcif);
                tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX0 +
                        (i2s->call_record_dam_ifc*2),
@@ -582,7 +609,6 @@ static void tegra_cs42l73_shutdown(struct snd_pcm_substream *substream)
                if (!i2s->dam_ch_refcount)
                        tegra30_dam_free_controller(i2s->dam_ifc);
         } else {
-
                if (!i2s->is_call_mode_rec)
                        return;
 
@@ -619,6 +645,7 @@ static int tegra_voice_call_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_card *card = codec->card;
        struct tegra_cs42l73 *machine = snd_soc_card_get_drvdata(card);
@@ -649,10 +676,10 @@ static int tegra_voice_call_hw_params(struct snd_pcm_substream *substream,
        }
 
        i2s_daifmt = SND_SOC_DAIFMT_NB_NF;
-       i2s_daifmt |= pdata->i2s_param[HIFI_CODEC].is_i2s_master ?
+       i2s_daifmt |= pdata->i2s_param[VOICE_CODEC].is_i2s_master ?
                        SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBM_CFM;
 
-       switch (pdata->i2s_param[HIFI_CODEC].i2s_mode) {
+       switch (pdata->i2s_param[VOICE_CODEC].i2s_mode) {
        case TEGRA_DAIFMT_I2S:
                i2s_daifmt |= SND_SOC_DAIFMT_I2S;
                break;
@@ -673,6 +700,10 @@ static int tegra_voice_call_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
+       a2220_port_path_change(pdata->i2s_param[VOICE_CODEC].is_i2s_master ?
+                                                  A100_msg_PortB_A_PASS :
+                                                  A100_msg_PortA_B_PASS);
+
        err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
        if (err < 0) {
                if (!(machine->util_data.set_mclk % mclk))
@@ -693,6 +724,12 @@ static int tegra_voice_call_hw_params(struct snd_pcm_substream *substream,
                return err;
        }
 
+       err = snd_soc_dai_set_fmt(cpu_dai, i2s_daifmt);
+       if (err < 0) {
+               dev_err(card->dev, "cpu_dai fmt not set\n");
+               return err;
+       }
+
        err = snd_soc_dai_set_sysclk(codec_dai, 0, rate, SND_SOC_CLOCK_IN);
        if (err < 0) {
                dev_err(card->dev, "codec_dai clock not set\n");
@@ -701,8 +738,8 @@ static int tegra_voice_call_hw_params(struct snd_pcm_substream *substream,
 
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
        /* codec configuration */
-       machine->codec_info[HIFI_CODEC].rate = params_rate(params);
-       machine->codec_info[HIFI_CODEC].channels = params_channels(params);
+       machine->codec_info[VOICE_CODEC].rate = params_rate(params);
+       machine->codec_info[VOICE_CODEC].channels = params_channels(params);
 #endif
 
        machine->is_device_bt = 0;
@@ -717,10 +754,10 @@ static void tegra_voice_call_shutdown(struct snd_pcm_substream *substream)
                        snd_soc_card_get_drvdata(rtd->codec->card);
 
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
-       machine->codec_info[HIFI_CODEC].rate = 0;
-       machine->codec_info[HIFI_CODEC].channels = 0;
+       machine->codec_info[VOICE_CODEC].rate = 0;
+       machine->codec_info[VOICE_CODEC].channels = 0;
 #endif
-
+       machine->is_device_bt = 0;
        return;
 }
 
@@ -787,7 +824,7 @@ static void tegra_bt_voice_call_shutdown(struct snd_pcm_substream *substream)
        machine->codec_info[BT_SCO].channels = 0;
 #endif
 
-       return;
+       machine->is_device_bt = 0;
 }
 
 static struct snd_soc_ops tegra_cs42l73_ops = {
@@ -871,9 +908,11 @@ static int tegra_cs42l73_jack_notifier(struct notifier_block *self,
        case SND_JACK_HEADPHONE:
                /*For now force headset mic mode*/
                /*state = BIT_HEADSET_NO_MIC; */
+               snd_soc_update_bits(codec, CS42L73_PWRCTL2, PDN_MIC2_BIAS, 0);
                state = BIT_HEADSET;
                break;
        case SND_JACK_HEADSET:
+               snd_soc_update_bits(codec, CS42L73_PWRCTL2, PDN_MIC2_BIAS, 0);
                state = BIT_HEADSET;
                break;
        case SND_JACK_MICROPHONE:
@@ -921,29 +960,82 @@ static int tegra_cs42l73_event_int_mic(struct snd_soc_dapm_widget *w,
                        regulator_disable(machine->dmic_1v8_reg);
                }
        }
-
        if (!(machine->gpio_requested & GPIO_INT_MIC_EN))
                return 0;
 
        gpio_set_value_cansleep(pdata->gpio_int_mic_en,
-                               SND_SOC_DAPM_EVENT_ON(event));
+                               !!SND_SOC_DAPM_EVENT_ON(event));
 
        return 0;
 }
 
+static void tegra_speaker_throttle(unsigned int new_state,  void *priv_data)
+{
+       struct tegra_cs42l73 *machine = priv_data;
+       struct snd_soc_card *card;
+       struct snd_soc_codec *codec;
+
+       if (!machine)
+               return;
+
+       card = machine->pcard;
+       codec = card->rtd[DAI_LINK_HIFI].codec;
+
+       /* set codec voulme to 0 dB, E0 state */
+       snd_soc_write(codec, CS42L73_SPKDVOL, 0x0);
+       snd_soc_write(codec, CS42L73_ESLDVOL, 0x0);
+
+}
+
 static int tegra_cs42l73_event_int_spk(struct snd_soc_dapm_widget *w,
                                        struct snd_kcontrol *k, int event)
 {
        struct snd_soc_dapm_context *dapm = w->dapm;
        struct snd_soc_card *card = dapm->card;
+       struct snd_soc_codec *codec = card->rtd[DAI_LINK_HIFI].codec;
        struct tegra_cs42l73 *machine = snd_soc_card_get_drvdata(card);
        struct tegra_asoc_platform_data *pdata = machine->pdata;
+       unsigned int approved;
+       int ret;
+
+       if (machine->spkr_reg) {
+               if (SND_SOC_DAPM_EVENT_ON(event))
+                       regulator_enable(machine->spkr_reg);
+               else
+                       regulator_disable(machine->spkr_reg);
+       }
 
+       if (machine->spk_edp_client == NULL)
+               goto err_null_spk_edp_client;
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               ret = edp_update_client_request(
+                               machine->spk_edp_client,
+                               TEGRA_SPK_EDP_NEG_1, &approved);
+               if (ret || approved != TEGRA_SPK_EDP_NEG_1) {
+                       /* set codec voulme to 0 dB, E0 state */
+                       snd_soc_write(codec, CS42L73_SPKDVOL, 0x0);
+                       snd_soc_write(codec, CS42L73_ESLDVOL, 0x0);
+               } else {
+                       /* set codec voulme to +6 dB, E-1 state */
+                       snd_soc_write(codec, CS42L73_SPKDVOL, 0x0c);
+                       snd_soc_write(codec, CS42L73_ESLDVOL, 0x0c);
+               }
+       } else {
+               ret = edp_update_client_request(
+                                       machine->spk_edp_client,
+                                       TEGRA_SPK_EDP_1, NULL);
+               if (ret) {
+                       dev_err(card->dev,
+                               "E+1 state transition failed\n");
+               }
+       }
+err_null_spk_edp_client:
        if (!(machine->gpio_requested & GPIO_SPKR_EN))
                return 0;
 
        gpio_set_value_cansleep(pdata->gpio_spkr_en,
-                               SND_SOC_DAPM_EVENT_ON(event));
+                               !!SND_SOC_DAPM_EVENT_ON(event));
 
        return 0;
 }
@@ -967,7 +1059,7 @@ static int tegra_cs42l73_event_ext_mic(struct snd_soc_dapm_widget *w,
                return 0;
 
        gpio_set_value_cansleep(pdata->gpio_ext_mic_en,
-                               SND_SOC_DAPM_EVENT_ON(event));
+                               !!SND_SOC_DAPM_EVENT_ON(event));
 
        return 0;
 }
@@ -976,6 +1068,7 @@ static int tegra_cs42l73_event_ext_mic(struct snd_soc_dapm_widget *w,
 static const struct snd_soc_dapm_widget tegra_cs42l73_dapm_widgets[] = {
        SND_SOC_DAPM_SPK("Int Spk", tegra_cs42l73_event_int_spk),
        SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_HP("Earpiece", NULL),
        SND_SOC_DAPM_MIC("Headset Mic", tegra_cs42l73_event_ext_mic),
        SND_SOC_DAPM_MIC("Int D-Mic", tegra_cs42l73_event_int_mic),
 };
@@ -983,20 +1076,25 @@ static const struct snd_soc_dapm_widget tegra_cs42l73_dapm_widgets[] = {
 /* cs42l73 Audio Map */
 static const struct snd_soc_dapm_route tegra_cs42l73_audio_map[] = {
        {"Int Spk", NULL, "SPKOUT"},
+       {"Int Spk", NULL, "SPKLINEOUT"},
+       {"Int Spk", NULL, "EAROUT"},
+       {"Earpiece", NULL, "EAROUT"},
        {"MIC2", NULL, "Headset Mic"},
+       {"ADC Left", NULL, "Headset Mic"},
+       {"ADC Right", NULL, "Headset Mic"},
        /* Headphone (L+R)->  HPOUTA, HPOUTB */
        {"Headphone", NULL, "HPOUTA"},
        {"Headphone", NULL, "HPOUTB"},
-       /* DMIC -> DMIC Left/Right and VSPIN */
+       /* DMIC -> DMIC Left/Right */
        {"DMIC Left", NULL, "Int D-Mic"},
        {"DMIC Right", NULL, "Int D-Mic"},
-       {"VSPIN", NULL, "Int D-Mic"},
 };
 
 static const struct snd_kcontrol_new tegra_cs42l73_controls[] = {
        SOC_DAPM_PIN_SWITCH("Int Spk"),
        SOC_DAPM_PIN_SWITCH("Int D-Mic"),
        SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Earpiece"),
 };
 
 static int tegra_cs42l73_init(struct snd_soc_pcm_runtime *rtd)
@@ -1013,9 +1111,13 @@ static int tegra_cs42l73_init(struct snd_soc_pcm_runtime *rtd)
 
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
        if (machine->codec_info[BASEBAND].i2s_id != -1)
-               i2s->is_dam_used = true;
+                       i2s->is_dam_used = true;
 #endif
 
+       if ((i2s->id == machine->codec_info[HIFI_CODEC].i2s_id) &&
+               (i2s->id != machine->codec_info[VOICE_CODEC].i2s_id))
+               i2s->is_dam_used = false;
+
        if (machine->init_done)
                return 0;
 
@@ -1053,7 +1155,6 @@ static int tegra_cs42l73_init(struct snd_soc_pcm_runtime *rtd)
                gpio_direction_output(pdata->gpio_spkr_en, 0);
        }
 
-
        /* Add call mode switch control */
        ret = snd_ctl_add(codec->card->snd_card,
                snd_ctl_new1(&tegra_call_mode_control, machine));
@@ -1070,11 +1171,11 @@ static int tegra_cs42l73_init(struct snd_soc_pcm_runtime *rtd)
 static struct snd_soc_dai_link tegra_cs42l73_dai[NUM_DAI_LINKS] = {
        [DAI_LINK_HIFI] = {
                        .name = "CS42L73",
-                       .stream_name = "VSP Playback Record",
+                       .stream_name = "ASP Playback Record",
                        .codec_name = "cs42l73.0-004a",
                        .platform_name = "tegra-pcm-audio",
                    .cpu_dai_name = "tegra30-i2s.1",
-                       .codec_dai_name = "cs42l73-vsp",
+                       .codec_dai_name = "cs42l73-asp",
                        .init = tegra_cs42l73_init,
                        .ops = &tegra_cs42l73_ops,
                },
@@ -1124,10 +1225,8 @@ static int tegra_cs42l73_suspend_post(struct snd_soc_card *card)
        if (gpio_is_valid(gpio->gpio))
                disable_irq(gpio_to_irq(gpio->gpio));
 
-       if (machine->clock_enabled) {
-               machine->clock_enabled = 0;
+       if (machine->clock_enabled && !machine->is_call_mode)
                tegra_asoc_utils_clk_disable(&machine->util_data);
-       }
 
        return 0;
 }
@@ -1138,6 +1237,9 @@ static int tegra_cs42l73_resume_pre(struct snd_soc_card *card)
        struct snd_soc_jack_gpio *gpio = &tegra_cs42l73_hp_jack_gpio;
        struct tegra_cs42l73 *machine = snd_soc_card_get_drvdata(card);
 
+       if (machine->clock_enabled && !machine->is_call_mode)
+               tegra_asoc_utils_clk_enable(&machine->util_data);
+
        if (gpio_is_valid(gpio->gpio)) {
                val = gpio_get_value(gpio->gpio);
                val = gpio->invert ? !val : val;
@@ -1145,11 +1247,6 @@ static int tegra_cs42l73_resume_pre(struct snd_soc_card *card)
                enable_irq(gpio_to_irq(gpio->gpio));
        }
 
-       if (!machine->clock_enabled) {
-               machine->clock_enabled = 1;
-               tegra_asoc_utils_clk_enable(&machine->util_data);
-       }
-
        return 0;
 }
 
@@ -1177,9 +1274,9 @@ static int tegra_cs42l73_set_bias_level_post(struct snd_soc_card *card,
                level == SND_SOC_BIAS_OFF && machine->clock_enabled) {
                machine->clock_enabled = 0;
                tegra_asoc_utils_clk_disable(&machine->util_data);
+               machine->bias_level = level;
        }
 
-       machine->bias_level = level;
 
        return 0 ;
 }
@@ -1193,7 +1290,6 @@ static struct snd_soc_card snd_soc_tegra_cs42l73 = {
        .resume_pre = tegra_cs42l73_resume_pre,
        .set_bias_level = tegra_cs42l73_set_bias_level,
        .set_bias_level_post = tegra_cs42l73_set_bias_level_post,
-
        .controls = tegra_cs42l73_controls,
        .num_controls = ARRAY_SIZE(tegra_cs42l73_controls),
        .dapm_widgets = tegra_cs42l73_dapm_widgets,
@@ -1206,8 +1302,10 @@ static struct snd_soc_card snd_soc_tegra_cs42l73 = {
 static __devinit int tegra_cs42l73_driver_probe(struct platform_device *pdev)
 {
        struct snd_soc_card *card = &snd_soc_tegra_cs42l73;
+       struct snd_soc_codec *codec;
        struct tegra_cs42l73 *machine;
        struct tegra_asoc_platform_data *pdata;
+       struct edp_manager *battery_manager = NULL;
        int ret;
        int i;
        pdata = pdev->dev.platform_data;
@@ -1267,9 +1365,15 @@ static __devinit int tegra_cs42l73_driver_probe(struct platform_device *pdev)
                machine->hmic_reg = 0;
        }
 
+       machine->spkr_reg = regulator_get(&pdev->dev, "vdd_sys_audio");
+       if (IS_ERR(machine->spkr_reg)) {
+               dev_info(&pdev->dev, "No speaker regulator found\n");
+               machine->spkr_reg = 0;
+       }
+
 #ifdef CONFIG_SWITCH
        /* Addd h2w swith class support */
-       ret = switch_dev_register(&tegra_cs42l73_headset_switch);
+       ret = tegra_asoc_switch_register(&tegra_cs42l73_headset_switch);
        if (ret < 0)
                goto err_fini_utils;
 #endif
@@ -1290,11 +1394,10 @@ static __devinit int tegra_cs42l73_driver_probe(struct platform_device *pdev)
                        pdata->i2s_param[i].rate;
                machine->codec_info[i].channels =
                        pdata->i2s_param[i].channels;
-               if ((pdata->i2s_param[i].i2s_mode == TEGRA_DAIFMT_DSP_A) ||
-                       (pdata->i2s_param[i].i2s_mode == TEGRA_DAIFMT_DSP_B))
-                       machine->codec_info[i].is_format_dsp = 1;
-               else
-                       machine->codec_info[i].is_format_dsp = 0;
+               machine->codec_info[i].i2s_mode =
+                       pdata->i2s_param[i].i2s_mode;
+               machine->codec_info[i].bit_clk =
+                       pdata->i2s_param[i].bit_clk;
        }
 
        tegra_cs42l73_dai[DAI_LINK_HIFI].cpu_dai_name =
@@ -1302,6 +1405,9 @@ static __devinit int tegra_cs42l73_driver_probe(struct platform_device *pdev)
 
        tegra_cs42l73_dai[DAI_LINK_BTSCO].cpu_dai_name =
        tegra_cs42l73_i2s_dai_name[machine->codec_info[BT_SCO].i2s_id];
+
+       tegra_cs42l73_dai[DAI_LINK_VOICE_CALL].cpu_dai_name =
+       tegra_cs42l73_i2s_dai_name[machine->codec_info[VOICE_CODEC].i2s_id];
 #endif
        card->dapm.idle_bias_off = 1;
        ret = snd_soc_register_card(card);
@@ -1326,13 +1432,63 @@ static __devinit int tegra_cs42l73_driver_probe(struct platform_device *pdev)
                goto err_unregister_card;
        }
 
+       if (!pdata->edp_support)
+               return 0;
+
+       machine->spk_edp_client = devm_kzalloc(&pdev->dev,
+                                       sizeof(struct edp_client),
+                                       GFP_KERNEL);
+       if (IS_ERR_OR_NULL(machine->spk_edp_client)) {
+               dev_err(&pdev->dev, "could not allocate edp client\n");
+               return 0;
+       }
+       machine->spk_edp_client->name[EDP_NAME_LEN - 1] = '\0';
+       strncpy(machine->spk_edp_client->name, "speaker", EDP_NAME_LEN - 1);
+       machine->spk_edp_client->states = pdata->edp_states;
+       machine->spk_edp_client->num_states = TEGRA_SPK_EDP_NUM_STATES;
+       machine->spk_edp_client->e0_index = TEGRA_SPK_EDP_ZERO;
+       machine->spk_edp_client->priority = EDP_MAX_PRIO + 2;
+       machine->spk_edp_client->throttle = tegra_speaker_throttle;
+       machine->spk_edp_client->private_data = machine;
+
+       battery_manager = edp_get_manager("battery");
+       if (!battery_manager) {
+               devm_kfree(&pdev->dev, machine->spk_edp_client);
+               machine->spk_edp_client = NULL;
+               dev_err(&pdev->dev, "unable to get edp manager\n");
+       } else {
+               /* register speaker edp client */
+               ret = edp_register_client(battery_manager,
+                                       machine->spk_edp_client);
+               if (ret) {
+                       dev_err(&pdev->dev, "unable to register edp client\n");
+                       devm_kfree(&pdev->dev, machine->spk_edp_client);
+                       machine->spk_edp_client = NULL;
+                       return 0;
+               }
+               codec = card->rtd[DAI_LINK_HIFI].codec;
+               /* set codec volume to 0 dB , E0 state*/
+               snd_soc_write(codec, CS42L73_SPKDVOL, 0x0);
+               snd_soc_write(codec, CS42L73_ESLDVOL, 0x0);
+               /* request E1 */
+               ret = edp_update_client_request(machine->spk_edp_client,
+                               TEGRA_SPK_EDP_1, NULL);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                                       "unable to set E1 EDP state\n");
+                       edp_unregister_client(machine->spk_edp_client);
+                       devm_kfree(&pdev->dev, machine->spk_edp_client);
+                       machine->spk_edp_client = NULL;
+               }
+       }
+
        return 0;
 
 err_unregister_card:
        snd_soc_unregister_card(card);
 err_unregister_switch:
 #ifdef CONFIG_SWITCH
-       switch_dev_unregister(&tegra_cs42l73_headset_switch);
+       tegra_asoc_switch_unregister(&tegra_cs42l73_headset_switch);
 err_fini_utils:
 #endif
        tegra_asoc_utils_fini(&machine->util_data);
@@ -1348,6 +1504,10 @@ static int __devexit tegra_cs42l73_driver_remove(struct platform_device *pdev)
        struct tegra_cs42l73 *machine = snd_soc_card_get_drvdata(card);
        struct tegra_asoc_platform_data *pdata = machine->pdata;
 
+#ifdef CONFIG_SWITCH
+       tegra_asoc_switch_unregister(&tegra_cs42l73_headset_switch);
+#endif
+
        if (machine->gpio_requested & GPIO_HP_DET)
                snd_soc_jack_free_gpios(&tegra_cs42l73_hp_jack,
                                        1,
@@ -1361,6 +1521,8 @@ static int __devexit tegra_cs42l73_driver_remove(struct platform_device *pdev)
                regulator_put(machine->dmic_1v8_reg);
        if (machine->hmic_reg)
                regulator_put(machine->hmic_reg);
+       if (machine->spkr_reg)
+               regulator_put(machine->spkr_reg);
 
        if (gpio_is_valid(pdata->gpio_ldo1_en)) {
                gpio_set_value(pdata->gpio_ldo1_en, 0);