summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorDara Ramesh <dramesh@nvidia.com>2013-01-22 20:14:33 +0530
committerRiham Haidar <rhaidar@nvidia.com>2013-06-08 21:05:02 -0700
commitb5c8b695e314c9e70a9b6c9cf34fad56ec85013e (patch)
treeae05faedbf9f2c3b9e0778ad31c384d8e80bbba2 /sound
parent19d92bf60080ab4a93af7eea2d3df41d1e0445ec (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.c149
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: