summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-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: