diff options
author | Dara Ramesh <dramesh@nvidia.com> | 2013-01-22 20:14:33 +0530 |
---|---|---|
committer | Riham Haidar <rhaidar@nvidia.com> | 2013-06-08 21:05:02 -0700 |
commit | b5c8b695e314c9e70a9b6c9cf34fad56ec85013e (patch) | |
tree | ae05faedbf9f2c3b9e0778ad31c384d8e80bbba2 /sound | |
parent | 19d92bf60080ab4a93af7eea2d3df41d1e0445ec (diff) |
soc: tegra: rt5640: add speaker AMP EDP support
a) registered speaker AMP EDP client
b) implemented throttle callback function for EDP
Bug 1160686
Change-Id: I44d006613e9972709a3c0d8ffb7858c09271c1b2
Signed-off-by: Dara Ramesh <dramesh@nvidia.com>
Reviewed-on: http://git-master/r/193066
Reviewed-by: Hayden Du <haydend@nvidia.com>
Reviewed-by: Sivaram Nair <sivaramn@nvidia.com>
Reviewed-by: Automatic_Commit_Validation_User
Tested-by: Vijay Mali <vmali@nvidia.com>
Reviewed-by: Kerwin Wan <kerwinw@nvidia.com>
Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/tegra/tegra_rt5640.c | 149 |
1 files changed, 145 insertions, 4 deletions
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c index 44609531a5a1..7b3ed820fc37 100644 --- a/sound/soc/tegra/tegra_rt5640.c +++ b/sound/soc/tegra/tegra_rt5640.c @@ -36,6 +36,7 @@ #include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <linux/delay.h> +#include <linux/edp.h> #ifdef CONFIG_SWITCH #include <linux/switch.h> #endif @@ -65,6 +66,11 @@ #define GPIO_EXT_MIC_EN BIT(3) #define GPIO_HP_DET BIT(4) +#define DAI_LINK_HIFI 0 +#define DAI_LINK_SPDIF 1 +#define DAI_LINK_BTSCO 2 +#define NUM_DAI_LINKS 3 + struct tegra30_i2s *i2s_tfa = NULL; struct snd_soc_codec *codec_rt; @@ -74,6 +80,8 @@ struct tegra_rt5640 { struct regulator *spk_reg; struct regulator *dmic_reg; struct regulator *cdc_en; + struct snd_soc_card *pcard; + struct edp_client *spk_edp_client; int gpio_requested; #ifdef CONFIG_SWITCH int jack_status; @@ -439,6 +447,54 @@ static struct snd_soc_jack_pin tegra_rt5640_hp_jack_pins[] = { #endif +static void tegra_speaker_edp_set_volume(struct snd_soc_codec *codec, + int l_vol, + int r_vol) +{ + snd_soc_update_bits(codec, + RT5640_SPK_VOL, + RT5640_L_VOL_MASK, + l_vol << RT5640_L_VOL_SFT); + snd_soc_update_bits(codec, + RT5640_SPK_VOL, + RT5640_R_VOL_MASK, + r_vol << RT5640_R_VOL_SFT); +} + +static void tegra_speaker_throttle(unsigned int new_state, void *priv_data) +{ + struct tegra_rt5640 *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 volume to reflect the new E-state */ + switch (new_state) { + case TEGRA_SPK_EDP_NEG_1: + /* set codec voulme to 0dB (100%), E-1 state */ + tegra_speaker_edp_set_volume(codec, 0x0, 0x0); + break; + case TEGRA_SPK_EDP_ZERO: + /* set codec volume to -16.5dB (78%), E0 state */ + tegra_speaker_edp_set_volume(codec, 0x13, 0x13); + break; + case TEGRA_SPK_EDP_1: + /* turn off codec volume, -46.5 dB, E1 state */ + tegra_speaker_edp_set_volume(codec, 0x27, 0x27); + break; + default: + pr_err("%s: New E-state %d don't support!\n", + __func__, new_state); + break; + } + +} + static int tegra_rt5640_event_int_spk(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -446,6 +502,9 @@ static int tegra_rt5640_event_int_spk(struct snd_soc_dapm_widget *w, struct snd_soc_card *card = dapm->card; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); struct tegra_asoc_platform_data *pdata = machine->pdata; + struct snd_soc_codec *codec = card->rtd[DAI_LINK_HIFI].codec; + unsigned int approved = TEGRA_SPK_EDP_NUM_STATES; + int ret; if (machine->spk_reg) { if (SND_SOC_DAPM_EVENT_ON(event)) { @@ -469,6 +528,36 @@ static int tegra_rt5640_event_int_spk(struct snd_soc_dapm_widget *w, Tfa9887_Powerdown(1); } } + + 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) { + if (approved == TEGRA_SPK_EDP_ZERO) + /* set codec volume to -16.5dB (78%),E0 state */ + tegra_speaker_edp_set_volume(codec, 0x13, 0x13); + else if (approved == TEGRA_SPK_EDP_1) + /* turn off codec volume,-46.5 dB, E1 state */ + tegra_speaker_edp_set_volume(codec, 0x27, 0x27); + } else { + /* set codec voulme to 0dB (100%), E-1 state */ + tegra_speaker_edp_set_volume(codec, 0x0, 0x0); + } + } 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; @@ -671,8 +760,8 @@ static int tegra_rt5640_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static struct snd_soc_dai_link tegra_rt5640_dai[] = { - { +static struct snd_soc_dai_link tegra_rt5640_dai[NUM_DAI_LINKS] = { + [DAI_LINK_HIFI] = { .name = "RT5640", .stream_name = "RT5640 PCM", .codec_name = "rt5640.4-001c", @@ -682,7 +771,7 @@ static struct snd_soc_dai_link tegra_rt5640_dai[] = { .init = tegra_rt5640_init, .ops = &tegra_rt5640_ops, }, - { + [DAI_LINK_SPDIF] = { .name = "SPDIF", .stream_name = "SPDIF PCM", .codec_name = "spdif-dit.0", @@ -691,7 +780,7 @@ static struct snd_soc_dai_link tegra_rt5640_dai[] = { .codec_dai_name = "dit-hifi", .ops = &tegra_spdif_ops, }, - { + [DAI_LINK_BTSCO] = { .name = "BT-SCO", .stream_name = "BT SCO PCM", .codec_name = "spdif-dit.1", @@ -812,6 +901,8 @@ static __devinit int tegra_rt5640_driver_probe(struct platform_device *pdev) struct snd_soc_card *card = &snd_soc_tegra_rt5640; struct tegra_rt5640 *machine; struct tegra_asoc_platform_data *pdata; + struct snd_soc_codec *codec; + struct edp_manager *battery_manager = NULL; int ret; pdata = pdev->dev.platform_data; @@ -889,6 +980,7 @@ static __devinit int tegra_rt5640_driver_probe(struct platform_device *pdev) } machine->pdata = pdata; + machine->pcard = card; ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev, card); if (ret) @@ -949,6 +1041,55 @@ static __devinit int tegra_rt5640_driver_probe(struct platform_device *pdev) } #endif + + 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; + } + + strncpy(machine->spk_edp_client->name, "speaker", EDP_NAME_LEN - 1); + machine->spk_edp_client->name[EDP_NAME_LEN - 1] = '\0'; + 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 -16.5dB (78%), E0 state */ + tegra_speaker_edp_set_volume(codec, 0x13, 0x13); + + /* 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: |