soc: tegra: cs42l73: add speaker AMP EDP support
Dara Ramesh [Tue, 22 Jan 2013 14:41:19 +0000 (19:41 +0530)]
a) registered speaker AMP EDP client
b) implemented throttle callback function for EDP

Bug 1160686

Change-Id: I79906d3fd5dbfbedea7512d9745c59d700e325d5
Signed-off-by: Dara Ramesh <dramesh@nvidia.com>
Reviewed-on: http://git-master/r/193065
Reviewed-by: Riham Haidar <rhaidar@nvidia.com>
Tested-by: Riham Haidar <rhaidar@nvidia.com>

arch/arm/mach-tegra/include/mach/tegra_asoc_pdata.h
sound/soc/tegra/tegra_cs42l73.c

index 658629e..a62e076 100644 (file)
@@ -36,6 +36,13 @@ struct i2s_config {
        int bit_clk;
 };
 
+enum tegra_speaker_edp_states {
+       TEGRA_SPK_EDP_NEG_1,
+       TEGRA_SPK_EDP_ZERO,
+       TEGRA_SPK_EDP_1,
+       TEGRA_SPK_EDP_NUM_STATES,
+};
+
 struct tegra_asoc_platform_data {
        const char *codec_name;
        const char *codec_dai_name;
@@ -49,5 +56,6 @@ struct tegra_asoc_platform_data {
        int gpio_codec2;
        int gpio_codec3;
        unsigned int debounce_time_hp;
+       unsigned int edp_states[TEGRA_SPK_EDP_NUM_STATES];
        struct i2s_config i2s_param[NUM_I2S_DEVICES];
 };
index 2b71cdd..3f5d600 100644 (file)
@@ -1,4 +1,4 @@
-/*
+       /*
  * tegra_cs42l73.c - Tegra machine ASoC driver for boards using CS42L73 codec.
  *
  * Author: Vijay Mali <vmali@nvidia.com>
@@ -31,6 +31,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
@@ -92,6 +93,7 @@ struct tegra_cs42l73 {
        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
@@ -968,13 +970,34 @@ static int tegra_cs42l73_event_int_mic(struct snd_soc_dapm_widget *w,
        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))
@@ -983,6 +1006,32 @@ static int tegra_cs42l73_event_int_spk(struct snd_soc_dapm_widget *w,
                        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;
 
@@ -1254,8 +1303,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;
@@ -1382,6 +1433,53 @@ static __devinit int tegra_cs42l73_driver_probe(struct platform_device *pdev)
                goto err_unregister_card;
        }
 
+       if (pdata->edp_states == NULL)
+               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) {
+               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;
+               }
+               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 E0 */
+               ret = edp_update_client_request(machine->spk_edp_client,
+                               TEGRA_SPK_EDP_ZERO, NULL);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                                       "unable to set E0 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: