summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSumit Bhattacharya <sumitb@nvidia.com>2011-03-29 19:16:51 +0530
committerVarun Colbert <vcolbert@nvidia.com>2011-04-13 18:05:50 -0700
commit78802f82d0b54857e5dabd6c7fd6f5ab16afedf4 (patch)
tree8a13f0dbc1cf44c612983ad96f75b6d3993d515b
parenta12a3c264124e02232e2b102bfc718682cb374b6 (diff)
ASOC: TEGRA: Make alsa more configurable
This change mainly takes care of following things: 1) Codec_dai code will use dap apis to query the board specific dap tables to find the codec configuration needed for a particular routing and will configure codec accordingly. So long we were configuring codec hardcodedly. 2) Moved all codec and board specific routing codes to codec specific files from tegra_soc_controls.c. 3) Modifying the codec routing logic to make it simpler and robust. 4) Except DAP Mclk we will turn off all other audio related clocks when not in use. 5) I2s and SPDIF bit clock frequencies will be dynamically calculated based on the stream properties. 6) DAS power management and DAP MCLK related codes are moved to codec dai since they are more closely related to codec than i2s. Change-Id: I531558bec108af5828c8ff2ed932f4d25e00afdb Reviewed-on: http://git-master/r/24683 Tested-by: Sumit Bhattacharya <sumitb@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--sound/soc/tegra/tegra_i2s.c177
-rw-r--r--sound/soc/tegra/tegra_pcm.c10
-rw-r--r--sound/soc/tegra/tegra_soc.h19
-rw-r--r--sound/soc/tegra/tegra_soc_controls.c431
-rw-r--r--sound/soc/tegra/tegra_soc_wm8753.c429
-rw-r--r--sound/soc/tegra/tegra_soc_wm8903.c375
-rw-r--r--sound/soc/tegra/tegra_spdif.c39
7 files changed, 815 insertions, 665 deletions
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
index e460a76968f6..1f40cff697d8 100644
--- a/sound/soc/tegra/tegra_i2s.c
+++ b/sound/soc/tegra/tegra_i2s.c
@@ -1,7 +1,7 @@
/*
* tegra_i2s.c -- ALSA Soc Audio Layer
*
- * (c) 2010 Nvidia Graphics Pvt. Ltd.
+ * (c) 2010-2011 Nvidia Graphics Pvt. Ltd.
* http://www.nvidia.com
*
* (c) 2006 Wolfson Microelectronics PLC.
@@ -24,8 +24,6 @@ struct tegra_i2s_info {
struct platform_device *pdev;
struct tegra_audio_platform_data *pdata;
struct clk *i2s_clk;
- struct clk *dap_mclk;
- struct clk *audio_sync_clk;
phys_addr_t i2s_phys;
void __iomem *i2s_base;
@@ -34,6 +32,7 @@ struct tegra_i2s_info {
int irq;
/* Control for whole I2S (Data format, etc.) */
unsigned int bit_format;
+ bool i2s_master;
int ref_count;
struct i2s_runtime_data i2s_regs;
struct das_regs_cache das_regs;
@@ -118,26 +117,27 @@ static int tegra_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct tegra_i2s_info *info = dai->private_data;
- int ret = 0;
- int val;
unsigned int i2s_id = dai->id;
- unsigned int rate;
+ int val;
+ unsigned int rate, sample_size;
+
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
val = I2S_BIT_SIZE_16;
+ sample_size = 16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
val = I2S_BIT_SIZE_24;
+ sample_size = 24;
break;
case SNDRV_PCM_FORMAT_S32_LE:
val = I2S_BIT_SIZE_32;
+ sample_size = 32;
break;
default:
- ret =-EINVAL;
- goto err;
+ return -EINVAL;
}
-
i2s_set_bit_size(i2s_id, val);
switch (params_rate(params)) {
@@ -150,36 +150,46 @@ static int tegra_i2s_hw_params(struct snd_pcm_substream *substream,
val = params_rate(params);
break;
default:
- ret = -EINVAL;
- goto err;
+ return -EINVAL;
}
- rate = clk_get_rate(info->i2s_clk);
- if (info->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
- rate *= 2;
+ if (info->i2s_master) {
+ /* Min BCLK = samplerate * channel * bits per sample * 2 */
+ rate = val * params_channels(params) * sample_size * 2;
+
+ /* For DSP mode we need double BCLK */
+ if (info->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
+ rate *= 2;
- i2s_set_channel_bit_count(i2s_id, val, rate);
+ /* Ensure I2s clk rate is atleast greater than min BCLK */
+ clk_set_rate(info->i2s_clk, rate);
+ if (clk_get_rate(info->i2s_clk) < rate)
+ clk_set_rate(info->i2s_clk, rate << 1);
- return 0;
+ rate = clk_get_rate(info->i2s_clk);
+ if (info->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
+ rate *= 2;
-err:
- return ret;
+ i2s_set_channel_bit_count(i2s_id, val, rate);
+ }
+
+ return 0;
}
static int tegra_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
- int val1;
- int val2;
+ struct tegra_i2s_info *info = cpu_dai->private_data;
unsigned int i2s_id = cpu_dai->id;
+ int val1, val2;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
val1 = 1;
break;
case SND_SOC_DAIFMT_CBM_CFM:
- val1= 0;
+ val1 = 0;
break;
case SND_SOC_DAIFMT_CBS_CFM:
case SND_SOC_DAIFMT_CBM_CFS:
@@ -188,6 +198,8 @@ static int tegra_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
default:
return -EINVAL;
}
+ i2s_set_master(i2s_id, val1);
+ info->i2s_master = val1;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
@@ -213,48 +225,8 @@ static int tegra_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
default:
return -EINVAL;
}
-
- i2s_set_bit_format(i2s_id,val1);
- i2s_set_left_right_control_polarity(i2s_id,val2);
-
- /* Clock inversion */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_DSP_A:
- case SND_SOC_DAIFMT_DSP_B:
- /* frame inversion not valid for DSP modes */
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- break;
- case SND_SOC_DAIFMT_IB_NF:
- /* aif1 |= WM8903_AIF_BCLK_INV; */
- break;
- default:
- return -EINVAL;
- }
- break;
- case SND_SOC_DAIFMT_I2S:
- case SND_SOC_DAIFMT_RIGHT_J:
- case SND_SOC_DAIFMT_LEFT_J:
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- break;
- case SND_SOC_DAIFMT_IB_IF:
- /* aif1 |= WM8903_AIF_BCLK_INV |
- * WM8903_AIF_LRCLK_INV; */
- break;
- case SND_SOC_DAIFMT_IB_NF:
- /* aif1 |= WM8903_AIF_BCLK_INV; */
- break;
- case SND_SOC_DAIFMT_NB_IF:
- /* aif1 |= WM8903_AIF_LRCLK_INV; */
- break;
- default:
- return -EINVAL;
- }
- break;
- default:
- return -EINVAL;
- }
+ i2s_set_bit_format(i2s_id, val1);
+ i2s_set_left_right_control_polarity(i2s_id, val2);
return 0;
}
@@ -262,18 +234,6 @@ static int tegra_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
static int tegra_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
- struct tegra_i2s_info* info = cpu_dai->private_data;
- struct tegra_audio_platform_data *pdata = info->pdev->dev.platform_data;
-
- if (info && info->i2s_clk) {
- clk_set_rate(info->i2s_clk, pdata->i2s_clk_rate);
- }
- else {
- pr_err("%s: could not get i2s-%d clock\n", __func__,
- cpu_dai->id+1);
- return -EIO;
- }
-
return 0;
}
@@ -310,7 +270,6 @@ static int i2s_configure(struct tegra_i2s_info *info )
{
struct platform_device *pdev = info->pdev;
struct tegra_audio_platform_data *pdata = pdev->dev.platform_data;
- struct clk *i2s_clk;
unsigned int i2s_id = pdev->id;
unsigned int rate;
@@ -319,22 +278,15 @@ static int i2s_configure(struct tegra_i2s_info *info )
i2s_fifo_clear(i2s_id, I2S_FIFO_RX);
i2s_set_left_right_control_polarity(i2s_id, 0); /* default */
- i2s_clk = clk_get(&pdev->dev, NULL);
- if (!i2s_clk) {
- dev_err(&pdev->dev, "%s: could not get i2s clock\n",
- __func__);
- return -EIO;
- }
-
- rate = clk_get_rate(i2s_clk);
+ rate = clk_get_rate(info->i2s_clk);
if (info->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
rate *= 2;
+ i2s_set_master(i2s_id, pdata->i2s_master);
+ info->i2s_master = pdata->i2s_master;
if (pdata->i2s_master && pdata->i2s_master_clk)
i2s_set_channel_bit_count(i2s_id, pdata->i2s_master_clk, rate);
- i2s_set_master(i2s_id, pdata->i2s_master);
-
i2s_set_fifo_mode(i2s_id, I2S_FIFO_TX, 1);
i2s_set_fifo_mode(i2s_id, I2S_FIFO_RX, 0);
@@ -350,10 +302,12 @@ int tegra_i2s_suspend(struct snd_soc_dai *cpu_dai)
{
struct tegra_i2s_info *info = cpu_dai->private_data;
+ clk_enable(info->i2s_clk);
+
i2s_get_all_regs(cpu_dai->id, &info->i2s_regs);
tegra_das_get_all_regs(&info->das_regs);
- clk_disable(info->dap_mclk);
+ clk_disable(info->i2s_clk);
return 0;
}
@@ -362,12 +316,14 @@ int tegra_i2s_resume(struct snd_soc_dai *cpu_dai)
{
struct tegra_i2s_info *info = cpu_dai->private_data;
- clk_enable(info->dap_mclk);
+ clk_enable(info->i2s_clk);
tegra_das_set_all_regs(&info->das_regs);
i2s_set_all_regs(cpu_dai->id, &info->i2s_regs);
tegra_jack_resume();
+ clk_disable(info->i2s_clk);
+
return 0;
}
@@ -381,13 +337,9 @@ static int tegra_i2s_startup(struct snd_pcm_substream *substream,
{
struct tegra_i2s_info *info = dai->private_data;
- if (!info->ref_count) {
- /* set das pins state to normal */
- tegra_das_power_mode(true);
-
- clk_enable(info->audio_sync_clk);
+ if (!info->ref_count)
clk_enable(info->i2s_clk);
- }
+
info->ref_count++;
return 0;
}
@@ -400,13 +352,8 @@ static void tegra_i2s_shutdown(struct snd_pcm_substream *substream,
if (info->ref_count > 0)
info->ref_count--;
- if (!info->ref_count) {
+ if (!info->ref_count)
clk_disable(info->i2s_clk);
- clk_disable(info->audio_sync_clk);
-
- /* set das pins state to tristate */
- tegra_das_power_mode(false);
- }
return;
}
@@ -434,13 +381,13 @@ struct snd_soc_dai tegra_i2s_dai[] = {
.suspend = tegra_i2s_suspend,
.resume = tegra_i2s_resume,
.playback = {
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = TEGRA_SAMPLE_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = TEGRA_SAMPLE_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -455,14 +402,14 @@ struct snd_soc_dai tegra_i2s_dai[] = {
.resume = tegra_i2s_resume,
.playback = {
.channels_min = 1,
- .channels_max = 1,
- .rates = TEGRA_VOICE_SAMPLE_RATES,
+ .channels_max = 2,
+ .rates = TEGRA_SAMPLE_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.channels_min = 1,
- .channels_max = 1,
- .rates = TEGRA_VOICE_SAMPLE_RATES,
+ .channels_max = 2,
+ .rates = TEGRA_SAMPLE_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &tegra_i2s_dai_ops,
@@ -531,22 +478,9 @@ static int tegra_i2s_driver_probe(struct platform_device *pdev)
err = PTR_ERR(info->i2s_clk);
goto fail_unmap_mem;
}
+ clk_enable(info->i2s_clk);
clk_set_rate(info->i2s_clk, info->pdata->i2s_clk_rate);
- info->dap_mclk = i2s_get_clock_by_name(info->pdata->dap_clk);
- if (IS_ERR(info->dap_mclk)) {
- err = PTR_ERR(info->dap_mclk);
- goto fail_unmap_mem;
- }
- clk_enable(info->dap_mclk);
-
- info->audio_sync_clk = i2s_get_clock_by_name(
- info->pdata->audio_sync_clk);
- if (IS_ERR(info->audio_sync_clk)) {
- err = PTR_ERR(info->audio_sync_clk);
- goto fail_unmap_mem;
- }
-
info->bit_format = TEGRA_AUDIO_BIT_FORMAT_DEFAULT;
if (info->pdata->mode == I2S_BIT_FORMAT_DSP)
info->bit_format = TEGRA_AUDIO_BIT_FORMAT_DSP;
@@ -563,6 +497,9 @@ static int tegra_i2s_driver_probe(struct platform_device *pdev)
}
}
+ /* Disable i2s clk to save power */
+ clk_disable(info->i2s_clk);
+
return 0;
fail_unmap_mem:
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index 28f6b6b0d09d..83314f23de53 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -1,7 +1,7 @@
/*
* tegra_pcm.c -- ALSA Soc Audio Layer
*
- * (c) 2010 Nvidia Graphics Pvt. Ltd.
+ * (c) 2010-2011 Nvidia Graphics Pvt. Ltd.
* http://www.nvidia.com
*
* (c) 2006 Wolfson Microelectronics PLC.
@@ -146,14 +146,14 @@ static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
prtd->state = STATE_INIT;
prtd->dma_state = STATE_INIT;
- tegra_pcm_play(prtd); /* dma enqueue req1 */
- tegra_pcm_play(prtd); /* dma enqueue req2 */
+ for (i = 0; i < DMA_REQ_QCOUNT; i++)
+ tegra_pcm_play(prtd); /* dma enqueue req */
} else if (prtd->state != STATE_INIT) {
/* start recording */
prtd->state = STATE_INIT;
prtd->dma_state = STATE_INIT;
- tegra_pcm_capture(prtd); /* dma enqueue req1 */
- tegra_pcm_capture(prtd); /* dma enqueue req2 */
+ for (i = 0; i < DMA_REQ_QCOUNT; i++)
+ tegra_pcm_capture(prtd); /* dma enqueue req */
}
break;
case SNDRV_PCM_TRIGGER_STOP:
diff --git a/sound/soc/tegra/tegra_soc.h b/sound/soc/tegra/tegra_soc.h
index 8dd2efaa6d13..238497aacb2c 100644
--- a/sound/soc/tegra/tegra_soc.h
+++ b/sound/soc/tegra/tegra_soc.h
@@ -1,7 +1,7 @@
/*
* tegra_soc.h -- SoC audio for tegra
*
- * (c) 2010 Nvidia Graphics Pvt. Ltd.
+ * (c) 2010-2011 Nvidia Graphics Pvt. Ltd.
* http://www.nvidia.com
*
* Copyright 2007 Wolfson Microelectronics PLC.
@@ -80,6 +80,16 @@
#define DMA_STEP_SIZE_MIN 8
#define DMA_REQ_QCOUNT 2
+#define TEGRA_AUDIO_OFF 0x0
+#define TEGRA_HEADPHONE 0x1
+#define TEGRA_LINEOUT 0x2
+#define TEGRA_SPK 0x4
+#define TEGRA_EAR_SPK 0x8
+#define TEGRA_INT_MIC 0x10
+#define TEGRA_EXT_MIC 0x20
+#define TEGRA_LINEIN 0x40
+#define TEGRA_HEADSET 0x80
+
struct tegra_dma_channel;
struct tegra_runtime_data {
@@ -97,9 +107,14 @@ struct tegra_runtime_data {
struct tegra_audio_data {
struct snd_soc_codec *codec;
+ struct clk *dap_mclk;
+ bool init_done;
+
int play_device;
int capture_device;
bool is_call_mode;
+
+ int codec_con;
};
struct wired_jack_conf {
@@ -113,8 +128,8 @@ struct wired_jack_conf {
int amp_reg_enabled;
};
+void tegra_ext_control(struct snd_soc_codec *codec, int new_con);
int tegra_controls_init(struct snd_soc_codec *codec);
-void tegra_controls_exit(void);
int tegra_jack_init(struct snd_soc_codec *codec);
void tegra_jack_exit(void);
diff --git a/sound/soc/tegra/tegra_soc_controls.c b/sound/soc/tegra/tegra_soc_controls.c
index 04ffd880ed0e..bd948509df62 100644
--- a/sound/soc/tegra/tegra_soc_controls.c
+++ b/sound/soc/tegra/tegra_soc_controls.c
@@ -1,7 +1,7 @@
/*
* tegra_soc_controls.c -- alsa controls for tegra SoC
*
- * Copyright (c) 2010, NVIDIA Corporation.
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,259 +18,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include <linux/gpio.h>
-#include <sound/soc-dapm.h>
-#include <linux/regulator/consumer.h>
-#include <mach/audio.h>
-
#include "tegra_soc.h"
-#define TEGRA_HP 0
-#define TEGRA_MIC 1
-#define TEGRA_LINE 2
-#define TEGRA_HEADSET 3
-#define TEGRA_HP_OFF 4
-
-#define TEGRA_LINEOUT_ON 0
-#define TEGRA_LINEOUT_OFF 1
-
-#define TEGRA_INT_SPK_ON 0
-#define TEGRA_INT_SPK_OFF 1
-
-extern struct wired_jack_conf tegra_wired_jack_conf;
-
-static struct tegra_audio_data *audio_data;
-
-static int tegra_jack_func;
-static int tegra_lineout_func;
-static int tegra_spk_func;
-
-static void tegra_ext_control(struct snd_soc_codec *codec)
-{
- /* set up jack connection */
- switch (tegra_jack_func) {
- case TEGRA_HP:
- /* set = unmute headphone */
- snd_soc_dapm_enable_pin(codec, "Mic Jack");
- snd_soc_dapm_disable_pin(codec, "Line Jack");
- snd_soc_dapm_enable_pin(codec, "Headphone Jack");
- snd_soc_dapm_disable_pin(codec, "Headset Jack");
- break;
- case TEGRA_MIC:
- /* reset = mute headphone */
- snd_soc_dapm_enable_pin(codec, "Mic Jack");
- snd_soc_dapm_disable_pin(codec, "Line Jack");
- snd_soc_dapm_disable_pin(codec, "Headphone Jack");
- snd_soc_dapm_disable_pin(codec, "Headset Jack");
- break;
- case TEGRA_LINE:
- snd_soc_dapm_disable_pin(codec, "Mic Jack");
- snd_soc_dapm_enable_pin(codec, "Line Jack");
- snd_soc_dapm_disable_pin(codec, "Headphone Jack");
- snd_soc_dapm_disable_pin(codec, "Headset Jack");
- break;
- case TEGRA_HEADSET:
- snd_soc_dapm_enable_pin(codec, "Mic Jack");
- snd_soc_dapm_disable_pin(codec, "Line Jack");
- snd_soc_dapm_disable_pin(codec, "Headphone Jack");
- snd_soc_dapm_enable_pin(codec, "Headset Jack");
- break;
- }
-
-
- if (tegra_lineout_func == TEGRA_LINEOUT_ON) {
- snd_soc_dapm_enable_pin(codec, "Lineout");
- } else {
- snd_soc_dapm_disable_pin(codec, "Lineout");
- }
-
- if (tegra_spk_func == TEGRA_INT_SPK_ON) {
- snd_soc_dapm_enable_pin(codec, "Int Spk");
- if (tegra_wired_jack_conf.amp_reg &&
- !tegra_wired_jack_conf.amp_reg_enabled) {
- regulator_enable(tegra_wired_jack_conf.amp_reg);
- tegra_wired_jack_conf.amp_reg_enabled = 1;
- }
- } else {
- snd_soc_dapm_disable_pin(codec, "Int Spk");
- if (tegra_wired_jack_conf.amp_reg &&
- tegra_wired_jack_conf.amp_reg_enabled) {
- regulator_disable(tegra_wired_jack_conf.amp_reg);
- tegra_wired_jack_conf.amp_reg_enabled = 0;
- }
- }
-
- /* signal a DAPM event */
- snd_soc_dapm_sync(codec);
-}
-
-static int tegra_get_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = tegra_jack_func;
- return 0;
-}
-
-static int tegra_set_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-
- if (tegra_jack_func == ucontrol->value.integer.value[0])
- return 0;
-
- tegra_jack_func = ucontrol->value.integer.value[0];
- tegra_ext_control(codec);
- return 1;
-}
-
-static int tegra_get_lineout(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = tegra_lineout_func;
- return 0;
-}
-
-static int tegra_set_lineout(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-
- if (tegra_lineout_func == ucontrol->value.integer.value[0])
- return 0;
-
- tegra_lineout_func = ucontrol->value.integer.value[0];
- tegra_ext_control(codec);
- return 1;
-}
-
-static int tegra_get_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = tegra_spk_func;
- return 0;
-}
-
-static int tegra_set_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-
-
- if (tegra_spk_func == ucontrol->value.integer.value[0])
- return 0;
-
- tegra_spk_func = ucontrol->value.integer.value[0];
- tegra_ext_control(codec);
- return 1;
-}
-
-static int tegra_dapm_event_int_spk(struct snd_soc_dapm_widget* w,
- struct snd_kcontrol* k, int event)
-{
- if (tegra_wired_jack_conf.en_spkr != -1)
- gpio_set_value_cansleep(tegra_wired_jack_conf.en_spkr,
- SND_SOC_DAPM_EVENT_ON(event));
-
- return 0;
-}
-
-static int tegra_dapm_event_int_mic(struct snd_soc_dapm_widget* w,
- struct snd_kcontrol* k, int event)
-{
- if (tegra_wired_jack_conf.en_mic_int != -1)
- gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_int,
- SND_SOC_DAPM_EVENT_ON(event));
-
- if (tegra_wired_jack_conf.en_mic_ext != -1)
- gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_ext,
- !(SND_SOC_DAPM_EVENT_ON(event)));
-
- return 0;
-}
-
-static int tegra_dapm_event_ext_mic(struct snd_soc_dapm_widget* w,
- struct snd_kcontrol* k, int event)
-{
- if (tegra_wired_jack_conf.en_mic_ext != -1)
- gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_ext,
- SND_SOC_DAPM_EVENT_ON(event));
-
- if (tegra_wired_jack_conf.en_mic_int != -1)
- gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_int,
- !(SND_SOC_DAPM_EVENT_ON(event)));
-
- return 0;
-}
-
-/*tegra machine dapm widgets */
-static const struct snd_soc_dapm_widget tegra_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", tegra_dapm_event_ext_mic),
- SND_SOC_DAPM_MIC("Int Mic", tegra_dapm_event_int_mic),
- SND_SOC_DAPM_SPK("Lineout", NULL),
- SND_SOC_DAPM_SPK("Int Spk", tegra_dapm_event_int_spk),
- SND_SOC_DAPM_LINE("Line Jack", NULL),
- SND_SOC_DAPM_HP("Headset Jack", NULL),
-};
-
-/* Tegra machine audio map (connections to the codec pins) */
-static const struct snd_soc_dapm_route audio_map[] = {
-
- /* headset Jack - in = micin, out = LHPOUT*/
- {"Headset Jack", NULL, "HPOUTL"},
- {"Headset Jack", NULL, "HPOUTR"},
-
- /* headphone connected to LHPOUT1, RHPOUT1 */
- {"Headphone Jack", NULL, "HPOUTR"},
- {"Headphone Jack", NULL, "HPOUTL"},
-
- /* build-in speaker connected to LON/P RON/P */
- {"Int Spk", NULL, "RON"},
- {"Int Spk", NULL, "ROP"},
- {"Int Spk", NULL, "LON"},
- {"Int Spk", NULL, "LOP"},
-
- /* lineout connected to LINEOUTR and LINEOUTL */
- {"Lineout", NULL, "LINEOUTR"},
- {"Lineout", NULL, "LINEOUTL"},
-
- /* external mic is stero */
- {"IN1L", NULL, "Mic Jack"},
- {"IN1R", NULL, "Mic Jack"},
-
- /* internal mic is mono */
- {"IN1R", NULL, "Int Mic"},
-
- {"IN3L", NULL, "Line Jack"},
- {"IN3R", NULL, "Line Jack"},
-};
-
-static const char *jack_function[] = {"Headphone", "Mic", "Line", "Headset",
- "Off"
- };
-static const char *lineout_function[] = {"On", "Off"};
-static const char *spk_function[] = {"On", "Off"};
-
-static const struct soc_enum tegra_enum[] = {
- SOC_ENUM_SINGLE_EXT(5, jack_function),
- SOC_ENUM_SINGLE_EXT(2, lineout_function),
- SOC_ENUM_SINGLE_EXT(2, spk_function),
-};
-
-static const struct snd_kcontrol_new tegra_controls[] = {
- SOC_ENUM_EXT("Jack Function", tegra_enum[0], tegra_get_jack,
- tegra_set_jack),
- SOC_ENUM_EXT("Lineout Function", tegra_enum[1], tegra_get_lineout,
- tegra_set_lineout),
- SOC_ENUM_EXT("Speaker Function", tegra_enum[2], tegra_get_spk,
- tegra_set_spk),
-};
-
-static void tegra_audio_route(int device_new, int is_call_mode_new)
+static void tegra_audio_route(struct tegra_audio_data* audio_data,
+ int device_new, int is_call_mode_new)
{
int play_device_new = device_new & TEGRA_AUDIO_DEVICE_OUT_ALL;
int capture_device_new = device_new & TEGRA_AUDIO_DEVICE_IN_ALL;
+ int codec_con = audio_data->codec_con;
int is_bt_sco_mode =
(play_device_new & TEGRA_AUDIO_DEVICE_OUT_BT_SCO) ||
(capture_device_new & TEGRA_AUDIO_DEVICE_OUT_BT_SCO);
@@ -279,51 +34,46 @@ static void tegra_audio_route(int device_new, int is_call_mode_new)
(audio_data->capture_device & TEGRA_AUDIO_DEVICE_OUT_BT_SCO);
if (play_device_new != audio_data->play_device) {
- if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADPHONE) {
- tegra_jack_func = TEGRA_HP;
- tegra_spk_func = TEGRA_INT_SPK_OFF;
- }
- else if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADSET) {
- tegra_jack_func = TEGRA_HEADSET;
- tegra_spk_func = TEGRA_INT_SPK_OFF;
- }
- else if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_LINE) {
- tegra_jack_func = TEGRA_LINE;
- tegra_spk_func = TEGRA_INT_SPK_OFF;
- }
+ codec_con &= ~(TEGRA_HEADPHONE | TEGRA_LINEOUT |
+ TEGRA_SPK | TEGRA_EAR_SPK | TEGRA_HEADSET);
- if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_SPEAKER) {
- tegra_lineout_func = TEGRA_LINEOUT_OFF;
- tegra_spk_func = TEGRA_INT_SPK_ON;
- }
- else if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_EAR_SPEAKER) {
- tegra_spk_func = TEGRA_INT_SPK_OFF;
- tegra_lineout_func = TEGRA_LINEOUT_ON;
- }
- else {
- tegra_lineout_func = TEGRA_LINEOUT_OFF;
- tegra_spk_func = TEGRA_INT_SPK_OFF;
- }
- tegra_ext_control(audio_data->codec);
+ if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADPHONE)
+ codec_con |= TEGRA_HEADPHONE;
+
+ if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_LINE)
+ codec_con |= TEGRA_LINEOUT;
+
+ if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_SPEAKER)
+ codec_con |= TEGRA_SPK;
+
+ if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_EAR_SPEAKER)
+ codec_con |= TEGRA_EAR_SPK;
+
+ if (play_device_new & TEGRA_AUDIO_DEVICE_OUT_HEADSET)
+ codec_con |= TEGRA_HEADSET;
+
+ tegra_ext_control(audio_data->codec, codec_con);
audio_data->play_device = play_device_new;
}
if (capture_device_new != audio_data->capture_device) {
+ codec_con &= ~(TEGRA_INT_MIC | TEGRA_EXT_MIC |
+ TEGRA_LINEIN | TEGRA_HEADSET);
+
if (capture_device_new & (TEGRA_AUDIO_DEVICE_IN_BUILTIN_MIC |
- TEGRA_AUDIO_DEVICE_IN_MIC |
- TEGRA_AUDIO_DEVICE_IN_BACK_MIC)) {
- if ((tegra_jack_func != TEGRA_HP) &&
- (tegra_jack_func != TEGRA_HEADSET)) {
- tegra_jack_func = TEGRA_MIC;
- }
- }
- else if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_HEADSET) {
- tegra_jack_func = TEGRA_HEADSET;
- }
- else if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_LINE) {
- tegra_jack_func = TEGRA_LINE;
- }
- tegra_ext_control(audio_data->codec);
+ TEGRA_AUDIO_DEVICE_IN_BACK_MIC))
+ codec_con |= TEGRA_INT_MIC;
+
+ if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_MIC)
+ codec_con |= TEGRA_EXT_MIC;
+
+ if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_LINE)
+ codec_con |= TEGRA_LINEIN;
+
+ if (capture_device_new & TEGRA_AUDIO_DEVICE_IN_HEADSET)
+ codec_con |= TEGRA_HEADSET;
+
+ tegra_ext_control(audio_data->codec, codec_con);
audio_data->capture_device = capture_device_new;
}
@@ -362,6 +112,8 @@ static int tegra_play_route_info(struct snd_kcontrol *kcontrol,
static int tegra_play_route_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol);
+
ucontrol->value.integer.value[0] = TEGRA_AUDIO_DEVICE_NONE;
if (audio_data) {
ucontrol->value.integer.value[0] = audio_data->play_device;
@@ -373,12 +125,14 @@ static int tegra_play_route_get(struct snd_kcontrol *kcontrol,
static int tegra_play_route_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol);
+
if (audio_data) {
int play_device_new = ucontrol->value.integer.value[0] &
TEGRA_AUDIO_DEVICE_OUT_ALL;
if (audio_data->play_device != play_device_new) {
- tegra_audio_route(
+ tegra_audio_route(audio_data,
play_device_new | audio_data->capture_device,
audio_data->is_call_mode);
return 1;
@@ -411,6 +165,8 @@ static int tegra_capture_route_info(struct snd_kcontrol *kcontrol,
static int tegra_capture_route_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol);
+
ucontrol->value.integer.value[0] = TEGRA_AUDIO_DEVICE_NONE;
if (audio_data) {
ucontrol->value.integer.value[0] = audio_data->capture_device;
@@ -422,12 +178,14 @@ static int tegra_capture_route_get(struct snd_kcontrol *kcontrol,
static int tegra_capture_route_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol);
+
if (audio_data) {
int capture_device_new = ucontrol->value.integer.value[0] &
TEGRA_AUDIO_DEVICE_IN_ALL;
if (audio_data->capture_device != capture_device_new) {
- tegra_audio_route(
+ tegra_audio_route(audio_data,
audio_data->play_device | capture_device_new,
audio_data->is_call_mode);
return 1;
@@ -460,6 +218,8 @@ static int tegra_call_mode_info(struct snd_kcontrol *kcontrol,
static int tegra_call_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol);
+
ucontrol->value.integer.value[0] = TEGRA_AUDIO_DEVICE_NONE;
if (audio_data) {
ucontrol->value.integer.value[0] = audio_data->is_call_mode;
@@ -471,11 +231,13 @@ static int tegra_call_mode_get(struct snd_kcontrol *kcontrol,
static int tegra_call_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
+ struct tegra_audio_data* audio_data = snd_kcontrol_chip(kcontrol);
+
if (audio_data) {
int is_call_mode_new = ucontrol->value.integer.value[0];
if (audio_data->is_call_mode != is_call_mode_new) {
- tegra_audio_route(
+ tegra_audio_route(audio_data,
audio_data->play_device |
audio_data->capture_device,
is_call_mode_new);
@@ -498,81 +260,26 @@ struct snd_kcontrol_new tegra_call_mode_control = {
int tegra_controls_init(struct snd_soc_codec *codec)
{
+ struct tegra_audio_data* audio_data = codec->socdev->codec_data;
int err;
- if (codec == NULL)
- return -ENODEV;
+ /* Add play route control */
+ err = snd_ctl_add(codec->card,
+ snd_ctl_new1(&tegra_play_route_control, audio_data));
+ if (err < 0)
+ return err;
- if (!audio_data) {
- audio_data = kzalloc(sizeof(*audio_data), GFP_KERNEL);
- if (!audio_data) {
- pr_err("failed to allocate tegra_audio_data \n");
- return -ENOMEM;
- }
+ /* Add capture route control */
+ err = snd_ctl_add(codec->card,
+ snd_ctl_new1(&tegra_capture_route_control, audio_data));
+ if (err < 0)
+ return err;
- /* Add tegra specific controls */
- err = snd_soc_add_controls(codec, tegra_controls,
- ARRAY_SIZE(tegra_controls));
- if (err < 0)
- goto fail;
-
- /* Add tegra specific widgets */
- snd_soc_dapm_new_controls(codec, tegra_dapm_widgets,
- ARRAY_SIZE(tegra_dapm_widgets));
-
- /* Set up tegra specific audio path audio_map */
- snd_soc_dapm_add_routes(codec, audio_map,
- ARRAY_SIZE(audio_map));
-
- audio_data->codec = codec;
- /* Add play route control */
- err = snd_ctl_add(codec->card,
- snd_ctl_new1(&tegra_play_route_control, NULL));
- if (err < 0)
- goto fail;
-
- /* Add capture route control */
- err = snd_ctl_add(codec->card,
- snd_ctl_new1(&tegra_capture_route_control, NULL));
- if (err < 0)
- goto fail;
-
- /* Add call mode switch control */
- err = snd_ctl_add(codec->card,
- snd_ctl_new1(&tegra_call_mode_control, NULL));
- if (err < 0)
- goto fail;
-
- /* Add jack detection */
- err = tegra_jack_init(codec);
- if (err < 0)
- goto fail;
-
- /* Default to HP output */
- tegra_jack_func = TEGRA_HP;
- tegra_lineout_func = TEGRA_LINEOUT_ON;
- tegra_spk_func = TEGRA_INT_SPK_OFF;
- tegra_ext_control(codec);
-
- snd_soc_dapm_sync(codec);
- }
+ /* Add call mode switch control */
+ err = snd_ctl_add(codec->card,
+ snd_ctl_new1(&tegra_call_mode_control, audio_data));
+ if (err < 0)
+ return err;
return 0;
-
-fail:
- if (audio_data) {
- kfree(audio_data);
- audio_data = 0;
- }
- return err;
-}
-
-void tegra_controls_exit(void)
-{
- tegra_jack_exit();
-
- if (audio_data) {
- kfree(audio_data);
- audio_data = 0;
- }
}
diff --git a/sound/soc/tegra/tegra_soc_wm8753.c b/sound/soc/tegra/tegra_soc_wm8753.c
index 0be84a031354..b285e6f82b33 100644
--- a/sound/soc/tegra/tegra_soc_wm8753.c
+++ b/sound/soc/tegra/tegra_soc_wm8753.c
@@ -1,7 +1,7 @@
/*
* tegra_soc_wm8753.c -- SoC audio for tegra
*
- * Copyright 2011 Nvidia Graphics Pvt. Ltd.
+ * Copyright 2010-2011 Nvidia Graphics Pvt. Ltd.
*
* Author: Sachin Nikam
* snikam@nvidia.com
@@ -26,6 +26,7 @@
#include "tegra_soc.h"
#include "../codecs/wm8753.h"
+#include <sound/soc-dapm.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
@@ -142,6 +143,7 @@ static struct platform_device *tegra_snd_device;
static struct regulator* wm8753_reg;
extern struct snd_soc_dai tegra_i2s_dai[];
+extern struct snd_soc_dai tegra_spdif_dai;
extern struct snd_soc_dai tegra_generic_codec_dai[];
extern struct snd_soc_platform tegra_soc_platform;
@@ -151,61 +153,49 @@ static int tegra_hifi_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- int err;
- unsigned int value;
- unsigned int channels, rate, bit_size = 16;
struct snd_soc_codec *codec = codec_dai->codec;
+ struct tegra_audio_data* audio_data = rtd->socdev->codec_data;
+ enum dac_dap_data_format data_fmt;
+ int dai_flag = 0, sys_clk;
+ unsigned int value;
+ int err;
- rate = params_rate(params); /* Sampling Rate in Hz */
- channels = params_channels(params); /* Number of channels */
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- bit_size = 16;
- break;
- case SNDRV_PCM_FORMAT_S20_3LE:
- bit_size = 20;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- bit_size = 24;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- bit_size = 32;
- break;
- default:
- pr_err(KERN_ERR "Invalid pcm format size\n");
- return EINVAL;
- }
+ if (tegra_das_is_port_master(tegra_audio_codec_type_hifi))
+ dai_flag |= SND_SOC_DAIFMT_CBM_CFM;
+ else
+ dai_flag |= SND_SOC_DAIFMT_CBS_CFS;
+
+ data_fmt = tegra_das_get_codec_data_fmt(tegra_audio_codec_type_hifi);
- err = snd_soc_dai_set_fmt(codec_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
+ /* We are supporting DSP and I2s format for now */
+ if (data_fmt & dac_dap_data_format_i2s)
+ dai_flag |= SND_SOC_DAIFMT_I2S;
+ else
+ dai_flag |= SND_SOC_DAIFMT_DSP_A;
+
+ err = snd_soc_dai_set_fmt(codec_dai, dai_flag);
if (err < 0) {
- pr_err(KERN_ERR "codec_dai fmt not set\n");
+ pr_err("codec_dai fmt not set \n");
return err;
}
- err = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
+
+ err = snd_soc_dai_set_fmt(cpu_dai, dai_flag);
if (err < 0) {
- pr_err(KERN_ERR "cpu_dai fmt not set\n");
+ pr_err("cpu_dai fmt not set \n");
return err;
}
- err = snd_soc_dai_set_sysclk(codec_dai, 0, bit_size*rate*channels*2*4,
- SND_SOC_CLOCK_IN);
+ sys_clk = clk_get_rate(audio_data->dap_mclk);
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, sys_clk, SND_SOC_CLOCK_IN);
if (err < 0) {
- pr_err(KERN_ERR "codec_dai clock not set\n");
+ pr_err("codec_dai clock not set\n");
return err;
}
- err = snd_soc_dai_set_sysclk(cpu_dai, 0, bit_size*rate*channels*2,
- SND_SOC_CLOCK_IN);
+ err = snd_soc_dai_set_sysclk(cpu_dai, 0, sys_clk, SND_SOC_CLOCK_IN);
if (err < 0) {
- pr_err(KERN_ERR "cpu_dai clock not set\n");
+ pr_err("cpu_dai clock not set\n");
return err;
}
@@ -326,24 +316,49 @@ static int tegra_voice_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct tegra_audio_data* audio_data = rtd->socdev->codec_data;
+ enum dac_dap_data_format data_fmt;
+ int dai_flag = 0, sys_clk;
int err;
- err = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_DSP_A | \
- SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBS_CFS);
+ if (tegra_das_is_port_master(tegra_audio_codec_type_bluetooth))
+ dai_flag |= SND_SOC_DAIFMT_CBM_CFM;
+ else
+ dai_flag |= SND_SOC_DAIFMT_CBS_CFS;
+
+ data_fmt = tegra_das_get_codec_data_fmt(tegra_audio_codec_type_bluetooth);
+
+ /* We are supporting DSP and I2s format for now */
+ if (data_fmt & dac_dap_data_format_dsp)
+ dai_flag |= SND_SOC_DAIFMT_DSP_A;
+ else
+ dai_flag |= SND_SOC_DAIFMT_I2S;
+ err = snd_soc_dai_set_fmt(codec_dai, dai_flag);
if (err < 0) {
- pr_err("%s:cpu_dai fmt not set \n", __func__);
- return err;
+ pr_err("codec_dai fmt not set \n");
+ return err;
}
- err = snd_soc_dai_set_sysclk(cpu_dai, 0, I2S2_CLK, SND_SOC_CLOCK_IN);
+ err = snd_soc_dai_set_fmt(cpu_dai, dai_flag);
+ if (err < 0) {
+ pr_err("cpu_dai fmt not set \n");
+ return err;
+ }
+ sys_clk = clk_get_rate(audio_data->dap_mclk);
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, sys_clk, SND_SOC_CLOCK_IN);
if (err < 0) {
- pr_err("%s:cpu_dai clock not set\n", __func__);
- return err;
+ pr_err("cpu_dai clock not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(cpu_dai, 0, sys_clk, SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ pr_err("cpu_dai clock not set\n");
+ return err;
}
return 0;
@@ -354,14 +369,161 @@ static int tegra_voice_hw_free(struct snd_pcm_substream *substream)
return 0;
}
+static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ return 0;
+}
+
+int tegra_codec_startup(struct snd_pcm_substream *substream)
+{
+ tegra_das_power_mode(true);
+
+ return 0;
+}
+
+void tegra_codec_shutdown(struct snd_pcm_substream *substream)
+{
+ tegra_das_power_mode(false);
+}
+
+int tegra_soc_suspend_pre(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+int tegra_soc_suspend_post(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct tegra_audio_data* audio_data = socdev->codec_data;
+
+ clk_disable(audio_data->dap_mclk);
+
+ return 0;
+}
+
+int tegra_soc_resume_pre(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct tegra_audio_data* audio_data = socdev->codec_data;
+
+ clk_enable(audio_data->dap_mclk);
+
+ return 0;
+}
+
+int tegra_soc_resume_post(struct platform_device *pdev)
+{
+ return 0;
+}
+
static struct snd_soc_ops tegra_hifi_ops = {
.hw_params = tegra_hifi_hw_params,
.hw_free = tegra_hifi_hw_free,
+ .startup = tegra_codec_startup,
+ .shutdown = tegra_codec_shutdown,
};
static struct snd_soc_ops tegra_voice_ops = {
.hw_params = tegra_voice_hw_params,
.hw_free = tegra_voice_hw_free,
+ .startup = tegra_codec_startup,
+ .shutdown = tegra_codec_shutdown,
+};
+
+static struct snd_soc_ops tegra_spdif_ops = {
+ .hw_params = tegra_spdif_hw_params,
+};
+
+void tegra_ext_control(struct snd_soc_codec *codec, int new_con)
+{
+ struct tegra_audio_data* audio_data = codec->socdev->codec_data;
+
+ /* Disconnect old codec routes and connect new routes*/
+ if (new_con & TEGRA_HEADPHONE)
+ snd_soc_dapm_enable_pin(codec, "Headphone");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headphone");
+
+ if (new_con & TEGRA_EAR_SPK)
+ snd_soc_dapm_enable_pin(codec, "EarPiece");
+ else
+ snd_soc_dapm_disable_pin(codec, "EarPiece");
+
+ if (new_con & TEGRA_SPK)
+ snd_soc_dapm_enable_pin(codec, "Int Spk");
+ else
+ snd_soc_dapm_disable_pin(codec, "Int Spk");
+
+ if (new_con & TEGRA_INT_MIC)
+ snd_soc_dapm_enable_pin(codec, "Int Mic");
+ else
+ snd_soc_dapm_disable_pin(codec, "Int Mic");
+
+ if (new_con & TEGRA_EXT_MIC)
+ snd_soc_dapm_enable_pin(codec, "Ext Mic");
+ else
+ snd_soc_dapm_disable_pin(codec, "Ext Mic");
+
+ if (new_con & TEGRA_LINEIN)
+ snd_soc_dapm_enable_pin(codec, "Linein");
+ else
+ snd_soc_dapm_disable_pin(codec, "Linein");
+
+ if (new_con & TEGRA_HEADSET)
+ snd_soc_dapm_enable_pin(codec, "Headset");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headset");
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync(codec);
+ audio_data->codec_con = new_con;
+}
+
+/*tegra machine dapm widgets */
+static const struct snd_soc_dapm_widget tegra_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_HP("EarPiece", NULL),
+ SND_SOC_DAPM_HP("Headset", NULL),
+ SND_SOC_DAPM_SPK("Int Spk", NULL),
+ SND_SOC_DAPM_MIC("Ext Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_LINE("Linein", NULL),
+};
+
+/* Tegra machine audio map (connections to the codec pins) */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* headphone connected to LHPOUT1, RHPOUT1 */
+ {"Headphone", NULL, "ROUT1"},
+ {"Headphone", NULL, "LOUT1"},
+
+ /* earpiece */
+ {"EarPiece", NULL, "ROUT2"},
+ {"EarPiece", NULL, "LOUT2"},
+
+ /* headset Jack */
+ {"Headset", NULL, "ROUT1"},
+ {"Headset", NULL, "LOUT1"},
+ {"MIC1", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Headset"},
+
+ /* build-in speaker */
+ {"Int Spk", NULL, "ROUT1"},
+ {"Int Spk", NULL, "LOUT1"},
+
+ /* internal mic is mono */
+ {"MIC1", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Int Mic"},
+
+ /* external mic is stero */
+ {"MIC2", NULL, "Mic Bias"},
+ {"MIC2N", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Ext Mic"},
+
+ /* Line in */
+ {"LINE1", NULL, "Linein"},
+ {"LINE2", NULL, "Linein"},
};
static void wm8753_intr_work(struct work_struct *work)
@@ -413,73 +575,106 @@ static irqreturn_t wm8753_irq(int irq, void *data)
static int tegra_codec_init(struct snd_soc_codec *codec)
{
- int ret;
+ struct tegra_audio_data* audio_data = codec->socdev->codec_data;
+ int ret = 0;
unsigned int value;
- ret = tegra_controls_init(codec);
- if (ret < 0)
- goto failed;
-
- if (!wm8753_jack) {
-
- wm8753_jack = kzalloc(sizeof(*wm8753_jack), GFP_KERNEL);
- if (!wm8753_jack) {
- pr_err("failed to allocate wm8753-jack\n");
- return -ENOMEM;
+ if (!audio_data->init_done) {
+ audio_data->dap_mclk = tegra_das_get_dap_mclk();
+ if (!audio_data->dap_mclk) {
+ pr_err("Failed to get dap mclk \n");
+ ret = -ENODEV;
+ return ret;
}
+ clk_enable(audio_data->dap_mclk);
- wm8753_jack->gpio = TEGRA_GPIO_PW3;
- wm8753_jack->pcodec = codec;
-
- INIT_WORK(&wm8753_jack->work, wm8753_intr_work);
-
- ret = snd_jack_new(codec->card, "Headphone Jack", SND_JACK_HEADPHONE,
- &wm8753_jack->jack);
- if (ret < 0)
- goto failed;
-
- ret = gpio_request(wm8753_jack->gpio, "headphone-detect-gpio");
- if (ret)
- goto failed;
-
- ret = gpio_direction_input(wm8753_jack->gpio);
- if (ret)
- goto gpio_failed;
+ /* Add tegra specific widgets */
+ snd_soc_dapm_new_controls(codec, tegra_dapm_widgets,
+ ARRAY_SIZE(tegra_dapm_widgets));
- tegra_gpio_enable(wm8753_jack->gpio);
+ /* Set up tegra specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map,
+ ARRAY_SIZE(audio_map));
- ret = request_irq(gpio_to_irq(wm8753_jack->gpio),
- wm8753_irq,
- IRQF_TRIGGER_FALLING,
- "wm8753",
- wm8753_jack);
+ /* Add jack detection */
+ ret = tegra_jack_init(codec);
+ if (ret < 0) {
+ pr_err("Failed in jack init \n");
+ return ret;
+ }
- if (ret)
- goto gpio_failed;
+ /* Default to OFF */
+ tegra_ext_control(codec, TEGRA_AUDIO_OFF);
- /* Configure GPIO2 pin to generate the interrupt */
- value = snd_soc_read(codec, WM8753_GPIO2);
- value |= (WM8753_GPIO2_GP2M_0 | WM8753_GPIO2_GP2M_1);
- value &= ~(WM8753_GPIO2_GP2M_2);
- snd_soc_write(codec, WM8753_GPIO2, value);
+ ret = tegra_controls_init(codec);
+ if (ret < 0) {
+ pr_err("Failed in controls init \n");
+ return ret;
+ }
- /* Active low Interrupt */
- value = snd_soc_read(codec, WM8753_GPIO1);
- value |= (WM8753_GPIO1_INTCON_1 | WM8753_GPIO1_INTCON_0);
- snd_soc_write(codec, WM8753_GPIO1, value);
+ if (!wm8753_jack) {
+ wm8753_jack = kzalloc(sizeof(*wm8753_jack), GFP_KERNEL);
+ if (!wm8753_jack) {
+ pr_err("failed to allocate wm8753-jack\n");
+ return -ENOMEM;
+ }
+
+ wm8753_jack->gpio = TEGRA_GPIO_PW3;
+ wm8753_jack->pcodec = codec;
+
+ INIT_WORK(&wm8753_jack->work, wm8753_intr_work);
+
+ ret = snd_jack_new(codec->card, "Headphone Jack", SND_JACK_HEADPHONE,
+ &wm8753_jack->jack);
+ if (ret < 0)
+ goto failed;
+
+ ret = gpio_request(wm8753_jack->gpio, "headphone-detect-gpio");
+ if (ret)
+ goto failed;
+
+ ret = gpio_direction_input(wm8753_jack->gpio);
+ if (ret)
+ goto gpio_failed;
+
+ tegra_gpio_enable(wm8753_jack->gpio);
+
+ ret = request_irq(gpio_to_irq(wm8753_jack->gpio),
+ wm8753_irq,
+ IRQF_TRIGGER_FALLING,
+ "wm8753",
+ wm8753_jack);
+
+ if (ret)
+ goto gpio_failed;
+
+ /* Configure GPIO2 pin to generate the interrupt */
+ value = snd_soc_read(codec, WM8753_GPIO2);
+ value |= (WM8753_GPIO2_GP2M_0 | WM8753_GPIO2_GP2M_1);
+ value &= ~(WM8753_GPIO2_GP2M_2);
+ snd_soc_write(codec, WM8753_GPIO2, value);
+
+ /* Active low Interrupt */
+ value = snd_soc_read(codec, WM8753_GPIO1);
+ value |= (WM8753_GPIO1_INTCON_1 | WM8753_GPIO1_INTCON_0);
+ snd_soc_write(codec, WM8753_GPIO1, value);
+
+ /* GPIO4 interrupt polarity -- interupt when low i.e Headphone connected */
+ value = snd_soc_read(codec, WM8753_INTPOL);
+ value |= (WM8753_INTPOL_GPIO4IPOL);
+ snd_soc_write(codec, WM8753_INTPOL, value);
+
+ /* GPIO4 interrupt enable and disable other interrupts */
+ value = snd_soc_read(codec, WM8753_INTEN);
+ value |= (WM8753_INTEN_GPIO4IEN);
+ value &= ~(WM8753_INTEN_MICSHTEN | WM8753_INTEN_MICDETEN |
+ WM8753_INTEN_GPIO3IEN | WM8753_INTEN_HPSWIEN |
+ WM8753_INTEN_GPIO5IEN | WM8753_INTEN_TSDIEN);
+ snd_soc_write(codec, WM8753_INTEN, value);
+ }
- /* GPIO4 interrupt polarity -- interupt when low i.e Headphone connected */
- value = snd_soc_read(codec, WM8753_INTPOL);
- value |= (WM8753_INTPOL_GPIO4IPOL);
- snd_soc_write(codec, WM8753_INTPOL, value);
-
- /* GPIO4 interrupt enable and disable other interrupts */
- value = snd_soc_read(codec, WM8753_INTEN);
- value |= (WM8753_INTEN_GPIO4IEN);
- value &= ~(WM8753_INTEN_MICSHTEN | WM8753_INTEN_MICDETEN |
- WM8753_INTEN_GPIO3IEN | WM8753_INTEN_HPSWIEN |
- WM8753_INTEN_GPIO5IEN | WM8753_INTEN_TSDIEN);
- snd_soc_write(codec, WM8753_INTEN, value);
+ audio_data->codec = codec;
+ audio_data->init_done = 1;
}
return ret;
@@ -509,6 +704,22 @@ static struct snd_soc_dai_link tegra_soc_dai[] = {
.init = tegra_codec_init,
.ops = &tegra_voice_ops,
},
+ {
+ .name = "Tegra-spdif",
+ .stream_name = "Tegra Spdif",
+ .cpu_dai = &tegra_spdif_dai,
+ .codec_dai = &tegra_generic_codec_dai[1],
+ .init = tegra_codec_init,
+ .ops = &tegra_spdif_ops,
+ },
+};
+
+static struct tegra_audio_data audio_data = {
+ .init_done = 0,
+ .play_device = TEGRA_AUDIO_DEVICE_NONE,
+ .capture_device = TEGRA_AUDIO_DEVICE_NONE,
+ .is_call_mode = false,
+ .codec_con = TEGRA_AUDIO_OFF,
};
static struct snd_soc_card tegra_snd_soc = {
@@ -516,11 +727,16 @@ static struct snd_soc_card tegra_snd_soc = {
.platform = &tegra_soc_platform,
.dai_link = tegra_soc_dai,
.num_links = ARRAY_SIZE(tegra_soc_dai),
+ .suspend_pre = tegra_soc_suspend_pre,
+ .suspend_post = tegra_soc_suspend_post,
+ .resume_pre = tegra_soc_resume_pre,
+ .resume_post = tegra_soc_resume_post,
};
static struct snd_soc_device tegra_snd_devdata = {
.card = &tegra_snd_soc,
.codec_dev = &soc_codec_dev_wm8753,
+ .codec_data = &audio_data,
};
static int __init tegra_init(void)
@@ -571,7 +787,6 @@ err_put_regulator:
static void __exit tegra_exit(void)
{
- tegra_controls_exit();
platform_device_unregister(tegra_snd_device);
regulator_disable(wm8753_reg);
regulator_put(wm8753_reg);
diff --git a/sound/soc/tegra/tegra_soc_wm8903.c b/sound/soc/tegra/tegra_soc_wm8903.c
index 3fe2dd4dc468..b76b6dd575f8 100644
--- a/sound/soc/tegra/tegra_soc_wm8903.c
+++ b/sound/soc/tegra/tegra_soc_wm8903.c
@@ -1,7 +1,7 @@
/*
* tegra_soc_wm8903.c -- SoC audio for tegra
*
- * (c) 2010 Nvidia Graphics Pvt. Ltd.
+ * (c) 2010-2011 Nvidia Graphics Pvt. Ltd.
* http://www.nvidia.com
*
* Copyright 2007 Wolfson Microelectronics PLC.
@@ -16,6 +16,9 @@
*/
#include "tegra_soc.h"
+#include <linux/gpio.h>
+#include <sound/soc-dapm.h>
+#include <linux/regulator/consumer.h>
#include "../codecs/wm8903.h"
static struct platform_device *tegra_snd_device;
@@ -24,22 +27,15 @@ extern struct snd_soc_dai tegra_i2s_dai[];
extern struct snd_soc_dai tegra_spdif_dai;
extern struct snd_soc_dai tegra_generic_codec_dai[];
extern struct snd_soc_platform tegra_soc_platform;
+extern struct wired_jack_conf tegra_wired_jack_conf;
/* codec register values */
-#define B07_INEMUTE 7
-#define B06_VOL_M3DB 6
#define B00_IN_VOL 0
#define B00_INR_ENA 0
#define B01_INL_ENA 1
-#define R06_MICBIAS_CTRL_0 6
-#define B07_MICDET_HYST_ENA 7
-#define B04_MICDET_THR 4
-#define B02_MICSHORT_THR 2
#define B01_MICDET_ENA 1
#define B00_MICBIAS_ENA 0
#define B15_DRC_ENA 15
-#define B03_DACL_ENA 3
-#define B02_DACR_ENA 2
#define B01_ADCL_ENA 1
#define B00_ADCR_ENA 0
#define B06_IN_CM_ENA 6
@@ -48,106 +44,116 @@ extern struct snd_soc_platform tegra_soc_platform;
#define B00_MODE 0
#define B06_AIF_ADCL 7
#define B06_AIF_ADCR 6
-#define B05_ADC_HPF_CUT 5
#define B04_ADC_HPF_ENA 4
-#define B01_ADCL_DATINV 1
-#define B00_ADCR_DATINV 0
#define R20_SIDETONE_CTRL 32
#define R29_DRC_1 41
#define SET_REG_VAL(r,m,l,v) (((r)&(~((m)<<(l))))|(((v)&(m))<<(l)))
+
static int tegra_hifi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
- int err;
struct snd_soc_codec *codec = codec_dai->codec;
- int CtrlReg = 0;
- int VolumeCtrlReg = 0;
- int SidetoneCtrlReg = 0;
- int SideToneAtenuation = 0;
-
- err = snd_soc_dai_set_fmt(codec_dai,
- SND_SOC_DAIFMT_I2S | \
- SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBS_CFS);
+ struct tegra_audio_data* audio_data = rtd->socdev->codec_data;
+ enum dac_dap_data_format data_fmt;
+ int dai_flag = 0, sys_clk;
+ int err;
+
+ if (tegra_das_is_port_master(tegra_audio_codec_type_hifi))
+ dai_flag |= SND_SOC_DAIFMT_CBM_CFM;
+ else
+ dai_flag |= SND_SOC_DAIFMT_CBS_CFS;
+
+ data_fmt = tegra_das_get_codec_data_fmt(tegra_audio_codec_type_hifi);
+
+ /* We are supporting DSP and I2s format for now */
+ if (data_fmt & dac_dap_data_format_i2s)
+ dai_flag |= SND_SOC_DAIFMT_I2S;
+ else
+ dai_flag |= SND_SOC_DAIFMT_DSP_A;
+
+ err = snd_soc_dai_set_fmt(codec_dai, dai_flag);
if (err < 0) {
pr_err("codec_dai fmt not set \n");
return err;
}
- err = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_I2S | \
- SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBS_CFS);
+ err = snd_soc_dai_set_fmt(cpu_dai, dai_flag);
if (err < 0) {
pr_err("cpu_dai fmt not set \n");
return err;
}
- err = snd_soc_dai_set_sysclk(codec_dai, 0, I2S1_CLK, SND_SOC_CLOCK_IN);
+ sys_clk = clk_get_rate(audio_data->dap_mclk);
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, sys_clk, SND_SOC_CLOCK_IN);
if (err < 0) {
pr_err("codec_dai clock not set\n");
return err;
}
- err = snd_soc_dai_set_sysclk(cpu_dai, 0, I2S1_CLK, SND_SOC_CLOCK_IN);
+ err = snd_soc_dai_set_sysclk(cpu_dai, 0, sys_clk, SND_SOC_CLOCK_IN);
if (err < 0) {
pr_err("cpu_dai clock not set\n");
return err;
}
if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
+ int CtrlReg = 0;
+ int VolumeCtrlReg = 0;
+ int SidetoneCtrlReg = 0;
+ int SideToneAtenuation = 0;
+
snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_0, 0X7);
snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_0, 0X7);
- // Mic Bias enable
+ /* Mic Bias enable */
CtrlReg = (0x1<<B00_MICBIAS_ENA) | (0x1<<B01_MICDET_ENA);
snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0, CtrlReg);
- // Enable DRC
+ /* Enable DRC */
CtrlReg = snd_soc_read(codec, WM8903_DRC_0);
CtrlReg |= (1<<B15_DRC_ENA);
snd_soc_write(codec, WM8903_DRC_0, CtrlReg);
- // Single Ended Mic
+ /* Single Ended Mic */
CtrlReg = (0x0<<B06_IN_CM_ENA) |
(0x0<<B00_MODE) | (0x0<<B04_IP_SEL_N)
| (0x1<<B02_IP_SEL_P);
VolumeCtrlReg = (0x1C << B00_IN_VOL);
- // Mic Setting
+ /* Mic Setting */
snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_1, CtrlReg);
snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_1, CtrlReg);
- // voulme for single ended mic
+ /* voulme for single ended mic */
snd_soc_write(codec, WM8903_ANALOGUE_LEFT_INPUT_0,
VolumeCtrlReg);
snd_soc_write(codec, WM8903_ANALOGUE_RIGHT_INPUT_0,
VolumeCtrlReg);
- // replicate mic setting on both channels
+ /* replicate mic setting on both channels */
CtrlReg = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_0);
CtrlReg = SET_REG_VAL(CtrlReg, 0x1, B06_AIF_ADCR, 0x0);
CtrlReg = SET_REG_VAL(CtrlReg, 0x1, B06_AIF_ADCL, 0x0);
snd_soc_write(codec, WM8903_AUDIO_INTERFACE_0, CtrlReg);
- // Enable analog inputs
+ /* Enable analog inputs */
CtrlReg = (0x1<<B01_INL_ENA) | (0x1<<B00_INR_ENA);
snd_soc_write(codec, WM8903_POWER_MANAGEMENT_0, CtrlReg);
- // ADC Settings
+ /* ADC Settings */
CtrlReg = snd_soc_read(codec, WM8903_ADC_DIGITAL_0);
CtrlReg |= (0x1<<B04_ADC_HPF_ENA);
snd_soc_write(codec, WM8903_ADC_DIGITAL_0, CtrlReg);
SidetoneCtrlReg = 0;
snd_soc_write(codec, R20_SIDETONE_CTRL, SidetoneCtrlReg);
- // Enable ADC
+ /* Enable ADC */
CtrlReg = snd_soc_read(codec, WM8903_POWER_MANAGEMENT_6);
CtrlReg |= (0x1<<B00_ADCR_ENA)|(0x1<<B01_ADCL_ENA);
snd_soc_write(codec, WM8903_POWER_MANAGEMENT_6, CtrlReg);
- // Enable Sidetone
+ /* Enable Sidetone */
SidetoneCtrlReg = (0x1<<2) | (0x2<<0);
- SideToneAtenuation = 12 ; // sidetone 0 db
+ SideToneAtenuation = 12 ; /* sidetone 0 db */
SidetoneCtrlReg |= (SideToneAtenuation<<8)
| (SideToneAtenuation<<4);
snd_soc_write(codec, R20_SIDETONE_CTRL, SidetoneCtrlReg);
CtrlReg = snd_soc_read(codec, R29_DRC_1);
- CtrlReg |= 0x3; //mic volume 18 db
+ CtrlReg |= 0x3; /*mic volume 18 db */
snd_soc_write(codec, R29_DRC_1, CtrlReg);
}
@@ -158,23 +164,51 @@ static int tegra_voice_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+ struct tegra_audio_data* audio_data = rtd->socdev->codec_data;
+ enum dac_dap_data_format data_fmt;
+ int dai_flag = 0, sys_clk;
int err;
- err = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_DSP_A | \
- SND_SOC_DAIFMT_NB_NF | \
- SND_SOC_DAIFMT_CBS_CFS);
+ if (tegra_das_is_port_master(tegra_audio_codec_type_bluetooth))
+ dai_flag |= SND_SOC_DAIFMT_CBM_CFM;
+ else
+ dai_flag |= SND_SOC_DAIFMT_CBS_CFS;
+
+ data_fmt = tegra_das_get_codec_data_fmt(tegra_audio_codec_type_bluetooth);
+
+ /* We are supporting DSP and I2s format for now */
+ if (data_fmt & dac_dap_data_format_dsp)
+ dai_flag |= SND_SOC_DAIFMT_DSP_A;
+ else
+ dai_flag |= SND_SOC_DAIFMT_I2S;
+
+ err = snd_soc_dai_set_fmt(codec_dai, dai_flag);
+ if (err < 0) {
+ pr_err("codec_dai fmt not set \n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(cpu_dai, dai_flag);
if (err < 0) {
pr_err("cpu_dai fmt not set \n");
return err;
}
- err = snd_soc_dai_set_sysclk(cpu_dai, 0, I2S2_CLK, SND_SOC_CLOCK_IN);
+ sys_clk = clk_get_rate(audio_data->dap_mclk);
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, sys_clk, SND_SOC_CLOCK_IN);
if (err < 0) {
pr_err("cpu_dai clock not set\n");
return err;
}
+
+ err = snd_soc_dai_set_sysclk(cpu_dai, 0, sys_clk, SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ pr_err("cpu_dai clock not set\n");
+ return err;
+ }
+
return 0;
}
@@ -184,21 +218,251 @@ static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+int tegra_codec_startup(struct snd_pcm_substream *substream)
+{
+ tegra_das_power_mode(true);
+
+ return 0;
+}
+
+void tegra_codec_shutdown(struct snd_pcm_substream *substream)
+{
+ tegra_das_power_mode(false);
+}
+
+int tegra_soc_suspend_pre(struct platform_device *pdev, pm_message_t state)
+{
+ return 0;
+}
+
+int tegra_soc_suspend_post(struct platform_device *pdev, pm_message_t state)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct tegra_audio_data* audio_data = socdev->codec_data;
+
+ clk_disable(audio_data->dap_mclk);
+
+ return 0;
+}
+
+int tegra_soc_resume_pre(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct tegra_audio_data* audio_data = socdev->codec_data;
+
+ clk_enable(audio_data->dap_mclk);
+
+ return 0;
+}
+
+int tegra_soc_resume_post(struct platform_device *pdev)
+{
+ return 0;
+}
+
static struct snd_soc_ops tegra_hifi_ops = {
.hw_params = tegra_hifi_hw_params,
+ .startup = tegra_codec_startup,
+ .shutdown = tegra_codec_shutdown,
};
static struct snd_soc_ops tegra_voice_ops = {
.hw_params = tegra_voice_hw_params,
+ .startup = tegra_codec_startup,
+ .shutdown = tegra_codec_shutdown,
};
static struct snd_soc_ops tegra_spdif_ops = {
.hw_params = tegra_spdif_hw_params,
};
+void tegra_ext_control(struct snd_soc_codec *codec, int new_con)
+{
+ struct tegra_audio_data* audio_data = codec->socdev->codec_data;
+
+ /* Disconnect old codec routes and connect new routes*/
+ if (new_con & TEGRA_HEADPHONE)
+ snd_soc_dapm_enable_pin(codec, "Headphone");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headphone");
+
+ if (new_con & (TEGRA_LINEOUT | TEGRA_EAR_SPK))
+ snd_soc_dapm_enable_pin(codec, "Lineout");
+ else
+ snd_soc_dapm_disable_pin(codec, "Lineout");
+
+ if (new_con & TEGRA_SPK)
+ snd_soc_dapm_enable_pin(codec, "Int Spk");
+ else
+ snd_soc_dapm_disable_pin(codec, "Int Spk");
+
+ if (new_con & TEGRA_INT_MIC)
+ snd_soc_dapm_enable_pin(codec, "Int Mic");
+ else
+ snd_soc_dapm_disable_pin(codec, "Int Mic");
+
+ if (new_con & TEGRA_EXT_MIC)
+ snd_soc_dapm_enable_pin(codec, "Ext Mic");
+ else
+ snd_soc_dapm_disable_pin(codec, "Ext Mic");
+
+ if (new_con & TEGRA_LINEIN)
+ snd_soc_dapm_enable_pin(codec, "Linein");
+ else
+ snd_soc_dapm_disable_pin(codec, "Linein");
+
+ if (new_con & TEGRA_HEADSET)
+ snd_soc_dapm_enable_pin(codec, "Headset");
+ else
+ snd_soc_dapm_disable_pin(codec, "Headset");
+
+ /* signal a DAPM event */
+ snd_soc_dapm_sync(codec);
+ audio_data->codec_con = new_con;
+}
+
+static int tegra_dapm_event_int_spk(struct snd_soc_dapm_widget* w,
+ struct snd_kcontrol* k, int event)
+{
+ if (tegra_wired_jack_conf.en_spkr != -1) {
+ if (tegra_wired_jack_conf.amp_reg) {
+ if (SND_SOC_DAPM_EVENT_ON(event) &&
+ !tegra_wired_jack_conf.amp_reg_enabled) {
+ regulator_enable(tegra_wired_jack_conf.amp_reg);
+ tegra_wired_jack_conf.amp_reg_enabled = 1;
+ }
+ else if (!SND_SOC_DAPM_EVENT_ON(event) &&
+ tegra_wired_jack_conf.amp_reg_enabled) {
+ regulator_disable(tegra_wired_jack_conf.amp_reg);
+ tegra_wired_jack_conf.amp_reg_enabled = 0;
+ }
+ }
+
+ gpio_set_value_cansleep(tegra_wired_jack_conf.en_spkr,
+ SND_SOC_DAPM_EVENT_ON(event) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+static int tegra_dapm_event_int_mic(struct snd_soc_dapm_widget* w,
+ struct snd_kcontrol* k, int event)
+{
+ if (tegra_wired_jack_conf.en_mic_int != -1)
+ gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_int,
+ SND_SOC_DAPM_EVENT_ON(event) ? 1 : 0);
+
+ if (tegra_wired_jack_conf.en_mic_ext != -1)
+ gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_ext,
+ SND_SOC_DAPM_EVENT_ON(event) ? 0 : 1);
+
+ return 0;
+}
+
+static int tegra_dapm_event_ext_mic(struct snd_soc_dapm_widget* w,
+ struct snd_kcontrol* k, int event)
+{
+ if (tegra_wired_jack_conf.en_mic_ext != -1)
+ gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_ext,
+ SND_SOC_DAPM_EVENT_ON(event) ? 1 : 0);
+
+ if (tegra_wired_jack_conf.en_mic_int != -1)
+ gpio_set_value_cansleep(tegra_wired_jack_conf.en_mic_int,
+ SND_SOC_DAPM_EVENT_ON(event) ? 0 : 1);
+
+ return 0;
+}
+
+/*tegra machine dapm widgets */
+static const struct snd_soc_dapm_widget tegra_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_HP("Headset", NULL),
+ SND_SOC_DAPM_SPK("Lineout", NULL),
+ SND_SOC_DAPM_SPK("Int Spk", tegra_dapm_event_int_spk),
+ SND_SOC_DAPM_MIC("Ext Mic", tegra_dapm_event_ext_mic),
+ SND_SOC_DAPM_MIC("Int Mic", tegra_dapm_event_int_mic),
+ SND_SOC_DAPM_LINE("Linein", NULL),
+};
+
+/* Tegra machine audio map (connections to the codec pins) */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+ /* headphone connected to LHPOUT1, RHPOUT1 */
+ {"Headphone", NULL, "HPOUTR"},
+ {"Headphone", NULL, "HPOUTL"},
+
+ /* headset Jack - in = micin, out = HPOUT*/
+ {"Headset", NULL, "HPOUTR"},
+ {"Headset", NULL, "HPOUTL"},
+ {"IN1L", NULL, "Headset"},
+ {"IN1R", NULL, "Headset"},
+
+ /* lineout connected to LINEOUTR and LINEOUTL */
+ {"Lineout", NULL, "LINEOUTR"},
+ {"Lineout", NULL, "LINEOUTL"},
+
+ /* build-in speaker connected to LON/P RON/P */
+ {"Int Spk", NULL, "RON"},
+ {"Int Spk", NULL, "ROP"},
+ {"Int Spk", NULL, "LON"},
+ {"Int Spk", NULL, "LOP"},
+
+ /* internal mic is mono */
+ {"IN1R", NULL, "Int Mic"},
+
+ /* external mic is stero */
+ {"IN1L", NULL, "Ext Mic"},
+ {"IN1R", NULL, "Ext Mic"},
+
+ /* Line In */
+ {"IN3L", NULL, "Linein"},
+ {"IN3R", NULL, "Linein"},
+};
+
+
static int tegra_codec_init(struct snd_soc_codec *codec)
{
- return tegra_controls_init(codec);
+ struct tegra_audio_data* audio_data = codec->socdev->codec_data;
+ int err = 0;
+
+ if (!audio_data->init_done) {
+ audio_data->dap_mclk = tegra_das_get_dap_mclk();
+ if (!audio_data->dap_mclk) {
+ pr_err("Failed to get dap mclk \n");
+ err = -ENODEV;
+ return err;
+ }
+ clk_enable(audio_data->dap_mclk);
+
+ /* Add tegra specific widgets */
+ snd_soc_dapm_new_controls(codec, tegra_dapm_widgets,
+ ARRAY_SIZE(tegra_dapm_widgets));
+
+ /* Set up tegra specific audio path audio_map */
+ snd_soc_dapm_add_routes(codec, audio_map,
+ ARRAY_SIZE(audio_map));
+
+ /* Add jack detection */
+ err = tegra_jack_init(codec);
+ if (err < 0) {
+ pr_err("Failed in jack init \n");
+ return err;
+ }
+
+ /* Default to OFF */
+ tegra_ext_control(codec, TEGRA_AUDIO_OFF);
+
+ err = tegra_controls_init(codec);
+ if (err < 0) {
+ pr_err("Failed in controls init \n");
+ return err;
+ }
+
+ audio_data->codec = codec;
+ audio_data->init_done = 1;
+ }
+
+ return err;
}
static struct snd_soc_dai_link tegra_soc_dai[] = {
@@ -228,17 +492,29 @@ static struct snd_soc_dai_link tegra_soc_dai[] = {
},
};
+static struct tegra_audio_data audio_data = {
+ .init_done = 0,
+ .play_device = TEGRA_AUDIO_DEVICE_NONE,
+ .capture_device = TEGRA_AUDIO_DEVICE_NONE,
+ .is_call_mode = false,
+ .codec_con = TEGRA_AUDIO_OFF,
+};
+
static struct snd_soc_card tegra_snd_soc = {
.name = "tegra",
.platform = &tegra_soc_platform,
.dai_link = tegra_soc_dai,
.num_links = ARRAY_SIZE(tegra_soc_dai),
+ .suspend_pre = tegra_soc_suspend_pre,
+ .suspend_post = tegra_soc_suspend_post,
+ .resume_pre = tegra_soc_resume_pre,
+ .resume_post = tegra_soc_resume_post,
};
-
static struct snd_soc_device tegra_snd_devdata = {
.card = &tegra_snd_soc,
.codec_dev = &soc_codec_dev_wm8903,
+ .codec_data = &audio_data,
};
static int __init tegra_init(void)
@@ -248,11 +524,12 @@ static int __init tegra_init(void)
tegra_snd_device = platform_device_alloc("soc-audio", -1);
if (!tegra_snd_device) {
pr_err("failed to allocate soc-audio \n");
- return ENOMEM;
+ return -ENOMEM;
}
platform_set_drvdata(tegra_snd_device, &tegra_snd_devdata);
tegra_snd_devdata.dev = &tegra_snd_device->dev;
+
ret = platform_device_add(tegra_snd_device);
if (ret) {
pr_err("audio device could not be added \n");
@@ -272,7 +549,7 @@ fail:
static void __exit tegra_exit(void)
{
- tegra_controls_exit();
+ tegra_jack_exit();
platform_device_unregister(tegra_snd_device);
}
diff --git a/sound/soc/tegra/tegra_spdif.c b/sound/soc/tegra/tegra_spdif.c
index d69cbd7cc86c..53c0b19c15d2 100644
--- a/sound/soc/tegra/tegra_spdif.c
+++ b/sound/soc/tegra/tegra_spdif.c
@@ -1,7 +1,7 @@
/*
* tegra_spdif.c -- ALSA Soc Audio Layer
*
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -109,22 +109,24 @@ static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct tegra_spdif_info *info = dai->private_data;
- int ret = 0;
int val;
+ unsigned int rate, sample_size;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
val = SPDIF_BIT_MODE_MODE16BIT;
+ sample_size = 16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
val = SPDIF_BIT_MODE_MODE24BIT;
+ sample_size = 16;
break;
case SNDRV_PCM_FORMAT_S32_LE:
val = SPDIF_BIT_MODE_MODERAW;
+ sample_size = 32;
break;
default:
- ret =-EINVAL;
- goto err;
+ return -EINVAL;
}
spdif_set_bit_mode(info->spdif_base, val);
@@ -140,15 +142,20 @@ static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
val = params_rate(params);
break;
default:
- ret = -EINVAL;
- goto err;
+ return -EINVAL;
}
+ /* Min BCLK = samplerate * channel * bits per sample * 4 */
+ rate = val * params_channels(params) * sample_size * 4;
+
+ /* Ensure Spdif clk rate is atleast greater than min BCLK */
+ clk_set_rate(info->spdif_clk, rate);
+ if (clk_get_rate(info->spdif_clk) < rate)
+ clk_set_rate(info->spdif_clk, rate << 1);
+
spdif_set_sample_rate(info->spdif_base, val);
- return 0;
-err:
- return ret;
+ return 0;
}
@@ -161,17 +168,6 @@ static int tegra_spdif_set_dai_fmt(struct snd_soc_dai *cpu_dai,
static int tegra_spdif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
int clk_id, unsigned int freq, int dir)
{
- struct tegra_spdif_info* info = cpu_dai->private_data;
- struct tegra_audio_platform_data *pdata = info->pdev->dev.platform_data;
-
- if (info && info->spdif_clk) {
- clk_set_rate(info->spdif_clk, pdata->spdif_clk_rate);
- }
- else {
- pr_err("%s: could not get spdif clock\n", __func__);
- return -EIO;
- }
-
return 0;
}
@@ -349,6 +345,7 @@ static int tegra_spdif_driver_probe(struct platform_device *pdev)
err = PTR_ERR(info->spdif_clk);
goto fail_unmap_mem;
}
+ clk_enable(info->spdif_clk);
clk_set_rate(info->spdif_clk, info->pdata->spdif_clk_rate);
spdif_initialize(info->spdif_base, AUDIO_TX_MODE);
@@ -360,6 +357,8 @@ static int tegra_spdif_driver_probe(struct platform_device *pdev)
if (err)
goto fail_unmap_mem;
+ /* Disable SPDIF clock to save power */
+ clk_disable(info->spdif_clk);
return 0;
fail_unmap_mem: